w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.47k stars 658 forks source link

[selectors] Add pseudo-classes for `<select>` being a drop-down box vs a list box #7422

Open zcorpan opened 2 years ago

zcorpan commented 2 years ago

See https://github.com/whatwg/html/issues/7050

Chromium supports a :-internal-list-box pseudo-class in the UA stylesheet, which matches <select> elements that render as a list box instead of as a dropdown box.

Gecko similarly supports :-moz-select-list-box in the UA stylesheet.

WebKit seems to use attribute selectors instead, although I think it's not entirely accurate since the decision per spec depends on the parsing result of the size attribute. (Demo: <select size="+02xyz">)

Web developers could rely on the multiple attribute being present to target list boxes, but that might not always work so well, in particular if you're writing CSS that should work for arbitrary HTML.

I think it makes sense to add a pseudo-class that matches select elements that are list boxes, e.g. :select-list-box. To match drop-downs, you can do select:not(:select-list-box).

Spec: https://drafts.csswg.org/selectors/

zcorpan commented 2 years ago

Is there interest in standardizing this pseudo-class (and make it also available to author CSS)?

cc @mfreed7

Via git blame of WebKit's html.css I found https://bugs.webkit.org/show_bug.cgi?id=220376 -- I think a pseudo-class as suggested here would be less prone to bugs like this (compared to attribute selectors). cc @pxlcoder

Via git blame of Gecko's html.css I found https://bugzilla.mozilla.org/show_bug.cgi?id=1643246 where attribute selectors were replaced with a pseudo-class. cc @emilio @heycam

mfreed7 commented 2 years ago

It certainly seems tricky to select this based on attribute selectors, so I'm supportive of trying to standardize a pseudo class.

Picking a name will be tricky. I'm not sure I like :select-list-box, since in my view, both in-page and pop-up <select>s contain a "listbox". One is just a pop-up listbox and the other is an in-page listbox. Perhaps something like :select-pops-up? Or at least something that refers to the pop-up behavior? Similarly, we need to be careful to distinguish whether this pseudo class refers to whether the listbox pops up or not (as I've assumed), or whether it refers to whether multi-select is supported. Those are (at least theoretically) distinct.

zcorpan commented 2 years ago

HTML's wording is "list box" for <select multiple> and "drop-down box" for <select>. However those terms are not web-exposed anywhere (prior to this pseudo-class), so they can be changed.

bradkemper commented 2 years ago

I believe the select element has an aria list box role either way, so that could be confusing.

zcorpan commented 2 years ago

How about "list box" and "drop-down list box"? (Not sure about "list box" vs "listbox".)

bradkemper commented 2 years ago

"Drop down" or "drop down list box" seem pretty clear (even though they are "pop up menus" on Mac). But just "list box" by itself is not clear, because I honestly wouldn't remember which one you meant without comparing it to the other term. I think if you had "multiple list box" (or similar*) and "drop down list box" as the two terms, then they'd both be clear, even in isolation.

zcorpan commented 2 years ago

It's not necessarily multi-select, though.

<select size=5>
  <option>this is still single-select list box
  <option>foo
  <option>bar
</select>
zcorpan commented 2 years ago

We could define only a pseudo-class for the drop-down, and rely on the :not() function to match the not-drop-down state.

select:drop-down-list-box {}
select:not(:drop-down-list-box) {}
dbaron commented 2 years ago

+1 to the idea of targeting the idea of having a dropdown. Perhaps something like :has-dropdown?

bradkemper commented 2 years ago

@zcorpan works for me. I also like the @dbaron refinement. Though there should probably be another dash, like :has-drop-down, so we don’t repeat the nowrap mistake.

Also, would a native date picker or color picker be considered a drop-down, like input[type=date]:has-drop-down?

bradkemper commented 2 years ago

I’m ambivalent about the extra dash, really.

zcorpan commented 2 years ago

Also, would a native date picker or color picker be considered a drop-down, like input[type=date]:has-drop-down?

Good question. I would say no. The date widgets are not guaranteed to have a drop-down or a popup, it would be conforming to have a control with only spinners, for example. The color well is specified to have a "color picker" that is separate from the color well, but nothing says it has to be a drop-down. Firefox on macOS opens a popup window for the color picker.

Since the need is specific to the select element, I think we should define it to only apply to select. Should the naming be different because of that?

zcorpan commented 2 years ago

A few names to consider

Example usage:

select:has-dropdown { ... }
select:not(:has-dropdown) { ... }

select:select-has-dropdown { ... }
select:not(:select-has-dropdown) { ... }

select:select-with-dropdown { ... }
select:not(:select-with-dropdown) { ... }

or without type selector (when not using :not())

:has-dropdown { ... }
select:not(:has-dropdown) { ... }

:select-has-dropdown { ... }
select:not(:select-has-dropdown) { ... }

:select-with-dropdown { ... }
select:not(:select-with-dropdown) { ... }

It is probably better in terms of ergonomics to have two pseudo-classes for the two states, e.g.:

Since it will only match select elements it seems reasonable to include it in the name, as people may expect :has-dropdown to apply to other things with dropdowns and :no-dropdown to apply to almost all elements.

mfreed7 commented 2 years ago

It seems to me that if this is only ever going to apply to <select>, then it should have "select" in the name. Otherwise, this feels confusing:

input[type=date]:has-dropdown { doesn't match, even when the date picker "drop down menu" is present }

As for :has-dropdown and :no-dropdown - isn't that exactly why we have :not(), to avoid the need for that?

I vote for :select-with-dropdown, because "with" indicates the <select> comes with a drop-down. Somehow (and I'm not sure why) but :select-has-dropdown sounds to me like it should apply only when the dropdown is open. Like it currently has a dropdown.

Interesting side reading on "drop-down" vs "dropdown": https://www.chicagomanualofstyle.org/qanda/data/faq/topics/HyphensEnDashesEmDashes/faq0153.html. Perhaps we should use "menu" instead?

zcorpan commented 2 years ago

I don't think "menu" distinguishes <select> vs <select size=5>. The name here is also "for developers", so we're good per the Microsoft Writing Style Guide. :-)

:not() indeed allows the same expressiveness, but it's a bit more cumbersome compared to using a direct pseudo-class. Compare with

:link { ... }
:visited { ... }

vs

:any-link:not(:visited) { ... }
:visited { ... }
tabatkins commented 2 years ago

+1 to "put select in the name" - it means we don't have to figure out whether this also applies to N other things, and verify if new things need to have it, possibly in only certain UAs, etc... Plus the general argument that if it only applies to 1 thing now, people will rely on that fact and use it on its own (without a tagname selector) with the intention of targeting selects specifically, and that code will then break in the future when we apply it to more. General policy: never use a generic name that could plausibly apply to many things unless there are at least three things that it already applies to.

Relying solely on :not() for the non-dropdown case is slightly unfortunate, as it requires you to use the tagname in the selector as well - select:not(:select-with-dropdown) - since just :not(:select-with-dropdown) will target random elements too. I think we should come up with a name for the other state. I think listbox is fine - even if it's a little unclear on its own, since it'll be contrasted with dropdown I think it's reasonable.

Or if we're pretty sure that select only has two states in this axis (which I think is a safe assumption, given 20+ years of history), :select-with-dropdown and :select-no-dropdown might work.

zcorpan commented 2 years ago

Since the dropdown menu is called a listbox in APG and Open UI, it's plausible people will assume listbox means the dropdown menu.

The <select> element certainly will not get new widget states. New widgets should be new elements.

zcorpan commented 2 years ago

So it sounds like :select-with-dropdown is the winner for the "with dropdown" state.

For the other state, we have:

In https://drafts.csswg.org/indexes/ I see several "no-" but no "without-", so the former seems more CSS-y and is also shorter.

Thus, I suggest we go with :select-with-dropdown and :select-no-dropdown.

bramus commented 2 years ago

In https://drafts.csswg.org/indexes/ I see several "no-" but no "without-", so the former seems more CSS-y and is also shorter.

Looking at that list, I see that these no- properties/values are (mostly) prefixed version of other ones that do not have a prefix. For example no-clip is the antonym of clip; not has-clip or with-clip.

Working my way back from the suggested select-no-dropdown, it should go hand in hand with select-dropdown then?

If select-dropdown does not make sense, and we do want to keep select-with-dropdown, its antonym – in similar fashion as clip/no-clip – should then become select-no-with-dropdown? From a linguistic POV the latter doesn’t make sense at all, and it would become select-without-dropdown.

tl;dr If we have select-with-dropdown, select-without-dropdown seems like a better opposite to me.

tabatkins commented 2 years ago

:select-dropdown strongly reads as selecting the dropdown itself, so I'd rule that out.

no-with-dropdown is definitely out, yeah. ^_^

I don't think we need to stick with pure English rules for this; we're not writing a sentence. no-wrap is similarly slightly awkward if read as normal English; it would be doesnt-wrap or something if we were shooting for that.

I think sticking with no-dropdown is fine/preferable, both for its length and its consistency.

nt1m commented 2 years ago

Might be good to reuse the well known CSS appearance values, as vocabulary:

select:menulist and select:listbox

zcorpan commented 2 years ago

I think only :listbox is not great because there are other listboxes other than "<select> as a listbox" (for example, the dropdown part of a dropdown <select> is also called a listbox). The appearance values are also legacy in favor of auto.

tabatkins commented 2 years ago

Yeah, and I wouldn't really consider them "well-known" either. I couldn't tell you what appearance values exist, for example, besides auto and button.

zcorpan commented 2 years ago

I should be able to remotely attend the TPAC CSS meeting either Thursday or Friday, but only before lunch. cc @astearns

bramus commented 2 years ago

Sidenote: If authors had the ability to numerically compare attribute values, they would be able to select those elements using this snippet:

select[size>1],
select[multiple] {
  /*🎉 */
}

This, in turn, would make a lovely custom selector as well:

@custom-selector :--with-listbox [size>1], [multiple];

select:--with-listbox {
  /*🎉 */
}
zcorpan commented 2 years ago

@bramus not sufficient for the UA stylesheet, at least, since browsers need to get cases like <select size="+02xyz"> correct.

zcorpan commented 2 years ago

@astearns is it possible to move this issue to some time before lunch (either Thursday or Friday)?

css-meeting-bot commented 2 years ago

The CSS Working Group just discussed Pseudo-classes for drop-down vs list box for <select>.

The full IRC log of that discussion <fantasai> Topic: Pseudo-classes for drop-down vs list box for <select>
<fantasai> github: https://github.com/w3c/csswg-drafts/issues/7422
<fantasai> zcorpan: Proposed pseudo-class for <select> elements
<fantasai> zcorpan: it's implemented in both Gecko and Chromium for their UA styles, under a vendor prefix
<fantasai> zcorpan: WebKit doesn't implement the pseudo-class, instead uses attribute selectors
<fantasai> zcorpan: and they have some bugs because of that, because hard to get the details right
<fantasai> zcorpan: so I think there's agreement in the issue that we should have this pseudo-class
<fantasai> zcorpan: and just need to settle thenaming
<emilio> q+
<zcorpan> :select-with-dropdown and :select-no-dropdown
<fantasai> zcorpan: :select-with-drop-down and :select-no-dropdown
<heycam> the WebKit UA style rules: https://searchfox.org/wubkat/rev/0c40ba62b482511fe03646f1d4982efd727475dd/Source/WebCore/css/html.css#1078-1092
<astearns> ack fantasai
<emilio> fantasai: haven't read the issue, was the bug in webkit about parsing the size attribute as int?
<fantasai> zcorpan: It's parsing the size attribute correctly, the UA style sheet has an approximation of what the HTML results are
<fantasai> zcorpan: so some edge cases will get wrong UA styles
<fantasai> zcorpan: get a drop-down widget, but UA style thinks it should be a list box
<fantasai> zcorpan: so a bit of a mismatch
<emilio> fantasai: asking because it's the way the ua parses integers not being replicatable in css
<emilio> ... maybe we want to introduce a way of selecting based on whether the attr is parsed correctly as an integer
<emilio> ... that's been requested and more generally useful
<heycam> q+
<fantasai> zcorpan: I guess it would be possible
<emilio> zcorpan: guess it would be possible
<fantasai> zcorpan: complication because HTML has multiple number attributes
<fantasai> zcorpan: It has integers and signed integers and floats
<fantasai> zcorpan: I guess CSS could have those as well
<fantasai> zcorpan: but CSS numbers are normally parsed differently from HTML numbers
<emilio> fantasai: we could make it the same or have a flag
<dbaron> "The display size of a select element is the result of applying the rules for parsing non-negative integers to the value of element's size attribute, if it has one and parsing it is successful. If applying those rules to the attribute's value is not successful, or if the size attribute is absent, then the element's display size is 4 if the element's multiple content attribute is present, and 1 otherwise." is the relevant quote from
<dbaron> https://html.spec.whatwg.org/multipage/#concept-select-size
<fantasai> zcorpan: Still seems more difficult to use as Web developer compared to pseudo-class
<fantasai> zcorpan: there's also the multiple attribute
<astearns> ack emilio
<fantasai> emilio: I don't think it's a bad idea, but do we really need 2 pseudo-classes instead of one?
<fantasai> emilio: would rather have single one, they're mutual exclusive
<fantasai> emilio: assuming it's <select>-specific
<fantasai> emilio: using select:listbox and select:not(listbox) would be nicer
<fantasai> zcorpan: I think that's an okay outcome, biggest difference is you need to include a type selector to match the :not() state, whereas with pseudo-classes can only use the pseudo-classes
<emilio> fantasai: we need to consider that we keep adding pseudo-classes
<emilio> ... if we don't really need a pseudo-class
<emilio> ... it's better not to have it
<emilio> ... so that there's less for people to memorize
<emilio> q+
<emilio> zcorpan: agreed
<astearns> ack heycam
<emilio> fantasai: if we can do this with integer parsing and it's not too tricky maybe that's a better way around it
<TabAtkins> In particular, if this is <select>-specific, then its name should be pretty <select>-specific, not generic. Probably means it needs a "select" in the name. But then it's just awkward to have to specify select again, like `select:not(:select-with-dropdown)`
<fantasai> heycam: I'm not strongly against adding pseudo-classes, but wondering if really that useful to expose to authors
<fantasai> heycam: if WebKit wants to fix compat issues, they can have an internal pseudo-class like other engines
<fantasai> heycam: but for authors, thinking whether they are using listbox or dropdown, is something they know ahead of time
<fantasai> heycam: I think it's rare for pages to switch between the two, so don't know how useful it is for authors to have different styles depending on this
<fantasai> heycam: or need to rely on the flexibility of how HTML parses these attributes, vs knowing through the DOM already
<fantasai> <fantasai> +1
<fantasai> zcorpan: for cases where dev controls both markup and CSS, but that's not always the case
<fantasai> zcorpan: maybe you're writing a stylesheet for a design system, want to apply to all WordPress pages
<fantasai> zcorpan: you don't know what HTML they will use
<astearns> ack emilio
<fantasai> zcorpan: so that's why pseudo-class makes sense
<fantasai> emilio: Was going to make similar comment to heycame
<fantasai> emilio: we should encourage use of multiple attribute, it's easy to write selectors for that
<fantasai> emilio: rather than relying on parsing of size attribute
<fantasai> emilio: if you're making design system, ask them to use that
<fantasai> emilio: but I'm on the edge on whether it's worth having a pseudo-class for this or not
<fantasai> zcorpan: multiple is a semantic difference
<fantasai> emilio: sorry, yes, you're riht
<fantasai> s/riht/right/
<fantasai> zcorpan: you can have a listbox that's not multiple
<emilio> fantasai: doesn't seem to me like the selector for this would be that difficult if we had number parsing
<emilio> ... is [multiple] plus size parsing as an integer >1
<emilio> ... the details of how to parse invalid size attributes is a concern for implementors but not authors
<fantasai> zcorpan: type out the two selectors?
<TabAtkins> `select[size > 1], select[multiple]`
<emilio> ... people usually don't write junk in their size attribute
<emilio> `select:is([multiple], [size]:not([size="1"]))` is probably close enough in practice today
<fantasai> `select:not([size>1],[multiple])`
<astearns> ack dbaron
<fantasai> dbaron: One, I tend to agree that if you're building a design system, you probably want these sorts of things
<fantasai> dbaron: I think part of the reason we don't see much demand is that select is so hard to style, don't run into this problem much
<fantasai> dbaron: if styling select worked more reliably, they would want on this more
<fremy> +1 to what dbaron just said, styling select is a pain
<fantasai> dbaron: Quoted HTML, which defines the display size of a select element
<fantasai> dbaron: condition for a combobox vs listbox is display size being equal to 1 or not
<fantasai> zcorpan: or multiple attribute being present or not
<fantasai> dbaron: There's a few reasons the selectors above are wrong
<fantasai> dbaron: one is that you need to test for size being zero
<fantasai> dbaron: because HTML applies rules for non-negative integers
<fantasai> dbaron: you want a valid size that is zero or 2 or more
<fantasai> dbaron: or the multiple attribute
<astearns> q?
<fantasai> dbaron: it's doable if you had HTML non-negative integer parsing
<fantasai> dbaron: but might be worth having this pseudo-selector anyway
<fantasai> TabAtkins: fantasai's point was that dealing with invalid things is the sort of things that UA needs to care about, but page author doesn't
<fantasai> TabAtkins: if they're putting random trashi in their size attribute, that's their problem
<heycam> +1 to if we do have a way to do this, would rather a specific pseudo-class rather than a general integer parsing selector
<fantasai> TabAtkins: but HTML UA needs to deal with all kinds of random trash
<fantasai> dbaron: As you go further down the stack to style sheets meant to apply to more and more pages, get closer to needing to match implementation
<fantasai> dbaron: bootstrap might get bugs on this
<fantasai> TabAtkins: Bootstrap can say, "don't write random trash in your size attribute and this will work correctly"
<fantasai> TabAtkins: if we were stricter in parsing this earlier we wouldn't have this problem for UAs
<fantasai> TabAtkins: but even widely-used library, nobody needs to deal with this much trash
<fantasai> TabAtkins: put a number here, and it works. Put a non-number here and it'll not work
<fantasai> fremy: Authors shouldn't have to read the spec to figure out the conditions
<fantasai> fremy: of course they can copy some weird code, but not very understandable
<fantasai> fremy: you will copy and paste it, but not clear what it does at first sight
<fantasai> fremy: if already implemented in engines, have this pseudo-class
<fantasai> fremy: that sounds useful so why not
<fantasai> fremy: why are we wasting time discussing if it's just removing a prefix in the code, seems silly
<astearns> ack fantasai
<fantasai> astearns: We started with what we called a pseudo-class, and now discussing maybe not having pseudo-class
<fantasai> astearns: with arguments presented so far, anyone convinced we should have it?
<fantasai> heycam: me
<fantasai> astearns: Should we try to resolve to add pseudo-class?
<fantasai> fantasai: I'd like to hear from Rachel Andrew and emeyer
<fantasai> fantasai: if they are like, "Yes, we should definitely have this!" then great
<emilio> FWIW this is how Gecko used to implement this with attribute selectors: https://hg.mozilla.org/mozilla-central/rev/13062d9979d254481851c0f8c9d9e608d91f6ae3
<fantasai> fantasai: If not, I'm skeptical
<rego_> there has been a lot of research about styling select in openUI, does anyone know if this topic was discussed?
<fantasai> emeyer: It's hard for me to say, a pseudo-class mostly feels correct here, but fantasai is correct, we have so many pseudo-classes now
<fantasai> emeyer: even more than I realized until recently
<fantasai> emeyer: So I'm not sure I'm 100% convinced that it's best
<fantasai> emeyer: Some way of doing this is good
<fantasai> emeyer: but whether it should be a pseudo-class or ???
<fantasai> fantasai: We would add number-based attribute selectors, and you would make a selector
<fantasai> astearns: It would be a copy-paste incantation
<fantasai> fantasai: still have to do that if it's a pseudo-classs!
<emilio> Another interesting edge case is `size=""`
<heycam> emilio, similar but not exactly the same as the current WebKit UA style rule selectors
<fantasai> TabAtkins: Who is asking for this? In the UA is one thing, but do other people need it?
<heycam> emilio, yes that is exactly the difference
<fantasai> zcorpan: reset stylesheets use it
<emilio> q+
<fantasai> zcorpan: incorrectly, usually
<fantasai> fantasai: They are copy-pasted, so they can write a proper selector
<astearns> ack emilio
<fantasai> zcorpan: but used in lots of pages, should get all the cases correct
<fremy> https://github.com/sansotmon/asistencia-salvarte/blob/50dde64556294e8e42dbb2f7950f1b9c078ebef7/web/core/themes/olivero/css/components/form-select.css
<TabAtkins> Importantly - if they're not getting it right, *is this causing problems for people*?
<fantasai> emilio: empty size attribute is another case
<fantasai> emilio: also slightly more efficient to do a pseudo-class
<fremy> ```[dir="ltr"] select[multiple] { padding-left: 9px }```
<TabAtkins> Are they getting bugs and ignoring them because parsing numbers via CSS is hard? Or are they just not getting bugs?
<fantasai> emilio: pre-parses faster than other random attributes
<fremy> ^ this is wrong, first hit on github
<fantasai> emilio: If ppl feel this is useful, then maybe it's not worth copy-pasting a weird incantation
<rachelandrew> q+
<astearns> ack rachelandrew
<fantasai> rachelandrew: I think in terms of being easier to explain and less error-prone, pseudo-class is good
<fantasai> rachelandrew: but do ppl actually want to do this, is also a valid concern
<fantasai> rachelandrew: if people aren't asking for it
<fantasai> rachelandrew: I like things to be straightforward
<fantasai> rachelandrew: but we also have a lot of pseudo-classes, and have to teach people
<fantasai> rachelandrew: so I don't have a strong feeling, but like emeyer I'm on the fence
<fantasai> astearns: Then what I want to do is to close off the discussion in this meeting, take back to issue, have whatever the actual correct integer and multiple check would be
<fantasai> astearns: so we have something to compare
<fantasai> astearns: and come back to this issue on a telecon
<fantasai> zcorpan: OK
<fantasai> astearns: any other questions on this?
<emilio> fantasai: do we want to add integer attr selectors?
<emilio> ... generally speaking, do we want a proposal for that?
<emilio> astearns: I dont' think we can decide that r/n
<fantasai> astearns: not decide right now
<fremy> select[multiple], select[size] { height: auto; }
<fremy> in bootstrap