font-feature-settingsとカスタムプロパティ

font-feature-sttingsプロパティは便利だけど、値の意図しない上書きが発生しやすいという扱いづらさがある。たとえば「ページ全体でloclを有効にした上で、h1要素ではpaltを有効にしたい」というようなとき、以下のようなCSSを書いてしまいがちだ。

body {
  font-feature-settings: "locl" 1;
}

h1 {
  font-feature-settings: "palt" 1;
}

しかしこのときh1palt機能タグはbodyから継承されたlocl機能タグに追加されるのではなく、これを上書きしてしまう。つまりh1ではloclが無効になる。これは正しくは以下のように書かないといけない。

body {
  font-feature-settings: "locl" 1;
}

h1 {
  font-feature-settings: "locl" 1, "palt" 1;
}

このように、font-feature-settingsプロパティを指定するときはどのような値が継承されているかをつねに把握し、そこに値を「重ねて」指定する必要がある。機能タグが2つとか3つとかのうちはまだいいけど、ちょっと複雑なことをしようとすればすぐに管理しきれなくなるだろう。

こういった煩雑さを回避する方法として、ユニバーサルセレクターとカスタムプロパティ(CSS変数)を利用する方法を考えてみた。

*,
*::before,
*::after {
  font-feature-settings:
    "locl" var(--locl, 1),
    "palt" var(--palt, 0);
}

h1 {
  --palt: 1;
}

まずプロジェクトで利用しているすべての機能タグをユニバーサルセレクターで列挙し、デフォルトで有効か無効か(01、またはonoff)をvar()関数のフォールバック値として定義しておく。その上で機能タグを有効(または無効)にしたいセレクターでカスタムプロパティを指定する。こうすることで、カスタムプロパティを指定したセレクター以外ではフォールバック値が適用されることになる。注意すべきなのは、機能タグを追加するときに該当のセレクターだけではなくユニバーサルセレクターにもデフォルト値とともに追加しないといけないという点。

そもそもfont-feature-settingsプロパティは低レベルAPIであり、可能な限りfont-variant系プロパティを利用することが推奨されている。こういった値の継承にともなう扱いの難しさを考えても、それが望ましい。しかし一方で、とくにCJKについてはfont-variant系の仕様と実装が十分ではないという現状があり、font-feature-settingsに頼らざるを得ない場面は少なからずある。その利用には慎重になるべきという前提のもと、思わぬバグを生まないための工夫として、このカスタムプロパティを使う手法はそれなりに有効な気がする。