ローディングスピナーは animation-delay で 200ms 遅らせて表示する
スピナーを opacity: 0 で置き、animation-delay: 200ms のフェードインで出すと、すぐ終わる処理ではスピナーが一瞬チラつかなくなる。
どんな時に使う?
保存ボタンや送信ボタンで、処理中にスピナーを出したいとき。100ms で終わる処理でもスピナーを即表示すると、一瞬だけ点滅して「何か起きた?」という落ち着かない見た目になる。逆に、一度出したスピナーがすぐ消えるのも同じくチラつきに見える。そこで「200ms 経ってから出す」「出したら最低 400ms は見せる」の2段構えにする。
コード
css
.spinner {
opacity: 0;
/* 1つ目: 200ms 待ってから表示(長さ0で一気に opacity: 1 にする)
2つ目: 回転は最初から動かしておく */
animation:
show 0s 200ms forwards,
spin 0.8s linear infinite;
}
@keyframes show {
to { opacity: 1; }
}
@keyframes spin {
to { rotate: 1turn; }
}「最低 400ms は見せる」部分は CSS だけではできないので、数行の JS で処理の終了を待たせる。
js
const start = performance.now();
button.append(spinner);
await save();
// 表示され始めてから経った時間(200ms の待ちを引く)
const shown = performance.now() - start - 200;
if (shown > 0) {
await new Promise((r) => setTimeout(r, 400 - shown));
}
spinner.remove();- 200ms 以内に終わった処理では
shownが 0 以下になり、スピナーは一度も描画されないまま消える。 - 表示された場合は、表示開始から 400ms に満たなければ残りの時間だけ待ってから消す。
- フェードを滑らかにしたいなら
show 150ms 200ms ease-out forwardsのように長さを付けてもよい。
注意点・対応状況
animation-delayとforwardsはすべての主要ブラウザで使える。opacity: 0の間もスピナーは要素として存在するので、スクリーンリーダー向けにはaria-busy="true"やライブリージョンでの「保存中」通知を別に用意する。- 200ms・400ms は目安。処理時間の分布を見て調整する。
prefers-reduced-motion: reduceの利用者には回転を止めて、表示だけにするなどの配慮をする。
出典
- @sanyampunia(2026-11-09)