w3c / csswg-drafts

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

[css-values-5] if() conditions with calc() comparisons #11104

Open fantasai opened 2 weeks ago

fantasai commented 2 weeks ago

Several of the examples in https://github.com/w3c/csswg-drafts/issues/5009 were comparing <length> values, but this was not included in https://github.com/w3c/csswg-drafts/issues/10064

Do we want this functionality, and if so what are the use cases and what should be the syntax?

Currently specced conditionals are:

  supports( [ <supports-condition> | <ident> : <declaration-value> ] ) |
  media( <media-query> ) |
  style( <style-query> )
tabatkins commented 2 weeks ago

Currently all the conditions in if() are functions; I propose we use the "just parentheses" syntax for this. Namely:

( <calc-sum> <mf-comparison> <calc-sum> )

This way you can write, say, flex-flow: if( (100vw > 200px): row ; else : column; );

(or, spread over multiple lines:

flex-flow: if(
    (100vw > 200px): row;
    else: column;
);

)

Loirooriol commented 2 weeks ago

if() is defined to resolve at computed-value time. But this needs layout:

width: if( (100% > 200px): max-content; else : stretch; );

So I guess it needs to be invalid? But it would be cool to have an if() as a syntax sugar for arithmetic conditionals, e.g.

width: if( (100% > 200px): 1em; else : 1lh; );
/* behaves like this: */
width: calc(max(0, sign(100% - 200px)) * 1em + (1 - max(0, sign(100% - 200px))) * 1lh)
tabatkins commented 2 weeks ago

Yeah, I think you'd need to use a CQ condition to test something similar to that; we need to be able to resolve the condition at computed-value time. I suppose we'd just say that the condition is always false if it uses values that can't be resolved at computed-value time.

I was hoping that if() would subsume the need for a conditional math function, but I guess it doesn't. :/ Gonna be a little hard to explain when exactly calc-if() is needed, unfortunately.


An additional request: the other thing brought up as a common conditional need is just comparing a value to an ident, so you can set a custom property on a component like --style: button and it'll do different things.

I think we can just slot that into the parenthesized syntax, with an <ident> [ '=' | '!' '=' ] <ident> form. It overlaps grammatically with the calc-sum version when you compare calc keywords with each other, but it would resolve the same under either interpretation, so that's fine.

LeaVerou commented 2 weeks ago

Why do we need the parens at all? What kind of ambiguity exists if we can simply use bare comparisons? Also, at first, we should probably expand the syntax of style() to allow for comparison operators in addition to :.

tabatkins commented 2 weeks ago

Why do we need the parens at all? What kind of ambiguity exists if we can simply use bare comparisons?

You need parens to work with the <boolean[]> syntax - the base grammar of a boolean expression must be either parenthesized or functions, in order to match with the <general-enclosed> term that catches future-compat.

We could, in theory, let you omit the parens if you were just doing a single comparison on its own. But then you'd need to add them if you did and/or/not. I'm moderately against the consistency break. Every existing construct that does comparisons uses parens, like @media (width < 600px) {...}

Also, at first, we should probably expand the syntax of style() to allow for comparison operators in addition to :.

That's something for Containment to define; if() just pulls that syntax in. (But yes, we should allow it.)