- From: CSS Meeting Bot via GitHub <noreply@w3.org>
- Date: Wed, 11 Feb 2026 17:50:27 +0000
- To: public-css-archive@w3.org
The CSS Working Group just discussed `[css-values] attr() and part() / slotted() / element backed pseudo-elements behave inconsistently.`, and agreed to the following:
* `RESOLVED: all pseudo-elements other than ::slotted look at the originating element for attr(), sibling-index(), etc.`
<details><summary>The full IRC log of that discussion</summary>
<TabAtkins> emilio: this is about attr(). I think i'm ok with current behavior, but it's very subtle. not sure i'd expect it as an author<br>
<TabAtkins> emilio: I think id' expect if I used ::part(), attr() in a property would work the same as on a ::before pseudo<br>
<TabAtkins> emilio: so two parts<br>
<TabAtkins> emilio: for pseudo-elements where we're allowed to query the computed style, that exposes the internal state of the element, right? I don't think we want<br>
<TabAtkins> emilio: and for ::part() I'd kinda expect it [???] to depend on where you specified it, but it's kinda annoying<br>
<TabAtkins> TabAtkins: if you use attr() in a ::before, it takes the attribute from the originating element. But if you use it in a ::part() rule, it takes it from the actual part backing element<br>
<JakeA> q+<br>
<TabAtkins> emilio: if you do `foo::part() { width: attr() }` that uses the value of the backing element's attr<br>
<TabAtkins> emilio: so that has the same issue as part-like pseudo-elements<br>
<noamr> q+<br>
<astearns> ack florian<br>
<TabAtkins> emilio: and I know for part-like pseudos we dont' want that<br>
<astearns> ack flackr<br>
<TabAtkins> TabAtkins: [asks for clarification]<br>
<TabAtkins> emilio: right now, *other* element-backed pseudo-elements take attr() from the originating element, not the backing element<br>
<astearns> ack JakeA<br>
<TabAtkins> JakeA: it seems bad that attr() in a light Dom stylesheet can pull attrs off of a clsoed shadow root<br>
<TabAtkins> emilio: current spec, yes, that's what will happen for ::part()<br>
<TabAtkins> JakeA: right, doesn't seem in the spirit of shadow<br>
<TabAtkins> emilio: so the question is, should we make ::part() property resolution depend on where the stylesheet is from, like tree-scoped names?<br>
<TabAtkins> emilio: it'd be annoying impl-wise<br>
<TabAtkins> emilio: because then an attr() change higher in the tree can affect a bunch of stuff further down<br>
<astearns> ack noamr<br>
<TabAtkins> noamr: I think, what Jake said aside, I think the current behavior makes sense. you can use custom proeprties to get the other behavior, setting it on the root and inheriting it into the part()<br>
<iank_> astearns: I was wrong - more inconsistent than i remembered: https://www.software.hixie.ch/utilities/js/live-dom-viewer/?saved=14517<br>
<emilio> q+<br>
<TabAtkins> noamr: the other behaviors are because the pseudoElement doens't *have* attributes<br>
<TabAtkins> noamr: also wondering, in a closed shadow since you're exposing a ::part() there might be other ways to expose that part<br>
<TabAtkins> emilio: not really, ::part() is kinda the only way to do this<br>
<JakeA> q+<br>
<TabAtkins> noamr: similarly to other element-backed things that don't have the shadow root thing.... I think it's ok that it's not consistent. I don't think consistency matters as much here.<br>
<astearns> ack emilio<br>
<astearns> ack JakeA<br>
<TabAtkins> JakeA: we have other pseudo-elements like ::file-selector-button, targeting a button in the input<br>
<TabAtkins> JakeA: if the shadow has an attribute, do we read from the UA shadow DOM?<br>
<TabAtkins> JakeA: because if not, I think ::part() should be consistent<br>
<TabAtkins> emilio: I think the element-based pseudos do use magic attributes in blink, so you'd be able to read those. would be unfortunate<br>
<TabAtkins> emilio: on the one hand for all these customizable things, the intention is that the tree shape is well-defined<br>
<TabAtkins> emilio: maybe attributes shoudln't.... I know we put aria attributes or tabindex on some input parts<br>
<JakeA> q+<br>
<TabAtkins> emilio: it feels like, from the pov of authors, when you're authoring a web component you don't think the attributes of those nodes are part of your external API. you expose the styling of a part with ::part(), but not the tohers<br>
<astearns> ack JakeA<br>
<TabAtkins> emilio: so I think the current behavior is pretty broken, we should make them all consistent and take from the originating element<br>
<TabAtkins> JakeA: there's also things like sibling-index()...<br>
<TabAtkins> emilio: right, they expose details that maybe shouldn't be<br>
<TabAtkins> astearns: i'm a little unclear with whether the problems with the shadow root is really this issue, or a separate one<br>
<TabAtkins> emilio: it's all the same underlying<br>
<TabAtkins> emilio: the OP just had an example with ::part() to highlight the inconsistency<br>
<TabAtkins> emilio: I think ::part() should behave the same as the other element-backed pseudos<br>
<lea> q?<br>
<lea> q+<br>
<TabAtkins> emilio: on the technical side... I think we could invent a similar concept to tree-scoped names here for attr(), so attr() lookup depends on the tree scope it's being styled from<br>
<astearns> ack lea<br>
<astearns> q+ TabAtkins<br>
<TabAtkins> lea: I think we should decide on these separately, I think there's different concerns for ::part() vs ::slotted()<br>
<TabAtkins> lea: ::part() encapsulation makes more sense, but ::slotted() elements are in the light dom<br>
<TabAtkins> emilio: agree they're different<br>
<TabAtkins> emilio: ::slotted(), depending on how we define this, would look at the real attribute<br>
<lea> q+<br>
<TabAtkins> emilio: so the q is whether we're okay with this weird case even tho it exposes more of the shadow tree than we want<br>
<TabAtkins> emilio: noam's right that you can workaround with a custom property in some cases<br>
<fantasai> scribe+<br>
<TabAtkins> astearns: I think authors already have the practice of putting things in custom properties that they want to expose, might be surprising that attr() pulls from the root as well<br>
<astearns> ack TabAtkins<br>
<fantasai> TabAtkins: First, if we defined something like "tree-scoped name" behavior, I agree that ::slotted() would pick up the attributes from the pseudo's own element, since it's in the right tree<br>
<noamr> q+<br>
<fantasai> TabAtkins: I would like attr() and things like sibling-index() to be consistent<br>
<keithamus> q+<br>
<fantasai> TabAtkins: They are consistent for normal pseudos.<br>
<astearns> zakim, close queue<br>
<Zakim> ok, astearns, the speaker queue is closed<br>
<fantasai> TabAtkins: But I wouldn't like it if styles on ::part() sometimes exposed details of shadow tree and other times didn't<br>
<fantasai> TabAtkins: Don't have a strong opinion on which way<br>
<emilio> !+<br>
<emilio> +1*<br>
<astearns> ack florian<br>
<astearns> ack fantasai<br>
<TabAtkins> fantasai: I can't speak for impl, but I think I agree with Lea that ::slotted() and ::part() are different here<br>
<TabAtkins> fantasai: if w elook at ::part() as the ability of the author to create a pseudo, same as the UA pseudos, it would be inconsistent with that mental model for it to take attributes/etc from anything other than the originating element<br>
<TabAtkins> fantasai: so if that's the model we should be consistent with UA pseudos<br>
<astearns> ack lea<br>
<TabAtkins> lea: to Tab, consistency is good, but matching author expectation is more important when they differ a lot<br>
<TabAtkins> lea: whatever we do for attr() we should indeed do for sibling-index(), those should be consistent between them<br>
<TabAtkins> lea: my observation is...[missed]<br>
<TabAtkins> lea: that authors today expose elements via ::part() by default if they don't have a reason to not do so<br>
<TabAtkins> lea: you need shadow DOM to use a lot of other ruseful features, like slots<br>
<TabAtkins> lea: so they're often not using encapsulation, instead the other features<br>
<emilio> q+<br>
<TabAtkins> lea: wonder if we could use the open/closed tree as the marker here<br>
<TabAtkins> lea: if you have a closed root it would be weird to leak when you can't get the info elsewhere<br>
<TabAtkins> lea: but with an open, it's weird to not expose, since you *can* get the information<br>
<TabAtkins> lea: using this would also help browser vendors, since they use closed shadows for UA shadow trees<br>
<astearns> ack noamr<br>
<TabAtkins> lea: a bit to the side, I think part and slotted are fundamentally broken concepts, maybe element-backed pseudos entirely<br>
<TabAtkins> noamr: i'm convinced that we should go by the main element, not the internal part. the embedder doens't need to know what attrs the part ahs, what siblings it has, etc<br>
<TabAtkins> noamr: whoever's embedding the element shoudln't need to know it's element-backed and where it is in the tree<br>
<astearns> ack keithamus<br>
<lea> s/ but with an open, it's weird to not expose, since you *can* get the information/ but with an open shadow root, it's defensible to expose this information, since you already have access to it via DOM methods/<br>
<TabAtkins> keithamus: I agree we shouldn't be changing the beahvior of a selector based on open or clsoed<br>
<TabAtkins> (personally I think the eopen/closed is a possibly good idea)<br>
<TabAtkins> keithamus: it seems to me that all pseudos should refer to their originating element for these things<br>
<lea> +1000 keithamus<br>
<TabAtkins> keithamus: with the possible exception for ::slotted(), hints we should have had a slotted combiantor<br>
<TabAtkins> (yeah ::slotted() is broken for a few reasons, the combinator didn't sail at the time)<br>
<JakeA> agreed<br>
<noamr> +1<br>
<TabAtkins> keithamus: like, i'm not sure I really understand the difference between element-backed and the rest. they should all act the same.<br>
<fantasai> +1<br>
<fantasai> PROPOSED: ::part() looks at the originating element for attr(), sibling-index(), etc.<br>
<TabAtkins> astearns: can we resolve don only taking from originating element?<br>
<TabAtkins> TabAtkins: i'm cool with that as a resolution<br>
<fantasai> alternatively, s/::part()/all pseudo-elements other than ::slotted/<br>
<lea> q?<br>
<TabAtkins> emilio: the details needs to be a bit more subtle<br>
<TabAtkins> astearns: objections?<br>
<TabAtkins> lea: can we record that this is another reason to look at a slotted combinator?<br>
<lea> slotted combinator issue: https://github.com/w3c/csswg-drafts/issues/7922<br>
<fantasai> PROPOSED': all pseudo-elements other than ::slotted look at the originating element for attr(), sibling-index(), etc.<br>
<TabAtkins> keithamus: I'd also like to ensure we get fantasai's correction, all pseudos other than ::slotted<br>
<fantasai> RESOLVED: all pseudo-elements other than ::slotted look at the originating element for attr(), sibling-index(), etc.<br>
</details>
--
GitHub Notification of comment by css-meeting-bot
Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/12721#issuecomment-3885982946 using your GitHub account
--
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config
Received on Wednesday, 11 February 2026 17:50:28 UTC