あなたに合った学習プランは?LINE適正コース診断はこちら プログラミングが全て無料で学習可能!
【CSS】カスタムプロパティとは?継承・スコープ・@propertyまで解説 - 忍者CODEマガジン

【CSS】カスタムプロパティとは?継承・スコープ・@propertyまで解説

プログラミング言語の辞書

CSSカスタムプロパティは、CSSのカスケードや継承の仕組みの中で評価される、仕様上の正式な「独自プロパティ」です。一般的には「CSS変数」と呼ばれることもありますが、仕様上の正式名称はカスタムプロパティです。CSS変数の基本的な使い方だけなら、--名前で定義してvar()で呼び出す、と覚えれば始められます。しかし、実務で大きなCSSを扱う場合は、継承、スコープ、カスケード、@propertyまで理解しておくと、意図しない上書きや無効な値を避けやすくなります。

この記事では、CSSカスタムプロパティについて仕様寄り・中級者向けの視点で解説します。

CSSカスタムプロパティとは?

CSSカスタムプロパティとは、開発者が任意の名前で定義できるCSSプロパティです。名前は--から始まります。

:root {
  --brand-color: #0068b7;
}

この--brand-colorがカスタムプロパティです。通常のCSSプロパティのようにブラウザが意味を持っているわけではなく、あとからvar()で参照される値として扱われます。

CSS変数との関係

「CSS変数」は、カスタムプロパティの使われ方を分かりやすく表した呼び方です。学習記事や現場の会話ではCSS変数と呼ばれることが多いですが、仕様やMDNなどの公式ドキュメントでは「custom properties」という名前で扱われます。

呼び方 位置づけ この記事での扱い
カスタムプロパティ CSS仕様上の正式名称 継承・@property・仕様理解の中心
CSS変数 機能のイメージを表す呼び方 基本的な使い方を説明するときの呼称

CSS変数を初めて使う方は、まず基本的なvar()の使い方を解説した記事から読むのがおすすめCSS変数の使い方とは?var()で色や余白をまとめて管理する方法を解説

カスタムプロパティはカスケードの影響を受ける

カスタムプロパティは、普通のCSSと同じようにカスケードの影響を受けます。つまり、同じ名前のカスタムプロパティが複数定義されている場合、詳細度や読み込み順によって最終的な値が決まります。

:root {
  --accent-color: #0068b7;
}

.campaign {
  --accent-color: #e95420;
}

.campaign .button {
  background-color: var(--accent-color);
}

この例では、.campaignの中にある.buttonでは#e95420が使われます。:rootの値をベースにしつつ、特定の範囲だけ上書きしているためです。

カスタムプロパティは「値の置き換え」ではなく、CSSのカスケード上で最終的な値が決まる仕組みです。

カスタムプロパティの継承とスコープ

カスタムプロパティは、通常は親要素から子要素へ継承されます。この性質があるため、テーマカラーやコンポーネント設定を親側で定義し、子要素で参照できます。

<section class="theme-dark">
  <article class="card">
    <h3>カードタイトル</h3>
  </article>
</section>
:root {
  --text-color: #222222;
  --surface-color: #ffffff;
}

.theme-dark {
  --text-color: #ffffff;
  --surface-color: #1f1f1f;
}

.card {
  color: var(--text-color);
  background-color: var(--surface-color);
}

.card.theme-darkの子孫なので、.theme-darkで上書きされた値を参照します。

スコープを狭めると影響範囲を管理しやすい

大規模なCSSでは、すべてを:rootに置くより、コンポーネント単位で必要な値を持たせた方が安全な場合があります。

.price-card {
  --price-card-border: #dddddd;
  --price-card-bg: #ffffff;

  border: 1px solid var(--price-card-border);
  background-color: var(--price-card-bg);
}

.price-card.is-recommend {
  --price-card-border: #0068b7;
  --price-card-bg: #e8f4fb;
}

このように部品専用の変数名にすると、他の部品と名前が衝突しにくくなります。

大規模CSSでは、汎用的すぎる名前のカスタムプロパティを増やすと、どこで上書きされているか追いにくくなります。

var()の評価タイミングと無効な値の落とし穴

カスタムプロパティは、定義した時点では値の妥当性が厳密に判断されません。実際にvar()で呼び出され、通常のCSSプロパティの値として使われる段階で有効かどうかが問題になります。

【NG例】
:root {
  --main-size: large-text;
}

.title {
  font-size: var(--main-size);
}

--main-size自体は定義できますが、font-sizelarge-textという値は使えません。そのため、.titlefont-size指定は無効になります。

【OK例】
:root {
  --main-size: 1.5rem;
}

.title {
  font-size: var(--main-size);
}

フォールバックも仕様として理解する

var()には、参照先が未定義だった場合のフォールバック値を指定できます。

.badge {
  background-color: var(--badge-color, #eeeeee);
}

この記事では詳しい使い方よりも、仕様上の意味を押さえておきましょう。フォールバックは「未定義のときに使う代替値」です。定義済みでも、その値が使う先のプロパティとして不正な場合は、別の問題になります。

@propertyとは?型・継承・初期値を定義する仕組み

@propertyは、カスタムプロパティに対して、受け付ける値の型、継承するかどうか、初期値を指定できるCSSのアットルールです。

@property --accent-color {
  syntax: "<color>";
  inherits: false;
  initial-value: #0068b7;
}

この例では、--accent-colorを色として扱い、親から継承せず、初期値を#0068b7にしています。

syntax:受け付ける値の型

syntaxには、カスタムプロパティがどの種類の値を受け付けるかを書きます。

@property --angle {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

<color><length><angle>など、CSSの値の型を指定できます。

inherits:継承するかどうか

通常のカスタムプロパティは継承されますが、@propertyではinherits: false;を指定できます。

@property --card-shadow-strength {
  syntax: "<number>";
  inherits: false;
  initial-value: 1;
}

継承させたくない部品内の設定値を扱うときに便利です。

initial-value:初期値

initial-valueは、値が指定されていないときの初期値です。

@property --progress {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 0%;
}

アニメーションや状態管理で、初期状態を明確にしたい場合に役立ちます。

オススメ:HTML、CSSの問題集に無料で挑戦しよう!

HTML、CSSを勉強していると、調べれば分かることもありますが、実際に自分でアウトプットするのは難しいと感じたことはありませんか?
アウトプットするためのおすすめの方法は、
「問題解くこと」です。
忍者CODEのHTML、CSS 学習の無料問題集では、HTML、CSSに関する問題を100問以上用意しており、LINE登録するだけで解答を無料で確認できます!

@propertyを使ったアニメーション例

@propertyは、カスタムプロパティをアニメーションさせたいときに特に有効です。

@property --angle {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

.box {
  transform: rotate(var(--angle));
  transition: --angle 0.3s ease;
}

.box:hover {
  --angle: 8deg;
}

型が定義されているため、ブラウザが0degから8degへの変化を解釈しやすくなります。

@propertyは便利ですが、通常の色や余白を管理するだけなら必須ではありません。型・初期値・継承制御が必要な場面で使いましょう。

@property はもう実務で使って大丈夫?

以前は一部のブラウザで対応が遅れていた @property ですが、現在では Chrome、Safari、Edge、Firefox のすべての主要ブラウザで完全対応(Baseline達成)しています。

「特定のブラウザだけアニメーションが動かない」といった心配をすることなく、モダンなWeb制作の現場で安心してそのまま導入することが可能です。

関連記事:「CSSのtransitionとは?アニメーションの基本と効かない原因を解説

Sass変数とカスタムプロパティの違い

Sass変数とカスタムプロパティは、どちらも値を管理するために使われます。しかし、動くタイミングが大きく違います。

項目 CSSカスタムプロパティ Sass変数
動く場所 ブラウザ上 コンパイル時
カスケードの影響 受ける 受けない
継承 基本的に継承される CSS出力後は変数として存在しない
JavaScriptから変更 可能 直接は不可
メディアクエリ条件 var()は条件式に使えない コンパイル前なら変数として使える

使い分けの考え方

ブラウザ上でテーマや部品の見た目を切り替えたい値は、CSSカスタムプロパティが向いています。一方、ブレイクポイントやビルド時に決まる設計値は、Sass変数の方が扱いやすい場合があります。

カスタムプロパティは実行時に変わる値、Sass変数はコンパイル時に固定される値、と考えると使い分けやすくなります。


calc()とカスタムプロパティを組み合わせる

カスタムプロパティは、calc()と組み合わせて設計値を展開できます。

:root {
  --unit: 8px;
  --space-card: calc(var(--unit) * 3);
  --space-section: calc(var(--unit) * 8);
}

.card {
  padding: var(--space-card);
}

.section {
  padding-block: var(--space-section);
}

基準値を--unitとして持たせることで、余白体系をまとめて管理できます。

【実務例】デザインシステム(コンポーネント)での活用方法

カスタムプロパティを calc() と組み合わせる最大のメリットは、「コンポーネントのバリエーションを1つの共通設計から量産できる」点にあります。

たとえば、以下のように共通のカードコンポーネント(.c-card)に対してベースの計算ルールを作っておき、特定のクラスが付いたときだけ「変数の中身」を上書きする手法が、モダンなデザインシステムでは鉄則となっています。

/* ① 基本となるコンポーネントの共通設計 */
.c-card {
  --card-scale: 1; /* 倍率の初期値 */
  
  /* ベースの数値に変数の「倍率」を掛け算して余白や文字サイズを決める */
  padding: calc(16px * var(--card-scale));
  font-size: calc(1rem * var(--card-scale));
  border-radius: calc(4px * var(--card-scale));
}

/* ② 異なるサイズ(バリエーション)の量産 */
.c-card--small {
  --card-scale: 0.8; /* 小さめサイズ:全体の要素が0.8倍に連動 */
}

.c-card--large {
  --card-scale: 1.5; /* 大きめサイズ:全体の要素が1.5倍に連動 */
}

 なぜこの設計にするべきなのか?

これまでのCSSであれば、--small--large 用に、それぞれ paddingfont-size の具体的な数値を手動で計算して別々に書き直す必要がありました。
しかし、カスタムプロパティと calc() で計算式を1つ組んでおけば、「倍率(数字)」を変えるだけで、すべての余白や文字サイズがプロポーショナル(比率を保ったまま)に自動連動します。大規模なサイト設計において非常に保守性の高いコードが実現できます。

計算を複雑にしすぎない

calc()とカスタムプロパティを組み合わせると便利ですが、何段階も計算を重ねると、最終的な値が読み取りにくくなります。

デザインシステムでは、計算の柔軟性よりも「見て意味が分かる変数名」を優先した方が保守しやすい場合があります。

実務で起きるカスタムプロパティの落とし穴

落とし穴1:メディアクエリの条件式にvar()を使っている

var()は通常のプロパティ値として使うものであり、メディアクエリの条件式には使えません。

【NG例】
:root {
  --breakpoint-md: 768px;
}

@media (min-width: var(--breakpoint-md)) {
  .layout {
    display: grid;
  }
}

ブレイクポイントを変数管理したい場合は、Sassなどのビルドツールで管理するか、CSSでは直接値を書く必要があります。

落とし穴2:上書き元が分からなくなる

大規模CSSでは、同じカスタムプロパティが複数箇所で上書きされることがあります。

:root {
  --accent-color: #0068b7;
}

.lp-page {
  --accent-color: #e95420;
}

.price-card.is-recommend {
  --accent-color: #2f8f46;
}

このような設計では、検証ツールで実際にどの値が効いているか確認する習慣が重要です。

落とし穴3:循環参照に近い書き方をしている

カスタムプロパティ同士を参照するときは、依存関係が分かりやすいようにしましょう。

:root {
  --base-color: #0068b7;
  --button-color: var(--base-color);
}

この程度なら問題ありませんが、複数の変数が互いに参照し合うような構成は、保守時に追いにくくなります。

大規模CSSやデザインシステムでの使い方

大規模なサイトやデザインシステムでは、カスタムプロパティを「デザイントークン」のように扱うことがあります。

:root {
  --color-brand-primary: #0068b7;
  --color-brand-secondary: #e95420;
  --color-text-default: #222222;
  --space-100: 4px;
  --space-200: 8px;
  --space-300: 16px;
  --radius-sm: 4px;
  --radius-md: 8px;
}

このように、色、余白、角丸などの基準値をまとめると、複数ページや複数部品で一貫したデザインを保ちやすくなります。

グローバル値とコンポーネント値を分ける

:root {
  --color-brand-primary: #0068b7;
}

.button {
  --button-bg: var(--color-brand-primary);
  background-color: var(--button-bg);
}

グローバルなブランド色を直接すべての部品に使うのではなく、部品側の変数に受け渡すと、あとから部品単位で上書きしやすくなります。

オススメ:HTML、CSSの問題集に無料で挑戦しよう!
HTMLを勉強していると、調べれば分かることもありますが、実際に自分でアウトプットするのは難しいと感じることがあります。
忍者CODEのHTML、CSS 学習の無料問題集では、HTML、CSSに関する問題を用意しています。

公式ドキュメントも確認する

カスタムプロパティや@propertyはCSS仕様に関わる内容です。より正確に理解したい場合は、公式ドキュメントも参考にしてください。

まとめ:カスタムプロパティは仕様理解まで押さえると強い

この記事では、CSSカスタムプロパティについて、継承、スコープ、カスケード、@property、Sass変数との違いを中心に解説しました。CSSカスタムプロパティは、単なる値の使い回しではなく、CSSのカスケードと継承の中で動く仕様上の機能です。

基本的なCSS変数の使い方に慣れたら、次は「どこで定義され、どこへ継承され、どの値に上書きされるのか」を意識しましょう。さらに@propertyを使うと、型・継承・初期値を明確にでき、大規模CSSやデザインシステムでも扱いやすくなります。