TailwindCSS 4.xのレイヤー設計パターン

TailwindCSS 4.xのレイヤー設計パターン

本記事では、TailwindCSS 4.xの新機能である@layerを活用し、保守性と拡張性に優れたテーマシステムを構築する具体的な手順を解説します。

TailwindCSS #Astro#Svelte#ハンズオン

TailwindCSS 4.xのレイヤー設計パターン

サムネイル

本記事では、TailwindCSS 4.xの新機能である@layerを活用し、保守性と拡張性に優れたテーマシステムを構築する具体的な手順を解説します。

更新日: 6/19/2025

実際に作業したリポジトリはこちらで公開しています。

概要

TailwindCSSは非常に強力なフレームワークですが、従来のバージョンではプロジェクトが大規模になるといくつかの課題が生じることがありました。

ISSUE - 課題

プロジェクトが大規模になると以下の問題が発生する

  • 特定のスタイルを上書きするために詳細度の高いセレクタを使用する必要がある
  • dark:プレフィックスを多用することでHTMLが肥大化する
  • 可読性が低下し、保守性が悪化する
SOLUTION - 解決策

TailwindCSS 4.xで導入された@layer機能を中心とした構造的なアプローチで課題を解決

  • @layerによる層分離でスタイルの適用順序を予測可能にする
  • CSS変数によるテーマ管理で保守性を向上させる
  • data-theme属性による自動的なテーマ切り替えを実現する

設計の要点

この設計の主なポイントは以下の通りです。

  1. @layerによる層分離: スタイルをtheme, base, components, utilitiesの4層に分けます。これにより、CSSの適用順序が予測可能になり、詳細度の競合を防ぎます。

  2. CSS変数によるテーマ管理: 配色やフォントなどの値をCSS変数として一元管理します。これにより、テーマの変更がCSSファイル一箇所への修正で完結し、保守性が向上します。

  3. data-theme属性によるテーマ切り替え: JavaScriptでdata-theme属性を制御します。TailwindCSS 4.xはこれを自動で認識し、dark:バリアントを適用するため、追加の設定は不要です。

実装手順

1. TailwindCSSの設定

まず、tailwind.config.jsで、ユーティリティクラスが直接的な色の値ではなくCSS変数を参照するように設定します。これが動的なテーマ変更の鍵となります。

/** @type {import('tailwindcss').Config} */
export default {
  // ...
  theme: {
    extend: {
      colors: {
        // 'surface'という名前で色を定義し、値にはCSS変数を指定
        surface: {
          DEFAULT: 'var(--color-surface)',
          secondary: 'var(--color-surface-secondary)',
        },
        'text-primary': 'var(--color-text-primary)',
      },
    }
  },
  plugins: [
    function({ addComponents }) {
      addComponents({
        // ボタンの基本スタイルを.btn-baseとして定義
        '.btn-base': {
          '@apply inline-flex items-center justify-center ...': {},
          // ...
        },
        // ボタンの配色を.btn-primary-colorsとして定義
        '.btn-primary-colors': {
          '@apply bg-accent-primary text-white ...': {},
          // ...
        },
      })
    }
  ]
}

2. レイヤーの定義

次に、global.cssでCSSの層構造を定義します。この@layerの記述順(themebasecomponentsutilities)がそのままCSSの優先順位となり、意図しないスタイルの上書きを防ぎます。

@config "./tailwind.config.js";
@import "tailwindcss";

@layer theme, base, components, utilities;

@import "./layers/theme.css" layer(theme);
@import "./layers/base.css" layer(base);
@import "./layers/components.css" layer(components);
@import "./layers/utilities.css" layer(utilities);

前述の通り、この構成では@custom-variantの追加設定は不要です。

3. 各レイヤーの作成

定義した層ごとに、役割に応じたCSSファイルを作成します。

theme.css: デザインシステムの根幹となるCSS変数を定義します。:rootでライトモードの値を、[data-theme="dark"]セレクタ内でダークモードの値を定義することで、テーマの切り替えを実現します。

:root {
  color-scheme: light;
  --color-surface: var(--color-white);
  --color-text-primary: var(--color-gray-900);
  /* ... */
}

[data-theme="dark"] {
  color-scheme: dark;
  --color-surface: var(--color-gray-900);
  --color-text-primary: var(--color-white);
  /* ... */
}

base.css: bodyの背景色やフォントなど、サイト全体に適用される基本的なスタイルを記述します。

components.css: UIの役割に基づいたクラス(例: .btn-primary)を定義します。内部で@applyを使い、configで定義したbtn-basebtn-primary-colorsなどを組み合わせます。

/* Button */
.btn-primary {
  @apply btn-base btn-primary-colors;
}

.btn-secondary {
  @apply btn-base btn-secondary-colors;
}

4. テーマ切り替えスクリプト

OSのテーマ設定に自動で追従する、シンプルなスクリプトをpublic/scripts/以下に配置します。このスクリプトは<html>タグのdata-theme属性を動的に書き換える役割を担います。

(function() {
  function applyTheme(isDark) {
    document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');
  }

  function getSystemTheme() {
    return window.matchMedia('(prefers-color-scheme: dark)').matches;
  }
  
  applyTheme(getSystemTheme());
  window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => applyTheme(e.matches));
})();

5. コンポーネントへの適用

コンポーネントファイルでは、components.cssで定義した、役割を示すクラス名を使用します。

<div class="card-interactive overflow-hidden">
  <div class="p-6">
    <h3 class="heading-3 mb-2">
      {title}
    </h3>
    <p class="body-text mb-4">
      {description}
    </p>
    <button class="btn-primary">
      {buttonText}
    </button>
  </div>
</div>

このように、HTML側ではbg-blue-500 text-white ...のようなユーティリティクラスの羅列を避けることができます。コンポーネントの構造がすっきりと見やすくなり、テーマに応じた色の切り替えはCSS側で自動的に処理されます。

まとめ

@layerによってスタイルの優先順位が固定されるため、意図しない上書きや!importantの使用を避けることができます。テーマ関連の指定をCSS変数に集約することで、デザインシステムの一貫性を保ちながら、変更にも柔軟に対応できます。HTMLにはコンポーネントの役割を示すクラスのみを記述するため、マークアップが簡潔になり、可読性も大きく向上します。加えて、TailwindCSSの機能により、最小限の設定でdata-theme属性とdark:バリアントが自動的に連携する点も、この設計の大きな利点です。

検索

検索条件に一致する記事が見つかりませんでした