Csswg-drafts: [css-fonts] font-weight: bolder and lighter are counter-intuitive

Created on 14 Jun 2018  ·  18Comments  ·  Source: w3c/csswg-drafts

I'm not sure if it will be possible with future calc() improvements, but it would be great if it was possible to write:

strong {
    font-weight: calc(inherit + 100); /*
    font-weight: calc(inherit + 200);
    font-weight: calc(inherit + 300);
    font-weight: calc(inherit + 400);
    font-weight: calc(inherit + 500);
    font-weight: calc(inherit + 600);
    font-weight: calc(inherit + 700);
    font-weight: calc(inherit + 800); */    
}

As opposed to bolder and lighter that are really hard to use and that never do what the designer wants.

Thanks in advance.

css-values-5

Most helpful comment

With few additional tools, i. e. functions, authors could build individual stepping patterns.

css :root { --font-weights: 100, 200, 300, 400, 500, 600, 700, 800, 900; --i: 4; } foo, bar { font-weight: index(var(--i), var(--font-weights)); } foo { --i: max(var(--i) + 1, 9); } bar { --i: min(var(--i) - 1, 1); }

All 18 comments

Allowing inherit in calc is probably a major CSS syntax change which may relate to CSS Syntax and probably CSS Cascading.

There's nothing wrong with it Syntax-wise (global keywords aren't handled at that level). Handling global keywords in the middle of other properties isn't problematic either - you can already do that in font-family. (font-family: foo inherit; is fine, it refers to a font named "foo inherit".)

It's just putting keywords into calc() that's problematic right now. But grabbing the inherited value of a property via a function or something shouldn't be problematic.

Actually, maybe it's better to have a special value like "bolder" and "lighter" that instead selects the immediately larger or lighter weights available for the current font.

In other words, font-weight: calc(inherit + 100) will have no effect if inherit is 300 and there is no 400 font-weight for the current font.

But grabbing the inherited value of a property via a function or something shouldn't be problematic.

I moderately disagree with this. It may not be a problem for font-weight especially given that it doesn't seem likely that we are going to extend it to a multi-value property. But it could happen that a property which currently only accepts a single numeric value can be extended to support non-numeric (computed) value, multiple values, or even be converted into shorthand. In those cases, we may have trouble to continue supporting this mechanism in those properties.

(Actually it's not even clear to me whether font-weight is going to be stuck in its current form. For example, could there be some new kind of font variation axis which supports adjusting vertical and horizontal direction weight differently, and its request becomes common so that we want to have font-weight support it directly?)

came here via https://github.com/w3c/csswg-drafts/issues/2764#ref-issue-365585720
so.. bump!

The above problem could be fixed if the _bolder_ and _lighter_ keywords would actually do what their name suggests; at least for variable fonts. No need for keyword handling in calc() then.

Maybe some variation of current() or inherited() could do the trick if nesting functions in calc() is supposedly easier. But that's a different story to than using an unknown variable set of bolder/lighter fonts.

_Variable Fonts are a thing now!_

Unfortunately neither "bolder" nor "lighter" move to the next weight variation within a _variable font_, but act binary as "bold" or "un-bold" aka "normal". That was fine when most ordinary fonts typically featured a binary state for normal/bold as well.

Also going bolder or lighter (font wise) within nested elements does not imply moving in steps of 100 _only_. A variable font could contain just three presets for its the "wght" axis using 200, 400, 600 or any other set of arbitrary numbers between 1 and 1000.
The same is true for an equivalent set of carefully crafted @font-face declarations using classic single font files.

With a fallback list of different variable fonts such as font-family: "Bahnschrift", "League Spartan Variable", "Source Sans Pro" one could build a working ramp of the _proper_ font-weights of such fonts whilst being ignorant about what "wght" values actually exist.

_Using "bolder" or "lighter" could (should) continiously select the next available weight variation._

Here each letter of the words "Fonts" and "Mood" is a set of nested <b> elements that might progressively get bolder from whatever weight <p> currently has set and eventually reaches whatever weight ("wght") the font's upper limit would be.

b {font-weight:bolder} /* life could be easy ... */

```html

Variable Fonts R côôl

Goooood Morning

![variable-bolder-fonts](https://user-images.githubusercontent.com/638340/73126205-0510c300-3fb0-11ea-8478-06397e423396.png)
Although the markup is ugly, the CSS remains fairly simple and _intuitive_.

Today I can do the same thing but with much more effort and have to use an explicit amount of nested B selectors - unlike the oneliner above. I also need to keep the ramp values at a specific start/end point or the result could look choppy when used in a possibly different context. 
If `<p>` happens to be set elsewhere to the usual `normal` (400), the effect would not look as intended.
```css
/* ... but it's not ... */
p {font: 200 2rem/1 "League Spartan","Bahnschrift","Source Sans Pro",sans-serif;}
p > b { font-weight: 300 }
p > b > b { font-weight: 400 }
p > b > b > b { font-weight: 500 }
p > b > b > b > b { font-weight: 600 }
p > b > b > b > b > b { font-weight: 700 }
p > b > b > b > b > b > b, strong { font-weight: 800 }

Using CSS properties the CSS can become simpler ...

p {--w:1 }
p b { font-weight: calc(var(--w) * 200) }

but the HTML gets even worse:

<p>G<b style="--w:1">o<b style="--w:2">o<b style="--w:3">o<b style="--w:4">o<b style="--w:5">d</b> Morning!</b></b></b></b></p>

Cheers.

Unfortunately neither "bolder" nor "lighter" move to the next weight variation within a variable font, but act binary as "bold" or "un-bold" aka "normal". That was fine when most ordinary fonts typically featured a binary state for normal/bold as well.

And tat is the crux of the issue. bolder and lighter date from a time when:

  • CSS was vastly simpler and less used than now
  • there were no WebFonts
  • there were no variable fonts
  • CSS was seen as a set of "hints" to affect presentation, not a completer visual design system

Looking at the definition (bolder means _bolder, if available, or the same weight, but at least not lighter _) which is as close to meaningless as can be while still being somewhat testable, I would class the bolder and lighter keywords as legacy compat features only.

Achieving the desired effect using calc, though, seems like a much better approach.

With few additional tools, i. e. functions, authors could build individual stepping patterns.

css :root { --font-weights: 100, 200, 300, 400, 500, 600, 700, 800, 900; --i: 4; } foo, bar { font-weight: index(var(--i), var(--font-weights)); } foo { --i: max(var(--i) + 1, 9); } bar { --i: min(var(--i) - 1, 1); }

[EDIT: my memory failed me miserably onthis. See comments below about the confusion.]
@Crissov ignoring the non-existing index() function, I had mixed results with setting a var that refers to itself (--i: max(var(--i) + 1, 9);), esp. in Edge/18 - although that'll be history soon.
I just noticed min(()/max() are now supported in Blink !! 😎🥰

Sidenote> increments/decrements could/should work like counters, if we could store their value in a variable at runtime...

I had mixed results with setting a var that refers to itself

Because that's not allowed and immediately causes the variable to be invalid-at-computed-value-time ^_^.

I had mixed results with setting a var that refers to itself

Hopefully you had consistent reults i.e. that didn't work anywhere. CSS is declarative. All uses of a variable have the same value. There is no order of execution. It is no an imperative programming language.

That and other reasons (inheritance, initial value) is why 'CSS Variables' is now termed 'Custom Properties'.

You're right of course! 🤐
I dug up the test file I confused this with.

--val: calc(2 + 2); 
--foo: calc(2 * var(--val)); /* Edge chokes */

The snippet's from a comment in an experiment I did ages ago toying with hsl() utilizing tons of calc() and var()s en masse to build basic color schemes and ramps.
Edge choked if calc() contained a var() that itself had calc() as it's _only_ value. Very occult behaviour.

I can't reproduce this today as the above code for --foo works fine in Edge/18, and given the file's age it must have been an earlier release.

How about adding two new functions with the same names as the existing keywords?

font-weight: bolder(250); /* add 250 to the weight */
font-weight: lighter(250); /* subtract 250 from the weight */
font-weight: bolder; /* use the legacy behavior */

This doesn’t require a CSS-wide change.

Yes, I should have checked my made-up code better. (In actual production code, Iʼve only used very simple vars yet.) I could have tried solving it with counters or the current font weight or the actual width of 1ch instead, but I hope the general idea came across.

As for dedicated functions suggested by @litherum, why not more generic ones? For instance: increase(<amount-to-add-to-inherited-value>, <clamp-fallback>) and decrease(<amount-to-substract-from-inherited-value>, <clamp-fallback>). The fallback value would be used if the increment or decrement operation resulted in a value outside the valid range or used incompatible units. These functions would need to work anywhere calc() does and use the appropriate (partial) inherited single value, e. g. in background-position: decrease(10px, center) increase(10%, 100%);.

coming back to @tabatkins earlier remark:

But grabbing the inherited value of a property via a function or something shouldn't be problematic.

Since multiple weights are a variable font thing anyways, I'd then suggest to use a property that already officially deals with it, instead of teaching and old dog new tricks.
With regular var()s that'd be:

font-variation-settings: "wght" calc( var(--normal) + 250);

Not sure if this is also animatable (some would certainly want that). I remember a CodePen doing that, but there might've been some JS involved - I don't remember that ;)

So if there were a "function or something", maybe sth. like this could get us much further:

font-variation-settings: "wght" calc( current() + 250);
font-variation-settings: "wght" calc( current(wght) + 42 * 100);

I have no opinion onits name, but thing is: this is a multi value property like there are many in CSS. The syntax and smartness of this "gimme the current|inherited value" function will be interesting to draft and for implementors to code :)

Cheers.

As for dedicated functions suggested by @litherum, why not more generic ones?

We already have generic ones. They are calc(), +, and -. The title of this issue is “font-weight: bolder and lighter are counter intuitive.”

If a generic function is preferred, we should close this issue as “no change” because calc() already exists.

font-variation-settings: "wght" calc( current() + 250);
this is a multi value property like there are many in CSS

Right. In particular, this sets the wght and resets everything else that font-variation-settings might have contained.

calc() is too generic, though, unless inherited, inherit, parent or something like that would be added as a local math constant (as suggested in the OP), similar to the new global math constants pi and e #4688. I think this recent development could solve the concerns initially raised by @upsuper.

Dedicated increment and decrement functions would avoid this new concept, but they would be limited to addition and subtraction, respectively, when you sometimes want multiplication or division (e g. in font size steps). In conclusion, they would probably be an inferior solution.

If a generic function is preferred, we should rename this issue (or create a new one).

font-variation-settings: "wght" calc( current() + 250);
this is a multi value property like there are many in CSS

Right. In particular, this sets the wght and resets everything else that font-variation-settings might have contained.

oh, I forgot that part :/ Thanks for the heads up!
However, this should work though (not tested):

--mo-variations: "wght" 300, "xhgt" 0;
font-variation-settings: "wght" calc( current() + 250) , var(--mo-variations);

[edit typo]

Was this page helpful?
0 / 5 - 0 ratings