border-radius and Background Animation Costs

Part of Hardware-Accelerated Properties in Core CSS Animation Fundamentals.

The problem

A search field expands and its corners soften from 8px to a pill shape. A hero image slowly pans with an animated background-position — a “Ken Burns” effect. An avatar morphs from a circle to a rounded square on hover. A card with rounded corners and overflow: hidden contains a carousel whose slides are animated with transform.

The first three animate paint properties directly. The fourth animates only a transform, but still stutters. Rounded corners and backgrounds have costs that are easy to miss because they do not look like “layout” properties.

Root cause analysis: shape and background are raster work

border-radius changes the geometry of the box’s background, border and shadow. Every frame, the browser re-rasterises those with the new curve. For a small button that is trivial; for a large panel with a shadow, the shadow blur has to be recomputed around the new shape too.

background-position and background-size change which image pixels land where. There is no pre-rasterised texture to slide — the background is re-painted every frame, including decoding-cache lookups and scaling for large images. A slow pan on a full-width hero repaints a full-width hero sixty times a second.

Rounded clipping is the hidden cost. An element with border-radius and overflow: hidden clips its children to a rounded rectangle. A rectangular clip is cheap for the compositor; a rounded clip generally requires a mask layer, and moving children inside it means the compositor blends through that mask every frame — in some engines forcing extra layers or even paint when children are promoted.

Borders that change colour or width repaint the element’s edges; width changes also trigger layout, as covered in the compositing tier reference.

Per-frame paint on a mid-range phoneMeasured per effect in isolation.Per-frame paint on a mid-range phonebudget 16.7 msborder-radius morph, 48px avatar0.3 msborder-radius morph, 600px panel with shadow7.4 msbackground-position pan, full-width hero11.8 msPan via translate on inner image0.2 ms
Measured per effect in isolation.

Step-by-step resolution

Finding substitutes for paint-heavy propertiesMatch the substitute to the visual intent, not the property name.Finding substitutes for paint-heavy properties1For background pans, move an inner img or pseudo-element with translate inside anoverflow-clipped box.The image texture slides on the compositor2For zoom effects, scale the inner element instead of background-size.No per-frame re-rasterisation3For radius morphs on small elements, keep the transition; it is cheap.No unnecessary complexity4For radius morphs on large elements, cross-fade two pre-shaped layers or use clip-path briefly.Paint once per shape5For rounded clip containers with moving children, apply radius to the children or clip only atrest.Avoids per-frame mask blending6Stop decorative pans under reduced motion.
Match the substitute to the visual intent, not the property name.

Production code pattern

<section class="hero">
  <img class="hero__image" src="coast.avif" alt="" width="1600" height="900">
  <div class="hero__content">…</div>
</section>
/* Ken Burns pan: move and scale the image element, not the background. */
.hero {
  position: relative;
  overflow: clip;                         /* rectangular clip: cheap to composite */
  aspect-ratio: 16 / 9;
}
.hero__image {
  position: absolute;
  inset: -4%;                             /* bleed so the pan never reveals an edge */
  inline-size: 108%;
  block-size: 108%;
  object-fit: cover;
  animation: drift 24s ease-in-out infinite alternate;
}
@keyframes drift {
  from { translate: -2% 0;  scale: 1; }
  to   { translate:  2% -1%; scale: 1.06; }
}

/* Avatar shape change: small enough that border-radius itself is fine. */
.avatar {
  border-radius: 50%;
  transition: border-radius 200ms ease-out;
}
.avatar:hover { border-radius: 22%; }

/* Rounded card with a sliding carousel: round the slides, keep the clip rectangular. */
.carousel {
  overflow: clip;                         /* no radius on the clipping element */
}
.carousel__slide {
  border-radius: 16px;                    /* the visual rounding lives on the slides */
}

@media (prefers-reduced-motion: reduce) {
  .hero__image { animation: none; translate: none; scale: none; }
  .avatar { transition: none; }
}

Rendering Impact: composite for the hero drift, small paint for the avatar. The hero image is rasterised once and moved and scaled as a texture; its decode happens before the animation starts. Moving radius off the clipping container avoids a rounded mask on every carousel frame.

Scaling the image upward over 24 seconds can soften it slightly, the raster-scale effect covered in fixing blurry text after transforms. For photographs a 6% zoom is invisible; for images with text or sharp lines, start at the larger scale and drift down.

Paint property and its compositor substituteSubstitutes need an extra element but remove per-frame paint.Paint property and its compositor substitutePer-frame tierSubstituteWorth converting whenbackground-positionpanPainttranslate on inner elementArea above a cardbackground-sizezoomPaintscale on inner elementAny large imageborder-radiusmorphPaintCross-fade pre-shaped layersLarge or shadowed boxesborder-colorchangePaintOpacity on an outline layerMany elements at onceRoundedoverflow clipMask per frameRadius on children insteadAnimated children inside
Substitutes need an extra element but remove per-frame paint.

Verification checklist

Constraints and trade-offs

  • Replacing a CSS background with an img changes semantics; use empty alt for decoration.
  • A bleed of a few percent is needed so translations never expose the container edge.
  • Moving radius from the container to children changes how borders and shadows on the container look.
  • clip-path with rounded inset() is another rounded clip and has similar costs when animated.
  • Very long, slow pans keep a layer promoted for the whole time; pause them when off-screen.

Frequently asked questions

Is animating border-radius expensive?

It repaints the element each frame. On small elements such as avatars and buttons the cost is negligible; on large panels, especially with shadows, it can take several milliseconds per frame.

How do I animate background-position without repaint?

Put the image on an inner element or pseudo-element inside an overflow-clipped container and animate its translate. The texture moves on the compositor.

Why does a rounded container slow down animations inside it?

Rounded overflow clipping usually needs a mask. Children that move inside it must be blended through that mask every frame, which rectangular clips avoid.