Skip to content

fix: insert position-area mapping styles into each target's own root - #446

Open
jpzwarte wants to merge 4 commits into
oddbird:mainfrom
jpzwarte:fix/444-position-area-on-host
Open

fix: insert position-area mapping styles into each target's own root#446
jpzwarte wants to merge 4 commits into
oddbird:mainfrom
jpzwarte:fix/444-position-area-on-host

Conversation

@jpzwarte

Copy link
Copy Markdown
Contributor

Fixes #444

position-area in a :host rule positions the shadow host, which lives in the outer tree, but the generated --pa-value-* mapping rule was inserted into the roots being polyfilled. A <style> in a shadow root never matches that root's own host, so the values stayed undefined and the host was left unpositioned.

Record the style container of each target while the rules are generated, and insert the stylesheet into those containers instead. Targets inside a polyfilled shadow root resolve to the same container as before.

`position-area` in a `:host` rule positions the shadow host, which lives
in the outer tree, but the generated `--pa-value-*` mapping rule was
inserted into the roots being polyfilled. A `<style>` in a shadow root
never matches that root's own host, so the values stayed undefined and
the host was left unpositioned.

Record the style container of each target while the rules are generated,
and insert the stylesheet into those containers instead. Targets inside a
polyfilled shadow root resolve to the same container as before.

Refs oddbird#444

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@netlify

netlify Bot commented Jul 25, 2026

Copy link
Copy Markdown

Deploy Preview for anchor-polyfill ready!

Name Link
🔨 Latest commit 9c989c6
🔍 Latest deploy log https://app.netlify.com/projects/anchor-polyfill/deploys/6a689f277cae980008308bea
😎 Deploy Preview https://deploy-preview-446--anchor-polyfill.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Jul 25, 2026

Copy link
Copy Markdown

Deploy Preview for anchor-position-wpt canceled.

Name Link
🔨 Latest commit 9c989c6
🔍 Latest deploy log https://app.netlify.com/projects/anchor-position-wpt/deploys/6a689f275eb5720008f2e111

@jpzwarte
jpzwarte marked this pull request as ready for review July 25, 2026 10:24

@jamesnw jamesnw left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a brief look so far. I would love a reproduction I can play with, as it isn't clear to me that this should work. I know anchor references can't cross shadow boundaries, but once an element has a default anchor, can it use it across shadow boundaries? Does that also work if you use anchor() without specifying an anchor?

Also, is this limited to position-area because we don't need to write custom styles for anchor-size() or anchor()?

Comment thread tests/e2e/shadow-dom.test.ts
@jpzwarte

jpzwarte commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

Just a brief look so far. I would love a reproduction I can play with, as it isn't clear to me that this should work.

I'm adding an example for this specific case. Without the fix you get this:

CleanShot 2026-07-27 at 12 01 19@2x

With the fix:

CleanShot 2026-07-27 at 12 04 00@2x

@jamesnw jamesnw left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this works. I've done a bit of thinking on whether this matches the spec.

In particular- https://drafts.csswg.org/css-anchor-position/#target:~:text=Note%3A%20An,them%2E%29

Note: An anchor-name defined by styles in one shadow tree won’t be seen by anchor functions in styles in a different shadow tree, preserving encapsulation. However, elements in different shadow trees can still anchor to each other, so long as both the anchor-name and anchor function come from styles in the same tree, such as by using ::part() to style an element inside a shadow. (Implicit anchor elements also aren’t intrinsically limited to a single tree, but the details of that will depend on the API assigning them.)

So :host{ bottom: anchor(--position-area-on-host top) } or :host{ position-anchor: --position-area-on-host } do not work. The example here works because we are setting a default anchor element with the style tag, which is in the same tree as where the anchor is defined.

If that matches your understanding, I think this makes sense, and there's just a few small changes. Thanks!

Comment thread src/transform.ts Outdated
// the tree of every element its rules match, as recorded while the
// rules were generated. Those are not always the roots being
// polyfilled: a `position-area` in a `:host` rule targets the shadow
// host, which lives in the outer tree. Fall back to the roots when

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the behavior we want? If there are no containers recorded, do we still need to add styles for this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

Comment thread src/utils.ts Outdated
// within its own tree, and the elements these rules target do not always live
// in the roots being polyfilled — a `:host` rule styles the shadow host,
// which sits in the *outer* tree.
containers?: Set<ShadowRoot | HTMLHeadElement>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was curious about making this required. It seems like the only change would be that transformCSS currently is removing containers from the returned styleData. I don't think we use it after transformCSS, but I think I'd prefer to have containers required on StyleData, and persist it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@jpzwarte

Copy link
Copy Markdown
Contributor Author

If that matches your understanding, I think this makes sense, and there's just a few small changes. Thanks!

It does match my understanding: :host { top: anchor(bottom) } works because the anchor is implicit; :host { top: anchor(--foo bottom) } doesn't because anchor-name: --foo is in a different tree.

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.

[BUG] position-area in a :host rule doesn't position the host

2 participants