컴포넌트

Spinner

진행률을 알 수 없는 작업의 처리 상태를 표시합니다.

설치

pnpm dlx ply-ui add spinner

사용법

size는 sm(16px), md(20px), lg(28px)입니다. label로 스크린 리더가 읽을 작업 이름을 지정합니다. 화면에는 원형 표시만 나타납니다.

주변에 같은 의미의 텍스트가 있다면 aria-hidden="true"로 중복 안내를 막습니다. 모션 줄이기를 설정한 환경에서는 회전하지 않습니다.

버튼 안의 로딩은 Button loading을 사용합니다. 퍼센트를 알 수 있는 작업은 Progress를 사용합니다.

속성

속성

타입

구현 코드

components/ui/spinner.tsx
spinner.tsx
import type { ComponentProps } from "react";
import { cx } from "../../lib/cx";
import "./spinner.css";

export type SpinnerProps = Omit<ComponentProps<"span">, "children"> & {
  /** 표시 크기입니다. @defaultValue "md" */
  size?: "sm" | "md" | "lg";
  /** 스크린 리더가 읽을 진행 상태입니다. @defaultValue "처리 중" */
  label?: string;
};

/** 진행률을 모르는 짧은 작업에 사용합니다. 진행률이 있으면 Progress를 사용합니다. */
export function Spinner({
  size = "md",
  label = "처리 중",
  className,
  ...props
}: SpinnerProps) {
  return (
    <span
      role="status"
      aria-label={label}
      {...props}
      data-size={size}
      className={cx("rbx-spinner", className)}
    />
  );
}
components/ui/spinner.css
spinner.css
@layer theme, base, components, utilities;
@layer components.ply-base, components.ply;

@layer components.ply {
  .rbx-spinner {
    display: inline-block;
    flex-shrink: 0;
    width: 20px;
    height: 20px;
    border: 2px solid currentColor;
    border-inline-end-color: transparent;
    border-radius: 50%;
    vertical-align: middle;
    animation: rbx-spinner-rotate 800ms linear infinite;
  }
  .rbx-spinner[data-size="sm"] {
    width: 16px;
    height: 16px;
  }
  .rbx-spinner[data-size="lg"] {
    width: 28px;
    height: 28px;
    border-width: 3px;
  }
  @keyframes rbx-spinner-rotate {
    to {
      transform: rotate(360deg);
    }
  }
  @media (prefers-reduced-motion: reduce) {
    .rbx-spinner {
      animation: none;
    }
  }
}

목차