
Theme Animation Simplified — Goodbye Circular Clip-Path, Hello Crossfade
I spent the entire morning fighting theme animations on LifeLog. And like any good war against CSS, I ended up surrendering — but with much cleaner code.
The Problem
Ever since I implemented the Theme Rail (that sidebar selector with 6 palettes + dark/light), I used a circular clip-path animation to transition between themes. The idea looked great on paper: a circle expanding from the click point, revealing the new theme underneath — Material Design style. In practice:
- Chromium: the native View Transition API already does crossfade, but I insisted on overriding it with
clip-pathon::view-transition-new(root). Result: visible stutter as the browser fights between the native VT and the custom animation. - Firefox/Safari: the fallback used an animated
clip-path: circle()overlay. It worked, but animatedclip-pathis expensive — heavy repaints, especially on Firefox Mobile.
There were moments where clicking a theme felt like a 100ms mini-freeze. Imperceptible on a high-end desktop, but sad on mobile.
Less Is More
After 3 commits iterating on the same approach (f8cc2e2, 8155f4e, 4142f14 — each polishing the same flawed strategy), I realized the problem was design, not implementation.
What changed:
- Chromium VT — stopped fighting
clip-pathand let the nativedocument.startViewTransitioncrossfade do its thing. Zero stutter, zero extra code. - Fallback (Firefox/Safari) — swapped the animated
clip-path: circle()for a simpleopacityfade withtransition: 0.35s ease. Much lighter, no expensive repaints. - Scroll persistence — added
sessionStorageto save scroll position before language switch navigation. When the user toggles PT/EN, the scroll returns to exactly where they were — no layout shift.
Tags Got Some Love Too
While I was at it, I fixed tag visibility on the post cards. They used background: transparent which made them invisible on the white theme. Now they use color-mix(in srgb, var(--color-bg) 15%, transparent) — a solid but subtle background, with the tag text using the project’s accent color. Works on both themes without media queries.
Morning Stats
- 6 files changed, 44 insertions, 52 deletions
- ~100 fewer lines of CSS/JS in PalettePicker alone
- Zero stutter on theme toggle — tested on Chromium, Firefox, and Safari
- E2E tests updated for dynamic post counts and the new search params (
?q=instead of?tag=)
The lesson of the day: complex animations are rarely worth the cost. A well-executed crossfade is smoother than a clip-path circle that janks — no matter how cool that circle looks in Figma.