TechHub

エンジニアの成長をサポートする技術情報サイト

← 記事一覧に戻る

モダンなCSSテクニックとは?Grid、Flexbox、CSS変数の実践的活用

公開日: 2024年4月9日 著者: mogura
モダンなCSSテクニックとは?Grid、Flexbox、CSS変数の実践的活用

疑問

モダンなCSSの機能を効果的に活用するには、どのようなテクニックがあるのでしょうか?Grid、Flexbox、CSS変数などの実践的な使い方を一緒に学んでいきましょう。

導入

CSSは年々進化を続けており、CSS Grid、Flexbox、CSS変数、コンテナクエリなど、強力な機能が追加されています。これらのモダンなCSS機能を活用することで、より効率的で保守しやすいスタイルシートを書くことができます。

本記事では、モダンなCSSの主要な機能と、実践的な使い方を詳しく解説します。レイアウト、アニメーション、レスポンシブデザインなど、様々な場面での活用方法を紹介していきます。

モダンなCSSのイメージ

解説

1. CSS Gridの活用

CSS Gridは、2次元のレイアウトを実装できる強力な機能です。行と列の両方を制御でき、複雑なレイアウトを簡単に実現できます。Flexboxが1次元(行または列)のレイアウトに適しているのに対し、Gridは2次元(行と列)のレイアウトに適しています。

基本的なGridレイアウト

CSS Gridの基本的な使い方:

グリッドコンテナの設定

.container {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: auto;
    gap: 1rem;
}


主なプロパティ
- display: grid: Gridを有効化
- grid-template-columns: 列の定義(repeat(3, 1fr)は3列を均等に)
- grid-template-rows: 行の定義
- gap: グリッドアイテム間の間隔(row-gapcolumn-gapのショートハンド)

グリッドアイテムの配置
- grid-column: 列の位置(span 2で2列分)
- grid-row: 行の位置
- grid-area: エリア名を指定

複雑なレイアウト

Gridを使用して複雑なレイアウトを実現する方法:

グリッドエリアの使用

.layout {
    display: grid;
    grid-template-areas:
        "header header header"
        "sidebar main main"
        "footer footer footer";
    grid-template-columns: 200px 1fr 1fr;
    grid-template-rows: auto 1fr auto;
}

.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.footer { grid-area: footer; }


自動配置の活用
- grid-auto-flow: row(デフォルト): 行方向に自動配置
- grid-auto-flow: column: 列方向に自動配置
- grid-auto-flow: dense: 隙間を埋めるように配置

minmax()の使用
.grid {
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}

これにより、最小幅300pxで自動的に列数が調整されます。

Gridの高度な機能

Gridの高度な機能:

サブグリッド(Subgrid)
親のグリッドラインを継承(ブラウザサポートが限定的)

.item {
    display: grid;
    grid-template-columns: subgrid;
}


グリッドラインの名前付け
.grid {
    grid-template-columns: [start] 1fr [middle] 1fr [end];
}
.item {
    grid-column: start / end;
}


アライメント
- justify-items: グリッドアイテムの水平方向の配置
- align-items: グリッドアイテムの垂直方向の配置
- place-items: 両方のショートハンド
- justify-content: グリッド全体の水平方向の配置
- align-content: グリッド全体の垂直方向の配置

CSS Gridの実装例

基本的なGridレイアウトから複雑なレイアウトまでの実装例です。

/* 基本的なGridレイアウト */
.grid-container {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 2rem;
    padding: 2rem;
}

.grid-item {
    background-color: #f0f0f0;
    padding: 1.5rem;
    border-radius: 8px;
}

/* レスポンシブなGrid */
.responsive-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 1.5rem;
}

/* 複雑なレイアウト(グリッドエリア) */
.layout {
    display: grid;
    grid-template-areas:
        "header header header"
        "sidebar main main"
        "footer footer footer";
    grid-template-columns: 250px 1fr 1fr;
    grid-template-rows: auto 1fr auto;
    gap: 1rem;
    min-height: 100vh;
}

.header {
    grid-area: header;
    background-color: #333;
    color: white;
    padding: 1rem;
}

.sidebar {
    grid-area: sidebar;
    background-color: #f5f5f5;
    padding: 1rem;
}

.main {
    grid-area: main;
    padding: 1rem;
}

.footer {
    grid-area: footer;
    background-color: #333;
    color: white;
    padding: 1rem;
}

/* モバイル対応 */
@media (max-width: 768px) {
    .layout {
        grid-template-areas:
            "header"
            "main"
            "sidebar"
            "footer";
        grid-template-columns: 1fr;
    }
}

/* ギャラリーレイアウト */
.gallery {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    gap: 1rem;
}

.gallery-item {
    aspect-ratio: 1 / 1;
    object-fit: cover;
}

/* カードレイアウト */
.card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 2rem;
}

.card {
    display: flex;
    flex-direction: column;
    background: white;
    border-radius: 8px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
    overflow: hidden;
}

.card-header {
    padding: 1.5rem;
    background-color: #f8f9fa;
}

.card-body {
    padding: 1.5rem;
    flex-grow: 1;
}

.card-footer {
    padding: 1rem 1.5rem;
    background-color: #f8f9fa;
    border-top: 1px solid #e9ecef;
}

2. Flexboxの活用

Flexboxは、1次元のレイアウト(行または列)を効率的に配置するためのCSSレイアウトモジュールです。要素を柔軟に配置でき、画面サイズに応じて自動的に調整されます。ナビゲーションバー、カードレイアウト、中央揃えなど、様々な場面で活用できます。

基本的なFlexbox

Flexboxの基本的な使い方:

Flexコンテナの設定

.container {
    display: flex;
    flex-direction: row; /* row, column, row-reverse, column-reverse */
    justify-content: center; /* 主軸方向の配置 */
    align-items: center; /* 交差軸方向の配置 */
    gap: 1rem;
}


主なプロパティ
- display: flex: Flexboxを有効化
- flex-direction: 主軸の方向
- justify-content: 主軸方向の配置(flex-start, center, flex-end, space-between, space-around, space-evenly)
- align-items: 交差軸方向の配置(flex-start, center, flex-end, stretch, baseline)
- flex-wrap: 折り返し(nowrap, wrap, wrap-reverse)

Flexアイテムのプロパティ
- flex: 伸縮の比率(flex-grow flex-shrink flex-basisのショートハンド)
- flex-grow: 伸びる比率(デフォルト: 0)
- flex-shrink: 縮む比率(デフォルト: 1)
- flex-basis: 基準となるサイズ(デフォルト: auto)

実践的なFlexboxパターン

Flexboxを使用した実践的なパターン:

中央揃え

.center {
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
}


ナビゲーションバー
.navbar {
    display: flex;
    justify-content: space-between;
    align-items: center;
}


カードレイアウト
.card-container {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
}

.card {
    flex: 1 1 300px; /* 最小幅300px、伸縮可能 */
}


フッターを下部に固定
body {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

.main {
    flex: 1;
}

Flexboxの高度な機能

Flexboxの高度な機能:

align-self
個別のアイテムの配置を制御

.item {
    align-self: flex-end;
}


order
アイテムの表示順序を変更(視覚的な順序のみ)
.item-1 { order: 2; }
.item-2 { order: 1; }


flex-basisの活用
.item {
    flex: 0 0 200px; /* 固定幅200px */
}


gapプロパティ
アイテム間の間隔を簡単に設定(row-gapcolumn-gapのショートハンド)

Flexboxの実装例

基本的なFlexboxから実践的なパターンまでの実装例です。

/* 基本的なFlexbox */
.flex-container {
    display: flex;
    gap: 1rem;
    padding: 1rem;
}

.flex-item {
    flex: 1;
    padding: 1rem;
    background-color: #f0f0f0;
}

/* 中央揃え */
.center {
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
}

/* ナビゲーションバー */
.navbar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 1rem 2rem;
    background-color: #333;
    color: white;
}

.nav-links {
    display: flex;
    gap: 2rem;
    list-style: none;
}

/* カードレイアウト */
.card-container {
    display: flex;
    flex-wrap: wrap;
    gap: 2rem;
    padding: 2rem;
}

.card {
    flex: 1 1 300px;
    max-width: 400px;
    display: flex;
    flex-direction: column;
    background: white;
    border-radius: 8px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.card-header {
    padding: 1.5rem;
    border-bottom: 1px solid #e9ecef;
}

.card-body {
    padding: 1.5rem;
    flex-grow: 1;
}

.card-footer {
    padding: 1rem 1.5rem;
    border-top: 1px solid #e9ecef;
    margin-top: auto;
}

/* フッターを下部に固定 */
body {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

.main-content {
    flex: 1;
}

/* ボタングループ */
.button-group {
    display: flex;
    gap: 1rem;
    flex-wrap: wrap;
}

.button {
    flex: 1 1 auto;
    min-width: 120px;
    padding: 0.75rem 1.5rem;
}

/* モバイル対応 */
@media (max-width: 768px) {
    .navbar {
        flex-direction: column;
        gap: 1rem;
    }
    
    .nav-links {
        flex-direction: column;
        width: 100%;
    }
    
    .card {
        flex: 1 1 100%;
    }
}

3. CSS変数(カスタムプロパティ)

CSS変数(カスタムプロパティ)は、値を再利用し、テーマの切り替えや動的な値の計算を可能にする強力な機能です。--で始まる名前で定義し、var()関数で参照します。JavaScriptからも動的に変更できるため、テーマの切り替えなどに最適です。

基本的な使い方

CSS変数の基本的な使い方:

変数の定義

:root {
    --primary-color: #0066cc;
    --secondary-color: #666666;
    --spacing: 1rem;
    --font-size-base: 16px;
}


変数の使用
.button {
    background-color: var(--primary-color);
    padding: var(--spacing);
    font-size: var(--font-size-base);
}


フォールバック値
.color {
    color: var(--text-color, #333333);
}

変数が定義されていない場合、#333333が使用されます。

スコープ
変数は定義された要素とその子要素で使用可能です。:rootで定義すると、すべての要素で使用できます。

テーマの切り替え

CSS変数を使用してテーマを切り替える方法:

テーマの定義

:root {
    --bg-color: #ffffff;
    --text-color: #333333;
}

[data-theme="dark"] {
    --bg-color: #1a1a1a;
    --text-color: #ffffff;
}


JavaScriptでの切り替え
document.documentElement.setAttribute('data-theme', 'dark');


複数のテーマ
[data-theme="light"] { /* ライトテーマ */ }
[data-theme="dark"] { /* ダークテーマ */ }
[data-theme="high-contrast"] { /* ハイコントラストテーマ */ }

動的な値の計算

CSS変数を使用して動的な値を計算する方法:

calc()との組み合わせ

:root {
    --base-size: 16px;
    --spacing: 1rem;
}

.element {
    font-size: calc(var(--base-size) * 1.5);
    margin: calc(var(--spacing) * 2);
}


JavaScriptからの変更
document.documentElement.style.setProperty('--spacing', '2rem');


条件付き値
.element {
    --size: var(--small-size, 100px);
}

@media (min-width: 768px) {
    .element {
        --size: var(--large-size, 200px);
    }
}

CSS変数の実装例

CSS変数の基本的な使い方からテーマ切り替えまでの実装例です。

/* CSS変数の定義 */
:root {
    /* カラー */
    --primary-color: #0066cc;
    --secondary-color: #666666;
    --success-color: #28a745;
    --error-color: #dc3545;
    --warning-color: #ffc107;
    
    /* スペーシング */
    --spacing-xs: 0.25rem;
    --spacing-sm: 0.5rem;
    --spacing-md: 1rem;
    --spacing-lg: 1.5rem;
    --spacing-xl: 2rem;
    
    /* タイポグラフィ */
    --font-size-base: 16px;
    --font-size-sm: 0.875rem;
    --font-size-lg: 1.25rem;
    --font-size-xl: 1.5rem;
    
    /* その他 */
    --border-radius: 4px;
    --box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

/* ダークテーマ */
[data-theme="dark"] {
    --primary-color: #4da6ff;
    --bg-color: #1a1a1a;
    --text-color: #ffffff;
    --border-color: #333333;
}

/* ライトテーマ */
[data-theme="light"] {
    --bg-color: #ffffff;
    --text-color: #333333;
    --border-color: #e0e0e0;
}

/* 変数の使用 */
body {
    background-color: var(--bg-color, #ffffff);
    color: var(--text-color, #333333);
    font-size: var(--font-size-base);
}

.button {
    background-color: var(--primary-color);
    color: white;
    padding: var(--spacing-sm) var(--spacing-md);
    border-radius: var(--border-radius);
    box-shadow: var(--box-shadow);
    border: none;
    cursor: pointer;
    transition: background-color 0.3s ease;
}

.button:hover {
    background-color: color-mix(in srgb, var(--primary-color) 80%, black);
}

.card {
    background-color: var(--bg-color);
    border: 1px solid var(--border-color);
    border-radius: var(--border-radius);
    padding: var(--spacing-lg);
    box-shadow: var(--box-shadow);
}

/* calc()との組み合わせ */
.container {
    --container-width: 1200px;
    --container-padding: var(--spacing-md);
    max-width: var(--container-width);
    padding: var(--container-padding);
    margin: 0 auto;
}

.content {
    width: calc(100% - var(--container-padding) * 2);
}

/* JavaScriptでのテーマ切り替え */
/*
const themeToggle = document.getElementById('theme-toggle');
const currentTheme = localStorage.getItem('theme') || 'light';

document.documentElement.setAttribute('data-theme', currentTheme);

themeToggle.addEventListener('click', () => {
    const newTheme = currentTheme === 'light' ? 'dark' : 'light';
    document.documentElement.setAttribute('data-theme', newTheme);
    localStorage.setItem('theme', newTheme);
});

// 動的な値の変更
function updateSpacing(value) {
    document.documentElement.style.setProperty('--spacing-md', value);
}
*/

4. コンテナクエリ

コンテナクエリは、親要素のサイズに応じてスタイルを適用できる機能です。メディアクエリがビューポートのサイズに基づくのに対し、コンテナクエリは親要素のサイズに基づきます。これにより、コンポーネントベースのレスポンシブデザインが可能になります。

基本的な使い方

コンテナクエリの基本的な使い方:

コンテナの定義

.card-container {
    container-type: inline-size;
    /* または */
    container: card-container / inline-size;
}


コンテナクエリの使用
@container (min-width: 400px) {
    .card {
        display: flex;
        flex-direction: row;
    }
}


コンテナ名の指定
.sidebar {
    container-name: sidebar;
    container-type: inline-size;
}

@container sidebar (min-width: 300px) {
    .widget {
        /* スタイル */
    }
}


container-typeの値
- inline-size: インライン方向のサイズ(幅)
- block-size: ブロック方向のサイズ(高さ)
- size: 両方向のサイズ

コンテナクエリの単位

コンテナクエリの単位(cqw、cqh、cqi、cqb、cqmin、cqmax)を使用できます。

単位の説明
- cqw: コンテナの幅の1%
- cqh: コンテナの高さの1%
- cqi: インライン方向のサイズの1%
- cqb: ブロック方向のサイズの1%
- cqmin: cqiとcqbの小さい方
- cqmax: cqiとcqbの大きい方

使用例

.card {
    font-size: clamp(1rem, 2cqw, 1.5rem);
    padding: 2cqw;
}


注意点
- コンテナクエリは比較的新しい機能で、ブラウザサポートが限定的
- フォールバックとしてメディアクエリも併用することを推奨

コンテナクエリの実装例

コンテナクエリを使用した実装例です。

/* コンテナの定義 */
.card-container {
    container-type: inline-size;
    container-name: card-container;
}

/* コンテナクエリの使用 */
.card {
    display: flex;
    flex-direction: column;
    padding: 1rem;
    background: white;
    border-radius: 8px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

/* コンテナが400px以上の場合は横並び */
@container card-container (min-width: 400px) {
    .card {
        flex-direction: row;
        gap: 1.5rem;
    }
    
    .card-image {
        flex: 0 0 200px;
    }
    
    .card-content {
        flex: 1;
    }
}

/* コンテナが600px以上の場合はさらに調整 */
@container card-container (min-width: 600px) {
    .card {
        padding: 2rem;
    }
    
    .card-title {
        font-size: 1.5rem;
    }
}

/* コンテナクエリの単位を使用 */
.responsive-text {
    font-size: clamp(1rem, 3cqw, 1.5rem);
    padding: 2cqw;
}

/* サイドバーのコンテナクエリ */
.sidebar {
    container-type: inline-size;
    container-name: sidebar;
}

.widget {
    padding: 1rem;
}

@container sidebar (min-width: 300px) {
    .widget {
        padding: 1.5rem;
        display: grid;
        grid-template-columns: repeat(2, 1fr);
        gap: 1rem;
    }
}

/* フォールバック(メディアクエリ) */
@media (min-width: 768px) {
    .card {
        flex-direction: row;
    }
}

/* コンテナクエリとメディアクエリの組み合わせ */
@container (min-width: 400px) and (max-width: 800px) {
    .card {
        /* スタイル */
    }
}

@media (min-width: 1024px) {
    @container (min-width: 500px) {
        .card {
            /* スタイル */
        }
    }
}

5. モダンなセレクタ

モダンなCSSセレクタ(:is()、:where()、:has()、:focus-visibleなど)は、より柔軟で強力なスタイリングを可能にします。これらのセレクタを使用することで、コードを簡潔にし、保守性を向上させることができます。

:is()と:where()

:is():where()は、複数のセレクタをグループ化する疑似クラスです。

:is()の使用

/* 従来の書き方 */
h1, h2, h3, h4, h5, h6 {
    color: #333;
}

/* :is()を使用 */
:is(h1, h2, h3, h4, h5, h6) {
    color: #333;
}


:where()の使用
:where():is()と同じですが、詳細度が0です。
:where(h1, h2, h3) {
    color: #333;
}


実用例
/* ネストされた要素のスタイリング */
article :is(h1, h2, h3) {
    margin-top: 2rem;
}

/* フォーム要素のスタイリング */
:where(input, textarea, select):focus {
    outline: 2px solid blue;
}

:has()セレクタ

:has()は、「親セレクタ」として機能し、特定の子要素を持つ親要素を選択できます。

基本的な使い方

/* 画像を含むカード */
.card:has(img) {
    display: flex;
    flex-direction: column;
}

/* エラーを含むフォーム */
.form:has(.error) {
    border-color: red;
}

/* リンクを含む段落 */
p:has(a) {
    font-weight: bold;
}


実用例
/* 子要素の状態に基づくスタイリング */
.nav:has(.active) {
    background-color: #f0f0f0;
}

/* 条件付きレイアウト */
.container:has(.sidebar) {
    display: grid;
    grid-template-columns: 250px 1fr;
}

:focus-visible

:focus-visibleは、キーボード操作でフォーカスされた場合のみスタイルを適用します。

使い方

/* すべてのフォーカス */
button:focus {
    outline: 2px solid blue;
}

/* キーボード操作でのみ */
button:focus-visible {
    outline: 2px solid blue;
    outline-offset: 2px;
}

/* マウスクリックでは非表示 */
button:focus:not(:focus-visible) {
    outline: none;
}


メリット
- キーボードユーザーにはフォーカスインジケーターを表示
- マウスユーザーには不要なアウトラインを非表示
- アクセシビリティとUXの両立

モダンなセレクタの実装例

モダンなセレクタを使用した実装例です。

/* :is()の使用例 */
/* 見出しのスタイリング */
:is(h1, h2, h3, h4, h5, h6) {
    color: #333;
    font-weight: bold;
    margin-top: 2rem;
    margin-bottom: 1rem;
}

/* ネストされた要素 */
article :is(h1, h2, h3) {
    border-bottom: 2px solid #0066cc;
    padding-bottom: 0.5rem;
}

/* :where()の使用例(詳細度0) */
:where(header, footer, nav) a {
    color: #0066cc;
    text-decoration: none;
}

/* 上書き可能 */
header a {
    color: white; /* これは適用される */
}

/* :has()の使用例 */
/* 画像を含むカード */
.card:has(img) {
    display: flex;
    flex-direction: column;
}

.card:has(img) .card-image {
    order: -1;
    margin-bottom: 1rem;
}

/* エラーを含むフォーム */
.form-group:has(.error) {
    border-left: 4px solid #dc3545;
    padding-left: 1rem;
}

.form-group:has(.error) label {
    color: #dc3545;
}

/* アクティブな項目を含むナビゲーション */
.nav:has(.active) {
    background-color: #f8f9fa;
}

/* サイドバーを含むレイアウト */
.layout:has(.sidebar) {
    display: grid;
    grid-template-columns: 250px 1fr;
    gap: 2rem;
}

/* :focus-visibleの使用例 */
.button {
    padding: 0.75rem 1.5rem;
    background-color: #0066cc;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}

/* キーボード操作でのみフォーカスインジケーターを表示 */
.button:focus-visible {
    outline: 3px solid #0066cc;
    outline-offset: 2px;
}

/* マウスクリックでは非表示 */
.button:focus:not(:focus-visible) {
    outline: none;
}

/* リンクのスタイリング */
a:focus-visible {
    outline: 2px solid #0066cc;
    outline-offset: 2px;
    border-radius: 2px;
}

/* フォーム要素 */
input:focus-visible,
textarea:focus-visible,
select:focus-visible {
    outline: 2px solid #0066cc;
    outline-offset: 2px;
    border-color: #0066cc;
}

6. アニメーションとトランジション

CSSアニメーションとトランジションは、ユーザー体験を向上させる重要な要素です。適切に使用することで、スムーズで自然な動きを実現できます。prefers-reduced-motionを尊重し、アクセシビリティも考慮することが重要です。

CSSアニメーション

CSSアニメーションの基本的な使い方:

アニメーションの定義

@keyframes fadeIn {
    from {
        opacity: 0;
        transform: translateY(20px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.element {
    animation: fadeIn 0.5s ease-in-out;
}


アニメーションプロパティ
- animation-name: アニメーション名
- animation-duration: 継続時間
- animation-timing-function: タイミング関数(ease, linear, ease-in-outなど)
- animation-delay: 遅延時間
- animation-iteration-count: 繰り返し回数(infiniteで無限)
- animation-direction: 方向(normal, reverse, alternateなど)
- animation-fill-mode: 開始前・終了後の状態(forwards, backwards, both)

トランジション

トランジションは、プロパティの変化をスムーズにアニメーション化します。

基本的な使い方

.button {
    background-color: #0066cc;
    transition: background-color 0.3s ease;
}

.button:hover {
    background-color: #0052a3;
}


複数のプロパティ
.card {
    transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.card:hover {
    transform: translateY(-4px);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}


トランジションプロパティ
- transition-property: トランジションを適用するプロパティ(allで全て)
- transition-duration: 継続時間
- transition-timing-function: タイミング関数
- transition-delay: 遅延時間

スクロール連動アニメーション

スクロール連動アニメーションは、scroll-timelineanimation-timelineを使用して実現できます(実験的機能)。

実装方法

@keyframes scroll-animation {
    from {
        opacity: 0;
        transform: translateY(50px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.element {
    animation: scroll-animation linear;
    animation-timeline: scroll();
}


注意点
- ブラウザサポートが限定的
- JavaScriptとの組み合わせも検討
- Intersection Observer APIを使用した実装も一般的

アニメーションとトランジションの実装例

CSSアニメーションとトランジションの実装例です。

/* フェードインアニメーション */
@keyframes fadeIn {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}

.fade-in {
    animation: fadeIn 0.5s ease-in-out;
}

/* スライドインアニメーション */
@keyframes slideInFromLeft {
    from {
        transform: translateX(-100%);
        opacity: 0;
    }
    to {
        transform: translateX(0);
        opacity: 1;
    }
}

.slide-in {
    animation: slideInFromLeft 0.5s ease-out;
}

/* ボタンのトランジション */
.button {
    background-color: #0066cc;
    color: white;
    padding: 0.75rem 1.5rem;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    transition: background-color 0.3s ease, transform 0.2s ease, box-shadow 0.3s ease;
}

.button:hover {
    background-color: #0052a3;
    transform: translateY(-2px);
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}

.button:active {
    transform: translateY(0);
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

/* カードのホバーエフェクト */
.card {
    background: white;
    border-radius: 8px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
    transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.card:hover {
    transform: translateY(-4px) scale(1.02);
    box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15);
}

/* ローディングアニメーション */
@keyframes spin {
    from {
        transform: rotate(0deg);
    }
    to {
        transform: rotate(360deg);
    }
}

.loader {
    border: 4px solid #f3f3f3;
    border-top: 4px solid #0066cc;
    border-radius: 50%;
    width: 40px;
    height: 40px;
    animation: spin 1s linear infinite;
}

/* パルスアニメーション */
@keyframes pulse {
    0%, 100% {
        opacity: 1;
    }
    50% {
        opacity: 0.5;
    }
}

.pulse {
    animation: pulse 2s ease-in-out infinite;
}

/* モーションを減らす設定を尊重 */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
}

/* 遅延アニメーション */
.stagger-item {
    opacity: 0;
    animation: fadeIn 0.5s ease-in-out forwards;
}

.stagger-item:nth-child(1) { animation-delay: 0.1s; }
.stagger-item:nth-child(2) { animation-delay: 0.2s; }
.stagger-item:nth-child(3) { animation-delay: 0.3s; }
.stagger-item:nth-child(4) { animation-delay: 0.4s; }

7. レスポンシブデザインのテクニック

モダンなCSS関数(clamp()、min()、max()、aspect-ratioなど)を使用して、より柔軟なレスポンシブデザインを実現できます。これらの関数により、メディアクエリを減らし、より流動的なレイアウトを作成できます。

クランプ関数

clamp()関数は、値を最小値と最大値の間でクランプ(制限)します。

基本的な使い方

font-size: clamp(1rem, 4vw, 2rem);
/* 最小1rem、推奨4vw、最大2rem */


実用例
/* レスポンシブなフォントサイズ */
.title {
    font-size: clamp(1.5rem, 5vw, 3rem);
}

/* レスポンシブなパディング */
.container {
    padding: clamp(1rem, 5vw, 3rem);
}

/* レスポンシブな幅 */
.content {
    width: clamp(300px, 90vw, 1200px);
}

aspect-ratio

aspect-ratioプロパティは、要素のアスペクト比を設定します。

基本的な使い方

.image {
    aspect-ratio: 16 / 9;
    width: 100%;
    height: auto;
}


実用例
/* 正方形の画像 */
.thumbnail {
    aspect-ratio: 1 / 1;
    object-fit: cover;
}

/* カードのアスペクト比 */
.card {
    aspect-ratio: 4 / 3;
}

/* 動画コンテナ */
.video-container {
    aspect-ratio: 16 / 9;
}

min()、max()、clamp()

min()max()clamp()関数の使い分け:

min()の使用
複数の値の最小値を選択

width: min(100%, 1200px);
/* 100%と1200pxの小さい方 */


max()の使用
複数の値の最大値を選択
padding: max(1rem, 5vw);
/* 1remと5vwの大きい方 */


clamp()の使用
値を最小値と最大値の間でクランプ
font-size: clamp(1rem, 4vw, 2rem);
/* 1rem以上2rem以下、推奨4vw */


実用例
/* コンテナの最大幅 */
.container {
    width: min(100% - 2rem, 1200px);
    margin: 0 auto;
}

/* 最小パディング */
.section {
    padding: max(2rem, 5vw);
}

レスポンシブデザインのテクニック実装例

clamp()、aspect-ratio、min()、max()を使用した実装例です。

/* clamp()の使用例 */
/* レスポンシブなフォントサイズ */
.title {
    font-size: clamp(1.5rem, 5vw, 3rem);
    line-height: 1.2;
}

.subtitle {
    font-size: clamp(1rem, 3vw, 1.5rem);
}

/* レスポンシブなパディング */
.container {
    padding: clamp(1rem, 5vw, 3rem);
}

.section {
    padding-top: clamp(2rem, 8vw, 6rem);
    padding-bottom: clamp(2rem, 8vw, 6rem);
}

/* レスポンシブな幅 */
.content {
    width: clamp(300px, 90vw, 1200px);
    margin: 0 auto;
}

/* aspect-ratioの使用例 */
/* 画像のアスペクト比 */
.image {
    width: 100%;
    aspect-ratio: 16 / 9;
    object-fit: cover;
}

.thumbnail {
    aspect-ratio: 1 / 1;
    object-fit: cover;
}

/* カードのアスペクト比 */
.card {
    aspect-ratio: 4 / 3;
    background: white;
    border-radius: 8px;
    overflow: hidden;
}

/* 動画コンテナ */
.video-container {
    position: relative;
    width: 100%;
    aspect-ratio: 16 / 9;
    background: #000;
}

.video-container iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}

/* min()とmax()の使用例 */
/* コンテナの最大幅 */
.container {
    width: min(100% - 2rem, 1200px);
    margin: 0 auto;
}

/* 最小パディング */
.section {
    padding: max(2rem, 5vw);
}

/* レスポンシブなギャップ */
.grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: max(1rem, 3vw);
}

/* 複数の値の組み合わせ */
.responsive-text {
    font-size: clamp(
        max(1rem, 2vw),
        4vw,
        min(2rem, 5vw)
    );
}

/* 実践的な例:カードレイアウト */
.card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(clamp(250px, 30vw, 400px), 1fr));
    gap: clamp(1rem, 3vw, 2rem);
    padding: clamp(1rem, 5vw, 3rem);
}

.card {
    aspect-ratio: 4 / 3;
    background: white;
    border-radius: clamp(4px, 1vw, 8px);
    padding: clamp(1rem, 3vw, 2rem);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.card-title {
    font-size: clamp(1.25rem, 3vw, 1.5rem);
    margin-bottom: clamp(0.5rem, 2vw, 1rem);
}

8. レイアウトのベストプラクティス

GridとFlexboxの適切な使い分けと、モダンなリセットCSSの使用により、効率的で保守しやすいレイアウトを実現できます。各機能の特徴を理解し、適切な場面で適切な機能を選択することが重要です。

GridとFlexboxの使い分け

GridとFlexboxの使い分けのガイドライン:

Gridを使用する場合
- 2次元レイアウト(行と列の両方を制御)
- 複雑なレイアウト(ヘッダー、サイドバー、メイン、フッターなど)
- アイテムの位置を正確に制御したい場合
- グリッドベースのデザイン

Flexboxを使用する場合
- 1次元レイアウト(行または列のいずれか)
- ナビゲーションバー、ボタングループ
- アイテムの均等な配置
- 中央揃え

組み合わせて使用
GridとFlexboxは競合するものではなく、組み合わせて使用できます。

.layout {
    display: grid;
    grid-template-columns: 250px 1fr;
}

.navbar {
    display: flex;
    justify-content: space-between;
}

モダンなリセットCSS

モダンなリセットCSSの例:

基本的なリセット

*, *::before, *::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

html {
    -webkit-text-size-adjust: 100%;
}

body {
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
}

img, picture, video, canvas, svg {
    display: block;
    max-width: 100%;
}

input, button, textarea, select {
    font: inherit;
}

p, h1, h2, h3, h4, h5, h6 {
    overflow-wrap: break-word;
}


注意点
- box-sizing: border-boxを設定
- 画像のmax-width: 100%を設定
- フォントの継承を設定

レイアウトのベストプラクティス実装例

GridとFlexboxの使い分けと、モダンなリセットCSSの実装例です。

/* モダンなリセットCSS */
*, *::before, *::after {
    box-sizing: border-box;
}

* {
    margin: 0;
    padding: 0;
}

html {
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
}

body {
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

img, picture, video, canvas, svg {
    display: block;
    max-width: 100%;
    height: auto;
}

input, button, textarea, select {
    font: inherit;
}

p, h1, h2, h3, h4, h5, h6 {
    overflow-wrap: break-word;
}

#root, #__next {
    isolation: isolate;
}

/* GridとFlexboxの組み合わせ例 */
/* ページレイアウト(Grid) */
.page-layout {
    display: grid;
    grid-template-areas:
        "header header"
        "sidebar main"
        "footer footer";
    grid-template-columns: 250px 1fr;
    grid-template-rows: auto 1fr auto;
    min-height: 100vh;
}

.header {
    grid-area: header;
    /* ナビゲーションバー(Flexbox) */
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 1rem 2rem;
}

.sidebar {
    grid-area: sidebar;
    /* サイドバーメニュー(Flexbox) */
    display: flex;
    flex-direction: column;
    gap: 1rem;
    padding: 1rem;
}

.main {
    grid-area: main;
    padding: 2rem;
}

.footer {
    grid-area: footer;
    /* フッターリンク(Flexbox) */
    display: flex;
    justify-content: center;
    gap: 2rem;
    padding: 1rem;
}

/* カードグリッド(Grid) */
.card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 2rem;
}

/* カード内のレイアウト(Flexbox) */
.card {
    display: flex;
    flex-direction: column;
    background: white;
    border-radius: 8px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
    overflow: hidden;
}

.card-header {
    padding: 1.5rem;
}

.card-body {
    padding: 1.5rem;
    flex-grow: 1;
}

.card-footer {
    padding: 1rem 1.5rem;
    margin-top: auto;
    display: flex;
    justify-content: space-between;
    align-items: center;
}

9. パフォーマンス最適化

CSSのパフォーマンス最適化には、will-change、content-visibility、containプロパティなどの機能を適切に使用することが重要です。これらの機能により、レンダリングパフォーマンスを向上させ、ユーザー体験を改善できます。

will-change

will-changeプロパティは、要素が変更されることをブラウザに事前に通知します。

使い方

.animated-element {
    will-change: transform;
    transition: transform 0.3s ease;
}

.animated-element:hover {
    transform: translateY(-10px);
}


注意点
- 必要な要素にのみ使用(過度な使用は逆効果)
- アニメーション終了後に削除することを推奨
- JavaScriptで動的に設定・削除

推奨される使用例
// アニメーション開始前に設定
element.style.willChange = 'transform';

// アニメーション終了後に削除
element.addEventListener('transitionend', () => {
    element.style.willChange = 'auto';
});

content-visibility

content-visibilityプロパティは、ビューポート外のコンテンツのレンダリングをスキップします。

使い方

.long-content {
    content-visibility: auto;
}

.section {
    content-visibility: auto;
    contain-intrinsic-size: 0 500px;
}


値の説明
- visible: デフォルト(通常のレンダリング)
- hidden: コンテンツをスキップ(display: noneに近い)
- auto: ビューポート外のコンテンツをスキップ

contain-intrinsic-size
スキップされたコンテンツの推定サイズを指定
.section {
    content-visibility: auto;
    contain-intrinsic-size: 0 500px; /* 幅0、高さ500px */
}

containプロパティ

containプロパティは、要素のレンダリングを分離し、パフォーマンスを向上させます。

使い方

.widget {
    contain: layout style paint;
}

.isolated-component {
    contain: strict; /* layout style paint size のショートハンド */
}


値の説明
- layout: レイアウトの分離
- style: スタイルの分離
- paint: ペイントの分離
- size: サイズの分離
- strict: すべての分離(layout style paint
- content: レイアウトとペイントの分離

使用例
.card {
    contain: layout style paint;
}

.modal {
    contain: strict;
}

パフォーマンス最適化の実装例

will-change、content-visibility、containプロパティを使用した実装例です。

/* will-changeの使用例 */
/* アニメーション要素 */
.animated-card {
    transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.animated-card:hover {
    will-change: transform;
    transform: translateY(-10px);
    box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15);
}

/* JavaScriptでの動的な設定 */
/*
const card = document.querySelector('.animated-card');

card.addEventListener('mouseenter', () => {
    card.style.willChange = 'transform';
});

card.addEventListener('transitionend', () => {
    card.style.willChange = 'auto';
});
*/

/* content-visibilityの使用例 */
/* 長いリスト */
.long-list {
    content-visibility: auto;
}

.list-item {
    contain-intrinsic-size: 0 50px;
    content-visibility: auto;
}

/* セクション */
.section {
    content-visibility: auto;
    contain-intrinsic-size: 0 500px;
    padding: 2rem;
}

/* モーダル(非表示時) */
.modal.hidden {
    content-visibility: hidden;
}

/* containプロパティの使用例 */
/* カードコンポーネント */
.card {
    contain: layout style paint;
    background: white;
    border-radius: 8px;
    padding: 1.5rem;
}

/* ウィジェット */
.widget {
    contain: layout style paint;
    isolation: isolate;
}

/* モーダル */
.modal {
    contain: strict;
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

/* リストアイテム */
.list-item {
    contain: layout style;
    padding: 1rem;
    border-bottom: 1px solid #e0e0e0;
}

/* 組み合わせた最適化 */
.optimized-component {
    /* レンダリングの分離 */
    contain: layout style paint;
    
    /* ビューポート外のコンテンツをスキップ */
    content-visibility: auto;
    contain-intrinsic-size: 0 200px;
    
    /* アニメーションの最適化 */
    will-change: transform;
    transition: transform 0.3s ease;
}

.optimized-component:hover {
    transform: scale(1.05);
}

10. 実践的な例:カードコンポーネント

これまで学んだモダンなCSSテクニックを組み合わせた、実践的なカードコンポーネントの実装例です。Grid、Flexbox、CSS変数、アニメーション、レスポンシブデザインなどを統合した完全な例を示します。

モダンなCSSを使用したカードコンポーネント

Grid、Flexbox、CSS変数、アニメーションなどを組み合わせたカードコンポーネントの完全な実装例です。

/* CSS変数の定義 */
:root {
    --card-bg: #ffffff;
    --card-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
    --card-shadow-hover: 0 8px 16px rgba(0, 0, 0, 0.15);
    --card-border-radius: clamp(4px, 1vw, 8px);
    --card-padding: clamp(1rem, 3vw, 2rem);
    --card-gap: clamp(1rem, 3vw, 2rem);
    --primary-color: #0066cc;
    --text-color: #333333;
    --text-color-light: #666666;
}

[data-theme="dark"] {
    --card-bg: #1a1a1a;
    --text-color: #ffffff;
    --text-color-light: #cccccc;
    --card-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
    --card-shadow-hover: 0 8px 16px rgba(0, 0, 0, 0.5);
}

/* カードグリッド(Grid) */
.card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(clamp(300px, 30vw, 400px), 1fr));
    gap: var(--card-gap);
    padding: var(--card-padding);
    container-type: inline-size;
}

/* カードコンポーネント(Flexbox) */
.card {
    display: flex;
    flex-direction: column;
    background: var(--card-bg);
    border-radius: var(--card-border-radius);
    box-shadow: var(--card-shadow);
    overflow: hidden;
    transition: transform 0.3s ease, box-shadow 0.3s ease;
    contain: layout style paint;
    aspect-ratio: 4 / 3;
}

.card:hover {
    transform: translateY(-4px) scale(1.02);
    box-shadow: var(--card-shadow-hover);
    will-change: transform;
}

.card:focus-visible {
    outline: 3px solid var(--primary-color);
    outline-offset: 2px;
}

/* カード画像 */
.card-image {
    width: 100%;
    aspect-ratio: 16 / 9;
    object-fit: cover;
    order: -1;
}

/* カードヘッダー */
.card-header {
    padding: var(--card-padding);
    padding-bottom: 0;
}

.card-title {
    font-size: clamp(1.25rem, 3vw, 1.5rem);
    color: var(--text-color);
    margin-bottom: clamp(0.5rem, 2vw, 1rem);
    line-height: 1.3;
}

/* カード本文 */
.card-body {
    padding: var(--card-padding);
    flex-grow: 1;
    color: var(--text-color-light);
    font-size: clamp(0.875rem, 2vw, 1rem);
    line-height: 1.6;
}

/* カードフッター(Flexbox) */
.card-footer {
    padding: var(--card-padding);
    padding-top: 0;
    margin-top: auto;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1rem;
}

.card-button {
    padding: clamp(0.5rem, 2vw, 0.75rem) clamp(1rem, 3vw, 1.5rem);
    background-color: var(--primary-color);
    color: white;
    border: none;
    border-radius: clamp(4px, 1vw, 6px);
    cursor: pointer;
    font-size: clamp(0.875rem, 2vw, 1rem);
    transition: background-color 0.3s ease, transform 0.2s ease;
}

.card-button:hover {
    background-color: color-mix(in srgb, var(--primary-color) 80%, black);
    transform: translateY(-2px);
}

.card-button:focus-visible {
    outline: 2px solid var(--primary-color);
    outline-offset: 2px;
}

/* コンテナクエリでの調整 */
@container (min-width: 400px) {
    .card {
        aspect-ratio: 3 / 2;
    }
}

@container (min-width: 600px) {
    .card {
        flex-direction: row;
        aspect-ratio: auto;
    }
    
    .card-image {
        flex: 0 0 40%;
        aspect-ratio: 1 / 1;
    }
}

/* モーションを減らす設定を尊重 */
@media (prefers-reduced-motion: reduce) {
    .card,
    .card-button {
        transition: none;
    }
    
    .card:hover {
        transform: none;
    }
}

/* アクセシビリティ */
.card:has(.card-button:focus) {
    box-shadow: var(--card-shadow-hover);
}

11. ベストプラクティス

モダンなCSSを効果的に活用するためのベストプラクティスをまとめます。適切な機能の選択、パフォーマンスの考慮、アクセシビリティの確保などが重要です。

機能の選択

Grid vs Flexbox
- 2次元レイアウト → Grid
- 1次元レイアウト → Flexbox
- 組み合わせて使用も可能

CSS変数の活用
- テーマ管理
- 値の再利用
- 動的な値の変更

モダンな関数の使用
- clamp(): レスポンシブな値
- min()/max(): 条件付き値
- aspect-ratio: アスペクト比の維持

パフォーマンスとアクセシビリティ

パフォーマンス
- will-changeを必要な要素にのみ使用
- content-visibilityで長いページを最適化
- containでレンダリングを分離

アクセシビリティ
- :focus-visibleでキーボードフォーカスを適切に表示
- prefers-reduced-motionを尊重
- 十分なコントラスト比を確保

まとめ

モダンなCSSの機能を活用することで、より効率的で保守しやすいスタイルシートを書くことができます。CSS GridとFlexboxの適切な使い分け、CSS変数によるテーマ管理、コンテナクエリによるコンポーネントベースのレスポンシブデザインなど、様々なテクニックを組み合わせることで、モダンなWebサイトを構築できます。

重要なのは、各機能の特徴を理解し、適切な場面で適切な機能を選択することです。Gridは2次元レイアウトに、Flexboxは1次元レイアウトに適しています。CSS変数はテーマ管理や値の再利用に、コンテナクエリはコンポーネントベースのレスポンシブデザインに適しています。

実践的なプロジェクトでこれらのテクニックを活用し、様々なデバイスとブラウザでテストすることで、より良いWebサイトを構築できるようになります。

Webアクセシビリティ(a11y)とは?実践的な実装ガイド