Skip to content
This repository has been archived by the owner on May 28, 2024. It is now read-only.

Switch syntax from :state(foo) to :--foo #6

Closed
tabatkins opened this issue Mar 4, 2020 · 20 comments · Fixed by #9
Closed

Switch syntax from :state(foo) to :--foo #6

tabatkins opened this issue Mar 4, 2020 · 20 comments · Fixed by #9

Comments

@tabatkins
Copy link

This was raised by @plinss in the CSSWG discussion last week.

The point of the state pseudoclass is to expose a class-like semantic via a pseudoclass. It's currently "namespaced" under the :state() function to avoid collisions with other pseudoclass names. However, the way we've avoided such clashes in other places in CSS is by just using the --ident pattern, which is by convention reserved solely for author-defined names.

Peter suggests that it would thus be more natural to expose this feature with that syntax, so that instead of :state(foo), you'd write :--foo. I agree with this!

  • It makes the feature mesh more naturally with the existing platform, so custom elements can expose their own pseudoclasses in a way that feels real.
  • It seems to extend to non-boolean states more naturally; you'd write such things as :--foo(bar), exactly like the non-boolean UA pseudo-classes, rather than the imo less readable :state(foo bar), or a novel syntax like :state(foo = bar).

A practical consequence of this is that we'd need to add a check to the .add() and .toggle() methods to ensure that the token being set starts with a "--", so we can throw otherwise. (This is the standard practice in similar APIs, to ensure a close concordance between all uses of the identifier, rather than accepting anything and implicitly prepending a "--" for CSS use.)

I believe the parsing difficulty is similar, but I'm not familiar with the impl details.

@othermaciej
Copy link

Ultimately this comes down to aesthetics. I like :state(foo) better than :--foo for the following reasons:

  1. Symmetry with ::part(foo) from CSS Shadow Parts (where no one proposed ::--foo instead afaik). In particular, I think alignment with ::part is more relevant than alignment with CSS Variables.
  2. state() is more semantically meaningful than --
  3. state() looks like you are doing something meaningful and supported, while -- flaunts that you are doing something custom and weird.

The first is probably the most important consideration here. Maybe it's possible to change shadow parts to use ::--foo syntax but I suspect it may be too late for that.

Regarding non-boolean states, as long as it's an enumerated set of values and not an open-ended set, it could be transformed into set of distinct states.

For example, let's say a my-alert element has a severity state that can be low, medium, high or literally-on-fire. Instead of :state(severity = literally-on-fire), it seems perfectly adequate to write :state(severity-literally-on-fire). And it's easy for my-element to achieve this syntax. :--severity(literally-on-fire) doesn't seem like a very big improvement.

I think there's actually more of a use case for shadow parts to be parameterized than custom states. For example, a list-type control could expose a shadow part for each list item, where the set of list items can be any number. But it's harder to imagine a non-boolean state that isn't equivalent to an enumerated set of boolean states.

@tabatkins
Copy link
Author

tabatkins commented Mar 6, 2020

  • state() is more semantically meaningful than --
  • state() looks like you are doing something meaningful and supported, while -- flaunts that you are doing something custom and weird.

I think this might just be your own authoring experience, and perhaps lack of authoring familiarity with modern custom stuff in CSS, talking? A lot of people used to be uncomfortable with -- and find it ugly or weird, but it is now a common thing to see in stylesheets due to custom properties, and it clearly indicates "this is exactly like a normal property, but it's defined by the page rather than the UA". This is the same semantic represented by :state() - it's a custom pseudo-class, nothing more nor less.

And custom properties are just the start here. Custom MQs will be ---prefixed, Houdini Custom Functions will be, etc. It's a widespread signal of something page-provided rather than UA-provided, and I think that's a pretty useful and important signal to provide. :--foo will be making this feature more consistent with the rest of CSS, not less.

Symmetry with ::part(foo) from CSS Shadow Parts (where no one proposed ::--foo instead afaik). In particular, I think alignment with ::part is more relevant than alignment with CSS Variables.

And now talking about alignment, this is where the distinction gets important.

If we'd done ::--foo for parts, then it would be impossible to express "I want the part with name A and name B". You could use :is() to express A or B, but you can't stack pseudo-elements like ::--foo::--bar to indicate intersection, because :: is secretly a combinator that selects into the pseudo-children of an element. (And I tried to make it actually be a combinator years ago, but it doesn't quite work backward-compatibly, unfortunately.)

If we'd used ::--foo, then, we'd have had to also have a :--foo functionality, and somehow figure out a reasonable distinction for what names go where to explain to authors.

So, yes, in an ideal world ::part() could have been done as a -- pseudo-element as well, but the busted design of pseudo-element syntax once again bites us. We will never escape the pain of the past.

I think there's actually more of a use case for shadow parts to be parameterized than custom states. For example, a list-type control could expose a shadow part for each list item, where the set of list items can be any number. But it's harder to imagine a non-boolean state that isn't equivalent to an enumerated set of boolean states.

CSS has a whole bunch of them? Every functional pseudoclass that currently exists in CSS is not an enumerated set.

@plinss
Copy link

plinss commented Mar 8, 2020

Agreeing with Tab, and expanding a bit. This change isn't about aesthetics, I actually think ':state(foo)' is prettier than ':--foo', this change is about turning this feature from a narrowly scoped special pseudo class to a general purpose custom pseudo-class, bringing custom elements that much closer to being able to do what native elements can.

':state(foo)' also stutters, pseudo classes are fundamentally all about exposing state, calling it state yet again adds nothing. We would never have done ':state(hover)' or ':state(visited)'. (This excludes the pseudo classes that are selector math, which are arguably something completely different and, given a time machine, would have a different syntax, just like pseudo elements do.)

I also want to point out that I explicitly stated a desire for this to be brought into alignment with shadow parts, by changing '::part(foo)' to '::--foo', but that's a different topic with its own set of concerns, and I'll address those elsewhere.

Non-boolean states is also a separate issue, which I also raised multiple times, and will be addressed elsewhere, let's not overload this issue. But there are existence proofs for the need for non-boolean state. Of course in theory all states can be expressed as a series of booleans, that's the foundation of digital computers. That doesn't mean the combinations don't get out of hand rapidly or are the most friendly to authors.

@JanMiksovsky
Copy link

@tabatkins I don't see any example of how the state would be declared, so want to confirm that it would be

element.attachInternals().states.add("--foo");

and not

element.attachInternals().states.add("foo");

That is, the -- is officially part of the declared state name.

Aside: if the above is correct, then this change would open up the possibility of eventually setting built-in pseudo-classes via states: e.g., states.add("checked").

@tabatkins
Copy link
Author

Yes, it would require using the full name, including the dashes, just like with all the custom property APIs.

@chrishtr
Copy link

I suggest just taking this to a vote at the CSSWG. I've asked to put this on the agenda for next week.

@tabatkins
Copy link
Author

Symmetry with ::part(foo) from CSS Shadow Parts (where no one proposed ::--foo instead afaik). In particular, I think alignment with ::part is more relevant than alignment with CSS Variables.

This was brought up in the Web Components meeting, and while we didn't consider it a blocker, it was considered a reasonable objection. Jan brought up a possibility to fix this: w3c/csswg-drafts#4900

@tabatkins
Copy link
Author

Minutes of Web Components meeting

10:18 Topic: :state
10:18 ScribeNick: fantasai
10:18 → caridy joined (~caridy@public.cloak)
10:19 TabAtkins: Topic came up in TAG review of :state() which allows custom states
10:19 TabAtkins: Suggestion to call this a "custom pseudo class" and use CSS's custom identifier syntax, :--foo()
10:19 TabAtkins: I believe this is the appropriate place to introduce custom pseudo classes
10:19 TabAtkins: For both the current design and future design we plan for
10:19 TabAtkins: I believe going with actual custom pseudo-class is the right way to go
10:19 q+
10:19 — Zakim sees hober on the speaker queue
10:20 TabAtkins: just check for -- in ident, and then expose accordingly
10:20 — annevk "Dom choke enlist"
10:20 TabAtkins: functional pseudo classes would be the same way
10:20 s/foo()/foo/
10:20 q+
10:20 — Zakim sees hober, Justin on the speaker queue
10:20 TabAtkins: Instead of :state(..)
10:20 TabAtkins: Maciej had argument against
10:20 TabAtkins: first argument is that :state() is more symmetrical with ::part()
10:20 TabAtkins: and since semantically close together, keep syntactically close to gether
10:21 TabAtkins: Second argument is that "state" as a word has meaning
10:21 TabAtkins: lastly, :--foo() looks like noise
10:21 TabAtkins: My counter arguments:
10:21 TabAtkins: I disagree with the noise argument, think it's a question of familiarity with syntax
10:21 — bkardell_ -- ftw \o/
10:21 TabAtkins: nobody much liked --foo syntax for custom properties at the beginning
10:21 TabAtkins: but now it's a widely-used convention
10:21 — hober apologizes for foisting -- on the world
10:22 — bkardell_ might be biased
10:22 TabAtkins: meaning author-defined
10:22 TabAtkins: Works same as any other pseudo-class, clearly author-defined
10:22 TabAtkins: Second argument, disagree that "state" is meaningful. It's like "mode". Provides no additional context.
10:22 TabAtkins: Every single pseudo-class is asserting some state.
10:22 — annevk anyone a link to the issue?
10:23 — fantasai was too burned out to argue effectively against :(
10:23 — annevk agenda, doh
10:23 WICG/webcomponents#738
10:23 TabAtkins: Meaning will be clear once people associate "--foo" with "foo is a custom thing"
10:23 TabAtkins: :state() doesn't make clear that it's custom
10:23 TabAtkins: Symmetry with ::part() is a reasonable argument, but
10:24 TabAtkins: pseudo-classes can be stacked and combined so that you can have multiple pseudo-classes combined together, to filter element on multiple criteria
10:24 TabAtkins: pseudo-elements are not like that, they're treated much more like tag names
10:24 TabAtkins: and it essentially includes a combinator
10:25 TabAtkins: If we wanted to select on multiple criteria [...]
10:25 TabAtkins: No way to do pseudo-element using --foo, so take multiple names in ::part()
10:25 TabAtkins: CSS syntax has always been bad in this way
10:25 — fantasai didn't catch what way theyr'e bad
10:25 — annevk so wait, :state is meaningless, but .states is okay?
10:25 ⇐ caridy quit (~caridy@public.cloak) Ping timeout: 180 seconds
10:25 TabAtkins: ::part() is how it is, because we couldn't do it any other way
10:26 q+ to ask if .states changes
10:26 — Zakim sees hober, Justin, annevk on the speaker queue
10:26 TabAtkins: Pseudo-classes can be done any way we want, and --foo is well-established, so I think using :--foo() is more meaningful here,
10:26 q-
10:26 — Zakim sees hober, annevk on the speaker queue
10:26 TabAtkins: and if it was possible for ::part() would have used there as well
10:26 TabAtkins: So I propose to make this change.
10:27 ack hober
10:27 — Zakim sees annevk on the speaker queue
10:27 q?
10:27 — Zakim sees annevk on the speaker queue
10:27 q+
10:27 — Zakim sees annevk, Justin on the speaker queue
10:27 hober: I'm not going to attempt to channel Maciej on this issue, I think he expressed himself well.
10:27 <bkardell_> q+
10:27 — Zakim sees annevk, Justin, bkardell_ on the speaker queue
10:27 hober: Also, particular choice of :--foo vs :state() is not a hill I'm interested in dying on
10:27 Pseudo-element syntax is bad because of the combinator built in.
10:27 — fantasai q+ to mention multi-state as a consideration
10:27 — Zakim sees annevk, Justin, bkardell_, fantasai on the speaker queue
10:28 hober: When this came up in the TAG review, main concern was that we wanted non-binary states
10:28 hober: I don't find that to be terribly compelling
10:28 hober: I haven't seen examples of interesting non-binary states
10:28 hober: that don't decompose into straightforward binary states
10:28 hober: That said, I agree with Tab that we may want functional pseudo-classes built into CSS in the future
10:29 hober: and it makes sense to think ahead and to try to make custom pseudo-classes and built-in pseudo-classes upgrade to functional form in the same way
10:29 hober: so compelling argument for using --foo here
10:29 TabAtkins: CSS already has functional pseudo-classes already
10:29 TabAtkins: beyond tree-structural ones like :nth-child(An+B)
10:29 TabAtkins: :dir() can decompose into binary :rtl / :ltr
10:29 TabAtkins: but :lang() can't
10:30 TabAtkins: I don't think :lang() is a one-off, I think it's a reasonable design of things that could be desired to expose by authors in the future
10:30 TabAtkins: so we should make sure that sort of thing is consistently allowed with whatever design we go with
10:30 q?
10:30 — Zakim sees annevk, Justin, bkardell_, fantasai on the speaker queue
10:30 ack annevk
10:30 annevk, you wanted to ask if .states changes
10:30 — Zakim sees Justin, bkardell_, fantasai on the speaker queue
10:30 annevk: Wanted to ask about the API
10:30 annevk: you said "state" is a meaningless word
10:30 annevk: but still have .states
10:30 q+
10:30 — Zakim sees Justin, bkardell_, fantasai, jan on the speaker queue
10:30 annevk: or was there a corresponding proposal to change?
10:31 TabAtkins: don't think needed to change
10:31 TabAtkins: CSS can occupy --syntax, but JS doesn't have such thing
10:31 TabAtkins: and controlling API makes sense to have a name
10:31 hober: why not call it something like custom pseudo-classes?
10:31 Justin: I think state is more confusing on the JS side
10:31 annevk: i was going to bikeshed this in a similar direction to hober
10:32 Justin: because CSS syntax have context that it's a pseudo-class
10:32 Justin: on JS side, it could mean anythng
10:32 q
10:32 — Zakim The speaker queue is:
10:32 — Zakim Justin
10:32 — Zakim bkardell_
10:32 — Zakim fantasai to mention multi-state as a consideration
10:32 — Zakim jan
10:32 — Zakim [end of queue]
10:32 TabAtkins: OK, let's take an action on me to go suggest a changeover to either pseudo-classes or custom-pseudo-classes or something
10:32 ack Justin
10:32 — Zakim sees bkardell_, fantasai, jan on the speaker queue
10:32 q+
10:32 — Zakim sees bkardell_, fantasai, jan, rniwa on the speaker queue
10:32 ack bkardell_:
10:32 — Zakim sees bkardell_, fantasai, jan, rniwa on the speaker queue
10:32 ack bkardell_
10:32 — Zakim sees fantasai, jan, rniwa on the speaker queue
10:32 bkardell_: Like Tess, don't know it's a hill I'm particularly interested in standing too hard for
10:33 bkardell_: but in 2014 CSSWG made decisions about how custom things would work, e.g. custom functions would be --foo()
10:33 s/function/pseudo-classes
10:33 bkardell_: This is that feature
10:33 bkardell_: TAG review had some answers that make me feel like we could live with it, if it's not that
10:33 bkardell_: but I don't honestly see a compelling reason to not be --foo
10:33 s/--foo/:--foo/
10:33 bkardell_: I think it's a good move.
10:34 bkardell_: Maciej's concern was about consistency, and it's about where consistency and how much
10:34 q?
10:34 — Zakim sees fantasai, jan, rniwa on the speaker queue
10:34 bkardell_: if all custom CSS shares the same syntax, that's very good
10:34 q+
10:34 — Zakim sees fantasai, jan, rniwa, Justin on the speaker queue
10:34 ack fantasai
10:34 fantasai, you wanted to mention multi-state as a consideration
10:34 — Zakim sees jan, rniwa, Justin on the speaker queue
10:35 fantasai: I support the idea that we will eventually expand to multi-state and parameterized pseudo-classes
10:35 <bkardell_> s/Maciej's concern /One of Maciej's concerns
10:35 fantasai: so we need to make sure that we can expand in that direction
10:36 fantasai: wrt the name of the JS API, I don't think calling it pseudo-classes is great
10:36 fantasai: a) I'm uncertain that it will be the only place we use this state
10:36 fantasai: b) pseudo-class is really hard to spell
10:36 ack jan
10:36 — Zakim sees rniwa, Justin on the speaker queue
10:36 TabAtkins: I think it's fine.
10:37 TabAtkins: Wrt functional pseudo-classes, discussed with plinss, not sure how it should work, but sticking with current design of DOM token name list seems least clumsy way to go forward
10:37 TabAtkins: s/forward/forward to me/
10:37 TabAtkins: plinss disagrees though, thinks better idea is to move to map
10:37 TabAtkins: where key is pseudo-class thing, and key is something about the argument, currently restricted to truthy
10:37 TabAtkins: I think even if there's APIs that make it look like a set, it's awkward
10:38 TabAtkins: would prefer to have boolean and non-boolean pseudos be different
10:38 — fantasai q+ to respond to Tab
10:38 — Zakim sees rniwa, Justin, fantasai on the speaker queue
10:38 +1 to your opinion TabAtkins
10:38 jan: I maintain an open source library that invested in supporting state and state selector
10:38 jan: When I saw Tab's proposal to change to :--foo(), my initial reaction was negative
10:38 jan: Tried to see what that would look like in the code
10:39 jan: after sitting with it for a while, I'm supportive of this change. I think it achieves the objectives that Tab mentions
10:39 jan: Makes it clear this is custom pseudo-class. Originally opposed, now supportive
10:39 jan: Also want to echo idea that it'd be fine to rename the API on the JS side
10:39 Here's the thread for boolean vs functional design: #4 (comment)
10:39 jan: in writing code to use states API, it's been very confusing because we use concept of state throughout code
10:40 jan: so had to annotate when talking about element internal state API
10:40 jan: clarify not referring to component state
10:40 jan: so would support focusing API on pseudo-classes to clarify that
10:40 q?
10:40 — Zakim sees rniwa, Justin, fantasai on the speaker queue
10:40 ack rniwa
10:40 — Zakim sees Justin, fantasai on the speaker queue
10:40 rniwa: Since talking about non-boolean state, make a point that attributes, we don't have different APIs for boolean and non-boolean APIs
10:40 rniwa: so don't see why have two different things for whether boolean or not
10:41 — hober hadn't thought of the attribute analogy; that's a great point, rniwa
10:41 rniwa: Another point I'd make, there's a lot of requests and complaints about no way to emulate states like :checked/etc.
10:41 rniwa: if thinking about custom pseudo-classes, we should solve that problem
10:41 rniwa: Premature to add new API without thinking about how to emulate element state, e.g. :disabled etc.
10:42 TabAtkins: design for single API I've seen looks really weird and awkward, if you have better suggestion, post on thread
10:42 q?
10:42 — Zakim sees Justin, fantasai on the speaker queue
10:42 TabAtkins: ...
10:42 rniwa: No, I'm talking about if you're making a custom form control
10:42 rniwa: You need to be able to say "I'm in a checked state"
10:42 TabAtkins: That's an entirely different thing, though
10:42 TabAtkins: like being able to opt into various aria states with element.internals API
10:43 an API for custom elements to work with built-in pseudo-classes
10:43 TabAtkins: That seems reasonable
10:43 hober: It's not ARIA, but similar thing
10:43 hober: API for custom elements to work with built-in pseudo-classes
10:43 hober: and that sounds like a great idea
10:43 TabAtkins: if we try to overload this proposal to do that, that implies either there's a restricted set of non-prefixed names you can use...
10:43 rniwa: Not saying we need to use the same API, but we need to figure out how that works
10:44 rniwa: if we design a bad API, and we can't add standard states to custom elements because we messed up this API, that would be bad
10:44 rniwa: So we should try to solve this for at least one of them, to make sure we do it well
10:44 TabAtkins: Fair, make sure that if we want to be consistent, can be consistent.
10:44 q?
10:44 — Zakim sees Justin, fantasai on the speaker queue
10:44 TabAtkins: So agree to do that design work sooner rather than later
10:44 annevk: Isn't that form-associated custom elements?
10:45 → caridy joined (~caridy@public.cloak)
10:45 rniwa: From-associated elements handle some, but some others not supported, e.g. :link
10:45 ⇐ caridy quit (~caridy@public.cloak) Client closed connection
10:45 rniwa: element.internal is way to communicate internal state of elmeent
10:45 rniwa: e.g. media-like element, has certain states
10:45 rniwa: would be weird to call this property pseudo-classes, because then this element.internals would be all about pseudo-classes
10:45 rniwa: which would be weird
10:45 ack Justin
10:45 — Zakim sees fantasai on the speaker queue
10:46 Justin: Wrt custom functions and non-binary state, don't think a map API is that awkward
10:46 Justin: using true/false for custom pseudo-class
10:46 Justin: Custom function vs non-binary is eager vs lazy
10:46 Justin: If discussion is going towards implementing a function on JS side, called during styling
10:47 Justin: Being able to push string values or some other type of values to custom pseudo points to a path forward for non-binary states, that have a consistent API
10:47 Justin: Comment I want to make, would it make sense, ::part seems to be a special selector in my mind
10:47 Justin: thinking about custom state, ::part ends up being conceptually a way to allow for abstracting over selectors into shadow root on the shadow host
10:48 Justin: right now very specific prescribed set of things you can do, but maybe ::part() grows more syntax
10:48 Justin: would it make sense for consistency, to allow ::part() to use --syntax?
10:48 Justin: would that be two ways to do the same thing?
10:48 TabAtkins: not sure how I feel about it
10:48 TabAtkins: Post it as an issue, worthy of thought
10:48 That seems awkward given part=""
10:49 hober: In context of CSS, I've often been someone who's skeptical of adding syntax aliases for things
10:49 hober: we've done that in some cases for legacy properties with bad names
10:49 q?
10:49 — Zakim sees fantasai on the speaker queue
10:49 hober: and I worry about expanding the API surface and confusing authors
10:49 <bkardell_> mapping legacy to a common better aligned this is definitely a case we do that tho, and it's an interesting idea
10:49 q+
10:49 — Zakim sees fantasai, jan on the speaker queue
10:49 ack fantasai
10:49 fantasai, you wanted to respond to Tab
10:49 hober: so base skepticism for aliases, but it's an interesting point
10:49 — Zakim sees jan on the speaker queue
10:49 ack fantasai
10:49 — Zakim sees jan on the speaker queue
10:49 <bkardell_> so +1 to what hober just said
10:50 → caridy joined (~caridy@public.cloak)
10:50 q?
10:50 — Zakim sees jan on the speaker queue
10:50 fantasai: CSS has distinct syntaxes for boolean vs functional pseudo-classes, so API needs to be clear which one the custom thing is
10:51 TabAtkins: ...
10:51 TabAtkins: [describes some thoughts in threads]
10:51 TabAtkins: There are also some pseudos that can be either functional or not, e.g. earlier proposal for :local-link/:local-link()
10:51 TabAtkins: If combined design, means we have to choose one of two paths
10:52 TabAtkins: either booleans can only be defined by true/false, anything defined with other syntax is functional
10:52 TabAtkins: or if you have boolean, :--foo and :--foo() are equivalent
10:52 TabAtkins: so those are choices to be made, not sure which one to make
10:52 TabAtkins: or if using separate APIs
10:52 TabAtkins: but do need to think about how it affects CSS syntax
10:52 annevk: Seems pretty self-evident that two separate APIs is the way to go
10:53 Justin: I prefer one API, because it seems for boolean functional attribute
10:53 Justin: ...?
10:53 TabAtkins: We had functional pseudo-class :local-link(...) which interpreted argument particular ways
10:54 TabAtkins: but you ould also use :local-link which defaults behavior
10:54 fantasai: It's not generally true in CSS that parens are optional like that, though
10:55 annevk: ...
10:55 hober: I like Justin's idea that the custom pseudo-classes without parens is equivalent of passing in "true"
10:55 (I proposed :heading at one point which was both :heading and :heading(level))
10:55 hober: parallel to attributes in the DOM
10:55 — fantasai thanks annevk
10:55 <bkardell_> there are lots of proposals, and if we had custom ones we could find out if they are good
10:55 hober: If simple rule for all the custom ones, that's great
10:56 TabAtkins: Wish we could have said could have parens optional
10:56 <bkardell_> local link is a good example of one I built myself and then realized i didn't understand it and it wasn't what I wanted at all :)
10:56 TabAtkins: but currently it's a syntax error, blows out entire style rule
10:56 ack jan
10:56 — Zakim sees no one on the speaker queue
10:56 jan: Rationalizing --pseudo-classes vs using for parts
10:57 jan: I do think important that place where dev is defining the custom thing, it's consistent with where they're using it
10:57 jan: replacing state with --, unclear what you would pass
10:57 jan: once Tab clarified that you include -- as part of the token being passed, that's OK
10:57

isn't

10:57 jan: I'd be concerned if you define part with part="bar" and refer to it with "--bar".
10:58 jan: I don't know the value of -- in signally "something custom going on"
10:58 jan: but name itself needs to be consistent
10:58 TabAtkins: we had discussion for custom properties, concluded it's important to always use full ident name including --
10:58 TabAtkins: for custom states, definitely have to include
10:58 TabAtkins: for ::part(), would apply the same rule
10:58 TabAtkins: that that means, it's the same rule
10:59 TabAtkins: might mean all part names have to start with --part
10:59 TabAtkins: or say current works, and part="--foo" means you can use --foo as a top-level pseudo-element
10:59 jan: Found it hard to remember whether to use one colon or two
10:59 jan: so preserving current part syntax is maybe helpful to keep that distinction

@ExE-Boss
Copy link

ExE-Boss commented Mar 24, 2020

I’d expect custom pseudo‑classes (e.g.: :‑‑foo) to behave more like postcss‑custom‑selectors and be distinct from custom state.

@gregwhitworth
Copy link

I'm actively needing to add custom state to a component, and while this is a valid point by @tabatkins I wanted to discuss it a bit further since I missed the F2F discussion of this.

10:24 TabAtkins: pseudo-classes can be stacked and combined so that you can have multiple pseudo-classes combined together, to filter element on multiple criteria
10:24 TabAtkins: pseudo-elements are not like that, they're treated much more like tag names
10:24 TabAtkins: and it essentially includes a combinator

I'm curious if we play this out a little bit with real examples. I'm working on a <select> component and I need a custom element of <pop-up> to have a state of open. I then have been looking through the functions we currently have and psuedo classes. Most of the classes I feel while possibly states (eg: required) they don't seem quite the same to me but I may be being pedantic. Now something like valid or invalid does seem to be a state as it follows a behavior. So I actually think leveraging a :state() is my preferred route. I guess aligning with I believe @annevk

I'd much rather have:

list-box::part(pop-up):state(open) rather than list-box::part(pop-up):--open

And yes I know that most CSS authors will recognize that -- is custom but I also agree with this sentiment:

10:57 jan: I do think important that place where dev is defining the custom thing, it's consistent with where they're using it

We could possibly create a clear definition of state for the web platform and alias CSS functions to relevant state(). This does produce incosistencies with custom functions, MQs but to the earlier point then we should deal with ::part() then if that is the line we want to draw in the sand as that is representing a custom part. Additionally custom elements are not prefixed with -- which makes me think that custom things from HTML actually already conform to this, whereas custom things in CSS do have an expectation of starting with -- so I do believe we have a solid consistency.

Sorry for the long thread and hopefully my stance is clear.

@tabatkins
Copy link
Author

I'd much rather have:

list-box::part(pop-up):state(open) rather than list-box::part(pop-up):--open

Note that Jan expressed similar sentiment at first but came around:

10:38 jan: When I saw Tab's proposal to change to :--foo(), my initial reaction was negative

10:38 jan: Tried to see what that would look like in the code

10:39 jan: after sitting with it for a while, I'm supportive of this change. I think it achieves the objectives that Tab mentions

10:39 jan: Makes it clear this is custom pseudo-class. Originally opposed, now supportive

In other words, give it a little time, you might come around from your initial dislike too. ^_^

We could possibly create a clear definition of state for the web platform and alias CSS functions to relevant state(). This does produce incosistencies with custom functions, MQs but to the earlier point then we should deal with ::part() then if that is the line we want to draw in the sand as that is representing a custom part. Additionally custom elements are not prefixed with -- which makes me think that custom things from HTML actually already conform to this, whereas custom things in CSS do have an expectation of starting with -- so I do believe we have a solid consistency.

Apologies, but I'm not sure what you're arguing for here. Could you elaborate?

@gregwhitworth
Copy link

Thanks Tab, and sorry if that last part got confusing. Hopefully this will make it clear:

Type Example in CSS (as it currently is)
Custom Property --foo
Custom Element my-foo
Custom Element Part ::part(foo)
Custom Element State :--foo

So the recommendation that it sounds like everyone is landing on is to have state change to use -- and it seems in the minutes that there was agreement on changing part to be the same. My question was in relation to my-foo which doesn't provide the same author hints that it's a custom element. Should this be adjusted to --my-foo to keep the same consistency? So that the table would then look like this?

Type Example in CSS (as it currently is)
Custom Property --foo
Custom Element --my-foo
Custom Element Part ::--foo
Custom Element State :--foo

Thanks!

@ExE-Boss
Copy link

‑‑my‑foo would have to be written as:

<--my-foo></--my-foo>

Since the tag name selector checks for exact name, unless you intend to make it check for my‑foo, which wouldn’t be backwards compatible.

@tabatkins
Copy link
Author

tabatkins commented Apr 7, 2020

Yeah, custom element names go by the HTML rules, which are just "must have a - in there somewhere". They're not, and don't need to be, consistent with CSS practice.

My question was in relation to my-foo which doesn't provide the same author hints that it's a custom element.

And more directly: the hint is that there's a - in it, which no built-in element has.

@gregwhitworth
Copy link

I'm solely popping in to say that I don't have a really compelling reason outside of aesthetics and so if most people are for this change then let's do it.

@trusktr
Copy link

trusktr commented Apr 28, 2020

If we go with --foo, should we call the feature "Custom Pseudo Classes" so that it aligns with what already exists? And then also make the API more like

this.attachInternals().pseudoClasses.add('--foo')

?

@ExE-Boss
Copy link

I personally think that Custom States and Custom Pseudo‑Classes should be separate, since my expectation is that Custom Pseudo‑Classes are controlled by the page, whereas Custom States are controlled by the element.

@tabatkins
Copy link
Author

RESOLVED: Syntax changes from :state(foo) to :--foo

Minutes of the CSSWG meeting

08:27 TabAtkins: Should I go over the history? Or just jump right in?
08:27 astearns: Please refresh us
08:28 TabAtkins: Custom elements want to be able to expose some way of selecting based on stuff happening internally to them. Like expose invalid state. Theoretically they can do this by altering the attributes on the host element, but this is bad because they shouldn't change outer page's stuff because elements shouldn't change themselves from underneath. Confusion & unintended interaction with scripts ensue
08:28 TabAtkins: We try avoid this in other places. Exposing the ARIA stuff directly as an API
08:29 TabAtkins: If an element wants to decalare it's in another state, attributes are bad. So :state can correspond to some JS API underneath that the element can manipulate internally. "I'm matching a light class and a wide class" or whatever
08:29 TabAtkins: Initially we realized that :state is a null word, it doesn't have any meaning on its own here, and we avoid that in CSS. All classes are a type of state.
08:30 TabAtkins: If we want to emulate the sort of thing that built-in elements can do, like :invalid, this is what we wanted to run into for custom pseudoclasses eventually. The syntax for custom pseudoclasses were supposed to be just like normal, but starts with --
08:30 TabAtkins: The suggestion is to change the syntax to that. So you could do :--light to match light mode.
08:30 TabAtkins: THere were other topics about boolean vs enum pseudoclasses, but we won't talk about htose now
08:31 TabAtkins: Just for now: Should we change from :state(stuff) to :--stuff?
08:31 makes sense to me
08:31 TabAtkins: WICG isn't a ruling body so we have to discuss this here
08:31 q+
08:31 — Zakim sees hober on the speaker queue
08:31 TabAtkins: (in CSS)
08:31 q+
08:31 — Zakim sees hober, AmeliaBR on the speaker queue
08:31 ack hober
08:31 — Zakim sees AmeliaBR on the speaker queue
08:31 q+
08:31 — Zakim sees AmeliaBR, fremy on the speaker queue
08:31 — fremy @hober: ahaha, so true :)
08:31 hober: tl;dr: I can live with this, but I prefer :state().
08:32 — cbiesinger find the tab? I think he's in the bay area
08:32 — fremy @hober: (was about the tab-finding thing)
08:32 hober: I'm not sure it would be useful to reiterate the reasons for preferring :state() because these are in the issue. Other folks like mjs and Greg have raised them.
08:32 ack AmeliaBR
08:32 — Zakim sees fremy on the speaker queue
08:32 s/WICG/the web components meeting/
08:32 hober: But if that's the consensus, we can live with it.
08:32 — hober it's not wicg
08:33 AmeliaBR: Same as hober in general. I'm a bit concerned if :--x and ::--x have two very distinct meanings, and I don't think we have widespread author undersatnding between pseudoclasses and pseudoelements. I don't know if the final proposal will keep the part wrapper for the pseudoelement and drop the part for pseudoclasses
08:34 TabAtkins: There's a possibility it might change. The reason is that pseudoclasses and pseudoelements work differently. Pseudoelements are a combinator and selector in one. If you want to select on more than one aspect (liek multiple part names) and we switch to ::-- then you could only select one. But for pseudoclasses you can select more than one
08:34 s/drop the part/drop the state wrapper/
08:34 TabAtkins: Due to unfortunate history, we're under different constraints for pseudoelement parts. ::part might be the best way, but we're in discussions. You might be able to expose the part names can be used directly as ::--
08:35 TabAtkins: So if you have a calendar widget and your days are exposed as parts, you want a partname of day, partname of "weekend" vs "weekday", etc. So all of these can be selected in whatever combination you need to style particular things. So you couldn't say ::--weekend but if you want to style weekend in the second week differently then you could use ::part that would let you select both at once
08:35 TabAtkins: I'm not caught up on this discussion. The current thought is to allow both -- in as many places as possible for the author.
08:36 AmeliaBR: My concern is that it could get confusing for authors of the difference between what's selecting the state of the custom element vs what's selecting a part within it
08:36 AmeliaBR++
08:36 s/undersatnding between/understanding of the difference between/
08:36 TabAtkins: Yep. It's just a big jumble. There is prior art here inside existing browsers.
08:36 fantasai: Can we not make it worse?
08:37 TabAtkins: I don't think we can avoid it. If it was an actual combinator, we could avoid it, but we have to provide a name because of how the syntax works and we cant avoid it. We won't maek it worse but we will be keeping current practice that we can't get aroudn
08:37 ack fremy
08:37 — Zakim sees no one on the speaker queue
08:38 fremy: I am very much in favor of this. Because typing :state() :state() :state() is annoying. Everytime you want to use this stuff you have to type this. I think there is a point in making it shorter. I did not consider the confusion between pseudoclasses and pseudoelements. But it's not an issue with the naming, it's an issue in general. But for practical purpose, typing :state() isn't useful. And selectors already can be quite long.
08:39 TabAtkins: Your point about "this isn't a question of the particular syntax, if we expose pseudoelements and pseudoclasses in any syntax, there might be a collision" but this is general and doesnt' have a bearing on this issue
08:39 fremy: Eventually you have to learn it anyway. So i'm not sure it's valid .... people already have to understand what + means if people want to use it. + is also confusing if you don't know what it means. It could have been better, i'm not sure it's a convincing argument
08:40 florian: As long as it's syntax based on obscure characters, people get confused on what's a pseudoclass and pseudoelement. But if they're named, it's harder to be confused
08:40 TabAtkins: THe confusion is partially that but it's alos what aspects get exposed as states vs parts. There's a venn-diagram and it's almost a circle
08:40 florian: In your example, weekend vs second week seems to be obviously part of the calendar. I think you'll get more reliable answers from authors about the difference between pseudoelements vs pseudoclasses
08:40 TabAtkins: I don't agree with your characterization. There's confusion there anyway.
08:41 s/between/between parts vs states than/
08:41 TabAtkins: Part names are controlled by the component using its own element. It chooses what to expose and what to expose it as. States are exposed by the element itself. So there's two different produces interoperating here. The component itself and the thing using the component and exposing it up higher. and they shouldn't syntactically collide because that makes for upgrade issues when suddenly your component uses a state name that you're already using as
08:41 a part name. As long as they're distinct syntacitcally it's fine
08:41 fantasai: Nobody's saying they should be in the same namespace
08:41 TabAtkins: That was the argumetn
08:42 fantasai: I thought the discussion was should it be :--state or :part()
08:42 TabAtkins: There are other proposals too.
08:42 s/:part()/::part(), not whether to but them all under :--foo/
08:42 TabAtkins: Unless we do think there is a sufficient separation between the two that needs to be caputred by different names in -- vs - then we can keep the convo on the current discussion about :-- vs :part()
08:43 AmeliaBR: I mentioned with :part() there would need to be wrappers still available for when you want the intersection of two classes. For state, is it okay to allow the state wrapper as optional? So you could have :state(foo) vs :--foo as equivalent so the stylesheet author who is using the web component can use the syntax that is most intuitive for htem, regardless of the web component's design?
08:43 I think that's extra confusing
08:44 s/ :part()/: :part()/
08:44 TabAtkins: The issue is keeping around the :state() as a fallbakc doesn't allow anything new. YOu can always select on multiple pseudoclasses by stacking them with :is(). But pseudoelements you can't do that. You can use "or" but you can't use "and" conditions. So you need syntax to do that. So we shouldn't have two different syntax for the same thing if the only reason is that some people might want to type parentheses
08:45 plinss: :state(--statname)? What about :state(--hover)??
08:45 AmeliaBR: That is a fair argument.
08:45 s/--hover/hover/
08:45 s/:state(--hover)/:state(hover)/
08:46 astearns: hober, Would you prefer the state() syntax, but you didn't want to describe the reasons. Can you summarize the state of the discussion in the web components group? I'm looking through the minutes and I can't find consensus
08:47 hober: Our positions have been made on the github issue. Some of that argument is aesthetic, but to go beyond that i'd have to catch myselfup
08:47 — fantasai TabAtkins, remind me where we're at wrt ::part()? I lost the thread...
08:47 astearns: Is the aesthetics just "naming things is hard" or is it about the typographic :-- not conveying as much information as the word does
08:47 — TabAtkins I have to go look it up, like I said I've switched away from the thread for a bit.
08:48 hober: I think some of the aesthetic concerns about it being ugly. which is not a position that is worth arguing for on this call. Other aspects of the concern were around matching ::part because these two features would be used in tandem and it's a bit jarring to have one of them be named and the other one not. There's probably more to it than that but I can't remember
08:48 to be very honest, I could also live with both options
08:48 I agree it's mostly down to esthetics
08:49 TabAtkins: That was one of the points that mjs made. For the rest of the thread people were riffing on mjs's points or talking about the aesthetics. With regard to mjs's things, there is active discussion on allowing custom pseudoelements to use the same syntax. I dropped looking at the thread to do other things but last time I did I put in some possibilities, but I don't know what happened to them. I think there's a path to getting them semantically similar.
08:49 TabAtkins: Everyone thought -- was ugly in custom properties, but now people ... still think it's ugly, but it conveys meaning
08:50 plinss: Are we going to want custom pseudoclasses in some other way? I can't think of one.
08:50 q+
08:50 — Zakim sees chrishtr on the speaker queue
08:51 ack chrishtr
08:51 — Zakim sees no one on the speaker queue
08:51 TabAtkins: The main use case is defining generally selector aliases. If you want :heading that selected

through

this would be easy to do in CSS, would be worthwhile... I would be okay with that, we can figure out something for htis later when we need to. Pseudoclasses here are more similar to regular pseudoclasses for video, etc. In terms of what has the most claim to the syntax space, pseudoelements have a better claim to it. If we want more for
08:51 things like JS, we can cross that bridge when we come ot it
08:51 s/pseudoelement have/custom elements have/
08:52 chrishtr: I read through the issue again, and it sounds like the debate is mostly aesthetic or developer understandability. Most of the people talking seemed to indicate they have a similar stance to hober. I actually personally don't care that much, there are arguments both ways. If everyone feels the same way, or leaning one way, we can just take a straw poll and pick the one that has the most votes
08:52 chrishtr: In interest of moving forward
08:52 astearns: I'm not that interested in voting. We have some people pushing for the change and most people say "yyyyyeah okay...."
08:53 astearns: I'm not hearing people objecting to the change or giving strong arguments why it should remain :state()
08:53 fantasai: We might want more feedback from the developer community. The question is more on their side what's easier for them
08:53 present_+
08:53 present+
08:53 astearns: It's not on them, it's on us to determine how this whole thing will work with custom states and states on custom elements and making sure we have consistency.
08:53 (forgot to do that earlier 😅)
08:53 Rossen_: Didn't TabAtkins run polls on twitter?
08:54 TabAtkins: No. But has been working on this. He initially thought :-- is ugly and weird, but he switched his opinion after using it because he felt it was more natural and CSSy. This matches my intuition
08:54 chrishtr: astearns, were you trying to drive toward resolution?
08:54 astearns: I was thinking we could get an unenthusiastic consensus
08:54 astearns: I'm happy to back down from that if anyone has a strong enough objection to making the change
08:54 s//Jan Mikovsky/
08:55 plinss: not objection, but it became clear that many people don't understand what a pseudoclasses actually is, and that it's just state. People seem to think that functional notation of pseudoclasses implies functional behavior, which is untrue. Rather than asking developers, we should educate developers about what pseudoclasses are. This is just how we expose state.
08:55 chrishtr: This is an argument in favor of :state()?
08:55 plinss: No, the opposite.
08:56 plinss: I'd rather it be :--
08:56 plinss++
08:56 plinss: I don't want state to be a weird thing for custom elements. I want custom elements to create pseudoclasses just like regular elements can
08:56 +1
08:56 AmeliaBR: The single colon means state, rather than having an extra state keyword
08:56 astearns: The syntax is difficult to teach, but if you want to avoid people having the impression that there's some functional behavior, i don't know which syntax would be better
08:57 chrishtr: Should we try for resolution?
08:57 astearns: Yes.
08:57 I support the proposed resolution
08:57 astearns: Let's resolve to make the change from :state(foo) to :--foo even if people are somewhat unenthusiastic. If someone is really unenthusiastic they can object
08:57 fantasai: plinss's argument convinced me.
08:57 astearns: Anyone else?
08:57 RESOLVED: Syntax changes from :state(foo) to :--foo

dbaron added a commit to dbaron/wgmeeting-github-ircbot that referenced this issue May 6, 2020
(The CSS Virtual face-to-face just had a discussion that should have
been posted to WICG/custom-state-pseudo-class#6.)
@othermaciej
Copy link

Given this resolution, could CSS WG please take up the corresponding issue for CSS Shadow Parts? It looks like consistency was discussed but deferred to a separate discussion?

@tabatkins
Copy link
Author

Yup, that's my plan shortly, it was discussed tangentially during the topic.

tkent-google added a commit that referenced this issue May 12, 2020
tkent-google added a commit that referenced this issue May 12, 2020
* Change the syntax; :state(foo) -> :--foo

Fixes #6
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Jan 18, 2021
This CL updates the custom state pseudo class syntax from :state(foo)
to :--foo.
See WICG/custom-state-pseudo-class#6.

This feature is not shipped yet.

Bug: 1012098
Change-Id: I71bd92113d89b073cdeba3b6ab78d2c7f0e091dd
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Jan 20, 2021
This CL updates the custom state pseudo class syntax from :state(foo)
to :--foo.
See WICG/custom-state-pseudo-class#6.

This feature is not shipped yet.

Bug: 1012098
Change-Id: I71bd92113d89b073cdeba3b6ab78d2c7f0e091dd
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Jan 21, 2021
This CL updates the custom state pseudo class syntax from :state(foo)
to :--foo.
See WICG/custom-state-pseudo-class#6.

This feature is not shipped yet.

Bug: 1012098
Change-Id: I71bd92113d89b073cdeba3b6ab78d2c7f0e091dd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2636075
Reviewed-by: Rune Lillesveen <futhark@chromium.org>
Commit-Queue: Kent Tamura <tkent@chromium.org>
Cr-Commit-Position: refs/heads/master@{#845429}
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Jan 21, 2021
This CL updates the custom state pseudo class syntax from :state(foo)
to :--foo.
See WICG/custom-state-pseudo-class#6.

This feature is not shipped yet.

Bug: 1012098
Change-Id: I71bd92113d89b073cdeba3b6ab78d2c7f0e091dd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2636075
Reviewed-by: Rune Lillesveen <futhark@chromium.org>
Commit-Queue: Kent Tamura <tkent@chromium.org>
Cr-Commit-Position: refs/heads/master@{#845429}
blueboxd pushed a commit to blueboxd/chromium-legacy that referenced this issue Jan 21, 2021
This CL updates the custom state pseudo class syntax from :state(foo)
to :--foo.
See WICG/custom-state-pseudo-class#6.

This feature is not shipped yet.

Bug: 1012098
Change-Id: I71bd92113d89b073cdeba3b6ab78d2c7f0e091dd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2636075
Reviewed-by: Rune Lillesveen <futhark@chromium.org>
Commit-Queue: Kent Tamura <tkent@chromium.org>
Cr-Commit-Position: refs/heads/master@{#845429}
moz-v2v-gh pushed a commit to mozilla/gecko-dev that referenced this issue Jan 22, 2021
…ntax, a=testonly

Automatic update from web-platform-tests
Custom state: Update the pseudo class syntax

This CL updates the custom state pseudo class syntax from :state(foo)
to :--foo.
See WICG/custom-state-pseudo-class#6.

This feature is not shipped yet.

Bug: 1012098
Change-Id: I71bd92113d89b073cdeba3b6ab78d2c7f0e091dd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2636075
Reviewed-by: Rune Lillesveen <futhark@chromium.org>
Commit-Queue: Kent Tamura <tkent@chromium.org>
Cr-Commit-Position: refs/heads/master@{#845429}

--

wpt-commits: 444e9de5e00d00cdc01aaf9197c6c53d7720945a
wpt-pr: 27229
gecko-dev-updater pushed a commit to marco-c/gecko-dev-wordified that referenced this issue Jan 25, 2021
…ntax, a=testonly

Automatic update from web-platform-tests
Custom state: Update the pseudo class syntax

This CL updates the custom state pseudo class syntax from :state(foo)
to :--foo.
See WICG/custom-state-pseudo-class#6.

This feature is not shipped yet.

Bug: 1012098
Change-Id: I71bd92113d89b073cdeba3b6ab78d2c7f0e091dd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2636075
Reviewed-by: Rune Lillesveen <futharkchromium.org>
Commit-Queue: Kent Tamura <tkentchromium.org>
Cr-Commit-Position: refs/heads/master{#845429}

--

wpt-commits: 444e9de5e00d00cdc01aaf9197c6c53d7720945a
wpt-pr: 27229

UltraBlame original commit: 9777af6b9c0fb69c1a3b70e2414b09565cf191b5
jamienicol pushed a commit to jamienicol/gecko that referenced this issue Jan 29, 2021
…ntax, a=testonly

Automatic update from web-platform-tests
Custom state: Update the pseudo class syntax

This CL updates the custom state pseudo class syntax from :state(foo)
to :--foo.
See WICG/custom-state-pseudo-class#6.

This feature is not shipped yet.

Bug: 1012098
Change-Id: I71bd92113d89b073cdeba3b6ab78d2c7f0e091dd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2636075
Reviewed-by: Rune Lillesveen <futhark@chromium.org>
Commit-Queue: Kent Tamura <tkent@chromium.org>
Cr-Commit-Position: refs/heads/master@{#845429}

--

wpt-commits: 444e9de5e00d00cdc01aaf9197c6c53d7720945a
wpt-pr: 27229
nigelmegitt pushed a commit to nigelmegitt/wgmeeting-github-ircbot that referenced this issue Jul 21, 2022
(The CSS Virtual face-to-face just had a discussion that should have
been posted to WICG/custom-state-pseudo-class#6.)
mjfroman pushed a commit to mjfroman/moz-libwebrtc-third-party that referenced this issue Oct 14, 2022
This CL updates the custom state pseudo class syntax from :state(foo)
to :--foo.
See WICG/custom-state-pseudo-class#6.

This feature is not shipped yet.

Bug: 1012098
Change-Id: I71bd92113d89b073cdeba3b6ab78d2c7f0e091dd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2636075
Reviewed-by: Rune Lillesveen <futhark@chromium.org>
Commit-Queue: Kent Tamura <tkent@chromium.org>
Cr-Commit-Position: refs/heads/master@{#845429}
GitOrigin-RevId: 7cfed512ee4eb89a5cf21da68e3809e7b4214129
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants