レスポンシブレイアウトは、デバイスのモデル一覧ではなく、コンテンツの制約から導き出す必要があります。Intrinsic sizing、flexbox、grid、メディアクエリ、コンテナクエリは、相互に補完する制御手段を提供します。
レスポンシブの基盤を確立する #
viewport 宣言によって、CSS viewport をデバイス幅に合わせます。
<meta name="viewport" content="width=device-width, initial-scale=1">user-scalable=no や小さい maximum-scale は追加しないでください。ページのズームはアクセシビリティ機構です。画面上のキーボードの周囲にコントロールを配置するアプリケーションでは、表示領域が変化してもレイアウト viewport が変化しない場合があるため、visual viewport API も必要になることがあります。
border-box sizing を使用し、置換コンテンツのサイズを制約します。
html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit;
}
img,
video,
svg {
display: block;
max-inline-size: 100%;
block-size: auto;
}これらのルールがないと、padding によってコンポーネントの幅が予期せず増加したり、intrinsic に幅の広いメディアが狭い viewport からはみ出したりします。
読み取り用コンテンツを中央揃えし、制約する #
読みやすいカラムには、最大行長と小さな画面向けのガターが必要です。
main {
inline-size: min(100%, 72ch);
margin-inline: auto;
padding-inline: 1rem;
}ch は 0 グリフの送り幅を近似します。本文の行長には便利ですが、サイズを選択した書体に依存させるべきでないコントロールには、rem とピクセルの方が適しています。グローバルな border-box 方針では、padding-inline によって制約された幅の内側に書字方向対応のガターが作られます。物理的な左・右プロパティを選択しなくても、左から右、右から左、縦書きのテキストに対応できます。
ページの主要コンテンツに固定の min-width を設定するのは避けてください。min-width: 480px のような宣言は、480 CSS ピクセル未満の viewport で水平スクロールを強制します。min()、max()、clamp() は、viewport に関する対応外の仮定を置かずに制約を維持します。
h1 {
font-size: clamp(2rem, 1.25rem + 3vw, 4rem);
}Flexbox と Grid を選択する #
Flexbox は、主軸に沿って一次元のシーケンスを配置します。Grid は行と列を定義し、両方の軸にまたがってアイテムを揃えられます。
.toolbar {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.75rem;
}flex-direction は主軸を決定します。justify-* プロパティは主軸に沿って空間を分配し、align-* プロパティは交差軸を制御します。align-items は子要素のデフォルトを設定し、align-self は個別のアイテムで上書きします。flex-flow は flex-direction と flex-wrap を組み合わせます。
Grid は、カラムを自動的に形成するカードに適しています。
.cards {
display: grid;
grid-template-columns: repeat(
auto-fit,
minmax(min(18rem, 100%), 1fr)
);
gap: 1rem;
}内側の min() によって、18 rem の最小幅が 18 rem 未満のコンテナからはみ出すのを防ぎます。auto-fit は空のトラックを折りたたむため、内容のあるカードが利用可能な幅を使えるようになります。
Grid のコンテンツを intrinsic minimum 未満まで縮小できるようにする必要がある場合は、minmax(0, 1fr) を使用します。
.sidebar-layout {
display: grid;
grid-template-columns: minmax(12rem, 1fr) minmax(0, 3fr);
}環境上の制約にメディアクエリを追加する #
メディアクエリは、コンテンツが収まらなくなったときにレイアウト変更を導入する必要があります。
@media (width < 48rem) {
.sidebar-layout {
grid-template-columns: 1fr;
}
}Range syntax を使うと、境界を直接表現できます。一般的なメディア特性には、width、height、orientation、aspect-ratio、resolution、prefers-color-scheme、prefers-reduced-motion、hover、pointer があります。
狭いタッチスクリーンと狭いデスクトップウィンドウでは、同一の操作方法を提供できないため、入力特性が重要になります。
@media (hover: hover) and (pointer: fine) {
.menu-item:hover {
text-decoration: underline;
}
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
scroll-behavior: auto;
animation-duration: 1ms;
animation-iteration-count: 1;
transition-duration: 1ms;
}
}デバイスがスマートフォンである証拠として hover: none を使用しないでください。ハイブリッドデバイスには複数の入力機構がある場合があります。
再利用可能なコンポーネントにコンテナクエリを使用する #
コンポーネントは、幅の広いページカラムにも狭いサイドバーにも表示される可能性があります。そのため、viewport よりもコンポーネント自身が利用できる空間の方が有用です。
.card-region {
container-type: inline-size;
}
.card {
display: grid;
gap: 1rem;
}
@container (width >= 32rem) {
.card {
grid-template-columns: 10rem minmax(0, 1fr);
}
}Containment は、クエリ対象コンポーネントの祖先に設定します。クエリによってスタイルを変更する同じ要素のサイズをクエリするのは避けてください。循環的な sizing logic が発生するためです。
コンテナクエリは、クエリコンテナ自体ではなく、その子孫をスタイルします。子アイテムは intrinsic minimum size によって、依然としてオーバーフローする可能性があります。そのため、コンテンツを縮小する必要がある場所では、min-inline-size: 0 または適切な wrapping policy を適用してください。
レイアウトの動作をテストする #
名前付きデバイスプリセットだけでなく、コンテンツが折り返し、オーバーフローし、または過剰な空白を残し始める幅でテストします。次の項目を含めてください。
- 長い単語、URL、翻訳テキスト、大きなフォント設定。
- キーボードフォーカスと、少なくとも 200 パーセントまでのズーム。
- モーション低減と両方のカラースキーム。
- 粗いポインターと細かいポインター。
- 空、最小、最大のデータセット。
レスポンシブレイアウトは、連続する幅の範囲全体で有効であり続けます。少数のスクリーンショット間を切り替えるものではありません。
コメント
GitHub で閲覧・コメントする(投稿にはログインが必要です)