CSSのtext-box-trimで、文字の上下の余白を削る方法

​​CSS​HTML,Playwright,フォント,日本語組版

CSS の text-box-trim を使うと、文字の上と下にできる余白を削れます。どこまで削るかは text-box-edge で決め、cap alphabetic と書けば、要素の上端が大文字の上端に、下端がベースラインに合います。ボタンの padding を上下同じにしても文字が中央に見えない、見出しの上に思ったより広い余白ができる、といったずれを、数値の調整なしに直せます。

補足

検証環境は Ubuntu 24.04、Node.js 22.23.4、Playwright 1.63.0、Chromium 153 です。フォントは Noto Sans CJK JP を使い、書き方ごとに検証用のページを作って、要素の高さを getBoundingClientRect() で測りました。Firefox と Safari、ほかのフォント、インライン要素(<span> など)への指定、grid や table のコンテナーへの指定、縦書き、段組みは試していません。

文字の上下の余白は、行間の余白と、フォントが持つ余白でできる

文字だけの要素の高さは、line-height で決まる行の高さです。その中には、行間を作るための余白(ハーフレディング)と、フォントがもともと持っている、文字の上下の余白が含まれます。

検証環境で、font-size: 40px、line-height: 1.5 の段落に「Hello」と書くと、要素の高さは 60px でした。一方、同じ大きさの「H」の文字そのものは、ベースラインから上に 30px しかありません(canvas の measureText() で測りました)。残りの 30px が、文字の上下の余白です。

text-box-edge で削る位置を決め、text-box-trim で削る側を決める

2つのプロパティを組み合わせて使います。

css
p {
  text-box-trim: trim-both;
  text-box-edge: cap alphabetic;
}

text-box-trim は、上と下のどちらを削るかを決めます(MDN の text-box-trim のページ)。

値 削る側
none 削らない(初期値)
trim-start 上だけ
trim-end 下だけ
trim-both 上と下

text-box-edge は、どの位置まで削るかを決めます。1つ目の値が上の位置、2つ目の値が下の位置です(MDN の text-box-edge のページ)。上には text(フォントのアセントの位置)、cap(大文字の上端)、ex(小文字の x の上端)を、下には text(フォントのディセントの位置)と alphabetic(ベースライン)を書けます。

text-box-edge の初期値は auto で、text と同じ扱いです。text-box-trim を指定しないか none にすると、text-box-edge を書いても何も削られません。

検証環境で、先ほどの 60px の段落に text-box-trim: trim-both を付け、text-box-edge を変えて高さを測った結果です。

text-box-edge 要素の高さ
(text-box-trim: none) 60px
text 58px
text alphabetic 46px
cap alphabetic 29.31px
ex alphabetic 21.72px

text で削れたのは、行間の余白の上下 1px ずつだけでした。cap alphabetic では、高さが「H」の高さ(30px)とほぼ同じになり、ex alphabetic では「x」の高さ(22px)とほぼ同じになりました。text-box-edge を cap alphabetic のまま text-box-trim を trim-start にすると 42.31px、trim-end にすると 47px で、片側だけが削られました。

3つの段落を縦に並べた画面。どれも「Hello」と書いた段落を水色の点線で囲んでいる。上の「指定なし」は文字の上下に余白があり、真ん中の「text-box: trim-both text」も同じくらいの余白がある。下の「text-box: trim-both cap alphabetic」は、点線の上端が大文字の H の上端に、下端が文字の下端に重なっている
点線が要素の範囲。cap alphabetic では、大文字の上端からベースラインまでになる(Chromium 153)

削っても、文字の大きさは変わりません。要素の範囲だけが狭くなります。そのため、大文字の高さより背の高い文字は、要素の外にはみ出して描かれます。検証環境で測ると、「Hello」の上端(l の頭)はベースラインから上に 32px あり、cap alphabetic で削った範囲(上に 29.31px)を少し越えていました。

text-box で、2つをまとめて書く

一括指定の text-box を使うと、1つの宣言で書けます。

css
p {
  text-box: trim-both cap alphabetic;
}

検証環境で、text-box の値から2つのプロパティの値がどうなるかを getComputedStyle() で調べました。

text-box の値 text-box-trim text-box-edge
trim-both cap alphabetic trim-both cap alphabetic
cap alphabetic trim-both cap alphabetic
trim-start trim-start auto
normal none auto

text-box-trim の値を省くと、初期値の none ではなく trim-both になります。MDN の text-box のページにも同じことが書かれています。そのため、text-box: cap alphabetic だけで上下の両方が削れます。

text-box-edge の上と下の値は、この順番でしか書けません。Chromium 153 の CSS.supports() では、cap alphabetic は使える値、alphabetic cap と cap(1つだけ)は使えない値でした。

ボタンの文字を上下の中央に置く

text-box-trim は、ボタンのように文字を padding で囲む部品で役に立ちます。padding を上下同じにしても、文字の上の余白と下の余白が同じにならないためです。検証環境で、font-size: 20px、padding: 12px 24px のボタンを text-box を付けずに測ると、要素の上端から大文字の上端までは約 20.3px、ベースラインから要素の下端までは約 19px でした。

css
.button {
  display: inline-block;
  font-size: 20px;
  padding: 12px 24px;
  line-height: 1.5;
  text-box: trim-both cap alphabetic;
}
2つのボタンを縦に並べた画面。どちらも水色の背景に白い太字で「Button」と書いてある。上の「指定なし(font-size: 20px、padding: 12px 24px)」のボタンは縦に高い。下の「text-box: trim-both cap alphabetic を追加」のボタンは低くなり、大文字の上端から上と、ベースラインから下の余白が同じに見える
text-box を加えると、padding がそのまま文字の上下の余白になる(Chromium 153)

このボタンの高さは、指定なしでは 54px、text-box: trim-both cap alphabetic を加えると 38.66px でした。38.66px は、上下の padding 12px ずつと、このフォントを 20px で表示したときの大文字の高さ 14.66px を足した値です。大文字の上端から要素の上端まで、ベースラインから要素の下端まで、どちらも padding の 12px になりました。

CSS Inline Layout Module Level 3 の仕様では、決まった長さではなくフォントの数値をもとに削るので、フォントを変えても意図した配置が保たれると説明されています。

削られるのは、最初の行の上と最後の行の下だけ

文章が複数行になっても、行と行の間は削られません。

検証環境で、font-size: 20px、line-height: 2 の2行の段落に text-box: trim-both cap alphabetic を付けると、高さは 80px から 54.66px になりました。減った 25.34px は、1行目の上で削られた 13.34px と、2行目の下で削られた 12px の合計です。1行の段落で上下を削ったときと同じ量なので、行間はそのまま残っています。

CSS Inline Layout Module Level 3 の仕様にも、削るのはブロックの最初の行の上と最後の行の下だと書かれています。

親に指定すると、中の最初の段落と最後の段落が削られる

text-box-trim は、段落そのものではなく、段落を囲む親の要素に指定しても効きます。

css
.card-body {
  text-box: trim-both cap alphabetic;
}
html
<div class="card-body">
  <p>First</p>
  <p>Second</p>
</div>

検証環境では、1つ目の段落の上と2つ目の段落の下だけが削られ、1つ目の段落の下と2つ目の段落の上は削られませんでした。カードの本文のように段落が並ぶ部品で、上下の余白をまとめて詰められます。

ただし、親の指定が届かない場合があります。

1つは、親と段落の間に padding か border があるときです。検証環境では、1つ目の段落に padding-top: 1px を付けるだけで、1つ目の段落の上は削られなくなりました。仕様にも、間に 0 でない padding や border があると効かないと書かれています。

もう1つは、親が flex コンテナーのときです。検証環境では、display: flex の親に指定しても、中の段落は削られませんでした。仕様の注記には、flex、grid、table のレイアウトには適用されず、その中にも伝わらないとあります。flex アイテムの段落そのものに指定すると、削られました。

仕様の定義では、text-box-trim は継承しないプロパティです。検証環境で親だけに指定すると、子の段落の text-box-trim の算出値は none でした。一方、text-box-edge は、子の算出値も親と同じ cap alphabetic になりました。

日本語の文字は、cap alphabetic だとはみ出す

cap と alphabetic は、英字の大文字の上端とベースラインです。検証環境のフォントでは、漢字やかなは、それより上と下に広がっていました。

3つの段落を縦に並べた画面。どれも「日本語の文字」と書いた段落を点線で囲んでいる。上の「指定なし」は文字の上下に余白がある。真ん中の「text-box: trim-both cap alphabetic」はピンクの点線で、文字の上の部分と下の部分が点線の外にはみ出している。下の「text-box: trim-both text」は、指定なしとほとんど変わらない
cap alphabetic では、漢字やかなが要素の範囲からはみ出す(Chromium 153)

検証環境で、40px の「日本語の文字」を canvas の measureText() で測ると、文字はベースラインから上に 34px、下に 4px まで描かれていました。cap alphabetic で削った要素の範囲は、ベースラインから上に 29.31px までで、下は 0px です。上に約 5px、下に 4px、文字がはみ出します。

text を指定すると、文字ははみ出しませんが、削れるのは行間の余白だけです。先ほどの 40px の段落では、要素の高さは 60px から 58px にしかなりませんでした。行間の余白が大きいほど、text でも削れる量は増えます。検証環境で、font-size: 20px、line-height: 2 の日本語の2行の段落に text-box: trim-both text を付けると、高さは 80px から 69px になりました。

CSS Inline Layout Module Level 3 の仕様には、漢字の全角の枠に合わせる ideographic と、字面に合わせる ideographic-ink という値もあります。ただ、Chromium 153 の CSS.supports() では、どちらも使えない値でした。MDN のブラウザ互換性データでも、この2つに対応しているのは Firefox 154 だけで、Chrome と Safari は対応していません。

対応しているブラウザ

MDN のブラウザ互換性データによると、text-box-trim、text-box-edge、text-box に対応したのは Chrome 133、Safari 18.2、Firefox 154 です。インライン要素への指定は、Chrome は対応しておらず、Firefox は 154、Safari は 26.5 から(縦書きでは効かない一部対応)となっています。

​Posted by 86