- CSS
CSS before・afterの使い方|コピペで使える装飾・アイコン20選
::before と ::after は、HTML を書き足さずに、要素の内側の先頭・末尾へ装飾用の箱を1つずつ追加できる CSS の疑似要素です。 content プロパティで中身を指定すると表示され、あとは普通の要素と同じように色・大きさ・位置を決められます。見出しの飾り線、吹き出しの三角、番号付きリストの丸数字、リンクのアイコンなど、装飾の多くはこの2つで作れます。
この記事では、content に指定できる値を一覧表にまとめ、コピペで使えるサンプルを20個、表示結果の説明つきで載せています。サンプルはすべて Chromium 141(Playwright 経由)で描画して確認しました。さらに「表示されない」ときの原因をチェックリスト化し、アイコンは Font Awesome 6 と SVG+mask の両方の書き方、スクリーンリーダーの読み上げへの配慮まで扱います。
before・afterの早見表
| 知りたいこと | 答え |
|---|---|
| 挿入される位置 | ::before は要素の中身の先頭、::after は中身の末尾(要素の外側ではない) |
| 必須のプロパティ | content。指定がないと箱そのものが作られない |
| 図形だけ出したい | content: "";(空文字)+ width・height・background |
| 最初の表示形式 | display: inline。幅と高さを効かせるには block・inline-block、または position: absolute |
| 好きな位置に置く | 親に position: relative、疑似要素に position: absolute |
| 1要素に使える数 | ::before と ::after が1つずつ(合計2つ) |
| 使えない要素 | img・input・textarea・select・video・iframe・br など、中身を持たない要素 |
:before と ::before |
どちらも動く。現在の書き方はコロン2つ |
| JavaScript から | DOM には存在しない。getComputedStyle(el, '::before') でスタイルは読める |
| 読み上げ | 文字は読み上げられることがある。飾りは content: "★" / ""; で読み上げ対象から外せる |
content に指定できる値の一覧です(詳しくは次の章)。
| 値 | 書き方の例 | 表示されるもの |
|---|---|---|
| 文字列 | content: "NEW"; |
NEW |
| 空文字 | content: ""; |
何も表示しない箱(図形の土台) |
| 属性の値 | content: attr(data-label); |
HTML の data-label 属性の値 |
| カウンター | content: counter(step); |
1、2、3… |
| 入れ子のカウンター | content: counters(sec, "."); |
1.1、1.2、2.1… |
| 画像 | content: url("icon.svg"); |
画像(元の大きさのまま) |
| 引用符 | content: open-quote; |
quotes プロパティで決めた開き括弧 |
| 代替テキストつき | content: "★" / "おすすめ"; |
画面には ★、読み上げは「おすすめ」 |
| 組み合わせ | content: "第" counter(ch) "章"; |
第1章 |
| 生成しない | content: none; |
疑似要素を作らない |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「CSS before・afterの使い方|コピペで使える装飾・アイコン20選」
before・afterの基本の書き方
<p class="note">本日中に発送します</p>
.note::before {
content: "※"; /* 必須 */
color: #dc2626;
margin-right: 4px;
}
.note::after {
content: "(土日を除く)";
font-size: 0.85em;
color: #64748b;
}
表示結果:「※ 本日中に発送します(土日を除く)」。※ は赤、括弧の部分は小さめの灰色になります。
ブラウザの中では、次のような構造として扱われます。
<p class="note">
::before ← 中身の先頭に追加される箱
本日中に発送します
::after ← 中身の末尾に追加される箱
</p>
疑似要素は「要素の前後」ではなく「要素の内側の先頭と末尾」に入ります。 そのため親要素の color や font-size を受け継ぎ、親の padding の内側に並びます。この位置関係が、後述の「img や input には使えない」理由にもつながります。
コロンは2つで書く
| 書き方 | 意味 | 例 |
|---|---|---|
::before(コロン2つ) |
疑似要素。要素の一部分や、追加される箱を指す | ::before、::after、::marker、::placeholder |
:hover(コロン1つ) |
疑似クラス。要素の状態を指す | :hover、:focus-visible、:first-child |
CSS3 で両者を見分けるためにコロン2つの書き方が導入されました。:before も互換性のために今でも動きます(Chromium 141 で p:before { content: "..." } が表示されることを確認)。新しく書くときは ::before にそろえると、状態と組み合わせたとき(a:hover::after)も読みやすくなります。
contentに指定できる値の一覧
content の値を、実際に Chromium 141 で表示した結果とともに整理します。値の種類は MDN の content のページを開いて確認しました。
| 種類 | CSS | 表示結果 | 補足 |
|---|---|---|---|
| 文字列 | content: "文字 "; |
文字 | 引用符で囲む。'...' でも可 |
| 空文字 | content: ""; |
(箱だけ) | 図形・線・アイコンの土台 |
| Unicode エスケープ | content: "\2192 \2605 \00A9"; |
→ ★ © | \ +16進数。直後の空白1つは区切りとして消える |
| 改行 | content: "1行目\A 2行目"; white-space: pre; |
2行に分かれる | \A だけでは改行されず、white-space の指定が必要 |
| 属性 | content: attr(data-label) ": "; |
在庫: | 属性がないときは空文字になる |
| カウンター | content: counter(n); |
4 | counter-reset・counter-increment と組み合わせる |
| カウンターの形式 | counter(n, upper-roman) / counter(n, decimal-leading-zero) / counter(n, cjk-ideographic) |
IV / 04 / 四 | 第2引数は list-style-type と同じ値 |
| 入れ子のカウンター | content: counters(sec, "."); |
1.1 | 階層を区切り文字でつなぐ |
| 画像 | content: url("icon.svg"); |
画像 | width・height を指定しても大きさは変わらない |
| グラデーション | content: linear-gradient(#e66465, #9198e5); |
グラデーションの四角 | display と大きさの指定が必要 |
| 引用符 | content: open-quote; / close-quote |
『 』 | 親に quotes: "『" "』"; を指定した場合 |
| 代替テキスト | content: "★" / "おすすめ"; |
★ | / の後ろが読み上げ用の文字 |
| 読み上げない | content: "★" / ""; |
★ | 飾りとして扱われる |
| 複数の値 | content: "A" "B" attr(data-x); |
AB | 空白区切りで並べると連結される |
| キーワード | content: none; / normal |
(なし) | 疑似要素が作られない。::before の初期値は normal(= none 扱い) |
知っておくと役立つポイントを3つ補足します。
- 引用符を付けないと無効:
content: X;と書いた場合、宣言が無効になりgetComputedStyleの値はnoneのままで、何も表示されませんでした url()の画像は大きさを変えられない:16px の SVG にwidth: 64px; height: 64px;を指定しても、表示は16px のままでした。大きさを決めたい画像は、content: "";+background-image(または後述のmask)で表示します- 定義していないカウンターは 0:
counter-resetをしていない名前をcounter()で読むと0が表示されました
attr()で幅などの数値も渡せる
attr() は長らく content の中で文字列として使うものでしたが、型を指定して content 以外のプロパティにも渡せるようになっています。Chromium 141 で次のコードが動くことを確認しました。
<p class="bar" data-w="80">在庫</p>
.bar::before {
content: "";
display: inline-block;
height: 10px;
width: attr(data-w px, 5px); /* data-w の値を px として使う。なければ 5px */
background: #2563eb;
}
表示結果:幅80px の青い棒が文字の前に出ます(getComputedStyle の width は 80px)。この型つきの attr() は対応ブラウザが限られるため(Firefox・Safari での動作はこの記事では未実行)、広い環境で動かしたい場合は CSS 変数(style="--w: 80px" と width: var(--w))を使う方法が確実です。
コピペで使える装飾サンプル20選
ここからがサンプル集です。どれも HTML と CSS をそのまま貼れば動く形にしてあります。色(#2563eb など)と大きさを変えるだけで、サイトの雰囲気に合わせられます。表示結果は Chromium 141 のスクリーンショットで確認した内容です。
| 番号 | 作れるもの | 使う疑似要素 | 鍵になる指定 |
|---|---|---|---|
| 1 | 見出しの左に縦線 | before | position: absolute |
| 2 | 見出しの下に短い線 | after | position: absolute |
| 3 | 見出しの両側に線 | before+after | display: flex、flex: 1 |
| 4 | 見出しの上に英字ラベル | before | attr() |
| 5 | かぎ括弧で囲む | before+after | 文字列 |
| 6 | 引用ボックス | before | Unicode エスケープ |
| 7 | 吹き出し(塗り) | after | border の三角 |
| 8 | 吹き出し(枠線) | before+after | 三角を2枚重ねる |
| 9 | リボン | before+after | border、clip-path |
| 10 | カードの角の NEW ラベル | before | rotate、overflow: hidden |
| 11 | 矢印つきリンク | after | border 2辺+rotate |
| 12 | パンくずの区切り | after | :not(:last-child) |
| 13 | 丸数字の番号付きリスト | before | counter() |
| 14 | 1.1 形式の入れ子番号 | before | counters() |
| 15 | チェックマークのリスト | before | border 2辺+rotate |
| 16 | 外部リンクアイコン | after | SVG+mask |
| 17 | 必須ラベル | after | 文字列 |
| 18 | 通知バッジ | after | attr() |
| 19 | ツールチップ | after | attr()、opacity |
| 20 | ホバーで伸びる下線 | after | scale、transition |
1. 見出しの左に縦線を付ける
<h2 class="h-bar">お知らせ</h2>
.h-bar { position: relative; padding-left: 16px; }
.h-bar::before {
content: "";
position: absolute; left: 0; top: 0.1em; bottom: 0.1em;
width: 6px; border-radius: 3px; background: #2563eb;
}
表示結果:見出しの左端に、太さ6px・角丸の青い縦線が文字の高さに合わせて表示されます。border-left と違い、線の長さ(top・bottom)や角丸を自由に決められます。
2. 見出しの下に短い線を付ける
<h2 class="h-underline">サービス紹介</h2>
.h-underline { padding-bottom: 12px; position: relative; }
.h-underline::after {
content: "";
position: absolute; left: 0; bottom: 0;
width: 48px; height: 4px; border-radius: 2px; background: #f59e0b;
}
表示結果:見出しの左下に、幅48px・太さ4px のオレンジ色の短い線が出ます。中央に置きたいときは left: 50%; translate: -50% 0; に変えます。
3. 見出しの両側に線を引く
<h2 class="h-lines">会員の声</h2>
.h-lines { display: flex; align-items: center; gap: 16px; }
.h-lines::before, .h-lines::after {
content: ""; flex: 1; height: 1px; background: #94a3b8;
}
表示結果:見出しの文字が中央に、その左右に細い線が伸びます。flex: 1 で残りの幅を2本の線が分け合うので、文字数が変わっても崩れません。
4. 見出しの上に英字ラベルを添える
<h2 class="h-en" data-en="ABOUT">私たちについて</h2>
.h-en::before {
content: attr(data-en);
display: block;
font-size: 12px; letter-spacing: 0.15em; color: #2563eb;
}
表示結果:見出しの上に、小さな青い文字で「ABOUT」が表示されます。文言を HTML の data-en 属性に書くので、見出しごとに CSS を増やさずに済みます。
5. かぎ括弧で囲む
<p><span class="kakko">重要</span>なお知らせです。</p>
.kakko::before { content: "「"; }
.kakko::after { content: "」"; }
表示結果:「重要」なお知らせです。
6. 引用ボックスに引用符を付ける
<blockquote class="quote">小さく作って、早く確かめる。</blockquote>
.quote { position: relative; margin: 0; padding: 24px 24px 24px 56px; background: #f1f5f9; border-radius: 8px; }
.quote::before {
content: "\201C"; /* “ */
position: absolute; left: 12px; top: 4px;
font-size: 56px; line-height: 1; color: #94a3b8; font-family: Georgia, serif;
}
表示結果:薄い灰色の角丸ボックスの左上に、大きな灰色の引用符(“)が表示されます。
7. 吹き出しを作る(塗りつぶし)
<p class="balloon">ご質問はこちらから</p>
.balloon { position: relative; display: inline-block; padding: 12px 16px; background: #2563eb; color: #fff; border-radius: 8px; }
.balloon::after {
content: "";
position: absolute; left: 24px; top: 100%;
border: 10px solid transparent; border-top-color: #2563eb;
}
表示結果:青い角丸の箱の左下に、下向きの三角が付きます。幅も高さも0の箱に太い border を付け、1辺だけ色を付けると三角になるという定番の作り方です。三角の向きは、色を付ける辺で変わります。
| 三角の向き | 色を付ける辺 | 置く位置 |
|---|---|---|
| 下向き ▼ | border-top-color |
top: 100% |
| 上向き ▲ | border-bottom-color |
bottom: 100% |
| 左向き ◀ | border-right-color |
right: 100% |
| 右向き ▶ | border-left-color |
left: 100% |
8. 吹き出しを作る(枠線つき)
<p class="balloon-line">こんにちは</p>
.balloon-line { position: relative; display: inline-block; padding: 12px 16px; background: #fff; border: 2px solid #2563eb; border-radius: 8px; }
.balloon-line::before, .balloon-line::after {
content: ""; position: absolute; top: 50%; translate: 0 -50%;
border: 10px solid transparent;
}
.balloon-line::before { right: 100%; border-right-color: #2563eb; }
.balloon-line::after { right: calc(100% - 3px); border-right-color: #fff; }
表示結果:白地に青い枠線の箱の左側に、枠線つきの三角が付きます。青い三角(before)の上に、3px ずらした白い三角(after)を重ねることで、枠線に見せています。
9. リボンを作る
<span class="ribbon">おすすめ</span>
.ribbon { position: relative; display: inline-block; margin-left: 10px; padding: 8px 20px 8px 16px; background: #dc2626; color: #fff; }
.ribbon::before { /* 左下の折り返し */
content: ""; position: absolute; left: 0; top: 100%;
border: 5px solid transparent; border-top-color: #7f1d1d; border-right-color: #7f1d1d;
}
.ribbon::after { /* 右端の切り込み */
content: ""; position: absolute; left: 100%; top: 0; height: 100%; width: 14px;
background: inherit; clip-path: polygon(0 0, 100% 0, 40% 50%, 100% 100%, 0 100%);
}
表示結果:赤い帯の右端が V 字に切れ込み、左下に濃い赤の小さな折り返しが付きます。親要素の左端から10px はみ出して見せると、巻き付いているように見えます。
10. カードの角にNEWラベルを付ける
<div class="card-new">入荷した備品</div>
.card-new { position: relative; overflow: hidden; width: 200px; padding: 24px 16px; border: 1px solid #cbd5e1; border-radius: 8px; }
.card-new::before {
content: "NEW";
position: absolute; top: 12px; right: -32px; width: 110px;
rotate: 45deg; text-align: center;
font-size: 12px; font-weight: 700; color: #fff; background: #dc2626;
}
表示結果:カードの右上の角に、斜め45度の赤い帯で「NEW」と表示されます。帯のはみ出した部分は、親の overflow: hidden で切り取っています。
11. 矢印つきリンクを作る
<a class="link-arrow" href="#">詳しく見る</a>
.link-arrow { display: inline-flex; align-items: center; gap: 8px; color: #2563eb; text-decoration: none; }
.link-arrow::after {
content: "";
width: 0.5em; height: 0.5em;
border-top: 2px solid currentColor; border-right: 2px solid currentColor;
rotate: 45deg; transition: translate 0.2s;
}
.link-arrow:hover::after { translate: 4px 0; }
表示結果:リンク文字の右に「>」の形の矢印が付き、マウスを乗せると右へ4px 動きます。正方形の上辺と右辺だけに線を引き、45度回転させて作っています。currentColor を使っているので、矢印の色は文字色に自動で合います。
12. パンくずリストの区切りを入れる
<ol class="breadcrumb">
<li><a href="#">ホーム</a></li>
<li><a href="#">記事</a></li>
<li>CSS</li>
</ol>
.breadcrumb { display: flex; flex-wrap: wrap; padding: 0; margin: 0; list-style: none; }
.breadcrumb li:not(:last-child)::after {
content: "/" / "";
margin: 0 8px; color: #94a3b8;
}
表示結果:ホーム / 記事 / CSS。最後の項目のあとには区切りが付きません。content: "/" / ""; として、区切り記号を読み上げの対象から外しています。
13. 丸数字の番号付きリストを作る
<ol class="steps">
<li>会員登録する</li>
<li>備品を選ぶ</li>
<li>注文を確定する</li>
</ol>
.steps { list-style: none; padding: 0; counter-reset: step; }
.steps li { position: relative; padding: 4px 0 4px 40px; margin-bottom: 8px; counter-increment: step; }
.steps li::before {
content: counter(step);
position: absolute; left: 0; top: 0;
width: 28px; height: 28px; border-radius: 50%;
display: grid; place-items: center;
font-size: 14px; font-weight: 700; color: #fff; background: #2563eb;
}
表示結果:各項目の左に、白い数字が入った青い丸(1・2・3)が並びます。
カウンターは3つの指定で動きます。
| 指定 | 役割 | 書く場所 |
|---|---|---|
counter-reset: step; |
カウンターを作って0にする | リスト全体(親) |
counter-increment: step; |
1つ進める | 各項目 |
content: counter(step); |
現在の値を表示する | 各項目の疑似要素 |
14. 1.1形式の入れ子番号を付ける
<ol class="outline">
<li>準備
<ol><li>道具</li><li>材料</li></ol>
</li>
<li>手順
<ol><li>下ごしらえ</li></ol>
</li>
</ol>
.outline, .outline ol { list-style: none; padding-left: 0; counter-reset: sec; }
.outline ol { padding-left: 24px; }
.outline li { counter-increment: sec; }
.outline li::before { content: counters(sec, ".") " "; font-weight: 700; color: #2563eb; }
表示結果:「1 準備」「1.1 道具」「1.2 材料」「2 手順」「2.1 下ごしらえ」と、階層をピリオドでつないだ番号が付きます。counters()(末尾に s)は、同じ名前のカウンターを外側から順に全部つなげて表示します。
15. チェックマークのリストを作る
<ul class="checks">
<li>コピペで使える</li>
<li>画像を用意しなくてよい</li>
</ul>
.checks { list-style: none; padding: 0; }
.checks li { position: relative; padding-left: 28px; margin-bottom: 6px; }
.checks li::before {
content: "";
position: absolute; left: 4px; top: 0.35em;
width: 12px; height: 6px;
border-left: 3px solid #16a34a; border-bottom: 3px solid #16a34a;
rotate: -45deg;
}
表示結果:各項目の左に緑のチェックマーク(✓)が付きます。長方形の左辺と下辺に線を引き、-45度回転させています。
16. 外部リンクにアイコンを付ける
<a href="https://example.com/" target="_blank" rel="noopener">公式ドキュメント</a>
a[target="_blank"]::after {
content: "" / "(新しいタブで開く)";
display: inline-block; width: 1em; height: 1em; margin-left: 4px; vertical-align: -0.125em;
background-color: currentColor;
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15 3h6v6M10 14 21 3M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'/%3E%3C/svg%3E") no-repeat center / contain;
}
表示結果:target="_blank" が付いたリンクの後ろにだけ、文字と同じ色の「四角から矢印が出る」アイコンが表示されます。属性セレクタを使うので、HTML にクラスを足す必要がありません。SVG と mask の仕組みは、後のアイコンの章で説明します。
17. 必須ラベルを付ける
<label class="required" for="name">お名前</label>
.required::after {
content: "必須";
margin-left: 8px; padding: 2px 6px; border-radius: 4px;
font-size: 11px; font-weight: 700; color: #fff; background: #dc2626;
}
表示結果:「お名前」の右に、赤地に白文字の小さな「必須」ラベルが付きます。必須であることは入力欄の required 属性でも伝えておくと、見た目に頼らずに伝わります。
18. 通知バッジを付ける
<span class="badge" data-count="3">通知</span>
.badge { position: relative; display: inline-block; padding: 8px 16px; border: 1px solid #cbd5e1; border-radius: 8px; }
.badge[data-count]::after {
content: attr(data-count);
position: absolute; top: -10px; right: -10px;
min-width: 20px; height: 20px; padding: 0 6px; box-sizing: border-box; border-radius: 10px;
font-size: 12px; line-height: 20px; text-align: center; color: #fff; background: #dc2626;
}
表示結果:「通知」の箱の右上に、赤い丸で「3」と表示されます。JavaScript で el.dataset.count = 5 と書き換えるだけで数字が変わり、属性を取り除けばバッジも消えます。
19. ツールチップを表示する
<button class="tip" data-tip="クリップボードにコピーします">コピー</button>
.tip { position: relative; }
.tip::after {
content: attr(data-tip);
position: absolute; left: 50%; bottom: calc(100% + 8px); translate: -50% 0;
padding: 6px 10px; border-radius: 6px; white-space: nowrap;
font-size: 12px; color: #fff; background: #1e293b;
opacity: 0; pointer-events: none; transition: opacity 0.2s;
}
.tip:hover::after, .tip:focus-visible::after { opacity: 1; }
表示結果:ボタンにマウスを乗せる、またはキーボードでフォーカスすると、ボタンの上に黒い吹き出しで「クリップボードにコピーします」と表示されます。画面の端に近い要素では吹き出しがはみ出すので、left・translate を調整します。
20. ホバーで伸びる下線を付ける
<a class="hover-line" href="#">記事一覧へ</a>
.hover-line { position: relative; color: #2563eb; text-decoration: none; }
.hover-line::after {
content: "";
position: absolute; left: 0; bottom: -2px; width: 100%; height: 2px;
background: currentColor; scale: 0 1; transform-origin: left; transition: scale 0.25s;
}
.hover-line:hover::after, .hover-line:focus-visible::after { scale: 1 1; }
表示結果:マウスを乗せると、リンクの下に左から右へ線が伸びます。transform-origin: right にすると右から伸びます。
アイコンを表示する2つの方法
アイコンを疑似要素で出す方法は、大きく「アイコンフォント」と「SVG」の2つです。
| 比べる点 | Font Awesome(アイコンフォント) | SVG+mask |
|---|---|---|
| 準備 | CSS とフォントファイルの読み込み | なし(CSS に SVG を埋め込める) |
| 色の変更 | color |
background-color(currentColor で文字色に連動) |
| 大きさ | font-size |
width・height |
| 読み込むデータ量 | フォント全体 | 使うアイコンの分だけ |
| 向いている場面 | 多くの種類のアイコンを使う | 数個のアイコンを軽く使いたい |
Font Awesome 6を疑似要素で使う
Font Awesome 6.7.2 の Free 版(npm の @fortawesome/fontawesome-free@6)を読み込んだ状態で、次の書き方が表示されることを確認しました。
/* Solid(塗り)のアイコン */
.ext::after {
content: "\f08e"; /* arrow-up-right-from-square */
font-family: "Font Awesome 6 Free";
font-weight: 900; /* Solid は 900 */
margin-left: 4px;
}
/* 同じ指定を、Font Awesome が用意している CSS 変数で短く書く */
.ext::after {
content: "\f08e";
font: var(--fa-font-solid);
margin-left: 4px;
}
/* Regular(線) */
.star::before {
content: "\f005";
font: var(--fa-font-regular); /* font-weight 400 */
}
/* Brands(ロゴ) */
.repo::before {
content: "\f09b";
font: var(--fa-font-brands); /* font-family が "Font Awesome 6 Brands" */
}
スタイルごとに font-family と font-weight の組み合わせが決まっています。
| スタイル | font-family | font-weight | CSS 変数 |
|---|---|---|---|
| Solid | "Font Awesome 6 Free" |
900 | --fa-font-solid |
| Regular | "Font Awesome 6 Free" |
400 | --fa-font-regular |
| Brands | "Font Awesome 6 Brands" |
400 | --fa-font-brands |
よく使うアイコンのコード(6.7.2 の CSS で確認)です。
| アイコン | クラス名 | content |
|---|---|---|
| 外部リンク | fa-arrow-up-right-from-square |
"\f08e" |
| チェック | fa-check |
"\f00c" |
| 丸チェック | fa-circle-check |
"\f058" |
| 星 | fa-star |
"\f005" |
| 封筒 | fa-envelope |
"\f0e0" |
| 電話 | fa-phone |
"\f095" |
| 右向きの山形 | fa-chevron-right |
"\f054" |
アイコンが四角(□に×)で表示されるときは、font-family の指定を確認します。 font-family を書かずに content: "\f08e" だけ指定したところ、文字が見つからないことを示す四角が表示されました。なお、6.7.2 の all.css には "Font Awesome 5 Free" という名前の定義も互換用に含まれており、バージョン5向けのフォント名のままでも表示されました。新しく書くコードでは、読み込んでいるバージョンの名前(または下記の CSS 変数)にそろえておくと見通しがよくなります。
なお、2026年10月時点で npm の最新版は Font Awesome 7.3.1 です。バージョン7のパッケージに含まれる CSS では、フォント名が "Font Awesome 7 Free"・"Font Awesome 7 Brands" に変わっており、変数名 --fa-font-solid などは引き続き定義されています(CSS ファイルの内容を確認。バージョン7での表示は未実行)。font: var(--fa-font-solid); と変数で書いておくと、バージョンを上げたときに書き換える箇所が減ります。
SVG+maskで文字色に連動するアイコンを作る
サンプル16で使った方法です。SVG を「色」ではなく「形の型抜き」として使い、色は background-color で付けます。
.icon-mail::before {
content: "" / "";
display: inline-block;
width: 1em;
height: 1em;
margin-right: 4px;
vertical-align: -0.125em;
background-color: currentColor; /* 文字色と同じ色で塗る */
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='5' width='18' height='14' rx='2'/%3E%3Cpath d='m3 7 9 6 9-6'/%3E%3C/svg%3E") no-repeat center / contain;
}
| 指定 | 役割 |
|---|---|
content: "" |
中身のない箱を作る |
width: 1em; height: 1em; |
文字の大きさに合わせた正方形にする |
background-color: currentColor |
箱全体を文字色で塗る |
mask: url(...) no-repeat center / contain |
SVG の形の部分だけを残して切り抜く |
content: url("icon.svg") や background-image で SVG を表示すると、色は SVG ファイルの中で決まった色になります。mask を使うと、リンクの色やホバー時の色、ダークモードの文字色にそのまま追従します。
SVG を url("data:image/svg+xml,...") として埋め込むときは、< を %3C、> を %3E、# を %23 に置き換えます。外部ファイルにして mask: url("mail.svg") ... と書いても同じです。
読み上げへの配慮
疑似要素の中身は DOM には入りませんが、スクリーンリーダーには読み上げられることがあります。Playwright の ariaSnapshot()(アクセシビリティツリーの内容を文字で取り出す機能)を使い、Chromium 141 で確認した結果です。
| CSS | 画面の表示 | アクセシビリティツリー上の文字 |
|---|---|---|
content: "★"; |
★ | ★ |
content: "★" / "おすすめ"; |
★ | おすすめ |
content: "★" / ""; |
★ | (なし) |
content: attr(data-label) ": "; |
在庫: | 在庫: |
content: "必須"; |
必須 | 必須 |
content: "" / "(新しいタブで開く)";(サンプル16) |
アイコン | リンク名が「公式ドキュメント (新しいタブで開く)」になる |
この結果から、使い分けは次のようになります。
| 疑似要素の役割 | 書き方 |
|---|---|
| 飾り(区切り線、矢印、記号) | content: "→" / ""; として読み上げから外す |
| 意味を持つアイコン | content: "" / "意味を表す言葉"; で代替テキストを付ける |
| 文章として必要な情報 | 疑似要素ではなく HTML に書く |
/ を使う代替テキストの構文は、比較的新しい仕様です。対応していないブラウザでは content の宣言全体が無効になり、疑似要素が表示されなくなります。広い環境に対応したいときは、通常の指定を先に書き、次の行で上書きする形にしておくと、どちらでも表示されます。
.breadcrumb li:not(:last-child)::after {
content: "/"; /* 代替テキストの構文に対応していない環境向け */
content: "/" / ""; /* 対応している環境ではこちらが使われる */
}
HTML に <i class="fa-solid fa-check"></i> のようにアイコン用の要素を置く場合は、aria-hidden="true" を付けると読み上げの対象から外せます(付けた要素がアクセシビリティツリーに現れないことを確認)。
また、疑似要素の文字は textContent や innerText に含まれません(Chromium 141 で確認)。マウスでの選択やコピーの対象にもならないのが一般に知られた挙動です。価格・日付・注意書きのように読者が必要とする情報は、HTML に書くのが確実です。
before・afterが表示されないときのチェックリスト
「書いたのに表示されない」ときの原因を、Chromium 141 で再現した結果とともにまとめます。上から順に確認すると、ほとんどの場合に原因が見つかります。
| 順 | 確認すること | 再現した結果 | 直し方 |
|---|---|---|---|
| 1 | content を書いているか |
content なしでは getComputedStyle の値が none になり、何も描画されない |
図形だけでも content: ""; を書く |
| 2 | content の文字列を引用符で囲んでいるか |
content: X; は無効になり none のまま |
content: "X"; |
| 3 | 対象が置換要素ではないか | img・input type="text"・textarea・select・video・iframe では描画されない |
親要素(span や div)で包み、親に付ける |
| 4 | display が inline のまま幅・高さを指定していないか |
content: ""; width: 20px; height: 20px; だけでは描画されない |
display: inline-block(または block、position: absolute) |
| 5 | 親に position: relative があるか |
親に指定がないと、ページの右上など離れた場所に表示された | 親に position: relative |
| 6 | z-index: -1 で親の背景の裏に回っていないか |
背景色のある親の中では見えなくなった | 親に isolation: isolate(または position: relative; z-index: 0;) |
| 7 | 親の overflow: hidden で切れていないか |
親の外側(top: -20px)に置いた分は描画されない |
位置を内側にする、または overflow を見直す |
| 8 | height: 100% の基準があるか |
親の高さが決まっていないと高さ 0px になった | position: absolute + top: 0; bottom: 0; で伸ばす |
| 9 | アイコンフォントの font-family・font-weight |
指定がないと四角が表示される | フォント名と太さを合わせる |
| 10 | セレクタが当たっているか | — | 開発者ツールの Elements で、要素の中に ::before が出ているか確認 |
img・inputに効かない理由
::before・::after は「要素の中身の先頭・末尾」に箱を追加する仕組みです。img や input は置換要素(中身を外部の画像や入力欄そのものに置き換えて表示する要素)で、CSS が箱を追加できる「中身」を持っていません。
Chromium 141 で、各要素に ::before { content: "X"; } を指定したときの結果です。
| 要素 | 疑似要素の描画 |
|---|---|
span・div・p・a・button・li など |
される |
img(画像が読み込めている) |
されない |
input type="text"・textarea・select |
されない |
video・iframe |
されない |
input type="checkbox" |
Chromium ではされた(ブラウザによって異なるため、頼らないほうが確実) |
img(画像が読み込めず alt を表示中) |
Chromium ではされた(同上) |
画像に飾りを重ねたいときは、親要素で包んで親に疑似要素を付けます。
<span class="thumb"><img src="item.jpg" alt="備品の写真"></span>
.thumb { position: relative; display: inline-block; }
.thumb::after {
content: "NEW";
position: absolute; top: 8px; left: 8px;
padding: 2px 8px; font-size: 12px; color: #fff; background: #dc2626;
}
チェックボックスやラジオボタンの見た目を変えたいときは、input の隣の label や span に疑似要素を付ける(input:checked + label::before)形にします。
z-index: -1で消えるしくみ
疑似要素を文字の後ろに敷きたくて z-index: -1 を指定すると、親の背景よりもさらに後ろへ回り、親の背景色に隠れます。親が重なりの基準(スタッキングコンテキスト)を作っていないためです。
/* 表示されない:親の背景の裏に回る */
.box { position: relative; background: #fff; }
.box::before { content: ""; position: absolute; inset: 0; background: #fde68a; z-index: -1; }
/* 表示される:親が重なりの基準になる */
.box { position: relative; background: #fff; isolation: isolate; }
isolation: isolate を付けた場合と、z-index: 0 を付けた場合のどちらでも、疑似要素が親の背景の手前・文字の後ろに表示されることを確認しました。
JavaScriptから疑似要素を扱う
疑似要素は DOM に存在しないので、querySelector('.box::before') では取得できません。できることは次のとおりです。
| やりたいこと | 方法 |
|---|---|
| スタイルを読む | getComputedStyle(el, '::before').content |
| 文言を変える | el.dataset.label = '新着'(CSS 側は content: attr(data-label)) |
| 色や大きさを変える | el.style.setProperty('--line-color', 'red')(CSS 側は background: var(--line-color)) |
| 表示・非表示を切り替える | クラスを付け外しする(el.classList.toggle('is-open')) |
| クリックを受け取る | 疑似要素だけを対象にはできない。親要素のイベントとして届く |
よくある質問
Q. ::before と ::after の違いは何ですか?
A. 追加される位置です。::before は要素の中身の先頭、::after は末尾に入ります。位置を position: absolute で決める場合は、どちらを使っても見た目は同じにできます。重なる順番は、あとに書かれる ::after のほうが手前になります。
Q. content: "" は必要ですか?
A. 必要です。content を指定しないと疑似要素そのものが作られないため、width や background を書いても何も表示されません。文字を出さず図形だけ表示したいときも、空文字の content: ""; を書きます。
Q. before・after で画像を表示できますか?
A. content: url("画像のパス"); で表示できますが、この方法では大きさを変えられません。大きさを決めたいときは、content: ""; と width・height を指定し、background-image(色を変えたいアイコンなら mask)で表示します。
Q. 1つの要素に疑似要素を3つ以上付けられますか?
A. ::before と ::after は1つずつ、合計2つまでです。3つ以上の飾りが必要なときは、内側に span を1つ足す、background を複数指定する、box-shadow を重ねる、といった方法と組み合わせます。
まとめ
::before・::afterは要素の内側の先頭・末尾に箱を追加する。contentを書くことが表示の条件contentには文字列・空文字・attr()・counter()・url()・引用符・代替テキスト(/)を指定できる- 図形は
content: "";+大きさ+backgroundやborder。位置は親relative・疑似要素absolute - 見出し装飾、吹き出し、リボン、番号付きリスト、バッジ、ツールチップまで HTML を増やさずに作れる
- 表示されないときは、content → 置換要素 → display → position → z-index の順に確認する
- アイコンは Font Awesome なら
font-familyとfont-weightを合わせ、SVG+mask なら文字色に連動する - 飾りは
content: "…" / "";で読み上げから外し、必要な情報は HTML に書く
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「CSS before・afterの使い方|コピペで使える装飾・アイコン20選」
株式会社RJCでは、CSSの疑似要素を使った装飾の作り方を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。