w3c / csswg-drafts

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

Clarify precedence of font-feature-settings descriptor and font-variant property #7498

Open faceless2 opened 2 years ago

faceless2 commented 2 years ago

I've put a test up at https://jsbin.com/veqidew/edit?html,output, here it is too:

 <style>
  @font-face {
    font-family: Lato-On;
    src: url("https://bfo.com/misc/Lato-Medium-Liga.ttf");
    font-feature-settings: 'liga' on;
  }
  @font-face {
    font-family: Lato-Off;
    src: url("https://bfo.com/misc/Lato-Medium-Liga.ttf");
    font-feature-settings: 'liga' off;
  }
  .feature-on {
     font-family: Lato-On;
  }
  .feature-off {
     font-family: Lato-Off;
  }
  .variation-off {
    font-variant-ligatures: none;
  }
  .variation-on {
    font-variant-ligatures: initial; /* = normal */
  }
  p {
    font-size: 100px;
    margin: 0;
  }
 </style>
 <body>
  <p class="feature-off variation-off">fi</p>
  <p class="feature-on variation-off">fi</p>
  <p class="feature-off variation-on">fi</p>
  <p class="feature-on variation-on">fi</p>
 </body>

The font precedence rules in both Fonts-3 and Fonts-4 seem clear - we are to:

Which means that we start with the "liga" feature on (the default), that's overridden by the font-feature-settings descriptor, and is then always overridden by the font-variant-ligatures property - the initial value is "normal" which turns it on, or I can set to "none" which turns it off. But it will always change - there is no font-variant-ligatures value that means "don't change anything".

This means that the first two paragraphs of the test have no change, and the second two have a ligature.

Correct?

The reason I ask is that's not the behaviour in Webkit or Gecko, which give precedence to font-feature-settings (Blink doesn't support the descriptor). It's also not what's being tested in https://wpt.fyi/results/css/css-fonts/font-feature-resolution-001.html from where the above test was derived, which came from the Chromium team. So I have a nagging doubt I have misunderstood. I've raised a PR anyway for that test, if I'm wrong it can be closed.

Related: https://github.com/w3c/csswg-drafts/issues/4358 - I think this could be closed, or at least reduced to remove font-feature-settings given you've got two browsers implementing the descriptor.

svgeesus commented 2 years ago

the initial value is "normal" which turns it on, or I can set to "none" which turns it off. But it will always change - there is no font-variant-ligatures value that means "don't change anything".

That seems like a bug

not the behaviour in Webkit or Gecko, which give precedence to font-feature-settings (Blink doesn't support the descriptor).

Hmm.

Giving the descriptor precedence seems more reasonable, and also aligns with implementations. If others agree, that means Fonts 4 should be changed, and Fonts 3 errata'ed.

(Leaving the PR alone for now, lets get consensus on the direction first).

faceless2 commented 2 years ago

Giving the descriptor precedence seems more reasonable, and also aligns with implementations. If others agree, that means Fonts 4 should be changed,

Conveniently (as there's going to be a conversation on this) I would have gone the other way. As font-stylesheets seem to be commonly created by someone other than the document author (eg fonts.google.com), giving them precedence over any font-variant properties used in the main document stylesheet seems the wrong way around to me. It would force authors to use the font-feature-settings property to override the descriptor, which is too low-level.

That seems like a bug

For context, rlig, liga, clig, calt, locl, ccmp, mark, mkmk are the only features on by default. liga and clig can be controlled by font-variant-ligatures: common-ligatures property, and calt can be controlled by font-variant-ligatures: contextual. So those three features the only ones where the current precedence rules would mean that setting them in the font-feature-settings descriptor would have no effect.

It doesn't seem like a big issue to me, they're in this testcase but I can't imagine it happening much in the real world. The cases I imagine for the font-feature-settings descriptor aren't ligatures: they're default stylistic alternates or weird custom shaping rules, that sort of thing. But I'm preprared to be corrected on that.

jfkthame commented 2 years ago

I think the issue here is a question of what the normal value for font-variant (and its subproperties) means. It's been a while since I worked on this, but my recollection is that our intention when designing this stuff was that normal means "use the default behavior for the font", not "set this particular collection of features that are considered normal". This could probably be made clearer in the spec, but I think it is the most natural interpretation of the Feature Precedence section:

General and font specific font feature property settings are resolved in the order below, in ascending order of precedence. This ordering is used to construct a combined list of font features that affect a given text run.

  1. Font features enabled by default, including features required for a given script.
  2. If the font is defined via an @font-face rule, the font features implied by the font-feature-settings descriptor in the @font-face rule.
  3. Font features implied by the value of the ‘font-variant’ property, the related ‘font-variant’ subproperties and any other CSS property that uses OpenType features (e.g. the ‘font-kerning’ property).
  4. Feature settings determined by properties other than ‘font-variant’ or ‘font-feature-settings’. For example, setting a non-default value for the ‘letter-spacing’ property disables optional ligatures.
  5. Font features implied by the value of ‘font-feature-settings’ property.

The font-variant-* properties come in at step 3 here, but (as I understand it), the normal value of a font-variant-* property does not mean "set it to a specific 'normal' value", but rather "don't touch it". Remember that default features such as common ligatures are already enabled at step 1; font-variant-ligatures: normal doesn't have to do anything in order to make this happen.

The font-feature-settings descriptor, then, which is step 2 here, is a tool to modify the defaults for the font, providing an updated base state to which font-variant properties can be applied. So if the @font-face rule says font-feature-settings: "liga" off, the default behavior of this font is to not use common ligatures. It's still possible for the author to override this, using font-variant-ligatures: common-ligatures, but font-variant-ligatures: normal does not change the ligature features from the (descriptor-modified) default.

I hope that's coherent enough to make sense.... I believe this reflects the implementation we've been shipping for some years now, and (as I understand it) this was always the intended relationship.

svgeesus commented 10 months ago

Re-opening the discussion

It would force authors to use the font-feature-settings property to override the descriptor, which is too low-level.

Agree that is an undesirable outcome. But

It's still possible for the author to override this, using font-variant-ligatures: common-ligatures, but font-variant-ligatures: normal does not change the ligature features from the (descriptor-modified) default.

is also persuasive.

svgeesus commented 1 month ago

Re-re-opening this discussion, as there has been a WPT pull request open for 4 years now so let's decide one way or the other.

@drott @jfkthame @faceless2

Giving the descriptor precedence seems more reasonable, and also aligns with implementations. If others agree, that means Fonts 4 should be changed, and Fonts 3 errata'ed.

That is still my position.