w3c / csswg-drafts

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

[css-values-4][Editorial] `<condition>` type that other specs reference #10457

Open LeaVerou opened 5 months ago

LeaVerou commented 5 months ago

This came up when I went to implement the resolution from #10064 and spec my if() proposal.

Currently, we have several specs using one or more types of conditionals, and in every case, this is defined inline. This means the boolean operators (and, or, not) are also defined inline, which is awkward, error-prone, and easy to forget.

Some examples:

Media Queries 5:

<media-query> = <media-condition>
             | [ not | only ]? <media-type> [ and <media-condition-without-or> ]?
<media-type> = <ident>

<media-condition> = <media-not> | <media-in-parens> [ <media-and>* | <media-or>* ]
<media-condition-without-or> = <media-not> | <media-in-parens> <media-and>*
<media-not> = not <media-in-parens>
<media-and> = and <media-in-parens>
<media-or> = or <media-in-parens>
<media-in-parens> = ( <media-condition> ) | <media-feature> | <general-enclosed>

@supports:

<supports-condition> = not <supports-in-parens>
                     | <supports-in-parens> [ and <supports-in-parens> ]*
                     | <supports-in-parens> [ or <supports-in-parens> ]*
<supports-in-parens> = ( <supports-condition> ) | <supports-feature> | <general-enclosed>
<supports-feature> = <supports-decl>
<supports-decl> = ( <declaration> )

CSS Conditional 4 adds…:

<supports-feature> = <supports-selector-fn> | <supports-decl>
<supports-selector-fn> = selector( <complex-selector> )

CSS Conditional 5 adds…:

<supports-feature> = <supports-selector-fn> | <supports-font-tech-fn>
                    | <supports-font-format-fn> | <supports-decl>
<supports-font-tech-fn> = font-tech( <font-tech> )
<supports-font-format-fn> = font-format( <font-format> )

@container:

<container-condition> = [ <container-name> ]? <container-query>
<container-name> = <custom-ident>
<container-query>     = not <query-in-parens>
                      | <query-in-parens> [ [ and <query-in-parens> ]* | [ or <query-in-parens> ]* ]
<query-in-parens>     = ( <container-query> )
                      | ( <size-feature> )
                      | style( <style-query> )
                      | <general-enclosed>

<style-query>         = not <style-in-parens>
                      | <style-in-parens> [ [ and <style-in-parens> ]* | [ or <style-in-parens> ]* ]
                      | <style-feature>
<style-in-parens>     = ( <style-query> )
                      | ( <style-feature> )
                      | <general-enclosed>

@import:

<import-conditions> = [ supports( [ <supports-condition> | <declaration> ] ) ]?
                     <media-query-list>?

Tab’s @when proposal:

media() = media( [ <mf-plain> | <mf-boolean> | <mf-range> ] )
supports() = supports( <declaration> )

In fact, there is an inline issue right there proposing a lighter form of what I’m arguing for here:

Define "boolean algebra, with X as leaves" in a generic way in Conditional, so all the conditional rules can reference it directly, rather than having to redefine boolean algebra on their own.

Proposal

I propose we introduce a new <boolean> or <condition> value type that other specs can reference. It will include both the grammar for the boolean algebra, as well as bare and functional forms for each conditional. Each conditional type should also include metadata like:

Not all condition types need to be defined in values, we could extend the <condition> token in other specs, as we often do with value types. E.g. size queries may be a better fit in css-contain, since they are not valid anywhere else.

The prose should explain that specs using <condition> should specify:

  1. Which types of conditions are allowed?
  2. Which types of conditions can be specified bare, i.e. without a function name.

To make this more concrete, this is the state of the current specs with conditionals with this framing:

Host supports() media() size() style()
@container - - - ✅ Bare
@supports ✅ Bare - - -
@media - ✅ Bare - -
@import - ✅ Bare -
if() -
@if / @when -

While this change is editorial, I think it will pave the way for a lot of quick DX wins involving mixing and matching conditions (e.g. supports() in @media.

It could also allow creating JS APIs that handle conditions generically, avoiding the current issue of e.g. having matchMedia() for media queries, but no way to detect when a container query matches or not.

tabatkins commented 5 months ago

Committed a first pass at the generic <boolean> grammar

LeaVerou commented 5 months ago

Committed a first pass at the generic <boolean> grammar

Thanks! I think the ergonomics will become more clear once we edit specs to reference it too.

Btw if we call it <boolean> that sort of implies we also have constants like true and false. If we don’t want that, perhaps <condition> is more clear.

cdoublev commented 3 months ago

Can you please tell me if you plan to use context-sensitive production notations?

Maybe the form should be @media <bool-test> = <media-feature> because I am not sure how to annotate <context> <type> = value using the above form.

A CSS parser would need to look for the closest production, ie. <local><type> has priority over (global) <type>.

I do not know if <dfn for="@foo">&lt;type></dfn> = value would be equivalent to <dfn>&lt;@rule/type></dfn> = value.


I think the remaining changes are: ```diff - = [ ]? + = [ ]? - = [ supports( [ | ] ) ]? ? + = [ supports( [ | ] ) ]? ? - = | [ not | only ]? [ and ]? + = | [ not | only ]? [ and ]? - @supports { } + @supports { } ``` ... and this would need to be defined either in prose or as suggested above: | Context | Valid `` | | ----------------------- | ----------------------------------------------------------------- | | `` | `() \| () \| style()` | | `supports()` | `` | | `` | `` | | `@supports` | `` | I am not sure for `if()` and `@when`.
fantasai commented 3 weeks ago

@tabatkins and I drafted up a <<boolean [ test ]>> multiplier type into css-values-5. We think this covers the use cases pretty well, and should hopefully be reasonably easy to understand. See https://drafts.csswg.org/css-values-5/#boolean Agenda+ to review with the CSSWG.

cdoublev commented 3 weeks ago
Some minor things... (edit: fixed in 963fd9f) I do not understand what the plural (emphasized below) refers to: > The `` notation wraps another value type in the square brackets within it, e.g. ` ]>`, and represents that value alone as well as boolean combinations of **those values** [...] I think *"evaluates"* is more appropriate than *"represents"* (which is already used in the quote above): > The `` production **represents** a true, false, or unknown value [...] I think `` should be ` ]>` on the second line: > ``` > ]> = not | ... > = | ( ) | > ``` ~~`` does not exist and should be [``](https://drafts.csswg.org/css-conditional-5/#typedef-style-feature):~~ (edit) never mind, I guess the spec of container queries will be updated accordingly. > ``` > = ]> > = () | style( ) | scroll-state( ) > = ) ]> | > = ) ]> | > = ) ]> | > ```

Perhaps syntax attribute names should be considered, noting that authors can now specify a value including a <syntax>. For example, <number range=[0,∞] unit=px> and <boolean from=[<test>] or similar.

css-meeting-bot commented 2 weeks ago

The CSS Working Group just discussed [css-values-4][Editorial] `<condition>` type that other specs reference, and agreed to the following:

The full IRC log of that discussion <TabAtkins> fantasai: we have a lot of places we'
<TabAtkins> fantasai: we're using the not/and/or microsyntax - MQs, SQs, style queries, etc
<TabAtkins> fantasai: and now if()
<TabAtkins> fantasai: So we decided it would be more understandable to factor out that commonality into its own syntax "multiplier"
<TabAtkins> fantasai: similar to * and # in grammars
<TabAtkins> fantasai: So we introduced `<boolean [<grammar-here>]>`
<TabAtkins> fantasai: This isn't for authors to write, it's part of our Value Definition Syntax
<fantasai> https://drafts.csswg.org/css-values-5/#boolean
<TabAtkins> fantasai: whatever's inside the brackets is a parameter for the boolean tree (it's the leaves of the and/or/not tree)
<fantasai> https://drafts.csswg.org/css-values-5/#value-defs
<TabAtkins> fantasai: It's defined here, and also in the valdefs list
<TabAtkins> fantasai: [describes the syntax]
<TabAtkins> astearns: Currently it's just in Values 5, hasn't moved to toher specs?
<TabAtkins> fantasai: right
<dbaron> btw my comment about naming was about sounding like true/false rather than and/or/not
<fantasai> TabAtkins: we have an example of applying it to @container queries, since that's a complex grammar
<fantasai> TabAtkins: makes it easier to understand
<fantasai> astearns: [reads dbaron's comment]
<fantasai> TabAtkins: Lea also had this comment
<fantasai> TabAtkins: open to suggestions
<TabAtkins> fantasai: we didn't want to go with "condition" because it's such a generic term, seemed like it would actually be less clear
<TabAtkins> dholbert: "boolean-condition"?
<TabAtkins> fantasai: note that it is a functinoal syntax, not just `<boolean>` by itself, there's something between the [].
<TabAtkins> astearns: do we want any particular resoltion?
<TabAtkins> fantasai: if we're happy, we can accept adding it to the VDS
<TabAtkins> astearns: I think I'd like to noodle on the name a bit more
<TabAtkins> astearns: but i don't really have an idea of what to repalce it with
<TabAtkins> fantasai: we could make it longer with `<boolean-expression [...]>` but unsure if that's clearer
<TabAtkins> dholbert: makes it clearer it's not just true/false in the programming sense
<TabAtkins> dholbert: in the issue I saw a bunch of `<*-condition>` grammar terms used with it, so maybe `<boolean-condition>` would be good to follow it
<TabAtkins> fantasai: What we're trying to say is that this is the epxression syntax, not the conditions themselves. The condition is inside the []
<fantasai> <boolean[ <test> ]> = not <boolean-group> | <boolean-group>
<fantasai> [ [ and <boolean-group> ]*
<fantasai> | [ or <boolean-group> ]* ]
<fantasai> <boolean-group> = <test> | ( <boolean[ <test> ]> ) | <general-enclosed>
<TabAtkins> fantasai: [explains]
<TabAtkins> fantasai: it's kinda a syntactic function in the same way as + is, just more sophisticated
<fantasai> <container-query> = <boolean[ <cq-test> ]>
<TabAtkins> astearns: I think I do prefer boolean-expr in that last example
<TabAtkins> fantasai: ok
<kbabbitt> I like <boolean-expression[...]> as well
<TabAtkins> fantasai: so should we resolve to add boolean-expression?
<kbabbitt> boolean-expr is fine too
<TabAtkins> TabAtkins: i really want to shorten it to expr, I did that reflexively when minuting
<fantasai> PROPOSED: Add <boolean-expression[...]> as a value definition syntax function multiplying its argument into a boolean expression microsyntax
<TabAtkins> fantasai: i'm fine, tho we don't usually use shortened terms
<fantasai> fantasai: it would be exposed in @property?
<TabAtkins> astearns: let's propose it with boolean-expr
<fantasai> TabAtkins: no, not unless we say so
<TabAtkins> astearns: objection?
<fantasai> RESOLVED: Add <boolean-expr[...]> as a value definition syntax function multiplying its argument into a boolean expression microsyntax
svgeesus commented 1 week ago

So https://github.com/w3c/csswg-drafts/labels/Needs%20Edits is removed (for values 5) but the specs listed at the top of the issue still all need to be edited.