Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[css-ui] Pseudo-elements for stylable select #10462

Open
josepharhar opened this issue Jun 17, 2024 · 10 comments
Open

[css-ui] Pseudo-elements for stylable select #10462

josepharhar opened this issue Jun 17, 2024 · 10 comments

Comments

@josepharhar
Copy link
Contributor

josepharhar commented Jun 17, 2024

Assuming that the shadowroot structure from #10380 is accepted, then I would like to propose pseudo-elements to target the elements inside the shadowroot. This will provide authors (and the UA stylesheet) the ability to style all of the content even without changing the markup to use any of the new content model (@brechtDR has mentioned this use case several times).

It would also be ideal for these pseudo-elements to target either the "fallback" element inside the UA shadowroot or the author provided element which gets slotted in depending on which one is rendered. For example, ::select-button could target <button pseudo="select-fallback-button"> in the UA shadowroot, or if the author provides a <button>, then the author-provided one instead. This has been requested in OpenUI but I haven't implemented it in the prototype yet because it is a bit harder to implement.

Here is the proposed structure from #10380 which I have implemented in the chromium prototype:

<select>
  #shadow-root
    <slot id="select-button">
      <button pseudo="select-fallback-button">
        <span pseudo="select-fallback-button-text"></span>
        <div pseudo="select-fallback-button-icon">
          <svg viewBox="0 0 20 16" fill="none"><path d="M4 6 L10 12 L 16 6"></path></svg>
        </div>
      </button>
    </slot>
    <slot id="select-datalist">
      <datalist pseudo="select-fallback-datalist">
        <slot id="select-datalist-options"></slot>
      </datalist>
    </slot>
</select>

Here is a list of pseudo-elements which would make all this content targetable:

  1. The button

We could have select::select-button which would target <button pseudo="select-fallback-button"> if the author doesn't provide a <button>, or the <button> if one is provided by the author.

  1. The fallback button's text

We could have select::select-fallback-button-text or select::select-button-text to target <span pseudo="select-fallback-button-text"> in the UA shadowroot. The UA stylesheet I've currently implemented applies a couple rules to this span. I'm not sure it makes sense to find a way to apply this to any author-provided elements.

  1. The fallback button's svg icon

We could have select::select-fallback-button-icon or select::select-button-icon to target <div pseudo="select-fallback-button-icon">. As with the fallback button text, I'm not sure that it makes sense to find a way to map this pseudo-element to any author provided elements.

  1. The datalist

We could have select::select-datalist to target <datalist pseudo="select-fallback-datalist"> if the author doesn't provide a <datalist>, or the <datalist> if the author provides one. Based on feedback from @annevk it sounds like this should be called ::picker(select) instead.

@annevk
Copy link
Member

annevk commented Jun 18, 2024

See #10440 for the ::picker pseudo-element idea.

@brechtDR
Copy link

See #10440 for the ::picker pseudo-element idea.

I like this ::picker idea. Sounds like this could open the possibility that @josepharhar is talking about. Especially handy in a situation where you can not reach the HTML but can only apply styles (this happens from time to time in agencies working with third parties).

And also, thinking a bit further, leaves possibilities open for (let's say....) a date picker in the future. (just as an example)

@josepharhar
Copy link
Contributor Author

As per this discussion: #10380 (comment)

I am trying to completely replace the select::select-button-icon pseudo-element with a UA style rule that looks like this:

select:open::select-fallback-button::after {
  content: counter(foo, disclosure-open);
}
select:not(:open)::select-fallback-button::after {
  content: counter(foo, disclosure-closed);
}

@josepharhar
Copy link
Contributor Author

@emilio brought up concerns regarding how it would be possible with these pseudos to allow multiple ways in CSS to target an element, and whether they should be exposed as pseudo elements or regular elements in which cases. This is possible if we go with the idea that I had in the issue description:

It would also be ideal for these pseudo-elements to target either the "fallback" element inside the UA shadowroot or the author provided element which gets slotted in depending on which one is rendered.

If you have the following markup:

<select style="appearance:base">
  <button>button</button>
</select>

Then the following two different selectors can target the author supplied button:

  1. select::select-button
  2. select > button

You could also use getComputedStyle to target this in two different ways, and emilio also mentioned getAnimations({subtree:true}). We have to make sure that in the case that the author doesn't supply a button, we avoid leaking the fallback button in the UA shadowroot to script.

Do people think this means that we should only support targeting the "fallback" button in the UA shadowroot via the pseudo-element? Do we need any particular kind of mitigation to support targeting either element?

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-ui] Pseudo-elements for stylable select.

The full IRC log of that discussion <gregwhitworth> jarhar0: we spoke about the shadowroot structure of the select element and we want to target them with the psuedo selects
<gregwhitworth> jarhar0: this is a proposal to use pseudo elements to target the fallback element or one that is supplied by the author
<gregwhitworth> jarhar0: I spoke with emilio about this before and it can target both author and shadow and CSS you can target the same element there was concern
<gregwhitworth> jarhar0: you can target them as pseudo elements and regular elements
<gregwhitworth> jarhar0: make it so that the pseudo elements don't target the author elements
<gregwhitworth> emilio: there are a fair amount of APIs that will need to cope with this and not make it really complicated and we'll get it wrong and things will be incosistent
<gregwhitworth> emilio: let's start with nothing too special or an alternative like animations don't expose nodes that are shadow parts
<gregwhitworth> emilio: we ideally would be able to solve that but if you're observing it from the outside it's a part, if you're in the tree you observe it like normal. We can do something like that
<ntim> q+
<gregwhitworth> emilio: we have a resolution to do more part() type psuedos and make them behave like shadow parts
<gregwhitworth> emilio: we don't expose a reference to them like other elements and then we can kick down the road to expose them in these APIs. Which honestly I've never seen anyone complain about that and you can put the datalist in the light tree if you want
<gregwhitworth> emilio: I haven't thought through all of the implications and I don't think we have a definition of this part "like" element for this
<gregwhitworth> emilio: effectively we won't hit that kind of problem
<ntim> q-

@josepharhar
Copy link
Contributor Author

One thing in favor of making the pseudo-element not target author provided elements I just thought of:

In the UA stylesheet, we have some rules which target either author provided buttons/options/datalists or the UA shadowroot ones, and other rules which only target the UA shadowroot ones. For example, there is a dropdown indicator on the UA shadowroot button which is being discussed here.

If we make select::select-button target either the UA or author button and we still want that dropdown rule to only apply to the UA button, then we would have to invent another type of internal pseudo-element which only targets the UA shadowroot button.

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-ui] Pseudo-elements for stylable select, and agreed to the following:

  • RESOLVED: Pseudo-element selectors apply only to the UA-provided elements in a particular role
The full IRC log of that discussion <gregwhitworth> q?
<keithamus> Both the pseudo elements for appearance base mode. Two issues, ???... should these elements cover the author provided thing or the button/datalist not be a target of the pseudo elements
<keithamus> The author provided button element; should the element target the author provided or just the fallback? There are trade-offs here.
<MikeSmith> RRSAgent, make minutes
<RRSAgent> I have made the request to generate https://www.w3.org/2024/08/08-css-minutes.html MikeSmith
<gregwhitworth> q+
<keithamus> ... Could be developer convenience but could have complications. How does it work with animations, subtree, etc.
<keithamus> ... There might be styles we could fall back but not both, so we'd need to invent some internal pseudo element to target the fallback
<keithamus> ... Might be better for developers if it targets both
<keithamus> s/Both the/jarhar: Both the
<gregwhitworth> ack gregwhitworth
<keithamus> gregwhitworth: when you go to change it, its no longer the element. I don't know the technical reasons emilio is pushing back on, but as a dev when I replace it I now own that element so wouldn't expect pseudo to work.
<gregwhitworth> ack fantasai
<keithamus> fantasai: we have several elements that replace default; button, dropdown, I think we have to think about when a dev is styling they might not know exactly how the control is put together.
<keithamus> ... It might be implemented as native for a while, upgraded to datalist, you didn't change your stylesheets, you're using a component library...
<keithamus> ... I can definitely see why you'd want them to match. The other technical concerns are certainly real.
<emilio> q+
<keithamus> ... From authoring it would be more convenient and more robust.
<gregwhitworth> ack emilio
<keithamus> emilio: I think I get both points. As author, I agree with Greg but if you have more generic styles it can be nice to have a single selector. As a component library, presumably they'd export the dropdown in some other way e.g. shadowpart or shadowdom so you cannot target directly yourself.
<keithamus> ... Given theres no precedent. Given the complications; like we have a bunch of APIs for exposing pseudo element stuff. Eg getanimations with subtree true returns pseudo element animations, but it would be weird to behave that way... you kind of want the element reference if you have the pseudo but can't do that with shadow root
<keithamus> ... Causes inconsistencies with various APIs like this.
<gregwhitworth> ack fantasai
<keithamus> ... Plus there are usecases for e.g. targeting built in UA one. It's just less complicated to explain if you use pseudo to match the provided, and otherwise not.
<emilio> q+
<keithamus> fantasai: I meant component library in a much more general sense, like not just web components but CSS libraries or something
<gregwhitworth> ack emilio
<fantasai> s/something/templates or something/
<keithamus> emilio: but then changing implementation from built-in datalist to custom one is a breaking change but no more changing than, for example, changing a button to a link
<keithamus> ... if you're using a 3rd party library you need to be mindful of these changes and breaking the API
<keithamus> jarhar: sounded like people are in favor of both sides?
<keithamus> gregwhitworth: We could straw poll or take it back to the github issue.
<keithamus> fantasai: from the agenda it looks like we have this one issue which is a superset of lots of small issues. Can we split this up?
<keithamus> gregwhitworth: you want to tackle this as 4 separate ones?
<keithamus> fantasai: if they're independently resolvable filing separately makes it easier to focus on each one
<keithamus> chrishtr: we could resolve on one of those
<keithamus> gregwhitworth: would we want to have a default position?
<keithamus> ... if an element gets replaced, the pseudo element being applicable irregardless of which one it is - do we want to resolve on that first?
<keithamus> jarhar: that's one of the three.
<keithamus> fantasai: I'd agree we should be consistent
<keithamus> ... as for which way, I feel like we've heard from two or three people
<keithamus> jarhar: I think we discussed this in openui before, a vague memory of people preferring the ???
<keithamus> q+
<gregwhitworth> keithamus: if we allow the pseudo's to target the author provided is the way to differentiate between author and useragent provided?
<fantasai> ::button:not(button)
<gregwhitworth> fantasai: yes, because you could...
<gregwhitworth> ^ typed out her example
<gregwhitworth> keithamus: is that more difficult than than the reverse
<gregwhitworth> keithamus: if I want to select for the built-in verse user provided; which one is more complicated
<emilio> q+
<gregwhitworth> q+
<gregwhitworth> ack keithamus
<keithamus> emilio: I think especially since you cannot use pseudo elements in :not()... I think fantasai's suggestion might not work without changes to pseudo syntax.
<keithamus> ... we don't reveal tag name of the pseudo element. You'd need select:has(datalist:thepsuedo) which is kind of annoyin
<keithamus> s/annoyin/annoying
<gregwhitworth> q?
<emilio> `select:has(> button)::button` or so
<gregwhitworth> ack emilio
<emilio> ack emilio
<keithamus> chrishtr: so this would be in favour of not ???. Like reducing complexity
<keithamus> chrishtr: if there's additional engine complexity in trying to match both at the same time, let's go with just the built-in. Let's developers in a straightforward way to differentiate and avoids complexity in the engine
<gregwhitworth> ack gregwhitworth
<keithamus> gregwhitworth: curious; two jobs to be done: when would I really want to differentiate? To interrogate if there's a user provided vs user-agent provided element.
<emilio> q+
<keithamus> ... I get fantasai's use case. The component library, there's a contract there... but its low probability. What's the use case though keithamus?
<gregwhitworth> keithamus: I don't think I have a good answer to that to be honest
<gregwhitworth> ack dbaron
<keithamus> dbaron: if we go down the path of making pseudo only match built-in, if the author provided ones are in some cases - I don't know how complex - but if they're complex they could have a pseudo class, to match an existing element. A pseudo class is a thing that matches what you already have, vs a pseudo class to match an element that exists in the UA
<keithamus> shadow. One way to do it.
<gregwhitworth> ack emilio
<keithamus> emilio: I dont think the rules here are particular complicated. You need to be a direct child of the select element, so I don't think the pseudo class is necessary. The reason for interrogating if it's a built-in, if you're not a built-in you can do more complex styling of the innards. If you have a big CSS codebase and 2 ways of addressing the
<keithamus> same thing in subtly different ways, it's not amazing but you could want
<keithamus> ... to target the built-in with basic styles and non-built-in for more complex styles. If the pseudo matches both you need to undo the pseudo rules with non pseudo selectors.
<jarhar> q?
<chrishtr> +1, good point
<fantasai> Option 1: Pseudo-element selectors apply to both the user-provided and UA-provided elements in a particular role
<keithamus> gregwhitworth: two foundational positions for this base one that informs the 3 other issues. User submitted will or won't apply. Should we take this back to the issues? I feel like I'm in favour of _not_ having them to apply to user-provided. Is there a strong reason to go in the other direction?
<fantasai> Option 2: Pseudo-element selectors apply only to the UA-provided elements in a particular role
<keithamus> ... does anyone have a strong position
<fantasai> Option 2.1: Also provide a pseudo-class to select to user-provided (and UA-provided?) elements in a particular role.
<keithamus> ... does anyone oppose doing a straw poll?
<keithamus> fantasai: I didn't fully catch what dbaron's position on pseudo classes was.
<keithamus> jarhar: I think it could differentiate what the element was selecting
<fantasai> 1.1: And also provide a pseudo-class on differentiating whether it's a real or pseudo element.
<keithamus> dbaron: you could do either way! But perhaps it's better to leave the sub-options out of the straw poll.
<chrishtr> option 2
<emilio> 2
<jarhar> 2
<ntim> 2
<keithamus> gregwhitworth: for straw poll we'll just do the main options. Please put opion 1 or option 2 in IRC
<fantasai> 0
<keithamus> 2
<gregwhitworth> 2
<astearns> abstain
<dbaron> 2 (weakly)
<fantasai> s/0/abstain
<fantasai> RESOLVED: Pseudo-element selectors apply only to the UA-provided elements in a particular role
<emilio> q+
<gregwhitworth> ack emilio
<keithamus> emilio: for the library use case, I think we resolved on the pseudo element kind of a part-like pseudo. If we were really concerned about providing the same API as a component library we could make exportparts work to expose the inner pseudo elements. Then you could expose the same API in both
<keithamus> gregwhitworth: does this resolve the other issues jarhar?
<keithamus> jarhar: I think it could be worth raising 1 new issue for each pseudo, asking should it exist and what should it be named. WIth that in mind we can close this issue
<keithamus> chrishtr: what are the three issues?
<keithamus> ... 1 is the one we resolved
<keithamus> ... 2 is do we need them
<keithamus> ... 3 is what are their names?
<keithamus> jarhar: yes
<keithamus> chrishtr: are we ready to discuss 2 or 3 yet?
<keithamus> gregwhitworth: yes I think it's a bookkeeping issue
<keithamus> chrishtr: jarhar could make issues after the meeting and copy the resolutions

@josepharhar
Copy link
Contributor Author

I filed a separate issue for the button here: #10717

@annevk
Copy link
Member

annevk commented Aug 13, 2024

Can someone explain the issue with it matching both in this issue? What is the problem #10462 (comment) hints at with getComputedStyle() and getAnimations()?

I suppose this doesn't really prevent anyone targeting arbitrary select elements from style sheets, but it will make those style sheets quite a bit more involved to write as you will have to account for both ::button and select > button, etc.

@josepharhar
Copy link
Contributor Author

josepharhar commented Aug 14, 2024

Can someone explain the issue with it matching both in this issue? What is the problem #10462 (comment) hints at with getComputedStyle() and getAnimations()?

I think that emilio said it has to do with the fact that there are two different ways to target the element in CSS and that some of them might want to return it as a pseudo element in some cases to avoid leaking shadow tree stuff, and that we want to return it as a proper element in other cases, and there is overlap between them.

I also think that this whole issue might be obsolete if we:

  1. Remove the author-provided datalist: [select] Removing the capability for the author to provide a datalist element openui/open-ui#1082
  2. Remove the fallback button or the pseudo-element for it: [css-ui] Pseudo-element for select's UA button #10717 (comment)

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

No branches or pull requests

5 participants