Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Designs, implements, audits, and repairs smooth, accessible, production-grade scroll-driven web interfaces. Use for sticky or pinned sections, scrollytelling, scroll-linked animation, parallax, progress indicators, tab-to-scroll synchronization, mobile scroll jank, skipped scroll steps, GSAP ScrollTrigger, Motion useScroll, CSS scroll-driven animations, IntersectionObserver, or complaints that scrolling feels laggy. Chooses the least-complex correct architecture, preserves native scrolling, avoi
.claude/skills/harshavarma02-zero-jank-scroll/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-03 | ✗→✓ | ▲ Improved | 47% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 31% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 144% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 66% | 0% |
| case-18 | ✗→✓ | ▲ Improved | 75% | 0% |
Build scroll-driven interfaces as resilient interaction systems, not animation demos.
The goal is not maximum motion. The goal is intentional motion that remains smooth, accessible, responsive, maintainable, and correct when content, viewport, input method, or browser behavior changes.
300vh, 400vh, or percentage ranges when real steps can represent the sequence.transform and opacity by default; justify paint-heavy or layout-changing animation.Before editing code, inspect:
overflow, contain, and transform properties,Do not add a new animation library before checking what the project already uses.
Choose exactly one primary class.
Examples:
Default architecture:
position: sticky for visual persistence,IntersectionObserver for active-step changes,scrollIntoView() for click-to-scroll.Do not use continuous progress math for a discrete state machine.
Examples:
Default architecture:
Treat CSS scroll-driven animation as progressive enhancement unless all required browsers support the needed behavior.
Examples:
Default architecture:
Do not use GSAP merely to highlight four steps.
Read references/architecture-matrix.md when the choice is ambiguous.
Prefer this conceptual structure for sticky step stories:
textsection ├── steps │ ├── step │ ├── step │ └── step └── stage └── sticky-stage ├── panel ├── panel └── panel
Requirements:
scroll-margin-block-start when a fixed header exists.Use svh for stable viewport-sized layout. Use dvh only where live browser-chrome resizing is intentionally desired.
For discrete steps:
rootMargin or explicit sentinels. Use a center activation band only when the design explicitly calls for center-based switching.Click behavior:
scrollIntoView() so it aligns with the same activation line.If a scroll listener is genuinely required:
preventDefault(),Default animated properties:
opacity,transform.Use caution with:
Avoid scroll-linked animation of:
Do not add will-change globally. Apply it narrowly only after profiling demonstrates a benefit, and remove it when it is no longer needed.
Pause inactive videos, canvas loops, WebGL renders, Lottie animations, and timers.
Read references/performance.md before optimizing or diagnosing jank.
Enable sticky choreography only when the viewport has enough width and height. Width-only breakpoints are insufficient.
For narrow or short viewports:
For reduced motion:
Accessibility requirements:
aria-current="step" for step navigation when appropriate.aria-hidden="true".inert for inactive panels containing interactive descendants when supported by the project.Read references/accessibility.md for the complete review.
Framework-independent rule: continuous animation values must not cause component-tree rerenders per frame.
For React:
For Vue and Svelte:
For SSR frameworks:
Read references/frameworks.md for framework-specific checks.
When repairing an implementation, investigate in this order:
Run the bundled source heuristic when useful:
bashnode scripts/audit-scroll-source.mjs <project-path>
Static findings are leads, not proof.
When browser tooling is available, test:
Collect:
Do not invent FPS, Core Web Vitals, or trace results. If runtime tools are unavailable, state exactly what was not measured.
Return:
List any known:
Do not mark the work complete unless:
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-03 | fail→pass | 26,981 | 23,569 | -13% | 1 | 1 | 0% | 4,961 | 7,299 | +47% | 0 | 0 | — |
case-16 | pass→pass | 16,374 | 15,581 | -5% | 1 | 1 | 0% | 2,966 | 5,371 | +81% | 0 | 0 | — |
case-01 | fail→pass | 29,579 | 26,322 | -11% | 1 | 1 | 0% | 5,947 | 7,811 | +31% | 0 | 0 | — |
case-02 | fail→fail | 32,377 | 29,558 | -9% | 1 | 1 | 0% | 6,261 | 8,250 | +32% | 0 | 0 | — |
case-15 | fail→pass | 8,910 | 8,440 | -5% | 1 | 1 | 0% | 1,735 | 4,225 | +144% | 0 | 0 | — |
case-04 | pass→pass | 25,923 | 21,451 | -17% | 1 | 1 | 0% | 5,477 | 6,835 | +25% | 0 | 0 | — |
case-05 | pass→pass | 14,297 | 15,621 | +9% | 1 | 1 | 0% | 2,887 | 5,736 | +99% | 0 | 0 | — |
case-06 | pass→pass | 12,203 | 13,913 | +14% | 1 | 1 | 0% | 2,522 | 5,426 | +115% | 0 | 0 | — |
case-07 | fail→pass | 19,521 | 15,684 | -20% | 1 | 1 | 0% | 3,949 | 6,562 | +66% | 0 | 0 | — |
case-08 | pass→pass | 14,158 | 14,955 | +6% | 1 | 1 | 0% | 2,493 | 5,258 | +111% | 0 | 0 | — |
case-09 | pass→pass | 13,097 | 16,484 | +26% | 1 | 1 | 0% | 2,414 | 5,744 | +138% | 0 | 0 | — |
case-10 | pass→pass | 12,284 | 32,680 | +166% | 1 | 1 | 0% | 2,375 | 5,836 | +146% | 0 | 0 | — |
case-11 | pass→pass | 20,936 | 16,861 | -19% | 1 | 1 | 0% | 3,332 | 5,954 | +79% | 0 | 0 | — |
case-12 | pass→pass | 14,841 | 15,792 | +6% | 1 | 1 | 0% | 2,519 | 5,404 | +115% | 0 | 0 | — |
case-13 | pass→pass | 13,867 | 14,188 | +2% | 1 | 1 | 0% | 2,299 | 5,258 | +129% | 0 | 0 | — |
case-14 | pass→pass | 9,229 | 10,409 | +13% | 1 | 1 | 0% | 1,923 | 4,225 | +120% | 0 | 0 | — |
case-17 | pass→pass | 12,410 | 9,625 | -22% | 1 | 1 | 0% | 2,185 | 4,245 | +94% | 0 | 0 | — |
case-18 | fail→pass | 17,057 | 24,683 | +45% | 1 | 1 | 0% | 2,900 | 5,061 | +75% | 0 | 0 | — |
case-19 | fail→pass | 17,524 | 15,156 | -14% | 1 | 1 | 0% | 2,964 | 5,223 | +76% | 0 | 0 | — |
case-20 | pass→pass | 10,951 | 8,279 | -24% | 1 | 1 | 0% | 1,829 | 4,076 | +123% | 0 | 0 | — |
case-21 | pass→pass | 12,651 | 13,914 | +10% | 1 | 1 | 0% | 2,421 | 5,143 | +112% | 0 | 0 | — |
case-22 | pass→pass | 13,462 | 8,566 | -36% | 1 | 1 | 0% | 2,173 | 3,858 | +78% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +27 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.