feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< script lang = "ts" >
import {
Carousel,
type CarouselSize,
type CarouselVariant,
type CarouselColor
} from '$uix/eidos/components/carousel';
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
import type { CarouselAlign , CarouselProviderSnippetProps } from '$soma/components/carousel';
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
import { compileMorfo } from '$uix/morfo';
import { carouselMorfo } from '@/uix/morfo/components/carousel';
import { getActiveUix } from '$active-uix';
const uix = getActiveUix();
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'a11y';
let tab = $state< Tab > ('live');
// ── Live state ────────────────────────────────────────────────────────
let value = $state(0);
let orientation = $state< 'horizontal' | 'vertical'>('horizontal');
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
// Layout
let slidesPerView = $state(1);
let slidesToScroll = $state(1);
let align = $state< CarouselAlign > ('start');
let gap = $state(0); // in rem; converted to `${ gap } rem` when forwarded
let verticalBlockSize = $state(16); // rem
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
let loop = $state(false);
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
// Drag
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
let dragEnabled = $state(true);
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
let dragThreshold = $state(0.25);
// Autoplay
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
let autoplay = $state(false);
let autoplayDelay = $state(4000);
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
let playOnInit = $state(true);
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
let pauseOnHover = $state(true);
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
let stopOnInteraction = $state(false);
let stopOnFocusIn = $state(true);
let stopOnMouseEnter = $state(false);
let stopOnLastSnap = $state(false);
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
let disabled = $state(false);
// `dir` controls keyboard arrow semantics (RTL swaps next/prev) and
// flips the translation sign so horizontal advance follows reading
// direction. `locale` is forwarded for downstream lang-aware behavior
// (announcer text, future label formatting).
type Direction = 'ltr' | 'rtl';
let dir = $state< Direction > ('ltr');
let locale = $state('en-US');
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
let size = $state< CarouselSize > ('md');
let variant = $state< CarouselVariant > ('surface');
let color = $state< CarouselColor > ('primary');
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
// Imperative-API demo: store the handle exposed via the snippet so the
// footer buttons + keyboard shortcut can drive the carousel.
let api = $state< CarouselProviderSnippetProps [ ' api ' ] | null > (null);
let isPlaying = $state(false);
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
type TraceEntry = { event : string ; family : string ; intent? : string ; at : number } ;
let trace = $state< TraceEntry [ ] > ([]);
let stageRef = $state< HTMLElement | null > (null);
$effect(() => {
const el = stageRef;
if (!el) return;
const obs = new MutationObserver((mutations) => {
for (const m of mutations) {
if (m.attributeName !== 'data-event') continue;
const target = m.target as Element;
const ev = target.getAttribute('data-event');
if (!ev) continue;
trace = [
{
event: ev,
family: target.getAttribute('data-event-family') ?? '—',
intent: target.getAttribute('data-event-intent') ?? undefined,
at: Date.now()
},
...trace
].slice(0, 6);
}
});
obs.observe(el, { attributes : true , subtree : true , attributeFilter : [ 'data-event' ] } );
return () => obs.disconnect();
});
const compiled = compileMorfo(carouselMorfo);
const partsList = $derived([...compiled.parts.byKebab.values()]);
const events = $derived([...compiled.actions.byName.values()]);
function fmtTime(at: number): string {
const d = new Date(at);
return `${ String ( d . getSeconds ()). padStart ( 2 , '0' )} .${ String ( d . getMilliseconds ()). padStart ( 3 , '0' )} `;
}
const slides = [
{ tone : 'var(--color-primary-element)' , label : 'Aurora · primary element' , sub : 'slide 1 of 4' } ,
{ tone : 'var(--color-affirm-element)' , label : 'Meadow · affirm element' , sub : 'slide 2 of 4' } ,
{ tone : 'var(--color-risk-element)' , label : 'Ember · risk element' , sub : 'slide 3 of 4' } ,
{ tone : 'var(--color-fulfill-element)' , label : 'Tide · fulfill element' , sub : 'slide 4 of 4' }
];
const eidosSnippet = $derived(
[
"< script lang = 'ts' > " ,
" import { Carousel } from '$uix/eidos/components/carousel';",
' let value = $state(0);',
'< /' + 'script>',
'',
'< Carousel ' ,
' bind:value',
orientation !== 'horizontal' && ` orientation="${ orientation } "`,
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
slidesPerView !== 1 && ` slidesPerView={ $ { slidesPerView }} `,
slidesToScroll !== 1 && ` slidesToScroll={ $ { slidesToScroll }} `,
align !== 'start' && ` align="${ align } "`,
gap !== 0 && ` gap="${ gap } rem"`,
orientation === 'vertical' && verticalBlockSize !== 16 && ` verticalBlockSize="${ verticalBlockSize } rem"`,
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
size !== 'md' && ` size="${ size } "`,
variant !== 'surface' && ` variant="${ variant } "`,
color !== 'primary' && ` color="${ color } "`,
loop & & ' loop',
!dragEnabled && ' dragEnabled={ false } ',
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
dragEnabled && dragThreshold !== 0.25 && ` dragThreshold={ $ { dragThreshold }} `,
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
autoplay & & ' autoplay',
autoplay && autoplayDelay !== 4000 && ` autoplayDelay={ $ { autoplayDelay }} `,
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
autoplay && !playOnInit && ' playOnInit={ false } ',
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
autoplay && !pauseOnHover && ' pauseOnHover={ false } ',
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
autoplay & & stopOnInteraction & & ' stopOnInteraction',
autoplay && !stopOnFocusIn && ' stopOnFocusIn={ false } ',
autoplay & & stopOnMouseEnter & & ' stopOnMouseEnter',
autoplay & & stopOnLastSnap & & ' stopOnLastSnap',
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
disabled & & ' disabled',
dir && ` dir="${ dir } "`,
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
'>',
' < Carousel.Viewport > ',
' < Carousel.ItemGroup > ',
' < Carousel.Item index = { 0 } > Slide A </ Carousel . Item > ',
' < Carousel.Item index = { 1 } > Slide B </ Carousel . Item > ',
' < Carousel.Item index = { 2 } > Slide C </ Carousel . Item > ',
' < / Carousel.ItemGroup > ',
' < Carousel.PrevTrigger / > ',
' < Carousel.NextTrigger / > ',
' < / Carousel.Viewport > ',
' < Carousel.IndicatorGroup > ',
' { #each slides as _ , i } ',
' < Carousel.Indicator index = { i } / > ',
' { /each } ',
' < / Carousel.IndicatorGroup > ',
'< / Carousel > '
]
.filter(Boolean)
.join('\n')
);
< / script >
< div data-uix-canvas-inner >
< header >
< div data-uix-eyebrow > Media · Carousel< / div >
< h1 data-uix-page-title > Carousel< / h1 >
< p data-uix-page-lede >
Horizontally or vertically scrollable rail of slides with keyboard,
button, indicator, drag and autoplay navigation. One slide per page.
Pattern follows the WAI-ARIA carousel
pattern with < code > role="region"< / code > + < code > aria-roledescription="carousel"< / code > ;
indicators map 1:1 with slides as a < code > tablist< / code > .
< / p >
< div data-uix-page-meta >
< span data-uix-meta-pill >
< span data-uix-meta-key > parts</ span > { compiled . parts . order . length }
< / span >
< span data-uix-meta-pill >
< span data-uix-meta-key > events</ span > { events . length }
< / span >
< span data-uix-meta-pill >
< span data-uix-meta-key > sizes< / span > 5
< / span >
< span data-uix-meta-pill >
< span data-uix-meta-key > apg< / span >
< a href = { carouselMorfo . apg } target="_blank" rel = "noreferrer" > carousel ↗</ a >
< / span >
< span data-uix-meta-pill >
< span data-uix-meta-key > index</ span > { value }
< / span >
< / div >
< / header >
< div data-uix-stage >
< div data-uix-stage-area bind:this = { stageRef } style="padding: var ( --uix-space-3 ); display: flex ; justify-content: center ;" >
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< div style = "inline-size: min(40rem, 100%);" >
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< Carousel
bind:value
{ orientation }
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
{ slidesPerView }
{ slidesToScroll }
{ align }
gap="{ gap } rem"
verticalBlockSize="{ verticalBlockSize } rem"
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
{ loop }
{ dragEnabled }
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
{ dragThreshold }
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
{ autoplay }
{ autoplayDelay }
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
{ playOnInit }
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
{ pauseOnHover }
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
{ stopOnInteraction }
{ stopOnFocusIn }
{ stopOnMouseEnter }
{ stopOnLastSnap }
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
{ disabled }
{ dir }
{ locale }
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
{ size }
{ variant }
{ color }
>
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
{ # snippet children ( snippet : CarouselProviderSnippetProps )}
{( api = snippet . api , isPlaying = snippet . isPlaying , '' )}
< Carousel.Viewport >
< Carousel.ItemGroup >
{ #each slides as slide , i ( slide . label )}
< Carousel.Item index = { i } >
< div
style="
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: { slide . tone } ;
color: var(--color-content-primary);
padding: var(--space-6) var(--space-4);
border-radius: var(--radius-md);
{ orientation === 'vertical' ? 'block-size: 100%;' : 'min-block-size: 12rem;' }
user-select: none;
"
>
< strong style = "font-size: var(--font-size-lg); margin-block-end: var(--space-2);" > { slide . label } </ strong >
< span style = "font-size: var(--font-size-sm); color: var(--color-content-secondary);" > { slide . sub } </ span >
< / div >
< / Carousel.Item >
{ /each }
< / Carousel.ItemGroup >
< Carousel.PrevTrigger / >
< Carousel.NextTrigger / >
< / Carousel.Viewport >
< Carousel.IndicatorGroup >
{ #each slides as _ , i ( i )}
< Carousel.Indicator index = { i } / >
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
{ /each }
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< / Carousel.IndicatorGroup >
{ /snippet }
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< / Carousel >
< / div >
< / div >
< div data-uix-stage-trace >
< span data-uix-stage-trace-key > trace< / span >
{ #if trace . length === 0 }
< span > arrow keys · drag · click indicators< / span >
{ : else }
{ #each trace . slice ( 0 , 3 ) as entry ( entry . at )}
< span >< span data-uix-stage-trace-event > { entry . event } </ span > · { entry . family }{ entry . intent ? ' · ' + entry . intent : '' } </ span >
< span style = "color: var(--uix-text-faint)" > { fmtTime ( entry . at )} </ span >
{ /each }
{ /if }
< span style = "margin-inline-start: auto;" >
< span data-uix-stage-trace-key > index</ span > { value }
< / span >
< / div >
< / div >
< div data-uix-tabs role = "tablist" >
< button data-uix-tab data-active = { tab === 'live' } onclick= {() => ( tab = 'live' )} > Live</ button >
< button data-uix-tab data-active = { tab === 'api' } onclick= {() => ( tab = 'api' )} >
API < span data-uix-tab-count > 8< / span >
< / button >
< button data-uix-tab data-active = { tab === 'morfo' } onclick= {() => ( tab = 'morfo' )} >
< span data-uix-layer-badge = "morfo" > morfo< / span >
< span data-uix-tab-count > { partsList . length } p · { events . length } e</ span >
< / button >
< button data-uix-tab data-active = { tab === 'sema' } onclick= {() => ( tab = 'sema' )} >
< span data-uix-layer-badge = "sema" > sema< / span >
< span data-uix-tab-count > { events . length } </ span >
< / button >
< button data-uix-tab data-active = { tab === 'recipe' } onclick= {() => ( tab = 'recipe' )} > Recipe</ button >
< button data-uix-tab data-active = { tab === 'a11y' } onclick= {() => ( tab = 'a11y' )} > A11y</ button >
< / div >
{ #if tab === 'live' }
< section data-uix-section >
< h2 data-uix-section-title > Controls< / h2 >
< p data-uix-section-desc >
Soma owns the index, autoplay timer, drag gesture, keyboard nav
and orientation. Eidos adds chrome (variant / size / color) and
renders the prev/next triggers as eidos < code > < Button iconOnly variant="ghost"> < / code > via
the canonical Button consumer pattern.
< / p >
< div data-uix-subsection-head >
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< span data-uix-layer-badge = "soma" > soma< / span > layout
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< / div >
< div data-uix-controls >
< label data-uix-control >
< span data-uix-control-label > orientation< / span >
< span data-uix-chips role = "radiogroup" >
{ #each [ 'horizontal' , 'vertical' ] as o ( o )}
< button data-uix-chip data-active = { orientation === o } onclick= {() => ( orientation = o as 'horizontal' | 'vertical' )} > { o } </ button >
{ /each }
< / span >
< / label >
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< label data-uix-control >
< span data-uix-control-label > slidesPerView< / span >
< span data-uix-chips role = "radiogroup" >
{ #each [ 1 , 2 , 3 , 4 ] as n ( n )}
< button data-uix-chip data-active = { slidesPerView === n } onclick= {() => ( slidesPerView = n )} > { n } </ button >
{ /each }
< / span >
< / label >
< label data-uix-control >
< span data-uix-control-label > slidesToScroll< / span >
< span data-uix-chips role = "radiogroup" >
{ #each [ 1 , 2 , 3 ] as n ( n )}
< button data-uix-chip data-active = { slidesToScroll === n } onclick= {() => ( slidesToScroll = n )} > { n } </ button >
{ /each }
< / span >
< / label >
< label data-uix-control >
< span data-uix-control-label > align< / span >
< span data-uix-chips role = "radiogroup" >
{ #each [ 'start' , 'center' , 'end' ] as a ( a )}
< button data-uix-chip data-active = { align === a } onclick= {() => ( align = a as CarouselAlign )} > { a } </ button >
{ /each }
< / span >
< / label >
< label data-uix-control >
< span data-uix-control-label > gap (rem)< / span >
< input type = "range" min = "0" max = "2" step = "0.25" bind:value = { gap } / >
< span data-uix-control-label-aux > { gap } </ span >
< / label >
< label data-uix-control >
< span data-uix-control-label > verticalBlockSize (rem)< / span >
< input type = "range" min = "8" max = "32" step = "1" bind:value = { verticalBlockSize } disabled= { orientation !== 'vertical' } />
< span data-uix-control-label-aux > { verticalBlockSize } </ span >
< / label >
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< label data-uix-control >
< span data-uix-control-label > loop< / span >
< span data-uix-switch >
< input type = "checkbox" bind:checked = { loop } / >
< span data-uix-switch-label > { loop ? 'on' : 'off' } </ span >
< / span >
< / label >
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< label data-uix-control >
< span data-uix-control-label > disabled< / span >
< span data-uix-switch >
< input type = "checkbox" bind:checked = { disabled } / >
< span data-uix-switch-label > { disabled ? 'on' : 'off' } </ span >
< / span >
< / label >
< / div >
< div data-uix-subsection-head >
< span data-uix-layer-badge = "soma" > soma< / span > drag
< / div >
< div data-uix-controls >
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< label data-uix-control >
< span data-uix-control-label > dragEnabled< / span >
< span data-uix-switch >
< input type = "checkbox" bind:checked = { dragEnabled } / >
< span data-uix-switch-label > { dragEnabled ? 'on' : 'off' } </ span >
< / span >
< / label >
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< label data-uix-control >
< span data-uix-control-label > dragThreshold (0– 1)< / span >
< input type = "range" min = "0.05" max = "0.6" step = "0.05" bind:value = { dragThreshold } disabled= { ! dragEnabled } />
< span data-uix-control-label-aux > { dragThreshold . toFixed ( 2 )} </ span >
< / label >
< / div >
< div data-uix-subsection-head >
< span data-uix-layer-badge = "soma" > soma< / span > autoplay
< / div >
< div data-uix-controls >
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< label data-uix-control >
< span data-uix-control-label > autoplay< / span >
< span data-uix-switch >
< input type = "checkbox" bind:checked = { autoplay } / >
< span data-uix-switch-label > { autoplay ? 'on' : 'off' } </ span >
< / span >
< / label >
< label data-uix-control >
< span data-uix-control-label > autoplayDelay (ms)< / span >
< input type = "range" min = "1000" max = "8000" step = "500" bind:value = { autoplayDelay } disabled= { ! autoplay } />
< span data-uix-control-label-aux > { autoplayDelay } </ span >
< / label >
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< label data-uix-control >
< span data-uix-control-label > playOnInit< / span >
< span data-uix-switch >
< input type = "checkbox" bind:checked = { playOnInit } disabled= { ! autoplay } />
< span data-uix-switch-label > { playOnInit ? 'on' : 'off' } </ span >
< / span >
< / label >
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< label data-uix-control >
< span data-uix-control-label > pauseOnHover< / span >
< span data-uix-switch >
< input type = "checkbox" bind:checked = { pauseOnHover } disabled= { ! autoplay } />
< span data-uix-switch-label > { pauseOnHover ? 'on' : 'off' } </ span >
< / span >
< / label >
< label data-uix-control >
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< span data-uix-control-label > stopOnInteraction< / span >
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< span data-uix-switch >
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< input type = "checkbox" bind:checked = { stopOnInteraction } disabled= { ! autoplay } />
< span data-uix-switch-label > { stopOnInteraction ? 'on' : 'off' } </ span >
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< / span >
< / label >
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< label data-uix-control >
< span data-uix-control-label > stopOnFocusIn< / span >
< span data-uix-switch >
< input type = "checkbox" bind:checked = { stopOnFocusIn } disabled= { ! autoplay } />
< span data-uix-switch-label > { stopOnFocusIn ? 'on' : 'off' } </ span >
< / span >
< / label >
< label data-uix-control >
< span data-uix-control-label > stopOnMouseEnter< / span >
< span data-uix-switch >
< input type = "checkbox" bind:checked = { stopOnMouseEnter } disabled= { ! autoplay } />
< span data-uix-switch-label > { stopOnMouseEnter ? 'on' : 'off' } </ span >
< / span >
< / label >
< label data-uix-control >
< span data-uix-control-label > stopOnLastSnap< / span >
< span data-uix-switch >
< input type = "checkbox" bind:checked = { stopOnLastSnap } disabled= { ! autoplay || loop } />
< span data-uix-switch-label > { stopOnLastSnap ? 'on' : 'off' } </ span >
< / span >
< / label >
< / div >
< div data-uix-subsection-head >
< span data-uix-layer-badge = "soma" > soma< / span > imperative API
< span style = "margin-inline-start: auto; color: var(--color-content-secondary); font-size: var(--font-size-xs);" >
exposed via the < code > api< / code > snippet prop — drive the carousel from external buttons
< / span >
< / div >
< div data-uix-controls >
< button data-uix-chip onclick = {() => api ? . scrollPrev ()} > scrollPrev()</button >
< button data-uix-chip onclick = {() => api ? . scrollNext ()} > scrollNext()</button >
< button data-uix-chip onclick = {() => api ? . scrollTo ( 0 )} > scrollTo(0)</button >
< button data-uix-chip onclick = {() => api ? . scrollTo ( slides . length - 1 )} > scrollTo(last)</button >
< button data-uix-chip onclick = {() => api ? . play ()} disabled= { ! autoplay || isPlaying } > play()</ button >
< button data-uix-chip onclick = {() => api ? . pause ()} disabled= { ! autoplay || ! isPlaying } > pause()</ button >
< button data-uix-chip onclick = {() => api ? . reset ()} disabled= { ! autoplay } > reset()</ button >
< span data-uix-control-label-aux > isPlaying = { isPlaying } </ span >
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< / div >
< div data-uix-subsection-head >
< span data-uix-layer-badge = "soma" > soma< / span > i18n
< / div >
< div data-uix-controls >
< label data-uix-control >
< span data-uix-control-label > locale< / span >
< select bind:value = { locale } >
< option value = "en-US" > en-US< / option >
< option value = "es-ES" > es-ES< / option >
< option value = "fr-FR" > fr-FR< / option >
< / select >
< / label >
< label data-uix-control >
< span data-uix-control-label > direction< / span >
< span data-uix-chips role = "radiogroup" >
{ #each [ 'ltr' , 'rtl' ] as value ( value )}
< button data-uix-chip data-active = { dir === value } onclick= {() => ( dir = value as Direction )} > { value } </ button >
{ /each }
< / span >
< / label >
< / div >
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< div data-uix-subsection-head >
< span data-uix-layer-badge = "eidos" > eidos< / span > visual props
< / div >
< div data-uix-controls >
< label data-uix-control >
< span data-uix-control-label > variant< / span >
< span data-uix-chips role = "radiogroup" >
{ #each [ 'surface' , 'outline' , 'ghost' ] as v ( v )}
< button data-uix-chip data-active = { variant === v } onclick= {() => ( variant = v as CarouselVariant )} > { v } </ button >
{ /each }
< / span >
< / label >
< label data-uix-control >
< span data-uix-control-label > size< / span >
< span data-uix-chips role = "radiogroup" >
{ #each [ 'xs' , 'sm' , 'md' , 'lg' , 'xl' ] as s ( s )}
< button data-uix-chip data-active = { size === s } onclick= {() => ( size = s as CarouselSize )} > { s } </ button >
{ /each }
< / span >
< / label >
< label data-uix-control >
< span data-uix-control-label > color< / span >
< span data-uix-chips role = "radiogroup" >
{ #each [ 'primary' , 'secondary' , 'neutral' , 'affirm' , 'fulfill' , 'risk' , 'threat' , 'loss' ] as c ( c )}
< button data-uix-chip data-active = { color === c } onclick= {() => ( color = c as CarouselColor )} > { c } </ button >
{ /each }
< / span >
< / label >
< / div >
< div data-uix-code >
< div data-uix-code-head >
< span data-uix-layer-badge = "eidos" > eidos< / span >
< span > composition · with bind:value< / span >
< span data-uix-code-lang > svelte< / span >
< / div >
< pre >< code > { eidosSnippet } </ code ></ pre >
< / div >
< / section >
{ /if }
{ #if tab === 'api' }
< section data-uix-section >
< h2 data-uix-section-title > API reference< / h2 >
< p data-uix-section-desc > 8 parts: Provider, Viewport, ItemGroup, Item, PrevTrigger, NextTrigger, IndicatorGroup, Indicator.< / p >
< div data-uix-subsection-head > Provider (Carousel)< / div >
< div data-uix-table-wrap >
< table data-uix-table >
< thead > < tr > < th > Prop< / th > < th > Type< / th > < th > Default< / th > < th > Description< / th > < / tr > < / thead >
< tbody >
< tr > < td class = "name" > value < span data-uix-tag data-kind = "bindable" > bindable< / span > < / td > < td class = "type" > number< / td > < td class = "default" > 0< / td > < td > Current slide index (0-based).< / td > < / tr >
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< tr > < td class = "name" > defaultValue< / td > < td class = "type" > number< / td > < td class = "default" > 0< / td > < td > Initial slide index when uncontrolled.< / td > < / tr >
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< tr > < td class = "name" > onValueChange< / td > < td class = "type" > (i: number) => void< / td > < td class = "default empty" > —< / td > < td > Called on every index change.< / td > < / tr >
< tr > < td class = "name" > orientation< / td > < td class = "type" > 'horizontal' | 'vertical'< / td > < td class = "default" > 'horizontal'< / td > < td > Scroll axis.< / td > < / tr >
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< tr > < td class = "name" > slidesPerView< / td > < td class = "type" > number< / td > < td class = "default" > 1< / td > < td > Slides visible at once (each Item basis = 100% / slidesPerView).< / td > < / tr >
< tr > < td class = "name" > slidesToScroll< / td > < td class = "type" > number< / td > < td class = "default" > 1< / td > < td > Slides advanced per next() / prev().< / td > < / tr >
< tr > < td class = "name" > align< / td > < td class = "type" > 'start' | 'center' | 'end'< / td > < td class = "default" > 'start'< / td > < td > Alignment of active slide(s) within viewport.< / td > < / tr >
< tr > < td class = "name" > gap< / td > < td class = "type" > string (CSS length)< / td > < td class = "default" > '0px'< / td > < td > Inline-axis gap between slides.< / td > < / tr >
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< tr > < td class = "name" > loop< / td > < td class = "type" > boolean< / td > < td class = "default" > false< / td > < td > Wrap at edges (also wraps autoplay).< / td > < / tr >
< tr > < td class = "name" > dragEnabled< / td > < td class = "type" > boolean< / td > < td class = "default" > true< / td > < td > Allow pointer drag to scrub.< / td > < / tr >
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< tr > < td class = "name" > dragThreshold< / td > < td class = "type" > number (0– 1)< / td > < td class = "default" > 0.25< / td > < td > Release fraction past which next slide commits.< / td > < / tr >
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< tr > < td class = "name" > autoplay< / td > < td class = "type" > boolean< / td > < td class = "default" > false< / td > < td > Enable timer-driven advance.< / td > < / tr >
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< tr > < td class = "name" > autoplayDelay< / td > < td class = "type" > number (ms)< / td > < td class = "default" > 4000< / td > < td > Interval in milliseconds.< / td > < / tr >
< tr > < td class = "name" > playOnInit< / td > < td class = "type" > boolean< / td > < td class = "default" > true< / td > < td > Start the autoplay timer on mount.< / td > < / tr >
< tr > < td class = "name" > pauseOnHover< / td > < td class = "type" > boolean< / td > < td class = "default" > true< / td > < td > Pause autoplay while hovered.< / td > < / tr >
< tr > < td class = "name" > stopOnInteraction< / td > < td class = "type" > boolean< / td > < td class = "default" > false< / td > < td > Latch-stop after user clicks prev/next/indicator or drags.< / td > < / tr >
< tr > < td class = "name" > stopOnFocusIn< / td > < td class = "type" > boolean< / td > < td class = "default" > true< / td > < td > Pause while focus is inside root.< / td > < / tr >
< tr > < td class = "name" > stopOnMouseEnter< / td > < td class = "type" > boolean< / td > < td class = "default" > false< / td > < td > Pause while cursor over root (Embla parity).< / td > < / tr >
< tr > < td class = "name" > stopOnLastSnap< / td > < td class = "type" > boolean< / td > < td class = "default" > false< / td > < td > Latch-stop when last snap reached (loop=false only).< / td > < / tr >
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< tr > < td class = "name" > disabled< / td > < td class = "type" > boolean< / td > < td class = "default" > false< / td > < td > Disable all navigation.< / td > < / tr >
< tr > < td class = "name" > aria-label< / td > < td class = "type" > string< / td > < td class = "default empty" > —< / td > < td > Override accessible name (falls back to lang ref).< / td > < / tr >
< tr > < td class = "name" > variant < span data-uix-tag data-kind = "eidos" > eidos< / span > < / td > < td class = "type" > ControlVariant: 'surface' | 'outline' | 'ghost'< / td > < td class = "default" > 'surface'< / td > < td > Container chrome.< / td > < / tr >
< tr > < td class = "name" > size < span data-uix-tag data-kind = "eidos" > eidos< / span > < / td > < td class = "type" > 'xs' | 'sm' | 'md' | 'lg' | 'xl'< / td > < td class = "default" > 'md'< / td > < td > Cascades to indicator + trigger size.< / td > < / tr >
< tr > < td class = "name" > color < span data-uix-tag data-kind = "eidos" > eidos< / span > < / td > < td class = "type" > ColorRole< / td > < td class = "default" > 'primary'< / td > < td > Indicator + focus accent.< / td > < / tr >
< tr > < td class = "name" > block < span data-uix-tag data-kind = "eidos" > eidos< / span > < / td > < td class = "type" > boolean< / td > < td class = "default" > true< / td > < td > Stretch root inline-size to container.< / td > < / tr >
fix(carousel): vertical overflow + close gaps vs embla/ark-ui/mantine
Real reference audit walked the published embla `.d.ts`, Mantine docs,
ark-ui, shadcn-svelte and Swiper. Found three classes of gaps in the
initial port (a) the vertical bug breaking the only non-default
orientation, (b) layout primitives every other lib ships (slidesPerView,
slidesToScroll, align, gap), (c) autoplay granularity (playOnInit,
stopOnInteraction, stopOnFocusIn, stopOnMouseEnter, stopOnLastSnap),
plus an imperative API every consumer needs.
## Vertical bug fix
Recipe now gives the root a default `block-size` in vertical orientation
(`16rem`, overridable via `--_carousel-vertical-block-size` or the new
eidos `verticalBlockSize` prop) and stretches the viewport to fill it.
Without this the viewport collapses to content height and items spill.
## Soma extensions
- `defaultValue` (uncontrolled init)
- `slidesPerView` (1+) — each Item basis = (100% − gap·(n−1)) / n
- `slidesToScroll` (1+) — paged group navigation
- `align: 'start' | 'center' | 'end'` — viewport alignment
- `gap: string` (CSS length) — between-slide spacing on the ItemGroup
- `playOnInit` — start autoplay on mount (default true)
- `stopOnInteraction` — latch-stop after user clicks prev/next/indicator or drags
- `stopOnFocusIn` — pause while focus is inside (default true)
- `stopOnMouseEnter` — pause while cursor over root (Embla parity)
- `stopOnLastSnap` — latch-stop at last snap when loop=false
- Snippet exposes `isPlaying` + an `api` handle with
`scrollNext / scrollPrev / scrollTo / play / pause / reset`
`commit()` now respects `slidesPerView` (last snap = count − spv).
`next()` / `prev()` step by `slidesToScroll`. Translate calc derives
from `slideSize = viewportSize / slidesPerView` so multi-view works.
Item flex-basis adapts to slidesPerView + gap.
## Eidos additions
- `verticalBlockSize` prop — forwarded as CSS custom property for the
vertical recipe override
- Recipe selectors for vertical orientation enforcing the block-size +
viewport stretch
- Style composition so consumers can still pass inline `style`
## Demo
Live tab now has 4 organized sections (layout / drag / autoplay /
imperative API) covering every new prop. The imperative API row drives
the carousel from external buttons via the snippet handle so consumers
see the pattern in action. `verticalBlockSize` slider exercises the
vertical fix. Snippet preview emits every set prop.
## Reference comparison
Following the canon's mandate to compare with references BEFORE
declaring done, the actual feature matrix from embla published `.d.ts`,
Mantine docs, ark-ui, shadcn-svelte, Swiper:
| Feature | embla | mantine | ark | swiper | UIX |
|------------------------|-------|---------|-----|--------|-----|
| Index nav | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ | ✓ |
| Drag | ✓ | ✓ | ✓ | ✓ | ✓ |
| Autoplay | plug | plug | ✓ | ✓ | ✓ |
| Orientation H/V | ✓ | ✓ | ✓ | ✓ | ✓ |
| slidesPerView | indir | ✓ | ✓ | ✓ | ✓ |
| slidesToScroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Align | ✓ | indir | per | ✓ | ✓ |
| Gap | css | ✓ | ✓ | ✓ | ✓ |
| defaultValue | startIndex | ✓ | ✓ | ✓ | ✓ |
| playOnInit | ✓ | indir | ✓ | ✓ | ✓ |
| stopOnInteraction | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnFocusIn | ✓ | ✓ | ✓ | partl | ✓ |
| stopOnMouseEnter | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopOnLastSnap | ✓ | ✓ | ✗ | ✓ | ✓ |
| Imperative API | ✓ | ✓ | ✓ | ✓ | ✓ |
| Indicators / triggers | DIY | ✓ | ✓ | ✓ | ✓ |
| Fade effect | plug | plug | ✗ | ✓ | ✗ deferred |
| Virtual slides | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
| Multirow grid | ✗ | ✗ | ✗ | ✓ | ✗ deferred |
Deferred power-user features (fade, virtual, multirow grid, cube/cards,
parallax) are documented as power-user in the reference audit, not
table-stakes, and would require Embla-class internals to ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< tr > < td class = "name" > verticalBlockSize < span data-uix-tag data-kind = "eidos" > eidos< / span > < / td > < td class = "type" > string (CSS length)< / td > < td class = "default" > '16rem'< / td > < td > Viewport block-size in vertical orientation.< / td > < / tr >
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
< / tbody >
< / table >
< / div >
< div data-uix-subsection-head > Viewport · ItemGroup< / div >
< p data-uix-section-desc style = "margin-top: 0.25rem;" >
< code > Viewport< / code > is the < code > overflow: hidden< / code > clipping
box. < code > ItemGroup< / code > is the translatable strip whose
< code > transform< / code > the provider drives. Both are pass-through —
no eidos-specific props.
< / p >
< div data-uix-subsection-head > Item< / div >
< div data-uix-table-wrap >
< table data-uix-table >
< thead > < tr > < th > Prop< / th > < th > Type< / th > < th > Default< / th > < th > Description< / th > < / tr > < / thead >
< tbody >
< tr > < td class = "name" > index< / td > < td class = "type" > number< / td > < td class = "default empty" > required< / td > < td > 0-based slide index. Used for < code > aria-roledescription< / code > + indicator wiring.< / td > < / tr >
< tr >< td class = "name" > children</ td >< td class = "type" > Snippet< [{ `{ index, isActive }` } ]> </ td >< td class = "default empty" > —</ td >< td > Slide body. Receives active flag for per-slide effects.</ td ></ tr >
< / tbody >
< / table >
< / div >
< div data-uix-subsection-head > PrevTrigger · NextTrigger< / div >
< div data-uix-table-wrap >
< table data-uix-table >
< thead > < tr > < th > Prop< / th > < th > Type< / th > < th > Default< / th > < th > Description< / th > < / tr > < / thead >
< tbody >
< tr > < td class = "name" > variant < span data-uix-tag data-kind = "eidos" > eidos< / span > < / td > < td class = "type" > ControlVariant: 'surface' | 'outline' | 'ghost'< / td > < td class = "default" > 'ghost'< / td > < td > Button variant.< / td > < / tr >
< tr > < td class = "name" > size < span data-uix-tag data-kind = "eidos" > eidos< / span > < / td > < td class = "type" > 'xs' | 'sm' | 'md' | 'lg' | 'xl'< / td > < td class = "default" > cascades from Carousel< / td > < td > Button size override.< / td > < / tr >
< tr > < td class = "name" > rounded < span data-uix-tag data-kind = "eidos" > eidos< / span > < / td > < td class = "type" > ButtonRounded< / td > < td class = "default" > 'full'< / td > < td > Corner radius.< / td > < / tr >
< tr > < td class = "name" > intent < span data-uix-tag data-kind = "eidos" > eidos< / span > < / td > < td class = "type" > Intent< / td > < td class = "default empty" > —< / td > < td > Evaluative intent.< / td > < / tr >
< tr > < td class = "name" > color < span data-uix-tag data-kind = "eidos" > eidos< / span > < / td > < td class = "type" > ColorRole< / td > < td class = "default empty" > —< / td > < td > Hierarchical color (under intent='neutral').< / td > < / tr >
< tr > < td class = "name" > iconOnly < span data-uix-tag data-kind = "eidos" > eidos< / span > < / td > < td class = "type" > boolean< / td > < td class = "default" > true< / td > < td > Square aspect, label sr-only.< / td > < / tr >
< tr > < td class = "name" > icon < span data-uix-tag data-kind = "eidos" > eidos< / span > < / td > < td class = "type" > Snippet< / td > < td class = "default" > chevron< / td > < td > Override default arrow glyph.< / td > < / tr >
< tr > < td class = "name" > child < span data-uix-tag data-kind = "soma" > soma< / span > < / td > < td class = "type" > Snippet< / td > < td class = "default empty" > —< / td > < td > Bypass Button entirely; render custom element.< / td > < / tr >
< / tbody >
< / table >
< / div >
< div data-uix-subsection-head > IndicatorGroup · Indicator< / div >
< div data-uix-table-wrap >
< table data-uix-table >
< thead > < tr > < th > Prop< / th > < th > Type< / th > < th > Default< / th > < th > Description< / th > < / tr > < / thead >
< tbody >
< tr > < td class = "name" > index (Indicator)< / td > < td class = "type" > number< / td > < td class = "default empty" > required< / td > < td > 0-based slide this indicator navigates to.< / td > < / tr >
< tr >< td class = "name" > aria-label (Indicator)</ td >< td class = "type" > string</ td >< td class = "default empty" > —</ td >< td > Override accessible name; falls back to "Go to slide { `{n}` } ".</ td ></ tr >
< tr >< td class = "name" > children (Indicator)</ td >< td class = "type" > Snippet< [{ `{ index, isActive }` } ]> </ td >< td class = "default empty" > —</ td >< td > Custom indicator body (default is a dot).</ td ></ tr >
< / tbody >
< / table >
< / div >
< / section >
{ /if }
{ #if tab === 'morfo' }
< section data-uix-section >
< h2 data-uix-section-title >
< span data-uix-layer-badge = "morfo" > morfo< / span >
· declarative contract
< / h2 >
< p data-uix-section-desc > Source: < code > src/uix/morfo/components/carousel.ts< / code > .< / p >
< div data-uix-table-wrap >
< table data-uix-table >
< thead > < tr > < th > Field< / th > < th > Value< / th > < / tr > < / thead >
< tbody >
< tr >< td class = "name" > name</ td >< td class = "type" > "{ carouselMorfo . name } "</ td ></ tr >
< tr >< td class = "name" > kebab</ td >< td class = "type" > "{ carouselMorfo . kebab } "</ td ></ tr >
< tr >< td class = "name" > apg</ td >< td >< a href = { carouselMorfo . apg } target="_blank" rel = "noreferrer" > { carouselMorfo . apg } </ a ></ td ></ tr >
< tr >< td class = "name" > parts.length</ td >< td class = "default" > { carouselMorfo . parts . length } </ td ></ tr >
< tr >< td class = "name" > events.length</ td >< td class = "default" > { events . length } </ td ></ tr >
< / tbody >
< / table >
< / div >
< div data-uix-subsection-head > Parts< / div >
< div data-uix-table-wrap >
< table data-uix-table >
< thead > < tr > < th > Part< / th > < th > Marker< / th > < th > Element< / th > < th > Role< / th > < th > Archetype< / th > < th > States< / th > < th > Optional< / th > < / tr > < / thead >
< tbody >
{ #each partsList as part ( part . kebab )}
< tr >
< td class = "name" > { part . kebab } </ td >
< td >< code data-uix-part-marker > [{ part . marker } ]</ code ></ td >
< td class = "type" > < { part . defaultElement } > </ td >
< td class = "type" > { part . role ?? '—' } </ td >
< td class = "default" > { part . archetype ?? '—' } </ td >
< td class = "default" > { part . states . length ? part . states . join ( ' | ' ) : '—' } </ td >
< td class = "default" > { part . optional ? 'yes' : 'no' } </ td >
< / tr >
{ /each }
< / tbody >
< / table >
< / div >
< div data-uix-subsection-head > Events declaration< / div >
< div data-uix-table-wrap >
< table data-uix-table >
< thead > < tr > < th > name< / th > < th > family< / th > < th > verb< / th > < th > sequence< / th > < th > intent< / th > < th > target< / th > < / tr > < / thead >
< tbody >
{ #each events as action ( action . name )}
{ @const sem = action . semantic }
{ @const intentDecl = 'intent' in sem ? sem.intent : undefined }
{ @const intentStr = typeof intentDecl === 'string' ? intentDecl : intentDecl ? `fromProp:$ {( intentDecl as { fromProp? : string }). fromProp ?? '?' } ` : '—' }
< tr >
< td class = "name" > { action . name } </ td >
< td class = "type" > { sem . family } </ td >
< td > { sem . verb ?? '—' } </ td >
< td > { sem . sequence ?? 'pre' } </ td >
< td class = "default" > { intentStr } </ td >
< td > { action . target } </ td >
< / tr >
{ /each }
< / tbody >
< / table >
< / div >
< / section >
{ /if }
{ #if tab === 'sema' }
< section data-uix-section id = "sema" >
< h2 data-uix-section-title >
< span data-uix-layer-badge = "sema" > sema< / span >
· events
< / h2 >
< div data-uix-table-wrap >
< table data-uix-table >
< thead > < tr > < th > Name< / th > < th > Family< / th > < th > Verb< / th > < th > Sequence< / th > < th > Intent< / th > < th > Play< / th > < / tr > < / thead >
< tbody >
{ #each events as action ( action . name )}
{ @const intentDecl = 'intent' in action . semantic ? action.semantic.intent : undefined }
{ @const effectiveIntent = typeof intentDecl === 'string' ? intentDecl : undefined }
< tr >
< td class = "name" > { action . name } </ td >
< td class = "type" > { action . semantic . family } </ td >
< td > { action . semantic . verb ?? '—' } </ td >
< td > { action . semantic . sequence ?? 'pre' } </ td >
< td class = "default" > { effectiveIntent ?? '—' } </ td >
< td >
< button
data-uix-play
onclick={() => {
const target = (stageRef?.querySelector('[data-carousel]') ?? stageRef) as HTMLElement | null;
if (!target) return;
void uix.events?.emit({
name: action.name,
family: action.semantic.family,
target,
...(effectiveIntent ? { intent : effectiveIntent } : {} )
});
}}
>▶ play< / button >
< / td >
< / tr >
{ /each }
< / tbody >
< / table >
< / div >
< / section >
{ /if }
{ #if tab === 'recipe' }
< section data-uix-section >
< h2 data-uix-section-title > Eidos recipe< / h2 >
< p data-uix-section-desc > Selectors at < code > src/uix/eidos/components/carousel/carousel.css< / code > .< / p >
< div data-uix-table-wrap >
< table data-uix-table >
< thead > < tr > < th > Selector< / th > < th > Source< / th > < th > What it paints< / th > < / tr > < / thead >
< tbody >
< tr > < td class = "name" > [data-carousel]< / td > < td > < span data-uix-tag data-kind = "eidos" > eidos< / span > < / td > < td > Root — flex column, gap, font.< / td > < / tr >
< tr > < td class = "name" > [data-carousel][data-variant]< / td > < td > < span data-uix-tag data-kind = "eidos" > eidos< / span > < / td > < td > Surface / outline / ghost chrome around the rail.< / td > < / tr >
< tr > < td class = "name" > [data-carousel][data-size]< / td > < td > < span data-uix-tag data-kind = "eidos" > eidos< / span > < / td > < td > Indicator dot diameter + gap.< / td > < / tr >
< tr > < td class = "name" > [data-carousel][data-color]< / td > < td > < span data-uix-tag data-kind = "eidos" > eidos< / span > < / td > < td > Active-indicator accent + focus ring.< / td > < / tr >
< tr > < td class = "name" > [data-carousel-viewport]< / td > < td > < span data-uix-tag data-kind = "soma" > morfo< / span > < / td > < td > Clipping box (overflow hidden, inline soma styles).< / td > < / tr >
< tr > < td class = "name" > [data-carousel-item-group][data-dragging]< / td > < td > < span data-uix-tag data-kind = "soma" > morfo< / span > < / td > < td > Transform transition off while dragging.< / td > < / tr >
< tr > < td class = "name" > [data-carousel-item]< / td > < td > < span data-uix-tag data-kind = "eidos" > eidos< / span > < / td > < td > Slide — 100% basis along axis.< / td > < / tr >
< tr > < td class = "name" > [data-carousel-prev-trigger] · [data-carousel-next-trigger]< / td > < td > < span data-uix-tag data-kind = "eidos" > eidos< / span > < / td > < td > Absolute-positioned over viewport edges; vertical flips to top/bottom.< / td > < / tr >
< tr > < td class = "name" > [data-carousel-indicator][data-state='active']< / td > < td > < span data-uix-tag data-kind = "eidos" > eidos< / span > < / td > < td > Active indicator: accent fill + 2.25× inline-size stretch.< / td > < / tr >
< / tbody >
< / table >
< / div >
< / section >
{ /if }
{ #if tab === 'a11y' }
< section data-uix-section >
< h2 data-uix-section-title > Accessibility< / h2 >
< p data-uix-section-desc >
Follows the APG < a href = { carouselMorfo . apg } target="_blank" rel = "noreferrer" > carousel pattern</ a > .
The root is < code > role="region"< / code > with < code > aria-roledescription="carousel"< / code > +
an accessible name. Each slide is < code > role="group"< / code > with
< code > aria-roledescription="slide"< / code > . Indicators form a
< code > role="tablist"< / code > with < code > role="tab"< / code > children.
< / p >
< div data-uix-subsection-head > Keyboard< / div >
< div data-uix-table-wrap >
< table data-uix-table >
< thead > < tr > < th > Key< / th > < th > Action< / th > < / tr > < / thead >
< tbody >
< tr > < td > < span data-uix-kbd > ArrowRight< / span > / < span data-uix-kbd > ArrowDown< / span > < / td > < td > Next slide.< / td > < / tr >
< tr > < td > < span data-uix-kbd > ArrowLeft< / span > / < span data-uix-kbd > ArrowUp< / span > < / td > < td > Previous slide.< / td > < / tr >
< tr > < td > < span data-uix-kbd > Home< / span > < / td > < td > First slide.< / td > < / tr >
< tr > < td > < span data-uix-kbd > End< / span > < / td > < td > Last slide.< / td > < / tr >
< tr > < td > < span data-uix-kbd > Tab< / span > < / td > < td > Move focus through prev / next / indicators / inner slide controls.< / td > < / tr >
< tr > < td > pointer drag< / td > < td > Scrub between slides; commits when past < code > dragThreshold< / code > .< / td > < / tr >
< / tbody >
< / table >
< / div >
< div data-uix-subsection-head > ARIA contract< / div >
< div data-uix-table-wrap >
< table data-uix-table >
< thead > < tr > < th > Part< / th > < th > Attribute< / th > < th > Value< / th > < / tr > < / thead >
< tbody >
< tr > < td class = "name" > provider< / td > < td > role< / td > < td class = "type" > "region"< / td > < / tr >
< tr > < td class = "name" > provider< / td > < td > aria-roledescription< / td > < td class = "type" > "carousel"< / td > < / tr >
< tr > < td class = "name" > provider< / td > < td > aria-label< / td > < td class = "type" > localized "Carousel"< / td > < / tr >
< tr > < td class = "name" > item< / td > < td > role< / td > < td class = "type" > "group"< / td > < / tr >
< tr > < td class = "name" > item< / td > < td > aria-roledescription< / td > < td class = "type" > "slide"< / td > < / tr >
< tr > < td class = "name" > prev-trigger / next-trigger< / td > < td > type / aria-label< / td > < td class = "type" > "button" / "Previous slide" | "Next slide"< / td > < / tr >
< tr > < td class = "name" > indicator-group< / td > < td > role / aria-orientation< / td > < td class = "type" > "tablist" / orientation< / td > < / tr >
< tr > < td class = "name" > indicator< / td > < td > role / aria-selected< / td > < td class = "type" > "tab" / "true|false"< / td > < / tr >
< / tbody >
< / table >
< / div >
< / section >
{ /if }
< / div >