Problem
There's no recipe for the classic "hero with a real <video> (or <img>) as the background" pattern — where the media is an actual DOM element (not a CSS background-image), an overlay/gradient sits on top of it for legibility, and the section's real content sits above both without the author manually wiring z-index.
Today an author has to hand-assemble three things themselves:
<section style="position: relative">
``<video class="sf-overlay" autoplay muted loop src="/hero.mp4">``</video>
<div class="sf-overlay" style="background: var(--sf-scrim-gradient)"></div>
<div style="position: relative; z-index: 1"> <!-- ← this z-index is the manual step -->
<h1>Hero content</h1>
</div>
</section>
.sf-overlay (core/layout.css, position: absolute; inset: 0) already solves positioning the video and the gradient div. Nothing solves the third line — stacking real content above them — automatically.
Not the same as .sf-surface-bg (#523 / #559)
.sf-surface-bg (landing via #559) solves a different, CSS-only case: a base color + background-image (photo/gradient/pattern) + an overlay, all composed as two layers of one background-image property on a single element — see the worked example in the discussion above. Because it's pure background-*, there is no separate element and therefore no stacking problem to solve: content in normal flow already paints above a background-image for free.
That composition cannot hold a real <video> element (autoplaying/looping video isn't expressible as a CSS background), and it does nothing for the "auto z-index my content" problem this issue is about. The two issues are complementary, not duplicates:
|
.sf-surface-bg (#523/#559) |
This issue (#489) |
| Media type |
CSS background-image only |
Real <img>/<video> element |
| Overlay |
2nd background-image layer |
Separate overlay element (like .sf-overlay) |
| Content stacking |
Automatic (nothing to do — it's just a background) |
Manual today — this issue closes that gap |
.sf-scrim is the closest existing precedent for "automatic content stacking": it already does position: relative; z-index: 1 on > :not(img, picture, video, svg, canvas) / .sf-scrim__content — but .sf-scrim doesn't position a background media element for you; it assumes you already have one in flow (e.g. an <img> as a normal child, not absolutely positioned as a full-bleed background).
Scope
- A macro/recipe (e.g.
.sf-media-bg — naming TBD) that, applied to a container holding a background <img>/<video> element (positioned via .sf-overlay or equivalent) plus a content element:
- Automatically stacks the content above the media + overlay via
z-index (reusing --sf-z-overlay or a dedicated token — no new token group), matching .sf-scrim's existing auto-stack pattern.
- Supports an optional overlay layer (reuse
--sf-scrim-gradient/--sf-scrim-color — don't reinvent overlay tokens).
- Document in
docs/macros.md with a full HTML usage example (like the one above, minus the manual z-index).
- Note in the docs how this differs from
.sf-surface-bg (CSS-background-only) so users pick the right one.
Acceptance criteria
Problem
There's no recipe for the classic "hero with a real
<video>(or<img>) as the background" pattern — where the media is an actual DOM element (not a CSSbackground-image), an overlay/gradient sits on top of it for legibility, and the section's real content sits above both without the author manually wiringz-index.Today an author has to hand-assemble three things themselves:
.sf-overlay(core/layout.css,position: absolute; inset: 0) already solves positioning the video and the gradient div. Nothing solves the third line — stacking real content above them — automatically.Not the same as
.sf-surface-bg(#523 / #559).sf-surface-bg(landing via #559) solves a different, CSS-only case: a base color +background-image(photo/gradient/pattern) + an overlay, all composed as two layers of onebackground-imageproperty on a single element — see the worked example in the discussion above. Because it's purebackground-*, there is no separate element and therefore no stacking problem to solve: content in normal flow already paints above abackground-imagefor free.That composition cannot hold a real
<video>element (autoplaying/looping video isn't expressible as a CSS background), and it does nothing for the "auto z-index my content" problem this issue is about. The two issues are complementary, not duplicates:.sf-surface-bg(#523/#559)background-imageonly<img>/<video>elementbackground-imagelayer.sf-overlay).sf-scrimis the closest existing precedent for "automatic content stacking": it already doesposition: relative; z-index: 1on> :not(img, picture, video, svg, canvas)/.sf-scrim__content— but.sf-scrimdoesn't position a background media element for you; it assumes you already have one in flow (e.g. an<img>as a normal child, not absolutely positioned as a full-bleed background).Scope
.sf-media-bg— naming TBD) that, applied to a container holding a background<img>/<video>element (positioned via.sf-overlayor equivalent) plus a content element:z-index(reusing--sf-z-overlayor a dedicated token — no new token group), matching.sf-scrim's existing auto-stack pattern.--sf-scrim-gradient/--sf-scrim-color— don't reinvent overlay tokens).docs/macros.mdwith a full HTML usage example (like the one above, minus the manualz-index)..sf-surface-bg(CSS-background-only) so users pick the right one.Acceptance criteria
<video>/<img>-background section auto-stacks real content above it — no manualz-indexneeded.sf-surface-bg