12 CSS Performance Tips That Actually Make a Difference
Cut render-blocking CSS, reduce unused styles, use contain and content-visibility, optimise selectors and squeeze every millisecond of rendering performance from your stylesheet.
CSS is often treated as inherently cheap. But at scale — large stylesheets, complex layouts, heavy animations — CSS can absolutely tank page performance. These 12 techniques are the ones that move the needle in real projects.
1. Inline critical CSS
Critical CSS is the styles needed to render above-the-fold content. Inline it in <style> tags in the <head> and load the rest asynchronously:
<style>/* Inlined critical CSS */</style>
<link rel="preload" href="main.css" as="style" onload="this.rel='stylesheet'">
2. Remove unused CSS
Tools like PurgeCSS or the built-in Coverage panel in Chrome DevTools will show you exactly which rules are never used. In a typical project, 60–70% of CSS goes unused.
3. Use layer for specificity management
@layer reset, base, components, utilities;
@layer reset { /* normalize */ }
@layer base { /* typography, body */ }
@layer components { /* cards, buttons */ }
@layer utilities { /* .sr-only, .truncate */ }
4. content-visibility: auto
.below-fold-section {
content-visibility: auto;
contain-intrinsic-size: 0 500px; /* estimated height */
}
The browser skips rendering off-screen sections entirely until they're about to enter the viewport. On long pages, this can reduce initial render time by 50%+.
5. CSS contain
.widget {
contain: layout style paint;
}
/* Or the shorthand */
.widget {
contain: strict;
}
Tells the browser that changes inside the element won't affect outside, enabling aggressive optimisation.
6. Avoid layout-triggering properties in animations
Never animate: width, height, top, left, margin, padding. Only animate transform and opacity.
7. Avoid universal selectors in deep trees
/* ✗ Slow — matches everything, then filters */
.card * { color: inherit; }
/* ✓ Fast — specific target */
.card p, .card span { color: inherit; }
8. Avoid :nth-child() on large lists
Pseudo-class selectors on large dynamic lists cause style recalculation on every DOM change. Add a class directly to the element instead.
9. Keep selector depth shallow
CSS selectors are matched right-to-left. Deep selectors like .page .section .card .body p require the browser to walk up five levels for every p in the document.
10. Minify and compress
Gzip cuts CSS file size by 60–80%. Brotli cuts it further. Enable on your server (most hosting does this automatically).
11. Preload web fonts
<link rel="preload" href="/fonts/syne.woff2" as="font" type="font/woff2" crossorigin>
Fonts are late-discovered resources. Preloading them prevents flash of unstyled text (FOUT).
12. font-display: swap
@font-face {
font-family: 'Syne';
src: url('/fonts/syne.woff2') format('woff2');
font-display: swap; /* Show fallback immediately, swap when loaded */
}