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

CTA ボタン デザイン 9選 — 伸びる影から跳ねるバーまで

9つのうち1つは伸びる影の長さの値がホバー状態に応じて伸び縮みし、1つは周囲の進行の値が決まった時間の間に0まで減り、もう1つは引いた位置の値が基準を超えると確定した状態に変わります。cta ボタン デザインの9つの動きを見ていきます。

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

9つのどれも決済処理や実際の通知サービスにはつながっていません。09のタップは実際のWeb Audioで拾った音ではなく擬似的に再現した反応にすぎず、この中にfetchを呼び出す処理は一つもありません。9つのうちどれも、訪問者が実際に行った操作に応じて動きます。01・07はmouseenter/mouseleaveとfocus/blurによるホバーやフォーカスの状態に応じ、02はmouseenter/focus/clickに応じ、03・04・08・09は単純なclickに応じ、05はclickとkeydownの両方に応じ、06はpointerdown-pointermove-pointerupと続くドラッグに応じます — 自動再生だけで動くものは9つの中に一つもありません。

9つのパターンの解剖 — どの値がいつ変わるか

9つのパターンは、ホバー・クリック・ドラッグのいずれかを動かす価値へ変換する3つの方法に分かれており、この層を省いてしまうと、cta ボタン 作り方の実例集が9つの同じ色の四角形の並びのように見えてしまい、1つのボタンに組み込まれた9つの小さな計算装置には見えなくなってしまいます。

なぜ3つのパターンは経過時間から直接、物理や三角関数の式を解くのか

03・05・08は範囲のある進行比率にはまったく触れず、それぞれ時間ベースの式を解いています。03のtick()は毎フレームGRAVITY(420)とDRAG(0.985)を各破片の速度に積み上げており、そのため散らばりが台本通りの弧ではなく実際に重力を受けて落ちていく破片のように見えます。05のease()は経過秒数をFREQ(14)で振動しDECAY(9)で減衰するサイン項に代入しており、これが反転した面が180度を超えてから落ち着く動きを生んでいます。08のframe()は経過時間をeaseInOutSine()に通して固定のCYCLE(1100ms)の中でハイライト帯を行き来させており、直線的なスクラブでは決してありません。

なぜ3つのパターンは同じ滑らかさの形で値を目標へ引き寄せるのか

01・06・09はいずれも同じ形を繰り返しています — 目標値と現在値の差に一定の係数を掛けて毎フレーム足し戻し、差が縮まるまで続けるという形です。01のstep()はlenをtargetへdiff * 0.28で近づけます。06のsettle()はドラッグ後のposを0か1へdiff * 0.3で戻します。09のtick()はタップ後に各バーの高さをMIN_Hへdiff * DECAY(0.14)で戻します。3つの間で違うのは係数と目標だけです。

なぜ3つのパターンはインデックス・ティック数・時間比率で動きを決めるのか

02・04・07はいずれも物理項以外の値から動きを計算しています。02のrunGlitch()は経過ティックを固定のTICK(40ms)とDURATION(320ms)の組と比較してオフセットを戻すタイミングを決めています。04のframe()は経過時間をDURATION(1500ms)で割った0から1の進行比率をstrokeDashoffsetに直接反映させます。07の文字ごとの遅延は経過時間ではなく各文字自身のインデックスをMath.pow(i, 0.82) * BASEに代入して決まっており、フレームループがどれだけ速くても画面上最後の文字(インデックス9)は1番目より400ms以上長く待たされます。

パターン別コード

01ロングシャドウ・レトロ — ホバー状態に応じて伸びる影の長さの値が変わるボタン

影が伸びる長さの値はホバー状態になると目標値まで伸び、ホバー状態が解けると元の長さの値に戻ります。

ロングシャドウ・レトロ — ホバー状態に応じて伸びる影の長さの値が変わるボタン
新しいタブで開く

01 · ロングシャドウ・レトロ — ホバー状態に応じて伸びる影の長さの値が変わるボタン — 計測 累積面積 2.695% · 動いたフレーム 8/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

MAXLEN=22buildShadowlerp

数字が示すもの

23フレームのうち01は8フレームで動き、累積面積2.70%・平均強度166.3を記録しています — 見えている変化は1pxずつずれた影の層が近い色から遠い色へ塗り替わっていく積み重ねで、step()内のdiff * 0.28という滑らかさがあるからこそ、影の長さは一気に飛ぶのではなく目標値へ緩やかに近づいていきます。

function buildShadow(l) {
  var n = Math.round(l);
  if (n <= 0) { return 'none'; }
  var parts = [];
  for (var i = 1; i <= n; i++) {
    var t = i / n;
    var r = Math.round(lerp(C_NEAR[0], C_FAR[0], t));
    var g = Math.round(lerp(C_NEAR[1], C_FAR[1], t));
    var b = Math.round(lerp(C_NEAR[2], C_FAR[2], t));
    parts.push(i + 'px ' + i + 'px 0 rgb(' + r + ',' + g + ',' + b + ')');
  }
  return parts.join(',');
}

function step() {
  var diff = target - len;
  len += diff * 0.28;
  if (Math.abs(diff) < 0.05) { len = target; }
  btn.style.boxShadow = buildShadow(len);
  if (Math.abs(target - len) > 0.05) {
    raf = requestAnimationFrame(step);
  } else {
    raf = null;
  }
}

02ずれ分離 — 重なったテキスト層の位置の値が一瞬ずれてから再び重なるボタン

ホバー状態になった瞬間、重なった2つのテキスト層の位置の値が決まった幅だけ一瞬ずれ、決まった時間が過ぎると再び同じ位置の値に重なります。

ずれ分離 — 重なったテキスト層の位置の値が一瞬ずれてから再び重なるボタン
新しいタブで開く

02 · ずれ分離 — 重なったテキスト層の位置の値が一瞬ずれてから再び重なるボタン — 計測 累積面積 0.914% · 動いたフレーム 7/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

SEED=20260920DURATION=320mulberry32

数字が示すもの

23フレームのうち02は7フレームで動き、累積面積0.91%・平均強度115.5を記録しています — runGlitch()は種を固定したmulberry32()の乱数をTICK(40ms)ごとに読み取りDURATION(320ms)に達するまで続けるので、同じSEEDであれば毎回のホバーで同じずれの並びが再現され、ホバーのたびに違う崩れ方にはなりません。

function runGlitch() {
  if (glitchTimer) { return; }
  var elapsed = 0;
  glitchTimer = setInterval(function () {
    elapsed += TICK;
    if (elapsed >= DURATION) {
      clearInterval(glitchTimer); glitchTimer = null;
      layerA.style.transform = 'translate(0px,0px)';
      layerB.style.transform = 'translate(0px,0px)';
      return;
    }
    var ax = (rng() - 0.5) * 10;
    var ay = (rng() - 0.5) * 4;
    var bx = (rng() - 0.5) * 10;
    var by = (rng() - 0.5) * 4;
    layerA.style.transform = 'translate(' + ax.toFixed(1) + 'px,' + ay.toFixed(1) + 'px)';
    layerB.style.transform = 'translate(' + bx.toFixed(1) + 'px,' + by.toFixed(1) + 'px)';
  }, TICK);
}

03破片発散 — クリック地点から複数の破片がそれぞれ異なる角度の値で散ってから消えるボタン

クリックが起きた地点を基準に複数の破片がそれぞれ異なる角度の値と速度の値で広がり、透明度の値が時間とともに下がって消えた状態になります。

破片発散 — クリック地点から複数の破片がそれぞれ異なる角度の値で散ってから消えるボタン
新しいタブで開く

03 · 破片発散 — クリック地点から複数の破片がそれぞれ異なる角度の値で散ってから消えるボタン — 計測 累積面積 3.59% · 動いたフレーム 15/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

GRAVITY=420COLORSburst

数字が示すもの

23フレームのうち03は15フレームで動き、累積面積3.59%・平均強度62.8を記録しています — burst()はクリック地点から18個の破片をそれぞれ異なる角度と速度で撃ち出し、tick()は毎フレームGRAVITY(420)とDRAG(0.985)を各破片の速度へ積み上げてLIFE(900ms)が尽きるまで消えていく様子を描いています。

function burst() {
  var cx = canvas.width / 2, cy = canvas.height * 0.35;
  particles = [];
  for (var i = 0; i < 18; i++) {
    var angle = (Math.PI * 2 * i) / 18 + (Math.random() - 0.5) * 0.5;
    var speed = 90 + Math.random() * 110;
    particles.push({
      x: cx, y: cy,
      vx: Math.cos(angle) * speed,
      vy: Math.sin(angle) * speed - 60,
      rot: Math.random() * Math.PI,
      vrot: (Math.random() - 0.5) * 10,
      color: COLORS[i % COLORS.length],
      age: 0
    });
  }
  if (!raf) { lastT = performance.now(); raf = requestAnimationFrame(tick); }
}

04周囲進行 — ボタンを囲む周囲の進行の値が決まった時間の間に減っていくボタン

ボタンを囲む周囲の進行の値は決まった時間の間に最初の値から0まで減っていき、その値が0になった状態になると決まった動作に移ります。

周囲進行 — ボタンを囲む周囲の進行の値が決まった時間の間に減っていくボタン
新しいタブで開く

04 · 周囲進行 — ボタンを囲む周囲の進行の値が決まった時間の間に減っていくボタン — 計測 累積面積 1.598% · 動いたフレーム 20/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

R=38CIRCstartCountdown

数字が示すもの

23フレームのうち04は20フレームで動き、累積面積1.60%・平均強度150.9を記録しています — frame()は経過時間をDURATION(1500ms)で割った0から1の進行比率をリングのstrokeDashoffsetに直接反映させて円周から0まで減らしていき、0に達した瞬間に下の完了ラベルへ切り替わります。

function frame(now) {
  var t = now - startT;
  var progress = Math.min(1, t / DURATION);
  var offset = CIRC * (1 - progress);
  bar.style.strokeDashoffset = offset.toFixed(2);
  if (progress < 1) {
    raf = requestAnimationFrame(frame);
  } else {
    running = false; raf = null;
    btn.textContent = '完了しました';
    setTimeout(function () { btn.textContent = '次のステップ'; bar.style.strokeDashoffset = CIRC.toFixed(2); }, 500);
  }
}
function startCountdown() {
  if (running) { return; }
  running = true;
  startT = performance.now();
  raf = requestAnimationFrame(frame);
}

05立体反転 — クリックすると回転の値が180度まで回り反対の面が現れるボタン

クリックが起きると回転の値が180度になるまで回って反対の面が現れ、回転の値が目標を少し超えてから元の位置に戻る状態を経ます。

立体反転 — クリックすると回転の値が180度まで回り反対の面が現れるボタン
新しいタブで開く

05 · 立体反転 — クリックすると回転の値が180度まで回り反対の面が現れるボタン — 計測 累積面積 5.267% · 動いたフレーム 16/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

OVERSHOOT_DEG=18flipease

数字が示すもの

23フレームのうち05は16フレームで動き、累積面積5.27%・平均強度110.7を記録しています — ease()は経過秒数をOVERSHOOT_DEG(18)で拡大しFREQ(14)の周波数で振動しながらDECAY(9)で減衰するサイン項に代入しており、固定のDUR(500ms)の中で面のrotateY()の値が180度を超えてから落ち着きます。

function ease(tSec) {
  if (tSec <= 0) { return 0; }
  var settle = 1 - Math.exp(-DECAY * tSec);
  var wobble = Math.sin(tSec * FREQ) * Math.exp(-DECAY * tSec) * (OVERSHOOT_DEG / 180);
  return settle + wobble;
}
function frame(now) {
  var t = (now - startT) / 1000;
  var p = Math.min(1, ease(t));
  current = base + p * 180;
  btn.style.transform = 'rotateY(' + current.toFixed(2) + 'deg)';
  if (t < DUR / 1000) {
    raf = requestAnimationFrame(frame);
  } else {
    base = base + 180;
    current = base;
    btn.style.transform = 'rotateY(' + current.toFixed(2) + 'deg)';
    raf = null;
  }
}

06スライド確定 — 引いた位置の値が基準を超えると確定状態になるボタン

引いた位置の値が決まった基準の値を超えた状態になると確定した状態に変わり、基準に届かないまま離された状態になると位置の値は最初の場所に戻ります。

スライド確定 — 引いた位置の値が基準を超えると確定状態になるボタン
新しいタブで開く

06 · スライド確定 — 引いた位置の値が基準を超えると確定状態になるボタン — 計測 累積面積 5.158% · 動いたフレーム 16/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

THRESH=0.7THUMB_W=40settle

数字が示すもの

23フレームのうち06は16フレームで動き、累積面積5.16%・平均強度218.7という9つの中で最も高い強度を記録しています — 40pxの取っ手をpointermoveのたびに描き直しているためで、THRESH(0.7)に届かないまま離された場合はこの同じdiff * 0.3の滑らかさが取っ手を最初の位置へ戻し、指を離した場所にそのまま残ることはありません。

function settle() {
  var diff = target - pos;
  pos += diff * 0.3;
  if (Math.abs(diff) < 0.002) { pos = target; }
  apply(pos);
  if (Math.abs(target - pos) > 0.002) {
    raf = requestAnimationFrame(settle);
  } else {
    raf = null;
  }
}

07順次表示 — ホバー状態で2つ目の文言が文字の順番の値に沿って一つずつ現れるボタン

ホバー状態になると2つ目の文言の各文字が自分の順番の値の分だけ遅れて順に現れた状態になり、ホバーが解けると最初の文言に戻ります。

順次表示 — ホバー状態で2つ目の文言が文字の順番の値に沿って一つずつ現れるボタン
新しいタブで開く

07 · 順次表示 — ホバー状態で2つ目の文言が文字の順番の値に沿って一つずつ現れるボタン — 計測 累積面積 0.943% · 動いたフレーム 10/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

BASE=70TEXTreveal

数字が示すもの

23フレームのうち07は10フレームで動き、累積面積0.94%・平均強度64.6を記録しています — 各文字自身のインデックスがMath.pow(i, 0.82) * BASE(70)に代入されて自分のtransitionDelayを決めるため、文言は左から右へ少しずつ速度を落としながら現れ、すべての文字が同じ遅延で一斉に現れることはありません。

TEXT.split('').forEach(function (ch, i) {
  var span = document.createElement('span');
  span.textContent = ch === ' ' ? ' ' : ch;
  span.style.transitionDelay = (Math.pow(i, 0.82) * BASE).toFixed(0) + 'ms';
  second.appendChild(span);
});
var maxDelay = Math.pow(TEXT.length - 1, 0.82) * BASE + 260;

function reveal() {
  second.classList.add('is-shown');
  first.classList.add('is-hidden');
}
function hide() {
  second.classList.remove('is-shown');
  first.classList.remove('is-hidden');
}

08流れる待機 — コンテンツが準備できるまで明るさの値が一方向へ流れるボタン

コンテンツが準備できていない状態の間、表面の明るさの値は片方の端からもう片方の端まで繰り返し流れ、コンテンツが準備できた状態になるとその流れが止まり実際の内容に変わります。

流れる待機 — コンテンツが準備できるまで明るさの値が一方向へ流れるボタン
新しいタブで開く

08 · 流れる待機 — コンテンツが準備できるまで明るさの値が一方向へ流れるボタン — 計測 累積面積 6.108% · 動いたフレーム 20/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

BAND=34CYCLE=1100easeInOutSine

数字が示すもの

23フレームのうち08は20フレームで動き、累積面積6.11%・平均強度38.7という9つの中で最も低い強度を記録しています — easeInOutSine()は幅34%のハイライト帯を固定のCYCLE(1100ms)の中で行き来させており、コントラストの低い流れながらもボタン表面を覆う範囲では9つの中で最も広く、それでいて鋭い変化には見えません。

function easeInOutSine(t) { return -(Math.cos(Math.PI * t) - 1) / 2; }

function frame(now) {
  var t = ((now - startT) % CYCLE) / CYCLE;
  var e = easeInOutSine(t);
  var pos = -BAND + e * (100 + BAND * 2);
  sweep.style.transform = 'translateX(' + pos.toFixed(1) + '%)';
  if (loading) { raf = requestAnimationFrame(frame); } else { raf = null; }
}

09バー反応 — クリックのたびに複数のバーの高さの値がそれぞれ異なって跳ねてから沈むボタン

クリックが起きるたびに並んだ複数のバーそれぞれの高さの値が互いに異なる大きさで一瞬跳ね上がり、時間が経つにつれ各バーの高さの値が減って最初の高さに沈んだ状態になります。

バー反応 — クリックのたびに複数のバーの高さの値がそれぞれ異なって跳ねてから沈むボタン
新しいタブで開く

09 · バー反応 — クリックのたびに複数のバーの高さの値がそれぞれ異なって跳ねてから沈むボタン — 計測 累積面積 0.293% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

DECAY=0.14MIN_H=0.14hit

数字が示すもの

23フレームすべてで09は動いており、累積面積0.29%・平均強度170.7を記録しています — hit()はクリックのたびに各バーをMIN_H(0.14)より上のランダムな高さまで跳ね上げ、tick()のdiff * DECAY(0.14)による沈み込みは計測した範囲内では完全には収まらないため、9つの中でこのパターンだけが静止したフレームに至りません。

function tick() {
  var moving = false;
  for (var i = 0; i < N; i++) {
    var b = bars[i];
    var diff = MIN_H - b.h;
    if (b.h > MIN_H) {
      b.h += diff * DECAY;
      if (b.h - MIN_H > 0.004) { moving = true; } else { b.h = MIN_H; }
    }
  }
  render();
  if (moving) { raf = requestAnimationFrame(tick); } else { raf = null; }
}
function hit() {
  for (var i = 0; i < N; i++) {
    bars[i].h = Math.max(bars[i].h, MIN_H + Math.random() * 0.82);
  }
  render();
  if (!raf) { raf = requestAnimationFrame(tick); }
}

値がパターンを変える場所 — 長さ・進行・位置のしきい値

3つの定数はパターンが何をするかを変えないまま感触だけを変えます — 値を入れ替えても同じホバー・クリック・ドラッグは同じ最終状態へたどり着き、そこまでの経路だけが変わります。

06のTHRESHが決めるのはドラッグがどれだけ進む必要があるかで、ドラッグ中の感触ではない理由

onUp()は離されたposをTHRESH(0.7)と比較するだけで、その上なら確定し、その下ならsettle()が同じdiff * 0.3の係数で取っ手を0へ戻します。THRESHを0.4に下げると同じドラッグはトラックの半分にも届かないうちに確定し、0.7では端近くまで強く引く必要があったのが、軽く押すだけで済むようになります。

09のDECAYが決めるのはバーの跳ね返りがどれだけ長く残るかで、どれだけ高く跳ねるかではない理由

hit()はDECAYに関係なく毎回同じ上限までバーを跳ね上げ、DECAYはその後tick()がどれだけ速く沈めるかだけを左右します。0.14では叩かれたバーが数フレームにわたってゆっくり沈み残響のように残りますが、0.45に上げると同じ高さまで跳ねても2〜3フレームで沈み、より硬く即座な打感になります。

05のOVERSHOOT_DEGが決めるのは反転が180度をどれだけ超えて揺れるかで、反転にかかる時間ではない理由

ease()の揺れの項はOVERSHOOT_DEG / 180をサインに掛けたものであり、反転全体の時間はDECAYとは別のDUR(500ms)だけが決めています。18では面が180度を超えて198度近くまで回ってから戻り、4ではほとんど超えずに止まり、ばね感の薄い機械的な回転に近づきます。

どこで壊れるか — 高速クリック・短いホバー・途中で離す

高速でクリックするとどうなるか

03のburst()はクリックのたびにparticles配列をまるごと置き換えるため、破裂の途中でもう一度クリックすると、前回のまだ落下中の破片は積み重なるのではなく消えてしまいます。04のstartCountdown()はrunningが真の間は早期リターンで守られているため、カウントダウン中の2回目のクリックはリングが終わるまで何も起こしません。09のhit()はまさにこの場合のために作られており、タップのたびにバーの現在の高さと新しいランダムな目標のどちらか大きい方を取るので、沈み切る前の素早い連続タップはまだMIN_Hに届いていないバーをさらに引き上げるだけです。

トランジションより短いホバーではどうなるか

01のstep()はホバーが終わった時点で最後に設定されたtarget(ホバーが解けた場合は0)へlenを近づけ続けるため、途中で切られたホバーは即座にリセットされるのではなく方向を反転させるだけです。07で最も遅い文字(10文字目、インデックス9)は最初の文字よりおよそ420ms余り遅れてtransitionDelayが発火するため、それより短いホバーが終わってhide()が呼ばれる時点では2つ目の文言の一部の文字がまだ現れていないことがあります。

基準に届く前にドラッグを離すとどうなるか

06のonUp()は離した瞬間のposだけをTHRESHと比較しており、届いていなければsettle()がドラッグ自体には一切かかっていない滑らかさでposを0へ戻します — pointermoveのハンドラは指の生の位置をそのまま反映するだけで一切イージングをかけないので、進行中のドラッグは指の位置に正確に追従し、離されて未確定になったドラッグだけが自分で動いて戻ります。

この系列が【コピペOK】CSS ボタン アニメーション 9選(SCSS + React)、css ボタン ホバー アニメーション 9選 — 受賞級の定番テク、css ボタン 押したら変わる 9選 — 状態遷移の実装と違うのは、それぞれの系列がどの瞬間に応じて何かを計算し直すかという点です。1つ目の系列はホバー・押下・待機・失敗・確定という1つの瞬間にひもづいた単一のトランスフォームや不透明度の変化を再生するだけで、引いた距離やクリック回数をアニメーション自体に織り込むことはありません。ホバー系列は9つの合図を入口と出口で何を伝えるかによって整理していますが、そのどれも範囲のある進行比率や訪問者が途中で越えられる基準値は持っていません。状態フィードバック系列はボタンが固定・回転・解決していく状態を単一のARIA属性に書き込んでおり、これは04のカウントダウンが内部で行っていることに近いのですが、06や09のようにドラッグやクリックを繰り返した値からアニメーションを導くことはありません。この系列の9つのソースファイルはすべて、パスワードt6t4u9ypで解除した先にあり、上の仮の文言を実際の商品コピーに置き換えるだけで本物のCTAへそのまま組み込めます。

3つのバリエーションとアクセシビリティ

この9つのcta ボタン デザインのうちバリエーションとして価値のある定数を持つのは3つだけです — 残る6つに調整可能な数値がないからではなく、THRESH・DECAY・OVERSHOOT_DEGの3つが、値をひとつ変えるだけで再生速度ではなく操作の感触そのものが最も目に見えて変わる箇所だからです。ボタン クリック エフェクトを比べるうえでも、この3つが最初に触るべき値になります。

バリエーション 何が変わるか 何と引き換えになるか
06のTHRESHを0.7から0.4へ 確定までに越える必要のあるトラックの割合が減る 0.7ではトラックの大半を越えないと確定を確信できないのに対し、0.4では3分の1を過ぎた時点で確定し、その確信を軽い操作感と引き換えにする
09のDECAYを0.14から0.45へ 叩かれたバーが元の高さへ戻る速さが上がる 0.14では跳ね返りが数フレームにわたり柔らかい打感として残るが、0.45では同じ跳ね上がりがほぼ即座に沈み、より硬く打楽器的なタップとして感じられる
05のOVERSHOOT_DEGを18から4へ 反転した面が180度を超えて揺れる幅が小さくなる 18ではばねのようなやや緩い感触の反転になるが、4では同じ反転がほとんど跳ねない硬く機械的な回転に近づく

9つとも自分の自動再生プレビューを始める前にwindow.matchMedia('(prefers-reduced-motion: reduce)').matchesを確認しています — 01・02・03・05・06・07はそのループの最初のインターバルが回るまで一切再生せずに待つのに対し、04・08・09はチェックを通過した直後にstartCountdown()・startLoading()・hit()をそれぞれ一度即座に呼ぶため、リング・流れ・バーはインターバルを待たずページ読み込みの瞬間から動き出します。wireReal()自体にはこの系列のどこにも縮小モーションの確認が入っていないため、設定に関わらずホバー・クリック・ドラッグは手で操作すれば必ずフルのアニメーションを再生します。02の2つ目のグリッチ層と07の2つ目の表示文言はどちらもaria-hiddenが付いており、それぞれが可視レイヤーを通じてスクリーンリーダーがすでに読み上げているテキストを重複させているだけだからです。06の取っ手だけがこの9つの中で自前のaria-labelを持ち、それ以外はボタン自体の可視テキストをアクセシブルネームとして頼っています。

FAQ

このマイクロインタラクション ボタンは何かアニメーションライブラリを使っていますか

いいえ — 9つともrequestAnimationFrame・setInterval・CSSのtransitionだけで動いており外部依存は一切ないため、HTML・SCSS・JSをそのまま貼り付けるだけで同じ動きを手元で確認できます。

9つのうちどれかが実際の決済・分析・通知APIを呼び出していますか

いいえ — ここにあるすべての値はローカルで計算されそのまま画面に反映されるだけです。09のメーターはタップに対する擬似的な反応であって実際のWeb Audioの読み取りではなく、この中にfetchや外部サービスへ到達する処理は一つもありません。

これは読むだけの参考例ですか、それともボタンへ貼り付けて使える実装ですか

貼り付けて使うことを前提に作られています — 上に載せたコードはどれも25行以内の核心部分だけで、仮の文言や配色こそが実際の商品自身のコピーやパレットへ置き換えるべき部分です。

Sources: MDN — CSS transition · MDN — Window.requestAnimationFrame · MDN — transform-function: rotateY() · MDN — prefers-reduced-motion

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

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