CSSの変数って結局どれを使えばいいのか。現場で一回は迷います。
CSSカスタムプロパティ(var)が出てきてから、Sassの変数やmixinの立場が変わった。変わったのに、昔の成功体験もまだ強い。結果として、プロジェクトごとに流派が分かれる。あるあるです。
ただ、どっちが上とか下とか、そういう話にしたくない。役割が違う。強みが違う。合う場面も違う。そこを押さえれば、迷いは減ります。
この記事は、CSSカスタムプロパティとSassの違いを、実務の匂いがする視点で整理します。なぜ今この比較が必要なのか。どう書くと事故が減るのか。メリットとデメリット。どこで混ぜるべきか。混ぜるときの地雷まで。
読んだあとに、あなたのプロジェクトの設計が少しだけ整う。そんな方向でいきます。
- なぜ今さら「違い」を整理する必要があるのか
- まず前提: CSSカスタムプロパティとSassは同じものではない
- CSSカスタムプロパティとは: ブラウザで動く変数
- Sassとは: CSSを作るための言語
- できること できないことを一発で見分ける表現
- メリットとデメリットをもう少し実務寄りに掘る
- やり方: どっちをどう使うかの現実的な答え
- やり方: ダークモードとユーザー設定で差が出る場面
- やり方: レスポンシブの値はどっちに持つべきか
- 地雷: 両方使うときに起きがちな事故
- メリット比較: どっちが速い どっちが軽い
- デメリット比較: 保守とチーム運用で差が出る
- 実務でのおすすめ構成: 迷いを減らすテンプレ
- 読者に有益な追加情報: どっちを選ぶかで迷った時の質問
- カスタムプロパティの小技: 使うと仕事が少しだけ楽になる
- Sassの小技: 現場で効く地味な整え方
- 最後に: 個人の偏見 どっちが好きか
なぜ今さら「違い」を整理する必要があるのか
理由は単純で、CSSが強くなったからです。
昔は、変数もネストも計算も、だいたいSassの担当だった。CSSは素朴で、頼れるのはプリプロセッサ。ところが今は、CSS側に変数がある。計算もある。条件分岐っぽいことも増えた。しかもブラウザ上で動く。
それでもSassが不要になったかというと、そうでもない。ビルド時に出来ることが強いし、コードベースを整理する道具として優秀です。
だから現場で起きるのがこのパターン。
- 全部Sassで書いてきたが、ダークモードが面倒になった
- CSS変数を導入したが、設計が崩れて変数だらけになった
- 両方混ぜたら、どこが真実の値なのか分からなくなった
このあたりで人は疲れます。疲れると、雑にコピペして増殖が始まる。最悪のループ。
なので、ここで一度、役割分担を決めておく。これが効きます。
まず前提: CSSカスタムプロパティとSassは同じものではない
似ているようで、根っこが違います。
CSSカスタムプロパティは、ブラウザが解釈して、実行時に値が決まる変数です。DOMやCSSの状態によって値が変わる。生きている変数。
Sassは、ビルド時にCSSを生成するための道具です。変数もmixinもfunctionも、最終的には普通のCSSに変換されます。生成のための変数。
この違いが、そのままメリットとデメリットになる。ここが肝。
CSSカスタムプロパティとは: ブラウザで動く変数
カスタムプロパティはこう書きます。
:root {
--color-primary: #1e40af;
--space-4: 16px;
}
.button {
background: var(--color-primary);
padding: var(--space-4);
}
ポイントは、値がCSSのカスケードに乗ること。つまり、スコープがある。上書きできる。状況で変えられる。
カスタムプロパティの強いところ
- 実行時に値を切り替えられる
- カスケードと継承で上書き出来る
- JSから書き換え可能
- テーマ切り替え(ダークモード等)に強い
- コンポーネント単位で局所的に変えられる
たとえば、ダークモード。これが一番分かりやすい勝ち筋です。
:root {
--bg: #ffffff;
--fg: #111111;
}
[data-theme="dark"] {
--bg: #0b0b0b;
--fg: #f2f2f2;
}
body {
background: var(--bg);
color: var(--fg);
}
これ、Sassの変数だけでやろうとすると、基本的にはCSSを二系統生成するか、クラスを大量に作る方向になります。出来なくはないが、しんどい。
カスタムプロパティの弱いところ
- 型や単位のミスが実行時まで気づきにくい
- ビルド時最適化(未使用削除など)の設計が必要
- 変数の依存関係が複雑になると追えなくなる
- 値の最終形がデバッグ前提になることがある
カスタムプロパティは便利です。便利だからこそ、雑に増えます。増えると、意味が薄い変数が増殖する。space-13みたいなやつ。誰が使うの、それ。そういう未来が見える。
なので、設計が大事。ここは本当に大事。言葉を変えると、ルールがないと破綻しやすい。
Sassとは: CSSを作るための言語
Sassはこういうやつです。
$primary: #1e40af;
$space-4: 16px;
.button {
background: $primary;
padding: $space-4;
}
これが最終的に普通のCSSになります。ブラウザにはSassの変数は残りません。ここがカスタムプロパティとの決定的な差です。
Sassの強いところ
- ビルド時に計算や分岐、ループが出来る
- mixinやfunctionで再利用設計がしやすい
- ファイル分割やモジュール化が得意
- タイポや単位ミスをビルドで弾けることがある
- 既存資産が多い(現場での運用ノウハウが豊富)
例えば、ユーティリティクラスを自動生成したい。こういう場面でSassは強い。
$spaces: (0, 4, 8, 12, 16, 24, 32);
@each $s in $spaces {
.mt-#{$s} { margin-top: #{$s}px; }
}
CSSでも似たことは出来るようになってきましたが、プロジェクトの整備としてはSassのほうが扱いやすい場面が多い。
Sassの弱いところ
- 実行時に値を切り替えられない(生成されたCSSは固定)
- テーマ切り替えやユーザー設定に弱い
- ビルド環境が必須
- ネスト多用でCSSが肥大化しやすい
Sassのネストは麻薬です。気持ちいい。だが深く潜ると戻れない。結果としてセレクタが太り、上書きが増え、メンテが地獄になる。何度も見た。
できること できないことを一発で見分ける表現
ここで、判断を雑にしていい基準を置きます。
CSSカスタムプロパティが得意
- ダークモードやテーマ切り替え
- コンポーネントごとの見た目の変化(親で上書き)
- JS連動のアニメーションやインタラクション(値を動かす)
- メディアクエリやコンテナクエリで値を切り替える
Sassが得意
- 設計をパッケージ化する(mixin, function)
- ループやマップでCSSを生成する
- 共通の計算やフォーマットを強制する
- 大規模なファイル分割と整理
カスタムプロパティは動的。Sassは生成。これだけ覚えておけば、半分は勝ちです。
メリットとデメリットをもう少し実務寄りに掘る
理屈は分かった。で、現場でどんな差が出るのか。
カスタムプロパティのメリット 実務編
- デザインシステムのテーマを差し替えやすい
- 白黒だけじゃなく、ブランドカラー別の展開がしやすい
- コンポーネントを別案件に持って行きやすい
- CSSだけで変更が完結しやすい
カスタムプロパティのデメリット 実務編
- 命名が雑だと破綻する(意味のない変数地獄)
- 値の追跡に慣れていない人が迷子になる
- 最終値がどこで決まったか、慣れるまで時間がかかる
あなたのチーム、変数の命名規則ありますか。カオスになっていませんか。
Sassのメリット 実務編
- 運用ルールをコードで押し付けられる
- mixinで統一したレイアウトやタイポが作りやすい
- 共通設計の再利用が楽(特に複数案件)
- ビルドで失敗が見えることがある
Sassのデメリット 実務編
- ビルド環境の維持が必要(Viteなどとセット運用)
- テーマ切り替えが苦手
- 設計をミスるとCSSが増えやすい
個人的には、Sassの最大の価値はmixinでも変数でもなく、チームの癖を一定に整える道具になれるところ。ここに偏愛があります。
やり方: どっちをどう使うかの現実的な答え
一番現実的なのは、両方使うことです。
ただし混ぜ方が雑だと、事故が増えます。なので、混ぜるなら役割分担を決める。
おすすめの役割分担(現場向け)
- Sass: ファイル構造、mixin、function、生成系、設計の土台
- CSSカスタムプロパティ: テーマ、可変値、コンポーネントの微調整、実行時の差し替え
例を出します。Sassで設計を作って、値はカスタムプロパティに逃がす。これが一番バランスが良いと感じています。
/* tokens.css */
:root {
--radius: 12px;
--shadow: 0 10px 30px rgba(0,0,0,.08);
--space-2: 8px;
--space-3: 12px;
}
/* component.scss */
.card {
border-radius: var(--radius);
box-shadow: var(--shadow);
padding: var(--space-3);
}
これなら、テーマ切り替えでradiusやshadowを変えられる。しかもコンポーネント側は読みやすい。
逆に、Sass変数で全部固定してしまうと、あとからダークモードやテーマ展開の要求が来たときに詰みやすい。あるあるです。
やり方: ダークモードとユーザー設定で差が出る場面
テーマ切り替えは、カスタムプロパティの主戦場です。
:root {
--bg: #ffffff;
--fg: #111111;
--link: #1e40af;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #0b0b0b;
--fg: #f2f2f2;
--link: #93c5fd;
}
}
この書き方は、ユーザーのOS設定に寄り添えます。押し付けない。これが良い。
Sassでこれをやるなら、dark用のCSSを別で出すか、darkクラス前提のCSSを生成する。どちらも出来るが、運用コストが上がりやすい。
やり方: レスポンシブの値はどっちに持つべきか
ここが揉めポイントです。現場でよくある。
ブレイクポイントやfont-sizeのスケール。Sassで完結したくなる。だが、カスタムプロパティを使うと後から調整しやすい。
カスタムプロパティでレスポンシブを扱う例
:root {
--container: 1100px;
--space: 16px;
}
@media (max-width: 767px) {
:root {
--container: 92vw;
--space: 12px;
}
}
.wrapper {
width: min(100%, var(--container));
padding-inline: var(--space);
}
こういうの、後から調整が速い。特に運用で微調整が多いサイトに向く。
Sassでレスポンシブを扱う例
$bp-sp: 767px;
@mixin sp {
@media (max-width: $bp-sp) { @content; }
}
.wrapper {
width: min(100%, 1100px);
padding-inline: 16px;
@include sp {
width: 92vw;
padding-inline: 12px;
}
}
読みやすさはSass側が勝ちやすい。チームの好みも出ます。僕はmixinでメディアクエリを統一するのが好きだ。
地雷: 両方使うときに起きがちな事故
混ぜるなら、ここを避けたい。
地雷1: 同じ概念をSass変数とCSS変数で二重管理
$primaryと–primaryが両方存在する。しかも微妙に値が違う。はい終わり。
色や余白など、意味が同じなら、どちらかが真実の値になるように決める。ここは絶対に必要。
地雷2: カスタムプロパティを増やし過ぎて設計が崩れる
変えたいから作る。分かる。だが、全部を変数にすると読めなくなる。
個人的ルールとしては、デザイントークンとして意味があるものだけを変数にします。コンポーネント固有の一回きりの値は、普通に直書きする。割り切り。
地雷3: 変数のスコープが意図せず漏れる
カスタムプロパティはカスケードに乗る。便利。だが漏れる。
親で上書きしたつもりが、想定外の子にも影響することがある。これを防ぐには、コンポーネントのルートで変数を閉じる設計が効く。
.card {
--card-bg: #fff;
background: var(--card-bg);
}
.card.is-warning {
--card-bg: #fff7ed;
}
card専用の変数をcard内で完結させる。こうすると事故が減ります。
地雷4: Sassのネストが深くなって上書き合戦
ネストは深くするほどCSSの詳細度が上がる。上がると上書きが必要になる。上書きが増えると、また詳細度を上げる。地獄の階段。
ネストは2段くらいで止める。これも僕の偏見。だが、だいたい当たる。
メリット比較: どっちが速い どっちが軽い
よくある疑問に答えます。
CSSカスタムプロパティはブラウザで解決されるので、動的に変えられる。そのぶん、値の評価が実行時に行われます。ただ、普通のサイトでこれがボトルネックになることは多くない。よほどアニメーションで大量に変数を動かすとか、極端なケースです。
Sassはビルドで固定されるので、実行時の負担は少ない。その代わり、CSSが増えやすい設計だとファイルサイズが太る。これが実害として出やすい。
つまり、速度や軽さで単純に勝敗はつかない。設計と運用が勝敗を決めます。
デメリット比較: 保守とチーム運用で差が出る
忙しい現場だと、保守性の差が一番効きます。
Sassは、設計の枠を作りやすい。mixinやfunctionで統一ができる。新しい人が入っても、ルールに乗せられる。
カスタムプロパティは、自由度が高い。自由は便利。だが、自由は破綻も呼ぶ。命名とスコープ、用途の整理が出来ていないと、数か月後に読めなくなる。
あなたの案件、半年後に別の人が触る可能性ありますか。未来の誰かを助けるなら、Sassの枠組みは効く。そう感じています。
実務でのおすすめ構成: 迷いを減らすテンプレ
僕がよく採用する形を置きます。好みです。
1) デザイントークンはCSSカスタムプロパティ
- 色
- 余白
- 角丸
- 影
- フォントサイズの段階
2) レイアウトの再利用はSassのmixin
- メディアクエリ
- コンテナ幅
- よく使うレイアウトパターン
3) コンポーネント固有の可変値はコンポーネント内のカスタムプロパティ
card専用、button専用の変数を閉じる。漏らさない。
4) 変数命名のルールを決める
ルールがないと増える。増えると死ぬ。これは断言。
例として、グローバルは–color-xxxや–space-xxx。コンポーネント専用は–card-xxxや–btn-xxx。こういう感じ。
読者に有益な追加情報: どっちを選ぶかで迷った時の質問
迷ったら、この2つを自分に聞くと良い。
この値は、実行時に変わる必要があるか。テーマや状態で変わるか。変わるならカスタムプロパティの出番。
この書き方は、チームのルールとして固定したいか。繰り返し使う形か。固定したいならSassの出番。
そしてもう一つ。あなたは今、変数を増やしたいだけではありませんか。
変数を増やすと気持ちいい。整理した気になる。だが整理とは違う。名前の付いた散らかしになることもある。ここは自戒です。
カスタムプロパティの小技: 使うと仕事が少しだけ楽になる
clampと組み合わせてタイポを滑らかに
:root {
--fs-1: clamp(14px, 1.2vw, 16px);
--fs-2: clamp(18px, 1.8vw, 22px);
}
body { font-size: var(--fs-1); }
h2 { font-size: var(--fs-2); }
これ、後から調整がラクです。Sassで計算して固定値を出すより、運用で強いと感じる場面が多い。
JSでテーマを切り替える時も素直
document.documentElement.dataset.theme = "dark";
これでCSS側が反応する。CSSが主体になる。好きなやり方です。
Sassの小技: 現場で効く地味な整え方
メディアクエリはmixinで統一
$bp-sp: 767px;
@mixin sp {
@media (max-width: $bp-sp) { @content; }
}
書き方が揃うと、レビューが速い。これが大きい。
mapでデザイントークンを管理しやすくする
$spaces: (
1: 4px,
2: 8px,
3: 12px,
4: 16px
);
@function space($n) {
@return map-get($spaces, $n);
}
.box { padding: space(4); }
こういう道具立てがあると、場当たりの値が減る。結果としてCSSが痩せます。
最後に: 個人の偏見 どっちが好きか
僕はどっちも好きです。ずるい答え。
ただ、カスタムプロパティの登場で、CSSはもうプリプロセッサの補助輪を外し始めたと思っています。テーマ切り替え、状態管理、設計の柔軟性。これは強い。
一方で、Sassのmixinやfunctionで設計を整えるのは、チーム開発の現実に刺さる。ルールで守る。速さと安全の話。
なので、僕の推しは役割分担です。Sassで骨格を作り、カスタムプロパティで血を通わせる。そんな感じ。
あなたの現場はどっち寄りですか。ダークモードやブランド展開が増えそうなら、カスタムプロパティに寄せると楽になるかもしれない。複数人で保守するなら、Sassの枠で整えると助かるかもしれない。迷いはゼロにならないが、迷う時間は減らせます。

