CSS Performance Optimization
Lesson 44 โข Advanced Track
What You'll Learn
- Understand the browser rendering pipeline (Layout โ Paint โ Composite)
- Animate only transform and opacity for smooth 60fps performance
- Use will-change and CSS containment for rendering optimisation
- Write efficient CSS selectors that browsers can match quickly
- Implement Critical CSS for faster initial page loads
- Understand Core Web Vitals and how CSS affects them
๐ก Real-World Analogy
The browser renders in three steps: Layout (measuring rooms in a house), Paint (painting walls), Composite (taking photos and layering them). Animating transform only triggers Composite โ like shuffling photos. Animating width triggers Layout โ like knocking down and rebuilding walls every frame.
Understanding the Rendering Pipeline
Every time the browser needs to display something on screen, it goes through up to five steps: Style (calculate which CSS rules apply), Layout (calculate element sizes and positions), Paint (fill in pixels โ colors, text, shadows), Composite (combine painted layers). The key to performance is minimising how many of these steps are triggered.
Properties like transform and opacity only trigger the Composite step โ the cheapest one. Properties like width, height, margin, and padding trigger Layout, which forces the browser to recalculate the position of every nearby element. On a 60fps display, you have only 16.6ms per frame โ Layout recalculations can easily exceed this budget.
Beyond animations, CSS performance also includes selector efficiency (how fast the browser can match selectors to elements), file size optimisation (removing unused CSS), and Critical CSS (inlining above-the-fold styles to eliminate render-blocking downloads).
๐ CSS Property Cost Reference
| Property | Triggers | Cost | Safe to Animate? |
|---|---|---|---|
| transform | Composite only | ๐ข Very low | โ Yes |
| opacity | Composite only | ๐ข Very low | โ Yes |
| filter | Paint + Composite | ๐ก Medium | โ ๏ธ Cautiously |
| background-color | Paint | ๐ก Medium | โ ๏ธ Cautiously |
| box-shadow | Paint | ๐ด High | โ Avoid |
| width/height | Layout + Paint | ๐ด Very high | โ Avoid |
1. Animation Performance: Good vs Bad
Hover over each shape to see the difference. The green box animates transform (smooth, GPU-accelerated). The red box animates width/height (triggers layout, causes jank). The blue circle uses will-change: transform to hint GPU layer promotion.
Good vs Bad CSS Animations
<!DOCTYPE html>
<html><head><style>
body { font-family: system-ui, sans-serif; padding: 24px; }
h2 { color: #1565C0; }
.comparison { display: flex; gap: 32px; align-items: center; margin: 16px 0; flex-wrap: wrap; }
.good-anim { width: 100px; height: 100px; background: #4CAF50; border-radius: 8px; transition: transform 0.3s, opacity 0.3s; cursor: pointer; }
.good-anim:hover { transform: translateY(-10px) scale(1.05); opacity: 0.9; }
.bad-anim { width: 100px; height: 100px; background: #F44336; bo
...2. CSS Containment & content-visibility
The contain property tells the browser that an element is isolated from the rest of the page. content-visibility: auto is even more powerful โ it skips rendering for off-screen elements entirely, dramatically improving performance for long pages and feeds.
CSS Containment & content-visibility
<!DOCTYPE html>
<html><head><style>
body { font-family: system-ui, sans-serif; padding: 24px; }
h1 { color: #1565C0; }
h2 { color: #333; margin-top: 24px; }
.demo-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin: 16px 0; }
.card { padding: 20px; border: 1px solid #ddd; border-radius: 8px; }
.card h3 { margin-top: 0; }
.card p { line-height: 1.7; color: #555; font-size: 0.95rem; }
.contained { contain: layout style paint; content-visibility: auto; }
.not-contained { /* no c
...3. Selector Performance & File Size
Browsers read selectors right-to-left. A deeply nested selector like div ul li a span first matches every <span> on the page, then checks each ancestor โ expensive on large DOMs. Prefer flat class selectors. Also avoid @import which loads stylesheets sequentially instead of in parallel.
Selector Performance & CSS Optimisation
<!DOCTYPE html>
<html><head><style>
body { font-family: system-ui, sans-serif; padding: 24px; }
h1 { color: #1565C0; }
.perf-table { width: 100%; border-collapse: collapse; margin: 16px 0; }
.perf-table th, .perf-table td { padding: 12px 16px; border: 1px solid #ddd; }
.perf-table th { background: #f5f5f5; text-align: left; }
.perf-table .selector { font-family: monospace; font-size: 0.85rem; }
.perf-table .speed { text-align: center; font-weight: 700; }
.fast { color: #2E7D32; }
.medium { color
...4. Critical CSS & Core Web Vitals
Critical CSS is the minimum styling needed to render above-the-fold content. By inlining it in <style> tags and deferring the rest, you eliminate render-blocking downloads and improve LCP (Largest Contentful Paint). This is one of the most impactful performance optimisations you can make.
Critical CSS & Web Vitals
<!DOCTYPE html>
<html><head><style>
body { font-family: system-ui, sans-serif; padding: 24px; }
h1 { color: #1565C0; }
h2 { color: #333; margin-top: 24px; }
.step { display: flex; gap: 16px; margin: 16px 0; align-items: flex-start; }
.step-num { width: 40px; height: 40px; background: #1976D2; color: white; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: 800; flex-shrink: 0; }
.step-content { flex: 1; }
.step-content h3 { margin: 0 0 4px; color: #333;
...When to Optimise CSS Performance
- Animations feel janky: Switch from animating width/height/top/left to transform/opacity. This alone fixes most animation performance issues.
- Long scrollable pages: Add
content-visibility: autoto sections below the fold. Can reduce initial rendering time by 50%+ on long pages. - Large CSS files: Use Chrome DevTools Coverage tab to find unused CSS. PurgeCSS can automate removal in build tools.
- Slow initial load: Implement Critical CSS โ inline above-the-fold styles and defer the rest.
Common Mistakes
- Overusing will-change โ Only apply to elements that actually animate. It promotes to a GPU layer and consumes VRAM.
- Large box-shadows on scroll โ Complex shadows repaint every frame during scroll. Use pseudo-elements with opacity transitions instead.
- Deep descendant selectors โ
div > ul > li > a > spanis slow on large DOMs. Use flat class selectors:.nav-link-text. - Using @import for CSS โ It blocks parallel loading. Use
<link>tags in HTML instead. - No explicit image dimensions โ Without
widthandheight, images cause layout shift (CLS) when they load. - Premature optimisation โ Don't add
containeverywhere. Profile first with DevTools, then optimise bottlenecks.
๐ Lesson Complete
- โ
Only
transformandopacityskip Layout and Paint (GPU-only) - โ
will-changehints the browser to pre-promote an element to a GPU layer - โ
contain: layout style paintisolates rendering for better performance - โ
content-visibility: autoskips rendering off-screen elements entirely - โ Browsers read selectors right-to-left โ keep selectors flat and class-based
- โ
Avoid
@importโ use<link>tags for parallel stylesheet loading - โ Critical CSS = inline above-the-fold styles, defer the rest
- โ Use Chrome DevTools Performance & Coverage tabs to profile before optimising
Sign up for free to track which lessons you've completed and get learning reminders.