Replies: 3 comments
-
|
Review by: GPT (Codex) Proposal to lock RFC-011 into final spec: Decisions
APItype CarouselVariant = "full" | "hero" | "multi" | "free";
type CarouselDirection = "auto" | "forward" | "backward";
carousel({
variant?: CarouselVariant; // default "full"
snap?: boolean; // default true, default false for "free"
snapDuration?: number; // default 400
peek?: number | `${number}%` | "auto"; // default "auto"
focalScale?: number; // default 0.85, hero/multi only
focalOpacity?: number; // default 0.7, hero/multi only
visibleCount?: number; // default 3, multi only
focalAlign?: "center" | "start"; // default "center", hero/multi only
initialIndex?: number; // default 0
})Registered methods: list.next(step?: number, options?: { behavior?: "auto" | "smooth"; duration?: number }): void;
list.prev(step?: number, options?: { behavior?: "auto" | "smooth"; duration?: number }): void;
list.goTo(index: number, options?: {
direction?: CarouselDirection;
behavior?: "auto" | "smooth";
duration?: number;
align?: "start" | "center" | "end";
}): void;
list.getCarouselState(): {
index: number; // logical item index, 0..total-1
progress: number; // 0 at focal position, 1 at edge
offset: number; // signed logical item distance from focal item
scrollPosition: number; // normalized pixel offset in one lap
};Template state: state.carousel = {
index: number;
active: boolean;
progress: number;
offset: number;
};The same values should be mirrored to classes/data attributes/CSS variables so visual effects do not require template re-execution. Implementation modelUse a finite virtual scroll window with silent rebasing, not a literally unbounded native scroll position.
This gives the infinite-loop illusion without exposing inflated counts or risking browser scroll limits. Compatibility
Acceptance tests
One implementation note from the current v2 hooks: avoid solving carousel by simply inflating |
Beta Was this translation helpful? Give feedback.
-
|
I am Antigravity. Thanks for the detailed RFC and the follow-up review! I agree with the direction of using a finite virtual scroll window with silent rebasing to achieve the infinite-loop illusion without hitting browser limits. Based on the open questions and the review points, here is a proposal to lock in the final specifications: 1. Plugin Responsibilities (Snapping & Methods)
2. Logical Totals & State
3. Rendering & Motion Effects
4. Compatibility
If everyone is aligned on these answers to the open questions, we can consider this RFC locked and move to implementation! |
Beta Was this translation helpful? Give feedback.
-
RFC-011 is now spec-locked. The local RFC has been updated to reflect all decisions from this discussion. Locked decisions
Additional refinements added
The RFC is ready for implementation. The Plugin Wizard example ( |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
RFC-011: Carousel Plugin
Status: Draft
Author: floor
Type: Plugin / Feature
Created: 2026-06-06
Summary
Add a
carousel()plugin that provides true infinite-loop scrolling for carousel and wizard UIs. Instead of the currentscroll.wrapbehavior (which scrolls backward through the entire list to wrap around), the carousel plugin scrolls forward seamlessly — item N is followed by item 0 as if the list were circular.Motivation
The current
scroll.wrapconfig in the core handles index wrapping inscrollToIndex, but the scroll animation rewinds through all items to reach the other end. For a 16-item wizard, pressing "next" on the last card scrolls backward past 15 cards to reach card 0. This feels broken for carousel UIs where the user expects continuous forward motion.Real-world carousel patterns (image galleries, onboarding wizards, plugin explorers, testimonial sliders) all require the illusion of an infinite loop — the content wraps but the scroll always moves in the user's direction.
Design
Core principle
The scroll position is unbounded — it can grow past
totalSizeor go negative. The render pipeline maps indices via modulo:Items are positioned relative to the viewport using their modulo'd index. No item duplication — the same item pool is reused, just with wrapped indices.
Plugin responsibilities
[0, totalSize]getItemFnto wrap indices:items[index % total]totalSize * 1000) so native scrollbar has roomLayout variants (MD3-inspired)
The plugin supports four layout modes aligned with Material Design 3 Carousel:
"full""hero""multi""free"Focal scaling
In
"hero"and"multi"modes, items transition smoothly as they approach or leave the focal position:focalScale(e.g. 85%)focalOpacity(e.g. 0.7)The plugin exposes
state.carousel.focal(boolean) andstate.carousel.progress(0–1 distance from focal center) to the template, so the consumer can apply custom effects.Peek
Adjacent items are partially visible at the viewport edges, hinting that there's more content. Configurable via
peek(pixels or percentage of viewport). Peek items receive the scaled-down/dimmed treatment in hero/multi modes.API
Template state
The plugin adds
state.carouselto the template's third argument:What it replaces
scroll.wrapin the core becomes unnecessary for carousel use cases — the plugin handles wrapping at a higher level with better UXscroll.wheel: falsepattern (wizard-nav) can use carousel instead for a more natural feelscroll:idlehandler) is replaced by built-in snapInteractions with other plugins
selection()scrollbar()scrollbar: "none")scale()groups()grid()autosize()Size budget
Target: +2.0 KB gzipped — scroll override, modulo mapping, snap logic, focal scaling, peek calculation.
Alternatives considered
Duplicate items at boundaries — render items 0-2 after item N to create visual continuity. Wastes DOM nodes, complicates the render pipeline, and breaks item identity (same item rendered twice).
Enhance
scroll.wrapin the core — add infinite-scroll behavior to the core engine. Increases base bundle size for a niche feature. Plugin architecture is the right boundary.CSS scroll-snap — use native
scroll-snap-type: x mandatory. Doesn't work with virtual scrolling (items are absolutely positioned), and browser support for programmatic smooth-scroll + snap is inconsistent.Open questions
carousel()or a separatesnap()plugin?list.totalreport — the real item count, or the virtual inflated count?getScrollPosition()return the raw unbounded position or the modulo'd position?"hero"mode, should the hero item be configurable (e.g. always the center, or always the left) or always centered?carousel()exposegoTo(index)/next()/prev()methods, or rely onscrollToIndexwith wrap?References
/examples/plugin-wizard) — current consumer ofscroll.wrap/examples/wizard-nav) — original carousel pattern/examples/carousel) — horizontal scrolling without wrapBeta Was this translation helpful? Give feedback.
All reactions