::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期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?