本文へスキップ

CSS変数を使ってみよう

CSS変数を使うメリット

サイト全体のメインカラーを変更するとき、同じカラーコードをCSSファイルの中から探して書き換えます。この作業には手間がかかります。10箇所、20箇所と増えてくると、どこかで書き換え漏れが出てきます。

CSS変数(正式にはカスタムプロパティ)を使うと、色やサイズなどの値を1箇所で定義できます。定義した値は、サイト全体で使い回せます。値を変更したいときは、定義している1箇所を直すだけで全ページに反映されます。

メインカラーを変数なしで書くと、こうなります。

/* 変数なし:同じ色を何度も書く */
.header { background-color: #1e90ff; }
.btn-primary { background-color: #1e90ff; }
.link { color: #1e90ff; }
.section-title::after { background: #1e90ff; }

CSS変数を使うと、こう変わります。

/* 変数あり:定義は1箇所だけ */
:root {
  --color-primary: #1e90ff;
}

.header { background-color: var(--color-primary); }
.btn-primary { background-color: var(--color-primary); }
.link { color: var(--color-primary); }
.section-title::after { background: var(--color-primary); }

メインカラーを別の色に変えたくなったら、:root の中の1行を書き換えるだけです。書き換え漏れも起きません。

Sass(SCSSの変数$color-primaryなど)を使っている方も多いと思います。Sassの変数との違いは後ほど触れますが、CSS変数にはブラウザが読み込んだ後に値を切り替えられるという強みがあります。レスポンシブ対応やダークモードなど、状況に応じて値を変えたい場面でこの特性が活きてきます。

基本の書き方をおさえよう

:root で宣言する

CSS変数は --(ハイフン2つ)から始まる名前で宣言します。使うときは var() 関数で呼び出します。

:root {
  --color-primary: #1e90ff;
  --color-text: #333333;
  --font-size-base: 16px;
  --spacing-md: 24px;
}

.card {
  color: var(--color-text);
  font-size: var(--font-size-base);
  padding: var(--spacing-md);
  border-top: 3px solid var(--color-primary);
}

:root はHTMLドキュメント全体を指すセレクタです。ここで宣言した変数は、ページ内のどこからでも使えます。サイト共通の色やサイズは :root にまとめておくのが基本です。

var() にはフォールバック値(予備の値)を指定できます。万が一変数が未定義だったときに使われる値です。

/* 第2引数がフォールバック値 */
.card {
  color: var(--color-text, #333333);
}

フォールバック値は「保険」のようなものです。変数の定義漏れがあっても表示が壊れません。僕は特に共通パーツに入れるようにしています。

開発者ツールのStyles。rootに宣言したカスタムプロパティが一覧で並んでいる

スコープを活かす

CSS変数は :root だけでなく、任意のセレクタの中で宣言できます。宣言した要素とその子要素の範囲内で有効になります。

:root {
  --color-primary: #1e90ff;
}

.card--danger {
  --color-primary: #e74c3c;
}

.card {
  border-top: 3px solid var(--color-primary);
}

.card__title {
  color: var(--color-primary);
}

この書き方だと、通常の .card は青いボーダーとタイトルになります。.card--danger クラスがついたカードだけ赤に変わる、という仕組みです。HTMLの構造は同じまま、クラスひとつで見た目を切り替えられます。

この仕組みが最初は分からなくても、.card--danger を付けるだけで色が切り替わるという結果だけ押さえておけば、先に進んで大丈夫です。

コンポーネント(パーツ)単位で変数を上書きするこのパターンは、実務でかなり使えます。ボタンのバリエーション、セクションごとの配色変更など、応用範囲が広いです。

CSS変数のPrimitive層とSemantic層の参照関係
開発者ツールのStyles。card--dangerのcolor-primaryが優先され、rootの値に取り消し線が引かれている

実案件で使える管理パターン3つ

カラーパレットを一元管理する

実際の案件で最初に変数化すべきなのは、やはり色です。デザインカンプから色を拾って、役割ごとに名前をつけていきます。

:root {
  /* ブランドカラー */
  --color-primary: #1e90ff;
  --color-primary-light: #e8f4fd;
  --color-primary-dark: #1565c0;

  /* テキスト */
  --color-text: #333333;
  --color-text-light: #666666;
  --color-text-muted: #999999;

  /* 背景 */
  --color-bg: #ffffff;
  --color-bg-gray: #f5f5f5;

  /* アクセント・状態 */
  --color-accent: #ffe066;
  --color-error: #e74c3c;
  --color-success: #2ecc71;
}

ポイントは変数名のつけ方です。

  • --color- で始めて、色の変数であることをすぐわかるようにする
  • 具体的な色名(--blue)ではなく、役割名(--color-primary)をつける
  • バリエーションは -light / -dark などのサフィックスで表現する

--blue のような色名にすると、ブランドカラーが青から緑に変わったときに困ります。変数名と実際の色がちぐはぐになるからです。役割名にしておけば、値だけ差し替えれば済みます。

フォントサイズ・余白をトークンとして管理する

色だけでなく、フォントサイズや余白も変数にしておくとサイト全体の統一感が出ます。デザイントークン(デザインの共通ルールを変数として定義したもの)という考え方です。

:root {
  /* フォントサイズ */
  --font-size-xs: 0.75rem;    /* 12px */
  --font-size-sm: 0.875rem;   /* 14px */
  --font-size-base: 1rem;     /* 16px */
  --font-size-lg: 1.25rem;    /* 20px */
  --font-size-xl: 1.5rem;     /* 24px */
  --font-size-2xl: 2rem;      /* 32px */

  /* 余白 */
  --spacing-xs: 4px;
  --spacing-sm: 8px;
  --spacing-md: 16px;
  --spacing-lg: 24px;
  --spacing-xl: 40px;
  --spacing-2xl: 64px;
}

使い方はカラーと同じです。

.section {
  padding: var(--spacing-2xl) 0;
}

.section__title {
  font-size: var(--font-size-2xl);
  margin-bottom: var(--spacing-lg);
}

.section__text {
  font-size: var(--font-size-base);
  margin-bottom: var(--spacing-md);
}

「この余白、16pxだっけ?24pxだっけ?」と迷うことがなくなります。デザインカンプのルールと変数名を対応させておくと、コーディング中に何度もFigmaを確認しに行く手間も減ります。

フォントサイズのコメントに /* 16px */ とピクセル値を添えているのは、理由があってのことです。rem の換算を都度計算しなくて済むからです。ちょっとした工夫ですが、後から見返したときに助かります。

ブレイクポイントごとに値を切り替える

CSS変数の真価が発揮されるのが、レスポンシブ対応の場面です。メディアクエリの中で変数を上書きするだけで、その変数を使っているすべての箇所に変更が反映されます。

:root {
  --font-size-heading: 1.5rem;
  --spacing-section: 40px;
  --grid-columns: 1;
}

@media (min-width: 768px) {
  :root {
    --font-size-heading: 2rem;
    --spacing-section: 64px;
    --grid-columns: 2;
  }
}

@media (min-width: 1024px) {
  :root {
    --grid-columns: 3;
  }
}

これを使う側はとてもシンプルになります。

.section {
  padding: var(--spacing-section) 0;
}

.section__title {
  font-size: var(--font-size-heading);
}

.card-grid {
  display: grid;
  grid-template-columns: repeat(var(--grid-columns), 1fr);
  gap: var(--spacing-lg);
}

変数を使わない書き方だと、メディアクエリの中でセレクタごとにスタイルを書き直す必要があります。.section.section__title.card-gridのように、使っている数だけ書き直しが増えます。変数で管理していれば、値を変えるだけで、使っている側は一切触らなくていいのです。これがCSS変数の大きなメリットです。

特にカラム数の切り替えは便利です。--grid-columns の値をブレイクポイントごとに変えるだけで、グリッドレイアウトが自動的に調整されます。

Sassと組み合わせるときのコツ

Sassの変数($color-primary)とCSS変数(--color-primary)は、名前が似ています。ただし、性質はまったく違います。

  • Sass変数はコンパイル時(CSSに変換するタイミング)に値が確定する。ブラウザはSass変数の存在を知らない
  • CSS変数はブラウザが読み込んだ後も値として残り続ける。メディアクエリやJavaScriptで動的に切り替えられる

実務では、この2つを完全にどちらかへ統一しません。僕は役割で使い分けるようにしています。

// _variables.scss

// Sass変数:ビルド時に確定する設定値
$breakpoint-md: 768px;
$breakpoint-lg: 1024px;
$font-family-base: "Noto Sans JP", sans-serif;
$border-radius: 6px;

// CSS変数:ブラウザ側で変わりうる値
:root {
  --color-primary: #1e90ff;
  --color-text: #333333;
  --font-size-heading: 1.5rem;
  --spacing-section: 40px;
}

@media (min-width: $breakpoint-md) {
  :root {
    --font-size-heading: 2rem;
    --spacing-section: 64px;
  }
}

使い分けの目安はこうです。

  • ブレイクポイントの数値やフォントファミリー、border-radiusなど。サイト全体で固定の値なので、Sass変数で十分です
  • 色、フォントサイズ、余白など。レスポンシブやコンポーネントのバリエーションで変わる可能性があるので、CSS変数が向いています

Sass変数でまずCSS変数の初期値を定義して、CSS変数としてブラウザに渡す、というパターンもあります。

// Sassで初期値を管理
$color-primary-default: #1e90ff;

:root {
  --color-primary: #{$color-primary-default};
}

Sassの #{}(インターポレーション)を使って、Sass変数の値をCSS変数に渡しています。こうすると、Sassのmixinやfunctionの中でも初期値を参照できるので、両方のいいとこ取りができます。

やりがちな失敗と対処法

CSS変数は便利ですが、実案件で使い始めると「あれ?」となるポイントがいくつかあります。先に知っておけば避けられるものばかりです。

フォールバック値の書き忘れ

共通のCSSファイルとは別に、ページ固有のCSSファイルがある構成では注意が必要です。共通CSSで :root に変数を定義していても、ページ固有CSSだけ読み込まれるケースがあります。そのケースでは変数が未定義になることがあります。公開後に表示崩れとして、クライアントの目に留まってしまうケースです。

/* フォールバックなし:変数が未定義だとスタイルが効かない */
.card {
  color: var(--color-text);
}

/* フォールバックあり:未定義でも #333 が適用される */
.card {
  color: var(--color-text, #333333);
}

すべてに書く必要はありません。ヘッダーやフッターなど、どのページでも確実に表示されるパーツには、フォールバック値を入れておくと安全です。

変数名がバラバラになる

チームで開発していると、同じ意味の変数に違う名前がついてしまうことがあります。

/* 人によって書き方がバラバラになりがち */
--main-color: #1e90ff;
--primary-color: #1e90ff;
--color-primary: #1e90ff;
--colorPrimary: #1e90ff;

案件の最初に命名ルールを決めておくだけで防げます。僕が最初に決めているのは、次のルールです。

  • カテゴリから始める:--color---font-size---spacing-
  • ケバブケース(ハイフン区切り)で統一する
  • サイズの段階には xs / sm / md / lg / xl / 2xl を使う

具体的にはこうなります。

  • --color-{役割} → 例:--color-primary--color-text--color-bg
  • --font-size-{段階}は次の値です。--font-size-sm--font-size-base--font-size-lg
  • --spacing-{段階} の例です。--spacing-sm--spacing-md--spacing-xl

CSSファイルの冒頭にコメントで命名規則を書いておくと、後からプロジェクトに入るメンバーにも伝わります。

変数のネストが深くなりすぎる

CSS変数の値に、別のCSS変数を使うこともできます。便利な機能ですが、やりすぎると追いかけにくくなります。

/* 2段階くらいなら問題なし */
:root {
  --color-blue: #1e90ff;
  --color-primary: var(--color-blue);
}

/* 3段階以上は追いかけにくい */
:root {
  --color-blue: #1e90ff;
  --color-brand: var(--color-blue);
  --color-primary: var(--color-brand);
  --color-header-bg: var(--color-primary);
}

僕は参照の深さを2段階までにとどめるようにしています。それ以上になるなら、構造を見直したほうがメンテナンスしやすくなります。

開発者ツールのComputed。varが解決されたあとの色がrgb(231, 76, 60)として出ている

おわりに

CSS変数を使うと、色やサイズの値を1箇所にまとめて管理できます。書き換えの手間も、レスポンシブでの出し分けも、この仕組みひとつでかなり楽になります。

とはいえ、既存のプロジェクトすべてを一度に変数化する必要はありません。Sass変数との使い分けや命名ルールなど、現場ごとに合う形は少しずつ変わってくると思います。

:root に色をひとつ、変数として切り出すところから手を動かしてみてください。

Contact

お気軽にご相談ください

X(Twitter)のDM、もしくは本サイトのCONTACTページからご連絡ください。
24時間以内に返信しますので、些細なことでもお気軽にご相談ください。