fix: add patchCSSOMto support setting anchor positioning properties in JS - #447
fix: add patchCSSOMto support setting anchor positioning properties in JS#447jpzwarte wants to merge 14 commits into
patchCSSOMto support setting anchor positioning properties in JS#447Conversation
`anchor-name` and `position-anchor` assigned from JavaScript are dropped by the CSSOM in a browser without native support, so nothing lands in the `style` attribute the polyfill reads. The demo wires up its anchor at runtime the way a design system component would, and does not work as a result. Refs oddbird#445 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
✅ Deploy Preview for anchor-polyfill ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for anchor-position-wpt canceled.
|
jamesnw
left a comment
There was a problem hiding this comment.
Mostly conceptual questions at this point-
Idea- instead of writing the unsupported values to the style tag, could we shift the values to custom properties? I think that would sidestep a lot of this. We could do it like the shifted properties in cascade.ts, so like --anchor-name-${INSTANCE_UUID}, and then it might just work when we need to read it.
Does this change make it work better for Lit, for instance?
This is useful for non-shadow DOM things, correct? It might be worth not tying the examples together.
Would it be possible to expose this as an option when the polyfill is run, or does it need to be applied as a separate step, so that the application can do some work before the polyfill can be run?
| // patch it never sees anchors that are wired up from JavaScript. | ||
| const PATCHED_PROPERTIES = { | ||
| anchorName: 'anchor-name', | ||
| positionAnchor: 'position-anchor', |
There was a problem hiding this comment.
Why not positionArea or other anchor-related properties?
There was a problem hiding this comment.
Because anchor-name and position-anchor are the dynamic parts that i'm setting from Lit. Example:
<sl-button id="button">Button</sl-button>
<sl-tooltip for="button">Tooltip</sl-tooltip>Which results in:
<sl-button id="button" style="anchor-name: --sl-tooltip-1">Button</sl-button>
<sl-tooltip for="button" style="position-anchor: --sl-tooltip-1">Tooltip</sl-tooltip>Everything else is part of :host.
I could add more properties of course, but i'm not sure its worth it?
There was a problem hiding this comment.
Perhaps including at least position-area as well is a good idea. I can at least think of a web component where you could specify where it is anchored. But where do you stop?
There was a problem hiding this comment.
There are only a handful of new properties for anchor positioning, and I'd rather just support them all.
There was a problem hiding this comment.
See 5b315b0. This was more than just adding more properties to PATCHED_PROPERTIES:
patchCSSOM works by stashing whatever you assign into a private custom property, so el.style.positionArea = 'top' really lands in the style attribute as --position-area-<id>: top. Nothing else in the polyfill knows to look there — the two properties that already worked did so because two spots in the parser had been hand-taught to recognise that private spelling.
So rather than hand-teach five more spots (which would have made every author rule match twice, since the cascade leaves both spellings behind), this commit adds one step to cascadeCSS that renames the private property back to the real one before anything parses it; from there the CSSOM-set value is indistinguishable from CSS the author wrote, and every existing parser handles it for free.
The position-try properties also had to be added to the shifted list so their values survive being written back to the style attribute, and that in turn exposed a bug where transformCSS mangled that attribute whenever the polyfill generated a fallback rule for an inline style.
I think it's a matter of preference. If you run |
True, but where would we put an example for it? A new |
Done! |
jamesnw
left a comment
There was a problem hiding this comment.
This is looking pretty close, I think!
This is useful for non-shadow DOM things, correct? It might be worth not tying the examples together.
True, but where would we put an example for it? A new cssom.html page?
It can just be added to index.html. I know it's super long, and eventually it would be nice to have a nicer way to add demos.
| // patch it never sees anchors that are wired up from JavaScript. | ||
| const PATCHED_PROPERTIES = { | ||
| anchorName: 'anchor-name', | ||
| positionAnchor: 'position-anchor', |
There was a problem hiding this comment.
There are only a handful of new properties for anchor positioning, and I'd rather just support them all.
patchCSSOMto support setting anchor-name and position-anchor in JSpatchCSSOMto support setting anchor positioning properties in JS
Fixes #445
patchCSSOM()(opt-in, exported from/fn) makes every anchor positioning property the polyfill supports settable from JavaScript:anchor-name,anchor-scope,position-anchor,position-area, and theposition-tryproperties.position-visibilityis left out, as the polyfill doesn't support it. It defines each onCSSStyleDeclarationand stores the value in the custom property the polyfill shifts that declaration into internally — the browser can't drop a custom property, and it's already what the polyfill reads back.setProperty(),getPropertyValue()andremoveProperty()take the dashed names as well.cascadeCSSthen restores those declarations to the property they were set on, before anything parses them, so a value set through the CSSOM is indistinguishable from one written in a stylesheet. That's what makes the properties beyondanchor-name/position-anchorwork at all, and it keeps the CSSOM out of every parser — no special cases inparse.ts,position-area.tsorfallback.ts.'anchorName' in element.stylebecomes true once it has run.CSS.supports('anchor-name: --a')is unaffected, and is what the README now recommends for feature detection.Two bugs fixed along the way, both of which predate the CSSOM work:
fetchInlineStyles()only searcheddocument. It now searches the polyfill roots too — both, since a shadow-scoped run still needs its host and any light-DOM anchors.transformCSS()corrupted an element'sstyleattribute whenever the polyfill generated a fallback rule for that element's inline styles. It pulled the declarations back out with a fixed-length slice, which breaks as soon as anything else shares the block, writing a mangled selector into the attribute and losing the element's real styles. Generated rules now go into a stylesheet of their own. This was already reachable from plain HTML (<div style="position-try-fallbacks: --flip">), where the fallback silently never applied either.Adds demos to
index.htmlandshadow-dom.html.