🏷️ CSS attr()がtype()変換で全プロパティ対応に — data属性がやっとCSSの「入力」になった

Chrome 133でattr()が刷新され、content限定・文字列のみだった制約が消えた。data属性を数値・色・角度としてどのプロパティにも流し込める。url()が意図的に禁止されたセキュリティ設計も含めて整理する。

読む深さ

attr() というCSS関数、使ったことありますか。「あるよ、content: attr(title) でツールチップもどきを作るやつでしょ」——そう、長いことそれしかできなかった関数です。

content プロパティ限定、返るのは文字列のみ。width: attr(data-width) と書きたくなって、動かなくて、「ああそういえば仕様だった」と思い出す。CSS歴が長い人ほど一度は踏んでいる罠だと思う。この制約がChrome 133(2025年2月4日Stable)で消えた。

data属性がスタイルの入力チャネルになる

新しいattr()は任意のプロパティで使えて、type() で型を指定できる。

/* data-cols="4" をグリッド列数として解釈。無ければ3 */
.grid {
  grid-template-columns: repeat(attr(data-cols type(<integer>), 3), 1fr);
}

/* data-brand="#e91e63" をそのまま色に */
.card { background-color: attr(data-brand type(<color>), gray); }

/* 単位だけ与えるショートハンドもある */
div { rotate: attr(data-rotation deg, 0deg); }

第2引数はフォールバックで、属性が無い・型として解釈できない場合に使われる。これまでこの手の「HTML側の値でスタイルを変える」は、CMSやJSでインラインの style="--cols: 4" を書き出すのが定石だった。カスタムプロパティ経由の間接接続。それが data-cols="4" という意味の通った属性のまま、直接CSSに読めるようになったわけだ。

CMSのテンプレートを書く人間としては、これが一番うれしい。style 属性はサニタイザーに剥がされがちだが、data-* は通ることが多い。データはHTMLに、見た目の解釈はCSSに、という役割分担が崩れない。

url()が使えないのは手抜きではなく設計

ひとつ、意図的にできないことがある。url()の中でattr()は使えない

/* これは仕様として無効 */
.avatar { background-image: url(attr(data-src)); }

不便に見えるけど、理由を知ると納得する。属性値はユーザー入力由来であることが多い、つまり信頼できない値だ。それがリクエストURLに化けられると、data-x="https://evil.example/?leak=..." のような値を仕込むだけでCSS経由の外部送信チャネルが生まれる。「信頼できない入力」と「外部送信」を同じ場所に置かない。仕様策定の段階でこの組み合わせを潰してあるのは、プラットフォームの学習の成果だと思う。

使うなら@supportsで包んで

現在地も正直に書いておく。対応はChromium系のみで、FirefoxとSafariは未対応、Baseline未達だ。全ブラウザで信じていいのは今も従来の content: attr(x) だけ。なので実戦投入するなら段階的強化になる。

.grid { grid-template-columns: repeat(3, 1fr); } /* 既定 */

@supports (grid-template-columns: repeat(attr(data-cols type(<integer>), 3), 1fr)) {
  .grid { grid-template-columns: repeat(attr(data-cols type(<integer>), 3), 1fr); }
}

非対応ブラウザは固定3列、対応ブラウザだけdata属性駆動。レイアウトが壊れない範囲の差なら、これで十分回る。細かい話だが、Chrome 136で attr(data-x string)raw-string に改名されているので、初期の解説記事のコードをコピーすると動かないことがある点も添えておく。

sibling-index()のときも思ったけど、最近のCSSは「JSで橋渡ししていた隙間」を一つずつ埋めに来ている。HTMLに書いてある事実を、JSを経由せずCSSが直接読む。地味な進化ほど、数年後の書き方を静かに変えるんだよね。あなたのコードベースでインラインstyleを書き出している箇所、data属性に置き換えられるのはどこだろう。

元ネタ: https://developer.chrome.com/blog/advanced-attr