크기
sm (16px) · md (24px, 기본) · lg (32px) — 숫자 값 deprecated
접근성
role=status + aria-label 필수 (기본: '로딩 중')
배치
인라인(size=sm) · 블록(size=md) · 오버레이
Button 통합
Button loading prop 사용 시 Loader2 자동 표시 — 별도 Spinner 불필요
애니메이션
spin (prefers-reduced-motion → pulse로 대체)

기본

라이브 (크기 3종 · role=status · aria-label)
코드 보기tsx
import { Spinner } from "@olundot/ui";

// Spinner는 <span role="status" aria-label="로딩 중" />을 기본으로 렌더링.
// label prop으로 접근성 텍스트를 맥락에 맞게 변경.
// size: "sm" (16px) · "md" (24px, 기본) · "lg" (32px)
export function SpinnerBasic() {
  return (
    <div style={{ display: "flex", gap: "16px", alignItems: "center" }}>
      <Spinner size="sm" label="소형 로딩 중" />
      <Spinner size="md" label="기본 로딩 중" />
      <Spinner size="lg" label="대형 로딩 중" />
    </div>
  );
}

인라인

정적 (버튼·텍스트 옆 인라인 배치)
제출 처리 중...
자동 저장 중
코드 보기tsx
import { Spinner } from "@olundot/ui";

export function SpinnerInline() {
  return (
    <div style={{ display: "grid", gap: "12px" }}>
      <div style={{ display: "flex", gap: "8px", alignItems: "center" }}>
        <Spinner size="sm" label="제출 중" />
        <span>제출 처리 중...</span>
      </div>
      <div style={{ display: "flex", gap: "8px", alignItems: "center" }}>
        <Spinner size="sm" label="저장 중" />
        <span style={{ fontSize: "0.875rem", color: "var(--text-secondary)" }}>
          자동 저장 중
        </span>
      </div>
    </div>
  );
}

Button loading 통합

라이브 (Button loading prop — Loader2 자동 표시)
코드 보기tsx
import { useState } from "react";
import { Button } from "@olundot/ui";

// Button의 loading prop을 사용하면 Loader2 아이콘이 자동으로 표시됨.
// 별도 Spinner를 Button 옆에 두는 패턴 대신 이 방식을 권장.
export function SpinnerToggle() {
  const [loading, setLoading] = useState(false);
  return (
    <div style={{ display: "flex", gap: "12px", alignItems: "center" }}>
      <Button
        loading={loading}
        onClick={() => {
          setLoading(true);
          setTimeout(() => setLoading(false), 2000);
        }}
      >
        {loading ? "처리 중..." : "작업 시작"}
      </Button>
    </div>
  );
}