Webサイト制作の現場で「色や余白を一括管理したい」「ダークモードを簡単に実装したい」と感じたことはないでしょうか。そんな悩みを一気に解決してくれるのがCSS変数(カスタムプロパティ)です。かつてはSassなどのプリプロセッサに頼っていた変数機能が、今やネイティブCSSだけで、しかもランタイム(実行時)で動的に切り替えられるようになりました。
2026年現在、CSS変数は単なる値の置き換えにとどまらず、@propertyによる型定義、アニメーション、JavaScript連携、さらには:has()やcolor-mix()との組み合わせによって、デザインシステムの中核を担う存在へと進化しています。本記事では、CSS変数の基本構文から実務で使える応用テクニック、最新仕様までを1記事で網羅的に解説します。初心者からベテランエンジニアまで、明日から使える知識を持ち帰っていただける構成になっています。
CSS変数(カスタムプロパティ)とは何か
CSS変数は正式には「CSSカスタムプロパティ」と呼ばれる機能で、色や余白、フォントサイズなどの値を再利用可能な形で保存できる仕組みです。CSS変数はしばらく前から存在していましたが、多くの開発者は色やフォントサイズを格納する用途でしか使っていませんでした。2026年になり、アニメーションを駆動させたり、柔軟なデザインシステムを構築したり、CSSだけで動的なレイアウトを作るための非常に強力なツールへと成長しています。
SassなどのプリプロセッサとCSS変数の違い
従来SassやLessなどで使われてきた変数とCSS変数の最大の違いは「いつ値が決まるか」にあります。Sass変数はビルド時に解決され、コンパイルされた後は出力に値が固定されます。一方、ネイティブCSS変数はランタイムに存在し、再ビルドなしで変更できます。
つまり、CSS変数はブラウザ上で動的に値を変更できるため、JavaScriptと連携してテーマ切り替えやユーザー設定の反映が容易です。
ビルド不要で即座に反映できる点こそ、現代のフロントエンド開発でCSS変数が選ばれる最大の理由です。
CSS変数を使うメリット
- デザイントークンの一元管理ができる
- ダークモード・ライトモード切替が数行で実装可能
- メディアクエリやコンテナクエリと組み合わせて動的なレスポンシブ対応ができる
- JavaScriptとシームレスに連携できる
- 保守性と可読性が大幅に向上する

CSS変数の基本的な書き方と構文
CSS変数は「--変数名」という形式で定義し、var()関数で呼び出します。
最もシンプルな例を見てみましょう。
:root {
--main-color: #3498db;
--base-spacing: 16px;
--font-family-base: 'Noto Sans JP', sans-serif;
}
.button {
background-color: var(--main-color);
padding: var(--base-spacing);
font-family: var(--font-family-base);
}:rootに定義する理由
:rootはHTMLドキュメント全体のルート要素を指す擬似クラスで、ここに定義した変数はページ全体から参照できます。
グローバル変数として扱いたい場合は:rootに、特定の要素配下だけで使いたい場合はそのセレクタ内に定義します。
var()関数とフォールバック値の指定
var()関数は第二引数にフォールバック値を指定できます。
変数が未定義だった場合に使われる代替値です。
.card {
/* --card-bg が未定義なら #ffffff が使われる */
background-color: var(--card-bg, #ffffff);
color: var(--card-text, #333333);
}変数名は大文字と小文字を区別します。–MainColor と –maincolor は別の変数として扱われるため、命名規則を統一しないと予期せぬバグの原因になります。
命名規則のベストプラクティス
大規模なプロジェクトでは命名規則を統一することが重要です。
一般的には以下のような階層的な命名が推奨されます。
:root {
/* プリミティブトークン */
--color-blue-500: #3498db;
--color-gray-100: #f5f5f5;
/* セマンティックトークン */
--color-primary: var(--color-blue-500);
--color-background: var(--color-gray-100);
/* コンポーネントトークン */
--button-bg: var(--color-primary);
--button-text: #ffffff;
}スコープとカスケードの仕組み
CSS変数は通常のCSSプロパティと同じくカスケードと継承のルールに従います。
これを理解しておくと、変数の上書きや局所的な変更が自在にコントロールできます。
変数のスコープを限定する方法
特定のセレクタ内で変数を再定義することで、その要素配下だけ値を変更できます。
:root {
--text-color: #333;
}
.alert {
--text-color: #d9534f; /* .alert内だけ赤色に */
color: var(--text-color);
}
.alert .icon {
color: var(--text-color); /* 親から継承して赤色 */
}継承を活用したコンポーネント設計
CSS変数は親要素から子要素へ自動的に継承されるため、コンポーネント単位で値を切り替える設計と非常に相性が良いです。
例えばカードコンポーネントに対してテーマ別のクラスを付与するだけで、内部のすべての要素に変更を反映できます。
.card {
--card-padding: 16px;
--card-radius: 8px;
padding: var(--card-padding);
border-radius: var(--card-radius);
}
.card.card--large {
--card-padding: 32px;
--card-radius: 16px;
}@propertyで型付きカスタムプロパティを定義する
2024年7月以降、すべての主要ブラウザで利用可能になった@propertyは、CSS変数の使い方を大きく変えた重要な機能です。@propertyルールはCSS HoudiniというAPI群の一部で、すべての主要ブラウザで完全サポートされました。この機能はCSSカスタムプロパティに新しいレベルの制御と柔軟性をもたらし、スタイルシートをより賢く、より動的にします。2024年7月9日にBaseline Newly availableとなりました。
@propertyの基本構文
@propertyルールはCSSカスタムプロパティを明示的に定義するために使用され、プロパティの型チェックや制約、デフォルト値の設定、カスタムプロパティが値を継承するかどうかの定義を可能にします。
@property --brand-color {
syntax: "<color>";
inherits: true;
initial-value: #3498db;
}
@property --rotation-angle {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}3つのディスクリプタが重要です。
- syntax:値の型を指定(<color>、<length>、<number>など)
- inherits:継承するかどうか(true / false)
- initial-value:初期値
syntaxとinheritsは必須です。
どちらかが欠けていると@propertyルール全体が無効として無視されるため注意してください。
指定できるsyntax(型)の種類
MDN公式ドキュメントによると、syntaxとしてサポートされている型には、<angle>(角度)、<color>(色)、<custom-ident>(カスタム識別子)、<image>(画像)、<integer>(整数)、<length>(長さ)、<length-percentage>(長さまたはパーセンテージ)、<number>(数値)、<percentage>(パーセンテージ)、<resolution>(解像度)、<string>(文字列)、<time>(時間)、<transform-function>(変形関数)、<transform-list>(変形リスト)、<url>(URL)などがあります。
@propertyでアニメーションが可能になる理由
通常のCSS変数は文字列として扱われるため、グラデーションや角度のアニメーションができませんでした。CSS変数(カスタムプロパティ)はブラウザがその型を知らないため、デフォルトではアニメーションしません。@propertyは構文・継承・初期値を宣言できるようにすることでこの問題を解決します。これにより色や数値、さらにはグラデーションさえも滑らかにトランジションできるようになります。
@property --gradient-angle {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
.animated-box {
background: linear-gradient(var(--gradient-angle), #ff6b6b, #4ecdc4);
transition: --gradient-angle 1s ease;
}
.animated-box:hover {
--gradient-angle: 180deg;
}
ダークモードとテーマ切り替えの実装
CSS変数の真価がもっとも発揮されるユースケースの一つが、ダークモードやテーマ切り替えです。
従来は全コンポーネントに対して色違いのクラスを用意する必要がありましたが、CSS変数を使えば値の差し替えだけで全体のテーマを一瞬で切り替えられます。
data属性を使ったテーマ切り替え
:root {
--bg-color: #ffffff;
--text-color: #1a1a1a;
--accent-color: #0066cc;
}
[data-theme="dark"] {
--bg-color: #1a1a1a;
--text-color: #f0f0f0;
--accent-color: #66b3ff;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
transition: background-color 0.3s, color 0.3s;
}JavaScript側では1行で切り替えが可能です。
// テーマ切り替え
document.documentElement.setAttribute('data-theme', 'dark');
// ユーザー設定をlocalStorageに保存
localStorage.setItem('theme', 'dark');prefers-color-schemeとの組み合わせ
OS設定に応じた自動切り替えを実装するにはメディアクエリと組み合わせます。
@media (prefers-color-scheme: dark) {
:root {
--bg-color: #1a1a1a;
--text-color: #f0f0f0;
}
}color-mix()で色のバリエーションを生成
2026年現在、color-mix()関数とCSS変数を組み合わせることで、ベースカラーから派生色を動的に生成できます。
:root {
--primary: oklch(60% 0.15 250);
}
.button {
background: var(--primary);
border: 1px solid color-mix(in oklch, var(--primary), black 20%);
}
.button:hover {
background: color-mix(in oklch, var(--primary), white 15%);
}JavaScriptとCSS変数を連携させる
CSS変数はランタイムで値を読み書きできるため、JavaScriptと連携することで強力な動的UIを実現できます。
JavaScriptから値を読み取る
// 現在の変数値を取得
const root = document.documentElement;
const primaryColor = getComputedStyle(root).getPropertyValue('--primary-color').trim();
console.log(primaryColor); // "#3498db"JavaScriptから値を設定する
// 変数値を動的に変更
document.documentElement.style.setProperty('--primary-color', '#e74c3c');
// 要素単位でも設定可能
const card = document.querySelector('.card');
card.style.setProperty('--card-padding', '24px');マウス座標を変数に渡して動的エフェクト
マウス位置をリアルタイムにCSS変数へ反映すれば、JavaScriptとCSSの境界を超えたインタラクティブな表現が可能です。
document.addEventListener('mousemove', (e) => {
document.documentElement.style.setProperty('--mouse-x', `${e.clientX}px`);
document.documentElement.style.setProperty('--mouse-y', `${e.clientY}px`);
});.spotlight {
background: radial-gradient(
circle at var(--mouse-x) var(--mouse-y),
rgba(255, 255, 255, 0.2),
transparent 200px
);
}calc()と組み合わせた高度な計算
CSS変数はcalc()関数と組み合わせることで、計算式の中で柔軟に使えます。
レスポンシブデザインやデザインシステムのスケーリングに非常に有用です。
スペーシングシステムの構築
:root {
--space-unit: 8px;
--space-xs: calc(var(--space-unit) * 1); /* 8px */
--space-sm: calc(var(--space-unit) * 2); /* 16px */
--space-md: calc(var(--space-unit) * 3); /* 24px */
--space-lg: calc(var(--space-unit) * 4); /* 32px */
--space-xl: calc(var(--space-unit) * 6); /* 48px */
}
.section {
padding: var(--space-lg) var(--space-md);
margin-bottom: var(--space-xl);
}流動的なタイポグラフィ
ビューポート幅に応じて滑らかに変化するフォントサイズを、CSS変数とclamp()で実現できます。
:root {
--font-size-base: clamp(1rem, 0.9rem + 0.5vw, 1.25rem);
--font-size-h1: clamp(2rem, 1.5rem + 2.5vw, 3.5rem);
--font-size-h2: clamp(1.5rem, 1.2rem + 1.5vw, 2.5rem);
}
body { font-size: var(--font-size-base); }
h1 { font-size: var(--font-size-h1); }
h2 { font-size: var(--font-size-h2); }
2026年の最新機能と組み合わせ
2026年のCSSは劇的に進化しており、CSS変数は他の最新機能と組み合わせることで真価を発揮します。モダンCSSにはランタイムパワーを持つネイティブ変数、ネスト、カスケード制御のための@layer、コンテキストスタイルのための@scope、コンテナクエリ、親要素を判別できる:has()、oklch()やcolor-mix()のような高度なカラー関数、そして数学関数や合成可能なカスタムプロパティが含まれます。これらは見た目だけの改良ではなく、プリプロセッサが解決してきたあらゆる種類のハックや回避策を取り除きます。
:has()セレクタとCSS変数
親要素が子要素の状態に応じてスタイルを変える:has()とCSS変数を組み合わせると、JavaScriptなしで複雑な状態管理が可能です。
.form {
--border-color: #ccc;
border: 2px solid var(--border-color);
}
.form:has(input:invalid) {
--border-color: #d9534f;
}
.form:has(input:focus) {
--border-color: #0066cc;
}コンテナクエリとの組み合わせ
コンテナのサイズに応じて変数値を変化させることで、真のコンポーネント指向レスポンシブが実現します。
.card-container {
container-type: inline-size;
}
.card {
--card-padding: 12px;
--card-font: 14px;
padding: var(--card-padding);
font-size: var(--card-font);
}
@container (min-width: 400px) {
.card {
--card-padding: 24px;
--card-font: 16px;
}
}CSS Mixinsへの展望
2026年にはChrome 146(予定)でCSS Mixinsが導入予定で、@mixin –name { ・・・ }で宣言の再利用ブロックを定義し、@apply –nameで適用できます。
Sassのミクスインに似ていますが、パラメータや条件分岐に対応したネイティブ機能です。これにより、CSS変数とミクスインを組み合わせた高度なデザインシステム構築が可能になります。
ブラウザ対応状況と互換性の確保
CSS変数の基本機能はモダンブラウザで広くサポートされていますが、最新機能を使う際にはフォールバックの考慮が必要です。
基本機能のサポート状況
2026年現在、CSSカスタムプロパティ(変数)の基本機能はすべての主要ブラウザで完全にサポートされています。
Internet Explorerはサポート対象外ですが、すでに公式サポートが終了しているため実務上は問題になりません。
@propertyのサポート状況
2024年7月以降、@property機能は最新のデバイスとブラウザバージョンで動作します。古いデバイスやブラウザでは動作しない可能性があります。つまり2026年時点で@propertyは実務投入可能な成熟した機能と言えます。
@supportsでフォールバックを実装
古いブラウザにも配慮する場合は@supportsでフィーチャークエリを使います。
/* フォールバック値 */
.button {
background-color: #3498db;
}
/* CSS変数対応ブラウザ */
@supports (--css: variables) {
.button {
background-color: var(--primary-color, #3498db);
}
}var()関数の第二引数のフォールバック値だけでは、CSS変数自体に対応していないブラウザには効果がありません。
完全な後方互換性を確保するには、変数を使わない通常のCSSプロパティを先に書き、後から変数で上書きするパターンが安全です。
実務で使えるベストプラクティス
ここまで学んだ知識を実務に落とし込むためのポイントを、現場で得られた知見ベースでまとめます。
変数の階層設計
大規模プロジェクトでは「3層構造」が推奨されます。
- プリミティブ層:原始的な値(
--color-blue-500など) - セマンティック層:意味を持つ値(
--color-primary、--color-dangerなど) - コンポーネント層:UIパーツ専用(
--button-bg、--card-borderなど)
この階層を分けることで、デザインシステムの変更があってもプリミティブ層を変えるだけで全体に反映できます。
過剰な変数化を避ける
何でも変数にすればよいわけではありません。
1回しか使わない値や、ローカルな調整値まで変数化すると、かえってコードが追いにくくなります。「2回以上同じ値を使う」「将来変更される可能性が高い」場合に限定すると、メンテナンス性が最適化されます。
パフォーマンスへの配慮
CSS変数の参照は基本的に高速ですが、JavaScriptから頻繁に書き換える場合は注意が必要です。requestAnimationFrameを使ったり、書き換える要素のスコープを最小化することで、再描画コストを抑えられます。
// 悪い例:即時実行で描画コストが高い
window.addEventListener('scroll', () => {
document.documentElement.style.setProperty('--scroll-y', window.scrollY + 'px');
});
// 良い例:requestAnimationFrameで最適化
let ticking = false;
window.addEventListener('scroll', () => {
if (!ticking) {
requestAnimationFrame(() => {
document.documentElement.style.setProperty('--scroll-y', window.scrollY + 'px');
ticking = false;
});
ticking = true;
}
});命名のアンチパターンを避ける
- 色そのものを名前にしない(
--redではなく--color-danger) - 抽象的すぎる名前を避ける(
--var1のような番号付け) - 一貫性のないケーシング(camelCaseとkebab-caseの混在)
よくある質問とトラブルシューティング
変数が反映されないときの確認ポイント
CSS変数が反映されない場合、以下の点をチェックしてください。
- 変数名のタイポ(
--が1つだけになっていないか) - 定義箇所のスコープ(参照する要素から見えているか)
- 大文字小文字の違い
- 無効な値が代入されていないか(DevToolsで赤線が引かれていないか)
メディアクエリの中で変数は使える?
2026年現在、@mediaのクエリ部分(例:@media (min-width: var(--breakpoint)))で直接CSS変数を使うことはまだ標準対応していません。
ブレークポイント自体は固定値で書き、その内側で変数を変更する設計が現実的です。
SassとCSS変数のどちらを使うべきか?
ネイティブCSSを最初に書き、プリプロセッサが必要な場面で追加する。プリプロセッサは消え去るわけではなく、もはや出発点ではないというだけです。動的な値の切り替えはCSS変数、ビルド時の最適化や複雑なロジックはSass、という使い分けが2026年の主流です。
まとめ
CSS変数(カスタムプロパティ)は、2026年現在もっとも実用性の高いCSS機能の一つに進化しました。
基本構文は--変数名とvar()関数というシンプルな仕組みですが、@propertyによる型定義、:has()やコンテナクエリとの組み合わせ、JavaScript連携によって、かつてのプリプロセッサの役割を超えた強力なツールとなっています。
本記事で押さえておくべきポイントを再度整理します。
- CSS変数はランタイムで動的に変更可能、ビルド不要でテーマ切り替えができる
:rootを起点とした階層的なスコープ設計が保守性を高める@propertyを使えば型チェック・初期値・継承・アニメーションが可能になる- JavaScriptとの連携で、マウス座標やスクロール量などの動的データをCSSに渡せる
- 過剰な変数化を避け、「2回以上使う・将来変わる可能性がある」値だけを変数化する
- 2026年は
color-mix()、:has()、コンテナクエリ、近日登場のCSS Mixinsとの組み合わせが鍵
CSS変数を使いこなせるかどうかは、現代のフロントエンド開発者にとって必須のスキルセットとなりました。
まずは小さなコンポーネントから変数化を始め、徐々にデザインシステム全体へ展開していくのがおすすめの進め方です。
本記事の内容を参考に、ぜひあなたのプロジェクトでも実践してみてください。
