お疲れ。そろそろCSSの「なんとなく動くけど、裏側の仕組みはよく分かっていない」というフェーズを抜け出して、後輩から「これ、どうなってるんですか?」と聞かれたときにドヤ顔で解説できるようになりたい頃合いだよね。
今回は、CSS設計の現場において「なくてはならない存在」でありながら、その挙動を意外と適当に扱われがちな `::before` 擬似要素と `content` プロパティの深層に切り込む。
ドキュメントの表面的な使い方をなぞるだけなら誰でもできる。プロのフロントエンドエンジニアとして、ブラウザが裏側でこいつらをどう解釈し、どう描画コストを払っているのか、そのリアリティまで含めて叩き込んでいこう。
—
1. `::before` とは何か? ブラウザの裏側の世界
まず大前提として、`::before` はHTMLのDOMツリーを汚染せずに、スタイリングの都合だけで「幻の要素」をDOMの直前(厳密には要素の最初の子要素として)に生み出す魔法の杖だ。
よくある勘違いが、「HTMLのソースコード上に `` が勝手に追加されているんでしょ?」というもの。違うんだな。
ブラウザはHTMLをパースしてDOMツリーを作り、CSSとマッチングさせてレンダーツリー(Render Tree)を構築する。`::before` が生成するボックスは、このレンダーツリーのレイヤーに直接ねじ込まれる。つまり、JavaScriptの `document.querySelector()` では絶対に捕まえられない。ここがミソだ。
だからこそ、アイコンフォントや装飾的なライン、クリアフィックスといった「HTMLの構造的意味を持たない、完全に見た目のための演出」をアタッチするのに最適解となる。HTMLのセマンティクスを汚さずに済むからね。
—
2. `content` プロパティの真実
`::before`(および `::after`)を語る上で欠かせないのが `content` プロパティだ。こいつの仕様をどこまで正確に理解しているかで、CSSエンジニアとしての格が分かる。
`content` に指定できる値は、単なる文字列だけじゃない。実務で使える引き出しは主に以下の4つだ。
1. 文字列 (`content: “★”;`)
2. 画像 (`content: url(“icon.svg”);`)
3. 属性値の取得 (`content: attr(data-label);`)
4. CSSカウンター (`content: counter(section);`)
特に現場で「おっ、分かってるね」となるのが `attr()` の活用と、装飾目的での `content: “”` の使い分けだ。
⚠️ 注意:装飾目的の `::before` には必ず `content: “”` を書け
「何も表示したくないから `content` プロパティ自体を省いちゃえ」というコードをたまに見るが、あれはバグの元だ。
CSSの仕様上、`content` プロパティが指定されていない `::before` は、ブラウザによってそもそもボックス自体が生成されない(Render Treeに乗らない)。「なんかスタイリングが効かねえな」と30分溶かす原因の第1位がこれだ。空の文字列でもいいから、必ず `content: “”;` は宣言する、これが鉄則だ。
—
3. 現場で即戦力になる実践コードパターン
百聞は一見に如かず。実務のコードベースでそのままコピペして使える、洗練された3つのパターンを共有しよう。
パターンA:リンクの外部アイコン自動付与(`attr()` の変態的な活用)
「外部リンクには自動で矢印のアイコンをつけたい。でもHTML側で全部にクラスやアイコン用タグを書くのはダルいし保守性最悪……」という要件、よくあるよね。`attr()` を使えばHTMLをいじらずに解決できる。
/ 外部サイトへのリンク(target=”_blank”など)を自動検知してアイコンを添える /
a[target=”_blank”]::before {
/ contentに空文字をセットしてボックスを確実に生成 /
content: “”;
/ インラインブロックとしてレイアウトに組み込む /
display: inline-block;
width: 1em;
height: 1em;
margin-right: 0.35em;
/ あらかじめ用意した外部 リンク用SVGアイコンを背景として読み込ませる /
background-image: url(‘data:image/svg+xml;charset=UTF-8,‘);
background-size: contain;
background-repeat: no-repeat;
/ テキストのベースラインと綺麗に垂直方向の中心を合わせる現代的な手法 /
vertical-align: -0.15em;
}
パターンB:リッチな「見出しの装飾(見出し前のアモorphousなアクセントライン)」
デザインカンプによよくある「タイトルの左側に縦のカラーバーがちょこんと付いているデザイン」。これもHTMLに余計な `` を書く必要はない。
.section-title {
position: relative;
padding-left: 1.2rem;
font-size: 1.5rem;
font-weight: 700;
color: #333;
}
.section-title::before {
content: “”;
position: absolute;
left: 0;
top: 50%;
/ 厳密なセンターリング:translateYで自身の高さの半分を持ち上げる /
transform: translateY(-50%);
width: 4px;
height: 1.2em;
/ ブランドカラーを適用 /
background: linear-gradient(135deg, #00c6ff, #0072ff);
border-radius: 2px;
}
この書き方の美しいところは、フォントサイズ(`font-size`)や行高が変わっても、`1.2em` という相対単位のおかげで、アクセントラインの高さがタイトルの大きさに完全に追従してくれる点だ。ピクセル固定値で書く素人臭さとはおさらばしよう。
パターンC:CSSカウンターを使った「連番付きリスト(olなし)」
デザイナーが「数字のデザインをゴリゴリにカスタマイズしたいから、標準の `
- ` の数字じゃなくてCSSで数字を振って!」と言ってきたときの処方箋。
- アクセシビリティ(a11y)への配慮を忘れるな
- 乱用はパフォーマンスの毒になる
/ 親要素でカウンターを初期化する /
.custom-ordered-list {
counter-reset: my-awesome-counter;
list-style: none; / デフォルトの数字を消す /
padding: 0;
}
.custom-ordered-list__item {
position: relative;
/ 番号が入る分の左パディングを確保 /
padding-left: 3rem;
margin-bottom: 1rem;
counter-increment: my-awesome-counter; / リストアイテムごとにインクリメント /
}
.custom-ordered-list__item::before {
/ counter()関数を使って現在の数値を文字列として出力する /
/ “0” + counter(…) で「01, 02, 03…」のようなゼロ埋め演出も可能 /
content: “0” counter(my-awesome-counter);
position: absolute;
left: 0;
top: 0;
width: 2rem;
height: 2rem;
background-color: #ff5722;
color: #fff;
font-weight: bold;
font-size: 0.85rem;
/ フレックスボックスを使って数字を綺麗に真ん中に配置 /
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
}
JavaScriptを一切使わず、CSSの仕様(Counters)だけで動的なナンバリングを完結させる。これぞプロの技だ。
—
4. チーフアーキテクトからの実践的なアドバイス
最後に、現場で `::before` を使うときの心構えをいくつか授けておこう。
`content: “★”` のように、スクリーンリーダーが読み上げるべき重要情報を `::before` に詰め込んではいけない。アクセシビリティツリーの解釈において、ブラウザや支援技術によっては擬似要素の内容を無視したり、予期せぬ読み上げをしたりするリスクがある。「あくまで視覚的な装飾(Decorative)」に徹することが鉄則だ。
何でもかんでも `::before` と `::after` で飾り立てると、DOMはクリーンでもブラウザのレンダーツリーが肥大化し、スクロール時のペイント負荷(Repaint / Reflow)にじわじわと効いてくる。特にスマホの低スペック端末では、過剰な複雑化は厳禁だ。
`::before` と `content` プロパティは、CSSが「単なる装飾言語」から「表現力豊かなデザイン言語」へと昇華するための重要パーツだ。
仕様の裏側を正しく理解し、HTMLの美しさを保ったままリッチなUIを構築していこう。
さて、理論はここまでだ。お前のエディタを開いて、さっそくこのコードを試してみな。何かハマったら、いつでも俺のところに聞きに来るといい。

コメント