アニメ下線リンク
純CSSの疑似要素だけで作る4種のホバー下線(左伸び・中央展開・スワップ・枠線)。ナビゲーションやテキストリンクの質感を高めます。
ライブデモ
使用例(お題: 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 In</a>
<a class="link link--center" href="#">Center Out</a>
<a class="link link--swap" href="#">Swap 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 In</a>
<a class="link link--center" href="#">Center Out</a>
<a class="link link--swap" href="#">Swap 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で提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。