Performance and reduced motion
The blinking switchboard on the Next.js site glows. How the glow is drawn, and how a page behaves when a reader asks for less motion, are the invisible half of craft.
Animate opacity and transform#
In Crafting the Next.js Website Rauno Freiberg explains that the glow is a CSS shadow but only opacity and transform animate. Animating box-shadow would trigger a repaint every frame, which modern computers absorb but older phones may not. So he toggles the opacity of pseudo elements that already carry a shadow, keyed on a data-state of off, medium, or high. The hover that spells NEXT replaces cherry-picked indices in sequence.
.switchboard.light { &:after, &:before { content: ''; position: absolute; inset: 0; opacity: 0; transition: opacity var(--transition-duration) ease; } &:before { background: #3291FF; box-shadow: 0px 0px 2px 1px rgba(50, 145, 255, 0.25); } &[data-state='medium']:before { opacity: 1; }}Emil Kowalski states the rule in Great Animations: “If our animations won’t run at 60 frames per second, everything else we’ve talked about becomes useless.” Transform and opacity only trigger composite; padding or margin trigger layout, paint, and composite. When the main thread is busy, use hardware-accelerated CSS or the Web Animations API; his example is a Vercel dashboard tab transition that dropped frames during page load until it moved to CSS.
Layer so the essence paints first#
The Vercel hero in What will you ship? is stacked, descending, as heading, SVG triangle, CSS grid lines, SVG rays, CSS rainbow gradient, and a GLSL shader, and needs no client-side code to show its essence. The shader is progressive enhancement: it fades in after load, can be code split, and light hardware detection skips it on low-powered devices. The pixel icons follow: pixel data renders as canvas for a smaller DOM and animation, with a base64 placeholder of about 1kb per icon, or as SVG above the fold.
Paco Coursey’s SVG Caching with <use> (2020) is a related economy: reuse rendered icon DOM through use with a stable hashed id, about 40% less markup in his example, as long as the id matches between server and client.
<svg> <circle id="circle" cx="5" r="5" fill="black" /></svg><svg> <use href="#circle" /></svg>Pause loops, leave interactions alone#
Reduced motion on the Vercel page draws a distinction worth copying. Looping complementary animations, the floating cursors and blinking carets, are paused under prefers-reduced-motion. Interactions are not, because they are explicit inputs with no extravagant movement. The loops are CSS keyframes, so rather than animation: none the page uses animation-play-state: paused. And a detail no one sane has reason to notice: the caret’s opacity is forced to 1, so the blink cannot pause while invisible.
@media (prefers-reduced-motion: reduce) { .cursor, .caret { animation-play-state: paused; } .caret { opacity: 1 !important; }}Emil’s reason for the media query is blunt: animations can make some people feel sick or distracted, and his example animates opacity only under reduced motion.
Slow it down and look#
Emil’s skill, emil-design-eng, adds two constraints. Blur can mask an imperfect transition, but “Keep blur under 20px. Heavy blur is expensive, especially in Safari.”
To see what the eye only senses, increase durations to 2 to 5 times normal, step frame by frame in the Chrome DevTools Animations panel, and test on real devices; in Good vs Great Animations, “playing the animation frame by frame, or in slow motion, helps you spot it.” Jim Nielsen slowed ::view-transition-old() to see an outline bug, and Kathryn Gonzalez learned to test on real devices. Three people, same tool.
The Vercel essay names the trade-off: “if an animation or interaction didn’t perform well, felt pompous, or out of rhythm relative to the page, we didn’t build it.” Rauno would rather polish a spring than chase a slow initial load, then asks: “is having a slow website with immaculate attention to visual craft desirable?” Invisible work is the easiest to trade away.
That is where the chapter lands: a glow that does not repaint, a hero that paints before its shader, a caret that pauses visible, designs found only in code. A site that feels fast to everyone is better craft than one immaculate only on the newest hardware.