@hjmds/design-contracts 1.27.2 → 1.28.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/activity-chip.d.ts +1 -1
- package/dist/activity-chip.js +1 -1
- package/dist/activity-chip.js.map +1 -1
- package/dist/auth-screen.d.ts +71 -5
- package/dist/auth-screen.d.ts.map +1 -1
- package/dist/auth-screen.js +60 -2
- package/dist/auth-screen.js.map +1 -1
- package/dist/base-recipes.d.ts +1 -1
- package/dist/base-recipes.js +1 -1
- package/dist/base-recipes.js.map +1 -1
- package/dist/behaviors.d.ts +1 -1
- package/dist/breadcrumb.d.ts +1 -1
- package/dist/breadcrumb.js +1 -1
- package/dist/breadcrumb.js.map +1 -1
- package/dist/calendar.d.ts +1 -1
- package/dist/calendar.js +2 -2
- package/dist/calendar.js.map +1 -1
- package/dist/catalog.d.ts +131 -87
- package/dist/catalog.d.ts.map +1 -1
- package/dist/component-contracts.d.ts +1 -1
- package/dist/component-contracts.js +1 -1
- package/dist/component-contracts.js.map +1 -1
- package/dist/component-recipes.d.ts +85 -57
- package/dist/component-recipes.d.ts.map +1 -1
- package/dist/component-recipes.js +95 -60
- package/dist/component-recipes.js.map +1 -1
- package/dist/conversation-row.d.ts +2 -2
- package/dist/conversation-row.d.ts.map +1 -1
- package/dist/conversation-row.js +6 -4
- package/dist/conversation-row.js.map +1 -1
- package/dist/counter-badge-recipe.d.ts +1 -1
- package/dist/counter-badge-recipe.js +1 -1
- package/dist/counter-badge-recipe.js.map +1 -1
- package/dist/data-table.d.ts +4 -4
- package/dist/data-table.js +4 -4
- package/dist/data-table.js.map +1 -1
- package/dist/date-picker.d.ts +1 -1
- package/dist/design-profile.d.ts.map +1 -1
- package/dist/design-profile.js +19 -16
- package/dist/design-profile.js.map +1 -1
- package/dist/file-picker.d.ts +1 -1
- package/dist/file-picker.js +1 -1
- package/dist/file-picker.js.map +1 -1
- package/dist/foundations.d.ts +18 -14
- package/dist/foundations.d.ts.map +1 -1
- package/dist/foundations.js +18 -13
- package/dist/foundations.js.map +1 -1
- package/dist/heading.d.ts +13 -13
- package/dist/heading.js.map +1 -1
- package/dist/number-field.d.ts +1 -1
- package/dist/otp-field.d.ts +1 -1
- package/dist/password-field.d.ts +1 -1
- package/dist/result.d.ts +1 -1
- package/dist/result.js +2 -2
- package/dist/result.js.map +1 -1
- package/dist/semantic-colors.d.ts +10 -0
- package/dist/semantic-colors.d.ts.map +1 -1
- package/dist/semantic-colors.js +6 -0
- package/dist/semantic-colors.js.map +1 -1
- package/dist/sheet.d.ts +7 -4
- package/dist/sheet.d.ts.map +1 -1
- package/dist/sheet.js +8 -5
- package/dist/sheet.js.map +1 -1
- package/dist/side-panel.d.ts +1 -1
- package/dist/side-panel.js +2 -2
- package/dist/side-panel.js.map +1 -1
- package/dist/steps.d.ts +1 -1
- package/dist/steps.js +1 -1
- package/dist/steps.js.map +1 -1
- package/dist/tag.d.ts +1 -1
- package/dist/tag.js +1 -1
- package/dist/tag.js.map +1 -1
- package/dist/timeline.d.ts +1 -1
- package/dist/timeline.js +1 -1
- package/dist/timeline.js.map +1 -1
- package/dist/top.d.ts +4 -4
- package/dist/top.d.ts.map +1 -1
- package/dist/top.js +3 -1
- package/dist/top.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/activity-chip.md +5 -1
- package/docs/auth-screen.md +2 -0
- package/docs/design-profile.md +14 -10
- package/docs/generated/component-maturity.md +1 -1
- package/docs/generated/renderer-evidence.json +9 -5
- package/docs/generated/renderer-evidence.md +1 -1
- package/docs/generated/showcase-manifest.json +1 -1
- package/docs/heading.md +14 -9
- package/docs/sheet.md +2 -0
- package/docs/top.md +4 -3
- package/docs/usage/README.md +1 -1
- package/docs/usage/components/auth-screen-layout.md +26 -9
- package/docs/usage/components/bottom-cta.md +6 -2
- package/docs/usage/components/breadcrumb.md +5 -1
- package/docs/usage/components/checkbox-group.md +5 -1
- package/docs/usage/components/collapsible.md +1 -0
- package/docs/usage/components/effect-surface.md +2 -2
- package/docs/usage/components/heading.md +14 -12
- package/docs/usage/components/list-row.md +7 -3
- package/docs/usage/components/list.md +5 -4
- package/docs/usage/components/notification-item.md +5 -1
- package/docs/usage/components/photo-source-sheet.md +1 -1
- package/docs/usage/components/screen-layout.md +7 -3
- package/docs/usage/components/search-screen.md +1 -1
- package/docs/usage/components/section.md +5 -1
- package/docs/usage/components/segmented-control.md +1 -1
- package/docs/usage/components/sheet.md +17 -13
- package/docs/usage/components/skeleton.md +41 -15
- package/docs/usage/components/statistic.md +5 -1
- package/docs/usage/components/switch.md +3 -2
- package/docs/usage/components/tag.md +5 -1
- package/docs/usage/components/text.md +6 -4
- package/docs/usage/components/top.md +1 -1
- package/docs/usage/compositions/adaptive-content.md +5 -1
- package/docs/usage/compositions/compound-controls.md +2 -1
- package/docs/usage/compositions/design-profile-comparison.md +1 -1
- package/docs/usage/compositions/family-drawer.md +5 -3
- package/docs/usage/compositions/input-sheet.md +1 -1
- package/docs/usage/compositions/interaction-flow-apply.md +3 -3
- package/docs/usage/compositions/photo-source.md +3 -3
- package/docs/usage/compositions/selection-motion.md +5 -1
- package/docs/usage/compositions/visual-foundations.md +5 -4
- package/docs/usage/screens/common-login.md +9 -7
- package/docs/usage/screens/landing.md +15 -14
- package/docs/usage/screens/notification-settings.md +4 -2
- package/docs/usage/tokens/typography.md +21 -13
- package/package.json +1 -1
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
현재 화면 위에 모달로 띄우는 보조 작업 패널에 쓴다. 설정 한 항목 편집, 선택 목록(테마·언어),
|
|
14
14
|
짧은 입력 폼, 공유·사진 출처 선택처럼 끝나면 원래 화면으로 돌아오는 작업이다. 기본은 하단에서 올라온다.
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
제목은 화면에 그리지 않고 대화상자의 접근성 이름으로만 쓴다(2026-10-17). 키보드 높이, top inset, 본문 maxHeight 보정은 Sheet가 소유한다. 제품 wrapper에서 Modal·키보드 listener·강제 maxHeight를
|
|
17
17
|
다시 만들지 않는다.
|
|
18
18
|
|
|
19
19
|
## 쓰지 않을 때
|
|
@@ -83,11 +83,11 @@ import { Sheet } from "@hjmds/react-native/overlays";
|
|
|
83
83
|
| `open` · `defaultOpen` | `boolean` | 비제어 `false` | Web은 제어하면 `onOpenChange` 필수, 비제어면 `trigger` 필수 |
|
|
84
84
|
| `onOpenChange` | `(open: boolean, detail: { reason }) => void` | — | reason: `trigger` · `close-action` · `escape` · `back` · `outside` · `swipe` · `programmatic`. Android 하드웨어 back은 Native에서만 `back` |
|
|
85
85
|
| `onDismissComplete` | `(detail: { reason }) => void` | — | 표면이 실제로 사라진 뒤 한 번. 다음 modal·화면 이동은 여기서 |
|
|
86
|
-
| `dismissPolicy` | `{ dismissible?, dismissWhileBusy?, outsideDismiss?, escapeOrBackDismiss?, swipeDismiss? }` | `dismissible` true · `dismissWhileBusy` false · `outsideDismiss`
|
|
86
|
+
| `dismissPolicy` | `{ dismissible?, dismissWhileBusy?, outsideDismiss?, escapeOrBackDismiss?, swipeDismiss? }` | `dismissible` true · `dismissWhileBusy` false · `outsideDismiss` true(모든 위치, 미게시 변경, 이전 하단 false) · `escapeOrBackDismiss` true · `swipeDismiss` true(미게시 변경, 이전 false) | 닫기 허용 범위. `resolveSheetDismissPolicy(placement, override)` |
|
|
87
87
|
| `keyboardAvoidance` (Native) | `boolean` | `true`(미게시 변경, 이전 `false`) | 모달이 키보드 높이만큼 비킨다. 안쪽 `ScreenLayout`은 중복 처리하지 않는다 |
|
|
88
88
|
| `showClose` | `boolean` | 하단 `false` · 옆 `true`(미게시) | 머리의 × 표시. 하단 시트는 드래그로 닫는 것이 기본이라 끈다 |
|
|
89
89
|
| `busy` | `boolean` | `false` | 사용자 닫기를 막는다 |
|
|
90
|
-
| `title` · `closeLabel` | 문구 | 필수 | Native `title`이 element면 `accessibilityTitle`(string)
|
|
90
|
+
| `title` · `closeLabel` | 문구 | 필수 | `title`은 화면에 보이지 않는다. Web은 `aria-labelledby`가 가리키는 시각 숨김 `h2`, Native는 대화상자 `accessibilityLabel`과 배치 밖 1pt 머리글이다(미게시). Native `title`이 element면 `accessibilityTitle`(string) 필수이며 element 자체는 그려지지 않는다. `closeLabel`은 손잡이·×·가림막 닫기의 이름 |
|
|
91
91
|
| `description` · `footer` | 노드(Native `description`은 `string`) | — | `footer`는 스크롤 밖 고정. 저장 행동은 여기 |
|
|
92
92
|
| `detents`(Web) · `activeDetent` · `onDetentChange` | `readonly ("medium" \| "large" \| "full")[]` · `(detent) => void` | — | 사용자가 높이를 바꿀 단계. `detentLabels: { expand, collapse }` 필수 |
|
|
93
93
|
| `keyboardAvoidance` · `scrollable`(Native) | `boolean` | `false` | 입력 폼은 둘 다 켠다 |
|
|
@@ -112,17 +112,17 @@ import { Sheet } from "@hjmds/react-native/overlays";
|
|
|
112
112
|
| --- | --- | --- |
|
|
113
113
|
| 크기 | 하단 시트 높이: `auto` 내용 높이(최대 화면 높이 × `maxHeightRatio` 0.9), `medium` 60%, `large` 85%, `full` 위 safe area 안 전체 높이(0.9 상한 없음). Web 하단 시트 최대 폭 640. 위 두 모서리만 `radius.xl` 24. 옆 시트(`start`·`end`): Web 폭 `min(28rem, 100%)`·높이 화면 − 32, Native 폭 88%(최대 420)·화면 높이 | `sheetRecipe.sizes`·`content`·`web`, `.hjm-sheet`, Native `Sheet` |
|
|
114
114
|
| 간격 | 두 플랫폼 같은 값: 좌우 `content.paddingHorizontal`(`spacing.lg` 20), 위·아래 `content.paddingTop/Bottom`(`spacing.sm` 12, 하단 시트는 아래 safe area를 더함), 머리·본문·footer 사이 `body.gap`(`spacing.md` 16), footer 위 `footer.paddingTop`(`spacing.sm` 12), 제목–닫기 `header.gap`(`spacing.sm` 12), footer 버튼 사이 `footer.gap`(`spacing.sm` 12) | `.hjm-sheet__*`, `sheetRecipe.content`·`body`·`footer` |
|
|
115
|
-
| 순서·정렬 | 머리(
|
|
116
|
-
| 고정·스크롤 | 본문만 스크롤, 머리·`footer` 고정(Native는 `scrollable`일 때 ScrollView). 고정 높이(`medium`·`large`·`full`, 옆 시트)에서는 본문이 머리·footer를 뺀 남은 높이를 차지하고 `footer`는 시트 아래에 붙는다. 그래서 본문의 `flex: 1` 자식(SearchScreen, 목록 화면)이 그 높이를 채운다(Web `.hjm-sheet__body` `flex: 1 1 auto`, Native 본문 `flexGrow: 1`·`minHeight: 0`, 1.13.1 변경). `auto`는 내용 높이 그대로다. 저장·확인은 `footer`에. 아래 안전 영역은 시트가 자기 여백에 더한다(`spacing.sm` 12 + 아래 inset, Web은 `env(safe-area-inset-bottom)`). 위 inset은 시트가 올라갈 높이를 줄인다. 제품이 inset을 다시 더하지 않는다 | `.hjm-sheet__body`, `sheetRecipe.safeArea`, Native `Sheet` |
|
|
115
|
+
| 순서·정렬 | 손잡이(하단) → 머리(설명 + 닫기, 둘 다 없으면 그리지 않음) → 본문 → `footer`. 보이는 제목 줄은 없다(미게시). 머리 최소 높이 44(`control.minTouchTarget`), ×는 설명이 없어도 끝 쪽 같은 자리다. 기본 하단 시트는 머리가 없어 본문이 손잡이 영역 바로 아래 `body.gap` 16에서 시작한다. Web footer는 오른쪽 정렬 가로 줄 [보조][주]. Native footer는 세로 열이라 꽉 찬 폭 버튼을 주 행동 먼저 둔다. 하단 시트는 항상 위 가운데 36×4 손잡이(터치 최소 폭 44)를 둔다. `detents`가 있으면 높이 전환 버튼, 없으면 `closeLabel` 이름의 닫기 버튼이다. 손잡이·머리 영역을 아래로 끌어 `sheetDragRecipe.dismissDistanceRatio`(시트 높이의 25%) 또는 `dismissVelocity` 0.5px/ms를 넘기면 `swipe`로 닫고, 아니면 제자리로 돌아간다(미게시, Web·Native 동일) | `sheetRecipe.header`·`handle`, `.hjm-sheet__footer`, `.hjm-sheet__handle` |
|
|
116
|
+
| 고정·스크롤 | 본문만 스크롤, 머리·`footer` 고정(Native는 `scrollable`일 때 ScrollView). 스크롤 영역은 시트 좌우 여백(`sheetRecipe.content.paddingHorizontal` 20) 끝까지 넓고 여백은 스크롤 내용 안쪽에 둔다(Web `.hjm-sheet__body` padding, Native ScrollView `marginHorizontal: -20` + 내용 `paddingHorizontal: 20`). 그래서 본문 ListRow의 8 표면이 잘리지 않고 내용 위치는 그대로다. 고정 높이(`medium`·`large`·`full`, 옆 시트)에서는 본문이 머리·footer를 뺀 남은 높이를 차지하고 `footer`는 시트 아래에 붙는다. 그래서 본문의 `flex: 1` 자식(SearchScreen, 목록 화면)이 그 높이를 채운다(Web `.hjm-sheet__body` `flex: 1 1 auto`, Native 본문 `flexGrow: 1`·`minHeight: 0`, 1.13.1 변경). `auto`는 내용 높이 그대로다. 저장·확인은 `footer`에. 아래 안전 영역은 시트가 자기 여백에 더한다(`spacing.sm` 12 + 아래 inset, Web은 `env(safe-area-inset-bottom)`). 위 inset은 시트가 올라갈 높이를 줄인다. 제품이 inset을 다시 더하지 않는다 | `.hjm-sheet__body`, `sheetRecipe.safeArea`, Native `Sheet` |
|
|
117
117
|
| 좁은 폭·큰 글자 | 폭 < 640이면 하단 시트가 화면 폭을 채운다. Web footer는 줄을 바꾼다. 큰 글자로 내용이 길어지면 `auto`는 최대 90%에서 멈추고 본문이 스크롤된다(`full`은 위 safe area까지) | `.hjm-sheet[data-placement="bottom"]`, `maxHeightRatio` |
|
|
118
118
|
|
|
119
119
|
```text
|
|
120
120
|
폭 < 640(모바일) Web 넓은 폭: 가운데, 최대 640
|
|
121
121
|
┌──────────────────────────────┐ ┌────────────────────────────────────┐
|
|
122
|
-
│
|
|
122
|
+
│ (뒤 화면 + scrim, 누르면 닫힘) │ │ (scrim, 누르면 닫힘) │
|
|
123
123
|
│ ╭──────────────────────────╮ │ │ ╭────────────────────╮ │
|
|
124
|
-
│ │
|
|
125
|
-
│ │
|
|
124
|
+
│ │ ▬▬ 손잡이 │ │ │ │ ▬▬ 손잡이 │ │
|
|
125
|
+
│ │ (설명) (showClose일 때 ×)│ │ ← 고정 │ │ 본문(스크롤) │ │
|
|
126
126
|
│ │──────────────────────────│ │ │ │ [취소] [저장] │ │
|
|
127
127
|
│ │ 본문 ↕ 스크롤 │ │ └──────┴────────────────────┴────────┘
|
|
128
128
|
│ │──────────────────────────│ │
|
|
@@ -135,20 +135,21 @@ import { Sheet } from "@hjmds/react-native/overlays";
|
|
|
135
135
|
|
|
136
136
|
## 꼭 지킬 것
|
|
137
137
|
|
|
138
|
-
- 하단 시트는 드래그로 내려 닫을 수 있어야 한다(필수).
|
|
138
|
+
- 하단 시트는 드래그로 내려 닫을 수 있어야 한다(필수). 시트 밖(가림막)을 누르거나 클릭해도 닫힌다(모든 위치, 미게시). ×는 하단 시트에서 기본으로 끈다. 드래그를 쓸 수 없는 사용자를 위해 손잡이 닫기 버튼, 가림막 닫기(Native는 `closeLabel` 이름의 버튼), Escape·Android back, VoiceOver 닫기 제스처는 유지한다. Web은 누름이 가림막에서 시작할 때만 닫아 시트 안에서 시작한 드래그 선택은 닫지 않는다. `outsideDismiss: false`·`swipeDismiss: false`는 결제 확인처럼 실수로 닫히면 안 되는 흐름에만 쓰고 이유를 제품 코드에 남긴다.
|
|
139
|
+
- 저장하지 않은 입력을 버릴지 묻는 흐름은 제어형 `onOpenChange`에서 처리한다. 가림막(`outside`)·드래그(`swipe`)·Escape·back·손잡이 모두 같은 경로로 들어오므로 확인을 건너뛰지 않는다. 저장 중 `busy`는 모든 사용자 닫기를 막는다.
|
|
139
140
|
|
|
140
141
|
- 입력 폼 Native Sheet는 `keyboardAvoidance`와 `scrollable`을 함께 켜고, 제품의 키보드 listener·maxHeight·
|
|
141
142
|
중첩 ScrollView를 지운다. 본문이 FlatList 등 가상화 목록이면 `scrollable={false}`로 둔다.
|
|
142
143
|
- 저장 버튼은 `footer`에 둔다. 저장 중에는 `busy`로 닫기를 막는다.
|
|
143
144
|
- 닫힌 뒤 다른 modal을 열거나 화면 이동은 `onDismissComplete`에서 한다. 두 modal을 동시에 띄우지 않는다.
|
|
144
|
-
- 문구(`title`, `closeLabel`, 본문)는 i18n 키로 넣는다.
|
|
145
|
+
- 문구(`title`, `closeLabel`, 본문)는 i18n 키로 넣는다. 보이지 않는 `title`도 스크린 리더가 읽으므로 번역한다. `as unknown as string` 캐스트나
|
|
145
146
|
`.hjm-sheet__header` CSS 덮어쓰기를 하지 않는다.
|
|
146
147
|
- Native `contentStyle`은 배치 key만 쓴다. 높이는 `size`로 정한다(`contentStyle={{ height }}` 금지, 배치 밖 key는 지원하지 않음).
|
|
147
148
|
- Web Sheet는 `layoutStyle`을 받지 않는다(Web `layoutStyle` 제외 15개 중 하나). 표면 위치·크기는 `placement`·`size`가 정한다.
|
|
148
149
|
|
|
149
150
|
## 플랫폼 차이
|
|
150
151
|
|
|
151
|
-
Flutter 제품은 `showModalBottomSheet` 대신 `showHjmBottomSheet`(`hjm_flutter`)로 연다. 같은 기본값(드래그 닫기·손잡이 표시·배경 탭 닫기 끔·× 없음)을 적용하고 본문은 `HjmSheetLayout`을 쓴다(미게시).
|
|
152
|
+
Flutter 제품은 `showModalBottomSheet` 대신 `showHjmBottomSheet`(`hjm_flutter`)로 연다. 같은 기본값(드래그 닫기·손잡이 표시·배경 탭 닫기 끔·× 없음)을 적용하고 본문은 `HjmSheetLayout`을 쓴다(미게시). 2026-10-17의 제목 숨김·가림막 닫기 결정은 아직 Flutter에 반영하지 않았다.
|
|
152
153
|
|
|
153
154
|
| 항목 | Web | Native |
|
|
154
155
|
| --- | --- | --- |
|
|
@@ -158,12 +159,12 @@ Flutter 제품은 `showModalBottomSheet` 대신 `showHjmBottomSheet`(`hjm_flutte
|
|
|
158
159
|
| 고정 높이 본문 채우기 | 항상 `flex: 1 1 auto` | `size`가 `auto`가 아니거나 옆 시트일 때 `flexGrow: 1`(1.13.1부터). `scrollable`이면 ScrollView 자체만 늘고 그 안 내용은 내용 높이다 |
|
|
159
160
|
| 초점 | `initialFocusRef`, `returnFocusRef`, focus trap | `returnFocusRef` |
|
|
160
161
|
| 겹침 순서 | `modalPriority`, `portalContainer` | RN `Modal` props(`testID` 등) |
|
|
161
|
-
| `title` 타입 | `ReactNode` | `string` 또는 element + `accessibilityTitle` |
|
|
162
|
+
| `title` 타입(보이지 않음) | `ReactNode`(시각 숨김 `h2` 안) | `string` 또는 element + `accessibilityTitle`(element는 그리지 않음) |
|
|
162
163
|
|
|
163
164
|
### GestureSheet(Native optional)
|
|
164
165
|
|
|
165
166
|
- controlled `open`/`onOpenChange(open)`만 있다. `title`·`closeLabel`·`children` 필수, `snapPoints` 기본 `["50%", "90%"]`,
|
|
166
|
-
`initialIndex` 기본 `0`, `busy`면 끌어 닫기·backdrop 닫기를 막는다. 닫기 버튼이 본문 끝에 자동으로 들어간다.
|
|
167
|
+
`initialIndex` 기본 `0`, backdrop을 누르면 닫히고 `busy`면 끌어 닫기·backdrop 닫기를 막는다. `title`은 보이지 않는 머리글·접근성 이름이다(미게시). 닫기 버튼이 본문 끝에 자동으로 들어간다.
|
|
167
168
|
- 화면을 `GestureHandlerRootView` 안의 `GestureSheetProvider`로 감싼다. 입력은 `GestureSheetInput`을 쓴다.
|
|
168
169
|
- RN `Modal` 안에 GestureSheet를 두면 Android back이 `onRequestClose`로만 간다. 그 host는
|
|
169
170
|
`dismissTopGestureSheet()`를 먼저 부르고 `false`일 때만 자기 자신을 닫는다.
|
|
@@ -180,6 +181,9 @@ Flutter 제품은 `showModalBottomSheet` 대신 `showHjmBottomSheet`(`hjm_flutte
|
|
|
180
181
|
|
|
181
182
|
## 근거
|
|
182
183
|
|
|
184
|
+
- 시트에 보이는 제목 없음: 2026-10-17 사용자 결정 — 시트 위쪽 제목 줄(예: SearchScreen 필터 시트의 "필터")을 모든 시트에서 없앤다. `title`은 대화상자 접근성 이름으로 남는다. Native Select·Combobox 선택 시트와 GestureSheet도 같다. 같은 작업에서 머리가 손잡이 영역(`.hjm-sheet__drag`, 2026-10-16) 안으로 들어가 `.hjm-sheet > .hjm-sheet__header` 선택자가 맞지 않아 Dialog 위 여백 20이 섞이던 Web 결함을 고쳤다.
|
|
185
|
+
- 가림막 누르면 닫힘: 2026-10-17 사용자 결정 — 하단 시트도 시트 밖을 누르면 닫힌다(2026-10-16의 드래그 전용 기본을 대체). 닫기 경로는 손잡이·드래그와 같은 `requestClose`라 `busy`·`dismissPolicy`·제어형 owner의 확인을 그대로 따른다.
|
|
186
|
+
- 스크롤 영역을 시트 여백 끝까지: 2026-10-17 1.27.2 채택 점검. utilverse 테마·언어 시트(Native `scrollable`)에서 여백 20이 ScrollView 바깥에 있어 ListRow 선택 표면의 양옆이 잘렸다(3x 측정: 선택 테두리 옆 선 없음, 위 선 21–381pt). Web은 본문 스크롤 상자가 이미 여백을 안쪽에 두어 잘림 0(402·1280, LTR·RTL)이었고 Native를 같은 구조로 맞췄다(수정 후 옆 선 13pt·끝 13pt).
|
|
183
187
|
- Sheet가 제목 정렬·키보드·top inset·maxHeight 보정 소유: 2026-09-23 소비 감사에서 제품마다 Sheet를 직접 조립하며 이 값을 따로 보정하고 있었다.
|
|
184
188
|
- Native `keyboardAvoidance` 기본 true: 2026-10-16 키보드 전수 조사 — 입력 시트가 옵션을 빠뜨려 키보드에 가려졌다.
|
|
185
189
|
- 디자인 프로필 상속(radius·floating shadow): 2026-10-07 테마 소비 경로 점검에서 고정 foundation/recipe 값이 남은 곳을 보완했다.
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
- 상태: 배포
|
|
5
5
|
- 지원: Web · Native
|
|
6
6
|
- 적용: 1.12.1
|
|
7
|
-
- 검토일: 2026-10-
|
|
8
|
-
- 근거: `src/component-recipes.ts`(`skeletonRecipe`), `src/foundations.ts`(`glyph`)
|
|
7
|
+
- 검토일: 2026-10-17
|
|
8
|
+
- 근거: `src/component-recipes.ts`(`skeletonRecipe`), `src/semantic-colors.ts`(`surface.placeholder`), `src/foundations.ts`(`glyph`·`control`)
|
|
9
9
|
- 스토리북: `배포/컴포넌트/상태와 알림/스켈레톤`
|
|
10
10
|
|
|
11
11
|
## 언제 쓰나
|
|
@@ -37,27 +37,38 @@
|
|
|
37
37
|
// Web
|
|
38
38
|
import { Skeleton } from "@hjmds/react/feedback";
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
<
|
|
43
|
-
|
|
40
|
+
// 영역에서 한 번만 알린다. Skeleton 자체는 aria-hidden이다.
|
|
41
|
+
<Stack gap="sm" role="status" aria-label={t("profile.loading")} aria-busy="true">
|
|
42
|
+
<Stack axis="inline" gap="sm" align="center">
|
|
43
|
+
<Skeleton shape="circle" />
|
|
44
|
+
<Skeleton shape="text" lines={2} layoutStyle={{ flex: 1 }} />
|
|
45
|
+
</Stack>
|
|
46
|
+
<Skeleton shape="media" />
|
|
47
|
+
</Stack>
|
|
44
48
|
```
|
|
45
49
|
|
|
46
50
|
```tsx
|
|
47
51
|
// Native
|
|
48
52
|
import { Skeleton } from "@hjmds/react-native/feedback";
|
|
49
53
|
|
|
50
|
-
|
|
51
|
-
<
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
<Stack gap="sm" accessible accessibilityLabel={t("profile.loading")} accessibilityState={{ busy: true }}>
|
|
55
|
+
<Stack axis="inline" gap="sm" align="center">
|
|
56
|
+
<Skeleton shape="circle" />
|
|
57
|
+
<Skeleton shape="text" lines={2} layoutStyle={{ flex: 1 }} />
|
|
58
|
+
</Stack>
|
|
59
|
+
<Skeleton shape="media" />
|
|
60
|
+
</Stack>
|
|
54
61
|
```
|
|
55
62
|
|
|
63
|
+
목록 행 하나를 대신할 때는 Skeleton을 조립하지 말고 [ListRow](list-row.md)의 `loading`을 쓴다. 행 높이·슬롯
|
|
64
|
+
배치를 그대로 유지하고 같은 채움색을 쓴다.
|
|
65
|
+
|
|
56
66
|
## 축과 기본값
|
|
57
67
|
|
|
58
68
|
| prop | 값 | 기본값 | 설명 |
|
|
59
69
|
| --- | --- | --- | --- |
|
|
60
|
-
| `shape` | `block`(높이 `spacing.xxl
|
|
70
|
+
| `shape` | `block`(높이 `spacing.xxl` 32·radius `md`) · `text`(`spacing.md` 16·`sm`) · `circle`(`glyph.xxl` 44 지름·`full`) · `media`(폭 100%·16:9·`md`) · `button`(`control.buttonHeight.medium` 44·`md`) | `block` | 모양과 기본 크기. `media`·`button`은 2026-10-17 추가 |
|
|
71
|
+
| `lines` | 양의 정수 | `1` | `text` 전용. 줄 수만큼 그리고 마지막 줄은 60%(`skeletonRecipe.lines.lastLineWidthPercent`), 줄 간격 `spacing.xs` 8. 펄스는 묶음 하나로 돈다. `width`는 묶음 전체 폭 |
|
|
61
72
|
| `animated` | `boolean` | `true`(펄스) | 0.9.12까지는 `false`였다. 정지 블록이 필요하면 `animated={false}`를 명시한다. reduced motion이면 두 renderer 모두 펄스를 멈추고 불투명한 끝 값으로 그린다(recipe `reducedMotion: "static"`) |
|
|
62
73
|
| `width` | Web `string \| number` · Native `ViewStyle["width"]` | 폭 100%(circle은 지름) | — |
|
|
63
74
|
| `height` | Web `string \| number` · Native `number` | shape 기본값 | — |
|
|
@@ -80,9 +91,10 @@ import { Skeleton } from "@hjmds/react-native/feedback";
|
|
|
80
91
|
|
|
81
92
|
| 항목 | 값 | 근거 |
|
|
82
93
|
| --- | --- | --- |
|
|
83
|
-
| 크기 | `block` 폭 100% · 높이
|
|
94
|
+
| 크기 | `block` 폭 100% · 높이 32, `text` 높이 16, `circle` 지름 44, `media` 폭 100% · 16:9, `button` 높이 44. 여러 줄 본문은 `lines`로(마지막 줄 60%) | `skeletonRecipe.shapes`·`.lines`, `.hjm-skeleton` |
|
|
95
|
+
| 색 | `semanticColors.surface.placeholder`(`text` 12% 투명도) 채움, 테두리 없음. 놓인 면(canvas·Card·프로필 테마)을 기준으로 진해진다 | `skeletonRecipe.background`, `--hjm-skeleton-background` |
|
|
84
96
|
| 간격 | 실제 콘텐츠의 간격 토큰을 그대로 쓴다(예: [Stack](stack.md) `spacing.xs` 8). Skeleton에 margin을 넣지 않는다 | — |
|
|
85
|
-
| 순서·정렬 | 로드 뒤 실제 콘텐츠가 놓일 자리에 같은 순서로.
|
|
97
|
+
| 순서·정렬 | 로드 뒤 실제 콘텐츠가 놓일 자리에 같은 순서로. 목록 행은 ListRow `loading`, 카드는 `media` + `text lines={2}` + `button` | — |
|
|
86
98
|
| 고정·스크롤 | 스크롤 영역 안의 콘텐츠만 대신한다. 고정 바(TopBar·BottomCTA)는 그대로 둔다 | — |
|
|
87
99
|
| 좁은 폭·큰 글자 | `block`·`text`는 폭 100%라 컨테이너를 따른다. 높이는 글자 크기와 무관한 고정값이다 | `.hjm-skeleton` |
|
|
88
100
|
|
|
@@ -93,13 +105,13 @@ import { Skeleton } from "@hjmds/react-native/feedback";
|
|
|
93
105
|
Native `style`은 제거된 속성이다.
|
|
94
106
|
- Web Skeleton은 `aria-hidden`이다. 로딩 사실을 알려야 하면 영역 단위로 한 번 알린다
|
|
95
107
|
(예: 감싼 영역의 `aria-busy`, 또는 Spinner 하나). Skeleton마다 라벨을 붙이지 않는다.
|
|
96
|
-
- Web은 `HjmProvider` 안에서 렌더한다.
|
|
108
|
+
- Web은 `HjmProvider` 안에서 렌더한다. 채움색·원 지름·버튼 높이·media 비율·줄 간격·펄스 값을 provider가 내보내는 `--hjm-skeleton-*` 변수로 읽는다.
|
|
97
109
|
|
|
98
110
|
## 플랫폼 차이
|
|
99
111
|
|
|
100
112
|
| 항목 | Web | Native |
|
|
101
113
|
| --- | --- | --- |
|
|
102
|
-
| 접근성 | 항상 `aria-hidden` | `accessibilityLabel`을 주면 `busy` 상태로 읽힘, 없으면 숨김 |
|
|
114
|
+
| 접근성 | 항상 `aria-hidden` | `accessibilityLabel`을 주면 `busy` 상태로 읽힘, 없으면 숨김(Android `no-hide-descendants`) |
|
|
103
115
|
| 높이 | `string \| number` | `number`만 |
|
|
104
116
|
| 모서리 직접 지정 | 없음 | `radius`(0.9 호환용, `shape`보다 우선) |
|
|
105
117
|
| reduced motion 판단 | `prefers-reduced-motion`·`.hjm-root[data-motion="reduced"]` | provider `environment.reducedMotion` |
|
|
@@ -112,3 +124,17 @@ import { Skeleton } from "@hjmds/react-native/feedback";
|
|
|
112
124
|
`{...(first ? { accessibilityLabel: t("…") } : {})}`처럼 조건부 spread로 넘긴다.
|
|
113
125
|
- 현재 Web `style`은 `width`·`height` prop 값 뒤에 펼쳐져 크기·배경을 덮을 수 있다(타입이 막지 않는다). 크기는 `width`·`height`,
|
|
114
126
|
배치는 `layoutStyle`로 준다.
|
|
127
|
+
|
|
128
|
+
## 근거
|
|
129
|
+
|
|
130
|
+
- 2026-10-17 사용자 요청("스켈레톤 유아이 개선해줘"): Native 스토리가 흰 바탕 위 옅은 테두리 막대 하나로만 보였다.
|
|
131
|
+
원인은 `skeletonRecipe.background`가 화면 바탕과 같은 `semanticColors.canvas`였고, 두 renderer가 그 위에 1px
|
|
132
|
+
테두리를 직접 그린 것이다. 2026-09-29 "넓은 회색·연파랑 면은 canvas + 테두리" 방향을 Skeleton에도 적용했지만,
|
|
133
|
+
자리표시는 채운 면이어야 모양이 읽히므로 Skeleton·ListRow `loading`을 그 방향의 예외로 둔다.
|
|
134
|
+
- 채움은 불투명 `surfaceAlt` 대신 `text` 12% 투명도다. 대비(바탕 대비, 펄스 최고점): 기본 밝은 테마 약 1.27:1(펄스 최저점 1.14),
|
|
135
|
+
어두운 테마 약 1.36:1(1.16). 불투명 `surfaceAlt`는 1.23 / 1.20, Card 면(`surface`) 위에서는 1.12 / 1.10이었고, `surfaceAlt`가 `bg`만큼 밝은
|
|
136
|
+
프로필 팔레트에서는 거의 보이지 않았다. 장식 요소라 텍스트 대비 기준은 적용하지 않는다.
|
|
137
|
+
- `media`·`button`·`lines`는 카드·본문 자리표시를 제품마다 막대로 다시 조립하던 일을 줄이려고 추가했다. 목록 행은
|
|
138
|
+
기존 ListRow `loading`이 맡으므로 별도 조합 컴포넌트는 두지 않는다.
|
|
139
|
+
- 움직임은 기존 불투명도 펄스(0.56↔1)를 유지한다. 빛 띠(shimmer)는 Native에 그라데이션 peer가 필요하고 reduced
|
|
140
|
+
motion에서는 어차피 정지해야 한다.
|
|
@@ -93,7 +93,7 @@ Native 프레임의 모서리는 `statisticRecipe.presentations`의 역할을 Pr
|
|
|
93
93
|
| 항목 | 값 | 근거 |
|
|
94
94
|
| --- | --- | --- |
|
|
95
95
|
| 크기 | `presentation="surface"`의 안쪽 여백: `comfortable` `spacing.md` 16 · `compact` `spacing.sm` 12(두 플랫폼). 테두리 `stroke.default` 1, radius `md` 12 | `statisticRecipe.density`·`presentations`, `styles.css` `.hjm-statistic[data-presentation="surface"]` |
|
|
96
|
-
| 글자 | 라벨 `label` 12
|
|
96
|
+
| 글자 | 라벨 `label` 12 medium(`compact` `caption` 11), 값 `heading` 24 semibold(`fontWeight.semibold`, `compact` `title` 18. 2026-10-17 큰 글자 단계 폐지로 `heading.level2` 굵기 참조를 끊음), prefix·suffix `body` 14 medium 본문색, hint `caption`, 추세 `caption` medium(두 플랫폼). 2026-10-06까지 Web은 라벨 본문 14 medium, 값은 밀도와 관계없이 18, prefix·suffix는 흐린색이었다(1.13.0 변경) | `statisticRecipe.density`·`label`·`value`·`affix`·`hint`·`trend`, `.hjm-statistic__*` |
|
|
97
97
|
| 간격 | 그룹 간격 `statisticRecipe.group.gap` = `spacing.xs` 8. 지표 안 줄 간격(라벨·값·추세) `comfortable` `spacing.xs` 8 · `compact` `spacing.xxs` 4(두 플랫폼) | `styles.css` `.hjm-statistic`·`.hjm-statistic-group`, `statisticRecipe` |
|
|
98
98
|
| 순서·정렬 | 화면 위쪽 요약 영역이나 카드 안에 둔다. 지표 여러 개는 [Grid](grid.md)를 직접 짜지 말고 `StatisticGroup`으로 묶는다 | — |
|
|
99
99
|
| 고정·스크롤 | — | — |
|
|
@@ -135,3 +135,7 @@ Native 프레임의 모서리는 `statisticRecipe.presentations`의 역할을 Pr
|
|
|
135
135
|
양쪽 움직이는 수치 Storybook에 소수와 음수(de-DE), 비라틴 숫자(ar-EG), 지수 표기(en-US),
|
|
136
136
|
동작 줄이기와 RTL 비교를 제공한다. 소수·음수 예제는 기록 개수가 아닌 측정값이다.
|
|
137
137
|
Web은 자리 단위 전환, Native는 지표 전체 전환이며 같은 시각 효과를 보장하지 않는다.
|
|
138
|
+
|
|
139
|
+
## 근거
|
|
140
|
+
|
|
141
|
+
2026-10-17 사용자 결정("굵기 좀더 줄여"): 모든 글자 굵기를 한 단계 낮췄다. 큰 숫자·선택 상태 600(큰 제목 단계는 같은 날 폐지), 제목·강조 500, label 500, 본문 400이며 정본은 [타이포그래피](../tokens/typography.md)다.
|
|
@@ -77,8 +77,8 @@ import { Switch } from "@hjmds/react-native/inputs";
|
|
|
77
77
|
|
|
78
78
|
| 항목 | 값 | 근거 |
|
|
79
79
|
| --- | --- | --- |
|
|
80
|
-
| 크기 | 트랙 `small` 44×26(thumb 22) · `medium` 52×32(thumb 28), 안쪽 여백 2. iOS Native는 `UISwitch` 고유 크기. 루트 최소 높이 44(`control.minTouchTarget`). 행 높이는 설명이 없으면 44
|
|
81
|
-
| 간격 | 이름과 트랙 사이 `spacing.sm` 12, 이름과 설명 사이 `spacing.xxs` 4. 여러 개를 쌓을 때는 [List](list.md)
|
|
80
|
+
| 크기 | 트랙 `small` 44×26(thumb 22) · `medium` 52×32(thumb 28), 안쪽 여백 2. iOS Native는 `UISwitch` 고유 크기. 루트 최소 높이 44(`control.minTouchTarget`). 행 높이는 설명이 없으면 44. 설명이 보이는 행은 위아래 안쪽 여백 8(`rowPaddingVertical`, ListRow comfortable과 같다)에 최소 56(`rowTwoLineMinHeight` = 이름 20 + 4 + 설명 16 + 8×2). Web·Native 같다. `labelVisibility="hidden"`이면 설명이 있어도 44 | `switchRecipe.sizes`·`rowMinHeight`·`rowPaddingVertical`·`rowTwoLineMinHeight`, `styles.css` `.hjm-switch[data-two-line]`, `react-native/src/inputs.tsx` |
|
|
81
|
+
| 간격 | 이름과 트랙 사이 `spacing.sm` 12, 이름과 설명 사이 `spacing.xxs` 4. 여러 개를 쌓을 때는 [List](list.md)(`separator`로 구분선 선택)에 넣고 Switch 사이에 Stack gap 같은 여백을 더하지 않는다. 행 간격은 행의 최소 높이·안쪽 여백이 맡는다(설명 있는 행 간격 56) | `styles.css` `.hjm-switch`·`.hjm-switch__copy`, `react-native/src/inputs.tsx` |
|
|
82
82
|
| 순서·정렬 | 설정 목록에서는 `presentation="row"`로 행 전체 폭을 쓰고 이름·설명이 앞(왼쪽), 트랙이 끝(오른쪽) | `styles.css` `.hjm-switch[data-presentation="row"]` |
|
|
83
83
|
| 고정·스크롤 | — | — |
|
|
84
84
|
| 좁은 폭·큰 글자 | 글자 배율 ≥ 1.6(`largeTextThreshold`)이면 `row`는 이름·설명 아래로 트랙을 내린다 | `switchRecipe.stackedTextScale`, `styles.css` `.hjm-root[data-large-text="true"]` |
|
|
@@ -130,3 +130,4 @@ presentation="row" 큰 글자(≥1.6)
|
|
|
130
130
|
## 근거
|
|
131
131
|
|
|
132
132
|
- 기본 `row` 통일: 2026-10-17 웹·앱 비교 도구에서 같은 설정 스위치가 Web은 트랙 먼저(inline), Native는 이름 먼저(row)로 거울처럼 그려졌다.
|
|
133
|
+
- 설명 있는 행 56·여백 8, 쌓을 때 gap 없음: 2026-10-17 사용자 보고(알림 설정 "간격이 커") — 글자 40에 ListRow 두 줄 68을 빌린 최소 높이와 미리보기의 Stack gap 16이 겹쳐 행 간격이 84였다.
|
|
@@ -55,7 +55,7 @@ import { Tag } from "@hjmds/react-native/data-display";
|
|
|
55
55
|
| `accessibilityLabel`(Native) | `string` | 라벨 | — |
|
|
56
56
|
| `style`·`labelStyle`(Native) | 지원하지 않음 | — | 제거된 속성 — `layoutStyle` 또는 `tone`. 현재 공개 props에서 제거되었다 |
|
|
57
57
|
|
|
58
|
-
모양은 radius `sm` 사각형, 최소 높이 20(큰 글자에서 늘어남), caption 크기
|
|
58
|
+
모양은 radius `sm` 사각형, 최소 높이 20(큰 글자에서 늘어남), caption 크기 medium(500) 글자다.
|
|
59
59
|
|
|
60
60
|
## 배치
|
|
61
61
|
|
|
@@ -84,3 +84,7 @@ Native의 모서리는 Provider의 `tokens.radius.sm`이다. metadata 의미와
|
|
|
84
84
|
| 추가 style prop | `className`·`style`(HTML 속성) | `style`·`labelStyle`(제거된 속성) |
|
|
85
85
|
| ref | `forwardRef`(`span`) | 없음 |
|
|
86
86
|
| import 경로 | `/display` | `/data-display` |
|
|
87
|
+
|
|
88
|
+
## 근거
|
|
89
|
+
|
|
90
|
+
2026-10-17 사용자 결정("굵기 좀더 줄여"): 모든 글자 굵기를 한 단계 낮췄다. 큰 숫자·선택 상태 600(큰 제목 단계는 같은 날 폐지), 제목·강조 500, label 500, 본문 400이며 정본은 [타이포그래피](../tokens/typography.md)다.
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
| 상황 | 대신 쓸 것 |
|
|
19
19
|
| --- | --- |
|
|
20
20
|
| 짧은 상태·결과 문구를 변경 알림으로 전달 | [StatusText](#statustext) |
|
|
21
|
-
| 문서 제목 단계(`h1`~)가
|
|
21
|
+
| 문서 제목 단계(`h1`~)가 필요한 제목(크기는 Text와 같은 단계, 최대 24px) | [Heading](heading.md) |
|
|
22
22
|
| 화면 첫 자리의 제목 블록(보조 문장·행동 포함) | [Top](top.md) |
|
|
23
23
|
| 본문 묶음의 헤더 행 | [Section](section.md) |
|
|
24
24
|
| 숫자 지표와 그 라벨 | [Statistic](statistic.md) |
|
|
@@ -61,7 +61,7 @@ import { Text } from "@hjmds/react-native/primitives";
|
|
|
61
61
|
| --- | --- | --- | --- |
|
|
62
62
|
| `variant` | `caption`(11) · `label`(12) · `body`(14) · `bodyLarge`(16) · `title`(18) · `titleLarge`(20) · `heading`(24) | `body` | 화면·섹션 제목은 모양이 아니라 [Heading](heading.md)으로 쓴다 |
|
|
63
63
|
| `fontRole` | `ui` · `display` · `reading` · `code` | 제목 variant는 display, 본문은 reading, caption/label은 ui | 명시값이 기본을 덮는다. [역할 지침](../tokens/font-roles.md), 1.16.0 변경 |
|
|
64
|
-
| `emphasis` | `regular` · `medium`
|
|
64
|
+
| `emphasis` | `regular` 400 · `medium` 500 · `strong` 500(제목 굵기) | 프로필이 있으면 variant 굵기, 없으면 `regular`. `label`은 프로필과 무관하게 variant 굵기 500 | 명시값이 프로필과 variant보다 우선 |
|
|
65
65
|
| `tone` | `primary` · `body` · `muted` · `subtle` · `weak` · `brand` · `danger` · `inverse` | `primary` | — |
|
|
66
66
|
| `as` | `span` · `p` · `div` · `strong` · `small` | `span` | Web만. 문단은 `as="p"` |
|
|
67
67
|
| `align` | `TextStyle["textAlign"]` | 쓰기 방향의 시작 쪽 | Native만 |
|
|
@@ -103,13 +103,13 @@ Native는 Text의 역할을 해당 stack에, 입력/조작 host는 제품 프로
|
|
|
103
103
|
## 함정
|
|
104
104
|
|
|
105
105
|
- `emphasis`를 명시하면 `variant`의 굵기를 덮는다. `designProfile`이 있으면 생략한 `emphasis`는 해당 프로필의 `tokens.typography[variant].fontWeight`를 따른다. 프로필이 없으면 기존 기본값 `regular`를 유지하므로 `variant="title"`도 보통 굵기로 그려진다. 그 경우 굵은 제목은 `emphasis="strong"`으로 지정한다(두 renderer 동일).
|
|
106
|
-
- `variant="label"`은 예외로, 프로필이 없어도 생략한 `emphasis`가 `typography.label` 굵기 500(medium)으로 그려진다.
|
|
106
|
+
- `variant="label"`은 예외로, 프로필이 없어도 생략한 `emphasis`가 `typography.label` 굵기 500(medium)으로 그려진다. label을 400으로 그리려면 `emphasis="regular"`를 명시한다.
|
|
107
107
|
Web은 이 기본 굵기를 inline `font-weight`로 주며 `data-emphasis`는 `regular`로 남는다. Native는 `fonts="pretendard"`일 때 `Pretendard-Medium` face로 그린다.
|
|
108
108
|
|
|
109
109
|
|
|
110
110
|
## 근거
|
|
111
111
|
|
|
112
|
-
2026-10-17 사용자 결정: 폼 라벨·섹션 부제목이 본문 대비 약해 `label` 기본 굵기를 500으로 정했다(본문 400과 제목 600
|
|
112
|
+
2026-10-17 사용자 결정: 폼 라벨·섹션 부제목이 본문 대비 약해 `label` 기본 굵기를 500으로 정했다(당시 본문 400과 제목 600 사이, 제목이 500으로 내려간 뒤에도 500 유지). 이전에는 토큰이 600이었지만 프로필 없는 Text는 400으로 그렸다. 두 renderer는 `resolveTextDefaultWeight`(`src/component-recipes.ts`) 하나로 기본 굵기를 고른다.
|
|
113
113
|
|
|
114
114
|
## StatusText
|
|
115
115
|
|
|
@@ -189,3 +189,5 @@ Native Android는 `accessibilityLiveRegion`만 사용한다. iOS는 앱이 activ
|
|
|
189
189
|
동일 결과를 매 렌더 다시 알리기 위해 key를 바꾸지 않는다. Toast·Notice에서 이미 알리는 결과를 중복 알리지 않는다.
|
|
190
190
|
Showcase의 `PatternStatus`는 이 공개 컴포넌트를 가리키는 내부 별칭일 뿐이며 제품에서 Showcase 파일을 import하지 않는다.
|
|
191
191
|
DOM 속성·Native host 호출 검사는 실제 VoiceOver·TalkBack의 낭독 확인과 구분한다.
|
|
192
|
+
- 2026-10-17 사용자 결정("왜 폰트가 진해"): 제목을 600으로 내린 뒤에도 `strong`이 700이라 읽지 않은 알림 제목·라벨 25곳이 제목보다 굵었다. 강조 단계를 400·500·600으로 낮췄다.
|
|
193
|
+
- 같은 날 두 번째 결정("굵기 좀더 줄여"): 제목을 500, 큰 제목(level1·2)을 600으로 한 단계씩 더 낮췄다. `strong`은 제목 굵기를 따라 500이 되어 `medium`과 같다(`typography.title.fontWeight` 참조). 600은 큰 숫자·선택 상태에만 쓴다(큰 제목 600 단계는 같은 날 큰 글자 단계와 함께 폐지).
|
|
@@ -90,6 +90,6 @@ import { Top } from "@hjmds/react-native/top";
|
|
|
90
90
|
|
|
91
91
|
### 프로필 모서리·제목 소비
|
|
92
92
|
|
|
93
|
-
2026-10-07 소비 감사에서 두 renderer의 제목 크기·행간·굵기가 foundation으로 확정된 recipe를 직접 읽어 프로필을 우회했다. `large`는 가까운 프로필의 `heading.level2
|
|
93
|
+
2026-10-07 소비 감사에서 두 renderer의 제목 크기·행간·굵기가 foundation으로 확정된 recipe를 직접 읽어 프로필을 우회했다. `large`는 가까운 프로필의 `heading.level3`(24, 2026-10-17 큰 글자 단계 폐지 전에는 `level2` 32), `medium`은 `typography.titleLarge`를 읽고 프로필이 없으면 기존 recipe를 유지한다. 시각 역할은 `headingLevel`과 독립이며 display 서체·제목 의미·보조 행동·초점은 바꾸지 않는다.
|
|
94
94
|
|
|
95
95
|
프로필 연결은 1.16.0 변경이며, 기본 배포 계약과 실제 Native 기기 검증은 구분한다.
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
| 영역 | 컴포넌트 | 위치 | 크기·간격 |
|
|
34
34
|
| --- | --- | --- | --- |
|
|
35
35
|
| 바깥 틀 | Stack. Web은 제품 화면의 `Container size="reading"` 안 | 화면의 본문 흐름 | gap md=16px, 폭은 Container를 따른다(Web 최대 `layout.readingMaxWidth` 720) |
|
|
36
|
-
| 패널 제목 | `Heading level="level5"` | 패널 Surface 맨 위 | `title` 글자 크기·
|
|
36
|
+
| 패널 제목 | `Heading level="level5"` | 패널 Surface 맨 위 | `title` 글자 크기·medium(500) |
|
|
37
37
|
| 내용 | 해당 공개 API | 입력과 행동 사이 | 자체 recipe 크기, 줄바꿈 허용 |
|
|
38
38
|
| 행동 | Button/기존 컨트롤 | 관련 항목 바로 뒤 | inline일 때 wrap, md=16px |
|
|
39
39
|
|
|
@@ -82,3 +82,7 @@ Storybook의 기존 gutter 때문에 contentInset=none을 쓰고, 마지막 입
|
|
|
82
82
|
스크롤 경계에 걸리지 않도록 본문 끝에 spacing.md(16) 여백을 둔다.
|
|
83
83
|
본문 조합 자체에 두 번째 scroll view를 만들지 말고 제품의 기존 화면 host를 재사용한다.
|
|
84
84
|
KeyboardAvoiding은 아래 여백을 제공하며 포커스된 필드를 자동 탐색하는 API는 아니다.
|
|
85
|
+
|
|
86
|
+
## 근거
|
|
87
|
+
|
|
88
|
+
2026-10-17 사용자 결정("굵기 좀더 줄여"): 모든 글자 굵기를 한 단계 낮췄다. 큰 숫자·선택 상태 600(큰 제목 단계는 같은 날 폐지), 제목·강조 500, label 500, 본문 400이며 정본은 [타이포그래피](../tokens/typography.md)다.
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
| 소요 시간 | DurationField | 설명 아래 | NumberField 세 칸, 칸 사이 `spacing.md` 16. 세 칸이 모두 들어가면 한 줄(칸 최대 `fieldGroupRecipe.inline.maxWidth`), 아니면 세 칸 모두 세로([FieldGroup](field-group.md)) |
|
|
65
65
|
| 확인 | InlineConfirm | 트리거 자리 그대로 | 트리거와 확인 행동이 같은 자리, Button 높이 44, 질문·버튼 줄 사이와 두 버튼 사이 `spacing.sm` 12 |
|
|
66
66
|
| 반응 | ReactionPicker | 제목 아래 | `ghost` `pill` Button 묶음, 사이 `spacing.xs` 8, 넘치면 줄바꿈 |
|
|
67
|
-
| 알림 종 | NotificationBell | 블록 맨 위, 시작 쪽
|
|
67
|
+
| 알림 종 | NotificationBell | 블록 맨 위, 시작 쪽 정렬. TopBar·헤더 끝에 둘 때는 `edge="end"` | IconButton(기본 `medium` 44) + CounterBadge `floating`(끝·위 모서리). `edge`(`start`·`end`·`both`)는 IconButton `edge`와 같은 값(`resolveIconButtonEdgeOverhang`, 44−24)/2 = 10을 벨 루트에 적용해 glyph 칸이 여백 선(TopBar 16)에 오고 배지는 아이콘 모서리를 지킨다 |
|
|
68
68
|
|
|
69
69
|
## 흐름과 상태
|
|
70
70
|
|
|
@@ -197,4 +197,5 @@ function CompoundBlocks() {
|
|
|
197
197
|
|
|
198
198
|
## 근거
|
|
199
199
|
|
|
200
|
+
- NotificationBell `edge`: 2026-10-17 1.27.2 채택 점검. Spint 홈·탐색 TopBar 끝의 벨은 IconButton을 감싸지만 `edge`가 없어 glyph 칸이 바 끝에서 26에 놓였다(Web 402·1280, LTR·RTL 측정 26 → 16, Native 3x 프레임: 버튼 끝 386 기준 15.7). 넘침은 안쪽 버튼이 아니라 벨 루트에 준다. 버튼에 주면 루트가 10 줄어 끝에 붙은 배지가 아이콘에서 10 안쪽으로 밀린다(측정: 배지 끝 − 버튼 끝 0 유지).
|
|
200
201
|
- 2026-10-17 구성 리뷰: 소요 시간 칸이 데스크톱에서 늘어나고 좁은 폭에서 한 칸만 다음 줄로 끊겨 FieldGroup recipe의 inline 규칙을 바꿨다.
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
| 테마 | 밝은 모드에서 보이는 차이 |
|
|
64
64
|
| --- | --- |
|
|
65
65
|
| 미니멀 | 흰 바탕·무채색 hairline, 그림자 없음, 굵기 500 제목과 넉넉한 행간, 넓은 안쪽 여백의 테두리 카드(cards) |
|
|
66
|
-
| 에디토리얼 | 시스템 serif
|
|
66
|
+
| 에디토리얼 | 시스템 serif 제목·본문(제목 크기는 공통 단계, 2026-10-17 56/42/30px 확대 제목 폐지), 따뜻한 잉크색과 와인 강조, 각진 모서리, 규칙선으로 나눈 rows |
|
|
67
67
|
| 터미널 | 모든 글자 monospace, 모서리 0, 그림자 없음, 녹회색 바탕·짙은 녹색 행동·호박색 강조, 규칙선 rows. 어두운 모드는 검정 콘솔 |
|
|
68
68
|
|
|
69
69
|
## 흐름과 상태
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
| 컴포넌트 | 역할 | 지침 |
|
|
21
21
|
| --- | --- | --- |
|
|
22
22
|
| `Container` · `ScrollView` | 시작 화면의 바깥 틀. HJM 화면 골격이 소유한다 | [Container](../components/container.md), [화면 여백](../tokens/layout.md) |
|
|
23
|
-
| `Heading level="level3"`, `Text` | 시작 화면의 제목·설명. 화면 제목은
|
|
23
|
+
| `Heading level="level3"`, `Text` | 시작 화면의 제목·설명. 화면 제목은 medium(500) `level3`(24, HJM 글자 상한)이다 | [Heading](../components/heading.md), [Text](../components/text.md) |
|
|
24
24
|
| `Button` primary | 시트를 여는 트리거(시작 화면의 주 행동) | [Button](../components/button.md) |
|
|
25
|
-
| `Sheet` | 하단 시트.
|
|
25
|
+
| `Sheet` | 하단 시트. 접근성 이름(`title`, 보이지 않음)·닫기·`footer`, 안전 영역·스크롤·`busy` | [Sheet](../components/sheet.md) |
|
|
26
26
|
| `Steps` | 현재 단계와 전체 단계 수, 실패하면 현재 단계 `error` | [Steps](../components/steps.md) |
|
|
27
27
|
| `ContentTransition preset="slide"` | 단계 본문 교체 | [ContentTransition](../components/content-transition.md) |
|
|
28
28
|
| `Heading level="level3"`, `Text` | 단계 본문 | [Heading](../components/heading.md), [Text](../components/text.md) |
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
| 바깥 틀 | 시작 화면: Web 문서 스크롤 > `Container size="reading"`, Native `ScrollView` > `Container size="reading"`. 시트: `Sheet`가 Modal로 화면 위에 뜨며 스크롤·안전 영역·키보드를 소유 | 시작 화면은 제품 화면 본문. 상단 안전 영역은 헤더(또는 [TopBar](../components/top-bar.md))가 맡는다 | 좌우 `Container gutter`: 폭 600 미만 `compact` 16, 이상 `regular` 20([화면 여백](../tokens/layout.md)). Native 위아래 `contentContainerStyle` `paddingVertical: spacing.lg` 20. 시트 안 inset은 제품이 다시 더하지 않는다 |
|
|
58
58
|
| 시작 화면 | `Stack gap="xl"` | 본문 흐름 안 | 자식 사이 `spacing.xl` 24 |
|
|
59
59
|
| 시트 표면 | `Sheet placement="bottom" size="auto"`(기본값) | 화면 아래에서 올라옴, 내용 높이 | radius `xl` 24, 최대 높이 90%. 좌우 `spacing.lg` 20·위아래 `spacing.sm` 12(두 플랫폼, `sheetRecipe.content`), Web 최대 폭 640 |
|
|
60
|
-
| 시트 머리 | Sheet `title
|
|
60
|
+
| 시트 머리 | Sheet 손잡이, `title`은 보이지 않는 접근성 이름 | 시트 맨 위 고정 | 보이는 제목 줄 없음(2026-10-17 사용자 결정: 시트는 보이는 제목 없이 `title`을 접근성 이름으로만 쓴다) |
|
|
61
61
|
| 단계 표시 | `Steps` | 본문 맨 위 | 항상 가로 한 줄, 아래 `spacing.xl` 24 |
|
|
62
62
|
| 단계 본문 | `ContentTransition` > `Stack gap="md"` | Steps 아래, 본문 스크롤 | 제목·설명 사이 `spacing.md` 16, `slide` 가로 16 이동 |
|
|
63
63
|
| 행동 | Sheet `footer` + Button ×2 | 시트 아래 고정(스크롤 밖) | Web 오른쪽 정렬 가로 [이전 ghost][계속 primary], 사이 `spacing.sm` 12. Native 세로 열 꽉 찬 폭 [계속]→[이전], 사이 `spacing.sm` 12, 위 `spacing.sm` 12. 높이 44 |
|
|
@@ -209,4 +209,6 @@ import { Steps } from "@hjmds/react-native/steps";
|
|
|
209
209
|
|
|
210
210
|
## 근거
|
|
211
211
|
|
|
212
|
+
2026-10-17 사용자 결정("굵기 좀더 줄여"): 모든 글자 굵기를 한 단계 낮췄다. 큰 숫자·선택 상태 600(큰 제목 단계는 같은 날 폐지), 제목·강조 500, label 500, 본문 400이며 정본은 [타이포그래피](../tokens/typography.md)다.
|
|
213
|
+
|
|
212
214
|
- 2026-10-17 구성 리뷰: 시작 화면 제목이 heavy 800 `level2`로 화면 제목 규칙(semibold)보다 무거웠고, 데스크톱에서 버튼과 문구가 1280 폭 전체로 늘어났다. 그래서 제목을 `level3`으로 낮추고 스토리에 읽기 폭 Container를 적용했다.
|
|
@@ -51,7 +51,7 @@ Native(하단 시트, 키보드 올라옴) Web(하단 시트)
|
|
|
51
51
|
| --- | --- | --- | --- |
|
|
52
52
|
| 바깥 틀 | 시트 안: `Sheet`(Modal)가 backdrop·본문 스크롤(Native `scrollable`)·키보드(Native `keyboardAvoidance`)·안전 영역을 소유. 트리거: 제품 화면의 바깥 틀(Web 문서 스크롤 > `Container`, Native `ScrollView` > `Container`) | 시트는 화면 위 오버레이, 트리거는 화면 본문 | 시트 안 여백은 아래 행들(Sheet 소유, 제품이 inset·키보드 여백을 다시 더하지 않는다). 트리거 화면 좌우는 `Container gutter` 폭 600 미만 `compact` 16 · 이상 `regular` 20([화면 여백](../tokens/layout.md)) |
|
|
53
53
|
| 시트 틀 | `Sheet` placement `bottom` | 화면 하단에 붙고 위 모서리만 둥글다 | radius `xl` 24, 최대 높이 `maxHeightRatio` 0.9(뷰포트의 90%), Web 최대 폭 640 |
|
|
54
|
-
| 머리 |
|
|
54
|
+
| 머리 | 손잡이(+ `showClose`일 때 끝 × IconButton). 보이는 제목 없음, `title`은 접근성 이름(2026-10-17 사용자 결정: 시트는 보이는 제목 없이 `title`을 접근성 이름으로만 쓴다) | 시트 맨 위, 고정 | × 머리 최소 높이 `control.minTouchTarget` 44·좌우 `spacing.lg` 20(+좌우 inset)·위 `spacing.sm` 12(두 플랫폼 같음) |
|
|
55
55
|
| 실패 알림 | `Notice tone="danger"` | 본문 맨 위(입력 위), 실패했을 때만, 스크롤 | 본문 항목 사이 `spacing.md` 16 |
|
|
56
56
|
| 본문 | `TextField` + 설명 | 머리 아래, **스크롤** | 항목 사이 `sheetRecipe.body.gap` `spacing.md` 16. Web body padding `spacing.lg` 20 |
|
|
57
57
|
| 완료 | footer `Button`(primary) | 본문 아래, **고정**(스크롤 밖) | 높이 `medium` 44. Native 위 `spacing.sm` 12, footer 세로 열이라 꽉 찬 폭(`fullWidth` 불필요). Web 오른쪽 정렬, 버튼 사이 `spacing.sm` 12, padding 0 `spacing.lg` 20 `spacing.lg` 20 |
|
|
@@ -64,8 +64,8 @@
|
|
|
64
64
|
| 제목 | `Heading level="level3"` | 묶음 맨 위 | 24/32(`heading.level3`), 묶음 안 간격 `layout.contentGap` 16(`Stack gap="md"`) |
|
|
65
65
|
| 현재 값 | `Text` | 제목 아래, 트리거 위 | `spacing.md` 16 |
|
|
66
66
|
| 트리거 | `Button`(primary) | 현재 값 아래 | `medium` 44(`control.buttonHeight`) |
|
|
67
|
-
| 시트 머리 | `Sheet`
|
|
68
|
-
| 선택지 | `RadioGroup`(`presentation="card"`, `accessibilityLabel` = 시트
|
|
67
|
+
| 시트 머리 | `Sheet` 손잡이(+ `showClose`일 때 끝 ×) | 시트 위 고정 | 보이는 제목 없음, `title`은 접근성 이름(2026-10-17 사용자 결정: 시트는 보이는 제목 없이 `title`을 접근성 이름으로만 쓴다). × 머리 최소 높이 `control.minTouchTarget` 44 |
|
|
68
|
+
| 선택지 | `RadioGroup`(`presentation="card"`, `accessibilityLabel` = 시트 `title`) | 시트 body, 스크롤 | 행 최소 높이 56(`layout.rowHeight.singleLine`), 항목 사이 `spacing.xs` 8(`selectionGroupRecipe`). 안내 문구와는 `Stack gap="md"` 16. 설치한 버전의 Sheet body gap과 중첩 여부를 확인하며 checkout 상태를 npm 게시 증거로 삼지 않는다 |
|
|
69
69
|
| 행동 | `Button` 적용·취소 | 시트 footer, 스크롤 밖 고정 | 간격 `spacing.sm` 12(`sheetRecipe.footer.gap`), Native 위 여백 `spacing.sm` 12 |
|
|
70
70
|
| 시트 틀 | `Sheet` | 하단, 모달 | Web 최대 폭 640(`sheetRecipe.web.maxWidth`)·최대 높이 90%, Native 가로 여백 `spacing.lg` 20·최대 높이 90%(`maxHeightRatio` 0.9), 하단 안전 영역 추가 |
|
|
71
71
|
|
|
@@ -199,7 +199,7 @@ Web `gutter`는 [화면 여백과 너비](../tokens/layout.md)처럼 폭 구간
|
|
|
199
199
|
| 항목 | Web | Native |
|
|
200
200
|
| --- | --- | --- |
|
|
201
201
|
| 트리거 ref | `Button ref` | `View ref collapsable={false}`로 감쌈 |
|
|
202
|
-
| 시트 머리 |
|
|
202
|
+
| 시트 머리 | 보이는 제목 없음(시각 숨김 `h2`) | 보이는 제목 없음(접근성 머리글) |
|
|
203
203
|
| footer | 가로 flex(wrap, 끝 정렬), [취소][적용] | 세로 View, 위 여백 `spacing.sm` 12, [적용] 위 [취소] 아래 |
|
|
204
204
|
| 초점 | focus trap + `returnFocusRef` | `returnFocusRef` |
|
|
205
205
|
| 상태 알림 | `role="status"` | live region(Android) + iOS 알림 호출 |
|
|
@@ -25,13 +25,13 @@
|
|
|
25
25
|
|
|
26
26
|
```text
|
|
27
27
|
부모 화면의 공개 슬롯
|
|
28
|
-
└─
|
|
28
|
+
└─ 손잡이 → 앨범 → 촬영 (제목은 접근성 이름만)
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
| 영역 | 컴포넌트 | 위치 | 크기·간격 |
|
|
32
32
|
| --- | --- | --- | --- |
|
|
33
33
|
| 바깥 틀 | PhotoSourceSheet 또는 포함 Surface | 부모 화면의 해당 슬롯 | [배치](../components/photo-source-sheet.md#배치)에 따른다 |
|
|
34
|
-
| 내용 | PhotoSourceSheet 내부 슬롯 |
|
|
34
|
+
| 내용 | PhotoSourceSheet 내부 슬롯 | 앨범 → 촬영(보이는 제목 없음(2026-10-17 사용자 결정: 시트는 보이는 제목 없이 `title`을 접근성 이름으로만 쓴다)) | spacing 토큰과 포함 컴포넌트 recipe |
|
|
35
35
|
| 행동 | Button 또는 공개 콜백 | 내용과 가까운 명시 진입점 | 주 행동 하나, 보조 행동과 구분 |
|
|
36
36
|
|
|
37
37
|
## 흐름과 상태
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
|
|
43
43
|
| 상태 | 모습 | 포커스·알림 |
|
|
44
44
|
| --- | --- | --- |
|
|
45
|
-
| 기본 |
|
|
45
|
+
| 기본 | 앨범 → 촬영(시트 이름 = `labels.title`) | 이름·선택 여부를 보조공학에 노출 |
|
|
46
46
|
| 진행 중 | 해당 작업 pending, 입력·기존 결과 보존 | 중복 요청 차단, 로딩에 포커스를 옮기지 않음 |
|
|
47
47
|
| 실패 | 권한 거부·취소는 기존 선택 유지 | 오류 근처 재시도, 필요할 때만 오류 읽기 |
|
|
48
48
|
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
| 영역 | 컴포넌트 | 위치 | 크기·간격 |
|
|
34
34
|
| --- | --- | --- | --- |
|
|
35
35
|
| 바깥 틀 | Stack. Web은 제품 화면의 `Container size="reading"` 안 | 화면의 본문 흐름 | gap lg=20px, 폭은 Container를 따른다(Web 최대 `layout.readingMaxWidth` 720) |
|
|
36
|
-
| 제목 | `Heading level="level3"` | 선택 위 | `heading` 글자 크기·
|
|
36
|
+
| 제목 | `Heading level="level3"` | 선택 위 | `heading` 글자 크기·medium(500) |
|
|
37
37
|
| 선택 | SegmentedControl 2개 | 제목 아래, 연결형 다음 필터형 | 기본 44px 터치 영역, 실제 글자에 따른 높이 |
|
|
38
38
|
| 결과·메모 | Text · TextField | 선택 컨트롤 아래 | 선택 변경 시 필드 재생성 없음 |
|
|
39
39
|
| Native 화면 host | ScreenLayout · KeyboardAvoiding | 본문 스크롤과 바깥 키보드 여백 | flex 1, 끝 여백 spacing.md=16 |
|
|
@@ -74,3 +74,7 @@ Web은 `@hjmds/react`의 해당 granular entry, Native는 `@hjmds/react-native`
|
|
|
74
74
|
| --- | --- | --- |
|
|
75
75
|
| 배치/테마 | Stack과 HjmProvider | Stack과 HjmNativeProvider |
|
|
76
76
|
| 큰 글자·좁은 폭 | 줄바꿈·단일 내용 | 같은 순서, OS 화면 검증은 별도 |
|
|
77
|
+
|
|
78
|
+
## 근거
|
|
79
|
+
|
|
80
|
+
2026-10-17 사용자 결정("굵기 좀더 줄여"): 모든 글자 굵기를 한 단계 낮췄다. 큰 숫자·선택 상태 600(큰 제목 단계는 같은 날 폐지), 제목·강조 500, label 500, 본문 400이며 정본은 [타이포그래피](../tokens/typography.md)다.
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
| Icon + `createLucideGlyph` | 의미 이름(`search`·`settings`·`notifications`·`back`)을 Lucide 그림에 연결 | [Icon](../components/icon.md) |
|
|
24
24
|
| Avatar + `createBlobatarFallback` | 사진이 없을 때 seed로 정해지는 얼굴, `expression: "happy"` | [Avatar](../components/avatar.md) |
|
|
25
25
|
| TextTransition | 문장이 바뀔 때 `fade`·`rise`·`slide`·`scale` 전환 | [TextTransition](../components/text-transition.md) |
|
|
26
|
-
| Heading | 효과 표면 안 제목, 얼굴 묶음 제목(`
|
|
26
|
+
| Heading | 효과 표면 안 제목, 얼굴 묶음 제목(`level3` + `semanticLevel={2}`, 24) | [Heading](../components/heading.md) |
|
|
27
27
|
| Switch | 배경 움직임 켜고 끄기(`presentation="row"`) | [Switch](../components/switch.md) |
|
|
28
28
|
| Button | 다음 장면 | [Button](../components/button.md) |
|
|
29
29
|
| `ScrollView` + `Container`(Native) | 바깥 틀. 세로 스크롤·위아래 여백, 좌우 gutter | [Container](../components/container.md), [화면 여백과 너비](../tokens/layout.md) |
|
|
@@ -46,7 +46,7 @@ Native 화면(ScrollView, 위아래 spacing.lg 20) > Container gutter 16(폭 < 6
|
|
|
46
46
|
┌──── EffectSurface (mesh) ────────────────────┐
|
|
47
47
|
│ 안쪽 여백 spacing.xl 24(두 플랫폼 같음) │
|
|
48
48
|
│ mesh (label) │
|
|
49
|
-
│ 작은 시작, 새로운 장면. (Heading
|
|
49
|
+
│ 작은 시작, 새로운 장면. (Heading level3) ↕ lg 20 │
|
|
50
50
|
│ 설명 (body) │
|
|
51
51
|
│ [ 이야기 시작하기 ] secondary │
|
|
52
52
|
└──────────────────────────────────────────────┘
|
|
@@ -106,7 +106,7 @@ const sceneKeys = ["intro.scene.welcome", "intro.scene.collect", "intro.scene.re
|
|
|
106
106
|
{/* EffectSurface는 여백이 없다. 안쪽 틀: .hero-inner { padding: var(--hjm-space-xl) } */}
|
|
107
107
|
<div className="hero-inner">
|
|
108
108
|
<Stack gap="lg">
|
|
109
|
-
<Heading level="
|
|
109
|
+
<Heading level="level3" semanticLevel={2}>{t("hero.title")}</Heading>
|
|
110
110
|
<Text>{t("hero.body")}</Text>
|
|
111
111
|
<Button tone="secondary" onClick={start}>{t("hero.start")}</Button>
|
|
112
112
|
</Stack>
|
|
@@ -148,7 +148,7 @@ const gutter = resolveWindowClass(width) === "compact" ? "compact" : "regular";
|
|
|
148
148
|
{/* EffectSurface는 여백이 없다. 안쪽 틀만 View padding으로 준다 */}
|
|
149
149
|
<View style={{ padding: spacing.xl }}>
|
|
150
150
|
<Stack gap="lg">
|
|
151
|
-
<Heading level="
|
|
151
|
+
<Heading level="level3" semanticLevel={2}>{t("hero.title")}</Heading>
|
|
152
152
|
<Text>{t("hero.body")}</Text>
|
|
153
153
|
<Button tone="secondary" onPress={start}>{t("hero.start")}</Button>
|
|
154
154
|
</Stack>
|
|
@@ -185,6 +185,7 @@ seed·문구·아이콘 세트·얼굴 목록은 제품 소유다. `createBlobat
|
|
|
185
185
|
- Blobatar seed에 이름·이메일을 쓰지 않는다. 공개 제품 식별자를 쓴다.
|
|
186
186
|
- EffectSurface는 화면 위쪽 히어로 하나에만 둔다. 스토리의 네 장 세로 쌓기는 비교용이다.
|
|
187
187
|
- 2026-10-06 예제 검수에서 Container·Stack·Heading을 두 플랫폼에 맞췄다. 효과 안쪽 여백은 spacing.xxl(32), 얼굴은 64, 제목은 level2로 동일하다. 행은 큰 글자에서 줄바꿈한다.
|
|
188
|
+
- 2026-10-17 사용자 결정("글자 너무 커", "큰 글자 정책은 없애"): 효과 표면·얼굴 묶음 제목 level2(32px)를 HJM 상한인 level3(24px)으로 바꿨다. 문서 단계는 `semanticLevel={2}`로 유지한다.
|
|
188
189
|
- Native EffectSurface는 AppState·reducedMotion을 반영한다. 백그라운드 중단과 내비게이션으로 가려진 화면의 중단은 다르므로, 마운트된 채 가려지는 제품 화면·목록은 `visible`을 연결한다. Storybook 정지 화면만으로 이 제품 연결을 검증했다고 보고하지 않는다.
|
|
189
190
|
|
|
190
191
|
## 근거
|
|
@@ -59,7 +59,8 @@ host: 남은 높이·safe area·키보드
|
|
|
59
59
|
| --- | --- |
|
|
60
60
|
| [AuthScreenLayout](../components/auth-screen-layout.md) | 필수 props·슬롯·플랫폼 차이 |
|
|
61
61
|
| [AuthProviderButton](../components/auth-provider-button.md) | 제공자 버튼(이름만) |
|
|
62
|
-
| [
|
|
62
|
+
| [AuthScreenLayout](../components/auth-screen-layout.md#공개-이름과-import)의 `AuthScreenHero` | 히어로 마크·eyebrow·태그라인·설명(`Top`·`Text`로 조립하지 않는다) |
|
|
63
|
+
| [AuthScreenLayout](../components/auth-screen-layout.md#공개-이름과-import)의 `AuthScreenFooter` | 동의 고지·정책 링크(ghost `Button`으로 만들지 않는다) |
|
|
63
64
|
| [Notice](../components/notice.md) | 로그인 실패 한 줄 |
|
|
64
65
|
| [Button](../components/button.md) | 동작·로딩·보조 행동 |
|
|
65
66
|
| [ScreenLayout](../components/screen-layout.md) | 화면 높이·본문 교체·스크롤 소유 |
|
|
@@ -68,27 +69,27 @@ host: 남은 높이·safe area·키보드
|
|
|
68
69
|
|
|
69
70
|
```tsx
|
|
70
71
|
// Web
|
|
71
|
-
import { AuthScreenLayout } from "@hjmds/react/auth-screen";
|
|
72
|
+
import { AuthScreenFooter, AuthScreenHero, AuthScreenLayout } from "@hjmds/react/auth-screen";
|
|
72
73
|
|
|
73
74
|
<AuthScreenLayout
|
|
74
75
|
mainCard
|
|
75
76
|
{...(pending ? { pendingLabel: t("auth.pending") } : {})}
|
|
76
|
-
hero={<
|
|
77
|
+
hero={<AuthScreenHero mark={<ProductMark />} title={t("auth.tagline")} description={t("auth.description")} />}
|
|
77
78
|
main={<ProviderButtons />} // pending이어도 같은 목록
|
|
78
|
-
footer={<
|
|
79
|
+
footer={<AuthScreenFooter notice={t("auth.consent")} links={policyLinks} />} // { label, href | onClick }
|
|
79
80
|
/>
|
|
80
81
|
```
|
|
81
82
|
|
|
82
83
|
```tsx
|
|
83
84
|
// Native
|
|
84
|
-
import { AuthScreenLayout } from "@hjmds/react-native/auth-screen";
|
|
85
|
+
import { AuthScreenFooter, AuthScreenHero, AuthScreenLayout } from "@hjmds/react-native/auth-screen";
|
|
85
86
|
|
|
86
87
|
<AuthScreenLayout
|
|
87
88
|
mainCard
|
|
88
89
|
{...(pending ? { pendingLabel: t("auth.pending") } : {})}
|
|
89
|
-
hero={<
|
|
90
|
+
hero={<AuthScreenHero mark={<ProductMark />} title={t("auth.tagline")} description={t("auth.description")} />}
|
|
90
91
|
main={<ProviderButtons />} // pending이어도 같은 목록
|
|
91
|
-
footer={<
|
|
92
|
+
footer={<AuthScreenFooter notice={t("auth.consent")} links={policyLinks} />} // { label, onPress }
|
|
92
93
|
/>
|
|
93
94
|
```
|
|
94
95
|
|
|
@@ -112,4 +113,5 @@ import { AuthScreenLayout } from "@hjmds/react-native/auth-screen";
|
|
|
112
113
|
## 근거
|
|
113
114
|
|
|
114
115
|
- 제공자 목록과 로그인 요청 분리 상황 표: 2026-10-13 전수 점검 — 최초 조회와 변경 요청의 상태를 같은 골격으로 설명하던 부분을 구체화했다.
|
|
116
|
+
- 히어로·하단 동의를 `AuthScreenHero`·`AuthScreenFooter`로 연결: 2026-10-17 사용자 피드백("굵기랑 크기 줄여", "간격 줄여") — `Top large` 히어로가 402 폭에서 두 줄 32/40이었고 ghost Button 링크 사이가 42였다. 수치는 [API 배치 규칙](../components/auth-screen-layout.md#배치).
|
|
115
117
|
- 빈 화면·로그인 필요 스토리 없음: 2026-10-06 해당하지 않는 본문 상태 스토리를 뺐다.
|