@hjmds/design-contracts 1.12.1 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/avatar-fallback.d.ts +11 -0
- package/dist/avatar-fallback.d.ts.map +1 -1
- package/dist/avatar-fallback.js +21 -0
- package/dist/avatar-fallback.js.map +1 -1
- package/dist/base-recipes.d.ts +17 -0
- package/dist/base-recipes.d.ts.map +1 -1
- package/dist/base-recipes.js +17 -0
- package/dist/base-recipes.js.map +1 -1
- package/dist/catalog.d.ts +25 -0
- package/dist/catalog.d.ts.map +1 -1
- package/dist/command-palette.d.ts +14 -9
- package/dist/command-palette.d.ts.map +1 -1
- package/dist/command-palette.js +8 -9
- package/dist/command-palette.js.map +1 -1
- package/dist/component-recipes.d.ts +17 -0
- package/dist/component-recipes.d.ts.map +1 -1
- package/dist/component-recipes.js +5 -0
- package/dist/component-recipes.js.map +1 -1
- package/dist/provider-button.d.ts.map +1 -1
- package/dist/provider-button.js +3 -0
- package/dist/provider-button.js.map +1 -1
- package/dist/reactions.d.ts +10 -0
- package/dist/reactions.d.ts.map +1 -1
- package/dist/reactions.js +7 -0
- package/dist/reactions.js.map +1 -1
- package/dist/screen-patterns.d.ts +147 -0
- package/dist/screen-patterns.d.ts.map +1 -0
- package/dist/screen-patterns.js +149 -0
- package/dist/screen-patterns.js.map +1 -0
- package/dist/slider.d.ts +8 -0
- package/dist/slider.d.ts.map +1 -1
- package/dist/slider.js +6 -1
- package/dist/slider.js.map +1 -1
- package/dist/upload-item.d.ts +5 -0
- package/dist/upload-item.d.ts.map +1 -1
- package/dist/upload-item.js +5 -0
- package/dist/upload-item.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/docs/action-session.md +3 -3
- package/docs/agreement.md +5 -0
- package/docs/avatar-fallback.md +7 -0
- package/docs/bottom-navigation.md +6 -0
- package/docs/brand-boundary.md +1 -1
- package/docs/button-label.md +6 -0
- package/docs/clipboard.md +3 -0
- package/docs/command-palette.md +45 -2
- package/docs/consumer-policy.md +5 -1
- package/docs/data-table.md +6 -4
- package/docs/dialog.md +8 -2
- package/docs/form.md +51 -0
- package/docs/generated/component-maturity.md +1 -1
- package/docs/generated/renderer-evidence.json +3 -3
- package/docs/generated/renderer-evidence.md +1 -1
- package/docs/generated/showcase-manifest.json +1 -1
- package/docs/link.md +8 -0
- package/docs/migration-native-legacy-removal.md +45 -1
- package/docs/optional-adapters.md +1 -1
- package/docs/password-field.md +5 -0
- package/docs/product-composition-adoption.md +40 -0
- package/docs/progress.md +19 -1
- package/docs/provider-button.md +13 -0
- package/docs/result.md +3 -0
- package/docs/screen-chrome.md +10 -0
- package/docs/screen-patterns.md +376 -0
- package/docs/sheet.md +12 -0
- package/docs/splitter.md +8 -2
- package/docs/theming.md +36 -29
- package/docs/toggle-group.md +7 -0
- package/docs/tour.md +7 -1
- package/docs/tree.md +5 -2
- package/docs/upload-item.md +7 -0
- package/docs/usage/README.md +236 -0
- package/docs/usage/STANDARD.md +108 -0
- package/docs/usage/components/accordion.md +107 -0
- package/docs/usage/components/activity-heatmap.md +104 -0
- package/docs/usage/components/affix.md +86 -0
- package/docs/usage/components/agreement.md +129 -0
- package/docs/usage/components/alert-dialog.md +130 -0
- package/docs/usage/components/anchor.md +96 -0
- package/docs/usage/components/aspect-ratio.md +89 -0
- package/docs/usage/components/asset.md +126 -0
- package/docs/usage/components/auth-provider-button.md +116 -0
- package/docs/usage/components/auth-screen-layout.md +129 -0
- package/docs/usage/components/avatar.md +114 -0
- package/docs/usage/components/badge.md +84 -0
- package/docs/usage/components/bottom-cta.md +125 -0
- package/docs/usage/components/bottom-info.md +99 -0
- package/docs/usage/components/bottom-navigation.md +136 -0
- package/docs/usage/components/breadcrumb.md +81 -0
- package/docs/usage/components/button.md +118 -0
- package/docs/usage/components/calendar.md +122 -0
- package/docs/usage/components/card.md +110 -0
- package/docs/usage/components/carousel.md +113 -0
- package/docs/usage/components/celebration.md +96 -0
- package/docs/usage/components/chat-message.md +122 -0
- package/docs/usage/components/chat-screen.md +112 -0
- package/docs/usage/components/checkbox-group.md +104 -0
- package/docs/usage/components/checkbox.md +103 -0
- package/docs/usage/components/chip.md +104 -0
- package/docs/usage/components/code-block.md +111 -0
- package/docs/usage/components/collapsible.md +112 -0
- package/docs/usage/components/color-picker.md +86 -0
- package/docs/usage/components/combobox.md +137 -0
- package/docs/usage/components/command-palette.md +125 -0
- package/docs/usage/components/comment-thread-screen.md +125 -0
- package/docs/usage/components/container.md +98 -0
- package/docs/usage/components/content-transition.md +101 -0
- package/docs/usage/components/context-menu.md +136 -0
- package/docs/usage/components/counter-badge.md +107 -0
- package/docs/usage/components/data-table.md +122 -0
- package/docs/usage/components/date-picker.md +142 -0
- package/docs/usage/components/date-range-picker.md +111 -0
- package/docs/usage/components/description-list.md +103 -0
- package/docs/usage/components/design-system-provider.md +124 -0
- package/docs/usage/components/dialog.md +176 -0
- package/docs/usage/components/divider.md +89 -0
- package/docs/usage/components/editor-screen.md +126 -0
- package/docs/usage/components/effect-surface.md +120 -0
- package/docs/usage/components/empty-state.md +114 -0
- package/docs/usage/components/field.md +129 -0
- package/docs/usage/components/file-picker.md +114 -0
- package/docs/usage/components/floating-action-button.md +138 -0
- package/docs/usage/components/form.md +162 -0
- package/docs/usage/components/grid.md +99 -0
- package/docs/usage/components/heading.md +87 -0
- package/docs/usage/components/icon-button.md +126 -0
- package/docs/usage/components/icon.md +105 -0
- package/docs/usage/components/image.md +122 -0
- package/docs/usage/components/keyboard-avoiding.md +93 -0
- package/docs/usage/components/keyboard-dock.md +110 -0
- package/docs/usage/components/keyboard-form-scroll-view.md +95 -0
- package/docs/usage/components/keyboard-motion-provider.md +86 -0
- package/docs/usage/components/layout.md +117 -0
- package/docs/usage/components/link.md +121 -0
- package/docs/usage/components/list-detail-screen.md +103 -0
- package/docs/usage/components/list-row.md +124 -0
- package/docs/usage/components/list.md +119 -0
- package/docs/usage/components/load-more.md +115 -0
- package/docs/usage/components/masonry.md +109 -0
- package/docs/usage/components/media-selection-screen.md +119 -0
- package/docs/usage/components/mentions.md +119 -0
- package/docs/usage/components/menu.md +129 -0
- package/docs/usage/components/menubar.md +93 -0
- package/docs/usage/components/message-composer.md +124 -0
- package/docs/usage/components/moderation-screen.md +113 -0
- package/docs/usage/components/notice.md +106 -0
- package/docs/usage/components/notification-inbox-screen.md +97 -0
- package/docs/usage/components/notification-item.md +98 -0
- package/docs/usage/components/number-field.md +131 -0
- package/docs/usage/components/onboarding-screen.md +106 -0
- package/docs/usage/components/otp-field.md +101 -0
- package/docs/usage/components/pagination.md +82 -0
- package/docs/usage/components/password-field.md +137 -0
- package/docs/usage/components/permission-screen.md +107 -0
- package/docs/usage/components/photo-source-sheet.md +119 -0
- package/docs/usage/components/popover.md +108 -0
- package/docs/usage/components/profile-screen.md +89 -0
- package/docs/usage/components/progress.md +122 -0
- package/docs/usage/components/qr-code.md +122 -0
- package/docs/usage/components/radio-group.md +124 -0
- package/docs/usage/components/radio.md +104 -0
- package/docs/usage/components/result.md +116 -0
- package/docs/usage/components/saved-items-screen.md +126 -0
- package/docs/usage/components/screen-layout.md +119 -0
- package/docs/usage/components/search-field.md +120 -0
- package/docs/usage/components/search-screen.md +215 -0
- package/docs/usage/components/section.md +111 -0
- package/docs/usage/components/segmented-control.md +138 -0
- package/docs/usage/components/select.md +142 -0
- package/docs/usage/components/settings-screen.md +126 -0
- package/docs/usage/components/shared-transition-element.md +111 -0
- package/docs/usage/components/shared-transition-screen.md +86 -0
- package/docs/usage/components/sheet.md +151 -0
- package/docs/usage/components/side-panel.md +104 -0
- package/docs/usage/components/sidebar.md +107 -0
- package/docs/usage/components/skeleton.md +105 -0
- package/docs/usage/components/skip-nav.md +76 -0
- package/docs/usage/components/slider.md +121 -0
- package/docs/usage/components/sortable-collection.md +127 -0
- package/docs/usage/components/spinner.md +86 -0
- package/docs/usage/components/splitter.md +103 -0
- package/docs/usage/components/stack.md +93 -0
- package/docs/usage/components/statistic.md +123 -0
- package/docs/usage/components/steps.md +110 -0
- package/docs/usage/components/surface.md +91 -0
- package/docs/usage/components/swipe-actions.md +124 -0
- package/docs/usage/components/switch.md +120 -0
- package/docs/usage/components/tabs.md +134 -0
- package/docs/usage/components/tag.md +84 -0
- package/docs/usage/components/tags-input.md +111 -0
- package/docs/usage/components/text-area.md +112 -0
- package/docs/usage/components/text-format.md +75 -0
- package/docs/usage/components/text-transition.md +104 -0
- package/docs/usage/components/text.md +101 -0
- package/docs/usage/components/thinking-orb.md +105 -0
- package/docs/usage/components/timeline.md +105 -0
- package/docs/usage/components/toast.md +145 -0
- package/docs/usage/components/toggle-group.md +95 -0
- package/docs/usage/components/tooltip.md +103 -0
- package/docs/usage/components/top-bar.md +124 -0
- package/docs/usage/components/top.md +89 -0
- package/docs/usage/components/tour.md +118 -0
- package/docs/usage/components/transfer-list.md +115 -0
- package/docs/usage/components/tree.md +91 -0
- package/docs/usage/components/upload-item.md +99 -0
- package/docs/usage/components/virtual-list.md +105 -0
- package/docs/usage/components/visually-hidden.md +72 -0
- package/docs/usage/components/watermark.md +78 -0
- package/docs/usage/compositions/action-recovery-optimistic.md +180 -0
- package/docs/usage/compositions/action-recovery-save.md +235 -0
- package/docs/usage/compositions/action-recovery-undo.md +193 -0
- package/docs/usage/compositions/common-message.md +132 -0
- package/docs/usage/compositions/common-notification.md +101 -0
- package/docs/usage/compositions/compound-controls.md +186 -0
- package/docs/usage/compositions/data-layouts.md +157 -0
- package/docs/usage/compositions/disclosure.md +144 -0
- package/docs/usage/compositions/environment-matrix.md +139 -0
- package/docs/usage/compositions/expo-interactions.md +149 -0
- package/docs/usage/compositions/family-drawer.md +201 -0
- package/docs/usage/compositions/floating-action-button.md +197 -0
- package/docs/usage/compositions/input-sheet.md +148 -0
- package/docs/usage/compositions/interaction-adapters.md +190 -0
- package/docs/usage/compositions/interaction-flow-apply.md +205 -0
- package/docs/usage/compositions/interaction-flow-draft.md +188 -0
- package/docs/usage/compositions/interaction-flow-search.md +171 -0
- package/docs/usage/compositions/native-renderers.md +106 -0
- package/docs/usage/compositions/navigation-bar-collection.md +164 -0
- package/docs/usage/compositions/optional-adapters.md +169 -0
- package/docs/usage/compositions/optional-motion.md +109 -0
- package/docs/usage/compositions/photo-source.md +104 -0
- package/docs/usage/compositions/purpose-input-comment.md +110 -0
- package/docs/usage/compositions/purpose-input-message.md +119 -0
- package/docs/usage/compositions/reference-first.md +96 -0
- package/docs/usage/compositions/reference-review.md +107 -0
- package/docs/usage/compositions/reference-settings.md +107 -0
- package/docs/usage/compositions/selection-scope.md +174 -0
- package/docs/usage/compositions/stea-event-ticket.md +166 -0
- package/docs/usage/compositions/stea-flip-card.md +162 -0
- package/docs/usage/compositions/stea-order-progress.md +184 -0
- package/docs/usage/compositions/stea-otp-verify.md +215 -0
- package/docs/usage/compositions/stea-pixel-empty.md +140 -0
- package/docs/usage/compositions/stea-schedule-card.md +169 -0
- package/docs/usage/compositions/stea-stat-summary.md +154 -0
- package/docs/usage/compositions/time-selection.md +174 -0
- package/docs/usage/compositions/toast-layout.md +128 -0
- package/docs/usage/compositions/visual-foundations.md +185 -0
- package/docs/usage/compositions/web-additions.md +146 -0
- package/docs/usage/compositions/web-navigation.md +143 -0
- package/docs/usage/screens/common-chat.md +127 -0
- package/docs/usage/screens/common-comments.md +108 -0
- package/docs/usage/screens/common-inbox.md +110 -0
- package/docs/usage/screens/common-login.md +98 -0
- package/docs/usage/screens/common-profile.md +221 -0
- package/docs/usage/screens/common-saved.md +127 -0
- package/docs/usage/screens/common-search.md +274 -0
- package/docs/usage/screens/common-settings.md +126 -0
- package/docs/usage/screens/common-shell.md +108 -0
- package/docs/usage/screens/dashboard.md +245 -0
- package/docs/usage/screens/discovery-gallery.md +306 -0
- package/docs/usage/screens/flow-collection.md +96 -0
- package/docs/usage/screens/flow-editor.md +120 -0
- package/docs/usage/screens/flow-media.md +111 -0
- package/docs/usage/screens/flow-moderation.md +120 -0
- package/docs/usage/screens/flow-onboarding.md +193 -0
- package/docs/usage/screens/flow-permission.md +103 -0
- package/docs/usage/screens/landing.md +347 -0
- package/docs/usage/screens/mockup-studio.md +190 -0
- package/docs/usage/screens/notification-settings.md +206 -0
- package/docs/usage/screens/reference-comparison.md +159 -0
- package/docs/usage/templates/component.md +61 -0
- package/docs/usage/templates/composition.md +47 -0
- package/docs/usage/templates/screen.md +56 -0
- package/docs/usage/templates/token.md +32 -0
- package/docs/usage/tokens/color.md +142 -0
- package/docs/usage/tokens/elevation-opacity.md +86 -0
- package/docs/usage/tokens/layers.md +98 -0
- package/docs/usage/tokens/layout.md +114 -0
- package/docs/usage/tokens/motion.md +88 -0
- package/docs/usage/tokens/radius.md +53 -0
- package/docs/usage/tokens/size.md +74 -0
- package/docs/usage/tokens/spacing.md +73 -0
- package/docs/usage/tokens/stroke.md +50 -0
- package/docs/usage/tokens/theme-studio.md +70 -0
- package/docs/usage/tokens/typography-studio.md +70 -0
- package/docs/usage/tokens/typography.md +89 -0
- package/package.json +7 -1
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# 색상
|
|
2
|
+
|
|
3
|
+
- 단계: 토큰
|
|
4
|
+
- 상태: 배포
|
|
5
|
+
- 지원: Web · Native
|
|
6
|
+
- 적용: 1.12.1
|
|
7
|
+
- 검토일: 2026-10-06
|
|
8
|
+
- 근거: [브랜드 경계](../../brand-boundary.md), [테마 팔레트](../../theme-palette.md), [테마 사용법](../../theming.md), `src/colors.ts`, `src/semantic-colors.ts`, `src/component-recipes.ts`(`textRecipe`·`iconRecipe`), `src/base-recipes.ts`(`buttonRecipe`·`surfaceRecipe`), `packages/react/src/theme.ts`, `packages/react-native/src/provider.tsx`
|
|
9
|
+
- 스토리북: `배포/토큰/색과 글자/색상`
|
|
10
|
+
|
|
11
|
+
## 언제 쓰나
|
|
12
|
+
|
|
13
|
+
색은 팔레트 이름(파랑·회색)이 아니라 배경·글자·브랜드·피드백·테두리 **역할**로 고른다. 두 테마(light·dark)는 정확히
|
|
14
|
+
같은 key를 가지므로 역할로 고르면 다크 모드가 따라온다. 대부분은 컴포넌트의 `tone` prop이 역할을 대신 고르므로,
|
|
15
|
+
값을 직접 읽는 것은 제품 고유 영역(그래프·일러스트 틀·제품 전용 카드)일 때뿐이다.
|
|
16
|
+
|
|
17
|
+
고르는 순서:
|
|
18
|
+
|
|
19
|
+
1. 컴포넌트에 `tone`이 있으면 그것으로 고른다([Text](../components/text.md), [Icon](../components/icon.md),
|
|
20
|
+
[Badge](../components/badge.md), [Notice](../components/notice.md), [Button](../components/button.md)).
|
|
21
|
+
2. 없으면 아래 표에서 **칠할 대상**(용도 열)으로 역할을 찾는다. 같은 대상에 후보가 둘이면 위쪽(더 강한) 것을 먼저 본다.
|
|
22
|
+
3. 채운 바탕 위의 글자는 반드시 짝(`onPrimary`·`onDanger`·`onAccentFill`)을 쓴다.
|
|
23
|
+
|
|
24
|
+
## 값
|
|
25
|
+
|
|
26
|
+
값 열은 `light / dark`. Native 경로의 `theme`은 `useHjmNativeTheme()`, 역할 이름은 `semanticColors`(`@hjmds/design-contracts/tokens`)다.
|
|
27
|
+
|
|
28
|
+
### 배경
|
|
29
|
+
|
|
30
|
+
| 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
|
|
31
|
+
| --- | --- | --- | --- | --- |
|
|
32
|
+
| `canvas` = `bg` | `#ffffff` / `#0d1116` | `--hjm-color-bg` | `theme.colors.bg` | 화면 바탕. 카드·패널(Surface·Card)도 모든 tone이 `bg`를 칠하고 층은 테두리·그림자로 나눈다 |
|
|
33
|
+
| `surface.default` = `surface` | `#f2f4f6` / `#161b22` | `--hjm-color-surface` | `theme.colors.surface` | 바탕과 구분되는 옅은 면 |
|
|
34
|
+
| `surface.sunken` = `surfaceAlt` | `#e5e8eb` / `#1e232a` | `--hjm-color-surface-alt` | `theme.colors.surfaceAlt` | 한 단계 더 들어간 면(CodeBlock 바탕) |
|
|
35
|
+
| `surface.brand` = `surfaceAccent` | `#c9e2ff` / `#224159` | `--hjm-color-surface-accent` | `theme.colors.surfaceAccent` | 브랜드가 드러나는 옅은 면 |
|
|
36
|
+
|
|
37
|
+
### 글자
|
|
38
|
+
|
|
39
|
+
| 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
|
|
40
|
+
| --- | --- | --- | --- | --- |
|
|
41
|
+
| `content.primary` = `text` | `#191f28` / `#f3f5f7` | `--hjm-color-text` | `theme.colors.text` | 제목·강조 글자. Text `tone="primary"`(기본) |
|
|
42
|
+
| `content.body` = `textBody` | `#333d4b` / `#e1e5ea` | `--hjm-color-text-body` | `theme.colors.textBody` | 긴 본문. Text `tone="body"` |
|
|
43
|
+
| `content.secondary` = `textMuted` | `#4e5968` / `#cad0d8` | `--hjm-color-text-muted` | `theme.colors.textMuted` | 보조 설명. Text `tone="muted"` |
|
|
44
|
+
| `content.tertiary` = `textSub` | `#65707d` / `#9ba5b0` | `--hjm-color-text-sub` | `theme.colors.textSub` | 시간·출처 같은 약한 메타 정보. Text `tone="subtle"` |
|
|
45
|
+
| `content.decorative` = `textWeak` | `#8b95a1` / `#8b96a2` | `--hjm-color-text-weak` | `theme.colors.textWeak` | 비활성·placeholder·장식 표지. Text `tone="weak"`. 읽혀야 하는 글자에는 쓰지 않는다 |
|
|
46
|
+
|
|
47
|
+
### 브랜드
|
|
48
|
+
|
|
49
|
+
| 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
|
|
50
|
+
| --- | --- | --- | --- | --- |
|
|
51
|
+
| `action.brand.background` = `primary` | `#0369a1` / `#0476b4` | `--hjm-color-primary` | `theme.colors.primary` | 채운 주 행동 바탕. Button `tone="primary"` |
|
|
52
|
+
| `action.brand.content` = `onPrimary` | `#ffffff` / `#ffffff` | `--hjm-color-on-primary` | `theme.colors.onPrimary` | `primary` 위 글자·아이콘. Text·Icon `tone="inverse"` |
|
|
53
|
+
| `content.brand` = `contentBrand` | `#075985` / `#51bff6` | `--hjm-color-content-brand` | `theme.colors.contentBrand` | 바탕 위 브랜드 색 글자(링크·현재 위치·강조 숫자). Text `tone="brand"`. 포커스 링(`border.focus`)도 이 색 |
|
|
54
|
+
| `brandGradient` | `#0369a1` → `#155dfc` | — | `brandGradient` | HJM 조직 표면 서명. 제품 기본 브랜드가 아니다 |
|
|
55
|
+
|
|
56
|
+
### 피드백
|
|
57
|
+
|
|
58
|
+
| 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
|
|
59
|
+
| --- | --- | --- | --- | --- |
|
|
60
|
+
| `content.danger` = `danger` | `#b71919` / `#f87171` | `--hjm-color-danger` | `theme.colors.danger` | 오류 글자·아이콘. Text `tone="danger"`, 필드 오류 |
|
|
61
|
+
| `action.danger.background` = `dangerFill` | `#b91c1c` / `#b91c1c` | `--hjm-color-danger-fill` | `theme.colors.dangerFill` | 파괴 행동 바탕. Button `tone="danger"` |
|
|
62
|
+
| `action.danger.content` = `onDanger` | `#ffffff` / `#ffffff` | `--hjm-color-on-danger` | `theme.colors.onDanger` | `dangerFill` 위 글자 |
|
|
63
|
+
| `feedback.info.foreground` | `#6d28d9` / `#a78bfa` | `--hjm-accent-info` | `theme.palette.statusAccents.info` | 안내 |
|
|
64
|
+
| `feedback.success.foreground` | `#065f46` / `#34d399` | `--hjm-accent-success` | `theme.palette.statusAccents.success` | 완료 |
|
|
65
|
+
| `feedback.warning.foreground` | `#92400e` / `#fbbf24` | `--hjm-accent-warning` | `theme.palette.statusAccents.warning` | 주의가 필요한 상태 |
|
|
66
|
+
| `feedback.attention.foreground` | `#9a3412` / `#fb923c` | `--hjm-accent-attention` | `theme.palette.statusAccents.attention` | 눈길을 끌 새 소식 |
|
|
67
|
+
| `feedback.<tone>.background` · `.border` | 위 색 alpha 0.1 · 0.3 | — (`color-mix`) | `resolveColorReference(…, theme.palette)` | 피드백 영역의 옅은 바탕·테두리(Notice·Badge가 그린다) |
|
|
68
|
+
| `accentFill.<tone>` | info `#6d28d9` · success `#065f46` · warning `#92400e` · attention `#9a3412`(두 테마 공통) | `--hjm-accent-fill-<tone>` | `theme.palette.statusAccentFills.<tone>` | 꽉 찬 상태 표지 바탕. 글자는 `onAccentFill` `#ffffff` |
|
|
69
|
+
| `accentTint` | `weak` 0.1 · `base` 0.15 · `strong` 0.2 · `border` 0.3 | — | `accentTint` | 피드백 색을 옅게 깔 때 쓰는 alpha |
|
|
70
|
+
|
|
71
|
+
도메인 상태(예: "배송 지연")는 제품 어댑터에서 `info`·`success`·`warning`·`attention`·`danger` 중 하나로 매핑한다.
|
|
72
|
+
피드백 색은 `brandPalette`로 바꿀 수 없다(브랜드와 오류·성공이 같은 색이 되는 것을 막는다).
|
|
73
|
+
|
|
74
|
+
### 테두리·상호작용
|
|
75
|
+
|
|
76
|
+
| 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
|
|
77
|
+
| --- | --- | --- | --- | --- |
|
|
78
|
+
| `border.default` = `border` | `#e5e8eb` / `#6a788a` | `--hjm-color-border` | `theme.colors.border` | 영역 경계 hairline. Surface `bordered`, Divider. `border.subtle`은 alpha 0.7 |
|
|
79
|
+
| `border.control` = `borderControl` | `#6b7684` / `#929faf` | `--hjm-color-border-control` | `theme.colors.borderControl` | 쉬고 있는 컨트롤 윤곽(Button `secondary`, 필드) |
|
|
80
|
+
| `border.strong` = `textWeak` | `#8b95a1` / `#8b96a2` | `--hjm-color-text-weak` | `theme.colors.textWeak` | 강한 경계 |
|
|
81
|
+
| `border.focus` | `contentBrand` | `--hjm-color-focus` | `theme.colors.contentBrand` | 포커스 링. 컴포넌트가 그린다 |
|
|
82
|
+
| `interaction.hover` · `focus` · `pressed` · `selected` | `text` 0.06 · `contentBrand` 0.08 · `text` 0.1 · `primary` 0.1 | — | `resolveColorReference(…, theme.palette)` | 상태 덧칠. 제품 고유 누름 영역에만 직접 쓴다 |
|
|
83
|
+
|
|
84
|
+
### 함께 쓰는 쌍과 최소 대비
|
|
85
|
+
|
|
86
|
+
`checkBrandPaletteContrast`가 검사하는 쌍이다. 이 밖의 조합(예: `contentBrand` 글자를 `surfaceAccent` 위에)은 HJM이 대비를 보장하지 않는다.
|
|
87
|
+
|
|
88
|
+
- 4.5 이상: `text`·`textBody`·`textMuted`·`textSub`·`contentBrand`·`danger` on `bg`·`surface`, `onPrimary` on `primary`, `onDanger` on `dangerFill`.
|
|
89
|
+
- 3 이상: `primary`·`borderControl` vs `bg`·`surface`(형태), `textWeak` vs `bg`.
|
|
90
|
+
|
|
91
|
+
## 쓰는 법
|
|
92
|
+
|
|
93
|
+
```tsx
|
|
94
|
+
// Web
|
|
95
|
+
import { useHjmTheme } from "@hjmds/react/provider";
|
|
96
|
+
import { semanticColors } from "@hjmds/design-contracts/tokens";
|
|
97
|
+
import { resolveColorReference } from "@hjmds/design-contracts/color-references";
|
|
98
|
+
|
|
99
|
+
// 제품 CSS: .product-chart-axis { color: var(--hjm-color-text-sub); border-color: var(--hjm-color-border); }
|
|
100
|
+
const { palette } = useHjmTheme();
|
|
101
|
+
const good = resolveColorReference(semanticColors.feedback.success.foreground, palette);
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
// Native
|
|
106
|
+
import { View } from "react-native";
|
|
107
|
+
import { useHjmNativeTheme } from "@hjmds/react-native/provider";
|
|
108
|
+
import { semanticColors } from "@hjmds/design-contracts/tokens";
|
|
109
|
+
import { resolveColorReference } from "@hjmds/design-contracts/color-references";
|
|
110
|
+
|
|
111
|
+
const theme = useHjmNativeTheme();
|
|
112
|
+
<View style={{ backgroundColor: theme.colors.bg, borderColor: theme.colors.border, borderWidth: 1 }} />;
|
|
113
|
+
const tint = resolveColorReference(semanticColors.feedback.warning.background, theme.palette);
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
제품 브랜드는 Provider의 `brandPalette`로 17개 key 중 필요한 것만 바꾼다. 값은 [테마 편집](theme-studio.md)으로 고르고 대비를 확인한다.
|
|
117
|
+
|
|
118
|
+
```tsx
|
|
119
|
+
// Web
|
|
120
|
+
import { HjmProvider } from "@hjmds/react/provider";
|
|
121
|
+
<HjmProvider brandPalette={{ light: { primary: brand.light.fill, contentBrand: brand.light.text }, dark: { primary: brand.dark.fill, contentBrand: brand.dark.text } }}>
|
|
122
|
+
{app}
|
|
123
|
+
</HjmProvider>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## 하지 말 것
|
|
127
|
+
|
|
128
|
+
- `#0369a1`, `"gray"` 같은 색 값을 직접 쓰지 않는다. `THEMES.light.primary`를 import해 고정하는 것도 다크 모드와 브랜드를 깨뜨린다.
|
|
129
|
+
현재 palette(`useHjmTheme()`·`useHjmNativeTheme()`)를 읽는다.
|
|
130
|
+
- 제품 브랜드를 `--hjm-color-*` 재정의나 `.hjm-*` 덮어쓰기로 넣지 않는다. 경로는 `brandPalette` 하나다([브랜드 경계](../../brand-boundary.md)).
|
|
131
|
+
- 성공·오류 색을 브랜드 색으로, 브랜드 색을 상태 색으로 쓰지 않는다.
|
|
132
|
+
- `textWeak`로 읽혀야 하는 문구를 쓰지 않는다. 보조 문구는 `textMuted`·`textSub`다.
|
|
133
|
+
- 채운 `primary`·`dangerFill`·`accentFill` 위에 `text`를 올리지 않는다. 짝(`onPrimary`·`onDanger`·`onAccentFill`)을 쓴다.
|
|
134
|
+
- Showcase의 예시 색을 제품 기본값으로 복사하지 않는다.
|
|
135
|
+
|
|
136
|
+
## 플랫폼 차이
|
|
137
|
+
|
|
138
|
+
| 항목 | Web | Native |
|
|
139
|
+
| --- | --- | --- |
|
|
140
|
+
| 테마 색 읽기 | `--hjm-color-<kebab key>` CSS 변수 또는 `useHjmTheme().palette.theme` | `useHjmNativeTheme().colors` |
|
|
141
|
+
| 피드백 색 | `--hjm-accent-<tone>`, `--hjm-accent-fill-<tone>` | `theme.palette.statusAccents` · `statusAccentFills` |
|
|
142
|
+
| alpha 섞기 | CSS `color-mix` | `resolveColorReference` 또는 `withAlpha` |
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# 그림자와 투명도
|
|
2
|
+
|
|
3
|
+
- 단계: 토큰
|
|
4
|
+
- 상태: 배포
|
|
5
|
+
- 지원: Web · Native
|
|
6
|
+
- 적용: 1.12.1
|
|
7
|
+
- 검토일: 2026-10-06
|
|
8
|
+
- 근거: `src/foundations.ts`(`shadow`·`opacity`·`stateLayer`·`overlay`·`backdrop`·`scrim`), `src/component-contracts.ts`(`floatingSurfaceContract`), `src/component-recipes.ts`(`dialogRecipe`·`sheetRecipe`·`toastRecipe`·`bottomCtaRecipe`), `packages/react/src/theme.ts`, `packages/react/src/styles.css`, `packages/react-native/src/primitives.tsx`
|
|
9
|
+
- 스토리북: `배포/토큰/표면과 움직임/그림자와 투명도`
|
|
10
|
+
|
|
11
|
+
## 언제 쓰나
|
|
12
|
+
|
|
13
|
+
면이 다른 면 위에 떠 있음을 보일 때(그림자), 비활성·누름·끌기 상태를 흐리게 할 때(투명도), 상태 덧칠의 세기와
|
|
14
|
+
모달 뒤 배경막을 정할 때 쓴다. HJM은 층을 색 단계보다 테두리와 그림자로 나누므로(모든 Surface tone이 `bg`를 칠한다)
|
|
15
|
+
떠 있는 면이 필요하면 그림자 토큰을 고른다.
|
|
16
|
+
|
|
17
|
+
## 값
|
|
18
|
+
|
|
19
|
+
### 그림자
|
|
20
|
+
|
|
21
|
+
| 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
|
|
22
|
+
| --- | --- | --- | --- | --- |
|
|
23
|
+
| `shadow.raised` | `#000000` 0.08 · radius 4 · offsetY 1 | `--hjm-shadow-raised` | `shadow.raised` | 바탕에서 살짝 뜬 면 |
|
|
24
|
+
| `shadow.floating` | `#000000` 0.12 · radius 12 · offsetY 4 | `--hjm-shadow-floating` | `shadow.floating` | 떠 있는 면: Dialog·Sheet·Toast·팝오버(`floatingSurfaceContract`), Surface `tone="raised"`(Web) |
|
|
25
|
+
| `shadow.overlay` | `#000000` 0.16 · radius 24 · offsetY 8 | `--hjm-shadow-overlay` | `shadow.overlay` | 화면 위에 가장 높이 뜬 면 |
|
|
26
|
+
|
|
27
|
+
Web 변수는 `0 <offsetY>px <radius>px rgb(0 0 0 / <opacity>%)` box-shadow 문자열이다(blur = radius).
|
|
28
|
+
|
|
29
|
+
### 투명도
|
|
30
|
+
|
|
31
|
+
| 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
|
|
32
|
+
| --- | --- | --- | --- | --- |
|
|
33
|
+
| `opacity.disabled` | 0.5 | `--hjm-button-disabled-opacity`(Button) | `opacity.disabled` | 비활성 컨트롤 |
|
|
34
|
+
| `opacity.muted` | 0.72 | — | `opacity.muted` | 덜 중요한 내용(Calendar의 다른 달 날짜) |
|
|
35
|
+
| `opacity.pressed` | 0.86 | `--hjm-button-pressed-opacity`(Button) | `opacity.pressed` | 누르는 동안 |
|
|
36
|
+
| `opacity.dragged` | 0.64 | — | `opacity.dragged` | 끄는 중인 요소(Slider·Carousel·Toast 스와이프) |
|
|
37
|
+
|
|
38
|
+
### 상태 덧칠·배경막
|
|
39
|
+
|
|
40
|
+
| 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
|
|
41
|
+
| --- | --- | --- | --- | --- |
|
|
42
|
+
| `stateLayer.hover` | 0.06 | — | `stateLayer.hover` | 마우스 올림 덧칠 세기(`interaction.hover`) |
|
|
43
|
+
| `stateLayer.focus` | 0.08 | — | `stateLayer.focus` | 포커스 덧칠 세기 |
|
|
44
|
+
| `stateLayer.pressed` | 0.1 | — | `stateLayer.pressed` | 누름 덧칠 세기 |
|
|
45
|
+
| `stateLayer.selected` | 0.1 | — | `stateLayer.selected` | 선택 덧칠 세기 |
|
|
46
|
+
| `overlay.scrim` · `backdrop.modal` | `#000000` 0.6 | `--hjm-backdrop-modal` | `backdrop.modal` · `scrim`(`rgba(0, 0, 0, 0.6)`) | Dialog·Sheet·Tour 뒤 배경막 |
|
|
47
|
+
| `overlay.veil` · `backdrop.veil` | `#000000` 0.25 | — | `backdrop.veil` | 화면을 가볍게 덮는 막 |
|
|
48
|
+
|
|
49
|
+
Native 경로의 이름은 `@hjmds/design-contracts/foundations` import다.
|
|
50
|
+
|
|
51
|
+
## 쓰는 법
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
// Web
|
|
55
|
+
// 제품 고유 떠 있는 카드: .product-floating { box-shadow: var(--hjm-shadow-floating); }
|
|
56
|
+
import { Surface } from "@hjmds/react/layout";
|
|
57
|
+
|
|
58
|
+
<Surface tone="raised" padding="md">{children}</Surface>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
// Native
|
|
63
|
+
import { Pressable, View } from "react-native";
|
|
64
|
+
import { opacity, shadow } from "@hjmds/design-contracts/foundations";
|
|
65
|
+
|
|
66
|
+
const s = shadow.floating;
|
|
67
|
+
<View style={{ shadowColor: s.color, shadowOpacity: s.opacity, shadowRadius: s.radius, shadowOffset: { width: 0, height: s.offsetY }, elevation: 4 }} />;
|
|
68
|
+
<Pressable style={({ pressed }) => ({ opacity: pressed ? opacity.pressed : 1 })} onPress={open}>{tile}</Pressable>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## 하지 말 것
|
|
72
|
+
|
|
73
|
+
- 새 그림자(`0 2px 6px rgba(0,0,0,.2)`)를 만들지 않는다. 세 단계 중 고른다.
|
|
74
|
+
- 그림자를 겹쳐 층을 표현하지 않는다. 한 면에는 하나만 쓴다.
|
|
75
|
+
- 비활성을 투명도만으로 알리지 않는다. 컴포넌트 `disabled` prop을 써서 보조기기에도 알린다.
|
|
76
|
+
- 배경막 색·세기를 제품이 바꾸지 않는다. 모달은 HJM Dialog·Sheet가 배경막을 그린다.
|
|
77
|
+
|
|
78
|
+
## 플랫폼 차이
|
|
79
|
+
|
|
80
|
+
| 항목 | Web | Native |
|
|
81
|
+
| --- | --- | --- |
|
|
82
|
+
| 그림자 표현 | box-shadow 문자열(`--hjm-shadow-*`) | iOS `shadow*` 속성 + Android `elevation` |
|
|
83
|
+
| Surface `tone="raised"` | `--hjm-shadow-floating` | `shadow.floating` + `elevation` 4 |
|
|
84
|
+
| HJM 목록 팝업 그림자 | `--hjm-shadow-floating`(Select·Menubar·DatePicker·Menu·Popover) | Modal 기반 메뉴는 해당 플랫폼 표면 계약 |
|
|
85
|
+
|
|
86
|
+
2026-10-06 후속 검수에서 정의만 있던 Web 그림자 변수를 실제 떠 있는 표면에 연결했다. CommandPalette·Tour 같은 큰 강조 표면은 `shadow.overlay`, 일반 popup·Surface는 `shadow.floating`을 쓴다. Native Surface의 별도 blur 6도 floating token으로 맞췄다. 포커스 링·선택 테두리·스위치 손잡이의 inset 표현은 높이 그림자가 아니므로 각 컴포넌트 상태 계약을 유지한다.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# 겹침 순서
|
|
2
|
+
|
|
3
|
+
- 단계: 토큰
|
|
4
|
+
- 상태: 배포
|
|
5
|
+
- 지원: Web · Native
|
|
6
|
+
- 적용: 1.12.1
|
|
7
|
+
- 검토일: 2026-10-06
|
|
8
|
+
- 근거: [오버레이 스택](../../overlay-stack.md), `src/foundations.ts`(`layer`), `src/component-recipes.ts`(`toastRecipe.viewport.layer`·`tooltipRecipe.layer`), `packages/react/src/styles.css`, `packages/react-native/src/feedback.tsx`
|
|
9
|
+
- 스토리북: `배포/토큰/표면과 움직임/겹침 순서`
|
|
10
|
+
|
|
11
|
+
## 언제 쓰나
|
|
12
|
+
|
|
13
|
+
화면 위에 겹쳐 뜨는 것(고정 헤더·드롭다운·대화상자·툴팁·토스트)의 위아래 순서를 정할 때 쓴다. 값은 사이를 비워 둔
|
|
14
|
+
순서표다. 제품 고유 층은 두 토큰 사이 숫자를 쓰지 말고 같은 의미의 토큰을 고른다.
|
|
15
|
+
|
|
16
|
+
## 값
|
|
17
|
+
|
|
18
|
+
| 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
|
|
19
|
+
| --- | --- | --- | --- | --- |
|
|
20
|
+
| `layer.base` | 0 | `--hjm-layer-base` | `layer.base` | 일반 문서 흐름 |
|
|
21
|
+
| `layer.sticky` | 100 | `--hjm-layer-sticky` | `layer.sticky` | 스크롤해도 붙어 있는 헤더·탭·하단 바 |
|
|
22
|
+
| `layer.dropdown` | 400 | `--hjm-layer-dropdown` | `layer.dropdown` | 입력에 붙어 열리는 목록·메뉴 |
|
|
23
|
+
| `layer.overlay` | 800 | `--hjm-layer-overlay` | `layer.overlay` | 화면을 덮는 배경막(scrim)·패널 |
|
|
24
|
+
| `layer.modal` | 900 | `--hjm-layer-modal` | `layer.modal` | 대화상자·시트 본체 |
|
|
25
|
+
| `layer.tooltip` | 950 | `--hjm-layer-tooltip` | `layer.tooltip` | 툴팁(`tooltipRecipe.layer`) |
|
|
26
|
+
| `layer.toast` | 1000 | `--hjm-layer-toast` | `layer.toast` | 토스트. 기본 modal·tooltip 위(`toastRecipe.viewport.layer`, Native는 `zIndex`·`elevation`으로 쓴다) |
|
|
27
|
+
|
|
28
|
+
순서는 아래→위로 `base < sticky < dropdown < overlay < modal < tooltip < toast`다. Native 경로의 `layer`는
|
|
29
|
+
`@hjmds/design-contracts/foundations` import다.
|
|
30
|
+
|
|
31
|
+
## 쓰는 법
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
// Web
|
|
35
|
+
import { layer } from "@hjmds/design-contracts/foundations";
|
|
36
|
+
|
|
37
|
+
<header style={{ position: "sticky", insetBlockStart: 0, zIndex: layer.sticky }}>{topBar}</header>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
// Native
|
|
42
|
+
import { View } from "react-native";
|
|
43
|
+
import { layer } from "@hjmds/design-contracts/foundations";
|
|
44
|
+
|
|
45
|
+
<View style={{ position: "absolute", top: 0, left: 0, right: 0, zIndex: layer.sticky, elevation: layer.sticky }}>{banner}</View>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
대화상자·시트·토스트는 HJM 컴포넌트가 층을 정하므로 제품이 zIndex를 주지 않는다([Dialog](../components/dialog.md),
|
|
49
|
+
[Sheet](../components/sheet.md), [Toast](../components/toast.md)).
|
|
50
|
+
|
|
51
|
+
## 하지 말 것
|
|
52
|
+
|
|
53
|
+
- `z-index: 9999`처럼 순서표 밖 값을 쓰지 않는다. 가장 위가 필요하면 그것이 토스트인지부터 확인한다.
|
|
54
|
+
- 제품 헤더를 `layer.modal` 이상으로 올리지 않는다. 대화상자 배경막 위로 헤더가 떠서 닫기 전 조작이 가능해진다.
|
|
55
|
+
- 겹침 순서로 포커스 순서를 대신하지 않는다. 모달의 포커스 가두기는 컴포넌트가 맡는다.
|
|
56
|
+
- 1.12.1의 옛 숫자(700·800·1000 등)를 기준으로 맞춘 제품 `z-index`를 그대로 두지 않는다. 예를 들어 500인 제품 헤더는 예전엔
|
|
57
|
+
메뉴(800·900) 아래였지만 이제 메뉴(400)를 덮고, 950인 배너는 대화상자(1000) 아래였지만 이제 대화상자(900)를 덮는다.
|
|
58
|
+
제품 층은 숫자를 베끼지 말고 `var(--hjm-layer-sticky)`(메뉴가 덮어야 하는 제품 chrome), `var(--hjm-layer-dropdown)`보다 낮은 값(본문 위 떠 있는 내용),
|
|
59
|
+
`var(--hjm-layer-toast)`보다 높은 값(모든 HJM 층을 덮어야 하는 것만)처럼 토큰 기준으로 적는다.
|
|
60
|
+
|
|
61
|
+
## 플랫폼 차이
|
|
62
|
+
|
|
63
|
+
| 항목 | Web | Native |
|
|
64
|
+
| --- | --- | --- |
|
|
65
|
+
| HJM 컴포넌트가 쓰는 값 | FAB·BottomNavigation·sticky CTA는 `layer.sticky`, 목록·메뉴·Popover는 `layer.dropdown`, Dialog·Sheet는 `layer.modal + modalPriority`, Tooltip은 `layer.tooltip`, Toast는 `layer.toast`. 모달 내부 popup은 소유 모달 + 1 | Toast는 `layer.toast` 1000. Dialog·Sheet·Select는 RN `Modal`로 창 위에 뜬다 |
|
|
66
|
+
| 제품 고정 헤더 | `layer.sticky` 100이면 HJM 오버레이 아래에 있다 | `layer.sticky`, Android는 `elevation`도 같은 값 |
|
|
67
|
+
|
|
68
|
+
2026-10-06 후속 검수에서 Web의 별도 500–1400 순서표가 공통 토큰과 어긋나는 것을 확인해 토큰을 직접 소비하도록 바꿨다. 모달 내부 메뉴는 소유 모달보다 한 단계 위에 있어야 입력할 수 있으므로 +1 관계를 유지한다. 건너뛰기 링크는 키보드 초점 시 토스트에 가려지지 않게 `layer.toast + 1`을 쓴다. 이들은 새 독립 토큰이 아닌 소유·접근성 관계다.
|
|
69
|
+
|
|
70
|
+
### Web 표면별 실제 값
|
|
71
|
+
|
|
72
|
+
1.12.1까지 Web은 토큰과 다른 500–1400 숫자를 직접 썼다. 아래 값은 미게시(1.12.1 이후) 변경이며
|
|
73
|
+
`.changeset/shared-layer-elevation-consumption.md`가 이전 값을 함께 적는다. HJM 층끼리의 상대 순서는 Popover를 빼면 그대로다.
|
|
74
|
+
|
|
75
|
+
| Web 표면 | 토큰 | 값 | 1.12.1 값 |
|
|
76
|
+
| --- | --- | --- | --- |
|
|
77
|
+
| BottomCTA(`data-position="sticky"`), FloatingActionButton, BottomNavigation | `layer.sticky` | 100 | 1 · 500 · 700 |
|
|
78
|
+
| Select·Combobox 목록, DatePicker 팝오버, `useAnchoredPopup` 기본값, Menu, Menubar 패널, Mentions 목록, Popover | `layer.dropdown` | 400 | 800 · 900 · 950(Popover) |
|
|
79
|
+
| Dialog·AlertDialog·Sheet·SidePanel 배경막, ContextMenu, CommandPalette, Tour 배경막 | `layer.modal + modalPriority`(`getModalLayer`) | 900 + priority | 1000 + priority |
|
|
80
|
+
| Tour 팝업, 모달 안에서 열린 popup | 소유 모달 + 1 | 901 | 1001 |
|
|
81
|
+
| Tooltip | `layer.tooltip` | 950 | 1100 |
|
|
82
|
+
| Toast 영역 | `layer.toast` | 1000 | 1200 |
|
|
83
|
+
| SkipNav, Layout 건너뛰기 링크 | `layer.toast + 1` | 1001 | 1300 · 1400 |
|
|
84
|
+
|
|
85
|
+
Popover는 이제 메뉴와 같은 `dropdown` 층이다. 메뉴에서 연 Popover는 나중에 portal되므로 메뉴 위에 그려진다.
|
|
86
|
+
|
|
87
|
+
### `modalPriority` 기준
|
|
88
|
+
|
|
89
|
+
`getModalLayer(priority) = layer.modal + priority`다. 기준이 1000에서 900으로 내려와 문턱도 함께 내려왔다.
|
|
90
|
+
|
|
91
|
+
| 이 우선순위부터 | 모달이 덮는 것 | 1.12.1 문턱 |
|
|
92
|
+
| --- | --- | --- |
|
|
93
|
+
| 51 | Tooltip(950) | 101 |
|
|
94
|
+
| 101 | Toast(1000) | 201 |
|
|
95
|
+
| 102 | 건너뛰기 링크(1001) | 401 |
|
|
96
|
+
|
|
97
|
+
`modalPriority`는 모달 스택 내부 순서 조정 용도이며 제품이 전역 층을 바꾸는 우회로로 쓰지 않는다. 50을 넘는 값을 쓰는
|
|
98
|
+
제품은 위 문턱으로 다시 확인한다. 컨트롤 내부 장식의 0/1/2는 로컬 쌓임이며 전역 층 토큰과 구분한다.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# 화면 여백과 너비
|
|
2
|
+
|
|
3
|
+
- 단계: 토큰
|
|
4
|
+
- 상태: 배포
|
|
5
|
+
- 지원: Web · Native
|
|
6
|
+
- 적용: 1.12.1
|
|
7
|
+
- 검토일: 2026-10-06
|
|
8
|
+
- 근거: [Container](../../container.md), [반응형 Grid](../../responsive-grid.md), `src/foundations.ts`(`layout`·`breakpoint`), `src/container.ts`, `src/responsive.ts`, `src/component-recipes.ts`(`listRowRecipe`)
|
|
9
|
+
- 스토리북: `배포/토큰/공간과 크기/화면 여백과 너비`
|
|
10
|
+
|
|
11
|
+
## 언제 쓰나
|
|
12
|
+
|
|
13
|
+
화면 좌우 여백·본문 최대 폭·구획 간격·행 높이·breakpoint를 정하는 화면 배치의 기준값이다. 숫자는 Web CSS px,
|
|
14
|
+
Native dp(pt)로 같다. 화면 지침의 영역 구조는 이 값을 전제로 한다.
|
|
15
|
+
|
|
16
|
+
## 값
|
|
17
|
+
|
|
18
|
+
### 화면 좌우 여백·구획 간격
|
|
19
|
+
|
|
20
|
+
| 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
|
|
21
|
+
| --- | --- | --- | --- | --- |
|
|
22
|
+
| `layout.pagePadding.compact` | 16 (`spacing.md`) | — (Container `gutter="compact"`) | `layout.pagePadding.compact` | 폭 600 미만 화면 좌우 여백, AuthScreen compact |
|
|
23
|
+
| `layout.pagePadding.regular` | 20 (`spacing.lg`) | — (Container `gutter="regular"`, 기본) | `layout.pagePadding.regular` | 일반 화면 좌우 여백, AuthScreen regular |
|
|
24
|
+
| `layout.pagePadding.spacious` | 24 (`spacing.xl`) | — (Container `gutter="spacious"`) | `layout.pagePadding.spacious` | 여유 있는 넓은 화면 |
|
|
25
|
+
| — | 0 | — (Container `gutter="none"`) | — | 지도·갤러리처럼 가장자리까지 채우는 영역 |
|
|
26
|
+
| `layout.sectionGap` | 24 (`spacing.xl`) | `--hjm-space-xl` | `layout.sectionGap` | 화면 안 구획 사이(Stack `gap="xl"`) |
|
|
27
|
+
| `layout.contentGap` | 16 (`spacing.md`) | `--hjm-space-md` | `layout.contentGap` | 한 구획 안 요소 사이(Stack `gap="md"`) |
|
|
28
|
+
|
|
29
|
+
### 최대 폭
|
|
30
|
+
|
|
31
|
+
| 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
|
|
32
|
+
| --- | --- | --- | --- | --- |
|
|
33
|
+
| `layout.readingMaxWidth` | 720 | — (Container `size="reading"`) | `layout.readingMaxWidth` | 글 읽기·설정·폼처럼 한 줄 길이를 제한할 화면 |
|
|
34
|
+
| `layout.contentMaxWidth` | 1200 | — (Container `size="content"`, 기본) | `layout.contentMaxWidth` | 일반 제품 화면 |
|
|
35
|
+
| — | 제한 없음 | — (Container `size="full"`) | — | 의도적으로 가득 채우는 영역 |
|
|
36
|
+
|
|
37
|
+
### 행 높이
|
|
38
|
+
|
|
39
|
+
| 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
|
|
40
|
+
| --- | --- | --- | --- | --- |
|
|
41
|
+
| `layout.rowHeight.singleLine` | 56 | `--hjm-list-row-comfortable-one-line` | `layout.rowHeight.singleLine` | ListRow 한 줄 최소 높이(`comfortable` 기본). `relaxed` 64 · `spacious` 72 · `compact` 44(`control.minTouchTarget`) |
|
|
42
|
+
| `layout.rowHeight.twoLine` | 68 | `--hjm-list-row-comfortable-two-line` | `layout.rowHeight.twoLine` | ListRow 두 줄 최소 높이(`comfortable`). `relaxed` 76 · `spacious` 84 · `compact` 60. UploadItem 최소 높이 |
|
|
43
|
+
|
|
44
|
+
### Breakpoint(폭 구간)
|
|
45
|
+
|
|
46
|
+
| 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
|
|
47
|
+
| --- | --- | --- | --- | --- |
|
|
48
|
+
| `breakpoint.compact` | 0 | — | `breakpoint.compact` | `compact` 구간: 0 이상 600 미만. 휴대폰 세로 |
|
|
49
|
+
| `breakpoint.medium` | 600 | — | `breakpoint.medium` | `medium` 구간: 600 이상 960 미만. AlertDialog 버튼이 가로로 바뀌는 경계 |
|
|
50
|
+
| `breakpoint.expanded` | 960 | — | `breakpoint.expanded` | `expanded` 구간: 960 이상 1280 미만 |
|
|
51
|
+
| `breakpoint.wide` | 1280 | — | `breakpoint.wide` | `wide` 구간: 1280 이상 |
|
|
52
|
+
|
|
53
|
+
- 경계값은 포함한다. 폭 600은 `medium`이다(`resolveWindowClass`).
|
|
54
|
+
- 구간 이름은 기기 종류가 아니라 폭이다. 태블릿 세로·작은 브라우저 창도 폭으로 판단한다.
|
|
55
|
+
- `ResponsiveValue`는 `compact` 값이 필수이고, 빠진 큰 구간은 가장 가까운 좁은 구간 값을 물려받는다.
|
|
56
|
+
- Native 경로의 `layout`·`breakpoint`는 `@hjmds/design-contracts/foundations` import다. CSS 변수가 없는 값은 Container·Grid prop으로 쓴다.
|
|
57
|
+
|
|
58
|
+
## 쓰는 법
|
|
59
|
+
|
|
60
|
+
화면 본문은 Container로 감싸 최대 폭과 좌우 여백을 한 번에 정한다. 구획은 Stack `gap="xl"`(`layout.sectionGap`)로 띄운다.
|
|
61
|
+
|
|
62
|
+
```text
|
|
63
|
+
폭 < 600 (compact) 폭 ≥ 960 (expanded) — Container size="content"
|
|
64
|
+
┌────────────────────────┐ ┌──────────────────────────────────────────────┐
|
|
65
|
+
│←16→ 본문 ←16→│ │ ┌──── 최대 1200 ────┐ │
|
|
66
|
+
│ 구획 A │ │ ←20→ │ 구획 A │ ←20→ │
|
|
67
|
+
│ ↕ 24 (sectionGap) │ │ │ ↕ 24 │ │
|
|
68
|
+
│ 구획 B │ │ │ 구획 B │ │
|
|
69
|
+
└────────────────────────┘ └──────────────────────────────────────────────┘
|
|
70
|
+
gutter="compact"(16) gutter="regular"(20), inline 축 가운데 정렬
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
// Web
|
|
75
|
+
import { Container, Stack } from "@hjmds/react/layout";
|
|
76
|
+
import { resolveWindowClass } from "@hjmds/design-contracts/responsive";
|
|
77
|
+
|
|
78
|
+
const gutter = resolveWindowClass(window.innerWidth) === "compact" ? "compact" : "regular";
|
|
79
|
+
|
|
80
|
+
<Container size="content" gutter={gutter}>
|
|
81
|
+
<Stack gap="xl">{sections}</Stack>
|
|
82
|
+
</Container>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
// Native
|
|
87
|
+
import { useWindowDimensions } from "react-native";
|
|
88
|
+
import { Container, Stack } from "@hjmds/react-native/primitives";
|
|
89
|
+
import { resolveWindowClass } from "@hjmds/design-contracts/responsive";
|
|
90
|
+
|
|
91
|
+
const { width } = useWindowDimensions();
|
|
92
|
+
const gutter = resolveWindowClass(width) === "compact" ? "compact" : "regular";
|
|
93
|
+
|
|
94
|
+
<Container size="reading" gutter={gutter}>
|
|
95
|
+
<Stack gap="xl">{sections}</Stack>
|
|
96
|
+
</Container>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
폭에 따라 열 수가 바뀌는 배치는 [Grid](../components/grid.md)의 `columns`(ResponsiveValue)로, 화면 틀은 [Layout](../components/layout.md)으로 정한다.
|
|
100
|
+
|
|
101
|
+
## 하지 말 것
|
|
102
|
+
|
|
103
|
+
- `max-width: 1100px`, `@media (min-width: 768px)`처럼 토큰에 없는 폭을 만들지 않는다. 경계는 600·960·1280만 쓴다.
|
|
104
|
+
- 화면마다 `paddingHorizontal: 20`을 직접 적지 않는다. Container `gutter`나 `layout.pagePadding`을 쓴다.
|
|
105
|
+
- `left`/`right` 물리 방향 여백을 쓰지 않는다. Container는 inline 축 기준이라 RTL에서도 맞다.
|
|
106
|
+
- 기기 이름(phone·tablet)으로 분기하지 않는다. 폭 구간으로 분기한다.
|
|
107
|
+
|
|
108
|
+
## 플랫폼 차이
|
|
109
|
+
|
|
110
|
+
| 항목 | Web | Native |
|
|
111
|
+
| --- | --- | --- |
|
|
112
|
+
| 폭 읽기 | `window.innerWidth` 또는 컨테이너 폭 | `useWindowDimensions().width` |
|
|
113
|
+
| Container 폭·여백 | `max-inline-size` · `padding-inline` | `maxWidth` + `width: "100%"` · `paddingHorizontal` |
|
|
114
|
+
| CSS media query | 변수를 못 읽으므로 경계 숫자를 쓸 때는 600·960·1280만 | 해당 없음 |
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# 모션
|
|
2
|
+
|
|
3
|
+
- 단계: 토큰
|
|
4
|
+
- 상태: 배포
|
|
5
|
+
- 지원: Web · Native
|
|
6
|
+
- 적용: 1.12.1
|
|
7
|
+
- 검토일: 2026-10-06
|
|
8
|
+
- 근거: `src/foundations.ts`(`motion`·`easing`·`motionPreset`·`spring`), `packages/react/src/theme.ts`, `packages/react/src/provider.tsx`, `packages/react-native/src/provider.tsx`
|
|
9
|
+
- 스토리북: `배포/토큰/표면과 움직임/모션`
|
|
10
|
+
|
|
11
|
+
## 언제 쓰나
|
|
12
|
+
|
|
13
|
+
전환·나타남·사라짐의 길이와 곡선을 정할 때 쓴다. 먼저 의도(`motionPreset`)로 고르고, 길이와 곡선을 따로 고르는 것은 그 다음이다.
|
|
14
|
+
모든 프리셋은 OS의 "동작 줄이기"에서 어떻게 바뀌는지(`reducedMotion`)를 함께 정한다.
|
|
15
|
+
|
|
16
|
+
## 값
|
|
17
|
+
|
|
18
|
+
### 프리셋(먼저 고른다)
|
|
19
|
+
|
|
20
|
+
| 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
|
|
21
|
+
| --- | --- | --- | --- | --- |
|
|
22
|
+
| `motionPreset.micro` | 120ms · `standard` · 줄이기 `instant` | `--hjm-motion-fast` | `motionPreset.micro` | 눌림·토글·색 바뀜 같은 작은 상태 변화 |
|
|
23
|
+
| `motionPreset.enter` | 200ms · `enter` · 줄이기 `opacity` | `--hjm-motion-normal` | `motionPreset.enter` | 시트·토스트·팝오버가 나타남 |
|
|
24
|
+
| `motionPreset.exit` | 120ms · `exit` · 줄이기 `instant` | `--hjm-motion-fast` | `motionPreset.exit` | 사라짐(나타남보다 짧게) |
|
|
25
|
+
| `motionPreset.context` | 320ms · `emphasized` · 줄이기 `opacity` | `--hjm-motion-slow` | `motionPreset.context` | 화면 맥락이 바뀌는 큰 전환 |
|
|
26
|
+
|
|
27
|
+
줄이기 값: `instant` 즉시 바뀜, `opacity` 이동 없이 투명도만, `static` 움직임 없음.
|
|
28
|
+
|
|
29
|
+
### 길이·곡선·스프링
|
|
30
|
+
|
|
31
|
+
| 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
|
|
32
|
+
| --- | --- | --- | --- | --- |
|
|
33
|
+
| `motion.fast` | 120ms | `--hjm-motion-fast` | `motion.fast` | 작은 상태 변화 |
|
|
34
|
+
| `motion.normal` | 200ms | `--hjm-motion-normal` | `motion.normal` | 나타남 |
|
|
35
|
+
| `motion.slow` | 320ms | `--hjm-motion-slow` | `motion.slow` | 큰 전환 |
|
|
36
|
+
| `easing.standard` | `cubic-bezier(0.2, 0, 0, 1)` | — | `easing.standard` | 화면 안 이동 |
|
|
37
|
+
| `easing.enter` | `cubic-bezier(0, 0, 0, 1)` | — | `easing.enter` | 들어옴(감속) |
|
|
38
|
+
| `easing.exit` | `cubic-bezier(0.3, 0, 1, 1)` | — | `easing.exit` | 나감(가속) |
|
|
39
|
+
| `easing.emphasized` | `cubic-bezier(0.2, 0, 0, 1)` | — | `easing.emphasized` | 강조 전환 |
|
|
40
|
+
| `spring.responsive` | stiffness 760 · damping 52 · mass 1 | — | `spring.responsive` | 손을 따라가는 빠른 스프링(Native) |
|
|
41
|
+
| `spring.expressive` | stiffness 520 · damping 38 · mass 1 | — | `spring.expressive` | 튀는 느낌의 스프링(Native) |
|
|
42
|
+
|
|
43
|
+
Web의 `--hjm-motion-*`는 동작 줄이기가 켜지면 `0ms`가 된다. Native 경로의 이름은 `@hjmds/design-contracts/foundations` import다.
|
|
44
|
+
|
|
45
|
+
## 쓰는 법
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
// Web
|
|
49
|
+
import { easing, motionPreset } from "@hjmds/design-contracts/foundations";
|
|
50
|
+
|
|
51
|
+
// 제품 CSS: .product-chip { transition: background-color var(--hjm-motion-fast); }
|
|
52
|
+
const enter = motionPreset.enter;
|
|
53
|
+
const transition = `opacity ${enter.duration}ms cubic-bezier(${easing[enter.easing].join(", ")})`;
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
// Native
|
|
58
|
+
import { Animated } from "react-native";
|
|
59
|
+
import { Easing } from "react-native";
|
|
60
|
+
import { easing, motionPreset } from "@hjmds/design-contracts/foundations";
|
|
61
|
+
import { useHjmNativeTheme } from "@hjmds/react-native/provider";
|
|
62
|
+
|
|
63
|
+
const { environment } = useHjmNativeTheme();
|
|
64
|
+
const enter = motionPreset.enter;
|
|
65
|
+
Animated.timing(value, {
|
|
66
|
+
toValue: 1,
|
|
67
|
+
duration: environment.reducedMotion ? 0 : enter.duration,
|
|
68
|
+
easing: Easing.bezier(...easing[enter.easing]),
|
|
69
|
+
useNativeDriver: true,
|
|
70
|
+
}).start();
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Web에서 JS로 직접 움직일 때는 `useHjmTheme().environment.reducedMotion`을 확인한다.
|
|
74
|
+
|
|
75
|
+
## 하지 말 것
|
|
76
|
+
|
|
77
|
+
- 동작 줄이기를 무시하지 않는다. `reducedMotion`이 켜지면 프리셋의 줄이기 값대로 바꾼다.
|
|
78
|
+
- 300ms, `ease-in-out`처럼 토큰에 없는 길이·곡선을 쓰지 않는다.
|
|
79
|
+
- 사라짐을 나타남보다 길게 만들지 않는다.
|
|
80
|
+
- 반복 애니메이션으로 주의를 끌지 않는다. 로딩 표시(Spinner·Skeleton)는 컴포넌트가 맡는다.
|
|
81
|
+
|
|
82
|
+
## 플랫폼 차이
|
|
83
|
+
|
|
84
|
+
| 항목 | Web | Native |
|
|
85
|
+
| --- | --- | --- |
|
|
86
|
+
| 동작 줄이기 | `prefers-reduced-motion`을 Provider가 읽어 `--hjm-motion-*`를 `0ms`로, 루트에 `data-motion="reduced"` | `AccessibilityInfo`를 Provider가 읽어 `environment.reducedMotion`. 첫 프레임은 줄이기로 가정 |
|
|
87
|
+
| 곡선 | `cubic-bezier(...)` | `Easing.bezier(...)` |
|
|
88
|
+
| 스프링 | 쓰지 않음 | `spring.*` |
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# 둥글기
|
|
2
|
+
|
|
3
|
+
- 단계: 토큰
|
|
4
|
+
- 상태: 배포
|
|
5
|
+
- 지원: Web · Native
|
|
6
|
+
- 적용: 1.12.1
|
|
7
|
+
- 검토일: 2026-10-06
|
|
8
|
+
- 근거: `src/foundations.ts`(`radius`), `src/base-recipes.ts`(`buttonRecipe.shapes`·`fieldRecipe.shapes`·`surfaceDefaults`), `src/component-recipes.ts`, `packages/react/src/theme.ts`
|
|
9
|
+
- 스토리북: `배포/토큰/표면과 움직임/둥글기`
|
|
10
|
+
|
|
11
|
+
## 언제 쓰나
|
|
12
|
+
|
|
13
|
+
모서리 반경을 정할 때 쓴다. 컴포넌트는 자기 radius를 recipe로 이미 갖고 있으므로, 제품이 직접 고르는 것은 제품 고유 틀
|
|
14
|
+
(이미지 틀·제품 카드 안쪽 영역)일 때다. 고를 때는 아래 용도 열에서 같은 크기의 HJM 컴포넌트를 찾아 맞춘다.
|
|
15
|
+
|
|
16
|
+
## 값
|
|
17
|
+
|
|
18
|
+
| 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
|
|
19
|
+
| --- | --- | --- | --- | --- |
|
|
20
|
+
| `radius.sm` | 8 | `--hjm-radius-sm` | `radius.sm` · `theme.tokens.radius.sm` | Tooltip, Skeleton 글자 줄 |
|
|
21
|
+
| `radius.md` | 12 | `--hjm-radius-md` | `radius.md` · `theme.tokens.radius.md` | Button `shape="rounded"`(기본), 필드 `shape="medium"`, Notice, Skeleton 블록, Avatar `shape="rounded"`, 목록 팝업 |
|
|
22
|
+
| `radius.lg` | 16 | `--hjm-radius-lg` | `radius.lg` · `theme.tokens.radius.lg` | Surface·Card 기본, Dialog, Toast, SegmentedControl, 필드 `shape="large"` |
|
|
23
|
+
| `radius.xl` | 24 | `--hjm-radius-xl` | `radius.xl` · `theme.tokens.radius.xl` | Sheet 위쪽 모서리 |
|
|
24
|
+
| `radius.full` | 999 | `--hjm-radius-full` | `radius.full` · `theme.tokens.radius.full` | 원·캡슐: Button `shape="pill"`, Badge, Chip, SearchField, Avatar `shape="circle"`, BottomNavigation 캡슐 |
|
|
25
|
+
|
|
26
|
+
- 안쪽 요소의 radius는 바깥보다 크지 않게 고른다. 예: `radius.lg` Surface 안의 이미지 틀은 `radius.md`.
|
|
27
|
+
- Native 경로의 `radius`는 `@hjmds/design-contracts/foundations` import다.
|
|
28
|
+
|
|
29
|
+
## 쓰는 법
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
// Web
|
|
33
|
+
import { Surface } from "@hjmds/react/layout";
|
|
34
|
+
|
|
35
|
+
<Surface radius="lg" padding="md">{children}</Surface>
|
|
36
|
+
// 제품 고유 틀: .product-thumb { border-radius: var(--hjm-radius-md); overflow: hidden; }
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
// Native
|
|
41
|
+
import { StyleSheet } from "react-native";
|
|
42
|
+
import { radius } from "@hjmds/design-contracts/foundations";
|
|
43
|
+
import { Surface } from "@hjmds/react-native/primitives";
|
|
44
|
+
|
|
45
|
+
<Surface radius="lg" padding="md">{children}</Surface>
|
|
46
|
+
const styles = StyleSheet.create({ thumb: { borderRadius: radius.md, overflow: "hidden" } });
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## 하지 말 것
|
|
50
|
+
|
|
51
|
+
- `border-radius: 10px`처럼 토큰에 없는 값을 쓰지 않는다.
|
|
52
|
+
- HJM 컴포넌트의 radius를 `style`·`className`으로 덮지 않는다. Button·필드는 `shape`, Surface는 `radius` prop으로만 고른다.
|
|
53
|
+
- 원형을 만들려고 `50%`나 폭의 절반을 계산하지 않는다. `radius.full`을 쓴다.
|