Loading
처리 중임을 알리는 표시예요. 짧은 로딩과 좁은 자리에는 원형(Progress Circle)을, 진행률을 계산할 수 있는 작업에는 막대(Progress Bar)를, 화면 첫 진입과 목록에는 Skeleton 을 사용해요.
Anatomy
- 01Progress Circle1~4초의 짧은 로딩. 버튼 안이나 전체 화면 딤 위
- 02Progress Bar4초 이상, 진행률을 아는 작업
- 03Skeleton첫 진입과 목록 로딩. 실제 콘텐츠 자리에 골격만
Properties
Type
걸리는 시간으로 골라요. 세 가지 모두 같은 기준을 써요.
| 소요 시간 | 표시 |
|---|---|
| 1초 이내 | 표시하지 않아요. |
| 1~4초 | Progress Circle 또는 Skeleton |
| 4~10초 | Skeleton 또는 Progress Bar |
| 10초 이상 | Progress Bar + 예상 소요 시간 |
| 1분 이상 | 백그라운드 처리 또는 취소 수단 |
Size
Progress Circle 은 박스 크기를 정하면 링과 선 두께가 따라와요. 18·24·32는 아이콘 박스, 48은 전체 화면 딤용이에요.
Progress Bar 는 높이 4px 에 부모 폭을 따라요. Skeleton 은 실제 콘텐츠 크기에 맞추고, 아래는 모양별 기본값이에요.
Bar Type
| 타입 | 설명 |
|---|---|
| Indeterminate | 진행률을 모를 때. 너비 30% 구간이 반복해 지나가요. |
| Determinate | 진행률을 알 때. value(0~100) 만큼 좌에서 우로 채워요. |
Color
Progress Circle 은 트랙 없이 호 하나만 돌고, 색은 화면에 맞춰 바꿔요. Skeleton 은 --color-fill-normal 하나만 써요.
| 맥락 | 토큰 | 설명 |
|---|---|---|
| 기본 | --color-label-alternative | 밝은 배경 위 |
| 어두운 배경 · Black 버튼 | --color-static-white | 딤 위도 같아요 |
| 버튼 안 | currentColor | 버튼 라벨 색을 그대로 따라가요 |
| 그 밖 | 자유 | 브랜드·AI 색 등 화면에 맞게 |
Case
Guidelines
✓ DO
- 1~4초의 짧은 로딩에 써요.
- 버튼 안, 전체 화면 딤 위에 써요.
- 어두운 배경에선 static/white 로 바꿔요.
- 4초 이상, 진행률을 아는 작업에 써요.
- 10초를 넘으면 예상 시간도 보여줘요.
- 화면 전체 진행은 헤더 아래 전체 폭으로 둬요.
- 항목별 진행은 그 항목 아래에 둬요.
- 첫 진입과 목록 로딩에 써요.
- 실제 콘텐츠와 같은 자리·크기로 둬요.
- Circle 은 아바타, Text 는 한 줄에 써요.
- 여러 줄은 쌓고 마지막 줄은 짧게 해요.
✗ DON'T
- 1초 안에 끝나는 작업엔 안 띄워요.
- 진행률이 필요한 작업엔 Progress Bar 를 써요.
- 한 영역에 하나만 둬요.
- 실패하면 로딩을 걷고 오류와 다음 행동을 안내해요.
- 진행률을 모르면 Determinate 로 흉내내지 않아요.
- 1초 안에 끝나는 작업엔 안 띄워요.
- 한 영역에 하나만 둬요.
- 실패하면 막대를 걷고 오류와 다음 행동을 안내해요.
- 정적인 요소엔 쓰지 않아요.
- shimmer 그라데이션을 얹지 않아요.
- 실제 콘텐츠와 다른 크기로 두지 않아요. 끝날 때 화면이 튀어요.
- 실패하면 골격을 걷고 오류와 다음 행동을 안내해요.
Specification
Progress Circle
| 박스 | 링 | 선 두께 | 쓰는 자리 |
|---|---|---|---|
| 18px | 12px | 1.5px | 아이콘 박스. 버튼 안 같은 좁은 자리 |
| 24px | 16px | 2px | 아이콘 박스 |
| 32px | 22px | 3px | 아이콘 박스 |
| 48px | 32px | 4px | 전체 화면 딤 위 |
2초에 한 바퀴 돌며 호 길이가 함께 변해요. 멈춰 보이는 구간이 없어요.
| 항목 | 값 | 근거 |
|---|---|---|
| 주기 | 2,000ms | |
| 앞끝(trim end) | 0 → 1,667ms 동안 1% → 100% | 뒤끝과 구간이 겹쳐 호가 길어졌다 짧아져요 |
| 뒤끝(trim start) | 333 → 2,000ms 동안 0% → 99% | |
| 회전 | 주기당 356.4도, linear | 트림이 되돌아가는 각도와 같아 이음매가 보이지 않아요 |
| 이징 | cubic-bezier(0.333, 0, 0.667, 1) | 표준 ease-in-out 과 같은 곡선 |
| 구현 | stroke-dasharray · stroke-dashoffset |
Progress Bar
| 속성 | 값 | 설명 |
|---|---|---|
| 높이 | 4px | 기본값. 필요하면 height 로 조정해요 |
| 너비 | 100% | 부모 폭을 따라요 |
| border-radius | --radius-full | 트랙과 채움 모두 |
| 트랙 | --color-fill-normal | |
| 채움 | --color-label-alternative | color 로 브랜드·AI 색 지정 |
| 항목 | 값 | 근거 |
|---|---|---|
| Indeterminate 주기 | 1,500ms | |
| Indeterminate 구간 | 전체 너비의 30% 가 좌에서 우로 이동 | |
| Indeterminate 이징 | linear | 진행률을 모르므로 일정한 속도로 움직여요 |
| Determinate 전환 | 값이 바뀔 때 200ms ease-out |
Skeleton
| 모양 | 기본 크기 | radius | 쓰는 자리 |
|---|---|---|---|
| Rect | 240 × 120 | --radius-md (12px) | 카드·썸네일·이미지 자리 |
| Circle | 48 × 48 | --radius-full | 아바타 |
| Text | 240 × 16 | --radius-xs (6px) | 문장 한 줄 |
투명도가 100%와 30% 사이를 오가요. shimmer 는 쓰지 않아요.
| 항목 | 값 | 근거 |
|---|---|---|
| 주기 | 2,000ms | |
| 투명도 | 100% → 30% → 100% | |
| 이징 | ease-in-out | 양 끝에서 부드럽게 멈췄다 돌아와요 |
npm i @polarisoffice/pds-reactCircle · Base
박스 크기만 정하면 링과 선 두께가 따라와요. 기본은 18 이에요.
import { ProgressCircle } from '@polarisoffice/pds-react';
<ProgressCircle size={18} />
<ProgressCircle size={24} />
<ProgressCircle size={32} />
<ProgressCircle size={48} />Circle · Color
기본은 label/alternative 예요. 어두운 배경 위에서는 static/white 로 바꿔요.
import { ProgressCircle } from '@polarisoffice/pds-react';
<ProgressCircle size={32} />
<ProgressCircle size={32} color="var(--color-static-white)" />
<ProgressCircle size={32} color="var(--color-accent-normal)" />버튼 안
Button 의 loading 을 켜면 아이콘 자리에 들어가요. 클릭은 막히고 버튼 색은 그대로예요.
import { Button } from '@polarisoffice/pds-react';
<Button variant="primary" size={40} loading>저장중</Button>
// 라벨 없이 스피너만 — 폭이 흔들리지 않게 폭을 고정해요
<Button variant="primary" size={40} loading aria-label="저장중" style={{ width: 79 }} />화면 낭독기
role="status"·aria-label="loading" 이 기본이에요. 문구를 바꾸거나 옆에 안내가 있으면 숨겨요.
import { ProgressCircle } from '@polarisoffice/pds-react';
<ProgressCircle size={24} aria-label="문서를 불러오는 중" />
// 옆에 문구가 따로 있을 때 — 두 번 읽히지 않게 숨겨요
<ProgressCircle size={24} aria-hidden />Bar · Base
기본은 Indeterminate 예요. 너비는 부모 영역을 채우니 폭을 가진 자리에 넣어요.
import { ProgressBar } from '@polarisoffice/pds-react';
<ProgressBar />
<ProgressBar type="determinate" value={60} />Bar · Color
채움과 트랙 색을 바꿀 수 있어요. 어두운 배경 위에서는 채움을 static/white 로 둬요.
import { ProgressBar } from '@polarisoffice/pds-react';
<ProgressBar type="determinate" value={60} color="var(--color-accent-normal)" />
<ProgressBar type="determinate" value={60} color="var(--color-ai-normal)" />Bar · 진행률 문구와 함께
10초를 넘는 작업이면 남은 시간이나 퍼센트를 함께 보여줘요.
import { ProgressBar } from '@polarisoffice/pds-react';
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span>3분기 매출 보고서.xlsx</span>
<span>60%</span>
</div>
<ProgressBar type="determinate" value={60} aria-label="업로드 진행률" />Skeleton · Base
Rect · Circle · Text 세 모양. 크기를 주지 않으면 모양별 기본값으로 그려요.
import { Skeleton } from '@polarisoffice/pds-react';
<Skeleton shape="rect" />
<Skeleton shape="circle" />
<Skeleton shape="text" />Skeleton · Size
실제 콘텐츠 크기에 맞춰요. width 는 %도 받아요. Circle 은 width 만 주면 원을 유지해요.
import { Skeleton } from '@polarisoffice/pds-react';
<Skeleton shape="circle" width={40} />
<Skeleton shape="text" width={320} />
<Skeleton shape="text" width="60%" height={12} />Skeleton · 목록
아바타 + 두 줄이 기본 뼈대예요. 마지막 줄을 짧게 만들면 문단처럼 보여요.
import { Skeleton } from '@polarisoffice/pds-react';
{items.map((_, i) => (
<div key={i} style={{ display: 'flex', gap: 16, alignItems: 'center' }}>
<Skeleton shape="circle" width={40} />
<div style={{ display: 'flex', flexDirection: 'column', gap: 8, flex: 1 }}>
<Skeleton shape="text" width="100%" />
<Skeleton shape="text" width="60%" height={12} />
</div>
</div>
))}