モダンCSSでよく使用されるレイアウトを実装するまとめ
Post on:2021年11月25日
ヘッダ・フッタ・コンテンツ・サイドバー、フルページのレイアウト、最下部のフッタ、中央配置、カードなど、一般的なWebサイトやスマホアプリのUIをモダンCSSで実装する方法を紹介します。
CSSの実装はデバイスベースからコンテンツベースに移行しているのが現状です。これからのプロジェクトに役立つCSSのテクニックを解説します。
下記は各ポイントを意訳したものです。
※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。
- モダンCSSで実装するレイアウト
- アスペクト比が維持される画像カード
- 最大値と最小値を決め、伸縮するカード
- コンテナに合わせて最適化されるカード
- スペースに合わせて伸縮するパンケーキ
- 聖杯レイアウト
- ラインナップ
- 積み重なるパンケーキ
- RAM (Repeat, Auto, Minmax)
- メディアクエリなしのサイドバー
- これからの中央配置
- 12spanのグリッド
モダンCSSで実装するレイアウト
Layout patternsはGoogle DevelopersによるモダンCSSで実装されたレイアウトのパターン集です。カード、ダイナミックなグリッド、フルページのレイアウトなど、一般的なWebサイトやスマホアプリのUIを構築するのに役立ちます。
ここで紹介するレイアウトのほとんどは、CSS GridとFlexboxが使用されています。Webサイトやスマホアプリでよく使用されるUIパターンを実装するのに役立ち、すべてのエバーグリーンブラウザでサポートされています。
※エバーグリーンとは、最新版に自動アップデートされるブラウザのことです。
注意: コンテナクエリを使用した実験的なものも含まれています。現在、安定したブラウザではサポートされていません。
アスペクト比が維持される画像カード
カード内の画像のアスペクト比を維持したまま、カードのサイズを変更できます。
1 2 3 4 5 6 7 |
<div class="parent"> <div class="card"> <h1>Video Title</h1> <div class="visual"></div> <p>Descriptive Text goes here</p> </div> </div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
.parent { display: grid; place-items: center; } .visual { aspect-ratio: 16 / 9; } .card { width: 50%; display: flex; flex-direction: column; padding: 1rem; } |
aspect-ratio
プロパティを使用すると、カードのサイズを変更しても、緑の画像ブロックは16 / 9
で定義された16:9のアスペクト比が維持されます。
1 2 3 |
.video { aspect-ratio: 16 / 9; } |
aspect-ratio
を使用せずに16:9のアスペクト比を維持するには、padding-top: 56.25%;
のハックで上端と幅の比率を定義する必要がありました。このハックとパーセントを計算する必要をなくすために、このプロパティが存在します。
1 / 1
で正方形を作ったり、2 / 1
で2対1の比率を作ったり、画像が設定された比率で拡大縮小するために必要なことは何でもできます。
1 2 3 |
.square { aspect-ratio: 1 / 1; } |
参考:
- CSSのaspect-ratioプロパティがすべてのブラウザにサポートされました、画像をアスペクト比で実装する今までとこれからの実装方法
- CSS aspect-ratioプロパティの基礎知識、便利な使い方、実装に必要なプログレッシブエンハンスメント
- CSS aspect-ratioプロパティの使い方、レスポンシブやレイアウトシフトで大活躍
最大値と最小値を決め、伸縮するカード
カードの幅の最小値・最大値を決め、レスポンシブ対応のサイズを定義します。
1 2 3 4 5 6 7 |
<div class="parent"> <div class="card"> <h1>Title Here</h1> <div class="visual"></div> <p>Descriptive Text. Lorem ipsum dolor sit, amet consectetur adipisicing elit. Sed est error repellat veritatis.</p> </div> </div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
.parent { display: grid; place-items: center; } .card { width: clamp(23ch, 50%, 46ch); display: flex; flex-direction: column; padding: 1rem; } .visual { height: 125px; width: 100%; } |
width: clamp(<min>, <actual>, <max>);
でカードの幅を定義しています。clamp()
は最小値と最大値、そして実際のサイズを定義するものです。値を使用すると、次のようになります。
1 2 3 |
.parent { width: clamp(23ch, 50%, 46ch); } |
幅の最小値は23ch
で、最大値は46ch
です。ch
は文字幅の単位で、要素のフォントサイズ(具体的には0グリフの幅)に基づいています。実際のサイズは50%
で、これはこの要素の親の幅の50%を表しています。
clamp()
関数が行っているのは、50%
の幅が46ch
より大きくなる(広いビューポートの場合)か、23ch
より小さくなる(小さいビューポートの場合)まで、この要素が50%
の幅を維持させることです。親の伸縮に合わせて、カードの幅がクランプされた最大値まで増加し、クランプされた最小値まで減少します。
また、追加のプロパティ(place-items: center;
)で、親の中央に配置されたままになります。これにより、テキストの幅が広すぎたり(46ch
以上)、つぶれて狭くなったり(23ch
以下)することがないので、より読みやすいレイアウトが可能になります。
clamp()
を使用して、レスポンシブなタイポグラフィを実装することもできます。たとえば、font-size: clamp(1.5rem, 20vw, 3rem);
と記述すると、フォントサイズは常に1.5rem
と3rem
の間に固定され、ビューポートの幅に合わせて20vw
の実際の値に基づいて大きくなったり小さくなったりします。
これは、最小と最大の値で読みやすさを確保するための素晴らしいテクニックですが、すべてのモダンブラウザでサポートされているわけではないので、フォールバックを用意してテストすることを忘れないでください。
参考:
コンテナに合わせて最適化されるカード
独立したスタイルロジックを持ち、親のインラインの幅に基づいてスタイルが定義されているカード。
※コンテナクエリを使用するには、ChromeCanaryで#enable-container-queriesのフラグをオンにしてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<div class="container"> <div class="card"> <div class="visual"></div> <div> <div class="meta"> <h1>Card Title Here</h1> <h2 class="time">Subtitle</h2> </div> <p class="desc">Here is some descriptive text to support the main idea of the card. It will be hidden when there is less inline space.</p> <button>I'm a button</button> </div> </div> </div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
/* Set containment on parent */ .container { container: inline-size; width: 100%; max-width: 750px; margin: 0 auto; } /* Base Styles */ .visual { aspect-ratio: 1 / 1; } .desc { display: none; } .card { text-align: center; padding: 0.5rem; } /* Responsive styles */ /* 2-column grid layout at >=350px */ @container (min-width: 350px) { .card { display: grid; grid-template-columns: 40% 1fr; align-items: center; gap: 1rem; text-align: left; } } /* Display description at >=500px */ @container (min-width: 500px) { .desc { display: block; } } |
コンテナクエリを使用して、レスポンシブなカードを実装しています。このカードは、幅の狭いサイズでは1カラムになり、幅の広いサイズでは2カラムになります。
コンテナを作成するには、最初にcontainer
プロパティを親に設定します。
1 2 3 4 |
/* Set containment on parent */ .container { container: inline-size; } |
次に、基本のスタイルを定義します。
1 2 3 4 5 6 7 8 |
.desc { display: none; } .card { text-align: center; padding: 0.5rem; } |
親コンテナの幅に応じて、基本スタイルをアップデートします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/* 2-column grid layout at >=350px */ @container (min-width: 350px) { .card { display: grid; grid-template-columns: 40% 1fr; align-items: center; gap: 1rem; text-align: left; } } /* Display description at >=500px */ @container (min-width: 500px) { .desc { display: block; } } |
コンテナクエリはまったく同じコンポーネントをUIのさまざまな場所に配置した場合、独自のロジックを使用してサイズを変更し、最適に配置させることができます。カードのレイアウトは、グローバルのビューポートだけに頼っていたときよりも、適切にコントロールできます。
参考:
- CSSコンテナクエリの登場で、デザインのやり方も考え方も大きく変わる
- CSS コンテナクエリの基礎知識と便利な使い方を解説
- メディアクエリとは異なる、レスポンシブ対応のモジュール式コンポーネントの実装に適したコンテナクエリ
- CSSの新しい単位がすごく便利!コンテナベースの相対単位「コンテナ単位(qw, qh)」の基礎知識と使い方
スペースに合わせて伸縮するパンケーキ
スペースに合わせて伸縮し、最小値のサイズで次の行にスナップするレイアウトを作成します。
1 2 3 4 5 |
<div class="parent"> <div class="box">1</div> <div class="box">2</div> <div class="box">3</div> </div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
.parent { display: flex; flex-wrap: wrap; justify-content: center; } .box { /* Stretching: */ flex: 1 1 150px; margin: 5px; background: red; gap: 1rem; } |
マーケティングサイトなどでよく見られるレイアウトで、画像、タイトル、プロダクトの特徴を説明するテキストの3つのアイテムが並んでいるのが一般的です。小さなスクリーンでは積み重なり、大きなスクリーンでは横並びになります。
この実装にメディアクエリは不要です。flexboxを使用するだけで、スクリーンサイズが変更されたときにこれらの要素の配置を調整できます。
flex
プロパティはflex: <flex-grow> <flex-shrink> <flex-basis>
のショートハンドです。要素を伸縮させてスペースを埋めたい場合は、<flex-grow>
の値を1
にします。
1 2 3 4 5 6 7 |
.parent { display: flex; } .child { flex: 1 1 150px; } |
ボックスを<flex-basis>
サイズに合わせて表示し、小さいサイズでは縮小し、さらにスペースを埋めないようにするには、flex: 0 1 <flex-basis>
と記述します。150pxにしたい場合は、下記のようにします。
1 2 3 4 5 6 7 |
.parent { display: flex; } .child { flex: 0 1 150px; } |
参考:
聖杯レイアウト
聖杯レイアウトとは、ヘッダ、フッタ、2つのサイドバーがメインのコンテンツを挟むクラシックなレイアウトです。
1 2 3 4 5 6 7 |
<div class="parent"> <header class="section coral">Header</header> <div class="left-side section blue">Left Sidebar</div> <main class="section green"> Main Content</main> <div class="right-side section yellow">Right Sidebar</div> <footer class="section coral">Footer</footer> </div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
.parent { display: grid; grid-template: auto 1fr auto / auto 1fr auto; } header { padding: 2rem; grid-column: 1 / 4; } .left-side { grid-column: 1 / 2; } main { grid-column: 2 / 3; } .right-side { grid-column: 3 / 4; } footer { grid-column: 1 / 4; } |
このクラシックな聖杯レイアウトには、ヘッダ、フッタ、左サイドバー、右サイドバー、メインコンテンツがあります。後述する「積み重なるパンケーキ」と少し似ていますが、こちらにはサイドバーがあります。
このグリッド全体を1行のコードで記述するには、grid-template
プロパティを使用します。これにより、行と列の両方を同時に定義することができます。
grid-template: auto 1fr auto / auto 1fr auto;
は、スペースで区切られた1つ目と2つ目のリストの間のスラッシュが、行と列の間の区切りです。
1 2 3 4 |
.parent { display: grid; grid-template: auto 1fr auto / auto 1fr auto; } |
「積み重なるパンケーキ」は、ヘッダとフッタが自動的にサイズ調整されていましたが、ここでは子の固有サイズに基づいて左と右のサイドバーが自動的にサイズ調整されます。ただし、今回は垂直(高さ)ではなく、水平(幅)のサイズです。
参考:
- CSS Gridでレスポンシブ対応の代表的な5つのレイアウトを実装するテクニック
- Webページでよく使用されるレイアウトに役立つCSS Gridの実装テクニックのまとめ
- CSS Gridはレスポンシブ対応のよく使うレイアウトにも便利!効果的に使用するポイントのまとめ
- シンプルなCSSで実装できる!記事は中央に固定幅、画像は幅いっぱいに、フルブリードレイアウトを実装するテクニック
ラインナップ
ラインナップは、サイドバーに最小と最大のセーフエリアのサイズが指定され、残りのコンテンツが利用可能なスペースを埋めるレイアウトです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<div class="container"> <div class="card"> <h3>Title - Card 1</h3> <p contenteditable>Medium length description with a few more words here.</p> <div class="visual"></div> </div> <div class="card"> <h3>Title - Card 2</h3> <p contenteditable>Long Description. Lorem ipsum dolor sit, amet consectetur adipisicing elit.</p> <div class="visual"></div> </div> <div class="card"> <h3>Title - Card 3</h3> <p contenteditable>Short Description.</p> <div class="visual"></div> </div> </div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
.container { display: grid; grid-gap: 1rem; grid-template-columns: repeat(3, 1fr); } .visual { height: 100px; width: 100%; } .card { display: flex; flex-direction: column; padding: 1rem; justify-content: space-between; } h3 { margin: 0 } |
この実装のポイントは、justify-content: space-between;
を使用して、最初と最後の子要素をバウンディングボックスの端に配置し、残りのスペースを要素間に均等に配置することです。これらのカードはdisplay: flex;
で配置されており、flex-direction: column;
で方向が列に設定されています。
これにより、タイトル、説明文、画像ブロックが親カード内の縦列に配置されます。次に、justify-content: space-between;
を適用すると、最初の要素(タイトル)と最後の要素(画像ブロック)がflexコンテナの端に固定され、それらの間にある説明文が各端に等間隔で配置されます。
1 2 3 4 5 |
.container { display: flex; flex-direction: column; justify-content: space-between; } |
参考:
積み重なるパンケーキ
一般的にスティッキーフッタと呼ばれるこのレイアウトは、Webサイトやアプリでよく使用されています。
1 2 3 4 5 |
<div class="parent"> <header class="section yellow">Header</header> <main class="section blue">Main</main> <footer class="section green">Footer Content</footer> </div> |
1 2 3 4 5 6 7 |
.parent { display: grid; grid-template-rows: auto 1fr auto; /* Just for parent demo size */ height: 100vh; } |
前述の「伸縮するパンケーキ」とは異なり、このレイアウトはスクリーンサイズが変更されても子を折り返しません。一般的にスティッキーフッタと呼ばれるこのレイアウトは、Webサイトとアプリの両方で使用され、スマホアプリ(フッタは一般的にツールバー)とWebサイト(特にシングルページアプリ)で使用されています。
コンポーネントにdisplay: grid;
を追加すると、1カラムのグリッドが作成されますが、メインエリアの高さはその下にフッタがあるコンテンツと同じになります。
フッターを下部に固定するには、次のように記述します。
1 2 3 4 |
.parent { display: grid; grid-template-rows: auto 1fr auto; } |
このCSSでヘッダとフッタのコンテンツが自動的にその子のサイズを取得するように設定され、残りのスペース(1fr
)がメインエリアに適用されます。また、サイズがauto
に設定されたカラムは最小サイズを適用されるので、コンテンツのサイズが大きくなると、カラム自体もそれに合わせて大きくなります。
参考:
- 5分で完璧に分かる!CSS Gridの基本的な使い方を解説
- CSS Gridで実装する時に役立つチートシート、各プロパティと値でレイアウトがどのようになるか一目で分かる
- CSS Gridのカラム幅を1frにしたときのワナ!意図せぬ水平スクロールバーが表示されてしまった時の解決方法
RAM (Repeat, Auto, Minmax)
自動的に配置されたフレキシブルな子を備えたレスポンシブ対応のレイアウトです。
1 2 3 4 5 6 |
<div class="parent white"> <div class="box">1</div> <div class="box">2</div> <div class="box">3</div> <div class="box">4</div> </div> |
1 2 3 4 5 |
.parent { display: grid; grid-gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); } |
このレイアウトはここまでのテクニックを組み合わせて、自動的に配置されたフレキシブルな子を持つレスポンシブ対応のレイアウトを作成します。ここで覚えておくべき重要な用語はrepeat
、auto-(fit|fill)
、minmax()
で、頭文字をとってRAMと呼ばれています。
この3つをまとめて使用すると、次のようになります。
1 2 3 4 |
.parent { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); } |
repeat()
を使用していますが、明示的な数値の代わりにauto-fit
キーワードを使用しています。これにより子要素の自動配置が可能になります。これらの子要素の基本的な最小値は150px
で、最大値は1fr
です。つまり、小さなスクリーンでは1fr
の幅いっぱいに表示され、それぞれの幅が150px
になると、同じラインに配置されます。
auto-fit
は完全に空のトラックは0
に折りたたまれ、いっぱいになったトラックはスペースを占めるように成長します。ただし、これをauto-fill
に変更すると、空のトラックは埋められた場合と同じ量のスペースを占めるようになります。
1 2 3 4 |
.parent { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); } |
参考:
メディアクエリなしのサイドバー
サイドバーに最小と最大のセーフエリアサイズが与えられ、残りのコンテンツが利用可能なスペースを埋めるレイアウトです。
1 2 3 4 5 6 7 8 9 |
<div class="parent"> <div class="section yellow" contenteditable> Min: 100px / Max: 25% </div> <div class="section blue" contenteditable> This element takes the second grid position (1fr), meaning it takes up the rest of the remaining space. </div> </div> |
1 2 3 4 |
.parent { display: grid; grid-template-columns: minmax(100px, 25%) 1fr; } |
このレイアウトでは、グリッドレイアウトにminmax()
関数を利用します。この関数を使用して、サイドバーの最小サイズを100px
に、最大サイズを25%
にしています。サイドバーは、その25%
が100px
より小さくなるまで、常に親の水平方向のスペースの25%
を占有します。
grid-template-columns
プロパティにminmax(100px, 25%) 1fr
という値を定義しています。1カラム目(ここではサイドバー)には100px
のminmax
を25%
に設定し、2カラム目(ここではメインセクション)は残りのスペースを1fr
のトラックとして占有します。
1 2 3 4 |
.parent { display: grid; grid-template-columns: minmax(100px, 25%) 1fr; } |
参考:
- しっかり理解しておくと便利なCSSのテクニック、minmax()関数の使い方
- CSSの関数はどんどん便利になっている!minmax()を使うとMedia Queries無しでレスポンシブが簡単に実装できる
- 5分で完璧に分かる!CSS Gridの基本的な使い方を解説
これからの中央配置
1行のCSSで、子のdivを中央に配置します。
1 2 3 4 5 |
<div class="parent"> <div class="box" contenteditable=""> :) </div> </div> |
1 2 3 4 5 6 7 |
.parent { display: grid; place-items: center; /* Just for parent demo size */ height: 100vh; } |
place-items: center;
で、要素を親要素の中央に配置します。
最初にdisplay: grid;
を定義してから、place-items: center;
を定義します。place-items
は、align-items
とjustify-items
の両方を一度に定義するための省略形です。center
とすることで、align-items
とjustify-items
の両方がcenter
に設定されます。
1 2 3 4 |
.parent { display: grid; place-items: center; } |
参考:
12spanのグリッド
12分割されたグリッドで、トラック上にエリアを均等に配置できます。
1 2 3 4 5 6 |
<div class="parent"> <div class="span-12 section coral">Span 12</div> <div class="span-6 section green">Span 6</div> <div class="span-4 section yellow">Span 4</div> <div class="span-2 section blue">Span 2</div> </div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
.parent { display: grid; grid-template-columns: repeat(12, 1fr); } .span-12 { grid-column: 1 / span 12; } .span-6 { grid-column: 1 / span 6; } .span-4 { grid-column: 4 / span 4; } .span-2 { grid-column: 3 / span 2; } /* centering text */ .section { display: grid; place-items: center; text-align: center } |
もうひとつの定番レイアウトが、12spanのグリッドです。CSSでは、repeat()
関数で簡単にグリッドを作成できます。repeat(12, 1fr);
を使用すると、グリッドのテンプレートの列に1fr
ずつの12カラムになります。
1 2 3 4 5 6 7 8 |
.parent { display: grid; grid-template-columns: repeat(12, 1fr); } .child-span-12 { grid-column: 1 / 13; } |
これで12カラムのグリッドができたので、グリッドに子要素を配置できます。1つの方法として、グリッドラインを使用して配置できます。
たとえば、grid-column: 1 / 13
とすれば、最初の行から最後(13番目)の行まで12カラムにまたがります。grid-column: 1 / 5;
とすれば、最初の4カラムにまたがります。
もう一つの書き方は、span
キーワードを使用することです。span
は開始点を設定し、その開始点から何列目に渡るかを設定します。grid-column: 1 / span 12;
はgrid-column: 1 / 13;
となり、grid-column: 2 / span 6;
はgrid-column: 2 / 8;
と同じになります。
1 2 3 |
.child-span-12 { grid-column: 1 / span 12; } |
sponsors