目次
はじめに
Webデザインやコーディングを進めていく中で、「サイト全体の色や余白、フォントサイズを一括で管理したい」と感じたことはありませんか?
そんなときに力を発揮するのが、CSSの疑似クラス :root(ルート)です。
:root は、HTML文書の最上位要素にあたる特別なセレクタで、全ページ共通の“基準値”を定義するための場所として活用されます。
特に近年は、カスタムプロパティ(CSS変数)と組み合わせることで、ダークモード対応・デザイントークン設計・テーマ切り替えなどを柔軟に行えるようになり、モダンCSSの中でも欠かせない存在となっています。
この記事では、:root の基本的な役割から、html との違い、実践的な使い方、デザインシステム構築での活用法までを、初心者にもわかりやすく丁寧に解説します。
「ただのセレクタ」と思われがちな :root が、CSS全体の設計を支える“土台” である理由を、この記事を通してしっかり理解していきましょう。
:rootとは?
:root は、文書ツリーの最上位要素に一致するCSSの疑似クラスです。
HTML文書では html 要素そのものに一致します。
記法は疑似クラスなので、セレクタは :root { … }
しばしば CSSカスタムプロパティ(変数)を宣言する場所として使われ、テーマ設計やスケール設定の“基底値”を置くのに最適です。
:root {
--brand-hue: 260;
--brand: oklch(0.65 0.12 var(--brand-hue));
--space-1: .25rem;
--space-2: .5rem;
--space-3: 1rem;
--radius: 12px;
}
:root と html の違い
どちらもHTMLでは同じ要素に一致しますが、優先度(specificity)が異なる点が重要です。
:rootの詳細度は疑似クラス=0,1,0htmlはタイプセレクタ=0,0,1
同じ宣言が競合した場合、:root の方が強いため、基底スタイルを守りたいときに有利です。
html { --gap: 8px; }
:root { --gap: 12px; } /* ← こちらが勝つ */
なぜ :root に変数を置くのか
- グローバルで参照可能:すべての要素で
var(--name)が使える - テーマ切替が容易:あとから条件付きで上書きしやすい
- スケール設計の一元管理:余白、色、フォントサイズの基準を一本化
- 計算に強い:
calc()や色関数での再利用が簡単
カスタムプロパティの基本とフォールバック
カスタムプロパティは未定義時にフォールバックを用意すると堅牢です。
:root { --leading: 1.6; }
p { line-height: var(--leading, 1.5); } /* 未定義なら1.5を採用 */
フォールバックは未定義時のみ有効で、空文字や無効値でも「定義済み」とみなされることに注意。
フォールバックとは?(クリックすると説明が開くよ)
※CSSの「フォールバック(fallback)」とは、指定した値が使えない(無効・未定義)場合に、代わりに適用される“保険の値” のことを指します。
🔹 基本の考え方
カスタムプロパティを使うとき、次のように var() 関数内でフォールバック値を指定できます。
color: var(--main-color, #333);
この例では、
- –main-color が定義されている → その値を使う
- –main-color が未定義 → フォールバックの
#333を使う
という動作になります。
つまり、「もしこの変数がなかったら、こっちを使ってね」という保険的な仕組みです。
🔹 よくある使い方
① 変数が未定義のときに備える
:root {
--brand-color: #5b6cff;
}
button {
background: var(--brand-color, #999);
}
この場合、–brand-color が存在すればその値が優先され、
なければフォールバックのグレー #999 が使われます。
チーム開発やコンポーネント単位でのデザイン設計では特に重要です。
② ネストされた変数でもフォールバック可能
color: var(--text-color, var(--default-color, #000));
ここでは、「–text-color → だめなら –default-color → それもなければ #000」という多段フォールバックになります。
③ 新しいCSS機能のフォールバック
新しいプロパティを使うとき、未対応ブラウザのために下にフォールバックを置く方法もあります。
background: #333; /* 古いブラウザ用 */
background: oklch(0.65 0.12 260); /* 新しいブラウザ用 */
古いブラウザは oklch() を理解できないので無視し、上の #333 が使われます。
これも“フォールバック”の一種です。
🔹 よくある勘違い
フォールバックは「未定義時のみ」動作します。
つまり、変数が存在しても値が空(–color: ;)の場合、それは“定義済み”とみなされ、フォールバックは使われません。
:root {
--main-color: ;
}
p {
color: var(--main-color, #333); /* ← #333は使われない */
}
この場合、color は無効になり、文字はデフォルト色(黒)になります。
この仕様を知らずにハマる人が多いので注意です。
✔︎ポイント
- CSSのフォールバックは「保険をかける」仕組み。
- 変数未定義・新仕様未対応 に強いサイト設計ができる。
- チーム開発や多ブラウザ対応では必須の考え方。
| 用語 | 意味 |
|---|---|
| フォールバック | 値が未定義・無効なときの「代替値」 |
| 書き方 | var(–変数名, フォールバック値) |
| 動作条件 | 未定義時のみ有効(空値では発動しない) |
| 応用例 | カスタムプロパティ、ブラウザ非対応プロパティ、段階的適用 |
テーマ切り替え(ライト/ダーク)
:root に基本値、条件側で上書き—これが定番です。
:root {
--bg: #ffffff;
--fg: #0f1221;
--link: #2b5bff;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #0f1221;
--fg: #eef3ff;
--link: #8aa2ff;
}
}
body {
background: var(--bg);
color: var(--fg);
}
a { color: var(--link); }
ポイント:@media 内でも :root を使うと「同じ詳細度」かつ「後勝ち」で素直に切り替わります。
デザイントークンとスケール
スケール(余白・フォント・シャドウなど)を :root で管理すると、一括調整が一瞬で終わります。
:root {
--space-1: .25rem;
--space-2: .5rem;
--space-3: 1rem;
--space-4: 1.5rem;
--font-sm: clamp(12px, 1.2vw, 14px);
--font-md: clamp(14px, 1.4vw, 16px);
--font-lg: clamp(16px, 1.8vw, 20px);
--shadow-1: 0 1px 2px rgb(0 0 0 / .08);
--shadow-2: 0 4px 12px rgb(0 0 0 / .12);
}
「雰囲気を少しだけ大きく/小さく」も、数値を1箇所触るだけで全体に反映されます。
カラーを数式化する
色を一貫管理 し、濃淡やホバー色を関数で派生させると、手作業調整から解放されます。
:root {
--h: 262;
--c-base: oklch(0.65 0.12 var(--h));
--c-hover: oklch(0.58 0.13 var(--h));
--c-press: oklch(0.52 0.14 var(--h));
}
.button {
background: var(--c-base);
}
.button:hover { background: var(--c-hover); }
.button:active { background: var(--c-press); }
ユーザー設定との連動(アクセシビリティ)
ユーザーの環境設定に応じた上書きも :root が便利。
@media (prefers-reduced-motion: reduce) {
:root { --motion: 0; }
}
@media (prefers-reduced-motion: no-preference) {
:root { --motion: 1; }
}
.card {
transition: transform 300ms ease;
transition-duration: calc(300ms * var(--motion));
}
コンポーネントと局所上書き
グローバルは :root に置き、局所上書き はコンポーネントのルートで行うのが実務的。
:root { --card-radius: 16px; --card-bg: #fff; }
.profile-card {
--card-bg: #f8f9ff; /* このカードだけ背景変更 */
border-radius: var(--card-radius);
background: var(--card-bg);
}
注意:変数は カスケード & 継承 するため、上書き位置のスコープを意識すること。
JavaScriptとの連携
JSで :root の変数を書き換えると、全体に即時反映できます。
const root = document.documentElement; // ← :root
root.style.setProperty('--h', '200'); // ブランド色の色相を切替
テーマトグルなどのUIとは相性抜群です。
状態は localStorage に保存しても良いでしょう。
リセット/ノーマライズとの併用
グローバルCSSの頭で :root を宣言し、続いてリセット・ベースを挟むと、“変数→初期化→土台→コンポーネント” の流れが作れます。
/* 1) トークン */
:root { /* …変数群… */ }
/* 2) リセット/ノーマライズ */
/* 3) ベース(body, headings, a, imgなど) */
/* 4) レイアウト(grid/flexの骨格) */
/* 5) コンポーネント */
この順序を守ると、後半の層で変数を使い回しながらも、定義は常に一箇所に集約できます。
@layer と :root
カスケードレイヤーを使う場合、トークン用レイヤーを分けておくと予期せぬ上書きを防げます。
@layer tokens, base, components;
@layer tokens {
:root {
--gap: 12px;
--brand: #5b6cff;
}
}
@layer base {
body { padding: var(--gap); }
}
@layer components {
.btn-primary { background: var(--brand); }
}
レイヤー順は上から下へ強くなる(後勝ち)ため、tokensは最上段が基本。
上位レイヤーで値を参照する構造が安定します。
:root を使うときの落とし穴
- 未定義・綴りミス:var(–brandd) のようなスペル違いは気づきにくい。型チェック代わりに フォールバック を置く。
- 過剰なネスト上書き:コンポーネント内でさらに入れ子で変数を再上書きすると、追跡が困難に。ルールを決める(“上書きは1段まで”など)。
- 論理名の欠如:–blue-1 のような物理名より、–surface, –text-muted のように役割名で定義すると保守性が上がる。
- 単位の混在:rem と px を混ぜると計算が破綻しやすい。単位方針を先に決める。
- ダークテーマの片落ち:ライト側だけ –shadow を更新してダーク側を忘れる、など。チェックリスト化が吉。
ルートフォントサイズとスケール連動
タイポグラフィは :root の font-size と相性が良いです。環境に合わせて流体タイポに。
:root {
font-size: clamp(14px, 1.5vw, 18px);
--leading: 1.65;
}
body {
line-height: var(--leading);
}
ルートに置いた値は全体へ波及し、倍率変更が容易になります。
コンテナクエリ時代の:root
コンテナクエリは要素サイズ基準の分岐ですが、デザイントークンの定義場所としては依然 :root が有効。
- ベース値は :root
- ブレイクポイントでコンテナ単位に上書き(必要最小限)
この住み分けが設計をシンプルに保ちます。
印刷・ハイコントラスト対応
印刷用やハイコントラスト環境でも、:root 上書きが活躍します。
@media print {
:root { --link: #000; --bg: #fff; --fg: #000; }
}
@media (forced-colors: active) {
:root { --outline: CanvasText; }
}
シャドウDOMと:root
Web Componentsのシャドウツリー内では、そのツリーの :host が実質的なルートです。
グローバル :root の変数は継承可能ですが、コンポーネントごとに閉じたテーマを持たせたい場合は :host 側で上書きします。
/* shadow.css */
:host {
--chip-bg: var(--brand, #5b6cff);
}
小さな実例:ブランド一括切り替え
最後に、data-theme と :root を合わせた現場定番パターンです。
:root {
--brand-h: 260;
--brand: oklch(0.62 0.12 var(--brand-h));
--surface: #0f1221;
--text: #eef3ff;
}
:root[data-theme="light"] {
--surface: #ffffff;
--text: #0f1221;
}
.button-primary {
background: var(--brand);
color: var(--text);
border-radius: var(--radius, 12px);
padding: var(--space-3, 1rem) var(--space-4, 1.5rem);
}
// テーマトグル
const root = document.documentElement;
const next = root.dataset.theme === 'light' ? '' : 'light';
if (next) root.dataset.theme = next; else root.removeAttribute('data-theme');
data-theme を変えるだけで、全UIの色味が即時反映。
:root が“真の単一責任点”として効きます。
まとめ
- :root はHTML文書では
htmlに一致。ただし詳細度は :root の方が強い。 - デザイントークン(色・余白・フォント・影・動き) を :root に集約するのが実務の定石。
- メディアクエリや @layer、コンポーネント上書きと組み合わせ、基底→条件→局所の順に設計すると壊れにくい。
- JavaScriptと併用すれば、テーマやスケールの動的切替が容易。
- 変数名は「役割名」で、フォールバック・単位方針・チェックリストを整えると保守性が大幅に向上。
「CSS設計のコア」を担うのが :root。最初にここを整えるだけで、後のスタイル作業がぐっと楽になります。



