アニメ下線リンク

純CSSの疑似要素だけで作る4種のホバー下線(左伸び・中央展開・スワップ・枠線)。ナビゲーションやテキストリンクの質感を高めます。

#css#hover#link#animation

ライブデモ

使用例(お題: SaaS FlowDesk)

この技法を「SaaS FlowDesk」というテーマのダミーサイトで実際に使った例です。

HTML
<!-- FlowDesk: グローバルナビ。アニメ下線リンクを主役に -->
<div class="fd">
  <header class="fd__bar">
    <span class="fd__logo"><span class="fd__mark"></span>FlowDesk</span>
    <nav class="fd__nav">
      <a class="link link--grow" href="#">プロダクト</a>
      <a class="link link--center" href="#">料金プラン</a>
      <a class="link link--swap" href="#">導入事例</a>
      <a class="link link--draw" href="#">サポート</a>
    </nav>
    <span class="fd__cta">資料請求</span>
  </header>

  <section class="fd__body">
    <p class="fd__eyebrow">PRODUCT TOUR</p>
    <h1 class="fd__h1">必要な情報に、最短で。</h1>
    <p class="fd__lead">上のメニューにカーソルを合わせると、4種類の下線アニメーションが切り替わります。</p>
    <div class="fd__cards">
      <article class="card"><span class="card__ico">◆</span><b>ボード</b><span>進捗を一望</span></article>
      <article class="card"><span class="card__ico">◇</span><b>ドキュメント</b><span>共同編集</span></article>
      <article class="card"><span class="card__ico">◈</span><b>オートメーション</b><span>定型作業を自動化</span></article>
    </div>
  </section>
</div>
CSS
/* FlowDesk SaaS テーマ: 紺/青/白 */
* { box-sizing: border-box; }
body {
  margin: 0;
  font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
  background:
    radial-gradient(circle at 85% 10%, rgba(79,124,255,.22) 0%, transparent 45%),
    #0f1b34;
  color: #fff;
}

.fd {
  height: 400px;
  display: flex;
  flex-direction: column;
}

/* ナビバー */
.fd__bar {
  display: flex;
  align-items: center;
  gap: 30px;
  padding: 18px 32px;
  border-bottom: 1px solid rgba(255,255,255,.08);
}
.fd__logo {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-weight: 800;
  font-size: 17px;
}
.fd__mark {
  width: 18px;
  height: 18px;
  border-radius: 6px;
  background: linear-gradient(135deg, #4f7cff, #8fb0ff);
  box-shadow: 0 0 16px rgba(79,124,255,.6);
}
.fd__nav {
  display: flex;
  gap: 30px;
  margin: 0 auto;
}
.fd__cta {
  font-size: 13px;
  font-weight: 700;
  color: #fff;
  background: linear-gradient(135deg, #4f7cff, #6f96ff);
  padding: 9px 18px;
  border-radius: 999px;
  box-shadow: 0 8px 22px rgba(79,124,255,.4);
}

/* 主役: 下線アニメリンク(疑似要素のみ) */
.link {
  position: relative;
  color: #e9eefb;
  text-decoration: none;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: .01em;
  padding-bottom: 5px;
}
.link::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  height: 2px;
  width: 100%;
  background: linear-gradient(90deg, #4f7cff, #8fb0ff);
}
.link--grow::after {
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .35s cubic-bezier(.65,0,.35,1);
}
.link--grow:hover::after { transform: scaleX(1); }

.link--center::after {
  transform: scaleX(0);
  transform-origin: center;
  transition: transform .35s cubic-bezier(.65,0,.35,1);
}
.link--center:hover::after { transform: scaleX(1); }

.link--swap::after {
  transform: scaleX(0);
  transform-origin: right;
  transition: transform .3s ease;
}
.link--swap:hover::after {
  transform: scaleX(1);
  transform-origin: left;
}

.link--draw::after { display: none; }
.link--draw::before {
  content: "";
  position: absolute;
  left: -6px;
  right: -6px;
  top: -3px;
  bottom: -3px;
  border: 2px solid #4f7cff;
  border-radius: 6px;
  clip-path: inset(0 0 100% 0);
  opacity: 0;
  transition: clip-path .3s ease, opacity .3s ease;
}
.link--draw:hover::before {
  clip-path: inset(0 0 0 0);
  opacity: 1;
}

/* 本文 */
.fd__body {
  flex: 1;
  padding: 28px 32px;
  display: grid;
  align-content: center;
  gap: 12px;
}
.fd__eyebrow {
  margin: 0;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .18em;
  color: #7da0ff;
}
.fd__h1 {
  margin: 0;
  font-size: 28px;
  font-weight: 800;
}
.fd__lead {
  margin: 0;
  font-size: 13px;
  color: rgba(255,255,255,.62);
}
.fd__cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
  margin-top: 14px;
}
.card {
  display: grid;
  gap: 4px;
  padding: 16px;
  background: rgba(255,255,255,.05);
  border: 1px solid rgba(255,255,255,.09);
  border-radius: 14px;
  font-size: 12px;
}
.card__ico {
  font-size: 18px;
  color: #6f96ff;
}
.card b { font-size: 14px; }
.card span { color: rgba(255,255,255,.55); }

@media (prefers-reduced-motion: reduce) {
  .link::after, .link--draw::before { transition: none; }
}
JavaScript
// 下線アニメリンクは純CSSで完結するためJSは不要。
// 将来の計測フックのみ用意(実害なし・null安全)。
(() => {
  const nav = document.querySelector('.fd__nav');
  if (!nav) return; // null安全
  // ナビ内リンクのクリックでページ遷移しないデモ用ガード
  nav.addEventListener('click', (e) => {
    const a = e.target.closest('a');
    if (a) e.preventDefault();
  });
})();

コード

HTML
<!-- アニメ下線リンク: 4種類のホバー下線パターン -->
<div class="stage">
  <nav class="nav" aria-label="サンプルナビ">
    <a class="link link--grow" href="#">Slide&nbsp;In</a>
    <a class="link link--center" href="#">Center&nbsp;Out</a>
    <a class="link link--swap" href="#">Swap&nbsp;Through</a>
    <a class="link link--draw" href="#">Bracket</a>
  </nav>
  <p class="hint">各リンクにカーソルを乗せてください</p>
</div>
CSS
* { box-sizing: border-box; }
body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  place-items: center;
  font-family: "Segoe UI", system-ui, sans-serif;
  background: #11131c;
  color: #e9ecf5;
}

.stage {
  display: grid;
  place-items: center;
  gap: 28px;
}

.nav {
  display: flex;
  flex-wrap: wrap;
  gap: 34px;
  justify-content: center;
}

/* リンク共通: 下線はすべて疑似要素で描く(JS不要・純CSS) */
.link {
  position: relative;
  color: #e9ecf5;
  text-decoration: none;
  font-size: 19px;
  font-weight: 600;
  letter-spacing: .02em;
  padding-bottom: 4px;
}

.link::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  height: 2px;
  width: 100%;
  background: linear-gradient(90deg, #6ee7ff, #a78bff);
}

/* 1. 左から伸びる */
.link--grow::after {
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .35s cubic-bezier(.65,0,.35,1);
}
.link--grow:hover::after,
.link--grow:focus-visible::after { transform: scaleX(1); }

/* 2. 中央から両端へ */
.link--center::after {
  transform: scaleX(0);
  transform-origin: center;
  transition: transform .35s cubic-bezier(.65,0,.35,1);
}
.link--center:hover::after,
.link--center:focus-visible::after { transform: scaleX(1); }

/* 3. 右へ抜けて左から入る(スワップ) */
.link--swap::after {
  transform: scaleX(0);
  transform-origin: right;
  transition: transform .3s ease;
}
.link--swap:hover::after,
.link--swap:focus-visible::after {
  transform: scaleX(1);
  transform-origin: left;
}

/* 4. 上下の括弧線が閉じる */
.link--draw::after { display: none; }
.link--draw::before {
  content: "";
  position: absolute;
  left: -6px;
  right: -6px;
  top: -3px;
  bottom: -3px;
  border: 2px solid #6ee7ff;
  border-radius: 6px;
  clip-path: inset(0 0 100% 0);
  opacity: 0;
  transition: clip-path .3s ease, opacity .3s ease;
}
.link--draw:hover::before,
.link--draw:focus-visible::before {
  clip-path: inset(0 0 0 0);
  opacity: 1;
}

.link:focus-visible { outline: none; }

.hint {
  margin: 0;
  font-size: 13px;
  color: rgba(233, 236, 245, .5);
}

@media (prefers-reduced-motion: reduce) {
  .link::after, .link--draw::before { transition: none; }
}
JavaScript
// アニメ下線リンクは純CSSで完結。クリックでのページ遷移だけ抑止する。
(() => {
  document.querySelectorAll('.link').forEach((a) => {
    a.addEventListener('click', (e) => e.preventDefault()); // デモ用に遷移無効化
  });
})();

🤖 AIエージェント用プロンプト

このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「アニメ下線リンク」の効果を追加してください。

# 追加してほしい効果
アニメ下線リンク(マイクロインタラクション)
純CSSの疑似要素だけで作る4種のホバー下線(左伸び・中央展開・スワップ・枠線)。ナビゲーションやテキストリンクの質感を高めます。

# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】

# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- アニメ下線リンク: 4種類のホバー下線パターン -->
<div class="stage">
  <nav class="nav" aria-label="サンプルナビ">
    <a class="link link--grow" href="#">Slide&nbsp;In</a>
    <a class="link link--center" href="#">Center&nbsp;Out</a>
    <a class="link link--swap" href="#">Swap&nbsp;Through</a>
    <a class="link link--draw" href="#">Bracket</a>
  </nav>
  <p class="hint">各リンクにカーソルを乗せてください</p>
</div>

【CSS】
* { box-sizing: border-box; }
body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  place-items: center;
  font-family: "Segoe UI", system-ui, sans-serif;
  background: #11131c;
  color: #e9ecf5;
}

.stage {
  display: grid;
  place-items: center;
  gap: 28px;
}

.nav {
  display: flex;
  flex-wrap: wrap;
  gap: 34px;
  justify-content: center;
}

/* リンク共通: 下線はすべて疑似要素で描く(JS不要・純CSS) */
.link {
  position: relative;
  color: #e9ecf5;
  text-decoration: none;
  font-size: 19px;
  font-weight: 600;
  letter-spacing: .02em;
  padding-bottom: 4px;
}

.link::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  height: 2px;
  width: 100%;
  background: linear-gradient(90deg, #6ee7ff, #a78bff);
}

/* 1. 左から伸びる */
.link--grow::after {
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .35s cubic-bezier(.65,0,.35,1);
}
.link--grow:hover::after,
.link--grow:focus-visible::after { transform: scaleX(1); }

/* 2. 中央から両端へ */
.link--center::after {
  transform: scaleX(0);
  transform-origin: center;
  transition: transform .35s cubic-bezier(.65,0,.35,1);
}
.link--center:hover::after,
.link--center:focus-visible::after { transform: scaleX(1); }

/* 3. 右へ抜けて左から入る(スワップ) */
.link--swap::after {
  transform: scaleX(0);
  transform-origin: right;
  transition: transform .3s ease;
}
.link--swap:hover::after,
.link--swap:focus-visible::after {
  transform: scaleX(1);
  transform-origin: left;
}

/* 4. 上下の括弧線が閉じる */
.link--draw::after { display: none; }
.link--draw::before {
  content: "";
  position: absolute;
  left: -6px;
  right: -6px;
  top: -3px;
  bottom: -3px;
  border: 2px solid #6ee7ff;
  border-radius: 6px;
  clip-path: inset(0 0 100% 0);
  opacity: 0;
  transition: clip-path .3s ease, opacity .3s ease;
}
.link--draw:hover::before,
.link--draw:focus-visible::before {
  clip-path: inset(0 0 0 0);
  opacity: 1;
}

.link:focus-visible { outline: none; }

.hint {
  margin: 0;
  font-size: 13px;
  color: rgba(233, 236, 245, .5);
}

@media (prefers-reduced-motion: reduce) {
  .link::after, .link--draw::before { transition: none; }
}

【JavaScript】
// アニメ下線リンクは純CSSで完結。クリックでのページ遷移だけ抑止する。
(() => {
  document.querySelectorAll('.link').forEach((a) => {
    a.addEventListener('click', (e) => e.preventDefault()); // デモ用に遷移無効化
  });
})();

# 外部ライブラリ
なし(追加ライブラリ不要)

# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。