Webサイトのコーディング(実装)では、デザインデータ通りの数値を反映しているにも関わらず、ブラウザ上の表示がデザインデータと比べてずれる(余白が大きくなりすぎる)場合があります。
これはブラウザのline-heightの仕様によるものですが、line-height制御の難しさがデザインデータとコーディング後(ブラウザ表示)の余白のずれにつながっていると考えています。
この問題を解決するため、line-heightの不要な上下の余白を打ち消すCSSを試したところ効果を実感できたので、この方法のメリット・デメリットや活用についてまとめました。
チームで進めている実際のプロジェクトでこの方法を試してから、2020年1月の時点で9ヶ月ほど経ちますが、メリットとデメリットを比較してメリットが完全に上回っていると感じています。
Webサイトのデザインデータをコーディングで反映する際にレイアウト上のずれが発生しやすい原因として、line-heightに不要な上下の余白が生まれるというブラウザの仕様があります。
仕様上どうしても発生してしまう余白のため、これまではその不要な余白が足されることを計算して、実際の数値から不要な余白分を減算した数値でコーディングする必要がありました。この減算がコーディングを複雑化させる原因であり、デザインデータ通りの余白の反映を難しくしています。
デザインデータに沿ったデザインとしてコーディングに反映させるためには、目視で適切な余白を調整しながらコーディングをする必要があります。それと同時にコンポーネントルールの設計も必要です。
よってデザインデータに沿った余白をコーディングで再現するためには、デザイナーとフロントエンドエンジニア両方の視点と知見が同時に求められます。
これは難易度の高い問題だと感じています。
line-heightの不要な余白の問題は、対応する箇所が多いことと計算が複雑化することです。パーツごとにCSSのcalc()関数を用いて計算することで調整はできますが、その場合はパーツ(コンポーネント)ごとの計算が複雑化して管理しにくくなるデメリットがあります。
Webサイトをデザインしたデザイナー自身がコーディングする場合は、実際のブラウザの表示状態とデザインデータで指定されている余白(CSSのmargin)の大きさを比較しながらパーツごとにちょうど良い余白に調整しやすいです。
それでもコーディングの難易度や複雑度は高いことは、長期運用やサイト拡張時のメンテナンス上の問題になっていきます。
この問題を解決するために、参考ページ(この記事の最下部に記載)のコード例を元にline-heightの不要な余白を打ち消す(相殺)するSassのmixinを試しました。このmixinはlineHeightCropという名前で定義して使っています。
以下はline-heightの不要な余白を打ち消すCSS(lineHeightCrop)のメリットとデメリットです。
※そもそもとして、現在(2020年1月20日時点)では、セキュリティの観点からIE11を含む全てのバージョンのInternet Explorerの使用を避けたほうが良い前提があります。
メリットとデメリットを比較した際に、メリットがデメリットを完全に上回っていると感じています。対象要素で::before擬似要素と::after擬似要素が使えなくなるデメリットがありますが、divタグを入れ子にすることで対応できる場合が多いはずです。
実際のプロジェクトで使って9ヶ月ほど経ちますが、今のところ特に問題が見当たりません。デザイン品質維持とコーディング効率化のメリットのほうが大きいです。問題が見つかれば対策を考えますが、それまでは継続して使っていきます。
以下はlineHeightCropのコード例です。
// lineHeightCrop(line-heightの不要な余白を打ち消す)のmixinのコード例。
// f-variables.scssという共通の変数定義用のscssファイルに記述して一元管理化しています。
@mixin lineHeightCrop($line-height) {
&::before {
display: block;
width: 0;
height: 0;
margin-top: calc((1 - #{$line-height}) * .5em);
content: "";
}
&::after {
display: block;
width: 0;
height: 0;
margin-bottom: calc((1 - #{$line-height}) * .5em);
content: "";
}
}
@mixin lineHeightCrop($line-height)の部分は1度設定すれば、そのまま使えるので便利です。後から編集することも特にありません。
@mixin lineHeightCrop($line-height)の部分は、自分はf-variables.scssという共通の変数定義用のscssファイルに記述して一括管理しやすくしています。
// 要素ごとのセレクタに記述
@include lineHeightCrop(2);
@include lineHeightCrop(1.5);
// 実際には以下のように変数で管理するほうが効率的です
@include lineHeightCrop($leading-loose); // $leading-loose = 2
@include lineHeightCrop($leading-tight); // $leading-tight = 1.5
// コード例
.c-heading-lv4{
color: $color-bk;
font-size: $text-4xl;
line-height: $leading-x-tight;
@include lineHeightCrop($leading-x-tight);
font-weight: $font-bold;
@media screen and (max-width: $screen-sp){
font-size: $text-mb-4xl;
}
}
@include lineHeightCrop(2);や@include lineHeightCrop(1.5)、@include lineHeightCrop($leading-loose);がよく使うコードです。これらのコードを要素のセレクタ部分に追記します。かっこ内の引数は、その要素のline-heightの値と合わせます。
例えばpタグのline-heightの値が2なら@include lineHeightCrop(2);と書き、h1タグのline-heightの値が1.5なら@include lineHeightCrop(1.5);と書きます。
実数として数値で書くこともできますが、管理効率化のために@include lineHeightCrop($leading-snug);のように変数化しておくことを推奨します(効率的な変数化の方法は後述)。
// pタグに適用する場合の例
.c-text {
font-size: $text-base;
line-height: $leading-loose;
margin-top: 1.5em;
margin-bottom: 1.5em;
color: $alpha-black-900;
@include lineHeightCrop($leading-loose);
@include media-query('bp-sm') {
font-size: $text-sm;
}
&:first-of-type {
margin-top: 0;
}
&:last-of-type {
margin-bottom: 0;
}
}
// h1タグに適用する場合の例
.c-heading-lv1 {
font-size: $text-7xl;
margin-bottom: $space-lv4;
text-align: center;
line-height: $leading-tight;
@include lineHeightCrop($leading-tight);
@include media-query('bp-md') {
text-align: left;
}
@include media-query('bp-sm') {
font-size: $text-4xl;
margin: 0 auto $space-lv4;
}
}
以下lineHeightCropとは直接関係が無いですが、補足としてline-heightの効率化についても記しておきます。
自分はデザインの品質を高めるために見出しや段落など要素によってline-heightの値を使い分けているのですが、共通定義用のscssファイルに以下のように書いています。これによって一元管理がしやすくなります。
// f-variables.scss内に記述
// line-heightの変数定義
$leading-none: 1 !default;
$leading-x-tight: 1.25 !default;
$leading-tight: 1.5 !default;
$leading-snug: 1.6 !default;
$leading-normal: 1.7 !default;
$leading-relaxed: 1.8 !default;
$leading-loose: 2 !default;
これらの変数の命名規則はTailwind CSSに沿っています。
日本語が主体かつ日本語とアルファベットが組み合わさった文字組みのデザイン(タイポグラフィ)では、以上の7種類のline-heightを使い分けることで統一感を持たせつつ、読みやすさを維持できると思います。
※日本語(和文)とアルファベット(欧文)が混ざった文字組みのことは、和欧混植と表現される場合もあります。
line-heightの中で感覚値として利用頻度が高いのは、$leading-tight(1.5)、$leading-snug(1.6)、$leading-relaxed(1.8)、$leading-loose(2)です。
主に次の要素に使っています。
IE11対応のため使えない場合がありますが、ブラウザの表示を試して問題ない場合は、line-heightの不要な上下の余白が発生するテキスト要素のほぼ全てに使っています。
light-height: 1;では対応できない場合が多いと考えるので、light-height: 1;による対応はなるべく避けています。主な理由は、ユーザーが閲覧する環境によって、文章が必ずしも1行に収まるとは限らないためです。
特にスマートフォンの場合は表示できる横幅が狭くテキストが複数行になる状況が多いため、スマートフォンで見た際にも読みやすい文字組みを維持したいと考えています。
light-height: 1;の状態でテキストが複数行になった場合は文章が極端に読みづらくなります。見た目の印象も悪くなってしまうので、これを徹底的に改善したいという意図があります。
リストアイテム(liタグ)にlineHeightCropのmixinを適応させてIE11で表示した場合に意図しないレイアウトになってしまう問題がありました。
IE11のサポートをしないWebサイトの場合は問題ありませんが、何らかの事情によりIE11の表示もサポートする場合は、リストアイテムにはlineHeightCropのmixinを適応させないという運用のほうが効率的かもしれません。
※繰り返しとなりますが、現在ではセキュリティの観点からIE11を含む全てのバージョンのInternet Explorerの使用を避けたほうが良いと考えます。IEで正常に表示されるように対応するよりも、IE以外のブラウザ利用を勧めるほうがよりユーザーのためになるのではと思います。
上記のlineHeightCropのようなmixinを使用しなくても、将来的にはleading-trimという新しいCSSプロパティで解決できるようになるかもしれません。今後leading-trimプロパティが正式に利用可能になればline-heightによる意図しない余白が生まれる問題を解決できそうなので、とても期待しています。
The example above first uses text-edge (also a new property) to tell the browser the desired edge of the text is the cap height and the alphabetic baseline. Then it uses leading-trim to trim it from both sides. Note that leading-trim only affects the text box; it doesn’t cut off the text within it.
The extra space reserved in the default line height causes text to not always be centered in the text box. With leading-trim, you can reliably vertically center your text.
Leading-Trim: The Future of Digital Typesetting
※注意:leading-trimプロパティは早期草案であり、現時点(2022年6月7日現在)ではまだ使用できません。また今後各ブラウザがこの仕様を採用する場合も何らかの変更が入る可能性があります。
leading-trimにはとても期待を寄せていますがまだ使用できないため、line-heightによる余白問題はlineHeightCropのmixinで対応しています。
leading-trim
プロパティデザインデータとコーディングの余白に差が出てしまうという問題には、今でも苦労し続けています。
自分がデザインしたページを自分がコーディングする場合はコーディング時に納得いくまで調整を繰り返せるのですが、役割分担によっては自分がコーディングを担当しない場合もあります。
デザインの質を維持するためにline-heightの余白を1px単位で追求していきたい気持ちがありつつも、難易度が高く手間がかかる工程ゆえにそれを自分以外の誰かに要望することを心苦しく思う気持ちもあり、葛藤が続いています。
その状況で上記の方法を知り、デザインの品質維持とコーディングの効率化を両立する解決策として、現状の最適解なのではと期待しています。実際にこの方法を取り入れ始めたのは、チームで進めているプロジェクトからでした。
デザイナーに限らず個人によって考え方は様々だと思いますが、自分の感覚としては全ての要素の余白が気になります。
しかし厳密に全ての要素を1px単位で追求していくのは時間的に非現実的だと考えているので、ある程度の許容度は必要です。
OSやブラウザの違いによっても差異は生まれます。ユーザーの閲覧環境によって表示が変化するオンスクリーンのデザインである以上、差異を許容するのが自然だと思います。
一方でline-heightの不要な上下の余白による表示のずれは、差異の許容量を超えていると判断しています。その問題を解決したいと思って試行錯誤を続けています。現時点で最適な改善策だと思っている方法がlineHeightCropによる余白の制御です。
余白の基礎については過去に作成したスライド資料があるので、もし良ければ見てみてください。
デザイナー視点としては、エンジニアへコーディングを依頼した際にline-heightの余白も含めてデザインデータに沿ったデザインかそれ以上の品質で仕上げていただけたときに、とても嬉しく思います。
それは1pxの余白の差がデザインの質を左右する重要な要素であることと、line-heightの不要余白を含めたコーディング時のデザイン調整にどれだけ手間がかかり大変なのかを、これまでの経験で実感してきたからかもしれません(今もデザインとコーディングのどちらも好きで面白いと思っています)。
そのためかlineHeightCropが期待通りに動いてくれて、不要な余白が打ち消された安定表示を確認できたときの嬉しさは大きかったです。
lineHeightCropがデザイナーやエンジニアの手助けになり、結果的により良いデザインがユーザーへ届き、価値につながれば幸いです。