Components / structure
Carousel
Its keep-or-cut decision is on the v3 plan for week nine, alongside Feed, and it has not been made.
Summary
A rotating set of slides.
| APG pattern | Carousel |
| Built on | native elements |
| Status | stable |
| Since | 0.1.0 |
<Carousel label="Featured work" autoRotate slides={…} />
Anatomy
A data-se="carousel" region with a viewport, slides, controls and dots.
| Part | Element | Required |
|---|---|---|
controls | <div> | yes |
dot | <button> | yes |
dots | <div> | yes |
label | <span> | yes |
slide | <div> | yes |
viewport | <div> | yes |
Properties
| Property | Values | Default | What it does |
|---|---|---|---|
slides | CarouselSlide[] | required | The slides, in rotation order. Each needs a label — it names the slide in the dots and is the only text a reader gets before choosing to go there. |
label | string | required | Names the carousel region. Required in practice: an unnamed region is one a screen reader can enter and cannot describe. |
autoRotate | boolean | false | Adds a Pause control. Rotation always stops on hover and on focus. |
interval | number | 5000 | Milliseconds between slides when autoRotate is on. Rotation stops on hover and on keyboard focus anywhere inside, so this is a ceiling rather than a promise. |
States
| State | Expressed as | |
|---|---|---|
| Pressed | :active | |
| Disabled | :disabled | |
| Focused | :focus-visible | |
| Hover | :hover | |
| Current | aria-current |
Behavior
Rotation is pausable by a visible control and stops on hover and on keyboard focus anywhere inside. A carousel that moves while someone is reading it is worse than no carousel.
Accessibility
The controls carry aria-controls pointing at the slide container, which needed an id it did not have until the upstream port found it.
Each slide is named and counted, so a reader knows there are others and how many.
Reduced motion stops the rotation, through the reset rather than a check here.
Keyboard
None of its own. The previous, next and pause controls are ordinary tab stops; Enter or Space activates them.
Roles, states and properties
| Attribute | Set by |
|---|---|
aria-atomic | always false |
aria-controls | set at render |
aria-current | set at render |
aria-disabled | set at render |
aria-label | set at render, always Choose slide to display |
aria-live | set at render |
aria-roledescription | always carousel, always slide |
role | always region, always group |
Focus
Rotation stops on keyboard focus anywhere inside, and on hover. A carousel that moves while someone is reading it is worse than no carousel. Focus is never moved onto a slide as it rotates.
Screen reader
Not recorded as verified. Stage 4 of the readiness ladder — driving each component with VoiceOver and NVDA — is unstarted across the package, and no component should claim otherwise until docs/journal/ records the session that did it.
Contrast and target size
npm run check:contrast audits every color pair this component uses across all four skins, light and dark, and fails the build on a shortfall. Two waivers are on the record in docs/audit/DEFERRED.md.
It takes the touch floor under (pointer: coarse) — the visual size is unchanged on a desktop pointer.
Tested
test/apg/batch-a.test.jsxtest/apg/batch-d.test.jsxtest/apg/partials-sweep.test.jsxtest/browser/upstream-natives.spec.mjs
Known gaps
- 18 upstream refs — the tablist variant is the tabs pattern
Appearance
| Token | Used for |
|---|---|
--se-accent | background, intent |
--se-bg | background, border-color |
--se-border | border, border-color |
--se-border-strong | background |
--se-border-width | border, border-width |
--se-control-h | block-size, inline-size |
--se-control-h-lg | block-size, inline-size |
--se-control-h-sm | block-size, inline-size |
--se-duration-instant | transition |
--se-ease-standard | transition |
--se-focus-color | outline |
--se-focus-offset | outline-offset |
--se-focus-width | outline |
--se-mix-down | background, border-color |
--se-mix-hover | background, border-color |
--se-mix-line | border-color |
--se-mix-press | background |
--se-mix-shade | background, border-color |
--se-mix-soft | background |
--se-negative | intent |
--se-positive | intent |
--se-radius-lg | border-radius |
--se-radius-pill | border-radius |
--se-space-1 | gap |
--se-space-2 | block-size, gap, inline-size, padding |
--se-space-3 | gap, padding-inline |
--se-space-4 | padding-inline |
--se-space-5 | padding, padding-inline |
--se-surface-sunk | background |
--se-target-min | block-size, inline-size, min-block-size, min-inline-size, padding |
--se-text | intent |
--se-text-disabled | color |
--se-text-md | font-size |
--se-text-on-intent | color |
--se-text-xs | font-size |
Motion
Slides cross over --se-duration-medium. The dots do not animate.
Content
Each slide's label names it for the dots and for a screen reader — it is the only text a reader gets before choosing to go there.
Usage
Ask whether you need one first. Most carousels exist because nobody would decide what matters most, and readers overwhelmingly see only the first slide. A stack usually serves them better, and a data-se-layout="grid" of cards serves them better still.
The baseline survey rates it high cost, low value, hard accessibility and recommends deferring indefinitely. It is built here, which is a fact about the batch order rather than a recommendation.
Notes
One of four patterns with no upstream test suite, so nothing external will catch a regression.
Its keep-or-cut decision is on the v3 plan for week nine, alongside Feed, and it has not been made.