What this covers
A day aligning the live note demo beside the home-page hero and the dark pipeline scroll direction with CSS alone, no animation library. Alongside, I also did font CLS cleanup and active-indicator/accessibility cleanup.
To keep it from sprawling, I group it per task in the order background → the situation → core work → lesson.
Day summary
| Task | What I wanted | What I did | Result |
|---|---|---|---|
| 1. Scroll-linked home direction | A "record → promotion" story scroll direction, no JS library | sticky stage + progress phase, clip-path repeat loop, view() entrance | Home narrative built with 0kb library |
| 2. Typography CLS cleanup | Zero text shift (CLS) even on slow networks | tag/badge/body font swap → optional | CLS 0 on slow networks (regression test) |
| 3. UI consistency & accessibility | Unified active indicator + resolved contrast violations | Single active rail·aria-current, contrast AA tokenization, VR baseline | Unified indicator across pages, a11y violations resolved |
1. Scroll-linked home narrative
Background (concepts)
- scrolly / sticky scrolly: a direction that pins one element with
position: stickyand turns scroll amount into progress, so phases advance as much as you scroll. - scroll-timeline / view-timeline (
animation-timeline): a CSS feature that uses the scroll position itself as the animation's time axis without JS.view()takes "how far an element has entered the viewport" as the time axis. - CSS keyframes: an animation defined with
@keyframesthat runs over time. It repeats (loops) on its own, independent of scroll.
What the situation was
- The recent landing-page flow leans toward using CSS keyframes + native scroll-timeline instead of a JS animation library.
- The problem is that when the three axes — time loop (repeats on its own) / scroll phase (advances by progress) / scroll entrance (view reveal) — get mixed, they break each other.
- sticky scrolly and view-timeline both quietly die at the same time with just one line of
overflow: hiddenon a parent.
Core work
- The pipeline section derived phases from a sticky stage + wrapper-height-based progress — one step per screen, rail and grid fixed.
- The hero live demo built its repeat loop with just a clip-path steps wipe +
animation-delay. Zero hydration (the process of attaching JS to server HTML to bring it alive) cost, crisp text, and reserving space withmin-heightso the loop doesn't shake the layout. - List/card entrances fade in with
animation-timeline: view()+animation-range: entry— I separated the class and time axis from the loop. - Under
prefers-reduced-motionI release sticky and fix it to the final promoted state → only motion is off, the information degrades intact.
Lesson
- Landing motion is possible with 0kb of JS — the scroll-timeline·sticky·keyframes combo matches the recent flow.
- The three time axes (time loop / scroll phase / scroll entrance) break each other unless you separate them by class and timeline.
- sticky scrolly quietly dies from one line of
overflow: hiddenon an ancestor — when it breaks, suspect the parent chain first. - A decorative demo shouldn't shake layout, via
aria-hidden+min-heightreservation.
→ Scroll-driven narrative layout · CSS view-timeline entry animation · 2026 startup landing page trends
2. Typography CLS cleanup
Background (concepts)
- CLS (Cumulative Layout Shift): how much elements suddenly shift as the page loads. Lower is better.
font-display: swapvsoptional:swapswaps the fallback font to the webfont if the webfont arrives late.optionalkeeps the fallback font as-is if the font doesn't arrive within a very short wait.
What the situation was
- Tag pills, badges, and body were
font-display: swap, so when the webfont arrived late the glyph metrics changed and the layout shifted (CLS spike).
Core work
- Changed tag pill, badge, and body fonts from
swap→optional. - Added a regression test that keeps CLS 0 in the slow-network +
optionalcombination.
Lesson
font-display: optionalsuits document UI where measurable CLS takes priority over perfect reproduction of the brand font.
→ font-display optional and CLS
3. UI consistency & accessibility
Background (concepts)
aria-current: an attribute that tells assistive tech the currently active item (current page/filter).- Contrast AA: the WCAG contrast-ratio standard. Text/background contrast must clear this so low-vision users can read it too.
- VR (Visual Regression) baseline: saving screen screenshots as a baseline, then catching whether later changes unintentionally altered the screen via pixel comparison.
What the situation was
- The active-state indicator (rail·underline·pill) differed per page, so the cost of scanning by eye was high.
- There were places where the accent color's text contrast didn't meet the standard.
Core work
- Unified the active indicator into a single rail, attached
aria-currentto filters, and limited body width. - Adjusted the accent color to a contrast AA-passing value and pulled it out as a
--signal-texttoken (recurrence prevention). - Took VR baselines for the main breakpoints. Typecheck + unit (vitest) + E2E all green.
Lesson
- The active indicator must be unified in a single source so it doesn't drift per page.
- Tokenizing the contrast value keeps the same violation from recurring next time.