このページでは、コンテンツエリアのレイアウト方法について紹介します。
- タイポグラフィと余白のスケーリング設計
- コンテンツ外側の余白の管理方法
- コンテンツ間の余白の管理方法
- コンテンツ幅の管理方法
- 内部の全幅コンテンツの管理方法
これらについて紹介します。
タイポグラフィと余白のスケーリング設計
まずは、WEBで情報を伝える上で最も重要となるタイポグラフィの基本を抑えておきましょう。
これらについては、すでに別ページで解説していますので、そちらをご覧ください。
調和の取れたスケーリング規則が設計されていれば、それだけで十分コンテンツが読みやすい状態となります。
このページの解説で採用するスタイル
先ほど紹介した関連ページで紹介したタイポグラフィと余白のスケーリングを組み合わせて採用しています。(あえて個別解説はここではしませんので、なぜこれらのスタイルを採用したかは関連ページをご覧ください。)
:root {
/* ベースとなる流体フォントサイズ */
--REM: clamp(0.95rem, 0.915rem + 0.15vw, 1.05rem);
/* フォントサイズのスケーリング */
--fz--xs: calc(1em * (8 / 10));
--fz--s: calc(1em * (8 / 9));
--fz--base: var(--REM);
--fz--l: calc(1em * (8 / 7));
--fz--xl: calc(1em * (8 / 6));
--fz--2xl: calc(1em * (8 / 5));
--fz--3xl: calc(1em * (8 / 4));
--fz--4xl: calc(1em * (8 / 3));
--fz--5xl: calc(1em * (8 / 2));
/* ハーフレディング */
--hl-unit: calc(var(--REM) * 0.125); /* ≒ 2px */
--hl--xs: var(--hl-unit);
--hl--s: calc(var(--hl-unit) * 2);
--hl--base: calc(var(--hl-unit) * 3);
--hl--l: calc(var(--hl-unit) * 4);
/* 余白のスケーリング */
--s-unit: calc(var(--REM) * 0.5); /* ≒ 8px */
--s--5: calc(0.5 * var(--s-unit));
--s--10: var(--s-unit);
--s--15: calc(1.5 * var(--s-unit));
--s--20: calc(2 * var(--s-unit));
--s--30: calc(3 * var(--s-unit));
--s--40: calc(5 * var(--s-unit));
--s--50: calc(8 * var(--s-unit));
--s--60: calc(13 * var(--s-unit));
--s--70: calc(21 * var(--s-unit));
--s--80: calc(34 * var(--s-unit));
}
body {
--hl: var(--hl--base);
font-size: var(--fz--base);
}
/* ligh-height をハーフレディングで管理 */
* {
line-height: calc(1em + var(--hl) * 2);
}
/* 見出しのスケーリング */
h1 {
font-size: var(--fz--3xl);
}
h2 {
font-size: var(--fz--2xl);
}
h3 {
font-size: var(--fz--xl);
}
h4 {
font-size: var(--fz--l);
} コンテンツ外側(左右)の余白の管理
コンテンツエリアの左右につける余白は、専用のCSS変数で管理するのを個人的にはお勧めします。
用途が明確であることと、後述する全幅コンテンツの管理にも活用できるためです。
例えば Lism CSS では、has--gutter というクラスを用意し、そのクラスに対して--gutter-sizeというCSS変数を設定しています。
:root{ --gutter-size: var(--s--30);}.has--gutter { padding-inline: var(--gutter-size);}このように専用の変数やクラスとして役割を分離しておくと、コンテンツエリアだけでなく、ヘッダーやフッターでも、同じ余白を簡単に設定できます。
コンテンツどうしの間(上下)の余白管理
コンテンツエリア内の通常フローの中で、コンテンツとコンテンツの間の余白を管理する方法について考えていきます。
margin-top(margin-block-start) と margin-bottom (margin-block-end) のどちらを使って余白を確保するかについて、数年前はよく議論になっていました。
しかし最近では、top派が多いように感じます。
自分もそうです。
なぜかというと、フクロウセレクタ(* + *)を使えばとてもシンプルに書けるからです。
/* コンテンツ間の余白管理の例 */article > * + * { margin-block-start: 2rem;}さらに、見出し要素の上側は余白を多くとるという対応も簡単にできます。
/* 見出し要素の上側は余白を多くとる */article > :is(h1, h2, h3, h4) { margin-block-start: 4rem;}Lism CSS では、これらのコンテンツ間の余白管理を行うクラスとして次のようなl--flowを用意しています。
:root{ --flow--base: var(--s--30);}.l--flow > * + * { --flow: var(--flow--base); margin-block-start: var(--flow);}
/* 見出しの上部の余白は広くする. */.l--flow > :where(h1, h2, h3, h4, h5, h6) { margin-block-start: calc(var(--flow) * 2);}
.l--flow > :first-child { margin-block-start: 0px;}コンテンツ幅の管理
次に、コンテンツ幅を管理する方法について考えていきます。
例えば、ページのコンテンツエリアが次のような構造をしているとします。
<body> <main> <article> ...コンテンツ... </article> </main></body>ここで、例えばarticle内のコンテンツ幅を50remにしたい時を考えてみましょう。
コンテンツ幅を管理する方法は、主に2パターンあります。
/* ① ラッパー要素(コンテンツの親要素)の幅を制御する */article{ max-width: 50rem; margin-inline: auto;}
/* ② 各コンテンツ(ラッパー直下の要素)の幅を制御する */article > * { max-width: 50rem; margin-inline: auto;}どちらも一長一短ありますが、個人的には ② の方法を推奨しています。
なぜかというと、コンテンツエリアの途中で全幅サイズのコンテンツを作る場合の処理がシンプルになるためです。
全幅コンテンツの実装方法の違い
①のアプローチを採用した場合、全幅コンテンツのCSSは次のようになります。
.fullwide{ max-width: 100vw; margin-inline: calc(50% - 50vw);}一見シンプルに見えますが、vw(vi)などの単位ではスクロールバーの幅が考慮されないという問題点があります。
また、サイドバーがある時にも破綻しないようにしようとするとCSSが複雑化していきます。
スクロールバーの幅問題については、JSを使って対処するか、もしくはモダンCSSであれば@propertyとcqw(cqi)を使った変数定義でbodyサイズを記憶させておくというアプローチもあります。(後ほど軽く解説します)
一方、②であれば、基本のCSSはmax-width:100%だけでOKで、has--gutter直下での例外処理を加えれば済みます。
.fullwide{ max-inline-size: 100%;}
/* コンテンツ両端の余白を考慮した全幅コンテンツの実装 */:where(.has--gutter) > .fullwide { max-inline-size: calc(100% + var(--gutter-size) * 2); margin-inline: calc(var(--gutter-size) * -1);}コンテンツ幅の管理クラスを用意しておく
②のアプローチでは、コンテンツ幅の管理クラスを用意しておくと便利です。
Lism CSS では、is--wrapper として用意しています。
.is--wrapper { --contentSize: 50rem;}.is--wrapper > * { --max-sz: var(--contentSize, 100%); max-inline-size: min(100%, var(--contentSize)); /* min ないとimg要素等がはみ出すことがある */ margin-inline: auto;}ここまでのスタイルを組み合わせて完成
ここまで用意してきたスタイルを駆使して、コンテンツエリアを実装してみましょう。
@import '../_typography.css';
:root{
--gutter-size: var(--s--30);
--flow--base: var(--s--30);
}
/* コンテンツの両端の余白 */
.has--gutter {
padding-inline: var(--gutter-size);
}
/* コンテンツ幅の制御 */
.is--wrapper {
--contentSize: 50rem;
}
.is--wrapper > * {
--max-sz: var(--contentSize, 100%);
max-inline-size: min(100%, var(--contentSize)); /* min ないとimg要素等がはみ出すことがある */
margin-inline: auto;
}
/* コンテンツ間の余白 */
.l--flow > * + * {
--flow: var(--flow--base);
margin-block-start: var(--flow);
}
/* 見出しの上部の余白は広くする. */
.l--flow > :where(h1, h2, h3, h4, h5, h6) {
margin-block-start: calc(var(--flow) * 2);
}
.l--flow > :first-child {
margin-block-start: 0px;
}
/* その他 - コンテンツエリア上下の余白 */
main{
padding-block: var(--s--40);
} おまけ:@propertyとcqwを使った全幅サイズ管理
最後に、途中でちらっと紹介した、全幅サイズのコンテンツを vwを使わずに@propertyとcqwを使って管理する方法について、補足として実例を紹介します。
コンテンツ幅の管理パターンの①で特に有効なテクニックですが、②を採用していても覚えておいて損はありません。
/* 直前のデモと同じスタイルを読み込む */
@import '../demo/_src.css';
/* スクロールバーを除いたウインドウサイズを保持するためのプロパティ */
@property --fullwide-width {
syntax: '<length-percentage>';
initial-value: 100cqw;
inherits: true;
}
/* コンテナ定義 */
.is--container{
container-type: inline-size;
}
.is--container > * {
/* コンテナサイズをセット */
--fullwide-width: 100cqw;
}
.is--container.has--gutter > * {
/* 左右に余白がある場合はそれを考慮してセット */
--fullwide-width: calc(100cqw + var(--gutter-size) * 2);
}
/* コンテンツエリア自体に幅を指定する */
article{
max-width: 50rem;
margin-inline: auto;
/* デモ用ガイドライン */
border: dashed 1px #ccc;
}
/* 全幅コンテンツ */
.fullwide{
/* 左右に飛び出す */
max-width: var(--fullwide-width);
margin-inline: calc(50% - calc(var(--fullwide-width) / 2));
/* デモ用ガイド装飾 */
padding: 1.5em;
border: solid 2px;
background-color: rgba(0, 100, 200, 0.2);
}
/* 以下、デモ用ダミーコンテンツ */
._dummybox_{
background-color: rgb(240, 240, 240);
padding: 4em 2em;
text-align: center;
}
--fullwide-widthという@propertyで定義した変数を用意しておき、is--containerでコンテナ定義した要素の直下で100cqwをセットしています。
今回の例では、mainタグがコンテナとなりその直下のarticleで、コンテナサイズがセットされる仕組みです。
is--containerの配置場所に気をつけなくてはいけませんが、こういったテクニックを活用することで、100vwでスクロールバーの幅が含まれてしま う問題を解決できます。
bodyタグに container-type を指定するのはお勧めしません。position:fixedが使えなくなります。(将来的にはその問題は解決しそうですが…)