Section 4 · Motion and Architecture
Rendering Performance
18 min4 min readCode playground
Lesson at a glance
Know which CSS changes trigger layout, paint or only compositing, animate the cheap properties, and use content-visibility and aspect-ratio to keep long pages fast and stable.
- 1The rendering pipeline
- 2Animate the cheap properties
- 3Skip work you can’t see
- 4Don’t move things after they appear
- 5Where you are now
No video here — just the real thing: a written lesson, runnable examples, a quiz and practice.
Waypoint’s schedule looked finished, and then the team opened it on a three-year-old Android phone at the venue’s busy Wi-Fi. The save toast stuttered, the first scroll felt sticky, and speaker photos pushed the text down as they loaded. Nothing was broken; it was just doing more work than it needed to.
CSS performance comes down to one question: how much of the rendering pipeline does this change force the browser to run?
The rendering pipeline
After your CSS changes something, the browser runs up to four stages to update the screen:
- Style: work out which rules apply and compute each element’s values.
- Layout: calculate every box’s size and position.
- Paint: fill in the pixels: text, colors, borders, shadows, images.
- Composite: assemble the painted layers on the GPU, applying transforms and opacity.
The trick is that each change only needs to start at a certain stage. Change width and layout must run, then paint, then compositing. Change background-color and layout can be skipped. Change transform or opacity on an element that has its own layer, and the browser can skip layout and paint, moving existing pixels around on the GPU.
For a single click, the difference doesn’t matter. For an animation running 60 times a second, or a style change that touches 500 elements, it decides whether the page feels smooth.
Animate the cheap properties
The save toast was animated like this:
@keyframes toast-in {
from { left: -320px; }
to { left: 16px; }
}left is a geometry property: every frame re-runs layout for the toast (and checks whether anything else moved), then repaints. The fix keeps the toast’s position fixed and moves its picture instead:
.toast {
position: fixed;
left: 16px;
bottom: 16px;
animation: toast-in 250ms ease-out;
}
@keyframes toast-in {
from { translate: -336px 0; opacity: 0; }
to { translate: 0 0; opacity: 1; }
}Same visual result, a fraction of the work. This is why every animation in this course used translate, scale, opacity and friends. The same reasoning applies to transitions: a hover that changes box-shadow repaints on each frame, which is fine for one card and noticeable for a grid of fifty animating at once. Fading in a pre-painted shadow on a pseudo-element with opacity is the cheaper alternative when it matters.
Skip work you can’t see
The schedule renders two days of sessions, but the user sees a screenful at a time. content-visibility: auto lets the browser skip layout and paint for sections that are off-screen:
.day {
content-visibility: auto;
contain-intrinsic-size: auto 1200px;
}Skipped content has no size, so contain-intrinsic-size gives the browser an estimate to use as a placeholder; with auto, it remembers the real size once the section has been rendered. Without it, sections collapse to zero and the scrollbar jumps as you scroll. Content stays in the DOM and in the accessibility tree, and find-in-page still works. content-visibility: auto has been Baseline since September 2025, when Safari 26 added the auto value; a browser without it simply renders every section, as before.
Don’t move things after they appear
Cumulative Layout Shift, one of the Core Web Vitals, measures content jumping as the page loads. In CSS, the usual culprit is media without reserved space. A speaker photo with width: 100% and no height is 0px tall until the image arrives, then shoves everything down. Reserve the space:
.speaker img {
width: 100%;
height: auto;
aspect-ratio: 1;
object-fit: cover;
}width and height attributes on the <img> do the same job, since browsers derive an aspect ratio from them. For web fonts, font-display: swap plus a fallback font with similar metrics keeps text from reflowing when the font loads.
Where you are now
Across twenty lessons you’ve taken Waypoint’s schedule from a stylesheet that fought you to one you can predict: layers and :where() settle conflicts, custom properties expose component APIs, grid, subgrid and container queries handle layout, oklch() and light-dark() handle color, logical properties make it work in Arabic, and motion is kind and cheap. The habit to keep is the one from the first lesson: before you change CSS, predict what the browser will do, then check.
Key takeaways
- Every visual change goes through some of style, layout, paint and composite; geometry properties trigger layout, visual properties trigger paint, and
transformandopacitycan skip both. - Animate
translate,scale,rotateandopacity; animatingleft,widthormarginre-runs layout on every frame. content-visibility: autowithcontain-intrinsic-sizelets the browser skip rendering off-screen sections of a long page.- Reserving space with
aspect-ratio(or width and height attributes) prevents layout shifts when images and embeds load. - Measure before optimising, with the DevTools Performance panel and paint flashing, and remove
will-changeonce an animation ends.