CSSで、可変フォントの太さや幅を細かく指定する方法
可変フォント(variable font)は、太さや幅などのバリエーションを1つのファイルに収めたフォントです。CSS の font-variation-settings を使うと、"wght" 650 のように軸の名前と数値を書いて、その値の字形で表示できます。太さ(wght)や幅(wdth)は専用のプロパティで、フォント独自の軸は font-variation-settings で指定するのが基本です。
検証環境は Ubuntu 24.04、Node.js 22.23.4、Playwright 1.63.0、Chromium 153(ヘッドレス)です。フォントは、npm の @fontsource-variable/roboto-flex 5.3.0 に入っている Roboto Flex(ラテン文字の範囲のファイル)を @font-face で読み込みました。文字の太さは、48px の「Typography」を描いた画像の黒い画素の量で比べています。Firefox と Safari、日本語の可変フォントでは試していません。各ブラウザーの対応バージョンは、互換性データ(@mdn/browser-compat-data 8.1.8)の値です。
- 1. フォントが持つ軸と、値の範囲を調べる
- 2. 書き方は「引用符で囲んだ4文字のタグ」と数値
- 3. 太さと幅は、font-weight と font-stretch でも同じになる
- 4. 大文字・小文字を間違えたり、範囲を超えたりしたとき
- 5. font-weight より font-variation-settings が優先される
- 6. 子要素で一部の軸だけを書くと、親の指定が消える
- 7. opsz は、font-optical-sizing が文字サイズに合わせて決める
- 8. アニメーションさせるなら、両端で同じ軸を並べる
- 9. @font-face の font-weight を1つの値にすると、太さが変わらない
- 10. ブラウザーの対応
フォントが持つ軸と、値の範囲を調べる
font-variation-settings に書ける軸は、フォントによって違います。Python の fontTools でフォントファイルの軸の一覧(fvar テーブル)を読むと、Roboto Flex には13の軸がありました。その一部です。
| 軸のタグ | 内容 | 最小 | 既定 | 最大 |
|---|---|---|---|---|
wght |
太さ | 100 | 400 | 1000 |
wdth |
幅 | 25 | 100 | 151 |
opsz |
オプティカルサイズ | 8 | 14 | 144 |
GRAD |
グレード | -200 | 0 | 150 |
XTRA |
字の内側の空き(カウンター)の横幅 | 323 | 468 | 603 |
wght、wdth、opsz は、仕様で決められた「登録された軸」です。登録された軸は小文字、GRAD や XTRA のようなフォント独自の軸は大文字で書くのが慣習ですが、従っていないフォントもあります(MDN の font-variation-settings の説明)。

書き方は「引用符で囲んだ4文字のタグ」と数値
軸のタグは、引用符で囲んだ4文字で書きます。複数の軸を指定するときは、カンマで区切ります。
.title {
font-family: "Roboto Flex", sans-serif;
font-variation-settings: "wght" 650, "wdth" 75;
}
検証環境の Chromium で CSS.supports() を使って確かめると、"wght" 650.5 のような小数や "GRAD" -50 のような負の数、calc(600 + 50) は使えました。引用符のない wght 650、3文字の "wgh" 650、数値のない "wght" は、宣言ごと無効になりました。
同じ軸を2回書くと、後の値が使われます。"wght" 700, "wdth" 80, "wght" 500 の計算値は "wdth" 80, "wght" 500 でした。計算値では、軸がタグの文字コードの順(大文字が小文字より先)に並べ替えられます(CSS Fonts 4 の仕様の font-variation-settings の節)。
太さと幅は、font-weight と font-stretch でも同じになる
48px の「Typography」で、幅と黒い画素の量を比べました。
| 指定 | 幅 | 黒い画素の量 |
|---|---|---|
| 指定なし | 218.83px | 1974 |
font-weight: 650 |
249.22px | 3760 |
font-variation-settings: "wght" 650 |
249.22px | 3760 |
font-stretch: 75% |
190.03px | 1808 |
font-variation-settings: "wdth" 75 |
190.03px | 1808 |
font-weight: 650; font-stretch: 75% |
223.80px | 3507 |
font-variation-settings: "wght" 650, "wdth" 75 |
223.80px | 3507 |
font-weight: 650 は "wght" 650 と、font-stretch: 75% は "wdth" 75 と、幅も黒い画素の量も同じでした。CSS Fonts 4 の仕様でも、wght には font-weight を、wdth には font-width(font-stretch の新しい名前)を使い、font-variation-settings で指定しないように勧めています。理由として、専用のプロパティなら軸ごとに別々に指定でき、可変フォントでないフォントでも太字などを合成できる場合があることを挙げています。
font-width という名前は、互換性データでは Chrome と Edge が未対応です。Chrome でも幅を変えたいときは、font-stretch を使います。
font-variation-settings を使うのは、GRAD や XTRA のような、専用のプロパティがない軸を変えたいときです。たとえば "GRAD" 150 では、幅は 218.83px のまま、黒い画素の量だけが 1974 から 2790 に増えました。"GRAD" -200 では 1170 に減りました。Google Fonts の軸の登録簿でも、GRAD(グレード)は全体の幅や行の折り返しを変えずに、字を細く・太く見せる軸と説明されています。
大文字・小文字を間違えたり、範囲を超えたりしたとき
軸のタグは、大文字と小文字を区別します(仕様の font-variation-settings の節)。"WGHT" 900 も "grad" 150 も、CSS.supports() では有効な値でしたが、表示は指定なしと同じでした。仕様では、フォントにない軸の指定は無視されます。値としては正しいので、font-variation-settings が効かないときは、タグの大文字・小文字を確かめます。
フォントの範囲を超える値は、範囲の端の値として扱われます。"wght" 2000 は "wght" 1000 と、"wdth" 10 は "wdth" 25 と、幅も黒い画素の量も同じでした。
font-weight より font-variation-settings が優先される
同じ要素に font-weight: 300 と font-variation-settings: "wght" 800 を書くと、表示は "wght" 800 だけを書いたときと同じでした。仕様のフォントの機能と軸を適用する順番では、font-variation-settings は font-weight の後に適用されます。
この優先順位は、継承されたときにも効きます。
.note {
font-variation-settings: "wght" 300;
}
.note strong {
font-weight: 800;
}
検証環境では、.note の中の strong は、font-weight の計算値が 800 になっても、表示は "wght" 300 のままでした(幅 217.48px、黒い画素の量 1565)。font-variation-settings は継承されるので、子要素にも親の "wght" 300 が適用されます。親で wght を font-variation-settings に書くと、中で font-weight を変えた要素の太さが変わらなくなります。
子要素で一部の軸だけを書くと、親の指定が消える
font-variation-settings は、軸ごとではなく、リスト全体で1つの値です。親で "wght" 800, "wdth" 75 を指定し、子で "GRAD" 150 だけを書くと、子の計算値は "GRAD" 150 だけになりました。子では親の wght と wdth が消えて、既定の太さと幅("wght" 400、"wdth" 100)に戻ります。太さと幅が既定値に戻った結果、単語の幅は 244.88px から 218.83px に変わりました。

これを避ける方法を2つ試しました。
1つ目は、太さと幅を font-weight と font-stretch で指定し、font-variation-settings には専用のプロパティがない軸だけを書く方法です。
.card {
font-weight: 800;
font-stretch: 75%;
}
.card .highlight {
font-variation-settings: "GRAD" 150;
}
.highlight の幅は 244.88px で、"wght" 800, "wdth" 75, "GRAD" 150 を1つの要素に書いたときと同じでした。
2つ目は、軸の値をカスタムプロパティにして、font-variation-settings のリストを1か所で組み立てる方法です。
.vf {
font-variation-settings:
"wght" var(--wght, 400),
"wdth" var(--wdth, 100),
"GRAD" var(--grad, 0);
}
.card {
--wght: 800;
--wdth: 75;
}
.card .highlight {
--grad: 150;
}
子で --grad だけを変えても、.vf を付けた .highlight の計算値は "GRAD" 150, "wdth" 75, "wght" 800 になり、親で決めた太さと幅が残りました。この方法では、font-variation-settings を書いた要素の中で font-weight を変えても太さは変わらないので、太さも --wght で変えます。
opsz は、font-optical-sizing が文字サイズに合わせて決める
opsz(オプティカルサイズ)は、文字サイズに合わせて字形を調整する軸です。font-optical-sizing の初期値は auto です。検証環境では、auto で描いた字は、文字サイズの px の値を opsz に書いたときと、画像が画素まで一致しました。
| 文字サイズ | auto(初期値) |
"opsz" に文字サイズの値 |
none |
"opsz" 14 |
|---|---|---|---|---|
| 12px | 63.11px | 63.11px | 61.61px | 61.61px |
| 48px | 218.83px | 218.83px | 246.42px | 246.42px |
| 120px | 504.42px | 504.42px | 616.06px | 616.06px |
表の値は「Typography」の幅です。opsz を直接書いた列は、font-optical-sizing: none と組み合わせています。auto と、opsz に文字サイズと同じ値を書いたものは、どの文字サイズでも、撮った画像が画素まで一致しました。none にすると、幅はこのフォントの既定値の "opsz" 14 と同じになりました。上の節で比べた 48px の値も、auto のときの値です。
font-optical-sizing: auto のまま font-variation-settings: "opsz" 8 を書くと、幅はどの文字サイズでも "opsz" 8 と同じで、120px では画像も画素まで一致しました。仕様の font-variation-settings の節では、opsz にも font-variation-settings ではなく font-optical-sizing を使うよう勧めています。
アニメーションさせるなら、両端で同じ軸を並べる
font-variation-settings は、トランジションやアニメーションで滑らかに変えられます。ただし、始まりと終わりに同じ軸が並んでいる場合だけです(仕様の font-variation-settings の節)。Web Animations API で再生位置を止めて、途中の計算値を読みました。
| 始まり → 終わり | 25% | 50% | 75% |
|---|---|---|---|
"wght" 100 → "wght" 900 |
"wght" 300 |
"wght" 500 |
"wght" 700 |
"wght" 100, "wdth" 50 → "wdth" 150, "wght" 900 |
"wdth" 75, "wght" 300 |
"wdth" 100, "wght" 500 |
"wdth" 125, "wght" 700 |
"wght" 100 → "wght" 900, "wdth" 50 |
"wght" 100 |
"wdth" 50, "wght" 900 |
"wdth" 50, "wght" 900 |
normal → "wght" 900 |
normal |
"wght" 900 |
"wght" 900 |
軸の並び順が違っても、同じ軸がそろっていれば値が少しずつ変わりました。片方にしかない軸があるときや、normal から始めたときは、途中で一気に切り替わりました。検証環境では、25% の時点では始まりの値のまま、50% の時点では終わりの値になっていました。滑らかに変えたいときは、normal の代わりに "wght" 400 のような既定値を書いて、両端の軸をそろえます。
@font-face の font-weight を1つの値にすると、太さが変わらない
可変フォントを @font-face で読み込むとき、font-weight の記述子の書き方で、font-weight プロパティの効き方が変わりました。
@font-face {
font-family: "Roboto Flex";
src: url(roboto-flex.woff2) format("woff2");
font-weight: 100 1000;
font-stretch: 25% 151%;
}
記述子を3通りにして、font-weight: 700 と "wght" 700 の結果を比べました。
@font-face の font-weight |
font-weight: 700 |
"wght" 700 |
|---|---|---|
100 1000(範囲) |
255.34px/4102 | 255.34px/4102 |
| 書かない | 255.34px/4102 | 255.34px/4102 |
400(1つの値) |
218.83px/2979 | 255.34px/4102 |
表の値は、幅と黒い画素の量です。記述子を 400 にすると、font-weight: 700 で幅は指定なしの 218.83px のまま、黒い画素の量だけが 1974 から 2979 に増えました。font-synthesis-weight: none を足すと 1974 に戻ったので、増えた分はブラウザーが合成した太字です。wght 軸は 400 のままで、その上から線を太くしています。
仕様の、機能と軸を適用する順番では、font-weight プロパティで決まる軸の値は、@font-face の font-weight の範囲に収められます。記述子の初期値の auto では、この範囲による制限はかかりません(仕様の @font-face の font-weight の節)。可変フォントでは、記述子を範囲で書くか、書かずにおきます。font-variation-settings の "wght" 700 は、記述子が 400 でも効きました。
ブラウザーの対応
互換性データでの対応状況は次のとおりです。
| ブラウザー | font-variation-settings |
font-optical-sizing |
font-stretch |
font-width |
|---|---|---|---|---|
| Chrome(Android を含む) | 62 から | 79 から | 60 から | 未対応 |
| Firefox(Android を含む) | 62 から | 62 から | 9 から | 155 から |
| Safari | 11 から | 13.1 から | 11 から | 18.4 から |
| iOS の Safari | 11 から | 13.4 から | 11 から | 18.4 から |





