Files
HMDesign/docs/patterns/loading-screen.md
T

5.8 KiB
Raw Blame History

Loading Screen (Pattern)

현재 HM 구현의 기준 문서. 공통 원칙은 docs/guidelines/프로젝트-지침.md(§6.10, §6.11)를 따른다.

Layout visual reference: 외부 ui_viz Reference (ZIP 미포함) (dashboard-composition.md §18, platform-mapping/dashboard-loading-web.md)
Implementation: 의존하는 primitive 없음. 24개 막대 스피너 + 텍스트, CSS animation만 사용

1. 상태

항목 내용
구현 상태 구현 완료 (2026-10-06)
대표 Story Patterns/Layout/Loading Screen → All (Inline, Custom label, Narrow parent, Fullscreen)
경위 요청은 "Loading Bar"였으나 ui_viz에 Loading Bar는 없었다. 실제 있는 Dashboard LoadingScreen(스피너)을 사용자가 지정해 구현했다

2. 역할 구분

컴포넌트 상황
Progress 진행률을 안다 (0~100%)
Skeleton 콘텐츠 모양을 안다
Sonner loading 작업 중임을 알리는 toast
Loading Screen 진행률도 모양도 모르는 채 영역/화면 전체가 준비 중

Progress는 Indeterminate 표현을 여전히 갖지 않는다(progress.md §11 유지). 버튼 안 inline spinner는 만들지 않았다(ui_viz §18.1과 동일).

3. 구성 / API

export 역할
LoadingScreen 초록 면 + 스피너 + 텍스트. 기본 inline, fullscreen이면 viewport 전체
<LoadingScreen />                          // inline, 부모 폭
<LoadingScreen>데이터를 불러오는 중</LoadingScreen>   // 라벨 교체("..."는 자동 추가)
<LoadingScreen fullscreen />               // position: fixed, inset 0
  • 추가 prop은 fullscreen뿐이다. speed, size 같은 prop은 만들지 않았다. 값은 --hm-loading-* Token으로 바꾼다.
  • 닫기 버튼, 취소, async lifecycle, 표시/숨김 fade(0.3s)는 두지 않았다. 소비자가 로딩 동안 mount하고 끝나면 제거한다.
  • Fullscreen은 Portal을 쓰지 않는다. transform이 있는 조상 안에 두면 fixed가 어긋난다.

4. 규격 (packages/ui/src/theme/loading-screen.css)

항목 ui_viz HM 판정
스피너 44×44, 24 bars, 막대 2.5×10, 15° 간격, 첫 막대 12시 동일 (실측 44×44, 막대 2.5×10, origin 1.25px 22px) MATCHED
모션 막대 opacity 1→0.08, 1.2s linear infinite, 지연 i×0.05s, 회전 없음 동일 MATCHED
텍스트 14/500/-0.02em, white 60%, 끝에 정적 "..." 동일, 폰트는 --hm-font-family-base MATCHED
Inline padding 36/24, radius 6, inset shadow 24px .45, gap 12, 가운데 정렬 동일 (실측 높이 149, ui_viz smoke 148은 폰트 line box 차이) MATCHED
Fullscreen fixed inset 0, assembly는 block·gap 0, 스피너는 묶음 leading edge(텍스트가 44px보다 넓으면 두 중심축이 어긋남), z-index 9999 fixed inset 0, inline과 같은 column·gap 12로 묶음 전체를 viewport 중앙(스피너·텍스트 중심축 일치) INTENTIONAL_CHANGE (2026-10-06 사용자 지시: 스피너가 왼쪽으로 치우쳐 보임)
면 색 #0a3322 (Dashboard-local) --hm-sem-action-primary (Default #123328) INTENTIONAL_CHANGE (HM Primitive 재사용, 새 색 만들지 않음)
폰트 Inter → Noto Sans KR HM 기본 폰트 INTENTIONAL_CHANGE
막대 끝 radius 1.5 → 렌더에서 pill 9999px MATCHED
숨김/표시 fade 0.3s 있음 없음 FOLLOW_UP (소비자 lifecycle)
닫기 버튼 + ESC demo 전용(REFERENCE_ONLY) 없음 (Story에 로컬 닫기 Button만) OLD_TEST_ONLY
focus trap, background 차단, task lifecycle TBD 만들지 않음 FOLLOW_UP

5. Token

--hm-loading-screen-{background, foreground, inline-padding-block, inline-padding-inline, inline-radius, inline-shadow, gap, z-index}, --hm-loading-spinner-{size, bar-width, bar-height, bar-count, bar-color, bar-min-opacity, duration, stagger}, --hm-loading-text-{font-size, font-weight, letter-spacing, color}. 면/전경은 --hm-sem-action-primary / --hm-sem-on-action-primary, 그림자는 --hm-sem-palette-black을 참조한다. 신규 Primitive 없음.

6. Accessibility / Reduced Motion (INTENTIONAL_CHANGE)

ui_viz는 스피너 semantics와 reduced motion을 TBD로 남겼다. HM은 최소한만 정했다.

  • 루트 role="status"(polite live region)가 텍스트를 알린다. 막대 컨테이너는 aria-hidden.
  • prefers-reduced-motion: reduce: 애니메이션을 끄고 막대 opacity를 시계 방향으로 줄여 정적 형태로 보여준다.
  • 진행률이 없으므로 aria-valuenow 등은 쓰지 않는다. focus 이동, 배경 입력 차단, aria-busy는 소비자 책임.

7. 검증 (2026-10-06)

  • npm run typecheck, npm run build, npm run build-storybook 통과. 새 warning 없음(기존 chunk size 안내는 이전부터).
  • 실측: inline 면 rgb(18,51,40), padding 36/24, radius 6, shadow inset 24px .45, 스피너 44×44, 막대 24개, animation hm-loading-fade 1.2s, 지연 0.05s, spinner→text gap 12.
  • Fullscreen(2026-10-06 수정 후): spinner centerX = label centerX = viewport centerX, 묶음 centerY = viewport centerY, gap 12. 1920/768/390 모두 오차 0, 가로 overflow 없음. reduced-motion에서도 위치 동일(animation만 none). inline은 높이 149, gap 12로 변화 없음.
  • 1920 / 768 / 390, 일반 / prefers-reduced-motion: reduce 모두 Playwright(headless Chromium)로 실측했다.
  • 기존 Progress / Skeleton / Sonner / Data Table / Dashboard Layout 코드는 수정하지 않았다.

8. 관련 파일

packages/ui/src/patterns/loading-screen/{loading-screen.tsx,index.ts}
packages/ui/src/theme/loading-screen.css
packages/ui/src/theme.css          (import)
packages/ui/src/index.ts           (export)
src/patterns/loading-screen.stories.tsx
.storybook/preview.tsx             (Patterns/Layout 정렬)