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

[selectors-4] Allow more pseudo-classes following a pseudo-element #7085

Closed
cdoublev opened this issue Feb 24, 2022 · 13 comments
Closed

[selectors-4] Allow more pseudo-classes following a pseudo-element #7085

cdoublev opened this issue Feb 24, 2022 · 13 comments

Comments

@cdoublev
Copy link
Collaborator

cdoublev commented Feb 24, 2022

There is already an issue mentioned in 3.6.3. Pseudo-classing Pseudo-elements (I did not find the corresponding issue on GH):

A pseudo-element may be immediately followed by any combination of the user action pseudo-classes [...]

ISSUE 3 Clarify that :not() and :is() can be used when containing above-mentioned pseudos.

I think the issue can be completed with:

  • all pseudo-classes: when following a pseudo-element explicitly defined to be matchable by this pseudo-class (eg. ::shadow:empty, ::shadow:defined, ::shadow-child:first-child, etc)
  • :where(): when containing user-action pseudo-classes as arguments
  • :defined: I'm not sure about this one because it is defined as matching elements that are fully defined, as dictated by the host language, not as matching pseudo-element

Pseudo-class functions which do not match the corresponding (pseudo-element) sub-classing condition could be considered as matching nothing instead of being invalid.

But I wonder if I'm missing something because:

  • ::before:hover does not work in Chrome/Firefox (not tested in other browsers)
  • MDN browser compat table includes Pseudo-element support with support from early versions of all browsers: is it the support of :hover followed by a pseudo-element, or the other way around? The french version defines that the :hover pseudo-class can follow any pseudo-element, but not the english version. Non-english versions are often translations of the english text.
  • I could not find any related issues on Chromium Issues or Mozilla Firebug Bugzilla
  • EDIT: I missed the related issue on Bugzilla and created an issue on Chromium Issues
  • Chrome DevTools does not allow to simulate user-action pseudo-classes like :hover on a pseudo-element (cf. this SO issue)
  • Firefox DevTools allows to set :hover but it triggers this state on the originating element
@SebastianZ
Copy link
Contributor

I could not find any related issues on Chromium Issues or Mozilla Firebug

Nice to read Firebug! 😄 One related Firefox/Gecko issue is here:
https://bugzilla.mozilla.org/show_bug.cgi?id=1122965

I couldn't find one for Chromium or WebKit.

Sebastian

@Loirooriol
Copy link
Contributor

This seems related to #6737 in the sense that some contexts restrict which selectors you can use, then :is()/:where()/:not() should probably be allowed but with their argument obeying the restriction.

@fantasai
Copy link
Collaborator

fantasai commented Nov 8, 2022

Agenda+ to discuss allowing :is()/:not()/:where()

  • after pseudo-elements, when restricted to containing the user-action pseudos (which are already allowed there)
  • generically stated, anywhere their arguments are allowed

See pull request #8041

@Loirooriol
Copy link
Contributor

provided their arguments all adhere to the above restriction

Does this mean that ::before:is(:hover, :first-child) is completely invalid, or becomes ::before:is(:hover)?

@fantasai
Copy link
Collaborator

fantasai commented Nov 8, 2022

@Loirooriol Good question, ::before:first-child is invalid, but since :is() has forgiving parsing maybe it should be valid?

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed Allow more pseudo-classes after pseudo-elements, and agreed to the following:

  • RESOLVED: allow the "logical combination pseudo-classes" after pseudo-elements, with their contents having the same restrictions as the pseudo-elements themselves otherwise would have
  • RESOLVED: Allow the logical-combination pseudo-classes generically, anywhere their contents are allowed.
  • RESOLVED: Allow pseudo-elements to define additional pseudo-classes they allow to be placed after them.
The full IRC log of that discussion <TabAtkins> Topic: Allow more pseudo-classes after pseudo-elements
<fantasai> TabAtkins: Currently, pseudo-elements allow a few pseudos to be put after them, :hover etc.
<fantasai> TabAtkins: question is whether to allow combinatoric ones (:is()/:not()/:where()) to be applied as well
<JakeA> q+
<fantasai> TabAtkins: Further, some pseudo-elements represent specific elements elsewhere in the DOM
<fantasai> TabAtkins: wondering if specific pseudo-elements can opt into allowing additional pseudo-elements
<fantasai> TabAtkins: but that's a different issue
<fantasai> TabAtkins: but first issue, allowing combinatoric ones
<fantasai> TabAtkins: Contents are still restricted to the user-action pseudos
<fantasai> JakeA: we're interested in this for view-transitions
<Rossen_> ack JakeA
<fantasai> JakeA: want to know if old or new view is only-child
<fantasai> JakeA: so allowing that would be useful
<TabAtkins> fantasai: two issues, one is we have a set of pseudos that are already allowed, and do we allow them to be put in :is()/etc
<TabAtkins> fantasai: second is allowing other pseudos on case-by-case basis, we should talk about that separately
<TabAtkins> fantasai: for is/not/where, it's about whether we can express the combinations of the user action pseudos
<fantasai> https://github.com//pull/8041/files
<tantek> could see this being useful for webdevs
<fantasai> oriol: these use forgiving parsing, would invalid pseudos be valid within them?
<fantasai> TabAtkins: yes
<fantasai> TabAtkins: this would allow more safely adding more pseudo-classes to a selector in the future
<fantasai> Rossen_: any other opinions?
<fantasai> TabAtkins: proposed, allow the "logical combination pseudo-classes" after pseudo-elements, with their contents having the same restrictions as the pseudo-elements themselves otherwise would have
<fantasai> RESOLVED: allow the "logical combination pseudo-classes" after pseudo-elements, with their contents having the same restrictions as the pseudo-elements themselves otherwise would have
<TabAtkins> fantasai: and more generically i want to resolve that the logical-combo pseudos can be used *anywhere* their contents are allowed
<TabAtkins> TabAtkins: I agree with being more generic if we can get away with it
<fantasai> fantasai: wanted to ask if anyone saw a problem with that
<tantek> no objection
<TabAtkins> (Don't think there's much issue beyond just implementing it.)
<TabAtkins> RESOLVED: Allow the logical-combination pseudo-classes generically, anywhere their contents are allowed.
<fremy> q+
<fantasai> TabAtkins: Second part of issue, should we allow additional pseudo-classes other than user-action pseudo-classes to be put after speudo-elements?
<fantasai> s/speu/pseu/
<fantasai> fremy: On previous resolution, does that mean you can change styles on ::before:hover ?
<fantasai> TabAtkins: yes
<fantasai> fremy: what if you create a loop with ::first-line:hover?
<fantasai> TabAtkins: hover loop, we already have that problem
<fantasai> Consider p:hover ::first-line
<fantasai> fremy: ok
<JakeA> I'm still +1 this
<bradk> +1
<fantasai> TabAtkins: back to issue, should we allow lifting restrictions on pseudo-elements followed by pseudo-classes?
<argyle> +1
<miriam> +1
<JakeA> q+
<Rossen_> ack fremy
<TabAtkins> fantasai: Okay as long as we do it as an allowlist, rather than blanket allowing
<Rossen_> ack JakeA
<TabAtkins> JakeA: would folks be happy with us using :only-child for view transitions?
<fantasai> TabAtkins: As long as it matches intent
<fantasai> TabAtkins: defined as up to two children, and this one says if you have only one
<fantasai> JakeA: But usually it doesn't count pseudos...
<fantasai> JakeA: but I guess if we're appying it to a pseudo
<bradk> :only-pseudo-element?
<fantasai> TabAtkins: pseudo-elements live in the tree
<TabAtkins> RESOLVED: Allow pseudo-elements to define additional pseudo-classes they allow to be placed after them.

@cdoublev
Copy link
Collaborator Author

cdoublev commented Nov 24, 2022

So basically, the basic syntax of the argument of logical combination pseudo-classes following a pseudo-element is now allowed to be a very small subset of <*-selector-list> specific to the pseudo-class and the pseudo-element.

It would be nice to define this syntax explicitly, to avoid parsing against <forgiving-selector-list> whereas a simple list of user action pseudo-class selectors would be expected.

I do not think pseudo-elements are focusable so user actions do not even need to be stackable, and the basic syntax of the argument of :is() or :where() following a pseudo-element could be <user-action-pseudo-class-selector>#, which is way more efficient to parse than <forgiving-selector-list>, I think.

EDIT: on second thought, implementers may not be fans of context-sensitive syntaxes and it may be simpler to validate the result of parsing against <*-selector-list>.

@cdoublev
Copy link
Collaborator Author

cdoublev commented Nov 26, 2022

I may have misunderstood your resolution. Can you please clarify whether a pseudo-class that is not allowed to follow a pseudo-element should be invalid or should match nothing? Currently, it should be a user action otherwise it is invalid, and it matches nothing if the user action is not defined to follow the pseudo-element (or vice-versa if the pseudo-element is not defined to precede the user-action).

I also think to ::before:is(div :>> :hover) or ::before:is(div **:hover) assuming :> or ** (cf. #7346, #4565) will ever be defined in Selectors 5, so my previous comment is definitely wrong. If the answer to my previous question is it should be invalid, can you clarify that logical combination pseudo-classes following a pseudo-element still accept a <*-selector-list>?

Or I should just wait for your edits...

@tabatkins
Copy link
Member

tabatkins commented Dec 9, 2022

@fantasai and I fixed in 7d86995

@cdoublev
Copy link
Collaborator Author

cdoublev commented Dec 12, 2022

It is clearer to me now, thanks. This issue is closed but has the "needs testcase" label, so I leave some suggestions below....


In 18. Grammar, a whitespace should also be forbidden:

  • between <compound-selector> and <pseudo-compound-selector>
  • between <pseudo-element-selector> and <pseudo-class-selector>
  • between occurrences of <pseudo-compound-selector>* and <pseudo-class-selector>*

Alternatively:

- White space is forbidden:
-   - [...]
+ White space is forbidden between any of the components of `<complex-selector>` or `<complex-real-selector>`, 
+ except around `<combinator>` and any of the top-level components of `<attribute-selector>`.

Pseudo-elements are forbidden in :is(), :where(), :not(), :has(), :nth-*-child(), either in prose or with syntax, which is inconsistent.

I suggest to define that :is() and :where() should take <forgiving-[real-]selector-list> as a list of <complex-real-selector>, and :not() and :nth-*-child() should take <real-selector-list>.

This would also allow to clarify the grammar of :current(), for which it is not clear whether pseudo-elements are allowed or not (I assume they are not).

- The negation pseudo-class, :not(), is a functional pseudo-class taking a selector list as an argument.
+ The negation pseudo-class, :not(), is a functional pseudo-class taking a <real-selector-list> as an argument.
- 1. Parse a list of <complex-selector>s from input, and let selector list be the result.
+ 1. Parse a list of <complex-real-selector>s from input, and let selector list be the result.
+ <real-selector-list> = <complex-real-selector-list>

The argument of logical combination pseudo-classes following a pseudo-element should be restricted to the same set of pseudo-classes allowed to follow the pseudo-element. For example, ::before:is(type) shoud be invalid, but I do not think there is a normative definition for this.

- Note: The logical combination pseudo-classes pass any restrictions on validity of pseudo-classes to their arguments.
+ Note: The logical combination pseudo-class arguments are restricted to the pseudo-classes that are allowed to follow the pseudo-element.

A compound selector preceding a pseudo-compound selector is excluded from the definition of the latter. div::before::marker:hover is not defined, only ::before:hover. I think it should be included.

- A pseudo-compound selector is a pseudo-element selector, optionally followed by additional pseudo-class selectors, without any combinators.
+ A pseudo-compound selector is a pseudo-element selector, optionally preceded by a real compound-selector, optionally followed by additional pseudo-class selectors, without any combinators.
- <complex-selector> = [ <compound-selector>? <pseudo-compound-selector>* ]! [ <combinator>? [ <compound-selector>? <pseudo-compound-selector>* ]!  ]*
+ <complex-selector> = <pseudo-compound-selector> [ <combinator>? <pseudo-compound-selector> ]*
+ <pseudo-compound-selector> = [<compound-selector>? [<pseudo-element-selector> <pseudo-class-selector>*]*]!

@tabatkins
Copy link
Member

In 18. Grammar, a whitespace should also be forbidden:

Yup, amended the prohibition between top-level components of <compound-selector> to also apply to <pseudo-compound-selector>, and additionally prohibited it between the components of a <complex-unit> (which I just added to make this easier to talk about).

Pseudo-elements are forbidden in :is(), :where(), :not(), :has(), :nth-*-child(), either in prose or with syntax, which is inconsistent.

I've redefined <forgiving-selector-list> to use <complex-real-selector>; no need for an additional grammar term. If we ever allow :is()/:where() to do pseudo-elements, we can just change this definition. I also fixed :not() to say its argument is a <complex-real-selector-list>.

I've left the prose forbidding pseudo-elements, tho; it's useful to have that called out explicitly rather than just being implicit from the grammar.

The argument of logical combination pseudo-classes following a pseudo-element should be restricted to the same set of pseudo-classes allowed to follow the pseudo-element.

Ah yes, that text was slightly too restrictive in what it was talking about. Fixed. (It was just a note, and the normative text it was duplicating did have the correct wording.)

A compound selector preceding a pseudo-compound selector is excluded from the definition of the latter.
div::before::marker:hover is not defined, only ::before:hover.

I'm not sure where you're getting that from. div::before::marker:hover is definitely allowed by the grammar (both now that I've provided <complex-unit>, and before when it was just expanded in-place).

The prose definition did make mention of it being preceded by a compound selector, explicitly, which I've fixed as part of another edit. Now it correctly mentions that it can be preceded by a compound or pseudo-compound selector.

tabatkins added a commit that referenced this issue Dec 12, 2022
tabatkins added a commit that referenced this issue Dec 12, 2022
…re than just what pseudo-classes are allowed in their args. #7085
@cdoublev
Copy link
Collaborator Author

A compound selector preceding a pseudo-compound selector is excluded from the definition of the latter.
div::before::marker:hover is not defined, only ::before:hover.

I'm not sure where you're getting that from. div::before::marker:hover is definitely allowed by the grammar (both now that I've provided <complex-unit>, and before when it was just expanded in-place).

The prose definition did make mention of it being preceded by a compound selector, explicitly, which I've fixed as part of another edit. Now it correctly mentions that it can be preceded by a compound or pseudo-compound selector.

<complex-selector-unit> and the changes in the prose were exactly what I was hoping for. I am bit late, but thank you for all these edits.

If you allow me a last comment, I feel like defining :is() as taking <forgiving-selector-list> is a bit over-generalized because its argument is highly context-sensitive, as noted in your addition to the introduction of 4. Logical Combinations.

The logical combination pseudo-classes are allowed anywhere that any other pseudo-classes are allowed, but pass any restrictions to their arguments. (For example, if only compound selectors are allowed, then only compound selectors are valid within an :is().)

Maybe something like Unless otherwise specified for the context, :is() is a functional pseudo-class taking a <forgiving-selector-list> as its sole argument would be more helpfull.

Same thing for :not().

@tabatkins
Copy link
Member

Nah, grammatically it's a forgiving-selector-list. The rules for what selectors are valid inside in that list can vary, is all. No different than the fact that a type selector can be valid per the grammar but contain an unknown namespace, rendering it invalid.

Selectors conflates parsing validity and selector validity when talking about invalid selectors. Doesn't particularly matter which level we address something on, except for readability/understandability purposes.

jakearchibald pushed a commit to jakearchibald/csswg-drafts that referenced this issue Jan 16, 2023
jakearchibald pushed a commit to jakearchibald/csswg-drafts that referenced this issue Jan 16, 2023
jakearchibald pushed a commit to jakearchibald/csswg-drafts that referenced this issue Jan 16, 2023
jakearchibald pushed a commit to jakearchibald/csswg-drafts that referenced this issue Jan 16, 2023
…re than just what pseudo-classes are allowed in their args. w3c#7085
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

7 participants