【CSS】::before・::after擬似要素の使い方:画像・アイコン・ホバーエフェクト

css-before-after

CSSの::before::afterは、HTMLの構造を汚すことなく、アイコンや図形、テキストなどの装飾を自由に追加できる擬似要素です。

本記事では、必須プロパティなどの基本ルールから実務で頻出するデザイン実装、動的なJavaScript連携や効かない時の解決策まで解説します。

また、CSSに関するカテゴリーページから学びたい内容を決めたい人は、以下のCSSページをご確認ください。

目次

::before・::afterの擬似要素とは?基本の書き方

Webデザインにおいて、HTMLの記述を汚すことなくCSSだけでアイコンを追加したり、複雑な装飾を作り出したりできる機能が「擬似要素(::before)」です。

ちなみに、「:before」とコロン1つで書かれている古いコードを見かけますが、CSS3以降は擬似クラス(:hoverなど)と区別するため、擬似要素はコロンを2つ重ねて「::before」と書くのが現在の標準仕様です。

迷った際は、迷わず::(ダブルコロン)を使用してください。

ここでは、基本的な::beforeの使い方を解説します。

::before擬似要素とは?基本の書き方
  • ::before::afterの違いと使い分け
  • contentプロパティのルールと基本構文

::beforeと::afterの違いと使い分け

::before::afterの違いはシンプルです。

  • ::before:要素の中身の一番最初に挿入される。
  • ::after:要素の中身の一番最後に挿入される。

初心者が陥りやすいのが、「::beforeだから、指定したHTMLタグの手前(外側)に要素が作られる」と勘違いすることです。

実際には、「指定したタグのコンテンツ手前」に作られます。

そのため、例えば親要素にoverflow: hidden;がかかっていると、擬似要素も一緒に見えなくなる仕様に注意が必要です。

また、「1つの要素に対して複数を作りたい!」と思うかもしれませんが、1つの要素に対して::before::afterはそれぞれ1つずつしか存在できません。

実務では、1つの要素に対して「左側にアイコン(::before)」「右側に矢印(::after)」というように役割を分けて同時に使うのがよいです。

これにより、HTML側に余計な<span><img>タグを増やすことなく、クリーンなコードでリッチなボタンを実装できます。

⭕️ ::beforeで左にアイコン、::afterで右に矢印を配置

/* 💡 ボタン本体の設定(擬似要素の基準とするためposition: relativeが必須) */
.pseudo-btn {
  position: relative;
  display: inline-block;
  padding: 15px 50px 15px 40px;
}

/* ⭕️ ::beforeで左側にアイコン(★)を配置 */
.pseudo-btn::before {
  content: “★”; /* 💡 挿入する文字 */
  position: absolute;
  left: 15px;
}

/* ⭕️ ::afterで右側に矢印(>)を配置 */
.pseudo-btn::after {
  content: “>”; /* 💡 挿入する文字 */
  position: absolute;
  right: 15px;
}
HTMLコード表示
<div class="pseudo-btn-wrapper">
  
  <p class="pseudo-caption">⭕️ ::beforeで左にアイコン、::afterで右に矢印を配置</p>

  <div class="pseudo-demo-area">
    
    <a href="#" class="pseudo-btn">詳しく見る</a>

  </div>

  <div class="pseudo-code">
    /* 💡 ボタン本体の設定(擬似要素の基準とするためposition: relativeが必須) */<br>
    <span class="hl-blue">.pseudo-btn</span> {<br>
      <span class="hl-green">position: relative;</span><br>
      <span class="hl-green">display: inline-block;</span><br>
      <span class="hl-green">padding: 15px 50px 15px 40px;</span><br>
    }<br><br>

    /* ⭕️ ::beforeで左側にアイコン(★)を配置 */<br>
    <span class="hl-blue">.pseudo-btn::before</span> {<br>
      <span class="hl-red">content: "★";</span> /* 💡 挿入する文字 */<br>
      <span class="hl-green">position: absolute;</span><br>
      <span class="hl-green">left: 15px;</span><br>
    }<br><br>

    /* ⭕️ ::afterで右側に矢印(>)を配置 */<br>
    <span class="hl-blue">.pseudo-btn::after</span> {<br>
      <span class="hl-red">content: ">";</span> /* 💡 挿入する文字 */<br>
      <span class="hl-green">position: absolute;</span><br>
      <span class="hl-green">right: 15px;</span><br>
    }
  </div>

</div>
CSSコード表示
.pseudo-btn-wrapper {
  background-color: #f8f9fa;
  padding: 20px;
  border-radius: 8px;
  border: 1px solid #dee2e6;
}

.pseudo-caption {
  font-size: 13px;
  font-weight: bold;
  margin-bottom: 20px;
  color: #198754;
  text-align: center;
}

.pseudo-demo-area {
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: #e9ecef;
  padding: 40px 20px;
  border-radius: 4px;
  border: 1px dashed #adb5bd;
  margin-bottom: 20px;
}

/* 💡 ボタン本体のスタイル */
.pseudo-btn {
  /* 擬似要素の絶対配置の基準点にする */
  position: relative; 
  display: inline-block;
  padding: 15px 50px 15px 40px;
  background-color: #0d6efd;
  color: #fff;
  text-decoration: none;
  font-weight: bold;
  border-radius: 30px;
  transition: all 0.3s ease;
}

.pseudo-btn:hover {
  background-color: #0b5ed7;
  transform: translateY(-2px);
  box-shadow: 0 4px 10px rgba(13, 110, 253, 0.3);
}

/* 💡 ::before(左のアイコン) */
.pseudo-btn::before {
  content: "★";
  position: absolute;
  top: 50%;
  left: 15px;
  transform: translateY(-50%);
  color: #ffc107;
  font-size: 18px;
}

/* 💡 ::after(右の矢印) */
.pseudo-btn::after {
  content: "❯";
  position: absolute;
  top: 50%;
  right: 15px;
  transform: translateY(-50%);
  font-size: 12px;
  transition: right 0.3s ease;
}

/* ホバー時に矢印だけ少し右に動かすテクニック */
.pseudo-btn:hover::after {
  right: 10px;
}

/* コードブロック装飾 */
.pseudo-code {
  background-color: #282c34;
  color: #abb2bf;
  padding: 15px;
  border-radius: 4px;
  font-family: monospace;
  font-size: 13px;
  line-height: 1.6;
  border-left: 4px solid #0d6efd;
}

.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }

contentプロパティのルールと基本構文

擬似要素を扱う上で欠かせないプロパティがcontentです。

contentプロパティを指定しない限り、ブラウザ上には何一つ表示されません。

例えば、擬似要素を使って「NEW」のようなバッジやリボンなどの「装飾図形」を作る際は以下の3点セットです。

  1. content: "";:空文字を指定して要素を存在させる
  2. display: block;またはposition: absolute;:幅や高さを持たせる
  3. 親要素にposition: relative;をかけて位置を固定する。

これさえ守れば、HTMLの構造を汚すことなく、要素の好きな位置にラベルや装飾を貼り付けられます。

⭕️ contentプロパティを活用して、HTMLを汚さずにバッジを作る

最新の記事タイトル

ここに記事の抜粋文が入ります。HTMLには「NEW」という文字は存在しません。

/* 💡 親となるカード(はみ出したバッジを隠すためにoverflowを設定) */
.card {
  position: relative;
  overflow: hidden;
}

/* ⭕️ ::beforeを使って斜めの「NEW」リボンを作る */
.card::before {
  content: “NEW”; /* 💡 テキストを入れる場合 */
  position: absolute; /* 💡 幅と高さを持つために必須 */
  top: 15px;
  left: -35px;
  transform: rotate(-45deg); /* 💡 斜めに傾ける */
  background-color: #dc3545;
}
HTMLコード表示
<div class="pseudo-content-wrapper">
  
  <p class="pseudo-caption">⭕️ contentプロパティを活用して、HTMLを汚さずにバッジを作る</p>

  <div class="pseudo-demo-area">
    
    <div class="pseudo-card">
      <div class="pseudo-card-title">最新の記事タイトル</div>
      <p class="pseudo-card-txt">ここに記事の抜粋文が入ります。HTMLには「NEW」という文字は存在しません。</p>
    </div>

  </div>

  <div class="pseudo-code">
    /* 💡 親となるカード(はみ出したバッジを隠すためにoverflowを設定) */<br>
    <span class="hl-blue">.card</span> {<br>
      <span class="hl-green">position: relative;</span><br>
      <span class="hl-green">overflow: hidden;</span><br>
    }<br><br>

    /* ⭕️ ::beforeを使って斜めの「NEW」リボンを作る */<br>
    <span class="hl-blue">.card::before</span> {<br>
      <span class="hl-red">content: "NEW";</span> /* 💡 テキストを入れる場合 */<br>
      <span class="hl-green">position: absolute;</span> /* 💡 幅と高さを持つために必須 */<br>
      <span class="hl-green">top: 15px;</span><br>
      <span class="hl-green">left: -35px;</span><br>
      <span class="hl-green">transform: rotate(-45deg);</span> /* 💡 斜めに傾ける */<br>
      <span class="hl-green">background-color: #dc3545;</span><br>
    }
  </div>

</div>
CSSコード表示
.pseudo-content-wrapper {
  background-color: #f8f9fa;
  padding: 20px;
  border-radius: 8px;
  border: 1px solid #dee2e6;
}

.pseudo-demo-area {
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: #e9ecef;
  padding: 40px 20px;
  border-radius: 4px;
  border: 1px dashed #adb5bd;
  margin-bottom: 20px;
}

/* 💡 カード本体 */
.pseudo-card {
  position: relative;
  overflow: hidden; /* はみ出したリボンをカットする */
  background-color: #fff;
  border-radius: 8px;
  padding: 30px;
  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
  width: 100%;
  max-width: 350px;
}

.pseudo-card-title {
  margin: 0 0 10px 0;
  font-size: 18px;
  color: #333;
}

.pseudo-card-txt {
  margin: 0;
  font-size: 14px;
  color: #666;
  line-height: 1.5;
}

/* ⭕️ リボンバッジの作成 */
.pseudo-card::before {
  content: "NEW"; /* 表示するテキスト */
  position: absolute;
  top: 5px;
  left: -40px;
  width: 120px;
  padding: 5px 0;
  background-color: #dc3545;
  color: #fff;
  font-size: 12px;
  font-weight: bold;
  text-align: center;
  /* 45度回転させて左上に配置する */
  transform: rotate(-45deg);
  box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}

/* コードブロック装飾 */
.pseudo-code {
  background-color: #282c34;
  color: #abb2bf;
  padding: 15px;
  border-radius: 4px;
  font-family: monospace;
  font-size: 13px;
  line-height: 1.6;
  border-left: 4px solid #0d6efd;
}

.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }

併用するpositionプロパティの使い方を詳しく知りたい人は「【html&css】positionの使い方とabsolute・fixed・relativeの使い分け」を一読ください。

contentに文字・画像・アイコンを挿入する方法

::before::afterを実務で使いこなす心臓部となるのがcontentプロパティです。

このプロパティには、単なるテキストだけでなく、特殊な記号、画像、さらにはHTMLのデータ属性から取得した動的な値まで、様々なものを挿入できます。

ここでは、各挿入方法における書き方、「画像のサイズが変わらない問題」や「改行されない問題」の解決策を解説します。

contentに文字・画像・アイコンを挿入する方法
  • テキストの挿入と改行のテクニック
  • 画像やSVG・アイコンフォントの表示
  • attr()を使ったカスタムデータ属性の取得と番号

テキストの挿入と改行のテクニック

contentプロパティの基本的な使い方は、テキストの挿入です。

引用符を出力したり、Unicodeを使って特殊記号を表示したりできます。

また、実務であるのが「擬似要素の中で改行したい」というケースです。

CSSのcontent内で改行を行いたい場合は、改行コード\Aを記述し、同時に要素へwhite-space: pre;(または pre-wrap)を指定するのがよいです。

white-spaceの指定を忘れると、改行コード\Aを書いても半角スペースに変換されてしまい改行されません。

⭕️ \A と white-space: pre; を組み合わせた改行テクニック

改行バッジ

引用(Quotes)

CSSは奥が深い
/* ❌ brはそのまま文字として出力される */
.badge-wrong::before {
  content: “期間限定
SALE”;

}

/* ⭕️ \A と white-space: pre; をセットで使う */
.badge-correct::before {
  /* 💡 \A の部分で改行される */
  content: “期間限定\A SALE”;
  white-space: pre;
  text-align: center;
}

/* 💡 引用符(“ ”)を擬似要素で前後に自動挿入する */
blockquote::before {
  content: open-quote; /* 💡 始まりの引用符 */
}
blockquote::after {
  content: close-quote; /* 💡 終わりの引用符 */
}
HTMLコード表示
<div class="ct-tech-wrapper">
  
  <p class="ct-caption">⭕️ \A と white-space: pre; を組み合わせた改行テクニック</p>

  <div class="ct-demo-area">
    
    <div class="ct-box">
      <p class="ct-label">改行バッジ</p>
      <div class="ct-line-break-badge"></div>
    </div>

    <div class="ct-box" style="margin-left: 20px;">
      <p class="ct-label">引用(Quotes)</p>
      <blockquote class="ct-quote-text">CSSは奥が深い</blockquote>
    </div>

  </div>

  <div class="ct-code">
    /* ❌ brはそのまま文字として出力される */<br>
    <span class="hl-blue">.badge-wrong::before</span> {<br>
      <span class="hl-red">content: "期間限定<br>SALE";</span><br>
    }<br><br>

    /* ⭕️ \A と white-space: pre; をセットで使う */<br>
    <span class="hl-blue">.badge-correct::before</span> {<br>
      <span class="hl-comment">/* 💡 \A の部分で改行される */</span><br>
      <span class="hl-red">content: "期間限定\A SALE";</span><br>
      <span class="hl-red">white-space: pre;</span><br>
      <span class="hl-green">text-align: center;</span><br>
    }<br><br>

    /* 💡 引用符(“ ”)を擬似要素で前後に自動挿入する */<br>
    <span class="hl-blue">blockquote::before</span> {<br>
      <span class="hl-red">content: open-quote;</span> /* 💡 始まりの引用符 */<br>
    }<br>
    <span class="hl-blue">blockquote::after</span> {<br>
      <span class="hl-red">content: close-quote;</span> /* 💡 終わりの引用符 */<br>
    }
  </div>

</div>
CSSコード表示
.ct-tech-wrapper {
  background-color: #f8f9fa;
  padding: 20px;
  border-radius: 8px;
  border: 1px solid #dee2e6;
}

.ct-caption {
  font-size: 13px;
  font-weight: bold;
  margin-bottom: 20px;
  color: #198754;
  text-align: center;
}

.ct-demo-area {
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: #e9ecef;
  padding: 40px 20px;
  border-radius: 4px;
  border: 1px dashed #adb5bd;
  margin-bottom: 20px;
}

.ct-box {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.ct-label {
  font-size: 12px;
  font-weight: bold;
  margin-bottom: 15px;
  color: #333;
  text-align: center;
}

/* ⭕️ 改行バッジのスタイル */
.ct-line-break-badge::before {
  content: "期間限定\A SALE"; /* \Aで改行 */
  white-space: pre; /* これがないと改行されない */
  display: inline-block;
  background-color: #dc3545;
  color: white;
  padding: 10px 15px;
  border-radius: 4px;
  font-weight: bold;
  text-align: center;
  line-height: 1.4;
}

/* 💡 引用符のスタイル */
.ct-quote-text {
  font-size: 18px;
  font-weight: bold;
  color: #0d6efd;
  quotes: "“" "”"; /* 引用符の種類を指定 */
}

.ct-quote-text::before {
  content: open-quote;
  font-size: 24px;
  color: #adb5bd;
  margin-right: 5px;
}

.ct-quote-text::after {
  content: close-quote;
  font-size: 24px;
  color: #adb5bd;
  margin-left: 5px;
}

/* コードブロック装飾 */
.ct-code {
  background-color: #282c34;
  color: #abb2bf;
  padding: 15px;
  border-radius: 4px;
  font-family: monospace;
  font-size: 13px;
  line-height: 1.6;
  border-left: 4px solid #0d6efd;
}

.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }

white-spaceプロパティの使い方を詳しく知りたい人は「【html&css】white-spaceの使い方とnowrap・pre・pre-wrapの使い分け」を一読ください。

画像やSVG・アイコンフォントの表示

リストの先頭にチェックマークをつけたり、ボタンにアイコンを添えたりする表現は必須です。

これらには、画像やSVG、あるいはFont Awesomeを利用します。

::before::afterに画像を実装する際、content: url('icon.svg');のように指定します。

しかし、contentに直接URLを指定すると、画像が「元のサイズのまま」表示され、CSSでwidthheightを指定しても画像自体は一切リサイズされません。

画像やSVGをリサイズしてアイコンとして配置したい場合、content: "";と空にしておき、background-imagebackground-size: contain;の組み合わせで表示するのがルールです。

また、Font Awesomeを使う場合は、Unicode(例:content: "\f00c";)を指定した上で、font-family: "Font Awesome 6 Free";font-weight: 900;をセットで指定しないと、文字化けしてしまいます。

⭕️ 画像は background-image で挿入すれば自由にサイズ変更可能

❌ content: url() はサイズ変更不可(はみ出す)
⭕️ background-image なら綺麗にリサイズ可能
💡 Unicode(\f058)で表現するアイコン
/* ❌ 画像の元サイズで表示され、widthが効かない */
.item-wrong::before {
  content: url(‘icon.svg’);
  width: 20px; /* 💡 画像自体は縮まない */
}

/* ⭕️ 背景画像として設定し、background-sizeで枠に合わせる */
.item-correct::before {
  content: “”; /* 💡 必ず空文字を入れる */
  display: inline-block;
  width: 20px;
  height: 20px;
  background-image: url(‘icon.svg’);
  background-size: contain; /* 💡 これで20pxに収まる */
}

/* 💡 アイコンフォント(FontAwesome等)の場合はfont-familyとweightが必須 */
.item-font::before {
  content: “\f058”; /* 💡 Unicode */
  font-family: “Font Awesome 6 Free”;
  font-weight: 900; /* 💡 Solidアイコンには必須 */
}
HTMLコード表示
<div class="ct-tech-wrapper">
  
  <p class="ct-caption">⭕️ 画像は background-image で挿入すれば自由にサイズ変更可能</p>

  <div class="ct-demo-area" style="flex-direction: column; align-items: flex-start; padding-left: 40px;">
    
    <div class="ct-list-item is-wrong-img">
      ❌ content: url() はサイズ変更不可(はみ出す)
    </div>

    <div class="ct-list-item is-correct-img">
      ⭕️ background-image なら綺麗にリサイズ可能
    </div>

    <div class="ct-list-item is-font-icon">
      💡 Unicode(\f058)で表現するアイコン
    </div>

  </div>

  <div class="ct-code">
    /* ❌ 画像の元サイズで表示され、widthが効かない */<br>
    <span class="hl-blue">.item-wrong::before</span> {<br>
      <span class="hl-red">content: url('icon.svg');</span><br>
      <span class="hl-green">width: 20px;</span> /* 💡 画像自体は縮まない */<br>
    }<br><br>

    /* ⭕️ 背景画像として設定し、background-sizeで枠に合わせる */<br>
    <span class="hl-blue">.item-correct::before</span> {<br>
      <span class="hl-red">content: "";</span> /* 💡 必ず空文字を入れる */<br>
      <span class="hl-green">display: inline-block;</span><br>
      <span class="hl-green">width: 20px;</span><br>
      <span class="hl-green">height: 20px;</span><br>
      <span class="hl-red">background-image: url('icon.svg');</span><br>
      <span class="hl-red">background-size: contain;</span> /* 💡 これで20pxに収まる */<br>
    }<br><br>

    /* 💡 アイコンフォント(FontAwesome等)の場合はfont-familyとweightが必須 */<br>
    <span class="hl-blue">.item-font::before</span> {<br>
      <span class="hl-red">content: "\f058";</span> /* 💡 Unicode */<br>
      <span class="hl-green">font-family: "Font Awesome 6 Free";</span><br>
      <span class="hl-green">font-weight: 900;</span> /* 💡 Solidアイコンには必須 */<br>
    }
  </div>

</div>
CSSコード表示
.ct-list-item {
  position: relative;
  padding-left: 35px;
  margin-bottom: 15px;
  font-size: 15px;
  font-weight: bold;
  color: #333;
}

/* ❌ 失敗パターンの擬似要素シミュレーション */
/* ※デモ用のため、巨大なダミー画像をクリップした状態を再現 */
.is-wrong-img::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 20px;
  height: 20px;
  background-color: #dc3545; /* 画像がはみ出しているイメージ */
  border-right: 10px solid #f8d7da;
  border-bottom: 10px solid #f8d7da;
}

/* ⭕️ 成功パターン(background-image) */
.is-correct-img::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  display: inline-block;
  width: 20px;
  height: 20px;
  /* 💡 ダミーのSVGアイコン(チェックマーク)をデータURIで指定 */
  background-image: url('data:image/svg+xml;utf8,<svg viewBox="0 0 24 24" fill="%230d6efd" xmlns="http://www.w3.org/2000/svg"><path d="M12 2C6.48 2 2 6.48 2 12C2 17.52 6.48 22 12 22C17.52 22 22 17.52 22 12C22 6.48 17.52 2 12 2ZM10 17L5 12L6.41 10.59L10 14.17L17.59 6.58L19 8L10 17Z"/></svg>');
  background-size: contain;
  background-repeat: no-repeat;
}

/* 💡 アイコンフォントパターン */
.is-font-icon::before {
  /* FontAwesomeがなくても表示できるよう、標準絵文字で代用デモ */
  content: "✅";
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  font-size: 18px;
}

background-imageプロパティの使い方を詳しく知りたい人は「【CSS】background-imageの使い方:サイズ・透過・レスポンシブ」を一読ください。

attr()を使ったカスタムデータ属性の取得と番号

CSSのcontentプロパティには、HTMLに記述したdata-*属性の値を動的に引っ張ってくるattr()関数やリストの番号を自動でカウントアップするcounter()関数が存在します。

例えば、ツールチップを実装する際は、HTMLを汚さずに<span data-tooltip="解説文">テキスト</span>のようにデータ属性を持たせ、CSS側でcontent: attr(data-tooltip);と呼び出してホバー時に表示させるのがメンテナンス性の高い手法です。

自動連番リストを作る際は親に counter-reset子に counter-increment擬似要素に content: counter() の3ステップをセットで覚えるのがよいです。

⭕️ attr()でHTMLの値を取得し、counter()で自動連番を作る

attr() ツールチップ

ここをホバー 🔍

counter() ステップリスト

  • 申し込み
  • 審査
  • 完了
/* ⭕️ HTMLの data-text 属性の値を、content に流し込む */
.tooltip:hover::after {
  content: attr(data-text);
  background-color: #333;
}

/* ⭕️ カウンターは「親でリセット」し「子で増やす」 */
.step-list {
  /* 💡 親要素で “my-count” という名前のカウンターを0にリセット */
  counter-reset: my-count;
}
.step-item::before {
  /* 💡 要素が現れるたびに “my-count” を1ずつ増やす */
  counter-increment: my-count;
  /* 💡 現在の数値を content に出力する */
  content: “STEP ” counter(my-count);
}
HTMLコード表示
<div class="ct-tech-wrapper">
  
  <p class="ct-caption">⭕️ attr()でHTMLの値を取得し、counter()で自動連番を作る</p>

  <div class="ct-demo-area" style="gap: 50px;">
    
    <div class="ct-box">
      <p class="ct-label">attr() ツールチップ</p>
      <span class="ct-tooltip-trigger" data-text="CSSだけで実装したふきだしです!">
        ここをホバー 🔍
      </span>
    </div>

    <div class="ct-box">
      <p class="ct-label">counter() ステップリスト</p>
      <ul class="ct-step-list">
        <li class="ct-step-item">申し込み</li>
        <li class="ct-step-item">審査</li>
        <li class="ct-step-item">完了</li>
      </ul>
    </div>

  </div>

  <div class="ct-code">
    /* ⭕️ HTMLの data-text 属性の値を、content に流し込む */<br>
    <span class="hl-blue">.tooltip:hover::after</span> {<br>
      <span class="hl-red">content: attr(data-text);</span><br>
      <span class="hl-green">background-color: #333;</span><br>
    }<br><br>

    /* ⭕️ カウンターは「親でリセット」し「子で増やす」 */<br>
    <span class="hl-blue">.step-list</span> {<br>
      <span class="hl-comment">/* 💡 親要素で "my-count" という名前のカウンターを0にリセット */</span><br>
      <span class="hl-red">counter-reset: my-count;</span><br>
    }<br>
    <span class="hl-blue">.step-item::before</span> {<br>
      <span class="hl-comment">/* 💡 要素が現れるたびに "my-count" を1ずつ増やす */</span><br>
      <span class="hl-red">counter-increment: my-count;</span><br>
      <span class="hl-comment">/* 💡 現在の数値を content に出力する */</span><br>
      <span class="hl-red">content: "STEP " counter(my-count);</span><br>
    }
  </div>

</div>
CSSコード表示
/* 💡 ツールチップのスタイル */
.ct-tooltip-trigger {
  position: relative;
  color: #0d6efd;
  font-weight: bold;
  cursor: pointer;
  border-bottom: 2px dashed #0d6efd;
  padding-bottom: 2px;
}

.ct-tooltip-trigger::after {
  /* 初期状態は空ではなく、attr()を入れておき透明にしておく */
  content: attr(data-text);
  position: absolute;
  bottom: 130%;
  left: 50%;
  transform: translateX(-50%);
  background-color: #333;
  color: #fff;
  padding: 5px 10px;
  border-radius: 4px;
  font-size: 12px;
  white-space: nowrap;
  opacity: 0;
  visibility: hidden;
  transition: all 0.2s ease;
}

/* ホバーで表示 */
.ct-tooltip-trigger:hover::after {
  opacity: 1;
  visibility: visible;
  bottom: 150%;
}

/* 💡 ステップリストのスタイル */
.ct-step-list {
  list-style: none;
  padding: 0;
  margin: 0;
  /* ⭕️ 親でカウンターをリセット */
  counter-reset: step-counter; 
}

.ct-step-item {
  position: relative;
  padding-left: 70px;
  margin-bottom: 15px;
  font-size: 15px;
  font-weight: bold;
  color: #333;
  line-height: 24px;
}

.ct-step-item::before {
  /* ⭕️ 子要素ごとにカウンターを増やす */
  counter-increment: step-counter;
  /* ⭕️ 現在の数値を表示 */
  content: "STEP " counter(step-counter);
  
  position: absolute;
  left: 0;
  top: 0;
  background-color: #198754;
  color: white;
  font-size: 11px;
  padding: 2px 8px;
  border-radius: 12px;
}

positionを使った自由な配置とレイアウト調整

擬似要素(::before::after)を扱う上で、contentプロパティと同じくらい重要になるのがレイアウトの制御です。

擬似要素はデフォルトではインライン要素としてテキストの並びに挿入されますが、自由に動かすためにはpositionプロパティを使った絶対配置の知識が欠かせません。

ここでは、思い通りの場所に配置する絶対配置の基本、ど真ん中への「中央寄せ」、要素同士を重ねる際のz-indexついて解説します。

positionを使った自由な配置とレイアウト調整
  • position: absoluteによる絶対配置と中央寄せ
  • 要素の重なりの制御と背景への配置

position: absoluteによる絶対配置と中央寄せ

擬似要素をアイコンや装飾として自由な位置に配置したい場合、必須となるのがpositionによる絶対配置です。

また、擬似要素で作った図形を親要素の「中央寄せ」や「上下中央」に配置するテクニックは、実務で毎日使います。

擬似要素を親要素の中央に配置するには、top: 50%; left: 50%; transform: translate(-50%, -50%);の3点セットです。

これを使えば、要素のサイズが変わっても中央配置を維持できます。

⭕️ 親に relative、子(擬似要素)に absolute が絶対配置のルール

❌ relativeなし
⭕️ 中央寄せ
/* ❌ 親に relative がないと、画面全体を基準にして飛んでいく */
.box-wrong {
  /* 💡 position: relative; が抜けている */
}
.box-wrong::before {
  content: “★”;
  position: absolute;
  top: 0;
}

/* ⭕️ 親に relative をかけ、transform で完璧な中央寄せを作る */
.box-correct {
  position: relative; /* 💡 基準点を作る(超重要) */
}
.box-correct::before {
  content: “★”;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%); /* 💡 自身のサイズの半分だけ戻す */
}
HTMLコード表示
<div class="pos-tech-wrapper">
  
  <p class="pos-caption">⭕️ 親に relative、子(擬似要素)に absolute が絶対配置のルール</p>

  <div class="pos-demo-area">
    
    <div class="pos-box is-wrong-pos">
      ❌ relativeなし
      </div>

    <div class="pos-box is-correct-pos">
      ⭕️ 中央寄せ
    </div>

  </div>

  <div class="pos-code">
    /* ❌ 親に relative がないと、画面全体を基準にして飛んでいく */<br>
    <span class="hl-blue">.box-wrong</span> {<br>
      <span class="hl-comment">/* 💡 position: relative; が抜けている */</span><br>
    }<br>
    <span class="hl-blue">.box-wrong::before</span> {<br>
      <span class="hl-green">content: "★";</span><br>
      <span class="hl-red">position: absolute;</span><br>
      <span class="hl-green">top: 0;</span><br>
    }<br><br>

    /* ⭕️ 親に relative をかけ、transform で完璧な中央寄せを作る */<br>
    <span class="hl-blue">.box-correct</span> {<br>
      <span class="hl-red">position: relative;</span> /* 💡 基準点を作る(超重要) */<br>
    }<br>
    <span class="hl-blue">.box-correct::before</span> {<br>
      <span class="hl-green">content: "★";</span><br>
      <span class="hl-red">position: absolute;</span><br>
      <span class="hl-green">top: 50%;</span><br>
      <span class="hl-green">left: 50%;</span><br>
      <span class="hl-red">transform: translate(-50%, -50%);</span> /* 💡 自身のサイズの半分だけ戻す */<br>
    }
  </div>

</div>
CSSコード表示
.pos-tech-wrapper {
  background-color: #f8f9fa;
  padding: 20px;
  border-radius: 8px;
  border: 1px solid #dee2e6;
}

.pos-caption {
  font-size: 13px;
  font-weight: bold;
  margin-bottom: 20px;
  color: #198754;
  text-align: center;
}

.pos-demo-area {
  display: flex;
  gap: 30px;
  justify-content: center;
  align-items: center;
  background-color: #e9ecef;
  padding: 50px 20px;
  border-radius: 4px;
  border: 1px dashed #adb5bd;
  margin-bottom: 20px;
}

.pos-box {
  width: 140px;
  height: 140px;
  background-color: #fff;
  border: 2px solid #ced4da;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: bold;
  font-size: 13px;
  color: #6c757d;
  text-align: center;
}

/* ❌ 失敗:親にrelativeがないため、デモ枠のさらに外(ブラウザ基準等)へ飛ぶ */
/* ※このデモでは影響を防ぐため、わざと表示させないようにしています */

/* ⭕️ 成功:完璧な中央配置 */
.is-correct-pos {
  /* 💡 基準点 */
  position: relative; 
  border-color: #0d6efd;
  color: #0d6efd;
}

.is-correct-pos::before {
  content: "★";
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 40px;
  color: rgba(13, 110, 253, 0.2);
  /* 💡 文字の裏に配置するため */
  z-index: 0;
}

.pos-code {
  background-color: #282c34;
  color: #abb2bf;
  padding: 15px;
  border-radius: 4px;
  font-family: monospace;
  font-size: 13px;
  line-height: 1.6;
  border-left: 4px solid #0d6efd;
}

.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }

要素の重なりの制御と背景への配置

画像を重ねる表現や背景画像の上に薄暗いフィルターをかけるオーバーレイは、モダンなWebデザインの必須テクニックです。

これらは、::beforeを背景として広げ、背景色や背景画像を設定し、z-indexを使って重なり順を制御することで実現します。

オーバーレイ(半透明の黒いフィルターなど)を作る際、z-index: -1;を極力使いません。

代わりに、親要素をposition: relative; z-index: 0;のようにスタッキングコンテキスト(重なりの基準)とし、擬似要素のオーバーレイをz-index: 1;、前面に出したいテキストなどをposition: relative; z-index: 2;にするのが背景の裏側に潜り込まない安全な施策です。

⭕️ z-index はマイナスを使わず、正の数で順序を明確に管理する

❌ -1の罠
(親の背景色の裏に消える)
⭕️ 正しい重なり
(綺麗なオーバーレイ)
/* ❌ z-index: -1 を指定すると、親要素の背景の裏に隠れて見えなくなる */
.overlay-wrong::before {
  content: “”;
  position: absolute;
  inset: 0; /* 💡 top, right, bottom, left をすべて0にするショートハンド */
  background-color: rgba(0, 0, 0, 0.5);
  z-index: -1; /* 💡 罠にハマる原因 */
}

/* ⭕️ 親=0、擬似要素(フィルター)=1、テキスト=2 の順番で重ねる */
.overlay-correct {
  position: relative;
  z-index: 0; /* 💡 重なりの基準を作る */
}
.overlay-correct::before {
  content: “”;
  position: absolute;
  inset: 0;
  background-color: rgba(0, 0, 0, 0.5);
  z-index: 1; /* 💡 フィルターを1にする */
}
.overlay-correct .text {
  position: relative;
  z-index: 2; /* 💡 テキストを一番手前(2)にする */
}
HTMLコード表示
<div class="zindex-tech-wrapper">
  
  <p class="zindex-caption">⭕️ z-index はマイナスを使わず、正の数で順序を明確に管理する</p>

  <div class="zindex-demo-area">
    
    <div class="zindex-box is-wrong-zindex">
      <span class="zindex-txt">❌ -1の罠<br><small>(親の背景色の裏に消える)</small></span>
    </div>

    <div class="zindex-box is-correct-zindex">
      <span class="zindex-txt">⭕️ 正しい重なり<br><small>(綺麗なオーバーレイ)</small></span>
    </div>

  </div>

  <div class="zindex-code">
    /* ❌ z-index: -1 を指定すると、親要素の背景の裏に隠れて見えなくなる */<br>
    <span class="hl-blue">.overlay-wrong::before</span> {<br>
      <span class="hl-green">content: "";</span><br>
      <span class="hl-green">position: absolute;</span><br>
      <span class="hl-green">inset: 0;</span> /* 💡 top, right, bottom, left をすべて0にするショートハンド */<br>
      <span class="hl-green">background-color: rgba(0, 0, 0, 0.5);</span><br>
      <span class="hl-red">z-index: -1;</span> /* 💡 罠にハマる原因 */<br>
    }<br><br>

    /* ⭕️ 親=0、擬似要素(フィルター)=1、テキスト=2 の順番で重ねる */<br>
    <span class="hl-blue">.overlay-correct</span> {<br>
      <span class="hl-green">position: relative;</span><br>
      <span class="hl-red">z-index: 0;</span> /* 💡 重なりの基準を作る */<br>
    }<br>
    <span class="hl-blue">.overlay-correct::before</span> {<br>
      <span class="hl-green">content: "";</span><br>
      <span class="hl-green">position: absolute;</span><br>
      <span class="hl-green">inset: 0;</span><br>
      <span class="hl-green">background-color: rgba(0, 0, 0, 0.5);</span><br>
      <span class="hl-red">z-index: 1;</span> /* 💡 フィルターを1にする */<br>
    }<br>
    <span class="hl-blue">.overlay-correct .text</span> {<br>
      <span class="hl-green">position: relative;</span><br>
      <span class="hl-red">z-index: 2;</span> /* 💡 テキストを一番手前(2)にする */<br>
    }
  </div>

</div>
CSSコード表示
.zindex-tech-wrapper {
  background-color: #f8f9fa;
  padding: 20px;
  border-radius: 8px;
  border: 1px solid #dee2e6;
}

.zindex-caption {
  font-size: 13px;
  font-weight: bold;
  margin-bottom: 20px;
  color: #198754;
  text-align: center;
}

.zindex-demo-area {
  display: flex;
  gap: 30px;
  justify-content: center;
  align-items: center;
  background-color: #e9ecef;
  padding: 40px 20px;
  border-radius: 4px;
  border: 1px dashed #adb5bd;
  margin-bottom: 20px;
}

/* 背景画像を持たせた共通のボックス設定 */
.zindex-box {
  position: relative;
  width: 160px;
  height: 120px;
  background-color: #0d6efd; /* 親要素の背景色 */
  background-image: url('https://picsum.photos/id/1015/300/200'); /* ダミーの背景画像 */
  background-size: cover;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  color: white;
  overflow: hidden;
}

.zindex-txt {
  font-weight: bold;
  font-size: 13px;
}

/* ❌ 失敗パターン */
.is-wrong-zindex::before {
  content: "";
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  background-color: rgba(0, 0, 0, 0.7); /* 暗いフィルター */
  z-index: -1; /* 親の背景(青色や画像)の後ろに回ってしまうため見えない */
}

/* ⭕️ 成功パターン */
.is-correct-zindex {
  /* z-index: 0は書かなくても動作しますが、明示的なスタッキングコンテキストとして機能します */
}

.is-correct-zindex::before {
  content: "";
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  background-color: rgba(0, 0, 0, 0.6);
  z-index: 1; /* 画像の手前、テキストの奥 */
}

.is-correct-zindex .zindex-txt {
  /* テキストを前面に出すための必須指定 */
  position: relative;
  z-index: 2;
}

/* コードブロック装飾 */
.zindex-code {
  background-color: #282c34;
  color: #abb2bf;
  padding: 15px;
  border-radius: 4px;
  font-family: monospace;
  font-size: 13px;
  line-height: 1.6;
  border-left: 4px solid #0d6efd;
}

.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }

重なりを制御するz-indexプロパティの使い方を詳しく知りたい人は「【html&css】z-indexとは?使い方と効かない時の対処」を一読ください。

CSSだけで図形を作る!丸・三角形・線のデザイン

Webデザインにおいて、アクセントや図形のために画像(SVGやPNG)を用意するのは、表示速度やメンテナンス性の観点から推奨されません。

擬似要素(::before::after)の魅力は、HTMLを一切汚さずに「CSSだけで自由自在に図形を描画できる」点にあります。

ここでは、実務で使用される「丸」「線」「三角形」「矢印」の作り方、目を惹きつけるホバーアニメーションの実装を解説します。

CSSだけで図形を作る!丸・三角形・線のデザイン
  • 丸や線を引く
  • borderプロパティを応用した三角形や矢印の作成
  • ボタンのホバーエフェクトとアニメーション

丸や線を引く

箇条書きリスト(<li>)の先頭にある黒い点をオリジナルデザインに変更したり、見出しの横や下に装飾となる直線を引いたりする表現はWebデザインの基本です。

これらは、擬似要素に幅と高さを持たせて「丸」や「線」を描画することで実現します。

リストのポチや見出しの線を配置する際、position: absolute;を使って絶対配置にします。

親要素(<li><hX>)に対してpadding-leftを図形の幅以上にしっかり確保することで、テキストが何行になってもアイコンの下に潜り込まないインデントを維持できます。

⭕️ 絶対配置とpaddingの組み合わせで、テキストの折り返し崩れを防ぐ

見出しのアクセント線

  • 短いテキストのリスト項目
  • 画面幅が狭くなってテキストが2行や3行に折り返したとしても、絶対に丸アイコンの下には潜り込まず、綺麗なインデントを保ちます。
/* 💡 見出しの左側に線を引く(css before line) */
.heading {
  position: relative;
  padding-left: 15px; /* 💡 線の幅+余白を確保 */
}
.heading::before {
  content: “”;
  position: absolute;
  left: 0;
  top: 0;
  width: 4px; /* 💡 線の太さ */
  height: 100%; /* 💡 親の高さに合わせる */
  background-color: #0d6efd;
}

/* ⭕️ 折り返しても崩れないカスタムリスト(css before dot) */
.list-item {
  position: relative;
  padding-left: 20px; /* 💡 テキストの開始位置をずらす */
}
.list-item::before {
  content: “”;
  position: absolute;
  left: 0;
  top: 8px; /* 💡 1行目の文字の高さに合わせる */
  width: 8px;
  height: 8px;
  border-radius: 50%; /* 💡 丸くする */
  background-color: #dc3545;
}
HTMLコード表示
<div class="shape-basic-wrapper">
  
  <p class="shape-caption">⭕️ 絶対配置とpaddingの組み合わせで、テキストの折り返し崩れを防ぐ</p>

  <div class="shape-demo-area">
    
    <h4 class="shape-heading">見出しのアクセント線</h4>

    <ul class="shape-list">
      <li class="shape-list-item">短いテキストのリスト項目</li>
      <li class="shape-list-item">画面幅が狭くなってテキストが2行や3行に折り返したとしても、絶対に丸アイコンの下には潜り込まず、綺麗なインデントを保ちます。</li>
    </ul>

  </div>

  <div class="shape-code">
    /* 💡 見出しの左側に線を引く(css before line) */<br>
    <span class="hl-blue">.heading</span> {<br>
      <span class="hl-green">position: relative;</span><br>
      <span class="hl-green">padding-left: 15px;</span> /* 💡 線の幅+余白を確保 */<br>
    }<br>
    <span class="hl-blue">.heading::before</span> {<br>
      <span class="hl-red">content: "";</span><br>
      <span class="hl-green">position: absolute;</span><br>
      <span class="hl-green">left: 0;</span><br>
      <span class="hl-green">top: 0;</span><br>
      <span class="hl-green">width: 4px;</span> /* 💡 線の太さ */<br>
      <span class="hl-green">height: 100%;</span> /* 💡 親の高さに合わせる */<br>
      <span class="hl-green">background-color: #0d6efd;</span><br>
    }<br><br>

    /* ⭕️ 折り返しても崩れないカスタムリスト(css before dot) */<br>
    <span class="hl-blue">.list-item</span> {<br>
      <span class="hl-green">position: relative;</span><br>
      <span class="hl-red">padding-left: 20px;</span> /* 💡 テキストの開始位置をずらす */<br>
    }<br>
    <span class="hl-blue">.list-item::before</span> {<br>
      <span class="hl-red">content: "";</span><br>
      <span class="hl-green">position: absolute;</span><br>
      <span class="hl-green">left: 0;</span><br>
      <span class="hl-green">top: 8px;</span> /* 💡 1行目の文字の高さに合わせる */<br>
      <span class="hl-green">width: 8px;</span><br>
      <span class="hl-green">height: 8px;</span><br>
      <span class="hl-green">border-radius: 50%;</span> /* 💡 丸くする */<br>
      <span class="hl-green">background-color: #dc3545;</span><br>
    }
  </div>

</div>
CSSコード表示
.shape-basic-wrapper {
  background-color: #f8f9fa;
  padding: 20px;
  border-radius: 8px;
  border: 1px solid #dee2e6;
}

.shape-caption {
  font-size: 13px;
  font-weight: bold;
  margin-bottom: 20px;
  color: #198754;
  text-align: center;
}

.shape-demo-area {
  background-color: #fff;
  padding: 30px;
  border-radius: 4px;
  border: 1px dashed #adb5bd;
  margin-bottom: 20px;
  max-width: 400px;
  margin-left: auto;
  margin-right: auto;
}

/* 💡 見出しの線(line) */
.shape-heading {
  position: relative;
  padding-left: 15px;
  margin-top: 0;
  margin-bottom: 20px;
  font-size: 18px;
  color: #333;
}
.shape-heading::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  width: 4px;
  height: 100%;
  background-color: #0d6efd;
  border-radius: 2px;
}

/* ⭕️ 折り返さないリストの丸(dot) */
.shape-list {
  list-style: none; /* デフォルトのポチを消す */
  padding: 0;
  margin: 0;
}
.shape-list-item {
  position: relative;
  padding-left: 20px; /* アイコン分の余白を絶対確保 */
  margin-bottom: 12px;
  font-size: 14px;
  line-height: 1.6;
  color: #555;
}
.shape-list-item::before {
  content: "";
  position: absolute;
  left: 0;
  top: 7px; /* 1行目の高さに微調整 */
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background-color: #dc3545;
}

/* コードブロック装飾 */
.shape-code {
  background-color: #282c34;
  color: #abb2bf;
  padding: 15px;
  border-radius: 4px;
  font-family: monospace;
  font-size: 13px;
  line-height: 1.6;
  border-left: 4px solid #0d6efd;
}

.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }

リストの使い方を詳しく知りたい人は「【html&css】ul・li・olの使い方とおしゃれなデザインリスト」を一読ください。

borderプロパティを応用した三角形や矢印の作成

CSSで塗りつぶしの三角形を作る際、widthheightを0にした上で、borderプロパティの重なりを利用するテクニックは長年使われているハックです。

一方、「線のみの矢印」を作る場合は、要素に2辺だけborderを引き、回転(transform: rotate)させる手法が用いられます。

「塗りつぶしの三角形」はborderハックで作りますが、「線だけの矢印」を作る際に::before::afterの両方を使って三角形をくり抜く古い書き方をする人がいます。

これはコードが冗長になるためNGです。

線だけの矢印は、::before1つだけを使い、四角形のborder-topborder-rightに色をつけ、transform: rotate(45deg);で45度回転させるのがクリーンな実装です。

⭕️ 用途で使い分ける!塗りつぶしは「border」、線だけなら「rotate」

塗りつぶし三角
borderハック

線だけの矢印
border + rotate

/* 💡 上向きの「塗りつぶし三角形」(css before 三角) */
.solid-triangle::before {
  content: “”;
  width: 0; /* 💡 縦横は必ずゼロ */
  height: 0;
  /* 💡 左右を透明にし、下(bottom)に色をつけると「上向き」になる */
  border-left: 20px solid transparent;
  border-right: 20px solid transparent;
  border-bottom: 30px solid #dc3545;
}

/* ⭕️ 右向きの「線だけの矢印」(css before 矢印) */
.line-arrow::before {
  content: “”;
  width: 15px; /* 💡 四角形のサイズを作る */
  height: 15px;
  /* 💡 上と右だけ線を引く */
  border-top: 3px solid #0d6efd;
  border-right: 3px solid #0d6efd;
  /* 💡 その四角形を時計回りに45度傾けることで「右向きの矢印」になる */
  transform: rotate(45deg);
}
HTMLコード表示
<div class="shape-arrow-wrapper">
  
  <p class="shape-caption">⭕️ 用途で使い分ける!塗りつぶしは「border」、線だけなら「rotate」</p>

  <div class="shape-demo-area" style="display: flex; justify-content: space-around;">
    
    <div class="shape-box">
      <p class="shape-label">塗りつぶし三角<br><small>borderハック</small></p>
      <div class="shape-triangle"></div>
    </div>

    <div class="shape-box">
      <p class="shape-label">線だけの矢印<br><small>border + rotate</small></p>
      <div class="shape-arrow"></div>
    </div>

  </div>

  <div class="shape-code">
    /* 💡 上向きの「塗りつぶし三角形」(css before 三角) */<br>
    <span class="hl-blue">.solid-triangle::before</span> {<br>
      <span class="hl-red">content: "";</span><br>
      <span class="hl-green">width: 0;</span> /* 💡 縦横は必ずゼロ */<br>
      <span class="hl-green">height: 0;</span><br>
      <span class="hl-comment">/* 💡 左右を透明にし、下(bottom)に色をつけると「上向き」になる */</span><br>
      <span class="hl-red">border-left: 20px solid transparent;</span><br>
      <span class="hl-red">border-right: 20px solid transparent;</span><br>
      <span class="hl-red">border-bottom: 30px solid #dc3545;</span><br>
    }<br><br>

    /* ⭕️ 右向きの「線だけの矢印」(css before 矢印) */<br>
    <span class="hl-blue">.line-arrow::before</span> {<br>
      <span class="hl-red">content: "";</span><br>
      <span class="hl-green">width: 15px;</span> /* 💡 四角形のサイズを作る */<br>
      <span class="hl-green">height: 15px;</span><br>
      <span class="hl-comment">/* 💡 上と右だけ線を引く */</span><br>
      <span class="hl-red">border-top: 3px solid #0d6efd;</span><br>
      <span class="hl-red">border-right: 3px solid #0d6efd;</span><br>
      <span class="hl-comment">/* 💡 その四角形を時計回りに45度傾けることで「右向きの矢印」になる */</span><br>
      <span class="hl-red">transform: rotate(45deg);</span><br>
    }
  </div>

</div>
CSSコード表示
.shape-arrow-wrapper {
  background-color: #f8f9fa;
  padding: 20px;
  border-radius: 8px;
  border: 1px solid #dee2e6;
}

.shape-box {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.shape-label {
  font-size: 12px;
  font-weight: bold;
  margin-bottom: 20px;
  color: #333;
  text-align: center;
}

/* 💡 塗りつぶし三角形 */
.shape-triangle {
  width: 0;
  height: 0;
  /* 下辺に色をつけて上向きにする */
  border-left: 25px solid transparent;
  border-right: 25px solid transparent;
  border-bottom: 40px solid #dc3545;
}

/* 💡 線だけの矢印(くの字) */
.shape-arrow {
  width: 25px;
  height: 25px;
  /* 上と右だけにborderを引く */
  border-top: 5px solid #0d6efd;
  border-right: 5px solid #0d6efd;
  /* 45度回転させて矢印にする */
  transform: rotate(45deg);
  margin-top: 5px; /* 表示位置の微調整 */
}

回転を制御するtransformプロパティの使い方を詳しく知りたい人は「【CSS】transformの使い方:移動・回転・拡大と効かない時の対策」を一読ください。

ボタンのホバーエフェクトとアニメーション

モダンなWebサイトにおいて、::before::afterを活用したボタンのアニメーションは必要不可欠です。

例えば、ボタンにマウスを乗せると「背景色が左からサッとスライドしてくる」、あるいは「アイコンがクルッと回転する」といった表現は、全て擬似要素とtransitionの組み合わせで作られています。

アニメーションのトランジションは、「ホバーしていない状態(ベースの要素)」に記述します。

また、背景色がスライドするようなアニメーションを作る際、widthleftを動かしません。

これらはブラウザの再描画を発生させ、カクつく原因になります。

transform: scaleX()transform: translateX()を使ってGPUでアニメーションさせるとヌルヌル動くボタンを作れます。

⭕️ 鉄則:transitionはベースに記述し、transformで滑らかに動かす

/* 💡 ベースとなるボタンの設定(はみ出しを隠す) */
.anim-btn {
  position: relative;
  overflow: hidden;
}

/* ⭕️ 背景色用の擬似要素を作る。最初はscaleX(0)で潰しておく */
.anim-btn::before {
  content: “”;
  position: absolute;
  inset: 0;
  background-color: #0d6efd;
  transform-origin: left; /* 💡 左を起点にする */
  transform: scaleX(0); /* 💡 横幅を0倍にして見えなくする */
  /* ⭕️ transitionはhover側ではなく、こっち(ベース)に書く! */
  transition: transform 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
}

/* 💡 ホバーされた時(css before hover transition) */
.anim-btn:hover::before {
  transform: scaleX(1); /* 💡 横幅を1倍(100%)に戻して背景を埋める */
}
HTMLコード表示
<div class="shape-hover-wrapper">
  
  <p class="shape-caption">⭕️ 鉄則:transitionはベースに記述し、transformで滑らかに動かす</p>

  <div class="shape-demo-area" style="text-align: center;">
    
    <a href="#" class="shape-anim-btn">
      <span class="shape-anim-txt">HOVER ME</span>
    </a>

  </div>

  <div class="shape-code">
    /* 💡 ベースとなるボタンの設定(はみ出しを隠す) */<br>
    <span class="hl-blue">.anim-btn</span> {<br>
      <span class="hl-green">position: relative;</span><br>
      <span class="hl-green">overflow: hidden;</span><br>
    }<br><br>

    /* ⭕️ 背景色用の擬似要素を作る。最初はscaleX(0)で潰しておく */<br>
    <span class="hl-blue">.anim-btn::before</span> {<br>
      <span class="hl-red">content: "";</span><br>
      <span class="hl-green">position: absolute;</span><br>
      <span class="hl-green">inset: 0;</span><br>
      <span class="hl-green">background-color: #0d6efd;</span><br>
      <span class="hl-green">transform-origin: left;</span> /* 💡 左を起点にする */<br>
      <span class="hl-red">transform: scaleX(0);</span> /* 💡 横幅を0倍にして見えなくする */<br>
      <span class="hl-comment">/* ⭕️ transitionはhover側ではなく、こっち(ベース)に書く! */</span><br>
      <span class="hl-red">transition: transform 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);</span><br>
    }<br><br>

    /* 💡 ホバーされた時(css before hover transition) */<br>
    <span class="hl-blue">.anim-btn:hover::before</span> {<br>
      <span class="hl-red">transform: scaleX(1);</span> /* 💡 横幅を1倍(100%)に戻して背景を埋める */<br>
    }
  </div>

</div>
CSSコード表示
.shape-hover-wrapper {
  background-color: #f8f9fa;
  padding: 20px;
  border-radius: 8px;
  border: 1px solid #dee2e6;
}

.shape-anim-btn {
  /* 基準点と、はみ出し隠し */
  position: relative;
  display: inline-block;
  overflow: hidden;
  padding: 20px 60px;
  background-color: #333;
  color: #fff;
  font-weight: bold;
  font-size: 16px;
  text-decoration: none;
  border-radius: 50px;
}

/* 💡 背景スライド用の擬似要素 */
.shape-anim-btn::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0; bottom: 0;
  background-color: #0d6efd;
  /* アニメーションの起点を「左」にする */
  transform-origin: left;
  /* 初期状態は横幅0倍(潰しておく) */
  transform: scaleX(0);
  /* ⭕️ transitionはここに書く! widthではなくtransformを動かす! */
  transition: transform 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
  z-index: 1; /* 背景とテキストの間に配置 */
}

/* 💡 ホバー時 */
.shape-anim-btn:hover::before {
  /* 潰していた横幅を元に戻す(スライドしてくるように見える) */
  transform: scaleX(1);
}

/* テキストが背景の下に隠れないように前面に出す */
.shape-anim-btn .shape-anim-txt {
  position: relative;
  z-index: 2;
  transition: color 0.4s ease;
}

アニメーションのベースとなるtransitionプロパティの使い方を詳しく知りたい人は「【CSS】transitionの使い方:animationやtransformとの違い」を一読ください。

::before・::afterが効かない!表示されない原因と解決策

CSSを書き進める中で、誰もが一度は「なぜかbeforeやafterが効かない」「画面に表示されない」という壁にぶつかります。

ここでは、実務で直面するトラブルの原因と解決策を解説します。

::before・::afterが効かない!表示されない原因と解決策
  • content: ""の書き忘れやスペルミス
  • 幅・高さが効かない
  • z-indexが効かない
  • inputタグやimgタグには::afterが使えない理由

content: “”の書き忘れやスペルミス

擬似要素が表示されない原因の第1位は、contentプロパティの指定忘れです。

文字を入れずに図形や背景として擬似要素を使いたい場合は、必ず最初にcontent: "";(空の文字列)を記述するのがルールです。

これがないと何も始まりません。

⭕️ 鉄則:図形を作るときも content: “”; は絶対に必要!

❌ contentなし
(図形は生成されない)

⭕️ content: “”; あり
(図形が生成される)

/* ❌ サイズと色を指定しても content がないと無に帰す */
.box-wrong::before {
  width: 50px;
  height: 50px;
  background-color: #dc3545;
  border-radius: 50%;
  /* 💡 表示されない! */
}

/* ⭕️ まずは content: “”; を宣言する */
.box-correct::before {
  content: “”; /* 💡 必須! */
  display: block; /* 💡 (後述)幅と高さを持たせるために必要 */
  width: 50px;
  height: 50px;
  background-color: #0d6efd;
  border-radius: 50%;
}
HTMLコード表示
<div class="nw-tech-wrapper">
  
  <p class="nw-caption">⭕️ 鉄則:図形を作るときも content: ""; は絶対に必要!</p>

  <div class="nw-demo-area">
    
    <div class="nw-box">
      <p class="nw-label">❌ contentなし<br><small>(図形は生成されない)</small></p>
      <div class="nw-shape is-no-content"></div>
    </div>

    <div class="nw-box">
      <p class="nw-label" style="color:#0d6efd;">⭕️ content: ""; あり<br><small>(図形が生成される)</small></p>
      <div class="nw-shape is-has-content"></div>
    </div>

  </div>

  <div class="nw-code">
    /* ❌ サイズと色を指定しても content がないと無に帰す */<br>
    <span class="hl-blue">.box-wrong::before</span> {<br>
      <span class="hl-green">width: 50px;</span><br>
      <span class="hl-green">height: 50px;</span><br>
      <span class="hl-green">background-color: #dc3545;</span><br>
      <span class="hl-green">border-radius: 50%;</span><br>
      <span class="hl-comment">/* 💡 表示されない! */</span><br>
    }<br><br>

    /* ⭕️ まずは content: ""; を宣言する */<br>
    <span class="hl-blue">.box-correct::before</span> {<br>
      <span class="hl-red">content: "";</span> /* 💡 必須! */<br>
      <span class="hl-green">display: block;</span> /* 💡 (後述)幅と高さを持たせるために必要 */<br>
      <span class="hl-green">width: 50px;</span><br>
      <span class="hl-green">height: 50px;</span><br>
      <span class="hl-green">background-color: #0d6efd;</span><br>
      <span class="hl-green">border-radius: 50%;</span><br>
    }
  </div>

</div>
CSSコード表示
.nw-tech-wrapper {
  background-color: #f8f9fa;
  padding: 20px;
  border-radius: 8px;
  border: 1px solid #dee2e6;
}

.nw-caption {
  font-size: 13px;
  font-weight: bold;
  margin-bottom: 20px;
  color: #198754;
  text-align: center;
}

.nw-demo-area {
  display: flex;
  gap: 50px;
  justify-content: center;
  align-items: flex-start;
  background-color: #e9ecef;
  padding: 40px 20px;
  border-radius: 4px;
  border: 1px dashed #adb5bd;
  margin-bottom: 20px;
}

.nw-box {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.nw-label {
  font-size: 12px;
  font-weight: bold;
  margin-bottom: 15px;
  color: #333;
  text-align: center;
  line-height: 1.4;
}

/* 擬似要素のベース */
.nw-shape {
  position: relative;
  width: 50px;
  height: 50px;
  border: 1px dashed #adb5bd; /* 領域をわかりやすくするための枠 */
}

/* ❌ 失敗パターン:contentなし */
.is-no-content::before {
  /* content: ""; をわざと記述しない */
  display: block;
  width: 50px;
  height: 50px;
  background-color: #dc3545;
  border-radius: 50%;
}

/* ⭕️ 成功パターン:contentあり */
.is-has-content::before {
  content: ""; /* これが命 */
  display: block;
  width: 50px;
  height: 50px;
  background-color: #0d6efd;
  border-radius: 50%;
}

/* コードブロック装飾 */
.nw-code {
  background-color: #282c34;
  color: #abb2bf;
  padding: 15px;
  border-radius: 4px;
  font-family: monospace;
  font-size: 13px;
  line-height: 1.6;
  border-left: 4px solid #0d6efd;
}

.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }

幅・高さが効かない

content: "";を書いたのに、まだ図形が表示されない、あるいは画像が途切れてしまう!という場合、原因は「要素の性質(display)」にあります。

擬似要素に幅や高さを持たせて図形やアイコンを作りたい場合は、display: inline-block;(または display: block;)、もしくはposition: absolute;のいずれかをセットで指定するのが鉄則です。

position: absolute;を指定すると、自動的にブロックレベルの振る舞いを持つようになります。

⭕️ 鉄則:幅と高さを持たせるなら display: inline-block が必須

❌ inlineのまま
(width/heightが完全に無視される)

テキスト

⭕️ inline-block
(幅と高さが正しく効く)

テキスト
/* ❌ デフォルト(inline)のままではサイズが効かず潰れる */
.text-wrong::before {
  content: “”;
  width: 20px; /* 💡 無視される */
  height: 20px; /* 💡 無視される */
  background-color: #dc3545;
}

/* ⭕️ display: inline-block; を指定してサイズを持たせる */
.text-correct::before {
  content: “”;
  display: inline-block; /* 💡 これが必須! */
  width: 20px;
  height: 20px;
  background-color: #0d6efd;
  vertical-align: middle; /* 💡 テキストと縦位置を合わせる */
}
HTMLコード表示
<div class="nw-tech-wrapper">
  
  <p class="nw-caption">⭕️ 鉄則:幅と高さを持たせるなら display: inline-block が必須</p>

  <div class="nw-demo-area">
    
    <div class="nw-box">
      <p class="nw-label">❌ inlineのまま<br><small>(width/heightが完全に無視される)</small></p>
      <div class="nw-shape-size is-wrong-size">テキスト</div>
    </div>

    <div class="nw-box">
      <p class="nw-label" style="color:#0d6efd;">⭕️ inline-block<br><small>(幅と高さが正しく効く)</small></p>
      <div class="nw-shape-size is-correct-size">テキスト</div>
    </div>

  </div>

  <div class="nw-code">
    /* ❌ デフォルト(inline)のままではサイズが効かず潰れる */<br>
    <span class="hl-blue">.text-wrong::before</span> {<br>
      <span class="hl-green">content: "";</span><br>
      <span class="hl-red">width: 20px;</span> /* 💡 無視される */<br>
      <span class="hl-red">height: 20px;</span> /* 💡 無視される */<br>
      <span class="hl-green">background-color: #dc3545;</span><br>
    }<br><br>

    /* ⭕️ display: inline-block; を指定してサイズを持たせる */<br>
    <span class="hl-blue">.text-correct::before</span> {<br>
      <span class="hl-green">content: "";</span><br>
      <span class="hl-red">display: inline-block;</span> /* 💡 これが必須! */<br>
      <span class="hl-green">width: 20px;</span><br>
      <span class="hl-green">height: 20px;</span><br>
      <span class="hl-green">background-color: #0d6efd;</span><br>
      <span class="hl-green">vertical-align: middle;</span> /* 💡 テキストと縦位置を合わせる */<br>
    }
  </div>

</div>
CSSコード表示
.nw-tech-wrapper {
  background-color: #f8f9fa;
  padding: 20px;
  border-radius: 8px;
  border: 1px solid #dee2e6;
}

.nw-caption {
  font-size: 13px;
  font-weight: bold;
  margin-bottom: 20px;
  color: #198754;
  text-align: center;
}

.nw-demo-area {
  display: flex;
  gap: 50px;
  justify-content: center;
  align-items: flex-start;
  background-color: #e9ecef;
  padding: 40px 20px;
  border-radius: 4px;
  border: 1px dashed #adb5bd;
  margin-bottom: 20px;
}

.nw-box {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.nw-label {
  font-size: 12px;
  font-weight: bold;
  margin-bottom: 15px;
  color: #333;
  text-align: center;
  line-height: 1.4;
}

/* デモ用のテキスト要素 */
.nw-shape-size {
  font-weight: bold;
  font-size: 16px;
  color: #333;
  /* 💡 ここにあった display: flex; を削除しました! */
}

/* ❌ 失敗パターン:inlineのまま */
.is-wrong-size::before {
  content: "";
  /* 💡 displayの指定なし(デフォルトのinline) */
  width: 20px;
  height: 20px;
  background-color: #dc3545;
  margin-right: 5px;
  /* サイズが効かないため、赤い四角形は画面から完全に消滅します */
}

/* ⭕️ 成功パターン:inline-block */
.is-correct-size::before {
  content: "";
  display: inline-block; /* 💡 これで幅と高さが効く! */
  width: 20px;
  height: 20px;
  background-color: #0d6efd;
  border-radius: 4px;
  vertical-align: middle; /* テキストの縦位置と揃える */
  margin-right: 5px; /* テキストとの隙間 */
  margin-top: -3px; /* 見栄えの微調整 */
}

/* コードブロック装飾 */
.nw-code {
  background-color: #282c34;
  color: #abb2bf;
  padding: 15px;
  border-radius: 4px;
  font-family: monospace;
  font-size: 13px;
  line-height: 1.6;
  border-left: 4px solid #0d6efd;
}

.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }

displayプロパティの使い方を詳しく知りたい人は「【html&css】displayの種類は?flexやinline-blockの違い」を一読ください。

z-indexが効かない

contentも書いた」「absoluteも書いた」「でも要素が裏側に隠れて出てこない!」という時、原因は z-indexの制御不良にあります。

z-indexはページ全体で絶対的な数値ではなく、「親要素のブロックの中での順位」です。

擬似要素の重なりを確実に制御したい場合は、親要素にposition: relative; z-index: 0;(または isolation: isolate;)を明示的に指定して「スタッキングコンテキスト(重なりの基準)」を作り出すのが他のレイアウトに干渉されない方法です。

⭕️ z-index:-1 の罠を避けるには、親要素で基準(z-index: 0)を作る

❌ 親に基準なし
黄色の丸が背景の裏に消える

⭕️ 親に z-index: 0
黄色の丸が正しく文字の背面に配置

/* ❌ 親に基準がない状態で z-index: -1 をかけると完全に見えなくなる */
.box-wrong {
  position: relative;
  background-color: #fff;
  /* 💡 z-index の指定がない */
}
.box-wrong::before {
  content: “”;
  position: absolute;
  z-index: -1; /* 💡 親の背景色(#fff)のさらに裏側に飛んでいく */
}

/* ⭕️ 親要素に z-index: 0; を設定して「ここが一番底だ」と定義する */
.box-correct {
  position: relative;
  z-index: 0; /* 💡 重なりの基準(スタッキングコンテキスト)を作る! */
  background-color: #fff;
}
.box-correct::before {
  content: “”;
  position: absolute;
  z-index: -1; /* 💡 親の背景より下にはいかず、文字の下に綺麗に配置される */
}
HTMLコード表示
<div class="nw-tech-wrapper">
  
  <p class="nw-caption">⭕️ z-index:-1 の罠を避けるには、親要素で基準(z-index: 0)を作る</p>

  <div class="nw-demo-area" style="gap: 30px;">
    
    <div class="nw-box is-wrong-z" style="background-color: #e9ecef; border: 1px solid #ced4da; padding: 20px;">
      <p class="nw-label" style="position:relative; z-index:2;">❌ 親に基準なし<br><small>黄色の丸が背景の裏に消える</small></p>
    </div>

    <div class="nw-box is-correct-z" style="background-color: #e9ecef; border: 1px solid #ced4da; padding: 20px;">
      <p class="nw-label" style="position:relative; z-index:2;">⭕️ 親に z-index: 0<br><small>黄色の丸が正しく文字の背面に配置</small></p>
    </div>

  </div>

  <div class="nw-code">
    /* ❌ 親に基準がない状態で z-index: -1 をかけると完全に見えなくなる */<br>
    <span class="hl-blue">.box-wrong</span> {<br>
      <span class="hl-green">position: relative;</span><br>
      <span class="hl-green">background-color: #fff;</span><br>
      <span class="hl-comment">/* 💡 z-index の指定がない */</span><br>
    }<br>
    <span class="hl-blue">.box-wrong::before</span> {<br>
      <span class="hl-green">content: "";</span><br>
      <span class="hl-green">position: absolute;</span><br>
      <span class="hl-red">z-index: -1;</span> /* 💡 親の背景色(#fff)のさらに裏側に飛んでいく */<br>
    }<br><br>

    /* ⭕️ 親要素に z-index: 0; を設定して「ここが一番底だ」と定義する */<br>
    <span class="hl-blue">.box-correct</span> {<br>
      <span class="hl-green">position: relative;</span><br>
      <span class="hl-red">z-index: 0;</span> /* 💡 重なりの基準(スタッキングコンテキスト)を作る! */<br>
      <span class="hl-green">background-color: #fff;</span><br>
    }<br>
    <span class="hl-blue">.box-correct::before</span> {<br>
      <span class="hl-green">content: "";</span><br>
      <span class="hl-green">position: absolute;</span><br>
      <span class="hl-red">z-index: -1;</span> /* 💡 親の背景より下にはいかず、文字の下に綺麗に配置される */<br>
    }
  </div>

</div>
CSSコード表示
/* ❌ 失敗パターンの親 */
.is-wrong-z {
  position: relative;
  /* z-indexの指定なし */
}

/* 擬似要素が親の親(デモエリアの背景)の裏にまで飛んでいく可能性がある */
.is-wrong-z::before {
  content: "";
  position: absolute;
  top: 10px;
  left: -10px;
  width: 50px;
  height: 50px;
  background-color: #ffc107;
  border-radius: 50%;
  z-index: -1; 
}

/* ⭕️ 成功パターンの親 */
.is-correct-z {
  position: relative;
  z-index: 0; /* これが魔法の一行 */
}

/* 擬似要素は親のブロックの中で-1(一番下)になるが、親の背景の裏にはいかない */
.is-correct-z::before {
  content: "";
  position: absolute;
  top: 10px;
  left: -10px;
  width: 50px;
  height: 50px;
  background-color: #ffc107;
  border-radius: 50%;
  z-index: -1; 
}

inputタグやimgタグには::afterが使えない理由

擬似要素を使い始めると、「検索フォームの入力欄の中にアイコンを出したい」や「画像の上にNEWマークを重ねたい」という要望が出てきます。

inputimgに対して擬似要素で装飾を加えたい場合、絶対にタグ単体に直接指定しません。

外側を<div><label>などの「親要素」で囲み、『親要素に対して』擬似要素を指定しposition: absolute;で入力欄や画像の上に被せるのがよいです。

⭕️ 空要素(input/img)の装飾は、必ず「親要素」に擬似要素をかける

❌ inputに直接指定
(アイコンは表示されない)

⭕️ 親要素に指定
(ラッパーに指定して上に被せる)

/* ❌ input自体には中身がないため、::afterは無視される */
.search-input::after {
  content: “🔍”; /* 💡 完全に無視されて表示されない */
  position: absolute;
}

/* ⭕️ 親要素(ラッパー)を作り、そこを基準にする */
.input-wrapper {
  position: relative; /* 💡 基準点を作る */
  display: block;
}
.input-wrapper::after {
  content: “🔍”; /* 💡 親要素になら表示できる */
  position: absolute; /* 💡 inputの上に被せるように配置 */
  right: 15px;
  top: 50%;
  transform: translateY(-50%);
  color: #6c757d;
  pointer-events: none; /* 💡 アイコンの下にあるinputをクリック可能にする魔法 */
}
HTMLコード表示
<div class="void-tech-wrapper">
  
  <p class="void-caption">⭕️ 空要素(input/img)の装飾は、必ず「親要素」に擬似要素をかける</p>

  <div class="void-demo-area" style="flex-direction: column; gap: 30px; align-items: center;">
    
    <div class="void-box" style="width: 100%; max-width: 300px;">
      <p class="void-label">❌ inputに直接指定<br><small>(アイコンは表示されない)</small></p>
      <input type="text" placeholder="キーワードを入力..." class="void-input is-wrong-input">
    </div>

    <div class="void-box" style="width: 100%; max-width: 300px;">
      <p class="void-label" style="color:#0d6efd;">⭕️ 親要素に指定<br><small>(ラッパーに指定して上に被せる)</small></p>
      
      <div class="void-input-wrapper is-correct-wrapper">
        <input type="text" placeholder="キーワードを入力..." class="void-input">
      </div>
      
    </div>

  </div>

  <div class="void-code">
    /* ❌ input自体には中身がないため、::afterは無視される */<br>
    <span class="hl-blue">.search-input::after</span> {<br>
      <span class="hl-red">content: "🔍";</span> /* 💡 完全に無視されて表示されない */<br>
      <span class="hl-green">position: absolute;</span><br>
    }<br><br>

    /* ⭕️ 親要素(ラッパー)を作り、そこを基準にする */<br>
    <span class="hl-blue">.input-wrapper</span> {<br>
      <span class="hl-red">position: relative;</span> /* 💡 基準点を作る */<br>
      <span class="hl-green">display: block;</span><br>
    }<br>
    <span class="hl-blue">.input-wrapper::after</span> {<br>
      <span class="hl-red">content: "🔍";</span> /* 💡 親要素になら表示できる */<br>
      <span class="hl-green">position: absolute;</span> /* 💡 inputの上に被せるように配置 */<br>
      <span class="hl-green">right: 15px;</span><br>
      <span class="hl-green">top: 50%;</span><br>
      <span class="hl-green">transform: translateY(-50%);</span><br>
      <span class="hl-green">color: #6c757d;</span><br>
      <span class="hl-green">pointer-events: none;</span> /* 💡 アイコンの下にあるinputをクリック可能にする魔法 */<br>
    }
  </div>

</div>
CSSコード表示
.void-tech-wrapper {
  background-color: #f8f9fa;
  padding: 20px;
  border-radius: 8px;
  border: 1px solid #dee2e6;
}

.void-caption {
  font-size: 13px;
  font-weight: bold;
  margin-bottom: 20px;
  color: #198754;
  text-align: center;
}

.void-demo-area {
  display: flex;
  background-color: #e9ecef;
  padding: 40px 20px;
  border-radius: 4px;
  border: 1px dashed #adb5bd;
  margin-bottom: 20px;
}

.void-box {
  display: flex;
  flex-direction: column;
}

.void-label {
  font-size: 12px;
  font-weight: bold;
  margin-bottom: 10px;
  color: #333;
  text-align: center;
  line-height: 1.4;
}

/* 共通のinputスタイル */
.void-input {
  width: 100%;
  padding: 12px 40px 12px 15px; /* 右側にアイコン用の余白をあける */
  border: 2px solid #ced4da;
  border-radius: 25px;
  font-size: 14px;
  outline: none;
  box-sizing: border-box;
}

/* ❌ 失敗パターン:inputに直接 */
.is-wrong-input::after {
  content: "🔍";
  position: absolute;
  right: 15px;
  top: 50%;
  /* 仕様上絶対に表示されません */
}

/* ⭕️ 成功パターン:ラッパー */
.is-correct-wrapper {
  position: relative; /* 💡 これが基準になる */
  display: block;
}

/* ラッパーに対して::afterをかける */
.is-correct-wrapper::after {
  content: "🔍";
  position: absolute;
  right: 15px;
  top: 50%;
  transform: translateY(-50%);
  color: #0d6efd;
  font-size: 16px;
  /* 💡 アイコン自体をクリックしても、下のinputにフォーカスが当たるようにする */
  pointer-events: none; 
}

/* コードブロック装飾 */
.void-code {
  background-color: #282c34;
  color: #abb2bf;
  padding: 15px;
  border-radius: 4px;
  font-family: monospace;
  font-size: 13px;
  line-height: 1.6;
  border-left: 4px solid #0d6efd;
}

.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }

【実務テク】「〇〇の後(after)」を実装するCSS集

CSSにおける「after」は、これまで解説してきた「::after 擬似要素(要素の直後に図形を作る)」だけではありません。

実務では、「3行の『後』で省略したい」「5秒『後』に消したい」「3つの要素の『後』で改行したい」といった、時間や行数、個数に対する「after」の制御が求められます。

ここでは、JavaScriptを使わずにCSSだけで完結できる、実務直結の「〇〇の後(after)」の制御テクニックを紹介します。

「〇〇の後(after)」を実装するCSS集
  • 2行・3行など指定行数の後で「…(三点リーダー)」で省略する
  • アニメーション終了後や数秒後に非表示にする方法
  • N個の要素の後で改行や印刷時の改ページ

2行・3行など指定行数の後で「…(三点リーダー)」で省略する

ブログの記事一覧やニュースの抜粋文などで、指定した行数でテキストをカットし、末尾に「…(三点リーダー)」を表示させる手法はWebデザインにおいて必須です。

複数行での省略は「文字数」ではなく「行数」で制御します。

現在は、WebKit独自の拡張プロパティであった-webkit-line-clampがすべてのモダンブラウザで標準としてサポートされているため、これを使うのがよいです。

必須となる4行のコード(display, box-orient, line-clamp, overflow)をセットで覚えましょう。

⭕️ -webkit-line-clamp を使えば、複数行の省略もCSSだけで完結する

1行で省略(css truncate after 1 line)

このテキストは1行に収まらないほど非常に長いテキストですが、CSSの力によって1行目の末尾で美しく三点リーダーに変換されて省略されます。

3行で省略(css ellipsis after 3 lines)

このテキストは複数行にまたがる非常に長いテキストです。Webデザインにおいて、抜粋文などを表示する際に、枠からはみ出してしまうとレイアウトが崩れる原因になります。そのため、指定した3行目の末尾で自動的に三点リーダーが付与されて省略されるようにCSSで制御しています。これでどれだけ文字数が多くても安心です。
/* 💡 1行で省略する場合の3点セット */
.truncate-1-line {
  white-space: nowrap; /* 💡 改行を禁止する */
  overflow: hidden; /* 💡 はみ出した分を隠す */
  text-overflow: ellipsis; /* 💡 はみ出たら「…」にする */
}

/* ⭕️ 複数行(ここでは3行)で省略する4点セット */
.truncate-3-lines {
  display: -webkit-box; /* 💡 必須 */
  -webkit-box-orient: vertical; /* 💡 必須 */
  -webkit-line-clamp: 3; /* 💡 ここで省略したい行数を指定する! */
  overflow: hidden; /* 💡 はみ出した分を隠す(必須) */
}
HTMLコード表示
<div class="after-tech-wrapper">
  
  <p class="after-caption">⭕️ -webkit-line-clamp を使えば、複数行の省略もCSSだけで完結する</p>

  <div class="after-demo-area" style="flex-direction: column; gap: 20px;">
    
    <div class="after-box" style="width: 100%; max-width: 350px;">
      <p class="after-label">1行で省略(css truncate after 1 line)</p>
      <div class="ellipsis-1-line">
        このテキストは1行に収まらないほど非常に長いテキストですが、CSSの力によって1行目の末尾で美しく三点リーダーに変換されて省略されます。
      </div>
    </div>

    <div class="after-box" style="width: 100%; max-width: 350px;">
      <p class="after-label" style="color:#0d6efd;">3行で省略(css ellipsis after 3 lines)</p>
      <div class="ellipsis-3-lines">
        このテキストは複数行にまたがる非常に長いテキストです。Webデザインにおいて、抜粋文などを表示する際に、枠からはみ出してしまうとレイアウトが崩れる原因になります。そのため、指定した3行目の末尾で自動的に三点リーダーが付与されて省略されるようにCSSで制御しています。これでどれだけ文字数が多くても安心です。
      </div>
    </div>

  </div>

  <div class="after-code">
    /* 💡 1行で省略する場合の3点セット */<br>
    <span class="hl-blue">.truncate-1-line</span> {<br>
      <span class="hl-red">white-space: nowrap;</span> /* 💡 改行を禁止する */<br>
      <span class="hl-red">overflow: hidden;</span> /* 💡 はみ出した分を隠す */<br>
      <span class="hl-red">text-overflow: ellipsis;</span> /* 💡 はみ出たら「…」にする */<br>
    }<br><br>

    /* ⭕️ 複数行(ここでは3行)で省略する4点セット */<br>
    <span class="hl-blue">.truncate-3-lines</span> {<br>
      <span class="hl-red">display: -webkit-box;</span> /* 💡 必須 */<br>
      <span class="hl-red">-webkit-box-orient: vertical;</span> /* 💡 必須 */<br>
      <span class="hl-red">-webkit-line-clamp: 3;</span> /* 💡 ここで省略したい行数を指定する! */<br>
      <span class="hl-red">overflow: hidden;</span> /* 💡 はみ出した分を隠す(必須) */<br>
    }
  </div>

</div>
CSSコード表示
.after-tech-wrapper {
  background-color: #f8f9fa;
  padding: 20px;
  border-radius: 8px;
  border: 1px solid #dee2e6;
}

.after-caption {
  font-size: 13px;
  font-weight: bold;
  margin-bottom: 20px;
  color: #198754;
  text-align: center;
}

.after-demo-area {
  display: flex;
  align-items: center;
  background-color: #e9ecef;
  padding: 40px 20px;
  border-radius: 4px;
  border: 1px dashed #adb5bd;
  margin-bottom: 20px;
}

.after-box {
  background-color: #fff;
  padding: 15px;
  border-radius: 8px;
  border: 1px solid #ced4da;
}

.after-label {
  font-size: 12px;
  font-weight: bold;
  margin-bottom: 10px;
  color: #333;
  border-bottom: 1px solid #eee;
  padding-bottom: 5px;
}

/* 💡 1行で省略 */
.ellipsis-1-line {
  font-size: 14px;
  line-height: 1.5;
  color: #555;
  
  /* 必須3点セット */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ⭕️ 3行で省略 */
.ellipsis-3-lines {
  font-size: 14px;
  line-height: 1.5;
  color: #555;
  
  /* 必須4点セット */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3; /* ここを2にすれば2行で省略可能 */
  overflow: hidden;
}

.after-code {
  background-color: #282c34;
  color: #abb2bf;
  padding: 15px;
  border-radius: 4px;
  font-family: monospace;
  font-size: 13px;
  line-height: 1.6;
  border-left: 4px solid #0d6efd;
}

.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }

アニメーション終了後や数秒後に非表示にする方法

「送信完了メッセージを5秒後にフワッと消したい」や「ホバーしてから1秒待ってアニメーションを開始したい」といった要件は頻出します。

数秒後にフワッと消して、なおかつ「クリックなどの干渉も防ぎたい」場合は、opacity: 0;visibility: hidden;を組み合わせ、animation-fill-mode: forwards;を指定して最終状態を維持することです。

ホバーの遅延には、transition-delayを使用します。

⭕️ display: none の代わりに、opacity と visibility をアニメーションさせる

✅ 送信が完了しました(5秒後に消えます)
1秒後に色が変わるボタン
/* ⭕️ 数秒後に非表示にして、最終状態を保持(forwards)する */
.flash-message {
  /* 💡 fadeOutアニメーションを、5秒遅延(5s)で、1秒かけて(1s)実行し、状態を保持(forwards) */
  animation: fadeOut 1s ease 5s forwards;
}

/* 💡 display: none ではなく、透明度と可視性をコントロールする */
@keyframes fadeOut {
  to {
    opacity: 0; /* 💡 透明にする */
    visibility: hidden; /* 💡 完全に見えなくし、クリック等も無効化する */
  }
}

/* ⭕️ ホバーの反応を遅らせるなら transition-delay */
.delay-btn {
  background-color: #6c757d;
  transition: background-color 0.3s ease;
}
.delay-btn:hover {
  background-color: #0d6efd;
  transition-delay: 1s; /* 💡 ホバーしてから1秒待って実行 */
}
HTMLコード表示
<div class="after-tech-wrapper">
  
  <p class="after-caption">⭕️ display: none の代わりに、opacity と visibility をアニメーションさせる</p>

  <div class="after-demo-area" style="gap: 20px;">
    
    <div class="flash-message">
      ✅ 送信が完了しました(5秒後に消えます)
    </div>

    <div class="delay-hover-btn">
      1秒後に色が変わるボタン
    </div>

  </div>

  <div class="after-code">
    /* ⭕️ 数秒後に非表示にして、最終状態を保持(forwards)する */<br>
    <span class="hl-blue">.flash-message</span> {<br>
      <span class="hl-comment">/* 💡 fadeOutアニメーションを、5秒遅延(5s)で、1秒かけて(1s)実行し、状態を保持(forwards) */</span><br>
      <span class="hl-red">animation: fadeOut 1s ease 5s forwards;</span><br>
    }<br><br>

    /* 💡 display: none ではなく、透明度と可視性をコントロールする */<br>
    <span class="hl-blue">@keyframes fadeOut</span> {<br>
      <span class="hl-green">to</span> {<br>
        <span class="hl-red">opacity: 0;</span> /* 💡 透明にする */<br>
        <span class="hl-red">visibility: hidden;</span> /* 💡 完全に見えなくし、クリック等も無効化する */<br>
      }<br>
    }<br><br>

    /* ⭕️ ホバーの反応を遅らせるなら transition-delay */<br>
    <span class="hl-blue">.delay-btn</span> {<br>
      <span class="hl-green">background-color: #6c757d;</span><br>
      <span class="hl-red">transition: background-color 0.3s ease;</span><br>
    }<br>
    <span class="hl-blue">.delay-btn:hover</span> {<br>
      <span class="hl-green">background-color: #0d6efd;</span><br>
      <span class="hl-red">transition-delay: 1s;</span> /* 💡 ホバーしてから1秒待って実行 */<br>
    }
  </div>

</div>
CSSコード表示
/* 💡 5秒後に消えるメッセージ */
.flash-message {
  background-color: #d1e7dd;
  color: #0f5132;
  padding: 15px 20px;
  border-radius: 4px;
  font-size: 13px;
  font-weight: bold;
  border: 1px solid #badbcc;
  /* fadeOutアニメーションを、1秒かけて実行。開始を5秒遅らせる。終わったらその状態を維持(forwards) */
  animation: demoFadeOut 1s ease 5s forwards;
}

@keyframes demoFadeOut {
  to {
    opacity: 0;
    visibility: hidden; /* display: none の代わり */
  }
}

/* 💡 1秒遅れるホバー */
.delay-hover-btn {
  background-color: #6c757d;
  color: white;
  padding: 15px 20px;
  border-radius: 4px;
  font-size: 13px;
  font-weight: bold;
  cursor: pointer;
  /* 基本のトランジション */
  transition: background-color 0.3s ease;
}

.delay-hover-btn:hover {
  background-color: #0d6efd;
  /* ホバーした瞬間ではなく、1秒(1s)待ってから色を変える */
  transition-delay: 1s;
}

CSSによるアニメーションの作り方を詳しく知りたい人は「【CSS】アニメーションの作り方:コピペで使えるおしゃれサンプル集」を一読ください。

N個の要素の後で改行や印刷時の改ページ

ギャラリーやカードのリストを作る際、「3つの要素の後で改行したい」というレイアウト制御やブラウザの印刷機能(Ctrl+P)を使った際に「この見出しの前や後で必ず改ページさせたい」という要件があります。

N個の後で改行させたい場合は、親にflex-wrap: wrap;を指定した上で、子要素にwidth: 33.333%;(3個で改行の場合)のように横幅を指定して自然に折り返させるのが安定します。

また、印刷時の改ページ制御には、古いpage-break-afterではなく、最新の標準仕様であるbreak-after: page;を使用します。

文字の途中で自然に改行させたい場合は、HTMLの<wbr>(改行可能位置の指定)やCSSのword-breakを活用します。

⭕️ Flexboxの改行は width (flex-basis) で制御し、印刷改行は break-after を使う

3個で改行するFlexレイアウト

1
2
3
4
5

🖨️ 印刷時の改ページ制御(プレビュー)

(印刷時はこのテキストは次のページに送られます)

/* ⭕️ N個で改行させるなら、隙間(gap)を考慮してwidthを計算(calc)する */
.flex-container {
  display: flex;
  flex-wrap: wrap; /* 💡 はみ出たら改行する設定をオン */
  gap: 10px; /* 💡 要素間の隙間 */
}
.flex-item {
  /* 💡 3個で改行させたい場合:(100% – 隙間2回分20px) / 3 */
  width: calc((100% – 20px) / 3);
}

/* ⭕️ 印刷時の改ページには break-after を使う */
.print-section {
  /* 💡 古い書き方: page-break-after: always; */
  break-after: page; /* 💡 この要素の「後」で必ず印刷ページを分ける */
}
HTMLコード表示
<div class="after-tech-wrapper">
  
  <p class="after-caption">⭕️ Flexboxの改行は width (flex-basis) で制御し、印刷改行は break-after を使う</p>

  <div class="after-demo-area" style="flex-direction: column;">
    
    <p class="after-label" style="align-self: flex-start;">3個で改行するFlexレイアウト</p>
    <div class="flex-wrap-container">
      <div class="flex-item">1</div>
      <div class="flex-item">2</div>
      <div class="flex-item">3</div>
      <div class="flex-item">4</div>
      <div class="flex-item">5</div>
    </div>

    <div style="margin-top: 30px; width: 100%; padding: 15px; border: 2px dashed #dc3545; background: #fff;">
      <p style="font-weight: bold; margin: 0 0 10px 0; color: #dc3545;">🖨️ 印刷時の改ページ制御(プレビュー)</p>
      <div class="print-page-break">
        この要素の直後(after)で必ず改ページされます。<br>
        <small>※Ctrl+P などで印刷プレビューを見ると確認できます。</small>
      </div>
      <p style="margin: 10px 0 0 0;">(印刷時はこのテキストは次のページに送られます)</p>
    </div>

  </div>

  <div class="after-code">
    /* ⭕️ N個で改行させるなら、隙間(gap)を考慮してwidthを計算(calc)する */<br>
    <span class="hl-blue">.flex-container</span> {<br>
      <span class="hl-green">display: flex;</span><br>
      <span class="hl-red">flex-wrap: wrap;</span> /* 💡 はみ出たら改行する設定をオン */<br>
      <span class="hl-green">gap: 10px;</span> /* 💡 要素間の隙間 */<br>
    }<br>
    <span class="hl-blue">.flex-item</span> {<br>
      <span class="hl-comment">/* 💡 3個で改行させたい場合:(100% - 隙間2回分20px) / 3 */</span><br>
      <span class="hl-red">width: calc((100% - 20px) / 3);</span><br>
    }<br><br>

    /* ⭕️ 印刷時の改ページには break-after を使う */<br>
    <span class="hl-blue">.print-section</span> {<br>
      <span class="hl-comment">/* 💡 古い書き方: page-break-after: always; */</span><br>
      <span class="hl-red">break-after: page;</span> /* 💡 この要素の「後」で必ず印刷ページを分ける */<br>
    }
  </div>

</div>
CSSコード表示
/* 💡 Flexboxの自動改行(3個) */
.flex-wrap-container {
  display: flex;
  flex-wrap: wrap; /* 折り返しを許可 */
  gap: 10px; /* 隙間 */
  width: 100%;
}

.flex-item {
  /* 3個並べるための計算式: (100% - gap10px * 2箇所) / 3 */
  width: calc((100% - 20px) / 3);
  background-color: #0d6efd;
  color: white;
  padding: 15px;
  text-align: center;
  font-weight: bold;
  border-radius: 4px;
  box-sizing: border-box;
}

/* 💡 印刷時の改ページ */
.print-page-break {
  background-color: #f8d7da;
  padding: 10px;
  border-radius: 4px;
  /* 印刷プレビュー時にこの要素の「後」でページを分ける */
  break-after: page;
}

JavaScriptでの::before活用

Web制作の実務において、「ボタンをクリックしたら::beforeのアイコンを変えたい」「スクロール量に応じて::beforeで作ったプログレスバーを伸ばしたい」というような、動的な変化が求められる場面は多々あります。

しかし、CSSの延長線上にある擬似要素をJavaScriptで操作しようとすると、通常のHTMLタグとは全く異なる特殊なルールに直面します。

ここでは、実務で必須となる「JavaScriptを使った擬似要素の動的コントロール」について解説します。

JavaScriptでの::before活用
  • JavaScriptで擬似要素を操作する

JavaScriptで擬似要素を操作する

JavaScriptで擬似要素を操作する際、擬似要素を直接触ろうとしません。

「親要素」または「隣接する兄弟要素」に対してアプローチを行います。

具体的な解決策は以下の2つです。

  1. クラスの付け替え(Toggle)
    親要素に.is-activeなどのクラスをJSで付与し、CSS側で.is-active::before { ... }のスタイルを適用する。
  2. CSS変数(カスタムプロパティ)の書き換え
    親要素に対してelement.style.setProperty('--var-name', value)で変数を書き換え、CSS側でcontent: var(--var-name);のように受け取る。

また、リンクへのホバー演出やチェックボックス・入力欄の装飾を動的に変えたい場合も、「ラッパー要素(親)」か「隣接する<label>要素」の状態をJSで監視し、そこを起点にスタイルを変化させるのがよいです。

⭕️ 鉄則:擬似要素は直接触らず、「親要素のクラス」や「CSS変数」をJSで操作する

タスク状況

下のボタンを押すと、JavaScriptが「親要素」のCSS変数を書き換え、それに連動して擬似要素(左上のバッジ)が変化します。

/* ⭕️ CSS側では「変数(var)」を使ってスタイルを定義しておく */
.card::before {
  /* 💡 JSから渡された変数(–dynamic-text)を表示する */
  content: var(–dynamic-text);
  position: absolute;
  /* 💡 JSから渡された変数(–dynamic-bg)を背景色にする */
  background-color: var(–dynamic-bg);
  transition: all 0.3s ease;
}

/* ⭕️ クラスの付け替えによるスタイル変更を用意しておく */
.card.is-highlight::before {
  transform: scale(1.2) rotate(-5deg); /* 💡 強調アニメーション */
  box-shadow: 0 4px 10px rgba(0,0,0,0.2);
}
HTMLコード表示
<div class="js-tech-wrapper">
  
  <p class="js-caption">⭕️ 鉄則:擬似要素は直接触らず、「親要素のクラス」や「CSS変数」をJSで操作する</p>

  <div class="js-demo-area">
    
    <div class="js-card" id="targetCard" style="--dynamic-bg: #6c757d; --dynamic-text: '未完了';">
      <div class="js-card-title">タスク状況</div>
      <p class="js-card-desc">下のボタンを押すと、JavaScriptが「親要素」のCSS変数を書き換え、それに連動して擬似要素(左上のバッジ)が変化します。</p>
    </div>

    <div class="js-btn-group">
      <button class="js-btn" onclick="updatePseudoElement()">✅ 完了にする(CSS変数変更)</button>
      <button class="js-btn is-outline" onclick="togglePseudoClass()">🔄 ハイライト(クラス付け替え)</button>
    </div>

  </div>

  <div class="js-code">
    /* ⭕️ CSS側では「変数(var)」を使ってスタイルを定義しておく */<br>
    <span class="hl-blue">.card::before</span> {<br>
      <span class="hl-comment">/* 💡 JSから渡された変数(--dynamic-text)を表示する */</span><br>
      <span class="hl-red">content: var(--dynamic-text);</span><br>
      <span class="hl-green">position: absolute;</span><br>
      <span class="hl-comment">/* 💡 JSから渡された変数(--dynamic-bg)を背景色にする */</span><br>
      <span class="hl-red">background-color: var(--dynamic-bg);</span><br>
      <span class="hl-green">transition: all 0.3s ease;</span><br>
    }<br><br>

    /* ⭕️ クラスの付け替えによるスタイル変更を用意しておく */<br>
    <span class="hl-blue">.card.is-highlight::before</span> {<br>
      <span class="hl-red">transform: scale(1.2) rotate(-5deg);</span> /* 💡 強調アニメーション */<br>
      <span class="hl-green">box-shadow: 0 4px 10px rgba(0,0,0,0.2);</span><br>
    }
  </div>

  <script>
    // 💡 擬似要素ではなく、必ず「親要素」を取得する
    const card = document.getElementById('targetCard');

    // ⭕️ 解決策1:親要素の「CSS変数」を書き換える
    function updatePseudoElement() {
      // 💡 setPropertyで変数を上書きすると、擬似要素がそれを読み込んで変化する
      card.style.setProperty('--dynamic-bg', '#198754');
      card.style.setProperty('--dynamic-text', '"完了!"'); // ※文字列を渡す際は引用符を二重にする点に注意
    }

    // ⭕️ 解決策2:親要素の「クラス」を付け替える
    function togglePseudoClass() {
      card.classList.toggle('is-highlight');
    }
  </script>

</div>
CSSコード表示
.js-tech-wrapper {
  background-color: #f8f9fa;
  padding: 20px;
  border-radius: 8px;
  border: 1px solid #dee2e6;
}

.js-caption {
  font-size: 13px;
  font-weight: bold;
  margin-bottom: 20px;
  color: #198754;
  text-align: center;
}

.js-demo-area {
  display: flex;
  flex-direction: column;
  align-items: center;
  background-color: #e9ecef;
  padding: 40px 20px;
  border-radius: 4px;
  border: 1px dashed #adb5bd;
  margin-bottom: 20px;
}

/* 💡 操作対象となる親要素のカード */
.js-card {
  position: relative;
  background-color: #fff;
  padding: 30px;
  border-radius: 8px;
  box-shadow: 0 4px 6px rgba(0,0,0,0.05);
  max-width: 350px;
  margin-bottom: 25px;
  border: 1px solid #ced4da;
  transition: all 0.3s ease;
}

.js-card-title {
  margin: 0 0 10px 0;
  color: #333;
}

.js-card-desc {
  margin: 0;
  font-size: 14px;
  color: #666;
  line-height: 1.5;
}

/* ⭕️ JSの変数を受け取って変化する擬似要素 */
.js-card::before {
  /* 💡 HTMLのstyle属性で定義された変数を読み込む */
  content: var(--dynamic-text);
  background-color: var(--dynamic-bg);
  
  position: absolute;
  top: -15px;
  left: -15px;
  padding: 8px 15px;
  color: white;
  font-size: 13px;
  font-weight: bold;
  border-radius: 20px;
  transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
}

/* ⭕️ JSでクラスが付与された時のスタイル */
.js-card.is-highlight {
  border-color: #ffc107;
  box-shadow: 0 0 0 4px rgba(255, 193, 7, 0.2);
}

.js-card.is-highlight::before {
  transform: scale(1.1) rotate(-5deg);
  box-shadow: 0 4px 10px rgba(0,0,0,0.2);
}

/* ボタン群のスタイル */
.js-btn-group {
  display: flex;
  gap: 15px;
}

.js-btn {
  padding: 10px 20px;
  background-color: #0d6efd;
  color: white;
  border: none;
  border-radius: 4px;
  font-size: 14px;
  font-weight: bold;
  cursor: pointer;
  transition: opacity 0.2s;
}

.js-btn:hover {
  opacity: 0.8;
}

.js-btn.is-outline {
  background-color: transparent;
  color: #0d6efd;
  border: 2px solid #0d6efd;
}

/* コードブロック装飾 */
.js-code {
  background-color: #282c34;
  color: #abb2bf;
  padding: 15px;
  border-radius: 4px;
  font-family: monospace;
  font-size: 13px;
  line-height: 1.6;
  border-left: 4px solid #0d6efd;
  margin-bottom: 20px;
}

.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }

まとめ

分かりやすいようにまとめを記載します。

本記事のまとめ
  • ::beforeは要素内のコンテンツの先頭に、::afterは末尾に仮想的な要素を生成する。
  • 画面に表示させるには、contentプロパティの指定が絶対条件となる(図形の場合は空文字 content: ""を指定)。
  • デフォルトはインライン要素のため、幅や高さを適用するにはdisplay: inline-blockposition: absoluteの指定が必要。
  • テキストを改行する場合は、content内に\Aを記述し、要素にwhite-space: pre;を指定する。
  • サイズ調整可能な画像アイコンを配置する場合は、content: url()ではなくbackground-imageを使用する。
  • 任意の位置に配置する場合は、親要素にposition: relative;、擬似要素にposition: absolute;を指定する。
  • 親要素の背景の裏側に潜り込むのを防ぐには、親要素にz-index: 0;を指定して基準を設ける。
  • <input><img>などの空要素には直接適用できないため、親要素で囲んで指定する。
  • 1つのHTMLタグに対して適用できる擬似要素は::before::afterの各1つずつ(最大2つ)である。
  • JavaScriptから直接取得やスタイル変更はできないため、親要素の「クラスの付け替え」や「CSS変数の書き換え」によって動的に操作する。

よくある質問(FAQ)

::beforeと::afterの決定的な違いは何ですか?

挿入される「位置」が異なります。

::beforeは指定した要素のコンテンツの一番最初(直前)に挿入され、::afterは一番最後(直後)に挿入されます。

どちらも機能自体は同じであるため、「左側にアイコンを置くならbefore」「右側に矢印を置くならafter」といったように、視覚的なレイアウトに合わせて使い分けるのが一般的です。

:before(コロン1つ)と::before(コロン2つ)の違いは何ですか?どちらを使うべきですか?

結論から言うと、現代のWeb制作では「コロン2つ(::)」を使うのが推奨される標準ルールです。

コロン1つはCSS2時代の古い書き方で、CSS3から「擬似クラス(:hoverなど)」と「擬似要素(::beforeなど)」を視覚的に区別するためにコロン2つが採用されました。

古いブラウザ(IE8など)に対応する必要があった時代はコロン1つが使われていましたが、現在ではコロン2つで記述するのが作法です。

::beforeを指定したのに画面に何も表示されないのはなぜですか?

最も多い原因は、contentプロパティの書き忘れです。

CSSの仕様上、図形を作りたい場合でもcontent: "";(空の文字列)を指定しないと要素自体が生成されません。

また、擬似要素はデフォルトで「インライン要素」として作られるため、幅や高さを指定したい場合はdisplay: inline-block;position: absolute;をセットで記述する必要があります。

<input>(入力欄)や<img>(画像)に擬似要素が効かない原因を教えてください。

擬似要素は「タグと閉じタグの内側(コンテンツ部分)」に追加される仕様だからです。

<input><img><br>といったタグは「空要素」と呼ばれ、そもそも内部にテキストなどのコンテンツを持つことができないため、擬似要素を挿入するスペースが存在しません。

これらの要素にアイコン等を重ねたい場合は、外側を<div>などの親要素で囲み、親要素に対して擬似要素を指定して上に被せるのが正しい解決策です。

擬似要素で作ったボタンやアイコンに、クリックイベントやリンクを設定できますか?

擬似要素自体に直接クリックイベントを設定することはできません。

擬似要素はDOMツリーには存在しない「CSSが作り出した見せかけの要素」だからです。

クリックさせたい場合は、擬似要素が属している「親要素」に対して<a>タグでリンクを貼ったり、JavaScriptのクリックイベントを設定したりする必要があります。

この記事を書いた人

sugiのアバター sugi Site operator

【経歴】玉川大学工学部卒業→新卒SIer企業入社→2年半後に独立→プログラミングスクール運営/受託案件→フリーランスエンジニア&SEOコンサル→Python特化のコンテンツサイトJob Code&UIコピペサイトCode Stock運営中

目次