carousel
Use a required content viewport and track around authored slide items. The TypeScript directive initializes the locally bundled Embla engine, synchronizes controls and accessible state, and supports pointer dragging and keyboard navigation.
<section
ng-carousel
align="start"
style="--carousel-item-size: 50%; --carousel-gap: 0.5rem"
>
<div>
<ul>
<li>Slide</li>
</ul>
</div>
</section>
Use orientation="vertical" for vertical movement and dir="rtl" for RTL.
--carousel-item-size controls single or multi-item layouts and
--carousel-gap controls spacing. Add autoplay and optionally
autoplay-delay="2000" for the same locally bundled plugin behavior as the
checked-in reference. The root mirrors snap index, snap count, item count, and
boundary state.
angularcss:carousel-ready and angularcss:carousel-change expose the Embla
API, zero-based snap index, snap count, selected item index, selected item, and
total item count. Bind those events with AngularTS when the application needs a
counter; AngularCSS does not create or replace an AngularTS model.
Example
View source
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>AngularCSS Carousel</title>
<link rel="stylesheet" href="../../css/preflight.css" />
<link rel="stylesheet" href="../../css/angular.css" />
<link rel="stylesheet" href="../example.css" />
<script src="../../js/angular-ts.umd.js"></script>
<script src="../../js/angular-css.umd.js"></script>
</head>
<body ng-app="angular.css" ng-cloak data-example="carousel-demo">
<main class="visual-example">
<section ng-carousel class="carousel-demo" aria-label="Numbered slides">
<div>
<ul>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square">
<span>1</span>
</section>
</article>
</div>
</li>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square">
<span>2</span>
</section>
</article>
</div>
</li>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square">
<span>3</span>
</section>
</article>
</div>
</li>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square">
<span>4</span>
</section>
</article>
</div>
</li>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square">
<span>5</span>
</section>
</article>
</div>
</li>
</ul>
</div>
<button
size="icon-sm"
variant="outline"
type="button"
aria-label="Previous slide"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
aria-hidden="true"
>
<path d="m15 18-6-6 6-6" />
</svg>
</button>
<button
size="icon-sm"
variant="outline"
type="button"
aria-label="Next slide"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
aria-hidden="true"
>
<path d="m9 18 6-6-6-6" />
</svg>
</button>
</section>
</main>
</body>
</html>
Behavior workflows
The following functional page covers API state, multiple visible items, vertical orientation, and autoplay.
View source
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>AngularCSS Carousel Workflows</title>
<link rel="stylesheet" href="../../css/preflight.css" />
<link rel="stylesheet" href="../../css/angular.css" />
<link rel="stylesheet" href="../example.css" />
<script src="../../js/angular-ts.umd.js"></script>
<script src="../../js/angular-css.umd.js"></script>
</head>
<body
ng-app="angular.css"
ng-cloak
ng-init="apiState={index:0,count:5}; autoplayState={index:0,count:5}"
data-example="carousel-api carousel-multiple carousel-orientation carousel-plugin"
>
<main
class="carousel-workflow-grid"
aria-label="Carousel behavior workflows"
>
<section class="carousel-workflow" aria-labelledby="carousel-api-heading">
<h2 id="carousel-api-heading">API</h2>
<section
ng-carousel
class="carousel-api-demo"
aria-label="API numbered slides"
ng-on-angularcss:carousel-ready="apiState=$event.detail"
ng-on-angularcss:carousel-change="apiState=$event.detail"
>
<div>
<ul>
<li>
<article class="card">
<section class="carousel-square">
<span>1</span>
</section>
</article>
</li>
<li>
<article class="card">
<section class="carousel-square">
<span>2</span>
</section>
</article>
</li>
<li>
<article class="card">
<section class="carousel-square">
<span>3</span>
</section>
</article>
</li>
<li>
<article class="card">
<section class="carousel-square">
<span>4</span>
</section>
</article>
</li>
<li>
<article class="card">
<section class="carousel-square">
<span>5</span>
</section>
</article>
</li>
</ul>
</div>
<button
size="icon-sm"
variant="outline"
type="button"
aria-label="Previous API slide"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
aria-hidden="true"
>
<path d="m15 18-6-6 6-6" />
</svg>
</button>
<button
size="icon-sm"
variant="outline"
type="button"
aria-label="Next API slide"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
aria-hidden="true"
>
<path d="m9 18 6-6-6-6" />
</svg>
</button>
</section>
<output class="carousel-status" aria-live="polite">
Slide {{ apiState.index + 1 }} of {{ apiState.count }}
</output>
</section>
<section
class="carousel-workflow"
aria-labelledby="carousel-multiple-heading"
>
<h2 id="carousel-multiple-heading">Multiple items</h2>
<section
ng-carousel
align="start"
class="carousel-multiple-demo"
aria-label="Multiple numbered slides"
>
<div>
<ul>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square carousel-square-medium">
<span>1</span>
</section>
</article>
</div>
</li>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square carousel-square-medium">
<span>2</span>
</section>
</article>
</div>
</li>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square carousel-square-medium">
<span>3</span>
</section>
</article>
</div>
</li>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square carousel-square-medium">
<span>4</span>
</section>
</article>
</div>
</li>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square carousel-square-medium">
<span>5</span>
</section>
</article>
</div>
</li>
</ul>
</div>
<button
size="icon-sm"
variant="outline"
type="button"
aria-label="Previous group"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
aria-hidden="true"
>
<path d="m15 18-6-6 6-6" />
</svg>
</button>
<button
size="icon-sm"
variant="outline"
type="button"
aria-label="Next group"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
aria-hidden="true"
>
<path d="m9 18 6-6-6-6" />
</svg>
</button>
</section>
</section>
<section
class="carousel-workflow"
aria-labelledby="carousel-orientation-heading"
>
<h2 id="carousel-orientation-heading">Vertical orientation</h2>
<section
ng-carousel
orientation="vertical"
align="start"
class="carousel-vertical-demo"
aria-label="Vertical numbered slides"
>
<div>
<ul>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-vertical-card">
<span>1</span>
</section>
</article>
</div>
</li>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-vertical-card">
<span>2</span>
</section>
</article>
</div>
</li>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-vertical-card">
<span>3</span>
</section>
</article>
</div>
</li>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-vertical-card">
<span>4</span>
</section>
</article>
</div>
</li>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-vertical-card">
<span>5</span>
</section>
</article>
</div>
</li>
</ul>
</div>
<button
size="icon-sm"
variant="outline"
type="button"
aria-label="Previous vertical slide"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
aria-hidden="true"
>
<path d="m15 18-6-6 6-6" />
</svg>
</button>
<button
size="icon-sm"
variant="outline"
type="button"
aria-label="Next vertical slide"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
aria-hidden="true"
>
<path d="m9 18 6-6-6-6" />
</svg>
</button>
</section>
</section>
<section
class="carousel-workflow"
aria-labelledby="carousel-plugin-heading"
>
<h2 id="carousel-plugin-heading">Autoplay plugin</h2>
<section
ng-carousel
autoplay
autoplay-delay="2000"
class="carousel-api-demo"
aria-label="Autoplay numbered slides"
ng-on-angularcss:carousel-ready="autoplayState=$event.detail"
ng-on-angularcss:carousel-change="autoplayState=$event.detail"
>
<div>
<ul>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square">
<span>1</span>
</section>
</article>
</div>
</li>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square">
<span>2</span>
</section>
</article>
</div>
</li>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square">
<span>3</span>
</section>
</article>
</div>
</li>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square">
<span>4</span>
</section>
</article>
</div>
</li>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square">
<span>5</span>
</section>
</article>
</div>
</li>
</ul>
</div>
<button
size="icon-sm"
variant="outline"
type="button"
aria-label="Previous autoplay slide"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
aria-hidden="true"
>
<path d="m15 18-6-6 6-6" />
</svg>
</button>
<button
size="icon-sm"
variant="outline"
type="button"
aria-label="Next autoplay slide"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
aria-hidden="true"
>
<path d="m9 18 6-6-6-6" />
</svg>
</button>
</section>
<output class="carousel-status" aria-live="polite">
Autoplay slide {{ autoplayState.index + 1 }} of {{ autoplayState.count
}}
</output>
</section>
</main>
</body>
</html>
Layout workflows
RTL direction, responsive item sizing, and custom spacing remain semantic HTML and authored CSS composition.
View source
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>AngularCSS Carousel Compositions</title>
<link rel="stylesheet" href="../../css/preflight.css" />
<link rel="stylesheet" href="../../css/angular.css" />
<link rel="stylesheet" href="../example.css" />
<script src="../../js/angular-ts.umd.js"></script>
<script src="../../js/angular-css.umd.js"></script>
</head>
<body
ng-app="angular.css"
ng-cloak
data-example="carousel-rtl carousel-size carousel-spacing"
>
<main
class="carousel-composition-grid"
aria-label="Carousel layout compositions"
>
<section
class="carousel-workflow"
aria-labelledby="carousel-rtl-heading"
dir="rtl"
lang="ar"
>
<h2 id="carousel-rtl-heading">من اليمين إلى اليسار</h2>
<section
ng-carousel
dir="rtl"
class="carousel-demo"
aria-label="شرائح مرقمة"
>
<div>
<ul>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square">
<span>١</span>
</section>
</article>
</div>
</li>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square">
<span>٢</span>
</section>
</article>
</div>
</li>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square">
<span>٣</span>
</section>
</article>
</div>
</li>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square">
<span>٤</span>
</section>
</article>
</div>
</li>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square">
<span>٥</span>
</section>
</article>
</div>
</li>
</ul>
</div>
<button
size="icon-sm"
variant="outline"
type="button"
aria-label="الشريحة السابقة"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
aria-hidden="true"
>
<path d="m15 18-6-6 6-6" />
</svg>
</button>
<button
size="icon-sm"
variant="outline"
type="button"
aria-label="الشريحة التالية"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
aria-hidden="true"
>
<path d="m9 18 6-6-6-6" />
</svg>
</button>
</section>
</section>
<section
class="carousel-workflow"
aria-labelledby="carousel-size-heading"
>
<h2 id="carousel-size-heading">Responsive size</h2>
<section
ng-carousel
align="start"
class="carousel-size-demo"
aria-label="Sized numbered slides"
>
<div>
<ul>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square carousel-square-medium">
<span>1</span>
</section>
</article>
</div>
</li>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square carousel-square-medium">
<span>2</span>
</section>
</article>
</div>
</li>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square carousel-square-medium">
<span>3</span>
</section>
</article>
</div>
</li>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square carousel-square-medium">
<span>4</span>
</section>
</article>
</div>
</li>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square carousel-square-medium">
<span>5</span>
</section>
</article>
</div>
</li>
</ul>
</div>
<button
size="icon-sm"
variant="outline"
type="button"
aria-label="Previous sized slide"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
aria-hidden="true"
>
<path d="m15 18-6-6 6-6" />
</svg>
</button>
<button
size="icon-sm"
variant="outline"
type="button"
aria-label="Next sized slide"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
aria-hidden="true"
>
<path d="m9 18 6-6-6-6" />
</svg>
</button>
</section>
</section>
<section
class="carousel-workflow carousel-composition-wide"
aria-labelledby="carousel-spacing-heading"
>
<h2 id="carousel-spacing-heading">Custom spacing</h2>
<section
ng-carousel
align="start"
class="carousel-spacing-demo"
aria-label="Tightly spaced numbered slides"
>
<div>
<ul>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square carousel-square-small">
<span>1</span>
</section>
</article>
</div>
</li>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square carousel-square-small">
<span>2</span>
</section>
</article>
</div>
</li>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square carousel-square-small">
<span>3</span>
</section>
</article>
</div>
</li>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square carousel-square-small">
<span>4</span>
</section>
</article>
</div>
</li>
<li>
<div class="carousel-item-padding">
<article class="card">
<section class="carousel-square carousel-square-small">
<span>5</span>
</section>
</article>
</div>
</li>
</ul>
</div>
<button
size="icon-sm"
variant="outline"
type="button"
aria-label="Previous tightly spaced slide"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
aria-hidden="true"
>
<path d="m15 18-6-6 6-6" />
</svg>
</button>
<button
size="icon-sm"
variant="outline"
type="button"
aria-label="Next tightly spaced slide"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
aria-hidden="true"
>
<path d="m9 18 6-6-6-6" />
</svg>
</button>
</section>
</section>
</main>
</body>
</html>
Installation
Install AngularCSS, load its stylesheet, and include the angular.css module in your AngularTS application. See Installation for the complete setup.
This component’s root directive is [ng-carousel]. Importing the package registers it with the AngularCSS angular.css module; there is no per-component JavaScript registration step.
Anatomy
Directive selectors
ng-carousel
Semantic structure
The content viewport and its direct track child are required. Items must be direct track children. Navigation controls and dots are optional.
API
Attributes and state
| Attribute | Access | Purpose |
|---|---|---|
align | Input | Cross-axis alignment: start, center, or end. |
aria-current | Output | Current item or date state. |
aria-disabled | Output | Semantic disabled state. |
aria-hidden | Output | Whether generated or collapsed content is hidden from assistive technology. |
aria-label | Input/output | Accessible name when visible text is insufficient. |
aria-roledescription | Output | Human-readable description of the component role. |
autoplay | Input | Enables the locally bundled Embla autoplay plugin. |
autoplay-delay | Input | Autoplay delay in milliseconds. |
contain-scroll | Input | Embla scroll containment mode. |
dir | Input | Text and interaction direction: ltr or rtl. |
drag-free | Input | Allows free dragging between snap points. |
draggable | Input | Set to false to disable pointer dragging. |
loop | Input | Allows navigation to wrap from the final item to the first. |
orientation | Input/output | Layout direction: horizontal or vertical. |
role | Input/output | Explicit semantic role when native HTML does not provide one. |
skip-snaps | Input | Allows momentum to skip snap points. |
slides-to-scroll | Input | Number of slides advanced as one snap group. |
tabindex | Input/output | Keyboard focus order for composite descendants. |
Input attributes are read from authored HTML. Output attributes are maintained by AngularCSS for CSS and testing. Input/output attributes may be authored for a controlled initial state and are then synchronized by the directive.
CSS custom properties
| Variable | Purpose |
|---|---|
--carousel-gap | Gap between slides; defaults to four spacing units. |
--carousel-item-size | Slide basis; defaults to 100%. |
DOM events
angularcss:carousel-changeangularcss:carousel-ready
Native DOM events continue to work normally. AngularTS event directives such as
ng-click and ng-keydown, plus the data-change model callback, remain application-owned.
Behavior
The directive uses Embla to own drag gestures, snap selection, orientation, loop boundaries, control availability, and optional autoplay. AngularTS remains responsible for counters, business actions, and other application state consumed from the carousel DOM events.
AngularCSS does not replace AngularTS interpolation, bindings, structural directives, form controllers, validation, or application state.
Accessibility
The root is an accessible carousel region, every authored item is exposed as a labeled slide, and unavailable previous or next controls are disabled. Give the region a useful accessible name and keep authored slide content semantic.
Authored accessible names and relationships are preserved. Test the final composition with keyboard navigation and assistive technology because labels and content come from the application.
Customization
Target [ng-carousel], semantic descendants, component classes, and generated state with ordinary CSS. Keep behavior and accessible state in the TypeScript directive; visual choices belong in the application stylesheet.
Read Customization for layer order, design tokens, state variants, and iframe demo isolation.
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.