Spinner

Indeterminate loading indicator.

Loading

html

<div class="spinner-wrap" role="status" aria-live="polite">
  <span class="spinner spinner--normal" style="width:28px;height:28px;border-top-color:#8A6FBF;" aria-hidden="true"></span>
  <span class="spinner-wrap__srtext">Loading</span>
</div>
<style>
.spinner-wrap { display: inline-block; }
.spinner-wrap__srtext { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.spinner { display: inline-block; border-radius: 50%; border: 3px solid #24143022; border-top-color: currentColor; }
@keyframes spinner-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: no-preference) {
  .spinner--slow { animation: spinner-spin 1.6s linear infinite; }
  .spinner--normal { animation: spinner-spin .9s linear infinite; }
  .spinner--fast { animation: spinner-spin .5s linear infinite; }
}
</style>

tailwind

<div role="status" aria-live="polite" class="inline-block">
  <span aria-hidden="true" class="spinner-tw spinner-tw--normal inline-block rounded-full border-[3px] border-[#24143022]" style="width:28px;height:28px;border-top-color:#8A6FBF;"></span>
  <span class="sr-only">Loading</span>
</div>
<style>
@keyframes spinner-tw-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: no-preference) {
  .spinner-tw--slow { animation: spinner-tw-spin 1.6s linear infinite; }
  .spinner-tw--normal { animation: spinner-tw-spin .9s linear infinite; }
  .spinner-tw--fast { animation: spinner-tw-spin .5s linear infinite; }
}
</style>

react

export function Spinner({
  label = "Loading",
  size = 28,
  color = "#8A6FBF",
  speed = "normal",
}) {
  const duration = { slow: "1.6s", normal: "0.9s", fast: "0.5s" }[speed] ?? "0.9s";
  return (
    <div role="status" aria-live="polite" style={{ display: "inline-block" }}>
      <span aria-hidden="true" className="spinner-ring" style={{ display: "inline-block", width: size, height: size, borderRadius: "50%",
        border: "3px solid #24143022", borderTopColor: color }} />
      <span style={{ position: "absolute", width: 1, height: 1, overflow: "hidden", clip: "rect(0 0 0 0)" }}>{label}</span>
      <style>{`
        @media (prefers-reduced-motion: no-preference) {
          .spinner-ring { animation: spinner-react-spin ${duration} linear infinite; }
          @keyframes spinner-react-spin { to { transform: rotate(360deg); } }
        }
      `}</style>
    </div>
  );
}