-
Notifications
You must be signed in to change notification settings - Fork 645
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
[css-variables-2] Custom units as simple variable desugaring #7379
Comments
I tried to find reasons why that would be annoying to implement, but can't think of any. So ... sounds good? |
Like the Idea! I always think that the behaviour of custom properties should (possibly) not differ from normal properties. .el {
padding-top: 2rem;
padding-bottom: 2padding-top;
} |
That however, is complicated, because it adds new and arbitrary dependencies between things that previously couldn't depend on each other. |
This also opens up the way to supporting new units through a polyfill:
|
Received a reply on Twitter where the author noted that they find the syntax confusing
I think it's a matter of getting used to it. Once you know how it works, it's OK to read imo. |
To be honest I was going to make the same point. Sure I can get used to it, but my first reaction is it's a slightly shorter but considerably less intuitive alternative syntax for something we can already do - increasing cognitive load to save a few characters. Clarity wins over brevity for me, so I don't think it's an improvement. |
Yeah, readability is an issue, but we had the same concerns about custom properties in general at first, and it seems like that was indeed fine once your eyes got used to it. (After all, in CSS spaces are required around subtraction anyway.) At least we're guaranteed these aren't confusable with built-in units 😃 However, I don't believe this is "slightly shorter" - it's hugely shorter. In raw characters it's a difference of 14 characters per use, including two pairs of parentheses. It's also not immediately distinguishable from more complex math (particularly when embedded in a larger math expression), so you have to parse it manually and realize it's just scaling a variable. That's a big cognitive, visual, and typing load for something that's meant to extremely simple and common. |
The CSS Working Group just discussed
The full IRC log of that discussion<TabAtkins> Topic: custom units as variables<TabAtkins> github: <TabAtkins> github: https://github.com//issues/7379 <fantasai> TabAtkins: A week or two ago Jonathan Neal had a suggestion in Twitter, just doing on pre-processor side, about a way to finally address custom units <fantasai> TabAtkins: where you want to set some length and use multiples of it <fantasai> TabAtkins: used all over design systems, but doing today with variables is awkward <fantasai> TabAtkins: have to explicitly use a calc and multiply, quite a lot of writing for what is ~3ch for pre-defined units <fantasai> TabAtkins: suggestion is to treat custom units just as variables <fantasai> TabAtkins: so if have number with --unit, this is a variable reference <fantasai> TabAtkins: triggers same stuff, but resolve it into the appropriat ecalc <fantasai> TabAtkins: so 3--unit would become calc(3 * var(--unit)) <fantasai> TabAtkins: can set up lengths with @Property rule <fantasai> TabAtkins: can have some control about whether absolute links are resolved as time of use or ?? by setting as <length> or not <fantasai> TabAtkins: seems to solve most problems of custom units <fantasai> TabAtkins: but doesn't prevent us from doing something more complicated using registration <fantasai> TabAtkins: later <fantasai> TabAtkins: This allows more readable usage for design systems, not complicated on implementation side <fantasai> TabAtkins: one of our implementers was looking for implementations problems and couldn't find any <fantasai> TabAtkins: Thoughts? <bramus> q+ <dbaron> +1, sounds simple and valuable <fantasai> astearns: ?? comment that they didn't find it particularly readable <florian> haven't spent much time thinking about it, but seems reasonable (and terse) <astearns> s/??/faceless/ <fantasai> astearns: and hides complexity that maybe should be expressed <fantasai> faceless: [...] <astearns> ack bramus <fantasai> faceless: but no objection <miriam> q+ <fantasai> ???: Would allow ability to polyfill new units as well, e.g. define --brm and use your new custom unit code to polyfill it <fantasai> ???: seems really nice <dbaron> s/???/bramus/ <dbaron> s/???/bramus/ <fantasai> astearns: For browsers that do not support the new unit, what happens when you use the custom property <fantasai> bramus: browser would support the real unit, which you have just made your custom unit as an alias, and for browsers that don't support it you can give them the fallback <astearns> q? <fantasai> TabAtkins: if we had ability to do parse-time rejection of declared properties... but need JS for that <astearns> ack miriam <fantasai> miriam: I think this would help solve cases where we would need to remove units from a value, e.g. viewport width ppl want to use them in a unitless place like line-height, but this wouldn't help with that case, right? <jensimmons> q+ <fantasai> TabAtkins: Right, that wouldn't help. What you need is the unit math in the spec to be implemented. <astearns> ack jensimmons <fantasai> jensimmons: I really love this, just wish the -- doesn't need to be there <fantasai> jensimmons: I do think it would be helpful to get some feedback, can think of 2-3 ppl working on responsive typography be good to get their feedback <fantasai> jensimmons: they're using mix of absolute and relative sizing in setting type sizes etc. <fantasai> jensimmons: could be very powerful <fantasai> TabAtkins: That's one of the major use cases, so would be great to get their feedback <lea> I love how general this is, +1 from me too <fantasai> astearns: Sounds like this is something we should pursue <fantasai> TabAtkins: Where to put it? Variables 1 is fairly mature, so suggest starting Variables 2 <fantasai> astearns: Makes sense to me <lea> +1 for variables-2 <fantasai> +1 <fantasai> astearns: Proposed resolution is to start variables-2, with this as the feature to add <fantasai> astearns: any objections? <fantasai> RESOLVED: Start new draft of variables-2 and add custom units as described here <fantasai> astearns: Let's keep this issue open for a little bit, so Jen you can get some additional people to give feedback |
As defined here this might be helpful for design system helpers like [the 8pt grid](The Comprehensive 8pt Grid Guide. Start your UI project right with this… | by Vitsky | The Startup | Medium). With the ability to inject the custom unit increment into the calc function you can start to do more like adding modular scales to CSS, or creating a complex What if it looked something like this: @unit --scale { /* Changing this from @property to something more specific avoids the need for initial */
syntax: "<length>";
value: --value; /* This will be the input value */
formula: calc(1rem * pow(1.5, var(--value)));
}
h1 {
font-size: 4--scale; /* 5.0625rem */
} You might also be able to do this to simplify clamp functions: @unit --fluid {
syntax: "<length>";
value: --value; /* This will be the input value */
formula: clamp(1rem, 1vw * var(--value), 1rem * var(--value));
}
h1 {
font-size: 4--fluid; /* easier to implement clamp function */
} There is more opportunity than syntax sugar for design systems 8pt grid that I think is worthy of exploration. |
Adding a note from @jonathantneal via Twitter where he talks about adding some of the above functionality. I think it’s needed as it greatly expands the utility of custom units.
@property --fluid {
syntax: "<length>";
initial: clamp(1rem, 1vw * 1var, 1rem * 1var);
inherits: true;
}
h1 {
font-size: 4--fluid;
} |
To expand just a little on @scottkellum’s comment, I was wondering if we could utilize a ‘nesting’ unit for math, similar to how we might utilize a nesting selector for rules.
|
This proposal leaves open the possibility for a more full-featured custom units proposal in the future (if you registered a custom --unit it would just win over a --unit property), but I'm explicitly not trying to do anything more complicated than simple variable substitution right now. This is because simple variable substitution solves the 90% case, afaict, and getting any more complex starts to get really complicated. For example, if you do |
Put a slightly different way - the approach I'm taking (just multiply the value by the substituted variable) works for everything that acts like a "unit" should - can be added together, multiplied, etc. If you're wanting something that doesn't work under this approach, you're not wanting a "unit", but something more complex, and we should address that with a different method. For example, with your --fluid example, that can mostly be done as: @property --fluid {
syntax: "<length>";
initial: min(1vw, 1rem);
inherits: true;
} This does not enforce the "no fluid lengths are ever allowed be less than Like, pretend for a moment that we have simple custom functions, like: @custom-function --fluid(--value) {
arg-syntax: --value "<number>";
result: clamp(1rem, var(--value) * 1vw, var(--value) * 1rem);
} This could work - you can say |
Thanks @tabatkins this makes sense! When I think of modular scales I think of them like exponential rulers that map to my mental model of units but I can see how units need more interoperability than that mental model provides. I like this custom function idea. |
Not something against, but it will look a bit odd :)
|
Custom units as syntactic sugar would be great. Custom functions would be awesome. It would allow us to build our own functions such as @custom-function --progress(--current, --min, --max) {
arg-syntax: --current "<length>", --min "<length>", --max "<length>";
result: clamp(0%, 100% * (var(--current) - var(--min)) / (var(--max) - var(--min)), 100%);
}
:root {
--fluid-ratio: --progress(100vw, 375px, 1920px)
}
.usage {
font-size: mix(--fluid-ratio, 1rem, 1.25rem)
} Custom functions would allow us to write more readable css with less repetition. @tabatkins Do you know if there are there any issues tracking custom functions? |
There is not currently such an issue. Feel free to open one. ^_^ |
Thought of another use-case that I would use these for:
I can't have both. The only way to have option 1 is to give up on option 2. At that point I have to use custom props & calc in order to reference a site-specific base font size. That size is often a clamp function combining body {
--bem: clamp(1rem, 0.9rem + 1vi, 1.5rem);
font-size: var(--bem);
}
h1 {
/* font-size: calc(2 * var(--bem)); */
font-size: 2--bem;
}
.small {
font-size: 1rem;
} |
Thought I’d mention the specific web components / design systems challenge discussed in #7613 If you want to use font-relative values in a Web Component for a UI element meant to be reusable across many sites,
So – with custom units, I’d introduce a either a host-relative /* In web components scoped styles */
h2 {
/* font-size: calc(2 * var(--cem)); */
font-size: 2--cem;
}
/* In parent page styles */
html {
font-size: 62.5%;
}
my-component {
--cem: 1.6rem;
} |
I do not have a personal interest for this feature (I came across this issue to report the following potential oversights) but it seems a bit unfortunate to me to introduce a slight inconsistenty in the way a custom variable is referenced: one would be able to specify The oversights/suggestions:
|
right?
even worse : |
Yeah, a And the spec already says
But I agree that the spec should refer to |
Right, sorry! The unit is parsed as an an ident sequence (string) and it must match |
Sure, that's correct. The name has to be a custom property name anyway, since it's referencing a custom property.
Others have already said this - the custom unit is part of the
Rather, it can't, because this isn't two tokens.
Yes, it'll be defined as just a fancy way to have a variable reference, which gives this behavior by default. |
I've had "custom units" on the back burner of my mind for years now, and never got around to working on them - they had enough question to answer that it seemed exhausting. This morning, tho, I saw a tweet by @jonathantneal exploring the idea of making them just be sugar over normal custom property usage, and... I think I love it?
The example they had:
That is, if we see a "custom unit" (aka a dimension whose unit is a dashed-ident), we just treat it as a variable reference (triggering the normal behavior of using a variable - the property is assumed valid, etc), and expand it at variable-resolution time into exactly that calc - given
N--foo
, producecalc(N * (var(--foo)))
.I think this was problematic in the past because there were questions of initial value, resolution-time behavior, etc., but afaict those are all answered now by just using a registered custom property. That is, if you've registered your property/"unit" as a
<length>
, then you can set it like--unit:1.2em;
and it'll resolve that into an absolute length immediately, inheriting as a px length. (Or, if you do want the unit to resolve at point of use instead of point of definition, just leave it unregistered, or registered with a*
grammar.)Plus, a property registration suffices to fully define the "unit" immediately, since you can just set its size in the
initial
descriptor. But it also leaves open the possibility of redefining it on the fly like any other custom property, if needed.So the above example could instead be set up as:
This still leaves the door open to do a more full-featured custom unit thing later if we want; full-featured "registered custom units" would just override the variable-based behavior instead. But for now I think this does 95% or more of what we want custom units to do, in a flexible and readable manner.
Thoughts?
The text was updated successfully, but these errors were encountered: