GODRICH
#cssアニメーション#ボタン#ローディング#トランジション#スクロール#ダッシュボード#実用ui#デザイントレンド

CSS タップ エフェクト 9選 — 一行でコピペOK

css タップ エフェクトとは、指でボタンやカード、リンクに触れた瞬間だけ色・大きさ・影が変わり、入力が確かに届いたとスマホの画面だけで伝える演出です。この記事では9種類を選び、それぞれ6〜25行のコードとして実装しました。

自動再生 · タイルを押すとその項目へ · すべて1つのzip

並び順は人気度ではなく、指先の一回のタップが実際に通っていく順番です。押すとまずリングが膨らみ(01)、押し込まれた感触が続き(02)、触れる機器によって反応が分かれ(03)、長く押すと待ち時間が挟まり(04)、リストとカードを選び(05・06)、いいねを短く確認し(07)、チェックボックスで状態を切り替え(08)、最後はリンクを押して次の画面へ進みます(09)。各コードは「スマホ ボタン タップ css」で調べて出てくる形の要点だけを切り出してあり、残りのマークアップと変形値はzipにそのまま入っています。07で一瞬だけ流れるいいねの確認だけをしっかり育てたい場合はCSS ハート アニメーションの九つがその一動作だけを扱い、01のリングを一度弾けさせるのではなくボタンの縁を回り続ける線にしたい場合はCSS 枠線 アニメーションがその続きになります。

01アウトラインのリングパルス

波紋ではなく外側のリング

ボタンの内側を塗りつぶす波紋ではなく、外側でbox-shadowのリングが一度だけ膨らんで消えます。タップが終わってもリングは最後まで再生される必要があるため、:activeではなくpointerdownでクラスを付け替えるJSを添えています。アイコンボタンや、すでに塗りつぶしリップルがある画面で二つ目の合図が要る場所に向いています。

アウトラインのリングパルス
新しいタブで開く

01 · アウトラインのリングパルス — 計測 累積面積 4.103% · 動いたフレーム 11/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

box-shadowpointerdown0.6초

アイコンボタンでの使いどころ

.btn::after {
  content: "";
  position: absolute;
  inset: -2px;
  border-radius: inherit;
  box-shadow: 0 0 0 0 rgba($color, .55);
  opacity: 0;
}
.btn.is-ringing::after {
  animation: ring $duration $easing forwards;
}
@keyframes ring {
  0%   { box-shadow: 0 0 0 0 rgba($color, .55); opacity: 1; }
  100% { box-shadow: 0 0 0 26px rgba($color, 0); opacity: 0; }
}

02押し込み縮小

押している間に縮む値

押している間scale(.96)まで縮み、box-shadowも同時に浅くなります。離すとバウンスなしで即座に元の大きさへ戻り、touch-action: manipulationでモバイルブラウザの300msタップ遅延とダブルタップ拡大も消します。送信・決済ボタンなど、押した確信が重要なCTAに向いています。

押し込み縮小+影の減少
新しいタブで開く

02 · 押し込み縮小+影の減少 — 計測 累積面積 6.326% · 動いたフレーム 19/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

scalebox-shadow0.2초

タップ遅延を消す一行

.btn {
  transform: scale(1);
  transition: transform $duration $easing, box-shadow $duration $easing;
  touch-action: manipulation;
}
.btn:active {
  transform: scale(.96);
  box-shadow: $shadow-press;
}

03hoverなし端末専用の:active

hoverなし端末だけに効かせる

@media (hover: none) and (pointer: coarse)のブロックの中にだけ:activeの背景色を置きます。マウスのある機器はこのブロック自体を読まないため、スマホでタップした後に灰色が張り付く定番のバグがここでは起きません。

hoverなし端末専用の:active
新しいタブで開く

03 · hoverなし端末専用の:active — 計測 累積面積 8.344% · 動いたフレーム 12/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

@media (pointer: coarse)hover: none:active

マウス機器では読まれない

@media (hover: none) and (pointer: coarse) {
  .btn:active {
    background: $stage-ink;
    color: $subject-cream;
  }
}

04長押しで現れる遅延リアクション

600ms待ってから動く

押した瞬間には何も起きず、transition-delayで600ms遅れてからカードが大きくなり影が濃くなります。背景が満ちていく進捗バーではなく、遅延ひとつで短いタップと長押しを見分けます。touch-action: noneでスクロール操作との奪い合いも避けています。

長押しで現れる遅延リアクション
新しいタブで開く

04 · 長押しで現れる遅延リアクション — 計測 累積面積 10.674% · 動いたフレーム 13/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

:active + transition 지연touch-action: none0.6초

進捗バーではなく遅延ひとつで

.card {
  transition: transform $duration $easing $hold,
              box-shadow $duration $easing $hold,
              border-color $duration $easing $hold;
  touch-action: none;
}
.card:active {
  transform: scale(1.06);
  box-shadow: $shadow-raised;
  border-color: $color;
}

05リスト行のタップハイライト

「css タップ ハイ ライト 消す」の次に置くもの

-webkit-tap-highlight-colorを透明にした上で、行をタップすると独自の背景色が一瞬光り0.2秒で消えます。行の高さは44px以上を確保して指の幅に合わせてあり、設定メニューやチャットルーム一覧のように行単位でタップするリストに向いています。

リスト行のタップハイライト
新しいタブで開く

05 · リスト行のタップハイライト — 計測 累積面積 19.752% · 動いたフレーム 16/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

-webkit-tap-highlight-color:active0.2초

指の幅に合わせた行の高さ

.list li {
  min-height: 44px;
  transition: background $duration $easing;
}
.list li:active {
  background: rgba($color, .18);
  transition: background 0s;
}

06カードタップのスケール+明るさ

カード全体が沈む二つの値

カードをタップしている間scale(.98)とfilter: brightness(.94)が同時にかかり、カード全体が押し込まれたように見えます。touch-action: manipulationで商品カードを素早く二度押したときの拡大ズームも防ぎます。

カードタップのスケール+明るさ
新しいタブで開く

06 · カードタップのスケール+明るさ — 計測 累積面積 7.167% · 動いたフレーム 19/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

scalefilter: brightnesstouch-action

二度押しの拡大を止める

.card {
  transform: scale(1);
  filter: brightness(1);
  transition: transform $duration $easing, filter $duration $easing;
  touch-action: manipulation;
}
.card:active {
  transform: scale(.98);
  filter: brightness(.94);
}

07タップ確認ジッター

140msだけの小さな震え

タップした直後の140msだけ±2px幅で小さく震え、同時にハートのアイコンが薄い色から濃い色へ塗り替わります。左右に大きく三回揺れるエラー表示とは幅も目的も違う、成功だけを知らせる短い震えです。

タップ確認ジッター(エラー揺れではない)
新しいタブで開く

07 · タップ確認ジッター(エラー揺れではない) — 計測 累積面積 1.997% · 動いたフレーム 9/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

translate 지터:active0.14초

エラー揺れとの違い

.btn:active { animation: jitter $duration $easing; }
.btn:active .btn__icon { color: $error; }

@keyframes jitter {
  0%   { transform: scale(1)    translate(0, 0); }
  25%  { transform: scale(1.18) translate(-2px, 1px); }
  50%  { transform: scale(1.1)  translate(2px, -1px); }
  75%  { transform: scale(1.16) translate(-1px, 1px); }
  100% { transform: scale(1)    translate(0, 0); }
}

08チェックボックスのタップフィードバック

チェックの線が描かれる仕組み

カスタムチェックボックスをタップすると、チェックの線がstroke-dashoffsetで描かれると同時に箱全体がscale(1.08)まで一度だけ弾みます。本物のinput[type=checkbox]は隠し、隣接兄弟セレクタで箱と線をまとめて動かします。

チェックボックスのタップフィードバック
新しいタブで開く

08 · チェックボックスのタップフィードバック — 計測 累積面積 1.833% · 動いたフレーム 9/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

stroke-dashoffsetscale0.2초

隠したinputと兄弟セレクタ

.cb__input:checked ~ .cb__box {
  border-color: $color;
  background: $color;
  transform: scale(1.08);
}
.cb__input:checked ~ .cb__box .cb__check {
  stroke-dashoffset: 0;
}
.cb__check {
  stroke-dasharray: 24;
  stroke-dashoffset: 24;
  transition: stroke-dashoffset $duration $easing;
}

09インラインリンクのタップフィードバック

文字の背後に出る色ブロック

本文中のリンクをタップすると文字の背後に色のブロックが現れ、0.14秒で消えます。既定のタップハイライトを透明にした場所にも、押した合図をこの背景色だけで残します。

インラインリンクのタップフィードバック
新しいタブで開く

09 · インラインリンクのタップフィードバック — 計測 累積面積 1.469% · 動いたフレーム 8/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

background:active0.14초

既定のハイライトを消した場所で

.p__link {
  background: transparent;
  transition: background $duration $easing;
}
.p__link:active {
  background: rgba($color, .32);
  transition: background 0s;
}

どこで壊れるか — 落とし穴

背景のアルファ値しか動かさない効果は、目には見えていても測定では止まって見える区間ができます。ai ランディングページの光るオーブのセクションをレンダリングしたときにも、まったく同じところで引っかかりました。

linearが変化を細かく割りすぎる

09のインラインリンクを最初に作ったときは、backgroundのアルファ値を0%から60%までlinearで均等に2秒かけて動かしていました。レンダリングしたあと測定ファイルを開くと、累積面積は「動いた」と出たのに強度の値が閾値ぎりぎりで低く、映像としては動いていても数値上は静止画に近い判定になりました。設定のピクセル閾値は255段階中12で、linearはフレーム数ぶん変化を均等に割るため、総量は変えずに範囲を0〜35%まで狭めて1フレームあたりの変化幅を広げてから、ようやく強度が閾値を超えました。この狭めた範囲を反映した圧縮ファイルの合言葉はe8dzq4mpで、それより前は09が測定上「動かないリンク」のままでした。

アクセシビリティ

動きを止めても残る最後の状態

「css ボタン 押す」ときの反応は9つともprefers-reduced-motion: reduceでは動きを止めますが、押した・チェックされた・ハイライトされたという最後の状態は残るので反応そのものは消えません。キーボード操作のために02・04・06・08には:focus-visibleの専用アウトラインを付けています。

.card:focus-visible {
  outline: 3px solid $color;
  outline-offset: 3px;
}

既定のフォーカスリングとタップ面

残りの五つ(01・03・05・07・09)はボタン・リスト・リンクの既定のフォーカスリングをそのまま使い、上書きしていません。タップする面の大きさもコードで確保していて、05のリスト行はmin-height: 44px、08のチェックボックスは見た目の箱が26pxでもlabel自体にmin-height: 44pxを与えて実際のタップ領域を広げています。ただし09のように文章の中に混じったリンクはWCAG 2.5.8の例外(文中・ブロックテキスト内のリンク)にあたるため、44pxは強制していません。@media (hover: none) and (pointer: coarse)はcaniuseのメディア機能対応表で確認できるとおり、Chrome・Safari・Firefoxとも早くから対応しています。

次に読む

タップ以外のCSSアニメーションはカテゴリー一覧にまとめてあり、このサイトの作り方は紹介ページに書いています。

FAQ

iOS Safariでも01のリングはタップした瞬間に現れますか?

:activeだけで作っていたら遅れて現れることがあります。MDNの:activeの解説には、iOS Safariは<body>など上位の要素にtouchstartリスナーが一つでもないと:activeのスタイルを遅延なく適用しない、と書かれています。01は最初から:activeではなくpointerdownイベントでクラスを直接付け替えているため、この遅延を避けています。

03のタッチ専用スタイルは、マウスも挿せるタブレットではどうなりますか?

(hover: none) and (pointer: coarse)は「主な入力手段」を基準に判定されます。マウスを挿しても端末自身がタッチを主入力として報告していればタッチ用スタイルのままで、逆にマウスが主入力に切り替わるとこのブロックは静かに効かなくなります。どちらの状態でも見た目が壊れることはありません。

「css タップ アニメーション」を9つ同じフォームに一緒に使ってもいいですか?

使えます。02・06・08はボタン・カード・チェックボックスという別々の要素に効いているので、同じ画面に混ざっても何を押したか迷いません。ただし04のように遅延がある反応と02のように即座に反応するものを同じ要素に重ねると、どちらの反応を待てばいいか分からなくなるため、要素ひとつにつき一つだけに絞ってください。指を離したあとも読み込み中から完了まで見せ続ける必要があるボタンは、この九つではなくcss ボタンの状態遷移のほうで扱っています。

解凍パスワードを入力してください

パスワードはこの記事の本文の中にあります。読み進めると出てきます。