細かすぎ…?FigmaやWebで文字の表示位置がズレる問題を知っておこう

この記事はGoodpatch Advent Calendar 2023 15日目の記事です。

株式会社グッドパッチでエンジニアをしているちげと申します。Strap というオンラインホワイトボードサービスの開発に携わっています。最近は小学生時代にハマっていたレゴを触って懐かしさと構造設計の奥深さに浸っています。

product.strap.app

今回はFigmaやWebで文字の表示位置がズレるいくつかの問題について取り上げて解説したいと思います。

時間がない人向けサク読み

  1. ハーフリーディングを削ることで、文字をぴったり上揃えできる。※(行間の高さ - フォントサイズ) ÷ 2 = ハーフリーディング
  2. Figmaのトリミング機能は現状Webでは実装できない。
  3. Figmaが小数点以下のピクセルを四捨五入する影響で、Webの実装と 0.Npx 分ズレたり、完璧に中央に揃わなかったりする。

上記の現状の解決策をまとめました。

問題としていること

僕がこれまでに経験したUIデザインや実装で、以下のようなケースにて、文字の位置がズレることがありました。自分で作ったデザインを実装することが多かったこともあり、デザインに関わってるエンジニアなら気になるポイントではないかなと思います。

  • ケース1:フォントと画像が横に並ぶと、上揃えがズレる
  • ケース2:Figma上の上下トリミング設定が、うまく実装に反映できない
  • ケース3:デザインと実装がなぜか微妙にズレる
  • ケース4:Figmaの文字の高さは、実は完璧には揃っていない

UIデザイナー向けには、「エンジニアリングでここまでできて、ココまではできないんだ」というエンジニアに求めるレベルの期待値調整時の一材料として、ご一読いただけると幸いです。

ケース3以降に関しては、細かすぎて「まぁいいか」で見逃してしまってもいいような場合も多いと思いますが、一応「こういうことがあるよ」ということを知識として取り上げておきます。

ケース1:フォントと画像が横に並ぶと、上揃えがズレる

課題の詳細

こちらの問題は、Figma のオートレイアウトなどで、画像などの四角い要素とフォントを横に並べた時に起こる問題です。上揃えにしていても、フォントの行間の高さがあるせいで、フォントの文字本体の大きさの上端に揃いません。

原因

こちらはハーフリーディングを削っていないことが原因です。

ここで少し基礎的な話を。CSSやFigmaで見かける、フォントのサイズに関連するプロパティとしては、主に以下の2つが挙げられます。

  • Font size(フォントサイズ)
  • Line height(行の高さ・行間)

上の画像はフォントのサイズと行間を示した図です。フォントは Noto Sans JP を使用して、フォント自体の高さを 16px に、行間を 150% に指定しています。行間を%で指定した場合の基準はフォントサイズになるため、この場合 16px の1.5倍である、24px が設定されます。

この時、上部もしくは下部に存在する赤い領域の高さをハーフリーディングと呼びます。

行間が24pxで、フォントサイズが16pxなので、その差は 24px - 16px = 8px。8pxのリーディングの内、上半分だけを取り出すので、ハーフリーディングは 4px となります。

このハーフリーディングを削ることで、上辺または下辺が揃うようになります。

Web実装での解決策

※デザイナー向けの解説:今はまだキレイな実装を行うことはできないのですが、一応実現できます。

1. パーツごとにCSSの calc() 関数を使う

可能なら font-size と line-height を変数化したいです。一部であれば採用してもいいと思います。ただし、対応箇所が多い場合は、管理が複雑そうな気がしています。

2. ::before::afterを使ってハーフリーディングを相殺するSassのmixinを定義

::before 擬似要素や ::after 擬似要素が全ての文字に付くと、個人的にはちょっと気持ち悪いので一部に使うようにしたいが、現状ベストな解な気がします。

3. (将来的には)CSSの text-box-edge text-box-trim を使う

text-box-edge: text;text-box-trim: both;という新しいプロパティを使うことで、今後解決できるようになる可能性があります。ただし、現状ほとんどのブラウザで使えない。少し前は text-edgeleading-trim という名前の機能でした。

iOS、Androidに関しては無知なのでわかりません、すみません。

ちなみに、ピクシブ社が公開しているデザインシステム、Charcoal ではデフォルトでハーフリーディングを削る処理が使用できます。

Figmaでの解決策

2023年10月現在、Figmaには、ハーフリーディングを削る機能はありません。 せめて、オートレイアウトでマイナスマージンが実装されることを切に願います🙏🙏🙏

どうしても実現したいなら、フォントの上下トリミング機能とオートレイアウトを併用しましょう。この方法についてはケース2で紹介する知識にも関連するので、この記事の一番最後で紹介します。

ケース2:Figma上の上下トリミング設定が、うまく実装に反映できない

課題の詳細と原因

Figmaには、上下トリミング(ベースラインまでのキャップハイト)機能があります。この設定は便利なのですが、実装には現状うまく反映できません。

理由はまだ将来的な機能である、CSSの text-box-edge text-box-trim を使う必要があるからです。 今回のケースでは、設定する値は text-box-edge: cap;text-box-trim: both; になります。

解説:上下トリミング機能とは

Figmaの上下トリミング機能とは、フォントの base line から cap line までの高さ(キャップハイトと呼ぶ)にサイズをトリミングする機能です。base line と cap line がどこかは下の画像をご覧ください。

フォントの各種基準線1
Figmaのテキスト設定から 三点リーダのアイコン「…」をクリックし、上下トリミングを「ベースラインまでのキャップハイト」にします。

これで以下のようになります。↓

「文字の高さが当たり判定を貫通してるじゃないか」と思うかもしれませんが、キャップハイトは英字を基準に設定されているので、日本語だとはみ出すことが多いです。

この16pxのフォントの画像の例で、cap-height が12pxになっていて、font-sizeとの差が4px(上側3px、下側1px)で、それぞれ整数であることに違和感を感じた方は、ぜひケース4も見ていってください。

解決策

完璧に実装するには、CSSの新機能の公開まで我慢するしかありませんが、一部条件下では実装できます。前章で紹介した、ハーフリーディング用の SCSS の mixin と同じことをしましょう。

line-heightの不要な上下の余白を打ち消して、デザインデータに沿ったコーディングを効率化する方法 | Blog | Yuya Kinoshita

::before::after を使ってマイナスマージンを指定する方法のため、そのマイナスマージンをキャップハイトのサイズになるように設定すれば良いわけです。

ただ、フォントごとにキャップハイトのサイズが異なる可能性があり、フォントごとにそのマイナスマージンを設定する対応が必要となる場合は対応が非常に困難です。MacとWindowsでPC内のフォントセットが異なる場合など、そのPCでどのフォントが使われるかわからない場合がほとんどです。そのため、Webフォントを使用するなどして、表示されるフォントが固定されている環境下でのみ、完璧に動作する形になります。

ケース3:デザインと実装がなぜか微妙にズレる

たまに、各種数字やパラメータは同じはずなのに、実装した画面を画像化してデザインデータに重ねた時に、微妙にずれていてなぜか合わない…なんてことがあります。

原因は色々ありますが、文字設定周りや画像の設定などでよく起こっていることを紹介します。

問題の詳細

フォントサイズ 15px、行間 150% で文字を作り、その下にすぐ画像を追加しました。縦の隙間は 0px です。 設定は Figma も Web でも一緒なのに、なぜか文字の位置がズレているように見えます。Webで実装した画面のほうの文字が若干上にズレていますね。なぜでしょうか。


← 左:Webで実装した画面(Chrome)   Figma で作った画面:右 →

原因

Figmaでは、小数点以下のピクセルは四捨五入されるのに対し、Webでは小数点以下の場合は、ブラウザ独自のアルゴリズムによって表示されるからです。

フォントサイズ 15px、行間 150% の場合、15 × 1.5 = 22.5px となります。 しかし、この時 Figma では四捨五入されて 23px で表示されます。これが根本的な原因です。

では、なぜ実装後のChromeのビューで文字が若干上にズレて見えるかというのは謎です。ちなみに実測値でだいたい 0.4px 程度上にズレていました。小数点以下のズレをどう解釈して表示するかはブラウザによって変わるらしく、その仕様を明かそうと思い調べましたがわかりませんでした。明かせたとしてもChromeだけの話になってしまうので、これ以上入り込むのはやめておきます。

どちらにせよ、Figmaでは小数点以下のピクセルが四捨五入される仕様のようですが、それがそのままWebに反映できるわけではないということです。

最近では低画素のパソコンは減りましたが、ピクセルパーフェクトで設計している小さい絵文字やアイコンなどが若干ボケるなどの影響を受ける可能性があります。

解決策

行間を % で指定するのはやめましょう。Figmaで % が指定されていたとしてもWebで px 指定すればこの問題は起こりません。もしあなたがUIデザイナーであるなら、エンジニアにその旨を伝えても良いと思います。

SCSSなら cell 関数や round 関数で少数点の切り上げ処理などを行うことができます。Figmaの仕様に合わせるなら round で四捨五入すると良いと思います。

width: ceil(123.4px);    // 切り上げ -> 124px
height: round(123.4px);  // 四捨五入 -> 123px
$fontSize: 15px;  // フォントサイズを定義
font-size: $fontSize;  // 定義した値を使用
line-height: round($fontSize * 1.5);  // 定義した値に1.5をかけて四捨五入

とはいえ、この問題が起こっていたとしても普通は気づかないことが多いので、%で指定する簡単さを鑑みると、自分が思うほど気にすることではないのかもしれません…。

ケース4:Figmaの文字の高さは、実は完璧には揃っていない

これは問題というよりは、「こういう風になってるよ」という仕様の共有なのですが……

UIデザインにおいて、文字をオートレイアウト等で中央揃えするタイミングや、上下トリミング設定を適用するタイミングがあると思います。この場合でもFigmaは小数点以下を四捨五入した値を使用して中心を計算しています。

例えば以下の画像。フォントサイズ 15px を上下トリミングすると、11px になります。ただ、上下トリミングの基準であるキャップハイト(大文字Xの高さ)は本来 11.4px です。0.4px分が削除されたサイズになっているということです。 これは、中央に見せるための視覚補正といった意図されたものではなく、ただ四捨五入した結果、11.4px が 11px になっているということです。もし 11.5px なら 12px として表示されます。

これを中央揃えするとどうなるかというと、中心から0.4px分上にズレることになります。ミリ単位で視覚補正をしたフォント作成者は涙目だと思います。

とはいえ、この問題も多くの人は気づかないと思うので、あまり気にすることではないのかもしれません……。

大きさの違う文字同士で中央揃えする場合は、デザイン面でベースラインに揃える機能の利用を検討するのも良いと思います。

コラム:Figmaで無理やりハーフリーディングを削る方法

マイナスのマージンを定義することができないため、上下トリミング機能とオートレイアウトを使います。上下トリミング機能については「解説:上下トリミング機能とは」の章をご覧ください。

上下トリミング機能をONにすると、サイズはキャップハイト(大文字Xの高さ)になります。そのため、オートレイアウトでテキストを一度ラップしてあげて、ハーフリーディングの高さに合うように調整してあげます。

こちらの例で言うと、青の領域がキャップハイトなので、上に3px、下に1pxのオートレイアウトフレームをレイヤーに追加してあげれば良いでしょう。

まとめ

本記事では、FigmaやWebで文字の表示位置がズレる問題と解決策、またその周辺のTipsについて解説しました。

重要なポイントをまとめると以下の通りです。

  • ハーフリーディングを削ると上辺や下辺に揃える時に便利です
  • Figmaは小数点以下を四捨五入しています
  • CSSの text-box-edgetext-box-trim が使えれば、Web実装の問題はすべて解決します

ここまで記事読みを完走していただきありがとうございました。完走した感想をぜひコメントしてください。


  1. base line から cap line までは cap height、base line から mean line までは x height、descender line から ascender lineまでがハーフリーディングを削ったフォントサイズ。 参考1:フォントの値を基準とした単位|【CSS】CSSの値と単位が新しくなっていますが、ついていけてますか? - Qiita、 参考2:5.2. 行内ボックス辺の計量:text-box-edge プロパティ|CSS Inline Layout Module Level 3 (日本語訳)