Skip to content

Implement background-attachment: fixed - #646

Open
nicoburns wants to merge 1 commit into
devin/1786159426-bg-tiling-refactorfrom
devin/1786159806-bg-attachment-fixed
Open

Implement background-attachment: fixed#646
nicoburns wants to merge 1 commit into
devin/1786159426-bg-tiling-refactorfrom
devin/1786159806-bg-attachment-fixed

Conversation

@nicoburns

@nicoburns nicoburns commented Aug 8, 2026

Copy link
Copy Markdown
Member

Summary

Implements background-attachment: fixed (stacked on #645). For a fixed layer, the background positioning area becomes the viewport instead of the element's origin box, and the layer is drawn in viewport space (unaffected by any scrolling) while still being clipped by the element's background-clip box:

// ElementCx
fn fixed_positioning_area(&self, layer) -> Option<(Rect, Affine)> {
    // None unless layer.attachment == Fixed, or if the element is affected
    // by a CSS transform (fixed then degrades to scroll per css-transforms)
    Some((Rect(0, 0, viewport_w, viewport_h), Affine::translate(initial_offset)))
}

// in each of draw_raster_image_layer / draw_gradient_layer / draw_svg_image_layer:
let (origin_rect, base_transform) = self
    .fixed_positioning_area(layer)
    .unwrap_or((self.box_rect(layer.origin), self.transform));

ImageLayerStyles gains an attachment field (cyclic per layer, like the other background-* lists); mask layers hardcode Scroll as there is no mask-attachment property. Percentage background-position/background-size on fixed layers resolve against the viewport, which falls out of reusing the positioning-area rect.

local is not included: the servo-engine stylo build doesn't parse the local keyword yet (it's gecko-only in longhands.toml), so that needs an upstream stylo change first.

Verified with the screenshot example that a fixed gradient layer renders the viewport-anchored slice through the element's box (distinct from an identical scroll layer), and just wpt css/css-backgrounds shows zero status changes vs the base branch (the attachment reftests there require script-driven scrolling, which the WPT runner skips).

Link to Devin session: https://dioxus.staging.devinenterprise.com/sessions/adffb5f273074c819eda1794acd87032
Requested by: @nicoburns

WPT results

5 newly passing, 4 newly failing (net +1).

Full diff (9 changed tests)
+ Fail => Pass css/CSS2/backgrounds/background-attachment-applies-to-007.xht
+ Fail => Pass css/CSS2/backgrounds/background-attachment-applies-to-009.xht
+ Fail => Pass css/CSS2/backgrounds/background-attachment-applies-to-012.xht
+ Fail => Pass css/CSS2/backgrounds/background-attachment-applies-to-013.xht
+ Fail => Pass css/CSS2/backgrounds/background-attachment-applies-to-014.xht
- Pass => Fail css/CSS2/backgrounds/background-bg-pos-206.xht
- Pass => Fail css/css-transforms/transform-fixed-bg-005.html
- Pass => Fail css/css-transforms/transform-fixed-bg-008.tentative.html
- Pass => Fail css/cssom-view/add-background-attachment-fixed-during-smooth-scroll.html

Generated by the WPT workflow.

@nicoburns nicoburns self-assigned this Aug 8, 2026
@staging-devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant