🚀AdvancedAdvanced CSS Architecture

CSS Performance and Optimization

Reduce layout thrashing, compositing costs, and selector overhead using will-change, content-visibility, contain, and efficient animation techniques.

Overview

CSS performance matters because the browser runs through four main steps for every frame: 1. **Style** recalculate selector matching 2. **Layout** compute element positions and sizes 3. **Paint** fill pixels on layers 4. **Composite** combine layers on the GPU Skipping expensive steps is the key to smooth 60fps UI: ```css /* Skip layout and paint by only animating compositor-only properties */ .efficient-anim { animation: slide-in 0.3s ease; } @keyframes slide-in { from { transform: translateX(-20px); opacity: 0; } /* GPU composited */ to { transform: translateX(0); opacity: 1; } } /* NOT this (triggers layout): */ @keyframes bad-anim { from { left: -20px; } /* forces layout recalc every frame */ to { left: 0; } } ``` **Performance properties:** - `will-change: transform, opacity` promotes element to its own layer - `contain: layout paint` isolates element from surrounding layout - `content-visibility: auto` skips off-screen rendering - `contain-intrinsic-size` provides a size placeholder for skipped elements

Key Points

  • •Only transform and opacity can be animated on the GPU without triggering layout or paint
  • •will-change: transform promotes an element to its own compositor layer before animation starts
  • •Overusing will-change wastes GPU memory; apply it only to actively animating elements
  • •content-visibility: auto skips rendering for off-screen elements, dramatically reducing initial paint time
  • •contain: layout prevents changes inside the element from triggering layout in the rest of the page
  • •contain: paint clips children to the element border, enabling separate paint records
  • •contain: strict is shorthand for layout + paint + size
  • •Selector performance: avoid deep descendant selectors; CSS reads selectors right-to-left

Examples

Efficient animations with compositor-only properties

/* Good: transform and opacity only touch the compositor layer */
.card-hover {
  transition: transform 0.2s ease, box-shadow 0.2s ease, opacity 0.2s ease;
}

.card-hover:hover {
  transform: translateY(-4px) scale(1.02);
  box-shadow: 0 12px 32px rgba(0,0,0,0.15);
  /* box-shadow triggers paint but not layout, acceptable */
}

/* Avoid animating layout-triggering properties */
/* Bad: */
.bad-hover:hover {
  margin-top: -4px; /* triggers layout recalc for all siblings */
  width: 102%;      /* triggers layout */
}

will-change for pre-promoted layers

/* Apply will-change before animation, remove after */
.menu-panel {
  will-change: transform; /* promoted before transition triggers */
  transition: transform 0.3s ease;
}

.menu-panel.open {
  transform: translateX(0);
}

.menu-panel.closed {
  transform: translateX(-100%);
}

/* For elements that animate constantly */
.spinning-loader {
  will-change: transform;
  animation: spin 1s linear infinite;
}

/* Remove will-change if animation is conditional */
.card:not(:hover) {
  will-change: auto; /* free GPU memory when not animating */
}

will-change is a hint to the browser to promote the element to its own layer ahead of time, reducing the first-frame cost of an animation.

content-visibility: auto for long pages

/* Great for pages with many sections */
.page-section {
  content-visibility: auto;
  /* Provide an estimated height so the scrollbar is accurate */
  contain-intrinsic-size: 0 600px;
}

/* The browser skips rendering off-screen sections entirely */
/* Rendering happens lazily as sections scroll into view */

/* For individual list items in a very long list */
.feed-item {
  content-visibility: auto;
  contain-intrinsic-size: 0 80px; /* approximate item height */
}

content-visibility: auto can reduce initial page render time by 40-80% on long pages.

CSS containment for layout isolation

/* Contain layout side effects of a widget */
.ad-widget {
  contain: layout paint; /* changes here do not trigger outer layout */
  overflow: hidden; /* paint containment requires this or clip-path */
}

/* Strict containment for independent components */
.data-grid {
  contain: strict; /* = layout + paint + size */
  width: 100%;
  height: 400px;
  overflow: auto;
}

/* Style containment prevents custom counters escaping */
.article {
  contain: style;
  counter-reset: section;
}

.article h2::before {
  counter-increment: section;
  content: counter(section) ". ";
}

Common Mistakes

  • Applying will-change to every element as a blanket optimization (it wastes GPU memory)
  • Animating width, height, top, or left, which trigger full layout recalculation every frame
  • Forgetting contain-intrinsic-size with content-visibility: auto causing layout instability
  • Removing will-change immediately on animation start instead of before it

Note

Before optimizing, measure. Use Chrome DevTools Performance tab to record a frame and check for long paint or layout tasks. Only apply will-change and contain to elements where profiling shows actual bottlenecks.

Exercises

Write CSS to style the HTML scaffold. The preview updates live — when it looks right, mark as done.

Loading editor…

Completed: 0 / 2 exercises

Knowledge Check

Question 1 of 3

Which two CSS properties can be animated without triggering layout or paint?