「EVERY LAYOUT」を 2,023 年 07 月 16 日に読んだ。
目次
メモ
監訳者まえがき p4
本書のもうひとつのテーマは、堅牢なレイアウトシステムの作成です。
特にレスポンシブWebデザインを前提としたとき、レイアウトに独自の制約ができたり、再利用性や一貫性の問題に突き当たってしまうことがよくあります。
その解決のために本書が提案するのは、「メディアクエリの使用を意図的に避ける」というものです。
ビューポートの幅に応じてレイアウトを切り替えるアプローチでは、限られたコンテキストでしかそのレイアウトが機能しなくなってしまうからです。
代わりに、「レイアウトプリミティブ」と呼ばれるパターンによって対処します。
ビューポートの幅ではなく、内包するコンテンツやコンテナのサイズに応じて最適なレイアウトを自動で導出できるアルゴリズムによって成り立つ、レイアウトのための小さなコンポーネントのことです。
レイアウトプリミティブは非常に高い再利用性を持つように設計されており、プロジェクトに導入することでレイアウトのためのコードが大幅に削減できます。
また副次的効果として、コンポーネント設計においてレイアウトの責務の分離が促されることになり、結果的にあらゆるコンポーネントの境界がよりはっきりとしたものになるという利点もあります。
レイアウトプリミティブは、レスポンシブWebデザインのベストプラクティスと言えます。
これを共通言語に据えることで、チームでのコミュニケーションもより円滑に進められるでしょう。
デザインカンプ中心のワークフローでは、最初からこれらのパターンを意識してくことで、より精緻な設計が実現できるはずです。
1 02 コンポジション p24
プログラマーにはおなじみの「継承よりコンポジション (Composition over inheritance)」という原則があります。
継承によってすべてを共通のオリジンに結びつけるよりも、単純な独立した部品 (オブジェクトやクラス、関数)を組み合わせるほうがより柔軟で効率的になるという考え方です。
「継承よりコンポジション」の原則は「ビジネスロジック」に限った話ではありません。
コンポジションを採用することは、フロントエンドのアーキテクチャやビジュアルデザインにおいても理にかなっているのです (Reactのドキュメントには、それだけについて言及しているページがあるほどです※2)。
1 03 単位 p29
Webに表示されるすべてのものは、端末の画面を構成する小さな光の点からできています。「ピクセル」です。
そのため、インターフェイスを構成する要素のサイズを測定するときには、ピクセルの観点から考え、CSSのpx単位を使うのが合理的とされています。
しかし、本当にそうでしょうか?
画面のピクセル配列にはいくつもの種類があります ※1 が、昨今のディスプレイの多くにはサブピクセルレンダリングが採用されています。
これは、ピクセルごとにカラーコンポーネントを操作することで、ギザギザとしたエッジを滑らかにし、「知覚される解像度」を向上させる技術です。
1pxの概念は、一般的に言われているよりも曖昧なものです。
また、画面解像度(画面を構成するピクセル数)にも違いがあります。
低解像度の画面では、ひとつの「CSSピクセル」(CSSにおける1px)がひとつの「デバイスピクセル」または「ハードウェアピクセル」に相当する一方で、高解像度の画面においては、CSSの1pxの中で複数のデバイスピクセルが使われることもあります。
つまり、単なるピクセルもあれば、ピクセルから構成されるピクセルもあるというわけです。
※1. Ian Mallett - Reference: Subpixel Geometry Zoo Page サブピクセル配列の種類の一覧です。
https://geometrian.com/programming/reference/subpixelzoo/index.php
画面は確かにピクセルで構成されていますが、ピクセルは規則正しいものでも不変でもありません。
拡大表示で閲覧しているユーザーにとって400pxに見えるボックスは、CSSピクセルにおける400pxではないことは自明です。
さらに、拡大表示する前でさえ、「デバイスピクセル」においてはそもそも400pxではないかもしれません。
CSSでpx単位を使うこと自体は間違いではありませんから、エラーメッセージを目にすることはないでしょう。
しかし、px単位は誤解を招きます。
ピクセルパーフェクトというものが実現可能な、望ましいものであるという、間違った前提のもとで努力することに繋がるのです。
スケーリングとアクセシビリティ p30
px単位を使った設計をすると、間違った考え方にとらわれやすくなるだけではせん。
明らかな欠点もあります。たとえばpxを使ってフォントサイズを設定すると、
ブラウザは、デザイナーがフォントをそのサイズに固定したがっていると解釈します。
その結果、ユーザーがブラウザ設定で選択しているフォントサイズは無視されてしまいます。
昨今のブラウザにはページのズーム機能(テキストを含むすべてのものが均等に拡大される機能)が備わっているという理由により、この問題は解決済みだと片付けられてしまうことがよくあるようです。
一方でエバン・ミント(Evan Minto)の調査よると、ブラウザ設定でデフォルトのフォントサイズを変更しているユーザーの数は Edge や Internet Explorer のユーザー数よりも多いといいます。
つまり、デフォルトのフォントサイズを変更しているユーザーを無視することには、あるブラウザを完全に無視するのと同じくらいの影響力があると考えてよいでしょう。
※2. The Myth of Pixel Perfection
ピクセルパーフェクトはデザインカンプとのページとの見た目を完全に一致させることを指す用語です。
レスポンシブデザインにおいて、これは非現実的で合理性を欠く考え方であると推摘する記事です。
https://www.kelliekowalski.com/articles/the-myth-of-pixel-perfection
emやrem、chやexといった単位にはそうした懸念がありません。
ユーザーのOSやブラウザによって設定されているデフォルトのフォントサイズに相対的な単位だからです。
これらの単位を使った値は、もちろんブラウザによってピクセルに変換されるわけですが、コンテキストや設定にした結果になります。
相対単位はいわば仲裁者のようなものです。
相対性 p31
ブラウザやOSにおいてユーザーが設定できるのは、通常、「ベース」つまり「本文」のフォントサイズのみです。
この値は1remとして表現できて、ルートのフォントサイズと対応しています。
段落要素は1remになりますが、それはこの要素が本文を意味するものであるためです。
また、1remがデフォルト値になっているため、この値を明示的に設定する必要はありません。
:root {
``` 冗長 ```
font-size: 1rem;
}
p {
``` やはり冗長 ```
font-size: 1rem;
}
見出しのような要素は、相対的に大きくなるように設定します。
そうしなければヒエラルキーが失われてしまうからです。
ここでは、 <h2> を 2.5rem にするとします。
h2 {
``` ルートのフォントサイズの 2.5 倍 ```
font-size: 2.5rem;
}
emやrem、chやexといった単位はすべてテキストに関係するサイズですが、もちろんmarginやpadding、borderなどのプロパティにも適用できます。
これらの単位は使い勝手がよいだけではなく、テキストがWebメディアの基礎であるという事実を常に思い出させてくれます。
テキストに由来するサイズからレイアウトを導き出すことをべば、美しいデザインが可能になるでしょう。
無駄な変換 p32
多くの人はわざわざremとpxを変換して、使用するremの値がピクセルとして半端にならないように気にしています。
たとえばベースのサイズが16pxの場合、2.4375remは39pxになりますが、2.43remは38.88pxです。
しかし、ピクセルの値が整数になるように変換する必要はありません。
ブラウザのサブピクセルレンダリングや端数処理によって自動的に調整されるからです。
それよりも、 1.25rem や 1.5rem 、 1.75rem など、単純な数を使うほうが簡単です。
あるいはモジュラースケールを使って、面倒な計算はcalc()に任せてもよいでしょう。
ビューポート単位 p33
Every Layout では、幅を基準としたメディアクエリの使用を避けています。
メディアクエリでは、レイアウトの構成をハードコーディングで表現することになります。
さらに、要素やコンポーネントが実際に置かれている状況と結びついていないのです。
先ほどの例のように、インターフェイスのスケーリングを個別の「ブレイクポイント」に基づいて行うのは恣意的な設定です。
960pxは何か特別な幅なのでしょうか?
959pxで小さいサイズになることが本当に適切だといえるのでしょうか?
ビューポート単位は、ブラウザのサイズに相対的です。
たとえば、 1vw は画面の幅の 1% 、 1vh は画面の高さの 1% と等しくなります。
ビューポート単位と calc() を利用すると、サイズがスケーリングされて、かつ「最小値」を確保するアルゴリズムが作成できます。
:root {
font-size: calc(1rem + 0.5vw);
}
1rem を式に含めることで、 font-size が (システムやブラウザ、ユーザー定義に基づいた) デフォルト値を下回らないようになります。
つまり、 1rem + 0vw でも結果は 1rem になるということです。
ch 単位と ex 単位 p35
ch単位とex単位は、それぞれ特定の文字の(おおよその)幅または高さに関係しています。
1ch はそのフォントの 0 の幅に基づいており、 1ex は x の文字の高さとしくなります。
後者はエックスハイトまたはコーパスサイズとしても知られています。
「公理」の節では、要素のカラム幅を制限して可読性を確保するために ch 単位を使っています。
カラム幅では1行あたりの文字数が問題になるため、この場合は ch (「character」の略語) が最適な単位です。
それによって、 <h2> と <h3> で font-size が異なっていたとしても (最大) カラムの設定は同じままにできます。
h2,
h3 {
max-width: 60ch;
}
h3 {
font-size: 2rem;
}
h2 {
font-size: 2.5rem;
}
テキスト行の幅のピクセル値は、 rem 基準の font-size と ch 基準の max-width の関係から導き出されます。
この値の決定をアルゴリズムに委ねること、つまりpx基準のwidthを用いたハードコーディングを避けることで、頻繁に発生しがちな重大なエラーをなくせます。
CSSレイアウトにおけるエラーとは、コンテンツが崩れたり読めなくなったりすることであり、「人間にとってのデータ損失」とも言えるでしょう。
p44
最後に紹介するのは、スタイルをローカルにするための「標準」技術である Shadow DOM です。
要素を shadowRoot にすることで、詳細度の低いセレクタを使っても要素の内側のみに作用するようになります。
欠点
id セレクタにインラインスタイル、そして Shadow DOM のいずれも欠点があります。
id セレクタ
詳細度の高さゆえに、全体に影響する問題をいくつも引き起こします。
また、都度idの名前を考える必要があります。
動的に一意の文字列を生成するほうが望ましい場合もよくあります。
インラインスタイル
運用が悪夢のようです。
そもそも、CSSが考案されたのはこれを解決するためです。
Shadow DOM
スタイルが Shadow DOM のルートから漏れ出ることを防ぐだけではなく、
同時に(ほとんどの)スタイルが「中」に入り込むこともできなくなります。
つまり、グローバルなスタイル設定が活用できません。
1 05 モジュラースケール p50
音楽は基本的に数学的な表現です。
組版において音楽的性質が語られるのは、組版と音楽は数学的な基礎を共有しているためです。
周波数やピッチ(音の高さ)、ハーモニー(和声)などの概念を耳にしたことがあるでしょう。
いずれも数学的に説明できるものですが、知覚されるピッチはそれぞれ複数の周波数から構成されていることをご存知でしょうか?
ギターの弦をはじいたときに生み出されるような楽音のひとつひとつは、それ自体がひとつのコンポジションでもあります。
そこでは異なる周波数(倍音)が集まって、倍音列を構成しているのです。
倍音列とは分数の数列であり、1ずつ増加する等差数列に基づいています。
1,2,3,4,5,6 // 等差数列
1,1/2,1/3,1/4,1/5,1/6 // 倍音列
1 06 公理 p57
数学者ユークリッドの発見によると、どれだけ複雑な図形や空間であっても、単純で明白な公理(あるいは公準)に基づいているといいます。
デザインにおいても同様で、公理に基づいていなければ調和が取れずに不自然な印象を与えてしまうでしょう。
この節では、いかにしてデザインの公理をシステム全体に浸透させるかを、「タイポグラフィにおけるカラム幅(measure)」を例にして紹介します。
カラム幅
テキストの行の長さ(文字数)のことをカラム幅と呼びます。
適切なカラム幅が設定されていることは、複数の行におよぶテキストを気持ちよく読み進めるために非常に重要です。
「The Elements Of Typographic Style」によれば、カラム幅を45から75文字の間の値にするのが妥当だとされています。
訳者注
日本語の場合、英語と違って全角文字であるため、カラム幅はおよそ半分の24文字から40文字程度を目安とするとよいでしょう。
印刷媒体向けにカラム幅を設定するのは比較的簡単な話です。
用紙の幅を単に、テキストを配置するカラムの数で割るだけです。
もちろん、マージンやガターは差し引いたうえで。
カラム幅の公理 p58
デザインの公理を短いフレーズや文で表現してみるのはよい習慣です。
カラム幅について言い表すとすれば、「カラム幅は60chを超えてはいけない」といったところでしょうか。
p60
しかし、ありがたいことにCSSにはch単位があります。
1chの値は、適用されているフォントの0の文字幅を基準とします。
重要なのは、font-sizeを変更すると1chだけでカラム幅に対するアルゴリズム的アプローチです。
導かれる結果は、デザイナーの値も変化し、カラム幅はそれに連動するということです。
ch単位の使用は、それがブラウザに指示した計算に基づいたものになります。
監訳者注
アルファベットでは1文字分の幅がそれぞれ異なるため、1文字分を正確に表現できるCSS単位がありません。
しかしCSS単位で表現できるものの中では、アルファベットの多くは0の文字に比較的近いため、目安としてch単位を使用しています。
日本語の場合であれば、1emの倍数を使用するのがよいでしょう。
chを使うとfont-sizeに依存しない公理が実現可能になります。
そのおかげであらゆる箇所への適用が可能になり、「間違ったことをする」危険がなくなります。
どこかのドキュメントに「カラム幅は60chを超えないこと」と書き留める必要はありません。
コード化することによってデザインそのものの特性にしてしまえるのです。
p61
本来、Webのためにデザインすることは、「目で見ずに」デザインするということです。
次の条件から生じる、あらゆる視覚的な組み合わせをすべて想定しておくことは不可能だからです。
1. レイアウトコンポーネントの自由な配置
2. エンドユーザーごとの状況や設定
2 01 Stack p68
フロー要素にはスペース(余白)が欠かせません。
物理的な意味でも、概念的な意味でも、前後の要素との間には距離を取る必要があります。
この役割を果たすのが margin プロパティです。
デザインシステムにおいては、要素やコンポーネントはそれぞれ独立したと考えます。
設計の時点では、周囲が別のコンテンツに取り囲まれるかどうかも、そのコンテンツがどのような性質なのかも決まっていません。
ある要素やコンポーネントがさまざまな場面で登場する可能性があり、それに応じてスペースの要件も異なります。
慣習的に、スタイル設定は要素や要素のクラスに対して行います。
これは、スタイル宣言を要素に「結びつけている」ということです。
基本的にはそのことに問題はありません。
しかしmarginは実のところ、近接するふたつの要素間の「関係性」を扱うプロパティです。
そのため、次のコードには問題があります。
p {
margin-bottom: 1.5rem;
}
この宣言ではコンテキストを考慮できておらず、マージン設定が適切なものになるかどうかは場合によります。
段落要素の後に他の要素が続く場合にはうまく機能します。
:last-child にあたる段落には余計なマージンができてしまいます。
さらには、親要素にパディングが設定されている場合、その内側で、この余計なマージンと親要素のパディングが足し合わされて二重のスペースができてしまいます。
それが、このアプローチのひとつの問題です。
解決策
コツは、個々の要素ではなくコンテキストに対してスタイルを設定することです。
Stack レイアウトプリミティブでは、要素間へのマージンの挿入を、それらの共通の親要素を介して行います。
.stack > * + * {
margin-top: 1.5rem;
}
隣接兄弟結合子 (+) を使用する場合、要素の前に別の要素が存在するときのみ margin-top が適用されます。
「余り」のマージンができることはありません。
そしてユニバーサル(またはワイルドカード)セレクタ(*)によって、あらゆる種類のすべての要素に作用するようになっています。
鍵となる * + * の組み合わせは、フクロウセレクタ ※1 と呼ばれています。
p84
ところが、案外そうでもありません。
CSS の手法の中には、個々の要素にあらゆるスタイルを適用できるようにするものも (見方によっては苦痛ではあるものの) ありますが、多くのスタイルはそのようにひとつずつ記述する必要がありません。
font-family や color, line-height のようなスタイルはすべて、継承させたりグローバルに適用したりできます (「グローバルスタイルとローカルスタイル」の節を参照)。
また、そうすべきでもあります。
これらのスタイル設定を個別に行うことには無駄があります。
:root {
font-family: sans-serif;
}
.box {
``` このスタイルは継承されるため不要 ```
``` font-family: sans-serif; ```
}
もちろん、複数のfont-familyを使ってデザインすることもあるでしょう。
しかし、その場合でも、すべてを個別的なものとしてスタイル設定するよりも、まずデフォルト(ベース)のスタイルを適用してから、その後で「例外」を設定するほうが効率的です。
好都合なことに、グローバルスタイルの多くは「ブランディング」に関わるスタイルです。
つまり美観に影響するスタイルであって、対象となる要素の「構造」には影響しません。
本書の目的は「レイアウトシステム」の作成と考察であり、ブランディング(美観) には言及しません。
構築しようとしているのは、動的でレスポンシブなワイヤーフレームであり、美観はその上に付け加えられるものです。
p96
筆者は当初。カラムの中央揃えにmarginのショートハンドを使用していました。(<body>要素を対象にすることがよくありました)。
.center {
max-width: 60ch;
margin: 0 auto;
}
しかしショートハンドプロパティは、数バイトの節約にはなるとはいえ、本来必要でない値も含めて宣言することになるという問題があります。
重要なのは、目的とするレイアウトを実現するために必要なCSSの値のみを設定することです。
デフォルト値や継承される値を意図せず取り消してしまうかもしれないからです。
たとえば、 .center 要素を Stack コンテキスト内に配置するとしましょう。
Stack によって子要素に margin-top が設定されますが、 .center に margin: 0 auto があれば取り消されてしまいます。
代わりに、明示的な margin-left と margin-right プロパティを使用するとよいでしょう。
そうすることで、コンテキストに応じて適用された垂直方向のマージンは保持されたまま、 .center 要素は他のレイアウトコンポーネントとのコンポジションや入れ子に使用できるようになります。
.center {
max-width: 60ch;
margin-left: auto;
margin-right: auto;
}
カラム幅
前述のコード例のように、 max-width は基本的に ch 単位で設定すべきです。
適切なカラム幅を基準に考えるためです。
適切なカラム幅を設定する方法については 「公理」の節を参照してください。
内在的Webデザイン p125
「内在的なWebデザイン (Intrinsic Web Design) ※2」はジェン・シモンズ(Jen Simmons) による造語で、
CSSが持つ機能や仕組みのWebメディアへの最適化に伴う、昨今の、新しいWebデザイン手法の到来を指し示しています。
本書で紹介した「アルゴリズム的」な自己制御型のレイアウトも、内在的なデザイン手法といえるかもしれません。
「内在的 (intrinsic)」 という言葉は、レイアウトパターンが自らについて計算するという内省的な処理を示しています。
この節では、「内在的」という言葉を特に、要素の幅が必然的に自身のコンテンツによって決定されることを表すのに用いてきました。
あるボタンの幅は、明示的に設定されない限り、その中にあるものの幅になります。
「CSS Box Sizing Module」は、以前は「Intrinsic & Extrinsic Sizing Module」と呼ばれていました。
内在的にも、外在的にも、要素のサイズがどのように決まるのかを定めているものだからです。
制作者としては、基本的には内在的なサイズ設定を優先して使用すべきです。
「公理」の節で述べたように、要素のサイズはコンテンツに応じてブラウザが決定できるようにします。
レイアウトを規定する代わりに、「意図の提示」を行うべきでしょう。
私たちは「部外者」なのです。
#2. Everything You Know About Web Design Just Changed
https://noti.st/jensimmons/h0XWcf
p183
しかし、 horizontal-tb モードにおいて、水平方向のスクロールがまったく許容されないというわけではありません。
垂直方向にスクロールするページ内で水平方向にスクロールするセクションでも、慎重かつ明快な形で実装できさえすれば、使い勝手よくコンテンツを閲覧できるようになります。
たとえば動画のストリーミングサービスでは、コンテンツのカテゴリを垂直方向に、番組を水平方向に一覧表示することがよくあります。
ここで極力避けたいのは、要素を縦横の双方向にスクロールさせることです。
これは、WCAGの「達成基準 1.4.10 リフロー」では不適合とみなされます。
解決策 p184
「Cluster」の節で述べたように、ブロックのフロー方向を変更するのに効率的な方法はフレックスボックスコンテキストを作成することです。
要素に display: flex を適用することで、その子要素の進行方向は (書字方向がデフォルトのLTR、つまり左から右になっていれば)下向きから右向きに切り替わります。
併せて使用されることの多い flex-wrap: wrap の宣言を省略することで、要素が1行に並ぶようになります。
そのコンテンツの行の幅が親要素を超えて広がる場合には、はみ出して配置されます。
デフォルトでは、その結果ページ自体に水平方向のスクロールが生じます。
しかし、実際にスクロールが必要なのはフレックスボックスのコンテンツのみなので、これは望ましくありません。
他の部分はそのままになっていたほうがよいでしょう。
そこで代わりに、フレックス要素に overflow: auto を適用します。
すると、はみ出しが実際に発生した箇所でのみ、その要素内をスクロールできるようになります。
.reel {
display: flex;
``` 必要なのは水平方向のスクロールのみ ```
overflow-x: auto;
}
スクロールできることを示す見た目の作成や、スペースの調整の問題もありますが、これがレイアウトの基礎的な仕組みです。
ブラウザの標準的な動作を利用しているため、一般的なカルーセルあるいはスライダーのjQueryプラグインとは大きく違い、堅牢かつコードも非常に簡潔になります。
p195
さらに、ボタンで展開するメニューに代わるものとしても使用できます。
ブラッドリー・トーント (Bradley Taunt) が「ソーセージリンク」と呼ぶパターンは、多くの人にとってハンバーガーメニューよりも使いやすいものでしょう。
しかしこの例のような場合では、目に見えるスクロールバーはやや過剰なものに感じられます。
そのためカスタム要素の実装では、 boolean の noBar prop を含めています。
もちろん、リンクがソーセージのような形でなくてもかまいません!
ただ語源として残っているだけです。
ひとつ注意しなければならないのは、スクロールバーが無いためにスクロールができないように見えるかもしれないことです。
一番右側に表示されている子要素が見切れていれば、要素がはみ出していてスクロールできるということが明白になるでしょう。
見切れていなければ、すべての要素がすでに表示されているように見えてしまいます。
2 11 Imposter p203
CSS において、 position プロパティの relative, absolute, fixed のいずれかを使って位置指定をすることは、Webレイアウトを「マニュアルオーバーライド」するようなものです。
自動レイアウトを取り止めて、自らの手で対応することになるからです。
民間航空機の操縦と同じように、これはごくまれな状況を除いて、引き受けたいと思うような責務ではありません。
ブラウザの標準的なレイアウトアルゴリズムから遠ざかる危うさについては、「Frame」の節で警告しました。
「position: absolute を指定すると、その要素はドキュメントの本来のフローから除外されます。
まるでその周りの要素が存在しないかのようにレイアウトされるのです。
ほとんどの場合、これは非常に望ましくないことで、コンテンツ同士が重なったり見づらくなったりといった問題を引き起こしやすくなります。」
しかし、コンテンツの上に別のコンテンツを覆い被せて、あえて見えなくしてしまいたい場合はどうでしょうか。
23分以上もWeb開発に携わっている人なら、ダイアログ要素や「ポップアップ」、独自ドロップダウンメニューの組み込みですでに経験済みでしょう。
Imposter 要素の目的は、汎用的な重ね合わせ要素をあなたのレイアウト集に追加することです。
これによって、ビューポートやドキュメント、または特定の「位置指定コンテナ」要素において、任意の要素を中央配置できるようになります。
解決策
要素を垂直方向に中央配置する方法はたくさんありますし、水平方向に中央配置する方法はもっと豊富です (Centerレイアウトの一部としていくつか紹介しています)。
しかし、他の要素やコンテンツの上に重ねつつ、中央配置ができる方法は限られています。
これらに同時に対処できるアプローチは、CSSグリッドを使うことです。
いったんグリッドを構築してしまえば、グリッドのライン番号に従ったコンテンツの配置ができるようになります。
フローの概念から解放されて、どこでも好きな場所に要素を重ね合わせられます。
grid-aria: 2 / 2 / 5 / 8
ソース内の順序とレイヤー
グリッドラインに沿ってコンテンツを配置するにしても、 position プロパティを使うにしても、
どの要素がどの要素の上に表示されるかは、デフォルトではソース内の順序によって決まります。
つまりふたつの要素が同じ空間を共有している場合、ソース内で最後になる要素が上に重なって表示されます。
好きなグリッドラインに沿って要素を配置できるので、デフォルトではレイヤーの上に重なる、ソース内の順序としては後になる要素を、縦軸方向の上部に置くこともできます。
これはよく見落とされることで、レイヤーを設定するためには position: absolute に z-index が必須だと思っている人もいます。
しかし実際には、ソース内の順序と関係なく重ね合わせたい場合を除いて z-index は必要ありません。
これもまた一種のオーバーライドですから、できるだけ避けるようにしてください。
激しくせめぎ合い、増大していく z-index の値にはうんざりするものの、 CSS を使用するうえでは対処しなければならない問題のひとつとしてよく挙げられます。
しかし筆者は z-index の問題に直面する機会はほとんどありません。
位置指定をめったに用いませんし、用いてもソース内の順序に気をつけているからです。
CSSグリッドは普遍的な解決策にはなりません。
これが機能するためには、位置指定を用いる要素にあらかじめ display: grid が指定されており、
列 (column) と行 (row) がちょうどいい数になっていないといけないからです。
もっと柔軟な方法が必要です。
解決策 p220
垂直方向の位置揃え
前述の currentColor の説明にもあるように、本書では、アイコンをテキストのように扱い、できるだけシームレスにテキストに添えるようにします。
幸いなことに、 SVG はデフォルトで、文字であるかのようにテキストの baseline に配置されます。
高さのあるアイコンには、 vertical-align: middle が使用できると思われるかもしれません。
しかし、大方の予想とは裏腹に、この場合に基準となるのは、フォントの垂直方向の中央ではなく、フォントの「小文字」の垂直方向の中央です。
そのため、おそらく望ましい結果にはならないでしょう。
vertical-align: baseline
vertical-align: middle
高さのあるアイコンの垂直方向の配置を調整するためには、おそらく vertical-align プロパティに長さを指定することになるでしょう。
この長さはベースラインから上方向への距離を表しており、負の値を取ることもできます。
vertical-align: -0.125em
本書の Icon レイアウトでは、アイコンはベースライン上に乗せるように配置します。
これが最も堅牢なアプローチなのです。
アイコンがベースラインよりも下にはみ出していると、テキストが折り返された場合に被さってしまう可能性があります。
高さを合わせる
ベースラインを基準とした場合のアイコンの適切な高さは、フォントの大文字と小文字の使い方や、ディセンダーの有無によって多少変わってきます。
特に、文字がすべて小文字で、かつディセンダーが含まれる場合には、バランスが悪く見えます。
この知覚的な問題を緩和するには、最初の文字は常に大文字にして、大文字の大きさにアイコンの大きさを揃えるようにすればよいでしょう。
しかし、アイコンを実際のフォントの大文字の大きさに合わせるとなると、また別の問題が出てきます。
1em を指定すればよいと思われるかもしれませんが、それではほとんどの場合うまくいきません。
1em はフォント自体の高さのほうに近いのです。
特定のフォントを使ったテキストをテキスト選択してみると、フォントの高さはたいていその大文字よりも大きいことがわかります。
言い換えれば、1emというのは文字のメトリクスではなく、フォントのメトリクスに対応しているのです。
筆者の検証では、より大文字の高さに近いのは0.75emでした。
そのため、バツ印のプレゼンテーション属性にはそれぞれ0.75emを指定します。
これによって、viewBoxで設定された正方形を形成します。
<svg viewBox="0 0 10 10" width="0.75em" height="0.75em" stroke="currentColor stroke-width="2">
<path d="M1,19,9M9,11,9" />
</svg>
使用しているフォントは左から、 Arial、 Georgia, Trebuchet, Verdana。
0.75emのアイコンはそれぞれの大文字の高さと一致しています。
まだ新しいcap単位を使用できれば、より正確にフォントの大文字の高さに揃えられるようになります。
しかし現時点ではほとんどのブラウザにサポートされていないので、本書のCSSではフォールバックとして0.75emを使用します。
.icon {
height: 0.75em;
height: 1cap;
width: 0.75em;
width: 1cap;
}
制作者がプレゼンテーション属性を省略した場合に備えて、CSSでも0.75emの値を指定しておいたほうがよいでしょう。
筆者のアンディが「Relative sizing with EM units」で述べたように、アイコンはテキストに合わせて拡大・縮小がされるようになっています。
0.75emは、コンテキスト上のfont-sizeと相対的な値になっています。
次のコードをご覧ください。
.small {
font-size: 0.75em;
}
.small icon {
``` アイコンの高さは自動的に0.75 0.75em になる ```
font-size: 1,25em;
}
.big icon {
``` アイコンの高さは自動的に 1.25 * 0.75em になる ```
}
小文字の高さに合わせる
アイコンに合わせるテキストを小文字にする場合では、アイコンの高さは小文字に揃えたほうがきれいに収まるでしょう。
これには、現時点ですでに、小文字の「x」の高さを基準とするex単位が採用できます。
その場合、小文字表記を強制してもよいでしょう。
.icon {
width: 1ex;
height: 1ex;
}
``` これがアイコンの親要素または祖先要素である想定 ```
.with-icon {
text-transform: lowercase;
}
アイコンとテキスト間のスペース設定
アイコンのスペース設定をどのように管理するか決めるためには、柔軟性と効率を秤にかけなければなりません。
デザインシステムにおいては、規則性と一貫性が守られるという意味で、柔軟性のなさがよしとされる場合もあります。
バツ印のアイコンを、ボタン要素内で「Close」というテキストの隣に配置した場合について考えてみます。
<button>
<svg class="icon"> ... </svg> Close
</button>
SVGとテキストノードの間にあるスペース文字 (厳密にはUnicodeのコードポイント U+0020) に注目してください。
これによって、アイコンとテキストの間に目に見えるスペースができますが、このスペースは調整ができません。
ソース上で同じスペース文字を連続させても、ブラウザによって単一のスペース幅まで切り詰められてしまうので効果はありません。
しかし、このスペースの大きさは、同じコンテキストにおける単語間のスペースと同じなので、このほうが都合がよいのです。
というのも、本書ではアイコンをテキストのように扱っているためです。
アイコンで単純にスペース文字を使用することには、他にもいくつかの利点があります。
1.
アイコンだけが単独で表示されている場合には、ソース上でスペース文字が残っていたとしても目に見えるスペースができません (ボタン内にスペースができると不均等に見えてしまいます)。
この条件でもスペースは切り詰められます。
2.
dir属性にrtl (右から左) の値を指定すると、アイコンの表示位置を左から右に入れ替えられます。
その場合、テキストの方向がスペースも含めて逆になるので、アイコンとテキストの間のスペースが維持されたままになります。
<button dir="rtl">
<svg class="icon"></svg> Close
</button>
特注のスタイルを記述して任意のクラスに適用するよりも、HTMLの基本的な機能を使ってデザインを実現するほうが賢いやり方です。
スペースの長さを制御したければ、複雑性が増加して再利用性が低下することを受け入れなければなりません。
そのためにはまず、既存のスペースを取り除くために、アイコンにフレックスコンテキストを設定する必要があります。
次のコードでは、 .with-icon 要素に inline-flex のコンテキストを設定しています。
icon {
height: 0.75em;
height: 1cap;
width: 0.75em;
width: 1cap;
}
.with-icon {
display: inline-flex;
align-items: baseline;
}
display inline-flex の値は、その名の通りフレックスコンテキストを生成しますが、コンテキストを作成している要素自体はインラインとして表示されます。
inline-flexを使用することで、スペースを取り除き、マージンだけでスペースあるいは隙間を設定できるようになります。
後はマージンを追加しましょう。
スペース文字からできるスペースと同じように、常に正しい位置に設定されるにはどうすればよいでしょうか?
テキストの前である左側にアイコンがある場合、margin-left: 0.5emを指定すればうまくいきます。
しかし、 dir="rel" を指定すると、マージンは右側に残ったままで、間違った側にでてしまいます。
この場合の正解は、CSSの論理プロパティを使用することです。
margin-top に margin-right 、 margin-bottom 、 margin-left はいずれも「物理的」な方向とを配置に関係していますが、プロパティは「コンテンツ」の方向を尊重します。
「ボックス」の節で述べたように、これはフロー方向や書字方向によって異なるものです。
今回は、アイコン要素に margin-inline-end を指定します。
これによって、テキストの方向に従って要素の「後ろ」にマージンが適用されるようになります。
.icon {
height: 0.75em;
height: 1cap;
width: 0.75em;
width: 1cap;
}
.with-icon {
display: inline-flex;
align-items: baseline;
}
.with-icon .icon {
margin-inline-end: var(--space, 0.5em);
}
dir="ltr"
dir="rtl"
このように柔軟なスペース設定ができるアプローチにも、ひとつ欠点が残ります。
それは、テキストが指定されていない場合でもマージンが適用されてしまうことです。
only-childの使用によって単独の要素を対象にすることはできても、残念なことに「テキストノードを伴わない」単独の要素を対象にすることはできません。
そのため、CSSだけでマージンを取り除くことができないのです。
代わりに、 with-icon クラスを削除することができます。
このクラスは、 margin によるスペースの設定を行うだけのものだからです。
そして、削除した場合に残るスペース文字は、前述したように自動的に切り詰められます。
この後のカスタム要素による実装では、 space prop が指定されている場合のみ <icon-l> が inline-flex の要素となり、スペース文字は削除されます。