ボタンの枠線、その中のアイコン、通知ボックスの帯やアイコン——ひとつの部品を作っていると、同じ色を何か所にも書くことになります。色違いのバリエーションを追加するたびに border-color と fill と background-color を並べて書き直すのは面倒ですし、どこか 1 つ書き換え忘れて色がちぐはぐになるのもよくある話です。そこで役に立つのが CSS の currentColor です。
currentColor は「その要素に効いている文字色」を指すキーワードで、色を受け取れるプロパティならどこにでも書けます。これを使うと部品の色指定が color の 1 行に集約され、バリエーションもホバーも color を書き換えるだけで済みます。この記事では、currentColor の仕組みと使えるプロパティ、ボタンや SVG アイコンでの実践例、カスタムプロパティ(CSS 変数)との使い分け、そして「色が変わらない」ときの原因までを、動かせるデモ付きで解説します。
目次
currentColor は「その要素に効いている color の値」を指すキーワード
MDN の説明どおり、currentColor キーワードはその要素の color プロパティの値を表します。#1a73e8 や rgb(26 115 232) と同じように「色の値」のひとつとして扱われるので、色を受け取るプロパティであれば、プロパティの種類を問わず書けます。
.box {
color: #1a73e8;
/* 以下の 3 つはすべて #1a73e8 になる */
border: 2px solid currentColor;
outline-color: currentColor;
box-shadow: 0 0 0 4px currentColor;
}
この例だけを見ると「#1a73e8 と 4 回書けばいいのでは」と思うかもしれません。currentColor の本当の価値は、色を変えるときに color の 1 か所だけ直せば全部が追従するところにあります。ホバー、フォーカス、色違いのバリエーション、ダークモードと、色を切り替える場面はいくらでもあるので、この差はすぐに効いてきます。
もうひとつ重要なのが、color は継承されるプロパティだという点です。親要素に color を指定すれば、その中の子孫要素にも同じ color が伝わります。つまり、子要素側で border-top: 1px solid currentColor; のように書いておけば、親から降りてきた文字色をそのまま参照できます。親の color を変えれば、その線の色も一緒に変わります。
border の色を省略すると文字色になるのは currentColor のおかげ
border: 1px solid; と色を書かずに枠線を指定すると、枠線が文字と同じ色で表示されます。これはブラウザーの気の利いた挙動ではなく、border-color の初期値が currentcolor だからです。border は一括指定プロパティなので、値を省略した border-color は初期値、つまり currentcolor にリセットされます。
つまり border: 2px solid currentColor; と border: 2px solid; は同じ結果になります。ただしチームで書くコードなら、currentColor と明示しておくほうが安全です。省略形は「色を書き忘れている」ようにも見えるため、あとから誰かが色を足してしまうことがあります。
初期値が currentcolor になっているプロパティは border-color だけではありません。主なものは次のとおりです。これらは色を指定しなければ、自動的に文字色が使われます。
| プロパティ | 役割と初期値の意味 |
|---|---|
border-color | 枠線の色。初期値が currentcolor なので、色を書かなければ文字色で描かれる |
border-block-color / border-inline-color | 論理プロパティ版の枠線の色。こちらも初期値は currentcolor |
text-decoration-color | 下線・取り消し線の色。初期値が currentcolor なので、既定では文字と同じ色になる |
column-rule-color | 段組み(マルチカラム)の段と段の間に引く線の色。初期値は currentcolor |
text-emphasis-color | 圏点(傍点)の色。初期値は currentcolor |
currentColor を書ける主なプロパティ
初期値が currentcolor ではないプロパティでも、色の値を受け取るものであれば currentColor と書けます。実務でよく使う組み合わせを整理しておきます。
| 書き方 | 使いどころ |
|---|---|
border-color: currentColor | 文字色に合わせた枠線。アウトラインボタンやタグの定番 |
background-color: currentColor | 文字色のベタ塗り。区切りの帯、インジケーター、下線代わりのバーなど |
outline-color: currentColor | フォーカスリングの色を部品の色に合わせる |
box-shadow: 0 2px 4px currentColor | 影の色。色の指定を省略した場合も currentColor が使われる |
text-decoration-color: currentColor | 下線の色を文字色に戻したいとき(初期値なので明示は任意) |
caret-color: currentColor | 入力欄のカーソル(キャレット)を文字色にそろえる |
fill: currentColor / stroke: currentColor | インライン SVG の塗りと線を文字色にそろえる。アイコンで最もよく使う |
box-shadow と text-shadow は少し特殊で、色の値を書かずに box-shadow: 0 2px 4px; と指定すると currentColor が使われます。ただし文字色そのままの影は多くの場合濃すぎるので、実際には rgb(0 0 0 / 0.15) のように別途指定するほうが自然です。「省略すると currentColor になる」という仕様を知っていれば、影の色が想定外に濃くなったときの原因にすぐ気付けます。
下のデモは、3 枚のカードでcolor の 1 行だけを変えているものです。枠線・上部の帯・アイコンの色は共通のクラスにまとめて書いてあり、カードごとの指定は一切ありません。CSS タブを開いて、色の指定がどこに 1 か所だけ書かれているかを確認してみてください。
<div class="cards">
<div class="card card--blue">
<div class="card__bar"></div>
<p class="card__title">
<svg class="card__icon" viewBox="0 0 24 24" aria-hidden="true">
<path d="M12 2 15.1 8.6 22 9.6l-5 5 1.2 7.1L12 18.3 5.8 21.7 7 14.6l-5-5 6.9-1z"/>
</svg>
color: #1a73e8
</p>
<p class="card__text">枠線・帯・アイコンの色は書いていません。</p>
</div>
<div class="card card--green">
<div class="card__bar"></div>
<p class="card__title">
<svg class="card__icon" viewBox="0 0 24 24" aria-hidden="true">
<path d="M12 2 15.1 8.6 22 9.6l-5 5 1.2 7.1L12 18.3 5.8 21.7 7 14.6l-5-5 6.9-1z"/>
</svg>
color: #188038
</p>
<p class="card__text">違うのは color の 1 行だけです。</p>
</div>
<div class="card card--red">
<div class="card__bar"></div>
<p class="card__title">
<svg class="card__icon" viewBox="0 0 24 24" aria-hidden="true">
<path d="M12 2 15.1 8.6 22 9.6l-5 5 1.2 7.1L12 18.3 5.8 21.7 7 14.6l-5-5 6.9-1z"/>
</svg>
color: #d93025
</p>
<p class="card__text">まとめて色が切り替わります。</p>
</div>
</div>
.cards {
display: flex;
flex-wrap: wrap;
gap: 16px;
padding: 16px;
font-family: sans-serif;
font-size: 14px;
}
/* 色に関する指定は、この共通クラスの中では一切書かない */
.card {
flex: 1 1 180px;
padding: 16px;
border-radius: 8px;
background: #fff;
/* border-color の初期値は currentcolor なので、色を省略すると文字色になる */
border: 2px solid;
}
.card__bar {
height: 6px;
margin-bottom: 12px;
border-radius: 3px;
/* 背景色にも currentColor が使える */
background-color: currentColor;
}
.card__title {
display: flex;
align-items: center;
gap: 6px;
margin: 0 0 8px;
font-weight: bold;
}
.card__icon {
width: 18px;
height: 18px;
/* インライン SVG の塗りも文字色に合わせる */
fill: currentColor;
}
.card__text {
margin: 0;
color: #555;
font-size: 13px;
}
/* 各カードで変えているのは color だけ */
.card--blue {
color: #1a73e8;
}
.card--green {
color: #188038;
}
.card--red {
color: #d93025;
}
currentColor を使わなければ、カードの種類ごとに border-color・background-color・fill を書くことになり、3 色なら 9 行に増えます。currentColor なら、色を増やすコストは 1 行だけです。
ボタンのホバーを color の 1 行だけで切り替える
currentColor が最も効くのが、状態によって色が変わるボタンです。枠線とアイコンを currentColor にしておけば、:hover や :focus-visible で書くのは color の 1 行で済みます。色違いのバリエーションも同じで、修飾クラスに color を書くだけで枠線もアイコンも付いてきます。
<div class="demo">
<p class="demo__note">マウスを載せると、文字色につられて枠線とアイコンの色も変わります。</p>
<div class="demo__row">
<button class="btn" type="button">
<svg class="btn__icon" viewBox="0 0 24 24" aria-hidden="true">
<path d="M12 21 3 12.3a5.4 5.4 0 0 1 7.6-7.6l1.4 1.3 1.4-1.3a5.4 5.4 0 1 1 7.6 7.6z"/>
</svg>
お気に入りに追加
</button>
<button class="btn btn--danger" type="button">
<svg class="btn__icon" viewBox="0 0 24 24" aria-hidden="true">
<path d="M6 7h12l-1 13H7zM9 4h6l1 2H8z"/>
</svg>
削除する
</button>
</div>
</div>
.demo {
padding: 16px;
font-family: sans-serif;
font-size: 14px;
}
.demo__note {
margin: 0 0 12px;
color: #555;
font-size: 13px;
}
.demo__row {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
.btn {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 10px 18px;
border-radius: 999px;
background: #fff;
font: inherit;
cursor: pointer;
/* この 1 行が枠線とアイコンの色の基準になる */
color: #1a73e8;
/* 枠線の色を書かない = currentColor */
border: 2px solid;
/* 色の変化をなめらかにする */
transition: color 0.2s, border-color 0.2s, background-color 0.2s;
}
.btn__icon {
width: 18px;
height: 18px;
fill: currentColor;
}
/* ホバーで変えるのも color だけ */
.btn:hover {
color: #0b47a1;
background-color: #eef4fd;
}
/* フォーカスリングの色も文字色に合わせる */
.btn:focus-visible {
outline: 2px solid currentColor;
outline-offset: 3px;
}
/* 色違いのボタンは color を上書きするだけで作れる */
.btn--danger {
color: #d93025;
}
.btn--danger:hover {
color: #a50e0e;
background-color: #fdeeec;
}
ポイントは、.btn の中に色の指定が color: #1a73e8; の 1 行しかないことです。枠線は border: 2px solid; と色を省略し、アイコンは fill: currentColor;、フォーカスリングは outline: 2px solid currentColor; にしてあります。そのため .btn:hover と .btn--danger では color を上書きするだけで、部品全体の色が切り替わります。
色の変化をアニメーションさせたいときは、transition: color 0.2s, border-color 0.2s, background-color 0.2s; のように、color だけでなく currentColor を使っているプロパティも並べて書いておくのが確実です。
インライン SVG のアイコンを文字色にそろえる
アイコンは currentColor の代表的な使い道です。SVG の塗りを決める fill 属性は色の値を受け取るので、fill="currentColor" と書いておけば、そのアイコンは置かれた場所の文字色で描かれます。ボタンの中に置けばボタンの色に、見出しの中に置けば見出しの色になる、という具合です。
配布されている素材は fill="#333333" のように色が埋め込まれていることが多いので、その部分を currentColor に書き換えたうえで、SVG のコードを HTML にそのまま貼り付けて使います。width と height は CSS 側で指定し、装飾目的のアイコンには aria-hidden="true" を付けて読み上げから外しておくと親切です。
<p class="note">
<svg class="icon" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M12 21 3 12.3a5.4 5.4 0 0 1 7.6-7.6l1.4 1.3 1.4-1.3a5.4 5.4 0 1 1 7.6 7.6z"/>
</svg>
この段落の文字色とアイコンの色がそろいます
</p>
fill は SVG の属性としても CSS のプロパティとしても書けます。属性に書き忘れた素材をまとめて扱いたいときは、CSS 側で指定するほうが確実です。SVG の fill 属性のようなプレゼンテーション属性は作成者のスタイルシートより弱いため、CSS の指定が優先されるからです。
.icon {
width: 1em; /* 文字サイズに連動させると収まりが良い */
height: 1em;
fill: currentColor;
vertical-align: -0.125em;
}
色は currentColor、大きさは em という組み合わせにしておくと、アイコンが周囲の文字にそのまま馴染むので、置く場所ごとの調整がほとんど要らなくなります。
通知ボックスを color の指定 1 行で作り分ける
お知らせ・成功・エラーのように、同じ形で色だけが違う部品も currentColor の出番です。枠線・左の帯・アイコンをすべて currentColor にまとめておけば、種類を増やすときに書くのは color の 1 行だけになります。
<div class="alerts">
<div class="alert alert--info">
<svg class="alert__icon" viewBox="0 0 24 24" aria-hidden="true">
<path d="M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm1 15h-2v-6h2zm0-8h-2V7h2z"/>
</svg>
<div class="alert__body">
<p class="alert__title">お知らせ</p>
<p class="alert__text">帯・枠線・アイコン・見出しの色を個別に指定していません。</p>
</div>
</div>
<div class="alert alert--success">
<svg class="alert__icon" viewBox="0 0 24 24" aria-hidden="true">
<path d="M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm-1 15-5-5 1.4-1.4L11 13.2l5.6-5.6L18 9z"/>
</svg>
<div class="alert__body">
<p class="alert__title">保存しました</p>
<p class="alert__text">修飾クラスで color を変えているだけです。</p>
</div>
</div>
<div class="alert alert--error">
<svg class="alert__icon" viewBox="0 0 24 24" aria-hidden="true">
<path d="M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm1 15h-2v-2h2zm0-4h-2V7h2z"/>
</svg>
<div class="alert__body">
<p class="alert__title">入力エラー</p>
<p class="alert__text">配色の追加は color を 1 行足すだけで済みます。</p>
</div>
</div>
</div>
.alerts {
display: grid;
gap: 12px;
padding: 16px;
font-family: sans-serif;
font-size: 14px;
}
.alert {
position: relative;
display: flex;
gap: 12px;
padding: 14px 16px 14px 24px;
border-radius: 8px;
background: #fff;
/* 枠線の色は指定しない(currentColor) */
border: 1px solid;
overflow: hidden;
}
/* 疑似要素は元の要素から color を受け継ぐので currentColor が使える */
.alert::before {
content: "";
position: absolute;
inset: 0 auto 0 0;
width: 6px;
background-color: currentColor;
}
.alert__icon {
flex: none;
width: 20px;
height: 20px;
margin-top: 2px;
fill: currentColor;
}
.alert__title {
margin: 0 0 4px;
font-weight: bold;
}
/* 本文だけは読みやすさのために色を上書きする */
.alert__text {
margin: 0;
color: #444;
font-size: 13px;
}
/* 種類ごとに変えるのは color の 1 行だけ */
.alert--info {
color: #1a73e8;
}
.alert--success {
color: #188038;
}
.alert--error {
color: #d93025;
}
CSS を見ると、.alert--info / .alert--success / .alert--error の中身が color だけになっているのが分かります。共通の .alert には枠線と ::before の帯、アイコンの fill が書かれていますが、いずれも色は currentColor です。
ここで効いているのが、疑似要素は生成元の要素から color を受け継ぐという性質です。.alert::before に background-color: currentColor; と書くと、その要素(.alert)の文字色が使われるので、帯の色も自動的にそろいます。::before や ::after で作る装飾と currentColor は相性が良い組み合わせです。
ひとつだけ注意したいのが、color は文字の色でもあるという点です。上のデモでは本文の .alert__text にだけ color: #444; を指定して、読みやすさを確保しています。部品のテーマ色をそのまま本文に使うと、色によっては文字が読みづらくなるので、長い文章の部分は個別に色を上書きしておくのが無難です。
カスタムプロパティ(CSS 変数)との違いと使い分け
「色をまとめて管理する」という話になると、--main-color のようなカスタムプロパティ(CSS 変数)を思い浮かべる方も多いはずです。どちらも色を一元管理できますが、性質はかなり違います。
| 比較 | currentColor | カスタムプロパティ |
|---|---|---|
| 値の出どころ | その要素に効いている color の値(固定) | 自分で定義した --名前 の値 |
| 持てる数 | 1 要素につき 1 色だけ | いくつでも定義できる |
| 入れられる値 | 色のみ | 色に限らず、サイズでも文字列でも何でも |
| 文字色への影響 | color がそのまま文字色になる | 定義しただけでは何も起きない |
| 定義の必要 | 不要(color があれば書ける) | あらかじめ --名前: 値; の定義が必要 |
使い分けの基準はシンプルで、その色が文字色と同じでよいなら currentColor、文字色とは別の色なら カスタムプロパティです。アウトラインボタンの枠線やアイコンのように、文字と一体で色が変わる要素は currentColor の独壇場です。一方で「文字は黒のまま、背景だけブランドカラー」のように文字色と切り離したい色は、color に縛られないカスタムプロパティのほうが素直に書けます。
両者は競合するものではなく、組み合わせて使うのが実用的です。サイト全体の色をカスタムプロパティで定義しておき、部品の中では color を起点に配る、という形にすると、色の管理とコードの短さを両立できます。
/* 1. サイト全体の色はカスタムプロパティで定義する */
:root {
--color-brand: #1a73e8;
--color-danger: #d93025;
}
/* 2. 部品では color に代入し、あとは currentColor で配る */
.btn {
color: var(--color-brand);
border: 2px solid;
}
.btn .icon {
fill: currentColor;
}
/* 3. バリエーションは color の代入先を差し替えるだけ */
.btn--danger {
color: var(--color-danger);
}
ダークモードへの対応も同じ考え方でまとめられます。prefers-color-scheme のメディアクエリで color(またはその元になるカスタムプロパティ)を差し替えれば、currentColor を使っている枠線もアイコンも一緒に切り替わります。
@media (prefers-color-scheme: dark) {
:root {
/* ここを変えるだけで、currentColor を使った箇所はすべて追従する */
--color-brand: #8ab4f8;
}
}
currentColor を使ったのに色が変わらないとき
currentColor がうまく効かない場面には、決まったパターンがあります。原因が分かれば対処も決まっているので、順に見ていきます。
img タグで読み込んだ SVG には外側の色が届かない
一番よく遭遇するのがこれです。fill="currentColor" と書いた SVG ファイルを <img src="icon-heart.svg"> として読み込むと、周囲の文字色をいくら変えてもアイコンは黒いままです。
理由は、<img> で読み込まれた SVG がページ本体とは別の文書として扱われるためです。読み込み元のページに書いた CSS は SVG の中には届かず、SVG 側から見た color は自分自身の値だけになります。そこに color の指定がなければ初期値(多くの環境では黒)が使われ、結果としてアイコンは黒く描かれます。
対処は、SVG のコードを HTML に直接書き込む(インライン SVG にする)ことです。同じ文書の一部になれば、周囲の color がそのまま継承されます。
background-image に指定した SVG でも currentColor は効かない
background-image: url("icon.svg"); のように背景画像として読み込む場合も、理由は <img> のときと同じです。data: URI で SVG を直接埋め込んだ場合も、やはり別の文書なので currentColor はページ側の文字色を参照できません。セレクトボックスの矢印などを背景画像で描いていて、色を変えられずに困るのはこのパターンです。
解決策は、画像を「絵」ではなく「型(マスク)」として使うことです。mask-image で SVG の形に切り抜き、実際の色は background-color: currentColor; で塗ります。こうすれば色を決めているのはページ側の CSS なので、文字色にきちんと追従します。
.icon-mask {
display: inline-block;
width: 1em;
height: 1em;
/* 色はこちらで決める */
background-color: currentColor;
/* SVG は形を切り抜く型として使う(接頭辞付きを先に書く) */
-webkit-mask: url("icon-heart.svg") no-repeat center / contain;
mask: url("icon-heart.svg") no-repeat center / contain;
}
ただしこの方法が使えるのは、単色のシルエットで表現できるアイコンに限られます。多色のイラストをマスクにすると塗り分けが失われて 1 色になるので、その場合はインライン SVG を選んでください。
SVG の中の path に色が直接書かれている
インライン SVG にしたのにアイコンの色が変わらない場合は、SVG の中身を確認してください。<path fill="#333333" /> のように、子要素そのものに色が指定されていることがよくあります。
この状態で svg { fill: currentColor; } と書いても、それは <svg> 要素に対する指定でしかありません。<path> は親から fill を継承しますが、自分自身に fill が指定されていれば、そちらが継承より優先されます。結果として #333333 のまま描かれるわけです。
対処は 2 つです。SVG 側の fill 属性を削除して親からの継承に任せるか、CSS のセレクターを <path> 自身に届くように書き換えるかです。素材をそのまま使いたいときは後者が手軽です。
/* path の fill 属性ごと上書きする。
SVG のプレゼンテーション属性は CSS より弱いので、こちらが勝つ */
.icon path,
.icon circle,
.icon rect {
fill: currentColor;
}
color プロパティ自身に currentColor を書いても自分の色にはならない
color: currentColor; という書き方は、循環しそうに見えて実は明確に定義されています。MDN の説明によれば、currentColor が color プロパティの値として使われた場合は、color が継承した値(親要素の文字色)が使われます。color: inherit; と書いたのと同じ結果になる、と考えて構いません。
つまり「自分の色を自分で参照する」わけではないので、期待した色にならないことがあります。紛らわしいので、親の色をそのまま使いたいときは素直に color: inherit; と書くのがおすすめです。とくにボタンやリンクなど、ブラウザーの既定スタイルが色を当ててくる要素で「親の文字色に戻したい」場面では color: inherit; が定番になります。
まとめ
currentColor は、その要素に効いている color の値を指すキーワードです。色を受け取るプロパティならどこにでも書けるので、border-color・background-color・outline-color・box-shadow・インライン SVG の fill や stroke といった色指定を、color の 1 行に集約できます。border-color や text-decoration-color のように初期値が currentcolor のプロパティは、色を書かないだけで自動的に文字色に追従します。
ホバーや色違いのバリエーション、ダークモードなど「色が切り替わる」場面ほど効果が大きく、書く量も書き換え漏れも確実に減ります。文字色と連動させたい色は currentColor、文字色とは独立した色はカスタムプロパティ、と役割を分けて使うのがコツです。色が変わらないときは、SVG を <img> や背景画像で読み込んでいないか、<path> に fill が直接書かれていないかをまず疑ってください。