Every Layoutで汎用性の高いCSS設計を学ぶ

CSSが難しい

ここ何年もWebアプリの開発に関わっていますが、使用する技術の中でもCSSは苦手意識がありました。CSSはある程度知識があれば実装自体はそれほど難しくありませんが、再利用性を高めて汎用性の高いCSSの設計にするのはまだ私のとって難しく感じます。これまで何度もWebアプリを開発していましたが、画面の数や要素が多くなってきたときに、似たようなレイアウトに対して同じようなスタイルをコピペして実装してしまい、コード量が増えてしまうケースが多々ありました。

CSSで思い通りの実装ができるだけでなく、再利用性や保守性を高めるようなCSSの設計論を学びたいと思って読んだのがEvery Layoutです。

Every Layout | 株式会社ボーンデジタル

定価 2,970円(本体2,700円+税10%) 発行・発売 株式会社 ボーンデジタル ISBN 978-4-86246-517-7 総ページ数 240ページ サイズ B5正寸 発売日 2021年11月下旬

https://www.borndigital.co.jp/book/24204/

Every Layoutでは、相対的な単位(rememなど)を有効に使いながら、小さなレイアウト(レイアウトプリミティブと呼ばれる部品。詳細は後述)の組み合わせによって、一貫性のあるレスポンシブデザインを効率的に実現するための設計論がまとめられています。

CSSフレームワーク

CSSを効率よく実装するための方法として、BootstrapTailwindなどのCSSフレームワークを使用する方法もあります。このようなフレームワークでは、あらかじめ豊富なスタイルが定義されており、HTMLのclass属性にユーティリティクラスと呼ばれるクラスを指定することで、CSSを自分で記述することなく様々なレイアウトが実現可能となります。

以下はTailwindを使ったdiv要素へのスタイルのサンプルです。

<div class="mx-auto flex max-w-sm items-center gap-x-4 rounded-xl bg-white p-6 shadow-lg outline outline-black/5 dark:bg-slate-800 dark:shadow-none dark:-outline-offset-1 dark:outline-white/10"> 
  ...
</div>

Tailwindの公式サイトからの抜粋

このようなライブラリは、開発効率を優先しながらサクッと良い感じのレイアウトを作りたいときには非常に便利です。一方、サンプルコードを見れば分かりますが、このようなライブラリを使う場合HTMLタグのclass属性が多くなるため、要素が多くなると可読性が落ちる傾向にあります。また、チームで分担しながら開発をする場合だと、ページごとに実装方針にばらつきが出やすくなります。このようなライブラリは小規模な開発で使用する場合や、Webページの中で部分的に適用したい場合には使い勝手が良さそうですが、Webサイト全体に対して積極的に採用するのは可読性や保守性の面で避けたいというのが私の個人的な意見です。

このような一般的なCSSフレームワークとは異なる戦略でCSSの再利用性を高めるための考え方を提供しているのが、Every Layoutです。

Every Layoutの考え方の概要

Every Layoutでは、プリミティブ(レイアウトプリミティブ)と呼ばれる小さなレイアウトの部品(コンポーネント)を定義し、プリミティブの組み合わせによって多くのレイアウトを実現することを目指します。

具体的には、以下の12個をレイアウトプリミティブとしてみなしています。

  • Stack
  • Box
  • Center
  • Cluster
  • Sidebar
  • Cover
  • Grid
  • Frame
  • Reel
  • Imposter
  • Icon

以下のHTMLは、このプリミティブを使って作成した入力フォームのサンプルです。
ここに登場するbox-l, stack-l, cluster-lは本の中でも登場するプリミティブを表現するためのカスタム要素で、それぞれのレイアウトの責務に応じた属性(props)を指定することができます。

<box-l padding="1.5rem">
	<form action="#">
		<stack-l space="1.5rem">
			<stack-l space="0.75rem">
				<label for="email">メールアドレス</label>
				<input id="email" type="email" name="email">
			</stack-l>
			<stack-l space="0.75rem">
				<label for="password">password</label>
				<input id="password" type="password" name="password">
			</stack-l>
			<stack-l space="0.75rem">
				<label for="passwordConfirm">password(確認)</label>
				<input id="passwordConfirm" type="password" name="passwordConfirm">
			</stack-l>
			<cluster-l>
				<button>送信</button>
			</cluster-l>
		</stack-l>
	</form>
</box-l>

Stackは、要素を縦方向に自然な間隔で積み重ねるためのレイアウトです。spaceプロパティで、要素間の余白のスペースを設定しています。
Boxは、コンテンツに一貫した余白・枠・背景を与えるための、最小単位の入れ物のレイアウトです。ここではpaddingのみを指定しています。
Clusterは、ボタンやタグのような要素群を、折り返し可能な横並びで配置するレイアウトです。

このように、プリミティブと呼ばれる部品を用意し、それらの組み合わせによってレイアウト構成を作成します。そして、個別のスタイリングが必要になった場合に例外的に対応していくのがEvery Layoutの考え方です。

なお、ここではカスタム要素を使用したHTMLにしていますが、CSSであらかじめスタイルを定義しておき、class属性の指定によってレイアウトすることも可能です。その場合、例えば以下のようなHTMLになります。

<div class="box">
	<div class="stack">
		...
	</div>
</div>

レイアウトプリミティブの組み合わせによって画面を構成することで、Tailwindなどのライブラリを使った場合に比べてclass属性が多くなって可読性が低下することを防げます。むしろ、Stack, Box, Clusterといったレイアウトの特性を知っておけば、HTMLのコードだけででレイアウト構成を把握しやすくなります。

ちなみに、Every Layoutで紹介されている各レイアウトプリミティブの概要は以下になります。
さらに詳細については知りたい方は本や他のサイトを参考にしてください。

  1. Stack
    • 要素を縦方向に自然な間隔で積み重ねるための、もっとも基本的なレイアウト
  2. Box
    • コンテンツに一貫した余白・枠・背景を与えるための、最小単位の入れ物
  3. Center
    • 要素を画面や親要素の中央に、サイズに依存せず配置するためのレイアウト
  4. Cluster
    • ボタンやタグのような要素群を、折り返し可能な横並びで配置するレイアウト
  5. Sidebar
    • 主要コンテンツと補助コンテンツの関係を、柔軟な幅配分で表現するレイアウト
  6. Switch
    • 画面幅や条件に応じて、縦並びと横並びを自動で切り替えるレイアウト
  7. Cover
    • 画面全体を覆う構成の中で、特定の要素だけを中央に配置するためのレイアウト
  8. Frame
    • アスペクト比を保ったまま、画像や動画を安全に収めるための枠組み
  9. Reel
    • 横方向にスクロール可能なコンテンツ列を、自然に扱うためのレイアウト
  10. Grid
    • カードなどの繰り返し要素を、画面サイズに応じて自動調整しながら並べるレイアウト
  11. Imposter
    • 通常のレイアウトの流れから外れ、重ねて表示したい要素を扱うための仕組み
  12. Icon
    • テキストとアイコンを、サイズや行高に影響されず安定して並べるためのレイアウト

柔軟性の高いレスポンシブデザインを実現するための基本

Every Layoutは、レスポンシブデザイン(PCとスマホのように異なる画面サイズであっても、サイズによってレイアウトが自動で最適化してユーザー体験を向上する仕組み)の実現を前提としています。

CSSでレスポンシブデザインを実現するためのメジャーな方法として、メディアクエリを使い(画面サイズによってブレイクポイントを設定する)、条件に合わせてスタイルを適用するという方法があります。

メディアクエリを使ったCSSは、例えば以下のようなコードになります。

h3 {
	font-size: 32px;
}

@media (min-width: 960px) {
  h3 {
    font-size: 40px;	
  }
}

このように書くと、画面幅が960px以上になったときに、h3要素のフォントサイズが40pxになり、959px以下の場合は32pxになります。

このように、メディアクエリを使えば画面幅によってスタイルを変更する形でのレスポンシブデザインが実現できますが、Every Layoutではメディアクエリを使用したレスポンシブデザインが本当に適切なのかを疑問視します。
先の例でいえば、画面幅が959pxの時と960px時でフォントサイズが大きく変わることになります。画面幅が1px変わるだけでサイズが大きく変わってしまうデザインは、本当に良いデザインと言えるのかでしょうか。メディアクエリでブレイクポイントを定めることなく、どんなサイズであっても最適なレイアウトになるような柔軟性を備えたレイアウト設計を目指すのがEvery Layoutの思想です。

※メディアクエリそのものを否定しているわけではなく、「レイアウトの基本をメディアクエリに依存しすぎる設計」に疑問を投げかけています。

先に紹介したレイアウトプリミティブの組み合わせによる構成は、メディアクエリを使用しない柔軟なレスポンシブデザインを実現するための方法ですが、なぜ柔軟なレイアウトが実現できるのか、その仕組みを理解するためには、いくつかの基礎知識が必要になります。

Every Layoutでは、12個のレイアウトプリミティブの解説の前に、6つのCSSに関する基礎(「ボックス」「コンポジション」「単位」「グローバルスタイルとローカルスタイル」「モジュラースケール」「公理」)についての解説から始まります。

ここでは、それらの基礎の中で「単位」にフォーカスを当て、そこで学んだことを簡単に紹介します。

px(ピクセル)よりも相対単位

CSSではサイズの単位としてpx(ピクセル)を使用することが多々あります。しかし、Every Layoutでは、サイズを指定する単位ではピクセルよりもrememといった相対単位を使うことを推奨しています。

なぜピクセルを推奨しないかというと、ピクセルは絶対的な値ではないからです。

まず、ピクセルにはハードウェアピクセルとCSSピクセルがあり、それぞれ別物です。
ハードウェアピクセルは、ディスプレイを構成する最小の発光点であり、物理的な存在です。同じ画面サイズであっても、ディスプレイの解像度によってピクセル数は変わります。

例えば、フルHDと4Kでは以下のようなピクセルの違いがあります。

  • フルHD(1920×1080) → 横1920個の物理ピクセル
  • 4K(3840×2160) → 同じサイズでも物理ピクセルは4倍

一方、CSSピクセルですが、これは物理ピクセルと1対1に対応しているわけではなく、OSとブラウザがデバイスの解像度によってピクセルを調節しています。例えば、フォントサイズを16pxとした場合、4Kスマホで物理基準の16pxは人間にとって非常に小さなサイズになってしまいます。そうならないように、OSとブラウザによってピクセルが調整されているそうです。つまり、ピクセルは絶対的なサイズではなく、デバイス毎に調整が入る相対的なサイズです。そのため、ピクセルを多用してレイアウトを作成していると、デバイスの違いによって思わぬレイアウト崩れを起こす可能性があります。

他にもピクセルを推奨しない理由はありますが、そういったいくつかの理由からrememなどの相対単位を推奨し、特にremの指定はEvery Layoutの中で数多く出てきます。

※px が悪いというよりも、「レイアウトの基準単位として px に依存すること」に注意を促しています。

vwを使用したレスポンシブのテクニック

メディアクエリを使った画面幅によってフォントサイズが変わるCSSのサンプルコードは以下のようになっていました。

h3 {
	font-size: 32px;
}

@media (min-width: 960px) {
  h3 {
    font-size: 40px;	
  }
}

Every Layoutではこのようなメディアクエリを使ったスタイリングを推奨していませんが、では画面幅によってフォントサイズを変えたい場合はどうすればよいでしょうか。

Every Layoutでは画面幅に応じてフォントサイズを変える方法として以下のようなサンプルコードがあります。

html {
    font-size: calc(1rem + 0.5vw);
}

このような指定をすると、画面サイズに合わせてフォントサイズが変わります。

vwは、viewport widthの1%(viewportが1000pxだった場合、1vwは10px)となる値です。小さい画面の場合はvw の値が小さいため、font-sizeはほぼ 1remになります。逆に大きい画面vw の値が大きいため、font-sizse1remよりも大きくなります。結果的に、ブレイクポイントなしで、レスポンシブ(画面幅によってフォントサイズが変わる)を実現できるようになります。

まとめ

書籍Every Layoutの概要と、考え方の一部を紹介しました。この本は2年ほど前に購入していたのですが、その時に読んだときは理解が難しい部分も多く、まだCSSに対する苦手意識は拭えませんでした。最近になって改めて読み直したいと思うようになり、難しい箇所はAIの助けを借りたりMDNのサイトで詳細を確認したりしながら少しずつ読み進めたところ、腑に落ちる部分が多く、若干ですがCSSの苦手意識も薄まりました。
CSSに対して苦手意識のある人や、CSSの設計論を学びたいという人にとってはレベルアップのきっかけになる書籍であると思います。

Every Layout | 株式会社ボーンデジタル

定価 2,970円(本体2,700円+税10%) 発行・発売 株式会社 ボーンデジタル ISBN 978-4-86246-517-7 総ページ数 240ページ サイズ B5正寸 発売日 2021年11月下旬

https://www.borndigital.co.jp/book/24204/