unitone の line-height の計算式 2025

まずは文字サイズの実装について

文字サイズについてはこれまでと変わりがなく調和数列をベースとしたものになっています。詳細が気になる方は過去の記事をご参照ください。

line-height の実装

unitone の line-height 実装の基本的な考え方は下記のとおりです。

そして肝心の line-height の実装について。上記で、文字サイズは –unitone–font-size を指定して算出できることを示しましたが「文字サイズを指定するたびにその文字サイズに最適な line-height も指定するのは面倒なので、文字サイズを指定すればその文字サイズに最適な line-height も自動的に反映されるようにしたい」と考えました。

上記は昨年の記事からの引用ですが、開発当初から一貫してこの考えです。

昨年からの計算式は下記のとおり。

  • 基本の line-height1.8line-height の最大値)。
  • 文字サイズが大きくなるほど line-height を小さくする。
  • 文字サイズが一定を超えると緩さが目立つので、基本の文字サイズの5倍のサイズ(現状決め打ち)のときに 1.1line-height の最小値)となるようにする。
  • (↑今はリニアな関数なので、指数関数的な関数にしたほうが良いのでは?感はあります。でも良いやり方がわからない。ご意見募集!)

これを式にするとこうなります。

{line-height} = -0.1 * {文字サイズの倍率(つまり何remか)} + 1.9;

※ただし line-height が 1.1 未満になったり、1.8 以上になることはない。
※また、文字サイズの倍率が5倍以上の場合は line-height は 1.1 を超えないものとする。

今年は昨年よりもう一歩踏み込んで、--unitone--font-size を指定せずに直接 style="font-size: 42px" のように指定した場合でも、最適な line-height が適用されるようにしました!(これまでも対応しているっちゃしていましたが、JavaScript が必要でした。今は不要!)

実際のコード

(わかりやすくするために実際とは異なる改変をしています)。

@property --unitone--property--1em {
  syntax: "<length>";
  inherits: false;
  initial-value: 0;
}

* {
  /**
   * A value to get the unitless px value in trigonometric functions. (For Safari)
   */
  --unitone--property--1em: 1em;

  /**
   * em unitless px real value
   */
  --unitone--result--1em-px: calc(tan(atan2(var(--unitone--property--1em), 1px)));
}

:root {
  /**
   * The 1rem px font size (no units).
   */
  --unitone--base-font-size: 16;

  /**
   * Gutter provided above and below the text in a line.
   */
  --unitone--half-leading: .3;
  --unitone--min-half-leading: .05;
  --unitone--min-line-height: calc(1 + 2 * min(var(--unitone--min-half-leading), var(--unitone--half-leading)));
  --unitone--max-line-height: calc(1 + 2 * var(--unitone--half-leading));
}

$_max-line-height-target-font-size-ratio: 5;
$_line-height-slope: calc((var(--unitone--min-line-height) - var(--unitone--max-line-height)) / #{ $_max-line-height-target-font-size-ratio - 1 });
$_line-height-intercept: calc(var(--unitone--max-line-height) - #{ $_line-height-slope });

$line-height: clamp(
  var(--unitone--min-line-height),
  #{ $_line-height-slope } * (var(--unitone--result--1em-px) / var(--unitone--base-font-size)) + #{ $_line-height-intercept },
  var(--unitone--max-line-height)
);

昨年からアップデートしたこと

  • 文字サイズの実数値を CSS で計算して、最適な line-height を適用
  • line-height の最小値を CSS カスタムプロパティ化
  • line-height の最大値を CSS カスタムプロパティ化

解説

tan()、atan2() を使った「1em → px」の変換

今回の実装で最も分かりにくいのは「1empx に変換するためになぜ三角関数を使うのか?」という点かなと思います。

結論から言えば、Safari が三角関数の内部で <length>(単位つきの値)をうまく扱えないため、empx(単位なし)に変換する工夫が必要だからです。

CSS の tan()atan2() は数値または角度を受け取ります。ところが Safari は、

tan(1em)

のような「単位付きの長さ」を入力するとうまく計算できない場合があります。そこで下記の「単位を外す」ためのトリックを使います。

1em(長さ)→ 角度 → 角度の傾き(単位なしの数値)

まず、atan2(y, x) は「(x, y) というベクトルの角度」を返します。たとえば

atan2(1em, 1px)

これは「底辺 1px、縦 1em の三角形の角度」を返します。その角度を tan() に渡すと角度の傾き(縦 / 底辺)がわかります。これは 1em / 1px なので「1em1px の何倍なのか」がわかります。

tan(atan2(1em, 1px)) = 1em / 1px

つまり 1empx に変換した、単位のない実数値が得られます。

@property と CSS カスタムプロパティを使う理由

tan()atan2() に直接単位付きの値を渡すと挙動が怪しい場合があるので、

  • @property--unitone--property--1em<length> に固定
  • それを使って 1empx(単位なし)に変換した値を CSS カスタムプロパティ(--unitone--result--1em-px)として保存

と、しています。

@property --unitone--property--1em {
  syntax: "<length>";
  inherits: false;
  initial-value: 0;
}

--unitone--property--1em: 1em;
--unitone--result--1em-px: calc(tan(atan2(var(--unitone--property--1em), 1px)));
inc2734のアバター

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です