CSSで読み込み中のスピナーを作るには
1 要素の border と @keyframes rotate だけで回転するスピナーを作る基本形を示す。
画像も追加の DOM も要らないため、読み込み中の表示を最小のコストで用意できる。
実行確認済みは、ブラウザで実際に描画して見た目を確認したものである。
公開:
<div class="loader" role="status" aria-live="polite">
<span class="loader__spinner" aria-hidden="true"></span>
<span class="loader__label">読み込み中…</span>
</div>.loader {
display: flex;
align-items: center;
gap: 12px;
font-size: 14px;
color: #475569;
}
.loader__spinner {
width: 32px;
height: 32px;
box-sizing: border-box;
border: 4px solid #e2e8f0; /* 円周の色 */
border-top-color: #0f766e; /* 1 辺だけ変えて回転を見せる */
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to {
transform: rotate(1turn);
}
}
@media (prefers-reduced-motion: reduce) {
.loader__spinner {
animation-duration: 2.4s;
}
}4 辺の border のうち 1 辺だけ色を変え、border-radius: 50% で円にしてから回すとスピナーになる。
全周が同じ色だと回転しても見た目が変わらないため、色差が「回っている」という手掛かりそのものになる。
つまずき
回らない・円にならないときは、まず幅と高さが同じか、border-radius: 50% が当たっているかを確認する。
span のようなインライン要素は width / height を無視するため、display: inline-block や flex アイテム化を忘れると潰れて見える。
また box-sizing: border-box を付けないと border の分だけ外形が太り、隣の要素とのすき間が設計値からずれる。
回転の中心がぶれる場合は transform-origin ではなく、要素自体が正方形になっていないことを疑うとよい。
動きを控えたい利用者への配慮
無限に回り続けるアニメーションは、前庭障害のある利用者にとって不快や体調不良の原因になりうる。
prefers-reduced-motion: reduce のメディアクエリを用意し、回転を止めるか十分に遅くするのが望ましい。
上の例では animation を消さずに周期を 2.4 秒へ伸ばしている。
完全に止めてしまうと「処理が進んでいる」という情報まで失われるため、遅くする形で残す判断もあるという点は覚えておきたい。
スクリーンリーダーへの伝え方
回転そのものは視覚的な表現でしかないので、スクリーンリーダーには文字で状態を伝える必要がある。
上の例では外側に role="status" と aria-live="polite" を置き、装飾でしかないスピナー本体には aria-hidden="true" を付けている。
こうすると「読み込み中…」というラベルだけが読み上げられ、意味のない要素は読み飛ばされる。
読み込みが終わったらラベルを結果の文言へ差し替えると、完了も同じ領域から伝わる。