やあ。今日も今日とてCSSの沼にハマっているかい?
フロントエンドの現場にいると、毎日何かしらのセレクタと格闘することになるよな。「クラスを余分に増やしたくない」「もっとセマンティックで堅牢なスタイリングをしたい」——そんな悩みを抱えた中級エンジニアの君なら、一度は属性セレクタの奥深さに魅入られたことがあるはずだ。
今日はな、数ある属性セレクタの中でも、「知っているとドヤ顔できるが、一歩間違えると大事故につながる」、なかなかにシブい奴を取り上げよう。
そう、ハイフン区切りプレフィックス`[attr|=value]`だ。
スペックシート上の定義をただなぞるような無粋な真似はしない。ブラウザの裏側の動きや、現場でどういう文脈の時にこいつを抜擢すべきか、シニアの視点からみっちり叩き込んでやろう。心して聞いてくれ。
—
1. `[attr|=value]` とは何か?(仕様のリアル)
まずは基本のおさらいだ。`[attr|=value]` は、「指定した属性の値が、指定された文字列で始まるか、あるいはその文字列の直後にハイフン(`-`)が続く要素」をターゲットにするセレクタだ。
言葉だけだと「は?」ってなるよな。具体例を見せたほうが早い。
/ lang属性が “en”、または “en-” で始まる要素にヒットする /
[lang|=”en”] {
color: #0056b3;
}
こいつがマッチするのは、次のようなHTML要素だ。
- `
Hello
` (完全一致)
- `
Hello, US
` (`en` の直後にハイフン)
- `
Hello, UK
` (同じく直後にハイフン)
だが、以下のようなケースにはマッチしない。これ、めちゃくちゃ重要だからテストに出すぞ。
- `
Hello
` (`en` の後にあるのはハイフンじゃなくて文字だからバツ)
- `
Hello
` (先頭が `en` じゃないからバツ)
そう、このセレクタはもともと、HTMLの `lang` 属性における言語コードのバリエーションを綺麗に一網打尽にするために生まれこいつなのだ。
—
2. ブラウザの裏側で何が起きているか?
さて、フロントエンドアーキテクトとして、ブラウザが裏側でどうやってこいつを処理しているかを知っておくのはエンジニアのたしなみだ。
ブラウザのレンダリングエンジン(BlinkやGeckoなど)がDOMツリーを構築し、スタイルを適用する「スタイル計算(Style Recalculation)」のフェーズ。
`[attr|=value]` に遭遇したブラウザは、要素の対象属性の値を取得し、以下の2つの条件を高速で判定している。
1. 属性値が `value` そのものであるか?
2. または、`value` + `-` (ハイフン)で始まる文字列であるか?
実はこれ、前方一致を表す `[attr^=”value”]` と非常に似ている。
実際、`[lang|=”en”]` は、正規表現的に書けば `[lang=”en”], [lang^=”en-“]` とやっていること自体はほぼ同じだ。しかし、ブラウザの内部パーサーやCSS仕様の歴史的背景から見ても、言語やコード体系の階層構造(サブタグ)をパースするために特化した、非常にエレガントな最適化がなされている。
—
3. なぜ `[attr^=”value”]` ではなく、あえて `[attr|=value]` なのか?
ここで君はこう思うはずだ。
「おいおい、前方一致なら `[lang^=”en”]` で十分じゃないか? なんでわざわざハイフン専用の `|=` なんて覚えなきゃいけないんだよ」ってね。
いい着眼点だ。確かに `[lang^=”en”]` と書けば、先ほどの `en-US` も `en-GB` も拾える。
しかし、ここに大きな罠がある。
もし、`
` という要素があった場合、`[lang^=”en”]` はこれもキャッチしてしまう。英語のつもりで書いたのに、予期せぬカスタム言語コードや別の単語にまでスタイルが漏れ出す可能性があるわけだ。
一方、`[attr|=value]` は、「ハイフンで区切られたサブカテゴリ(または完全一致)」を保証してくれる。つまり、意図しない部分一致によるバグを未然に防ぐ、安全装置(ガードレール)としての役割を果たすんだ。
—
4. 現場で使える!実践的コードスニペット
理屈はこれくらいにして、実際のプロジェクトでどう使うか見ていこう。コピペしてすぐにでもプロダクトのコードに組み込める実例を用意した。
ユースケース1: 多言語サイトの国別テーマ切り替え
一番王道の使い所だな。`` タグや親コンテナに付与された `lang` 属性に応じて、フォントのレンダリングや微調整を行いたい時だ。
/ — HTML —
…
…
…
—————- /
/ 日本語環境のベーススタイル /
[lang|=”ja”] {
font-family: “Hiragino Sans”, “Meiryo”, sans-serif;
line-height: 1.7; / 日本語は少し行間を広めに取るのがプロの技 /
}
/ 英語圏(en, en-US, en-GBなど)のベーススタイルを一括制御 /
[lang|=”en”] {
font-family: -apple-system, BlinkMacSystemFont, “Segoe UI”, Roboto, sans-serif;
letter-spacing: 0.02em; / 英語は少し文字詰まりを意識する /
}
わざわざ `html.is-english` みたいな冗長なクラスをJSで付与しなくても、DOM本来のセマンティクス(`lang`属性)だけでここまでスマートにスタイリングを分岐できる。これがCSS設計の醍醐味よ。
—
ユースケース2: BEM風に拡張されたデザインシステム・コンポーネント
デザインシステムを作っていると、モジュールのバリエーションを表現するために `button–primary` や `button–primary-alt` のようにハイフン繋ぎのモディファイアを量産することがあるよな。
ここでも `[attr|=value]` は使える。
/ — HTML —
—————- /
/ class属性の中に “btn-primary” が含まれ、かつ独立したモディファイアとして機能するものを選ぶ /
[class|=”btn-primary”] {
background-color: #007bff;
color: #ffffff;
border: none;
padding: 0.5em 1em;
border-radius: 4px;
}
/ 個別のバリエーションで上書き /
.btn-primary-dark {
background-color: #343a40;
}
クラス名を厳密に前方一致させつつ、他のボタン(例: `btn-secondary` など)を巻き込みたくない場合に、このハイフン区切りプレフィックスが絶妙な仕事をしてくれる。
—
5. チーフアーキテクトからの実践アドバイス・注意点
最後に、現場でこのセレクタを使う上での心構えをいくつか伝授しておこう。
1. セレクタの詳細度(Specificity)に注意しろ
属性セレクタは、クラスセレクタ(`.class`)と同等の詳細度を持つ(`0-1-0`)。IDセレクタほど強力ではないが、通常のタグセレクタよりは強い。多用しすぎると「あれ、なんでスタイルが当たらないんだ?」というスタイルの競合沼にハマる原因になるので、適用範囲は適切に絞ろう。
2. パフォーマンス過敏症になるな
「属性セレクタは遅いから使うな」という神話がまことしやかに囁かれることがあるが、現代のブラウザエンジンをナメてはいけない。数千・数万の要素を一度にレンダリングするような極端な環境(それこそ仮想スクロールを入れるべき場面)でなければ、セレクタの評価コストによるボトルネックなど体感できるレベルではない。それよりも保守性の高いコードを書くことのほうが100倍価値がある。
3. 「いつ使うべきか」の文脈をチームで共有しろ
「前方一致で十分なところを、無理に `|=` にする必要はない」。だが、「言語コードや、厳密にハイフン区切りでプレフィックス管理されているデータ属性」を扱う際には、このセレクタが最強のカードになる。コードレビューで「なんでここは `^=` じゃなくて `|` なの?」と聞かれた時に、今日の話をドヤ顔で説明してやってくれ。
—
さあ、理屈はここまでだ。
明日からのコーディングで、ただの `[class^=”…”]` から卒業し、この `[attr|=value]` を華麗に使いこなすクールなエンジニアであってくれ。
フロントエンドの旅路に、良きCSSの加護があらんことを。

コメント