@manufosela/multi-carousel

@manufosela/multi-carousel

Responsive multi-slide carousel web component with navigation and arrows

Multi Carousel

Responsive carousel with smooth transitions, keyboard navigation, and sync.

Basic Usage

Navigate with arrows, dots, or keyboard (click the carousel then use ← → Home End).

Slide 1
Slide 2
Slide 3
Slide 4
Navigate to see events...
<multi-carousel> <div class="slide">Slide 1</div> <div class="slide">Slide 2</div> <div class="slide">Slide 3</div> <div class="slide">Slide 4</div> </multi-carousel> <script> carousel.addEventListener('slide-change', (e) => { console.log(`Slide ${e.detail.index + 1} of ${e.detail.total}`); }); </script>

Autoplay

Auto-advances every 2 seconds. Loops infinitely.

Auto 1
Auto 2
Auto 3
<multi-carousel autoplay="2000"> <div class="slide">Auto 1</div> <div class="slide">Auto 2</div> <div class="slide">Auto 3</div> </multi-carousel>

No Loop

Stops at first and last slide. Arrows hide at boundaries.

First
Middle
Last
<multi-carousel no-loop> <div class="slide">First</div> <div class="slide">Middle</div> <div class="slide">Last</div> </multi-carousel>

Dynamic Slides

Add and remove slides on the fly. The carousel updates automatically via slotchange.

2 slides
Slide 1
Slide 2
const carousel = document.querySelector('multi-carousel'); // Add a slide const slide = document.createElement('div'); slide.className = 'slide'; slide.textContent = 'New Slide'; carousel.appendChild(slide); // Remove last slide carousel.removeChild(carousel.lastElementChild);

Custom Styling

Customize via CSS custom properties.

Styled 1
Styled 2
Styled 3
<style> .pink-carousel { --carousel-height: 200px; --carousel-bg: #1e293b; --carousel-nav-active: #ec4899; --carousel-arrow-bg: rgba(30,41,59,0.8); --carousel-arrow-color: white; } </style> <multi-carousel class="pink-carousel"> ... </multi-carousel>

Synced Carousels

Multiple carousels that stay in sync. The top one is the master.

Master 1
Master 2
Master 3
Synced 1
Synced 2
Synced 3
<multi-carousel id="master" master> <div class="slide">Master 1</div> <div class="slide">Master 2</div> <div class="slide">Master 3</div> </multi-carousel> <!-- Follows the master, no controls --> <multi-carousel master-id="master" hide-arrows hide-nav> <div class="slide">Detail 1</div> <div class="slide">Detail 2</div> <div class="slide">Detail 3</div> </multi-carousel>
Demo code (CodePen-ready HTML, CSS, JS)
HTML (html)
<h1>Multi Carousel</h1>
  <p class="subtitle">Responsive carousel with smooth transitions, keyboard navigation, and sync.</p>
  <div class="demo-links">
    <a href="https://manufosela.dev/ui-components/">← Back to components</a>
    <a href="https://github.com/manufosela/ui-components/tree/main/packages/multi-carousel" target="_blank" rel="noopener">GitHub Repo</a>
    <a href="playground.html">Playground</a>
    <a href="https://www.npmjs.com/package/@manufosela/multi-carousel" target="_blank" rel="noopener">npm</a>
  </div>
  <div class="demo-theme-toggle">
    <theme-toggle theme="dark"></theme-toggle>
  </div>

  <!-- Basic Usage -->
  <div class="demo-section">
    <h2>Basic Usage</h2>
    <p>Navigate with arrows, dots, or keyboard (click the carousel then use ← → Home End).</p>
    <multi-carousel id="basic">
      <div class="slide slide-1">Slide 1</div>
      <div class="slide slide-2">Slide 2</div>
      <div class="slide slide-3">Slide 3</div>
      <div class="slide slide-4">Slide 4</div>
    </multi-carousel>
    <div id="basic-log" class="event-log">Navigate to see events...</div>
    <div class="code-block">&lt;multi-carousel&gt;
  &lt;div class="slide"&gt;Slide 1&lt;/div&gt;
  &lt;div class="slide"&gt;Slide 2&lt;/div&gt;
  &lt;div class="slide"&gt;Slide 3&lt;/div&gt;
  &lt;div class="slide"&gt;Slide 4&lt;/div&gt;
&lt;/multi-carousel&gt;

&lt;script&gt;
  carousel.addEventListener('slide-change', (e) =&gt; {
    console.log(`Slide ${e.detail.index + 1} of ${e.detail.total}`);
  });
&lt;/script&gt;</div>
  </div>

  <!-- Autoplay -->
  <div class="demo-section">
    <h2>Autoplay</h2>
    <p>Auto-advances every 2 seconds. Loops infinitely.</p>
    <multi-carousel autoplay="2000">
      <div class="slide slide-2">Auto 1</div>
      <div class="slide slide-3">Auto 2</div>
      <div class="slide slide-4">Auto 3</div>
    </multi-carousel>
    <div class="code-block">&lt;multi-carousel autoplay="2000"&gt;
  &lt;div class="slide"&gt;Auto 1&lt;/div&gt;
  &lt;div class="slide"&gt;Auto 2&lt;/div&gt;
  &lt;div class="slide"&gt;Auto 3&lt;/div&gt;
&lt;/multi-carousel&gt;</div>
  </div>

  <!-- No Loop -->
  <div class="demo-section">
    <h2>No Loop</h2>
    <p>Stops at first and last slide. Arrows hide at boundaries.</p>
    <multi-carousel no-loop>
      <div class="slide slide-1">First</div>
      <div class="slide slide-3">Middle</div>
      <div class="slide slide-4">Last</div>
    </multi-carousel>
    <div class="code-block">&lt;multi-carousel no-loop&gt;
  &lt;div class="slide"&gt;First&lt;/div&gt;
  &lt;div class="slide"&gt;Middle&lt;/div&gt;
  &lt;div class="slide"&gt;Last&lt;/div&gt;
&lt;/multi-carousel&gt;</div>
  </div>

  <!-- Dynamic Slides -->
  <div class="demo-section">
    <h2>Dynamic Slides</h2>
    <p>Add and remove slides on the fly. The carousel updates automatically via <code>slotchange</code>.</p>
    <div style="display:flex; gap:0.5rem; flex-wrap:wrap; margin-bottom:1rem;">
      <button onclick="addSlide()">+ Add slide</button>
      <button onclick="removeSlide()" style="background:#ef4444;">− Remove last</button>
      <span id="dynamic-count" style="display:flex;align-items:center;color:var(--demo-muted);font-size:0.85rem;margin-left:0.5rem;">2 slides</span>
    </div>
    <multi-carousel id="dynamic">
      <div class="slide slide-1">Slide 1</div>
      <div class="slide slide-2">Slide 2</div>
    </multi-carousel>
    <div class="code-block">const carousel = document.querySelector('multi-carousel');

// Add a slide
const slide = document.createElement('div');
slide.className = 'slide';
slide.textContent = 'New Slide';
carousel.appendChild(slide);

// Remove last slide
carousel.removeChild(carousel.lastElementChild);</div>
  </div>

  <!-- Custom Styling -->
  <div class="demo-section">
    <h2>Custom Styling</h2>
    <p>Customize via CSS custom properties.</p>
    <multi-carousel style="
      --carousel-height: 200px;
      --carousel-bg: #1e293b;
      --carousel-nav-active: #ec4899;
      --carousel-arrow-bg: rgba(30,41,59,0.8);
      --carousel-arrow-color: white;
    ">
      <div class="slide slide-4">Styled 1</div>
      <div class="slide slide-1">Styled 2</div>
      <div class="slide slide-2">Styled 3</div>
    </multi-carousel>
    <div class="code-block">&lt;style&gt;
  .pink-carousel {
    --carousel-height: 200px;
    --carousel-bg: #1e293b;
    --carousel-nav-active: #ec4899;
    --carousel-arrow-bg: rgba(30,41,59,0.8);
    --carousel-arrow-color: white;
  }
&lt;/style&gt;

&lt;multi-carousel class="pink-carousel"&gt;
  ...
&lt;/multi-carousel&gt;</div>
  </div>

  <!-- Synced Carousels -->
  <div class="demo-section">
    <h2>Synced Carousels</h2>
    <p>Multiple carousels that stay in sync. The top one is the master.</p>
    <multi-carousel id="syncMaster" master style="--carousel-height: 200px;">
      <div class="slide slide-1">Master 1</div>
      <div class="slide slide-2">Master 2</div>
      <div class="slide slide-3">Master 3</div>
    </multi-carousel>
    <multi-carousel master-id="syncMaster" hide-arrows hide-nav style="--carousel-height: 120px; margin-top: 0.5rem; --carousel-bg: #0f172a;">
      <div class="slide slide-3">Synced 1</div>
      <div class="slide slide-1">Synced 2</div>
      <div class="slide slide-2">Synced 3</div>
    </multi-carousel>
    <div class="code-block">&lt;multi-carousel id="master" master&gt;
  &lt;div class="slide"&gt;Master 1&lt;/div&gt;
  &lt;div class="slide"&gt;Master 2&lt;/div&gt;
  &lt;div class="slide"&gt;Master 3&lt;/div&gt;
&lt;/multi-carousel&gt;

&lt;!-- Follows the master, no controls --&gt;
&lt;multi-carousel master-id="master" hide-arrows hide-nav&gt;
  &lt;div class="slide"&gt;Detail 1&lt;/div&gt;
  &lt;div class="slide"&gt;Detail 2&lt;/div&gt;
  &lt;div class="slide"&gt;Detail 3&lt;/div&gt;
&lt;/multi-carousel&gt;</div>
  </div>
CSS (css)
:root {
  --bg: #0c0f14;
  --bg-elevated: #141923;
  --bg-panel: #171d28;
  --border: #262f3f;
  --text: #f4f6fb;
  --text-muted: #a7b0c2;
  --text-dim: #7d879b;
  --accent: #ff8a3d;
  --accent-strong: #ff6a00;
  --accent-soft: rgba(255, 138, 61, 0.16);
  --shadow: 0 20px 50px rgba(5, 8, 14, 0.45);
  --radius-lg: 22px;
  --radius-md: 14px;
  --radius-sm: 10px;
  --max-width: 1160px;
}

:root {
      --demo-bg: #f5f5f7;
      --demo-card-bg: #ffffff;
      --demo-text: #1d1d1f;
      --demo-muted: #86868b;
      --demo-border: #d2d2d7;
      --demo-code-bg: #1d1d1f;
      --demo-code-text: #f5f5f7;
      --demo-log-bg: #0a1210;
      --demo-log-text: #86efac;
      --demo-log-border: #1a3a2a;
    }
    :root.dark {
      --demo-bg: #0f1117;
      --demo-card-bg: #1c2233;
      --demo-text: #f3f6ff;
      --demo-muted: #b8c0d9;
      --demo-border: #2b3247;
      --demo-code-bg: #0b0f1a;
      --demo-code-text: #d6d9e6;
    }
    h1 { color: var(--demo-text); }
    .demo-section {
      background: var(--demo-card-bg);
      padding: 2rem;
      border-radius: 12px;
      margin-bottom: 2rem;
      box-shadow: 0 2px 8px rgba(0,0,0,0.1);
    }
    h2 { margin-top: 0; color: var(--demo-text); }
    p { color: var(--demo-muted); line-height: 1.6; }
    a { color: #3b82f6; }
    .code-block {
      background: var(--demo-code-bg);
      color: var(--demo-code-text);
      padding: 1rem;
      border-radius: 8px;
      font-family: 'JetBrains Mono', monospace;
      font-size: 0.8rem;
      margin-top: 1rem;
      overflow: auto;
      max-height: 300px;
      white-space: pre;
      line-height: 1.6;
    }
    .event-log {
      background: var(--demo-log-bg);
      border: 1px solid var(--demo-log-border);
      border-radius: 8px;
      padding: 0.75rem 1rem;
      margin-top: 1rem;
      font-family: 'JetBrains Mono', monospace;
      font-size: 0.8rem;
      color: var(--demo-log-text);
      min-height: 2.5rem;
      max-height: 120px;
      overflow-y: auto;
      line-height: 1.5;
    }
    .event-log::before {
      content: '▸ events';
      display: block;
      font-size: 0.7rem;
      color: var(--demo-muted);
      margin-bottom: 0.25rem;
      text-transform: uppercase;
      letter-spacing: 0.05em;
    }
    .slide {
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 2rem;
      font-weight: bold;
      color: white;
      text-shadow: 0 2px 4px rgba(0,0,0,0.2);
    }
    .slide-1 { background: linear-gradient(135deg, #3b82f6, #8b5cf6); }
    .slide-2 { background: linear-gradient(135deg, #22c55e, #14b8a6); }
    .slide-3 { background: linear-gradient(135deg, #f59e0b, #ef4444); }
    .slide-4 { background: linear-gradient(135deg, #ec4899, #8b5cf6); }
    multi-carousel { --carousel-height: 250px; }
    footer {
      text-align: center;
      margin-top: 3rem;
      padding-top: 2rem;
      border-top: 1px solid var(--demo-border);
      color: var(--demo-muted);
      font-size: 0.9rem;
    }
    footer a { color: var(--demo-text); text-decoration: none; }
    footer a:hover { text-decoration: underline; }
  


@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;600;700&display=swap');

:root {
  --bg: #f5f5f7;
  --bg-2: #ffffff;
  --bg-spot-1: #f8e9d0;
  --bg-spot-2: #e8eef8;
  --card: #ffffff;
  --text: #1d1d1f;
  --muted: #6b7280;
  --line: #e5e7eb;
  --accent: #ffb000;
  --accent-2: #00a7d6;
  --accent-soft: rgba(255, 176, 0, 0.18);
  --surface-1: #f3f4f6;
  --surface-2: #eef2f7;
  --code-bg: #111827;
  --code-text: #f9fafb;
  --panel-bg: rgba(255, 255, 255, 0.85);
  --overlay-bg: rgba(255, 255, 255, 0.98);
}

:root.dark {
  --bg: #0f1117;
  --bg-2: #151a26;
  --bg-spot-1: #1a2136;
  --bg-spot-2: #1d1b34;
  --card: #1c2233;
  --text: #f3f6ff;
  --muted: #b8c0d9;
  --line: #2b3247;
  --accent: #ffb000;
  --accent-2: #00d0ff;
  --accent-soft: rgba(255, 176, 0, 0.25);
  --surface-1: #0b0f1a;
  --surface-2: #263046;
  --code-bg: #0b0f1a;
  --code-text: #d6d9e6;
  --panel-bg: rgba(28, 34, 51, 0.8);
  --overlay-bg: rgba(15, 17, 23, 0.98);
}

* {
  box-sizing: border-box;
}

a {
  color: var(--accent);
}

h1 {
  color: var(--accent);
}

.demo-links {
  margin-top: 12px;
  display: flex;
  gap: 12px;
  justify-content: center;
  flex-wrap: wrap;
}

.demo-links a {
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 6px 12px;
  color: var(--muted);
  text-decoration: none;
  font-size: 0.85rem;
  transition: border-color 0.2s ease, color 0.2s ease;
}

.demo-links a:hover {
  color: var(--text);
  border-color: var(--accent-2);
}

.demo-theme-toggle {
  margin-top: 12px;
  display: flex;
  justify-content: center;
}

header {
  border-bottom: 1px solid var(--line);
}

.demo-card,
.section,
.demo-section,
.panel,
.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 16px;
  color: var(--text);
  box-shadow: 0 18px 36px rgba(6, 10, 24, 0.45);
}

.demo-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.demo-card .code-block {
  margin-top: auto;
}

.demo-grid,
.grid {
  align-items: stretch;
}

.demo-card h2,
.demo-card h3,
.section h2,
.demo-section h2,
.panel-header {
  color: var(--text);
}

.label,
.stat-label,
.category-title,
.subtitle,
.hint,
.note {
  color: var(--muted);
}

.info-item,
.capability,
.preference,
.option-group,
.output,
.current-url,
.event-log,
.result-card,
.log,
.stat {
  background: var(--surface-1);
  border: 1px solid var(--line);
  color: var(--text);
}

.info-item .label,
.capability .name {
  color: var(--muted);
}

.panel-header,
.options,
.topbar,
.top-links {
  background: var(--panel-bg);
  border-bottom: 1px solid var(--line);
  color: var(--text);
}

.subtitle,
.hint,
.note,
.demo-card p,
.section p,
.demo-section p {
  color: var(--muted);
}

.code-block,
pre,
code {
  background: var(--code-bg);
  color: var(--code-text);
  border-radius: 8px;
}

.value-display,
.output,
.result,
.demo-output {
  background: var(--surface-1);
  border: 1px solid var(--line);
  color: var(--text);
}

button {
  background: linear-gradient(120deg, var(--accent), #ff6a00);
  color: #111;
  border: none;
}

button:hover {
  filter: brightness(1.05);
}

input,
select,
textarea {
  background: var(--surface-1);
  color: var(--text);
  border: 1px solid var(--line);
}

footer {
  color: var(--muted);
}

footer a {
  color: var(--text);
}

arc-slider {
  --arc-slider-text-color: var(--text);
  --arc-slider-value-bg: var(--surface-1);
  --arc-slider-value-border: var(--line);
  --arc-slider-value-shadow: 0 6px 16px rgba(0, 0, 0, 0.35);
}

rich-select {
  --caller-background: var(--card);
  --caller-color: var(--text);
  --caller-border: 1px solid var(--line);
  --caller-hover-background: var(--surface-2);
  --caller-hover-color: var(--text);
  --caller-hover-border-color: var(--line);
  --caller-focus-border-color: var(--accent-2);
  --caller-focus-shadow: 0 0 0 3px rgba(0, 208, 255, 0.25);
  --caller-disabled-background: var(--surface-1);
  --caller-disabled-color: #6b7280;
  --caller-disabled-border-color: var(--line);
  --arrow-color: var(--muted);
  --selectOptions-background: var(--card);
  --selectOptions-border: 1px solid var(--line);
  --selectOptions-shadow: 0 10px 22px rgba(0, 0, 0, 0.45);
  --input-background: var(--surface-1);
  --input-border: 1px solid var(--line);
  --input-color: var(--text);
  --input-placeholder-color: #6b7280;
  --option-color: var(--text);
  --option-hover-background: var(--surface-2);
  --option-hover-color: var(--text);
  --option-active-background: var(--accent);
  --option-active-color: #111;
  --option-selected-background: var(--accent-soft);
  --option-selected-color: var(--text);
  --option-disabled-background: var(--surface-1);
  --option-disabled-color: #6b7280;
}

multi-select {
  --multi-select-bg: var(--card);
  --multi-select-border-color: var(--line);
  --multi-select-border-hover: var(--line);
  --multi-select-border-focus: var(--accent-2);
  --multi-select-text-color: var(--text);
  --multi-select-placeholder-color: #6b7280;
  --multi-select-arrow-color: var(--muted);
  --multi-select-dropdown-bg: var(--card);
  --multi-select-shadow: 0 10px 22px rgba(0, 0, 0, 0.45);
  --multi-select-option-hover-bg: var(--surface-2);
  --multi-select-option-selected-bg: var(--accent-soft);
}

tab-nav {
  --tab-bg: var(--card);
  --tab-border: var(--line);
  --tab-text: var(--muted);
  --tab-active-text: var(--text);
  --tab-hover-bg: var(--surface-2);
  --tab-active-border: var(--accent);
  --tab-disabled: #9ca3af;
}

slider-underline {
  --slider-track: var(--surface-2);
  --slider-fill: var(--accent);
  --slider-thumb: var(--accent);
  --slider-label-color: var(--text);
  --slider-tick-color: #9ca3af;
  --slider-tick-value-color: var(--muted);
}

header-nav {
  --header-bg: var(--card);
  --header-shadow: 0 12px 24px rgba(0, 0, 0, 0.2);
  --header-link-color: var(--text);
  --header-link-hover: var(--accent);
  --header-mobile-hover-bg: var(--surface-2);
}

calendar-inline {
  --calendar-bg: var(--card);
  --calendar-shadow: 0 12px 24px rgba(0, 0, 0, 0.2);
  --calendar-text: var(--text);
  --calendar-accent: var(--accent);
  --calendar-today: var(--accent-soft);
  --calendar-selected: var(--accent);
  --calendar-hover-bg: var(--surface-2);
  --calendar-muted: var(--muted);
  --calendar-muted-strong: #9ca3af;
  --calendar-other-month: #9ca3af;
  --calendar-disabled: #cbd5e1;
  --calendar-holiday: #ef4444;
  --calendar-holiday-selected: #111;
}

marked-calendar {
  --calendar-bg: var(--card);
  --calendar-border: var(--line);
  --calendar-title: var(--text);
  --calendar-muted: var(--muted);
  --calendar-surface: var(--surface-1);
  --calendar-accent: var(--accent);
  --calendar-accent-hover: #ff7a1a;
  --calendar-border-strong: var(--line);
  --calendar-contrast: #111;
  --calendar-nav-bg: var(--surface-1);
  --calendar-nav-hover: var(--surface-2);
}

radar-chart {
  --radar-bg: var(--card);
  --radar-grid-color: var(--line);
  --radar-axis-color: #94a3b8;
  --radar-label-color: var(--muted);
}

multi-carousel {
  --carousel-bg: var(--card);
  --carousel-arrow-bg: var(--surface-1);
  --carousel-arrow-color: var(--text);
  --carousel-arrow-hover-bg: var(--surface-2);
  --carousel-arrow-hover-color: var(--text);
  --carousel-nav-bg: var(--surface-1);
  --carousel-nav-color: var(--muted);
  --carousel-nav-hover: #9ca3af;
  --carousel-nav-active: var(--accent);
  --carousel-focus-color: var(--accent-2);
}

nav-list {
  --nav-list-bg: var(--card);
  --nav-list-border-color: var(--line);
  --nav-list-selected-border-color: var(--accent);
  --nav-list-selected-bg: var(--surface-2);
  --nav-list-hover-bg: var(--surface-2);
  --nav-list-selected-color: var(--text);
}

theme-toggle {
  --theme-toggle-bg: var(--card);
  --theme-toggle-icon-color: var(--muted);
  --theme-toggle-hover-bg: var(--surface-2);
  --theme-toggle-active-bg: var(--surface-1);
  --theme-toggle-active-color: var(--text);
  --theme-toggle-dark-bg: var(--card);
  --theme-toggle-dark-border: var(--line);
  --theme-toggle-dark-icon-color: var(--muted);
  --theme-toggle-dark-active-bg: var(--surface-1);
  --theme-toggle-dark-active-color: var(--text);
  --theme-toggle-dark-hover-bg: var(--surface-2);
}

qr-code {
  --qr-fg: #0f1117;
  --qr-bg: #f3f6ff;
}

click-clock {
  --clock-color: var(--text);
  --clock-bg: var(--card);
  --clock-muted-color: var(--muted);
}

historical-line {
  --title-color: var(--text);
  --border-color: var(--line);
  --year-bg: var(--surface-1);
}

circle-steps {
  --steps-muted: var(--muted);
  --steps-text: var(--text);
  --steps-pending: var(--surface-2);
}

rich-inputfile {
  --input-border: var(--line);
  --input-border-focus: var(--accent-2);
  --input-bg: var(--card);
  --input-label-color: var(--text);
  --input-hover-bg: var(--surface-2);
  --input-drag-bg: var(--accent-soft);
  --input-disabled-bg: var(--surface-1);
  --input-success-border: #22c55e;
  --input-success-bg: rgba(34, 197, 94, 0.12);
  --input-icon-color: #94a3b8;
  --input-text-color: var(--muted);
  --input-accent-color: var(--accent-2);
  --input-file-bg: var(--surface-1);
  --input-preview-bg: var(--surface-1);
  --input-file-name-color: var(--text);
  --input-file-size-color: var(--muted);
  --input-error-color: #ef4444;
  --input-hint-color: var(--muted);
}

data-card {
  --data-card-bg: var(--card);
  --data-card-border-color: var(--line);
  --data-card-title-color: var(--text);
  --data-card-desc-color: var(--muted);
  --data-card-info-bg: var(--overlay-bg);
  --data-card-info-close-bg: var(--surface-2);
  --data-card-info-close-color: var(--text);
  --data-card-info-close-hover-bg: var(--surface-1);
  --data-card-info-text: var(--text);
  --data-card-loading-color: var(--muted);
  --data-card-info-trigger-hover: var(--accent);
}

app-modal {
  --app-modal-bg: var(--card);
  --app-modal-body-color: var(--text);
  --app-modal-standalone-bg: rgba(255, 176, 0, 0.35);
  --app-modal-standalone-color: #111;
  --app-modal-standalone-hover-bg: rgba(255, 176, 0, 0.6);
}
JS (js)
document.querySelectorAll('.footer-year').forEach(el => el.textContent = new Date().getFullYear());
  

    import "https://esm.sh/@manufosela/multi-carousel";

    // Dynamic slides
    const gradients = [
      'linear-gradient(135deg, #3b82f6, #8b5cf6)',
      'linear-gradient(135deg, #22c55e, #14b8a6)',
      'linear-gradient(135deg, #f59e0b, #ef4444)',
      'linear-gradient(135deg, #ec4899, #8b5cf6)',
      'linear-gradient(135deg, #06b6d4, #3b82f6)',
      'linear-gradient(135deg, #a855f7, #ec4899)',
    ];
    let slideCounter = 2;
    const dynamicCarousel = document.getElementById('dynamic');
    const countLabel = document.getElementById('dynamic-count');

    const updateCount = () => {
      const n = dynamicCarousel.querySelectorAll('.slide').length;
      countLabel.textContent = `${n} slide${n !== 1 ? 's' : ''}`;
    };

    window.addSlide = () => {
      slideCounter++;
      const slide = document.createElement('div');
      slide.className = 'slide';
      slide.style.background = gradients[(slideCounter - 1) % gradients.length];
      slide.textContent = `Slide ${slideCounter}`;
      dynamicCarousel.appendChild(slide);
      // Navigate to the new slide
      dynamicCarousel.updateComplete.then(() => {
        dynamicCarousel.goTo(dynamicCarousel._slideCount - 1);
      });
      updateCount();
    };

    window.removeSlide = () => {
      const slides = dynamicCarousel.querySelectorAll('.slide');
      if (slides.length > 1) {
        dynamicCarousel.removeChild(slides[slides.length - 1]);
        updateCount();
      }
    };

    // Basic Usage event log
    const basic = document.getElementById('basic');
    const basicLog = document.getElementById('basic-log');

    basic.addEventListener('slide-change', (e) => {
      const line = document.createElement('div');
      line.textContent = `slide-change → index: ${e.detail.index}, total: ${e.detail.total}`;
      basicLog.prepend(line);
      while (basicLog.children.length > 6) basicLog.lastElementChild.remove();
    });
  

    import '../../theme-toggle/src/theme-toggle.js';

    const root = document.documentElement;
    const toggle = document.querySelector('theme-toggle');
    if (toggle) {
      toggle.theme = root.classList.contains('dark') ? 'dark' : 'light';
      toggle.addEventListener('theme-changed', (event) => {
        const theme = event.detail?.theme;
        if (!theme) return;
        root.classList.toggle('dark', theme === 'dark');
      });
    }