Promoting Fixed and Sticky Elements
Part of Layer Promotion & will-change Strategy in Performance Budgeting & GPU Architecture.
The problem
An application has a fixed header, a sticky table header, a floating action button and a cookie banner pinned to the bottom. Scrolling is noticeably less smooth than on a page without them, and adding a frosted-glass effect to the header made it worse. Animating the header’s shrink-on-scroll effect seemed to make no difference at all — it was already slow.
Elements that stay in place while the page scrolls have a particular cost profile, and it is mostly not about their animations.
Root cause analysis: staying still while everything moves
Scrolling moves a layer. The page’s contents are composited, and scrolling translates them. An element that must stay in place while everything else moves cannot be part of that moving content, so browsers promote fixed elements — and sticky elements while they are stuck — to their own layers. The promotion is not something the author asked for and is usually the right decision: it lets scrolling happen on the compositor.
Each promoted element costs its area. A fixed header 64px tall on a 412px-wide phone at device pixel ratio 2.6 costs around 0.7MB — trivial. A full-viewport fixed overlay costs around 10MB, and a full-viewport fixed background image costs the same permanently, whether or not anything is animating, which is the arithmetic in texture memory budget for mobile.
backdrop-filter on a sticky surface is the expensive case. The filter must sample what is behind the element. While scrolling, the content behind changes every frame, so the blur is recomputed every frame over the element’s area. A frosted header across a wide viewport is one of the most expensive things a page can do during scroll.
Animating them is comparatively cheap. Because they are already promoted, a header that shrinks by scaling or translating costs almost nothing extra. A header that shrinks by animating height or padding costs layout for itself and everything below it on every frame — the usual property-choice problem, not a fixed-position problem.
They can promote their neighbours. Content that scrolls over a promoted sticky element may be promoted to stay above it, the cascade described in stacking contexts and accidental layer explosions.
Step-by-step resolution
Production code pattern
.site-header {
position: fixed;
inset-block-start: 0;
inline-size: 100%;
block-size: 64px;
z-index: 20;
background: color-mix(in oklch, var(--color-surface) 88%, transparent);
/* backdrop-filter: blur(12px); <- removed: re-blurs every scroll frame */
transform-origin: top center;
animation: header-shrink linear both;
animation-timeline: scroll(root block);
animation-range: 0 120px;
}
@keyframes header-shrink {
to { scale: 1 0.75; } /* scale, not height: no layout per frame */
}
.site-header__inner {
animation: header-counter linear both;
animation-timeline: scroll(root block);
animation-range: 0 120px;
}
@keyframes header-counter {
to { scale: 1 1.333; } /* counter-scale so the logo is not squashed */
}
/* Sticky table head: promoted only while stuck, and small. */
.table thead th {
position: sticky;
inset-block-start: 64px;
background: var(--color-surface);
}
@media (prefers-reduced-motion: reduce) {
.site-header, .site-header__inner { animation: none; scale: 1; }
}
Rendering Impact: composite. The header is already promoted because it is fixed, so scaling it adds nothing; the counter-scale keeps its contents undistorted. Removing
backdrop-filterremoves a per-frame blur over a wide, always-visible area.
Counter-scaling has limits: it corrects uniform distortion but not effects like border radius or shadows, which scale with the element. For headers that must genuinely change height rather than appear to, animate height and accept the layout cost, or cross-fade between two fixed-height headers.
Verification checklist
Constraints and trade-offs
- Counter-scaling cannot correct border radius, shadows or borders, which scale with the element.
- A translucent solid background is less visually rich than a blurred one.
- Sticky elements that are stuck for most of a page are effectively permanently promoted.
- Raising a header’s
z-indexto avoid promoting neighbours changes what can overlap it. - Fixed-position elements are affected by mobile browser chrome appearing and disappearing.
Frequently asked questions
Are fixed elements automatically promoted?
Browsers generally promote fixed-position elements, and sticky elements while they are stuck, so that scrolling can stay on the compositor.
Why is my frosted header expensive while scrolling?
backdrop-filter must re-sample and re-blur the content behind it, which changes on every scroll frame. Use a translucent solid colour on large sticky surfaces.
How should a header shrink on scroll?
With scale or translate, counter-scaling the contents if needed. Animating height or padding lays out the page on every frame.
Do sticky section headers cost a lot?
Individually they are small layers and cheap. Many of them at once add up, so keep their number and size in mind on long lists.
Related
- Layer Promotion & will-change Strategy — the parent topic
- Scroll-State Queries for Stuck and Snapped Motion — styling sticky elements when they stick
- When filter and backdrop-filter Are Worth the Paint — the cost of blur in detail