CSS変数(カスタムプロパティ)入門 — テーマ管理・ダークモード・コンポーネント設計の実践
この章の目次開く
**CSS変数(カスタムプロパティ)**を使うと、色・サイズ・余白などの値を一箇所で定義して使い回せます。「この青をサイト全体で少し暗くしたい」という変更が、値を1つ書き換えるだけで完了します。
学習者同じ色をあちこちにベタ書きしてて、変更のたびに全部直すのが大変…一箇所で管理できないの?
まさにその課題を解決するのがCSS変数です。Sassの変数($color)と似ていますが、CSS変数はブラウザが実行時に解決するため、JavaScriptからの操作やメディアクエリでの切り替えが可能です。

基本の書き方 — 定義と参照
変数は -- で始まる名前で定義し、var() で参照します。
/* :root に定義するとページ全体で使える */
:root {
--color-primary: #2563eb;
--color-text: #1a1a2e;
--color-bg: #ffffff;
--spacing-sm: 8px;
--spacing-md: 16px;
--spacing-lg: 24px;
--radius-md: 8px;
}
.btn {
background-color: var(--color-primary);
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--radius-md);
}
.card {
background-color: var(--color-bg);
color: var(--color-text);
padding: var(--spacing-lg);
border-radius: var(--radius-md);
}:root はHTMLの <html> 要素を指すセレクタで、ここに書いた変数はページ内のどこからでも参照できます。
デフォルト値(フォールバック)
変数が未定義のときのフォールバック値を指定できます。
.card {
background-color: var(--card-bg, #ffffff);
/* --card-bg が未定義なら #ffffff が使われる */
}
/* デフォルト値にさらに変数を入れることもできる */
.text {
color: var(--text-color, var(--color-text, #333));
}コンポーネント単位でCSS変数を使うとき、「指定されていなければデフォルト値を使う」というパターンが非常に便利です。
スコープ — 変数が有効な範囲
CSS変数はCSSのカスケードに従い、定義した要素とその子孫でのみ有効です。
:root {
--btn-bg: #2563eb; /* ページ全体で有効 */
}
.btn-danger {
--btn-bg: #dc2626; /* このクラスの中だけ上書き */
}
.btn {
background-color: var(--btn-bg);
}
/* .btn-danger 内の .btn → 赤、それ以外の .btn → 青 */
先生Sassの変数($color)はビルド時に解決されて固定値になるけど、CSS変数はブラウザが実行時に解決する。だからメディアクエリで値を切り替えたり、JavaScriptから動的に変えたりできるんだ。これが最大の違いだよ。
デザイントークンとしてのCSS変数
実務では、色・余白・角丸・フォントサイズなどのデザイン値をCSS変数で一元管理し、これをデザイントークンと呼びます。
:root {
/* カラー */
--color-primary: #2563eb;
--color-primary-hover: #1d4ed8;
--color-secondary: #64748b;
--color-success: #22c55e;
--color-danger: #ef4444;
--color-text: #1a1a2e;
--color-text-muted: #64748b;
--color-bg: #ffffff;
--color-surface: #f8fafc;
--color-border: #e2e8f0;
/* スペーシング */
--spacing-xs: 4px;
--spacing-sm: 8px;
--spacing-md: 16px;
--spacing-lg: 24px;
--spacing-xl: 48px;
/* 角丸 */
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 16px;
--radius-full: 9999px;
/* フォントサイズ */
--text-sm: 0.875rem;
--text-base: 1rem;
--text-lg: 1.125rem;
--text-xl: 1.25rem;
}ダークモード対応
CSS変数の最も実践的な使い方の一つがダークモード対応です。変数の値を切り替えるだけで、全コンポーネントのテーマが一気に変わります。
:root {
--color-bg: #ffffff;
--color-text: #1a1a2e;
--color-surface: #f8fafc;
--color-border: #e2e8f0;
}
@media (prefers-color-scheme: dark) {
:root {
--color-bg: #0f172a;
--color-text: #f1f5f9;
--color-surface: #1e293b;
--color-border: #334155;
}
}
body {
background-color: var(--color-bg);
color: var(--color-text);
}
.card {
background-color: var(--color-surface);
border: 1px solid var(--color-border);
}
学習者すごい…変数の値を変えるだけで全部切り替わるの?コンポーネント側は何も触らなくていいんだ。
そのとおりです。コンポーネント側は var(--color-bg) と書いておくだけで、ライトモードでもダークモードでも適切な色が適用されます。
クラスベースのダークモード切り替え
OSの設定ではなくユーザーの操作でテーマを切り替えたい場合は、クラスベースで制御します。
:root {
--color-bg: #ffffff;
--color-text: #1a1a2e;
}
:root.dark {
--color-bg: #0f172a;
--color-text: #f1f5f9;
}// JavaScriptでトグル
document.documentElement.classList.toggle('dark');コンポーネント設計でのCSS変数
CSS変数をコンポーネントの「外部インターフェース」として使うと、再利用性の高いコンポーネントが作れます。
/* ボタンコンポーネント:外からCSS変数でカスタマイズ可能 */
.btn {
background-color: var(--btn-bg, #2563eb);
color: var(--btn-color, #ffffff);
padding: var(--btn-padding, 8px 16px);
border-radius: var(--btn-radius, 8px);
}
/* 使う側で変数を上書き */
.hero .btn {
--btn-bg: #dc2626;
--btn-padding: 12px 32px;
}
.sidebar .btn {
--btn-bg: #64748b;
--btn-radius: 4px;
}
コンポーネントの内部CSSには触れず、変数だけを上書きしてバリエーションを作れるのがポイントです。
JavaScriptとの連携
CSS変数はJavaScriptから読み書きできます。これにより、ユーザーの操作に応じたリアルタイムなスタイル変更が可能です。
読み取り
const styles = getComputedStyle(document.documentElement);
const primary = styles.getPropertyValue('--color-primary');
// " #2563eb"(先頭にスペースが入ることがある → trim()推奨)書き込み
// テーマカラーを動的に変更
document.documentElement.style.setProperty('--color-primary', '#10b981');
// ユーザーがカラーピッカーで選んだ色をリアルタイムに反映
colorPicker.addEventListener('input', (e) => {
document.documentElement.style.setProperty('--color-primary', e.target.value);
});
先生JavaScriptで setProperty した値は、CSSのカスケードでインラインスタイルと同じ扱いになるよ。だからCSS側の :root { --color-primary: ... } より優先される。元に戻したいときは removeProperty で解除できる。
よくあるハマりどころ
変数名のタイプミスに気づかない
CSS変数は未定義でもエラーにならず、フォールバック値(var() の第2引数)が使われます。フォールバック値もなければプロパティが無効になるだけです。
/* タイプミス:--color-primry(aが抜けてる) */
.btn {
background-color: var(--color-primry);
/* 未定義 → 無効 → backgroundが透明になる */
}DevToolsで値が空になっていたら、変数名のスペルを確認しましょう。
calc() の中での単位
/* ❌ 数値だけの変数をcalcでpxに変換しようとする */
:root {
--spacing: 16;
}
.box {
padding: calc(var(--spacing) * 1px);
} /* 動くがやめた方がいい */
/* ⭕ 最初から単位付きで定義する */
:root {
--spacing: 16px;
}
.box {
padding: var(--spacing);
}ちゃんと使うためのポイント
- デザイントークン(色・余白・角丸など)はCSS変数で一元管理する
- 命名は
--カテゴリ-用途(--color-primary、--spacing-md) :rootにベーステーマを定義し、ダークモードは@media (prefers-color-scheme: dark)またはクラス(.dark)で変数を上書き- コンポーネントの「外部インターフェース」としてCSS変数を使うと再利用性が上がる
- CSS変数はJavaScriptから読み書きできる → テーマ切り替えやリアルタイムカスタマイズに活用
次の章では、スタイルが当たらない・意図せず上書きされる原因となる**詳細度とカスケード**を学びます。
参考リンク
- MDN — カスタムプロパティの使用 — カスタムプロパティの公式ガイド
- MDN — var() —
var()関数とフォールバックのリファレンス - MDN — カスタムプロパティ(--*) — カスタムプロパティ
--*の構文リファレンス
