CSS 下線 アニメーション9選 — 一行でコピペOK
css 下線 アニメーションは、単語や文の下・背後に敷いた線や面を動かして、読む人の視線をその一語に止める技法です。今回はこの技法を9つ選び、それぞれ6〜25行のコードにまとめました。上のグリッドは表示された瞬間から自動で繰り返し動いていて、触っても反応は変わりません。
自動再生 · タイルを押すとその項目へ · すべて1つのzip
9つは効果の強さではなく、文中で何を伝えるための強調かという順に並べました。まず文の一部を塗って目立たせる3つ(01〜03)、次に注意や完了を伝える2つ(04〜05)、続いてリンクや案内線で先へ誘う2つ(06〜07)、最後にバッジや数字を一度だけ弾ませる2つ(08〜09)という流れです。各項目のコードは要点だけを載せていて、残りはzipの中にまとめてあります。
01マーカー強調
キーワードの背後に黄色いブロックを重ね、transform: scaleX(0→1)で左から塗り広げたあと、mix-blend-mode: multiplyで文字の上に馴染ませます。規約や案内文の中で必ず読んでほしい一文、チュートリアルの強調語に向いています。
.hl {
position: relative;
isolation: isolate;
&::before {
content: ""; position: absolute; inset: 12% -2% 6%;
background: $color; border-radius: $r-xs;
transform: scaleX(0); transform-origin: left center;
mix-blend-mode: multiply; z-index: -1;
animation: mark $duration $easing infinite;
}
}
@keyframes mark {
0% { transform: scaleX(0); }
20% { transform: scaleX(1); }
75% { transform: scaleX(1); }
100% { transform: scaleX(0); }
}
02マーカー重ね
同じ黄色ブロックを::beforeと::afterの2枚に分け、それぞれ違うrotate角度と遅延でscaleXを重ねて塗り、手で二度なぞったようなずれた太さを作ります。ページの中で最も重要な一語や、FAQの答えのキーワードに向いています。
.hl::before, .hl::after {
content: ""; position: absolute; inset: 10% -2% 4%;
transform: scaleX(0); transform-origin: left center;
mix-blend-mode: multiply; z-index: -1;
}
.hl::before { background: $stage-yellow; rotate: -1.2deg; animation: mark $duration $easing infinite; }
.hl::after { background: $color; opacity: .55; rotate: 1deg; animation: mark $duration $easing infinite .3s; }
03手描き下線
インラインSVGの曲線パスをstroke-dashoffsetで140から0へ動かし、ペンで今しがた引いたように左から下線が現れます。引用文中のキーワードや、自己紹介ページの強調語に向いています。
.hl__path {
fill: none; stroke: $color; stroke-width: 3; stroke-linecap: round;
stroke-dasharray: 140; stroke-dashoffset: 140;
animation: draw $duration $easing infinite;
}
@keyframes draw {
0% { stroke-dashoffset: 140; }
35% { stroke-dashoffset: 0; }
80% { stroke-dashoffset: 0; }
100% { stroke-dashoffset: -140; }
}
04波線下線
ネイティブのtext-decoration-style: wavyはそのまま使い、@propertyで登録した長さの値をtext-underline-offsetに入れて0pxと5pxの間で揺らします。入力必須の合図や、スペルチェック風に注意を引きたい警告文に向いています。
@property --underline-offset {
syntax: "<length>"; inherits: true; initial-value: 0px;
}
.hl {
text-decoration-line: underline;
text-decoration-style: wavy;
text-decoration-color: $color;
text-underline-offset: var(--underline-offset);
animation: wave $duration linear infinite;
}
@keyframes wave {
0%, 100% { --underline-offset: 0px; }
50% { --underline-offset: 5px; }
}
05取り消し線アニメーション
文字の中央の高さに重ねた.price__strikeのバーをtransform: scaleX(0→1)で左から引き、値下げや完了処理の瞬間を目の前で見せます。割引前の価格表示や、片付いたToDo項目に向いています。
.price__strike {
position: absolute; left: -2%; right: -2%; top: 52%; height: 2px;
background: $color;
transform: scaleX(0); transform-origin: left center;
animation: strike $duration $easing infinite;
}
@keyframes strike {
0% { transform: scaleX(0); }
30% { transform: scaleX(1); }
80% { transform: scaleX(1); }
100% { transform: scaleX(0); }
}
06背景塗りつぶし
リンクの背景をbackground-sizeだけで0%から100%まで満たし、background-position: leftで固定して塗られた縁が後戻りしないようにします。ナビゲーションのリンクや、カード内のテキストボタンに向いています。
.nav {
background: linear-gradient(90deg, $color, $color);
background-repeat: no-repeat;
background-position: left center;
background-size: 0% 100%;
animation: fill $duration $easing infinite;
}
@keyframes fill {
0% { background-size: 0% 100%; }
25% { background-size: 100% 100%; }
75% { background-size: 100% 100%; }
100% { background-size: 0% 100%; }
}
07点線が伸びる
点を一定間隔で描いた細い帯を文字の下に置き、transform: scaleXで左から伸ばして点線が継ぎ足されるように見せます。段階ガイドで次の手順へ導く経路や、脚注をつなぐ線に向いています。
.hl__dots {
background-image: repeating-linear-gradient(90deg, $color 0 3px, transparent 3px 8px);
transform: scaleX(0); transform-origin: left center;
animation: grow $duration $easing infinite;
}
@keyframes grow {
0% { transform: scaleX(0); }
40% { transform: scaleX(1); }
80% { transform: scaleX(1); }
100% { transform: scaleX(0); }
}
08色のスイープ
バッジの上に幅250%の斜めグラデーション層を敷き、background-positionを150%から-150%へlinear・infiniteで動かして光の帯を継ぎ目なく走らせます。プロモーションバッジや、新着・限定を示すラベルに向いています。
.badge::after {
background: linear-gradient(115deg, transparent 30%, $color 48%, transparent 66%);
background-size: 250% 100%;
mix-blend-mode: overlay;
animation: sweep $duration linear infinite;
}
@keyframes sweep {
0% { background-position: 150% 0; }
100% { background-position: -150% 0; }
}
09テキストシャドウポップ
@propertyで登録した長さの値をfilter: drop-shadow()のぼかし半径に入れて0から10pxへ動かしつつ、同時にtransform: scale(1→1.06→1)で軽く弾ませます。価格表の最終価格や、カウントダウン数字を一度だけ強く見せたいときに向いています。
@property --shadow-blur {
syntax: "<length>"; inherits: false; initial-value: 0px;
}
.hl {
filter: drop-shadow(0 0 var(--shadow-blur) $color);
animation: pop $duration $easing infinite;
}
@keyframes pop {
0% { transform: scale(1); --shadow-blur: 0px; }
40% { transform: scale(1.06); --shadow-blur: 10px; }
100% { transform: scale(1); --shadow-blur: 0px; }
}
見えなくなる落とし穴はどこか
9つのうち01のマーカー強調と02のマーカー重ねは、最初に組んだときは画面にまったく映りませんでした。z-index: -1で文字の裏に置いた黄色いブロックを、親の.hlにisolation: isolateを付けずに置くと、位置指定のない祖先(舞台の背景)の描画よりも先に、つまり下に描かれてしまい完全に隠れます。isolation: isolateを1行足すとマーカーが現れ、レンダリング計測で0%だった塗られた面積が0.8%以上まで上がりました。
04の波線下線にも似た落とし穴がありました。text-underline-offsetを1pxから3pxの間だけで揺らすと、フレーム同士の変化がピクセルの判定基準より小さく、計測では「ほぼ動いていないアニメーション」として弾かれました。範囲を0pxから5pxに広げると波が普通に見えるようになりました。text-decoration-thicknessとtext-underline-offsetそのものはMDNの対応表にあるとおり主要ブラウザで広く安定して使えます。ここまでの修正が反映済みの9ファイルを開く合言葉はhx9yh6mrで、下のカードから取り出せます。
アクセシビリティ
OSで「視覚効果を減らす」を選んだ人には、9項目それぞれ違う形で強調だけが残ります。01・02のマーカーは塗られた状態のまま止まり、03の下線は描き終えた形で固定され、04の波線は真ん中の位置で動きを止めます。05の取り消し線は引かれた状態のまま残り、09の影は淡く残ってジャンプだけが消え、03・05・07のように別レイヤーを足した項目にはaria-hidden="true"を付けて読み上げが装飾を飛ばし本文だけを読むようにしています。prefers-reduced-motion自体がどのブラウザから使えるかはMDNのドキュメントにまとまっています。
@media (prefers-reduced-motion: reduce) {
.hl::before, .hl::after, .hl__path,
.price__strike, .hl__dots, .badge::after { animation: none; }
.hl { animation: none; text-underline-offset: 2px; filter: drop-shadow(0 0 4px $color); }
}
強調以外のCSSアニメーションはこちらの一覧からたどれます。サイトの成り立ちは紹介ページに書きました。
FAQ
マーカーと波線下線を同じ文に重ねてもいいですか?
技術的にはできますが、一つの文には一つの強調だけにとどめるのが読みやすいです。01・02のマーカーと04の波線を同じ語に重ねると、どちらが本当に大事な部分なのか分かりにくくなります。05の取り消し線と08のバッジのスイープのように文脈が離れた要素同士なら、同じ画面にあっても違和感はありません。
強調する単語はどうやって指定しますか?
HTMLでは強調したい部分だけをspanで囲み、残りはそのままにします。zipのreactフォルダに入っているReact版では、対象の文字列をpropsとして渡すだけで、動きの長さや色はSCSS側の変数が受け持ちます。
スクリーンリーダーではどう聞こえますか?
本文の文字そのものは触らず、背景や下線、影といった装飾の層だけを動かしているので、読み上げの内容は変わりません。03の手描き下線・05の取り消し線・07の点線のように装飾だけの層を足した項目にはaria-hidden="true"を付け、読み上げが装飾を飛ばして本文だけを読むようにしています。