CSSのattr()で、HTMLの属性値を数値や色として使う方法
CSS の attr() 関数に型や単位を書き添えると、HTML の属性の値を、数値や長さ、色として使えます。data-value="72" から棒の長さを、data-color="#00c8e8" から背景色を決める、といったことが CSS だけで書けます。値ごとにクラスを作ったり、JavaScript で style を書き換えたりしなくて済みます。
.bar {
width: calc(attr(data-value type(<number>), 0) * 1%);
background-color: attr(data-color type(<color>), #9aa5ab);
}
検証環境は Ubuntu 24.04、Node.js 22.23.4、Playwright 1.63.0、Chromium 153 です。ページを Playwright で開き、getComputedStyle() で計算値を調べました。Firefox や Safari など、Chromium 以外のブラウザーでの表示は試していません。各ブラウザーの対応バージョンは、互換性データ(@mdn/browser-compat-data 8.1.8)の値です。
型を書かない attr() は、いつも文字列になる
これまでの attr() は、content: attr(data-label) のように、属性の値を文字列として擬似要素に出す使い方が中心でした。型を書かないと、値は今でも文字列として扱われます。
検証環境で width: 40px; のあとに width: attr(data-w); と書き、data-w="120" の要素に当てたところ、幅は 120px にも 40px にもならず、auto(親の幅いっぱいの 500px)になりました。文字列の "120" は width の値として使えないので、宣言が計算値の時点で無効になり、初期値が使われたためです。MDN にも、型を省いたときは属性の値が CSS の文字列として扱われると書かれています。
数値や色として使いたいときは、属性名のあとに、どう読むかを書きます。書き方は2通りあります。
| 書き方 | 読み方 | 例 |
|---|---|---|
| 単位を書く | 数値として読み、その単位を付ける | attr(data-w px)、attr(data-r deg)、attr(data-p %) |
type() を書く |
書いた型として読む | attr(data-color type(<color>))、attr(data-v type(<number>)) |
type() に書ける型は、<number>、<integer>、<length>、<percentage>、<angle>、<color> などです。一覧は MDN の type() のページにあります。
単位を書いて、数値を長さや角度にする
属性に単位のない数値を書いておき、CSS 側で単位を付ける書き方です。
<div class="box" data-w="120" data-r="-3">…</div>
.box {
width: attr(data-w px, 10px);
rotate: attr(data-r deg, 0deg);
}
検証環境では、width が 120px、rotate が -3deg になりました。attr(data-p %, 0%) に data-p="40" を当てると、幅 500px の親の中で 200px になりました。カンマのあとの 10px などは、属性が読めなかったときの代わりの値です。
この書き方では、属性の値は単位のない数値でなければなりません。同じ attr(data-w px, 10px) で、属性の書き方を変えて試した結果です。
data-w の値 |
width の計算値 |
|---|---|
120 |
120px |
1e2 |
100px |
120px |
10px(代わりの値) |
80(前後に空白) |
10px(代わりの値) |
空(data-w="") |
10px(代わりの値) |
120px のように単位まで書いた値は、数値として読めないので代わりの値になりました。単位も属性に書きたいときは、次の type() を使います。前後に空白がある値も、検証環境の Chromium では代わりの値になったので、属性には数値だけを書くようにします。
type() で、長さや色として読む
type(<length> | <percentage>) のように書くと、属性に書いた単位ごと読み込めます。| で区切って、どれかに合えばよい型を並べられます。
.box {
width: attr(data-size type(<length> | <percentage>), 30px);
}
幅 500px の親の中で試したところ、data-size="50%" は 250px、data-size="120px" は 120px になりました。data-size="2em" は、font-size: 20px の要素で 40px になり、em はその要素の文字の大きさで計算されました。単位のない data-size="120" は、長さでも割合でもないので、代わりの値の 30px になりました。
色は type(<color>) で読みます。
.chip {
background-color: attr(data-color type(<color>), #9aa5ab);
}
data-color の値 |
background-color の計算値 |
|---|---|
#00c8e8 |
rgb(0, 200, 232) |
tomato |
rgb(255, 99, 71) |
rgb(216 88 120 / 50%) |
rgba(216, 88, 120, 0.5) |
みずいろ |
rgb(154, 165, 171)(代わりの値) |
| 属性なし | rgb(154, 165, 171)(代わりの値) |
16進数、色の名前、rgb() のどれでも、CSS に書くのと同じ書き方なら読めました。色として読めない値や、属性そのものがない場合は、代わりの値になりました。
type(<integer>) は整数だけを受け付けます。grid-column: span attr(data-span type(<integer>), 1); に data-span="3" を当てると、span 3 になり、幅 50px の列を3つまたいで 150px になりました。data-span="1.5" は整数ではないので、代わりの値の span 1 になりました。z-index も、data-z="5" から 5 になりました。
calc() や色の関数の中で使う
type(<number>) で読んだ数値は、calc() や hsl() の中に入れられます。単位を書く書き方では表せない計算も、これでできます。
.bar {
width: calc(attr(data-value type(<number>), 0) * 1%);
}
.label {
color: hsl(attr(data-hue type(<number>), 0) 100% 40%);
}
幅 500px の親の中で、data-value="72" の .bar は 360px になりました。data-hue="190" の .label は rgb(0, 170, 204) の青緑になりました。data-value="abc" のように数値でない値は、代わりの 0 が使われ、幅は 0px でした。
次の画像は、最初に挙げた、棒の長さと色を属性だけで決める例を、検証環境で表示したものです。棒は、幅 282px の枠の中で、data-value の割合の長さになりました。data-color を書いていない4本目は、代わりの灰色になっています。数字の表示は、::after の content: attr(data-value) "%" で出しています。

属性の値を JavaScript で変えると、表示もその場で変わります。検証環境で setAttribute('data-w', '200') を実行すると幅は 200px に、dataset.color = 'gold' とすると背景色は rgb(255, 215, 0) になりました。
代わりの値を書かないと、前の宣言には戻らない
属性が読めず、代わりの値も書いていないとき、attr() は前に書いた宣言に戻りません。
.box {
width: 40px;
width: attr(data-w px);
}
data-w のない要素で試したところ、width は 40px ではなく、auto(親の幅いっぱいの 500px)になりました。CSS Values 5 の仕様では、型を書いた attr() で代わりの値がないときは、宣言が計算値の時点で無効になるとされています。そのときの値は、プロパティが継承されるかどうかで変わりました。継承されない width は初期値の auto になりました。継承される color で、color: rgb(255, 0, 0); のあとに color: attr(data-color type(<color>)); と書き、読めない値を当てたところ、赤ではなく親要素の色になりました。
前の値を使いたいときは、その値を attr() のカンマのあとに書きます。
カスタムプロパティに入れるときは、どの要素の属性かに注意する
attr() は、カスタムプロパティに入れても使えます。ただし、属性を読むのは、そのカスタムプロパティを書いた要素です。
.parent { --w: attr(data-w px, 10px); }
.child { width: var(--w); }
data-w="120" を .child に書いて試すと、.child の幅は 10px でした。--w は、data-w のない .parent で値が決まり、代わりの値の 10px が子に継承されたためです。同じ要素に --w: attr(data-w px, 10px); width: var(--w); の両方を書いた場合は、120px になりました。
MDN にも、attr() を使ったカスタムプロパティを親に書くと、子には親の属性から作った値が継承されると説明されています。カスタムプロパティは、属性を持つ要素そのものに書くようにします。
属性の値は URL には使えない
attr() は、ページ上のどの属性でも読めます。そのため、属性の値を URL に使うことは仕様で禁止されていて、MDN にもそう書かれています。仕様によると、スクリプトが使うトークンのような属性の情報を、CSS だけで組み立てた URL から外部へ送れてしまうのを防ぐためです。
検証環境でも、background-image: image-set(attr(data-icon type(<string>))); に data-icon="a.png" を当てると、background-image は none になりました。image-set("a.png") と直接書いた場合は、background-image の計算値が image-set(url("a.png") 1dppx) になります。アイコンの画像を属性で切り替えたいときは、[data-icon="help"] のような属性セレクターで、ルールを書き分けます。
対応していないブラウザーへの備え
互換性データでの対応状況は次のとおりです。単位を書く書き方は、互換性データに別の項目がないため、表には載せていません。
| ブラウザー | type() を書いた attr() |
|---|---|
| Chrome・Edge(Android の Chrome を含む) | 133 から |
| Firefox(Android を含む) | 155 から |
| Safari | Technology Preview のみ |
| iOS の Safari | 対応していない |
対応していないブラウザーは、型を書いた attr() の宣言を読み飛ばします。MDN の色の例では、ふつうの値の宣言を先に書き、そのあとに attr() の宣言を書いています。
.chip {
background-color: #9aa5ab;
}
.chip[data-color] {
background-color: attr(data-color type(<color>), #9aa5ab);
}
型を書く今の attr() の書き方に対応しているかは、@supports (x: attr(x type(*))) で調べられると MDN に書かれています。検証環境の Chromium 153 でも、CSS.supports('x: attr(x type(*))') は true でした。
@supports (x: attr(x type(*))) {
/* 型を書いた attr() を使うルール */
}
なお、仕様の最新の草案には、attr(data-v number) のように数値として読む number というキーワードも書かれています。検証環境の Chromium 153 では、calc(attr(data-v number, 0) * 1px) に data-v="33" を当てると 33px になりました。ただし、MDN の attr() のページや互換性データにはまだ載っていないため、この記事では type(<number>) を使っています。





