# Carousel

Use carousel when each slide deserves focused attention and users benefit from comparing a small sequence of items. It supports image slides, custom templates, manual navigation, indicators, looping, and auto-play with hover pause.

## Import
```ts
import { CarouselComponent } from 'ui';
```

## Image slides
```ts
import { Component } from '@angular/core';
import { CarouselComponent, CarouselItem } from 'ui';

@Component({
  selector: 'app-carousel-basic-demo',
  standalone: true,
  imports: [CarouselComponent],
  template: ` <ui-carousel [items]="heroSlides" (itemClick)="(0)" /> `,
})
export class CarouselBasicDemoComponent {
  protected readonly heroSlides: CarouselItem[] = [
    {
      id: 'hero-1',
      image: 'https://picsum.photos/seed/ui-carousel-1/1200/640',
      title: 'Launch seasonal workspace themes',
      description:
        'Use carousel for broad visual storytelling when each slide needs enough room for a headline, supporting copy, and imagery.',
    },
    {
      id: 'hero-2',
      image: 'https://picsum.photos/seed/ui-carousel-2/1200/640',
      title: 'Promote key releases without custom layout',
      description:
        'Slides move one item at a time and keep controls, indicators, and transitions consistent across the product.',
    },
    {
      id: 'hero-3',
      image: 'https://picsum.photos/seed/ui-carousel-3/1200/640',
      title: 'Keep copy short and scannable',
      description:
        'Treat each slide like a focused card, not a scrolling page. Carousel works best when users can compare a small set of highlights.',
    },
  ];
}
```

## Controls, indicators, and loop
```ts
import { Component } from '@angular/core';
import { CarouselComponent, CarouselItem } from 'ui';

@Component({
  selector: 'app-carousel-navigation-demo',
  standalone: true,
  imports: [CarouselComponent],
  template: `
    <div style="display:grid;gap:1rem">
      <div
        style="display:grid;gap:0.75rem;padding:1rem;border:1px solid var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background-rest)"
      >
        <strong style="font-size:0.875rem;color:var(--color-neutral-foreground-rest)"
          >Full navigation</strong
        >
        <ui-carousel [items]="slides" />
      </div>

      <div
        style="display:grid;gap:0.75rem;padding:1rem;border:1px dashed var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background2-rest)"
      >
        <strong style="font-size:0.875rem;color:var(--color-neutral-foreground-rest)"
          >Indicators only, no loop</strong
        >
        <ui-carousel [items]="slides" [showControls]="false" [loop]="false" />
      </div>
    </div>
  `,
})
export class CarouselNavigationDemoComponent {
  protected readonly slides: CarouselItem[] = [
    {
      id: 'nav-1',
      image: 'https://picsum.photos/seed/ui-carousel-nav-1/960/480',
      title: 'Plan',
      description: 'First slide in a bounded sequence.',
    },
    {
      id: 'nav-2',
      image: 'https://picsum.photos/seed/ui-carousel-nav-2/960/480',
      title: 'Build',
      description: 'Second slide keeps the same surface and controls.',
    },
    {
      id: 'nav-3',
      image: 'https://picsum.photos/seed/ui-carousel-nav-3/960/480',
      title: 'Review',
      description: 'Last slide stops when loop is disabled.',
    },
  ];
}
```

## Auto-play behavior
```ts
import { Component } from '@angular/core';
import { CarouselComponent, CarouselItem } from 'ui';

@Component({
  selector: 'app-carousel-autoplay-demo',
  standalone: true,
  imports: [CarouselComponent],
  template: `
    <div style="display:grid;gap:1rem">
      <div
        style="padding:1rem;border:1px solid var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background-rest)"
      >
        <ui-carousel [items]="slides" [autoPlay]="true" [autoPlayInterval]="3500" />
      </div>

      <div
        style="display:flex;flex-wrap:wrap;gap:0.75rem;padding:0.875rem 1rem;border:1px dashed var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background2-rest);font-size:0.8125rem;line-height:1.45;color:var(--color-neutral-foreground3-rest)"
      >
        <span>Auto-play pauses on hover.</span>
        <span>Use it for passive highlights, not for critical reading or forms.</span>
      </div>
    </div>
  `,
})
export class CarouselAutoplayDemoComponent {
  protected readonly slides: CarouselItem[] = [
    {
      id: 'auto-1',
      image: 'https://picsum.photos/seed/ui-carousel-auto-1/1200/640',
      title: 'Passive campaign highlights',
      description: 'Useful for hero banners or dashboards where content can rotate on its own.',
    },
    {
      id: 'auto-2',
      image: 'https://picsum.photos/seed/ui-carousel-auto-2/1200/640',
      title: 'Do not rush the reader',
      description: 'Keep interval generous enough for users to parse text before the next slide.',
    },
    {
      id: 'auto-3',
      image: 'https://picsum.photos/seed/ui-carousel-auto-3/1200/640',
      title: 'Always keep manual controls',
      description:
        'Users should still be able to move backward and forward when something matters.',
    },
  ];
}
```

## Custom slide template
```ts
import { Component } from '@angular/core';
import { BadgeComponent, ButtonComponent, CarouselComponent, CarouselItem } from 'ui';

@Component({
  selector: 'app-carousel-custom-template-demo',
  standalone: true,
  imports: [CarouselComponent, BadgeComponent, ButtonComponent],
  template: `
    <ui-carousel
      [items]="workspaceSlides"
      [slideTemplate]="workspaceTemplate"
      [showIndicators]="true"
    />

    <ng-template #workspaceTemplate let-item>
      <div
        style="display:flex;flex-direction:column;justify-content:space-between;gap:1rem;width:100%;height:100%;padding:1.5rem;background:linear-gradient(180deg, var(--color-neutral-background-rest) 0%, var(--color-neutral-background2-rest) 100%);color:var(--color-neutral-foreground-rest);text-align:left"
      >
        <div style="display:flex;flex-wrap:wrap;gap:0.5rem">
          <ui-badge [text]="item.badge" variant="info" appearance="tint" />
          <ui-badge [text]="item.meta" variant="secondary" appearance="subtle" />
        </div>

        <div style="display:grid;gap:0.75rem;max-width:34rem">
          <h3 style="margin:0;font-size:1.5rem;line-height:1.2">{{ item.title }}</h3>
          <p
            style="margin:0;font-size:0.9375rem;line-height:1.6;color:var(--color-neutral-foreground2-rest)"
          >
            {{ item.description }}
          </p>
        </div>

        <div
          style="display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:0.875rem;padding:1rem;border:1px dashed var(--color-neutral-stroke-rest);border-radius:1rem;background:rgba(255,255,255,0.72)"
        >
          <div style="display:flex;flex-wrap:wrap;gap:0.75rem">
            <div style="display:grid;gap:0.125rem">
              <strong style="font-size:0.875rem">{{ item.metricA }}</strong>
              <span style="font-size:0.75rem;color:var(--color-neutral-foreground3-rest)"
                >open items</span
              >
            </div>
            <div style="display:grid;gap:0.125rem">
              <strong style="font-size:0.875rem">{{ item.metricB }}</strong>
              <span style="font-size:0.75rem;color:var(--color-neutral-foreground3-rest)"
                >contributors</span
              >
            </div>
          </div>

          <ui-button appearance="outline">Open workspace</ui-button>
        </div>
      </div>
    </ng-template>
  `,
})
export class CarouselCustomTemplateDemoComponent {
  protected readonly workspaceSlides: CarouselItem[] = [
    {
      id: 'template-1',
      badge: 'Product updates',
      meta: 'Q2 focus',
      title: 'Roll out the new navigation shell',
      description:
        'Custom slide templates work well when carousel needs richer layouts than an image with caption. Keep each slide structured like a card, not a full page.',
      metricA: '18',
      metricB: '6',
    },
    {
      id: 'template-2',
      badge: 'Design system',
      meta: 'Migration',
      title: 'Track components that still use legacy patterns',
      description:
        'You can embed badges, actions, or metric summaries and still reuse the same carousel navigation behavior.',
      metricA: '9',
      metricB: '4',
    },
    {
      id: 'template-3',
      badge: 'Planning',
      meta: 'Next sprint',
      title: 'Highlight team priorities without building custom chrome',
      description:
        'This pattern is useful for workspace overviews, roadmap highlights, onboarding callouts, or release spotlight sections.',
      metricA: '5',
      metricB: '8',
    },
  ];
}
```

## Workspace highlights
```ts
import { Component } from '@angular/core';
import { BadgeComponent, CarouselComponent, CarouselItem } from 'ui';

@Component({
  selector: 'app-carousel-workspace-highlights-demo',
  standalone: true,
  imports: [CarouselComponent, BadgeComponent],
  template: `
    <div style="display:grid;gap:1rem">
      <div
        style="display:flex;flex-wrap:wrap;gap:0.75rem;padding:0.875rem 1rem;border:1px dashed var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background2-rest)"
      >
        <ui-badge
          text="Use for a small set of high-value slides"
          variant="secondary"
          appearance="tint"
        />
        <ui-badge
          text="Avoid dumping long feeds into a carousel"
          variant="warning"
          appearance="outline"
        />
      </div>

      <ui-carousel [items]="workspaceSlides" [autoPlay]="true" [autoPlayInterval]="4500" />
    </div>
  `,
})
export class CarouselWorkspaceHighlightsDemoComponent {
  protected readonly workspaceSlides: CarouselItem[] = [
    {
      id: 'workspace-1',
      image: 'https://picsum.photos/seed/ui-carousel-workspace-1/1200/640',
      title: 'Workspace highlights',
      description:
        'A carousel can anchor a landing page or team hub when each slide represents one clear destination or story.',
    },
    {
      id: 'workspace-2',
      image: 'https://picsum.photos/seed/ui-carousel-workspace-2/1200/640',
      title: 'Keep the count of slides low',
      description:
        'Three to five slides is usually enough. Beyond that, users stop comparing and start ignoring the surface.',
    },
    {
      id: 'workspace-3',
      image: 'https://picsum.photos/seed/ui-carousel-workspace-3/1200/640',
      title: 'Pair motion with readable pacing',
      description:
        'If auto-play is enabled, keep timing slow and preserve manual navigation so the surface still feels user-controlled.',
    },
  ];
}
```

## Accessibility

### Controls
Previous, next, and indicator affordances are real buttons, so they stay keyboard reachable and expose clear labels such as `Previous slide`, `Next slide`, and `Go to slide N`.

### Auto-play
Auto-play pauses on hover. Use it only for passive content and keep intervals long enough for reading. Avoid using auto-play for critical instructional or form content that users must parse precisely.

### Images and custom slides
Image slides derive `alt` from slide `title`, with a localized fallback when no title is present. For custom templates, make sure meaningful text stays visible inside the slide and do not rely on imagery alone.

| Aspect | Behavior |
| --- | --- |
| controls | native buttons with labels |
| indicators | native buttons with labels per slide |
| image alt | slide `title` or fallback text |
| focus order | controls and indicators are keyboard reachable |
| auto-play | pauses on hover; should remain optional |
