Skip to content

feat(Carousel): add new component#190

Merged
johnleider merged 27 commits intomasterfrom
claude/carousel-component-vuetify-Apbrs
Apr 15, 2026
Merged

feat(Carousel): add new component#190
johnleider merged 27 commits intomasterfrom
claude/carousel-component-vuetify-Apbrs

Conversation

@johnleider
Copy link
Copy Markdown
Member

Summary

Adds a new Carousel component to the semantic components library. This is a headless carousel built on CSS scroll-snap with support for multi-slide display, partial-slide peeking, drag/swipe navigation, and programmatic control via previous/next buttons.

Key Changes

  • CarouselRoot: Root component that creates and provides step context for carousel navigation. Supports circular/bounded modes, orientation (horizontal/vertical), multi-slide display (perView), gap spacing, and peek configuration.

  • CarouselViewport: Scroll-snap container that handles native drag/swipe interaction and synchronizes scroll position with step selection state. Exposes isDragging state and manages scroll-to-selection synchronization.

  • CarouselSlide: Individual slide component that registers with the carousel context. Provides selection state, visibility tracking, and automatic sizing based on perView, gap, and peek values.

  • CarouselPrevious & CarouselNext: Navigation button components that automatically disable at carousel boundaries in non-circular mode. Expose isAtEdge state for styling.

  • Comprehensive test suite: 1000+ lines of tests covering rendering, navigation (next/prev/first/last), circular/bounded modes, disabled states, mandatory selection, slot props, ARIA attributes, data attributes, and inline styles.

  • Documentation: Full component documentation with anatomy, examples (basic, multi-slide, peek), recipes (circular, vertical, disabled), accessibility details, and FAQ.

  • Examples: Three example implementations demonstrating basic carousel, multi-slide display, and peek functionality.

Implementation Details

  • Built on Vue 3 composition API with renderless/headless design pattern
  • Uses step context from createStep composable for selection management
  • Implements WAI-ARIA carousel pattern with proper roles and ARIA attributes
  • Exposes comprehensive slot props for flexible styling and interaction
  • Automatic slide sizing calculation accounting for gaps and peek values
  • Scroll position synchronization with debouncing to prevent feedback loops
  • Data attributes (data-selected, data-active, data-disabled, data-index) for CSS-only styling
  • Supports both horizontal and vertical orientations
  • Circular navigation mode with wrapping from last to first slide

Component maturity updated from "draft" to "preview" with version 0.3.0.

https://claude.ai/code/session_01LYmjuphJqS2njPANKAoxPr

claude added 2 commits April 12, 2026 21:46
Implements a compound carousel component built on createStep with CSS
scroll-snap for native drag/swipe navigation. Supports multi-slide
display (perView), partial-slide peeking, circular navigation, and
full WAI-ARIA carousel pattern compliance.

Components: Root, Viewport, Slide, Previous, Next
Tests: 32 passing (registration, navigation, ARIA, data attributes, SSR)
Docs: page, basic/multi-slide/peek examples, recipes

https://claude.ai/code/session_01LYmjuphJqS2njPANKAoxPr
Follows the pattern from TabsRoot where defineEmits is declared
alongside defineModel for 'update:model-value'.

https://claude.ai/code/session_01LYmjuphJqS2njPANKAoxPr
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 12, 2026

Open in StackBlitz

commit: e0ff07d

@johnleider johnleider self-assigned this Apr 13, 2026
@johnleider johnleider modified the milestone: v1.0.0 Apr 13, 2026
@johnleider johnleider changed the title feat: add Carousel component with scroll-snap and multi-slide support feat(Carousel): add new component Apr 13, 2026
- Fix double-fire bug from v-bind="attrs" on non-renderless children
- Fix circular navigation dying with perView > 1
- Add mouse drag with smooth snap-to-slide on release
- Add autoplay prop with play/stop/pause/resume controls
- Rename CarouselSlide to CarouselItem for consistency
- Switch examples to data-attribute CSS selectors
- Move all imports from <script setup> to first <script> block (all 5 files)
- Replace manual document.addEventListener with useToggleScope + useDocumentEventListener in Viewport
- Add aria-disabled to CarouselNext/Previous (matches Pagination pattern)
- Wire up useLocale for all aria-labels (Carousel.next, Carousel.prev, Carousel.slide)
- Rename abbreviated variables (idx → index, pv → perView) in CarouselItem
- Switch onUnmounted → onBeforeUnmount for unregister cleanup
- Add defineEmits comment explaining vue-devtools requirement
- Update components.md rules: import enforcement, locale requirement, aria-disabled typing, onBeforeUnmount, defineEmits guidance
gap and peek are styling concerns that break the headless contract.
Consumers now control these via CSS (gap, padding utilities) and
slide sizing (flex/width classes). The viewport measures actual
slide offsets from the DOM for scroll-snap math instead of
calculating from prop values.
Previous, Next, and Viewport now register into the parts registry
with type and element ref instead of imperative watchEffect push.
Extends CarouselContext with autoplay timer state for child consumption.
…rement

CarouselItem now registers el on its ticket. CarouselViewport accesses
item elements from the registry instead of viewport.children. CarouselRoot
uses Atom and extends context with autoplay timer state.
…dicator

- Add behavior prop (ScrollBehavior) to control programmatic scroll
- Remove scrollbar-width and user-select from viewport inline styles
- Remove redundant select method and isUndefined guards from Indicator
- Align locale template vars to use size instead of total
- Remove unnecessary inline comments from Viewport
- Add label prop and aria-label to Root region
- Add aria-disabled and data-disabled to Root attrs
- Convert const arrow functions to function declarations
- Fix Indicator step() misuse (absolute vs relative index)
- Add Enter/Space keyboard handling to Indicator
- Add focus management after Indicator keyboard navigation
- Add inheritAttrs: false to LiveRegion
- Remove redundant aria-live from Viewport
- Export CarouselTicket and CarouselPartTicket types
- Fix stale mandatory test describe name
- Fix SSR typo in test
- Remove since from maturity.json
- Update accessibility table in docs
Consumer-controlled gap/peek via CSS classes was too much boilerplate
and broke the scroll-snap math. These are structural props tied to
scroll behavior, not decorative styling. The component owns the
layout: gap on viewport, padding for peek, flex basis on items.

Fixed flex basis to not double-subtract peek (padding already reduces
the content area, so only gap needs subtracting from the calc).
Move scrollbar-width: none and user-select: none (during drag) into
the component's inline styles so consumers don't need to add them.
@johnleider johnleider merged commit bab3f65 into master Apr 15, 2026
10 of 13 checks passed
@johnleider johnleider deleted the claude/carousel-component-vuetify-Apbrs branch April 15, 2026 20:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants