background-attachment: fixed ignored when ancestor uses transform-style: preserve-3d
0 reputation · 30 Jun 2024, 06:50 UTC
0 reputation · 30 Jun 2024, 06:50 UTC
In a CSS3 layout, a section uses background-attachment: fixed on a large hero image. An ancestor element carries transform-style: preserve-3d to support a 3D card-flip effect deeper in the tree. Under that combination, the fixed attachment appears to be treated as scroll in some engines, so the background moves with the page instead of staying pinned to the viewport.
The CSS Backgrounds specification does not clearly resolve this dependency, and Blink, WebKit, and Gecko are reported to handle it inconsistently. Restructuring the DOM to move the 3D context elsewhere is possible but costly, since the card-flip component is reused across several templates. Assume current stable versions of Chrome, Firefox, and Safari.
Is this interaction between transform-style: preserve-3d and background-attachment: fixed defined anywhere in the CSS specifications, or is it engine-specific behavior? Is there a selector or containment strategy that keeps the 3D rendering context from affecting the fixed background without removing preserve-3d? Which engine behavior should be treated as the reference when filing a cross-browser interop issue?
31775 reputation · 30 Jun 2024, 10:52 UTC
This interaction is not defined in any CSS specification. CSS Backgrounds and Borders Level 3 defines background-attachment: fixed as positioning the background relative to the viewport, but it says nothing about how 3D transform contexts (transform-style: preserve-3d) affect that relationship. The Transforms spec, in turn, does not mention fixed backgrounds. You are in a genuine spec gap, and the engines have filled it differently — so the behavior you see is engine-specific, not a bug in your CSS.
Based on current stable behavior (verify against your target versions, since interop bugs in this area do get fixed):
preserve-3d as irrelevant to fixed attachment. This matches the plain reading of the Backgrounds spec.scroll when it lives inside a 3D rendering context, so it moves with the page.The likely explanation for Blink/WebKit is that preserve-3d forces a shared 3D rendering context and changes the compositing/paint path for the whole subtree, and the fixed-background compositing optimization is dropped along the way. That is an inference about implementation, not a confirmed fact — the confirmed part is only that the spec does not mandate either behavior and the engines disagree.
You do not have to remove preserve-3d from the ancestor. Three scoped options, in order of preference:
position: fixed; inset: 0; z-index: -1 element (or pseudo-element on a wrapper above the 3D context) carrying the hero image. This sidesteps the undefined interaction entirely and is the most robust across engines.contain: paint on the element that owns the fixed background. This creates an independent paint containment boundary and, in practice, restores fixed behavior in Blink/WebKit. Caveat: paint containment clips overflow and can affect absolutely positioned descendants and hit-testing, so check the hero section's children.transform: none on the background's subtree (i.e., ensure no transform or preserve-3d applies between the background element and the viewport). This only works if that subtree genuinely needs no 3D transforms — it will silently disable any 3D animation placed there later.<div style="transform-style:preserve-3d">
<div style="height:2000px">
<div style="height:500px;background:url(hero.jpg) fixed"></div>
</div>
</div>Scroll the page in each browser: if the image moves with the content, you are seeing the Blink/WebKit behavior. Then add contain: paint to the inner div and confirm the image stays stationary in all three engines.
None, formally — with no spec text, no engine is 'correct'. Practically, Firefox's behavior matches the literal wording of Backgrounds 3 (viewport-relative), so file the issue against the CSS Working Group (csswg-drafts) asking for the interaction to be defined, using Gecko as the proposed reference behavior, and cross-file against Blink/WebKit pointing at the spec issue. Include your reduced test case; interop issues with minimal reproductions get traction far faster.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.