Compatibility
Support levels come from packages/catalog/compatibility-matrix.json — generated with the preset registry (Oct 18, 2026), not marketing copy.
Registry · 144 presets
Baseline targets: Chrome 124+, Safari 18+, Firefox 128+.Documented targets; verified by Playwright E2E (Chromium, Firefox, WebKit) in CI.
Scroll inside the frame to trigger view-linked motion. Use Replay to run again.
Reduced motion safe
Enable Reduce motion in OS settings — travel collapses, loops stop
vl-enter="fade-up" vl-timeline="view"Support levels
| Level | Meaning |
|---|---|
| stable | Required behavior with a documented safe path |
| progressive | Feature-detected enhancement over a working fallback |
| experimental | Motion Lab first; never required for the public experience |
Execution contract
Future-first, never future-only. HTML attributes express intent; browser features remain implementation details.
- baseline: Semantic content, layout and interaction remain usable.
- fallback: A supported CSS path preserves the feature intent.
- native: The browser executes the most capable standards-based path.
- frontier: Experimental syntax is isolated by capability detection.
CSS primitives matrix
Primitive × browser status from the generated matrix. Interactive version lives on the Showcase.
| Primitive | Status | Chrome | Safari | Firefox | Detection | Fallback | Reduced motion |
|---|---|---|---|---|---|---|---|
| CSS Animations / Transitions | stable | stable | stable | stable | baseline | Final static styles; content remains visible | Decorative travel collapses and continuous loops stop |
| @layer | stable | stable | stable | stable | baseline | Documented browser baseline; semantic HTML remains usable outside it | Not motion-specific |
| Scroll-driven animations | stable | stable | stable | progressive | @supports (animation-timeline: view()) | Time-based animation; content visible | Scroll-linked travel is removed or reduced to a static final state |
| view-timeline-name / timeline-scope | stable | stable | stable | progressive | @supports (timeline-scope: --vl-probe) | Time-based scene clock | Scene content resolves without scroll-linked travel |
| @starting-style + allow-discrete | stable | stable | stable | stable | @supports (transition-behavior: allow-discrete) | Instant state change | State changes immediately |
| position: sticky ([vl-stage]) | stable | stable | stable | stable | @supports (position: sticky) | Block flow | Stage remains readable without choreography |
| :has() | progressive | stable | stable | stable | @supports selector(:has(*)) | :hover / :focus-within | State remains available without decorative travel |
| sibling-index() / sibling-count() | progressive | stable | stable | progressive | @supports (animation-delay: calc(1ms * sibling-index())) | nth-child lists (≤12 children) | Distributed delays collapse |
| Typed attr() | progressive | progressive | progressive | progressive | @supports (--vl-probe: attr(data-vl-probe type(<number>))) | Enum value fallbacks | Typed values cannot override the reduced-motion layer |
| if() | experimental | experimental | experimental | unsupported | @supports (width: if(style(--vl-probe: 1): 1px; else: 2px)) | Baseline attribute selectors | Baseline selectors retain reduced-motion overrides |
| calc-size() | progressive | progressive | progressive | unsupported | @supports (height: calc-size(auto, size)) | Fixed size expand | Disclosure resolves immediately |
| offset-path | progressive | stable | stable | progressive | @supports (offset-path: path('M0,0 L1,1')) | Static layout | Path travel is removed |
| ::scroll-marker | experimental | experimental | unsupported | unsupported | @supports selector(::scroll-marker) | Native scrolling and authored navigation controls remain available | Navigation remains instant and usable |
| View Transitions API (MPA) | stable | stable | stable | unsupported | @supports (view-transition-name: none) | Instant navigation | Transition pseudo-elements animate for effectively zero duration |