@hjmds/design-contracts 1.27.2 → 1.28.1
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 +77 -5
- package/dist/auth-screen.d.ts.map +1 -1
- package/dist/auth-screen.js +63 -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 +98 -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/screen-patterns.d.ts +36 -0
- package/dist/screen-patterns.d.ts.map +1 -1
- package/dist/screen-patterns.js +20 -0
- package/dist/screen-patterns.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/SELECTION.md +1 -0
- package/docs/usage/components/auth-screen-layout.md +44 -11
- package/docs/usage/components/bottom-cta.md +6 -2
- package/docs/usage/components/breadcrumb.md +5 -1
- package/docs/usage/components/chat-screen.md +1 -0
- package/docs/usage/components/checkbox-group.md +5 -1
- package/docs/usage/components/collapsible.md +1 -0
- package/docs/usage/components/comment-thread-screen.md +2 -0
- package/docs/usage/components/dialog.md +1 -0
- package/docs/usage/components/editor-screen.md +1 -0
- package/docs/usage/components/effect-surface.md +2 -2
- package/docs/usage/components/field.md +1 -0
- package/docs/usage/components/heading.md +14 -12
- package/docs/usage/components/list-detail-screen.md +1 -0
- package/docs/usage/components/list-row.md +7 -3
- package/docs/usage/components/list.md +25 -4
- package/docs/usage/components/notification-item.md +5 -1
- package/docs/usage/components/onboarding-screen.md +1 -0
- package/docs/usage/components/photo-source-sheet.md +1 -1
- package/docs/usage/components/screen-layout.md +28 -3
- package/docs/usage/components/search-field.md +1 -0
- package/docs/usage/components/search-screen.md +4 -2
- package/docs/usage/components/section.md +5 -1
- package/docs/usage/components/segmented-control.md +1 -1
- package/docs/usage/components/sheet.md +18 -13
- package/docs/usage/components/skeleton.md +42 -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-area.md +1 -0
- 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
package/docs/design-profile.md
CHANGED
|
@@ -51,8 +51,8 @@ export const productDesign = defineHjmDesignProfile({
|
|
|
51
51
|
|
|
52
52
|
| 프리셋 | 팔레트 | 서체·제목 | 깊이·모서리 | 구성 |
|
|
53
53
|
| --- | --- | --- | --- | --- |
|
|
54
|
-
| minimal | 흰 바탕·무채색 잉크, hairline `border` #e6e6e6, 짙은 회색 행동(#3d3d3d) | ui
|
|
55
|
-
| editorial | 따뜻한 종이(#fdfbf7)·잉크(#1b1714), 와인 강조(#7a1f3d), 규칙선용 짙은 `border` #cfc5b8 | display·reading 시스템 serif stack
|
|
54
|
+
| minimal | 흰 바탕·무채색 잉크, hairline `border` #e6e6e6, 짙은 회색 행동(#3d3d3d) | ui 유지(제목 굵기는 공통 500), body 행간 26 | raised·floating 그림자 0(오버레이는 테두리와 scrim), 4/8/12/16 | cards(테두리·안쪽 24)/inline·dashboard |
|
|
55
|
+
| editorial | 따뜻한 종이(#fdfbf7)·잉크(#1b1714), 와인 강조(#7a1f3d), 규칙선용 짙은 `border` #cfc5b8 | display·reading 시스템 serif stack(제목 크기는 공통 단계), body 행간 26 | raised 그림자 0, 0/2/2/4 | rows(규칙선)/collapsible·editorial |
|
|
56
56
|
| terminal | 밝은 모드 녹회색(#c8d4c4)·짙은 녹색 잉크·호박갈색 강조, 어두운 모드 검정(#050a06)·녹색 형광 글자·호박색 강조 | ui=code이므로 display·reading도 monospace | 그림자 0, 모서리 0 | rows(규칙선)/collapsible·dashboard |
|
|
57
57
|
|
|
58
58
|
serif stack은 `ui-serif, Iowan Old Style, Georgia, AppleMyungjo, Batang, Noto Serif KR, serif`이며 폰트를 내려받지 않는다.
|
|
@@ -101,24 +101,28 @@ SegmentedControl·Tabs, ScreenLayout 및 optional OverviewScreen에 연결한다
|
|
|
101
101
|
유리의 실제 backdrop blur와 클레이 inset shadow는 아래 Surface 질감 계약으로 연결한다. Native의 선택형 host·OS·architecture 조건과 실제 기기 검증은 별도다.
|
|
102
102
|
|
|
103
103
|
|
|
104
|
-
##
|
|
104
|
+
## 제목 단계 지정하기
|
|
105
105
|
|
|
106
106
|
`tokens.heading`은 `level1`~`level5`의 fontSize/lineHeight/fontWeight를 부분 지정한다.
|
|
107
107
|
현재 Heading은 두 renderer 모두 이 값으로 그리고 문서 `semanticLevel`과 textScale을
|
|
108
|
-
따로 유지한다.
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
108
|
+
따로 유지한다. 모든 프리셋이 foundation의 24/24/24/20/18px를 쓴다. 브루탈리즘은 굵기만
|
|
109
|
+
800으로 명시한다(두꺼운 테두리·딱딱한 그림자를 버티는 제목 무게, 2026-10-17 사용자 Storybook 비교).
|
|
110
|
+
|
|
111
|
+
**글자 크기 상한(2026-10-17 사용자 결정, "글자 너무 커", "큰 글자 정책은 없애").** 프로필의
|
|
112
|
+
`typography`와 `heading` 어느 단계도 `typography.heading`의 24px를 넘을 수 없고, 넘으면 `RangeError`다.
|
|
113
|
+
그 전에는 에디토리얼 56/42/30px, 브루탈리즘 48/38/30px 같은 확대 제목이 허용됐지만 Native Storybook
|
|
114
|
+
서비스 소개 화면에서 지나치게 커 보여 큰 글자 단계와 함께 없앴다. 프리셋 정체성은 팔레트·서체 역할·
|
|
115
|
+
그림자·모서리·구성 축으로 구분한다.
|
|
112
116
|
|
|
113
117
|
```ts
|
|
114
118
|
const productDesign = defineHjmDesignProfile({
|
|
115
119
|
extends: "paper",
|
|
116
|
-
tokens: { heading: {
|
|
120
|
+
tokens: { heading: { level3: { fontWeight: "600" } } },
|
|
117
121
|
});
|
|
118
122
|
```
|
|
119
123
|
|
|
120
|
-
미지정 값은 preset을 상속한다. `tokens.typography.heading
|
|
121
|
-
|
|
124
|
+
미지정 값은 preset을 상속한다. `tokens.typography.heading`은 Heading level1~3,
|
|
125
|
+
`titleLarge`/`title`은 level4/5의 변경 기본값이며, 같은 필드에 명시한 `tokens.heading`이
|
|
122
126
|
우선한다. 나머지 프로필 축·본문 Text typography와 문서 순서는 바꾸지 않는다.
|
|
123
127
|
지원하지 않는 persisted heading 단계는 조용히 무시하지 않고 거부한다.
|
|
124
128
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Component maturity and evidence manifest
|
|
2
2
|
|
|
3
|
-
> Generated from `@hjmds/design-contracts` 1.
|
|
3
|
+
> Generated from `@hjmds/design-contracts` 1.28.1. Do not edit this file directly; run `pnpm contracts:sync`.
|
|
4
4
|
|
|
5
5
|
This document is the human-readable projection of the same catalog and Showcase manifest consumed by product CI.
|
|
6
6
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"designSystemVersion": "1.
|
|
3
|
+
"designSystemVersion": "1.28.1",
|
|
4
4
|
"source": "packages/design-contracts/docs/generated/showcase-manifest.json",
|
|
5
5
|
"surfaces": {
|
|
6
6
|
"web": {
|
|
7
7
|
"packageName": "@hjmds/react",
|
|
8
|
-
"packageVersion": "1.
|
|
8
|
+
"packageVersion": "1.28.1",
|
|
9
9
|
"activeComponents": 105,
|
|
10
10
|
"claimedComponents": 103,
|
|
11
11
|
"completeScenarioComponents": 103,
|
|
@@ -1044,7 +1044,9 @@
|
|
|
1044
1044
|
"claimed": true,
|
|
1045
1045
|
"subpath": "./auth-screen",
|
|
1046
1046
|
"exportNames": [
|
|
1047
|
-
"AuthScreenLayout"
|
|
1047
|
+
"AuthScreenLayout",
|
|
1048
|
+
"AuthScreenHero",
|
|
1049
|
+
"AuthScreenFooter"
|
|
1048
1050
|
],
|
|
1049
1051
|
"proofs": [
|
|
1050
1052
|
{
|
|
@@ -5700,7 +5702,7 @@
|
|
|
5700
5702
|
},
|
|
5701
5703
|
"native": {
|
|
5702
5704
|
"packageName": "@hjmds/react-native",
|
|
5703
|
-
"packageVersion": "1.
|
|
5705
|
+
"packageVersion": "1.28.1",
|
|
5704
5706
|
"activeComponents": 87,
|
|
5705
5707
|
"claimedComponents": 84,
|
|
5706
5708
|
"completeScenarioComponents": 83,
|
|
@@ -6621,7 +6623,9 @@
|
|
|
6621
6623
|
"claimed": true,
|
|
6622
6624
|
"subpath": "./auth-screen",
|
|
6623
6625
|
"exportNames": [
|
|
6624
|
-
"AuthScreenLayout"
|
|
6626
|
+
"AuthScreenLayout",
|
|
6627
|
+
"AuthScreenHero",
|
|
6628
|
+
"AuthScreenFooter"
|
|
6625
6629
|
],
|
|
6626
6630
|
"proofs": [
|
|
6627
6631
|
{
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Renderer evidence coverage
|
|
2
2
|
|
|
3
|
-
> Generated for HJM 1.
|
|
3
|
+
> Generated for HJM 1.28.1. Do not edit directly; run `pnpm evidence:sync`.
|
|
4
4
|
|
|
5
5
|
This projection joins the canonical surface maturity manifest with first-party renderer claims. Missing scenarios are explicit beta promotion debt; stable surfaces are blocked by CI until none remain.
|
|
6
6
|
|
package/docs/heading.md
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
# Heading contract
|
|
2
2
|
|
|
3
|
-
**문제.** 랜딩
|
|
3
|
+
**문제.** 랜딩 대표 제목, 결과 화면 제목, 카드 제목처럼 **문서 제목 단계를 실제로
|
|
4
4
|
그려야 하는 자리**.
|
|
5
5
|
|
|
6
|
-
**이미 있던 것을 꺼냈을 뿐이다.** `foundations`의 `heading
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
**이미 있던 것을 꺼냈을 뿐이다.** `foundations`의 `heading` 단계를 renderer로 노출하고
|
|
7
|
+
문서 단계(`h1`~`h6`)를 함께 낸다. 이 계약은 **새 크기를 만들지 않는다.** 크기는 `Text`와 같은
|
|
8
|
+
단계이며 가장 큰 값은 `typography.heading` 24px다.
|
|
9
|
+
|
|
10
|
+
**큰 글자 단계 폐지(2026-10-17 사용자 결정, "글자 너무 커", "큰 글자 정책은 없애").** 처음에는
|
|
11
|
+
level1 40px·level2 32px의 큰 글자 단계가 있었지만, Native Storybook 서비스 소개 화면의 히어로·구획 제목이
|
|
12
|
+
지나치게 커서 없앴다. `level1`·`level2`는 소비 코드 호환을 위해 이름만 남기고 `level3`과 같은 24/32/500이며
|
|
13
|
+
새 코드는 `level3`~`level5`로 위계를 만든다. 디자인 프로필도 24px를 넘는 제목 크기를 거부한다.
|
|
10
14
|
|
|
11
15
|
**Top·Section과 겹치지 않는다.**
|
|
12
16
|
|
|
@@ -25,16 +29,17 @@ level5 18px까지 다섯 단계가 있었지만 어떤 renderer도 노출하지
|
|
|
25
29
|
**Native.** 접근성 role은 `header` 하나뿐이라 문서 단계는 `aria-level`로 함께 싣는다.
|
|
26
30
|
|
|
27
31
|
|
|
28
|
-
## 디자인 프로필의
|
|
32
|
+
## 디자인 프로필의 제목 크기
|
|
29
33
|
|
|
30
34
|
2026-10-07 [Heading 갤러리](https://component.gallery/components/heading/)를 비교하며
|
|
31
35
|
현재 renderer가 level3~5만 프로필 typography에 연결하고 level1/2는 고정값으로 남긴
|
|
32
|
-
누락을 확인했다. 앱이 테마를 한 번 넣어
|
|
36
|
+
누락을 확인했다. 앱이 테마를 한 번 넣어 모든 제목 단계를 재사용하려는 요구 때문에
|
|
33
37
|
[디자인 프로필](design-profile.md)의 `tokens.heading`을 다섯 시각 단계로 둔다.
|
|
34
38
|
본문 크기에서 큰 제목을 임의로 배율 계산하는 대안은 계층과 행간이 달라질 수 있어
|
|
35
39
|
채택하지 않는다. 원본 갤러리의 수치·코드·폰트 자산을 복사한 것이 아니다.
|
|
36
40
|
|
|
37
|
-
프로필이 없으면
|
|
38
|
-
|
|
41
|
+
프로필이 없으면 24/24/24/20/18 크기를 쓴다(2026-10-17 큰 글자 단계 폐지 전에는 40/32/24/20/18).
|
|
42
|
+
level1~3은 typography heading, level4/5는 titleLarge/title alias를 helper에서 병합하고 명시적인 heading
|
|
43
|
+
override가 마지막에 우선한다. 결과가 24px를 넘으면 프로필 생성이 `RangeError`로 실패한다.
|
|
39
44
|
Web은 CSS 변수, Native는 Text host의 metrics로 반영하고 `semanticLevel`·문구·초점을
|
|
40
45
|
바꾸지 않는다. 테마 전환은 시각 크기만 바꾸며 실제 문서 단계는 기존 계약이 소유한다.
|
package/docs/sheet.md
CHANGED
|
@@ -84,3 +84,5 @@ Diairy QA W16 reproduced Chrome default backdrop blur undoing Dialog focus retur
|
|
|
84
84
|
0pt가 되고 검색 입력만 그려졌다(iPhone 17 Pro · iOS 26.5, 이전 코드로도 재현). 제품은 창 높이 `flexBasis`로 우회했다.
|
|
85
85
|
`auto`는 내용 높이 그대로다(늘 남는 공간이 없다). 바뀌는 모습: 고정 높이 시트의 `footer`가 본문 바로 아래가 아니라 시트 아래에 붙는다(Web과 같다).
|
|
86
86
|
버린 대안: 새 `bodyLayout` prop(기본값을 그대로 두면 Web과 다른 동작이 남고 제품마다 켜야 한다).
|
|
87
|
+
|
|
88
|
+
2026-10-17 사용자 결정 두 가지. 첫째, 시트는 위쪽에 보이는 제목 줄을 그리지 않는다(예: SearchScreen 필터 시트의 "필터"). 위 1.4·1.7의 제목 정렬·element 제목 설명은 기록으로 남긴다. 지금 `title`은 접근성 이름일 뿐이다. Web은 `aria-labelledby`가 가리키는 시각 숨김 `h2`, Native는 대화상자 `accessibilityLabel`과 배치 밖 1pt 머리글이다. Native element 제목은 그리지 않으므로 `accessibilityTitle`이 이름이 된다. 머리 행은 설명이나 ×가 있을 때만 그리고 ×는 끝 쪽 자리를 지킨다. Native Select·Combobox 선택 시트와 GestureSheet도 같은 규칙을 따르며 `sheetRecipe.title` 시각 값은 삭제했다. 둘째, 가림막을 누르면 모든 위치의 시트가 닫힌다(2026-10-16의 하단 드래그 전용 기본을 대체). 닫기 경로는 손잡이·드래그와 같은 `requestClose("outside")`이므로 `busy`·`dismissPolicy`·제어형 owner의 버리기 확인을 건너뛰지 않는다. 검사: `sheet-layout.browser.test.tsx`(접근성 이름, 머리 없음, 손잡이–본문 간격, 가림막 닫기·busy·owner 유지), `sheet-viewport.test.tsx`(Native 접근성 이름·보이는 제목 없음·× 위치·가림막 닫기·busy·`outsideDismiss: false`).
|
package/docs/top.md
CHANGED
|
@@ -18,8 +18,9 @@
|
|
|
18
18
|
|
|
19
19
|
**크기는 둘뿐이다.** `large`(화면의 첫 제목)와 `medium`(시트·모달 안의 첫 제목).
|
|
20
20
|
세 번째가 필요해지는 자리는 이미 `Section`이다 — 크기를 늘리는 대신 다른 컴포넌트를
|
|
21
|
-
쓰라는 신호로 둔다. 두 크기 모두 기존 타입 스케일(`heading.
|
|
22
|
-
그대로 쓰고 새 숫자를 만들지 않는다.
|
|
21
|
+
쓰라는 신호로 둔다. 두 크기 모두 기존 타입 스케일(`heading.level3` 24px, `typography.titleLarge` 20px)을
|
|
22
|
+
그대로 쓰고 새 숫자를 만들지 않는다. 2026-10-17 사용자 결정("글자 너무 커")으로 큰 글자 단계를 폐지하면서
|
|
23
|
+
`large`가 읽던 `heading.level2`(32px)를 HJM 상한인 `heading.level3`(24px)으로 바꿨다.
|
|
23
24
|
|
|
24
25
|
**잘리지 않는다.** `description`은 줄바꿈하며 말줄임하지 않는다. 큰 글자 설정에서 화면이
|
|
25
26
|
묻는 내용이 사라지면 안 되기 때문이다. `trailing` 보조 행동은 제목 줄을 공유하다가
|
|
@@ -33,6 +34,6 @@
|
|
|
33
34
|
|
|
34
35
|
## 프로필 모서리·제목 소비
|
|
35
36
|
|
|
36
|
-
2026-10-07 소비 감사에서 두 renderer의 제목 크기·행간·굵기가 foundation으로 확정된 recipe를 직접 읽어 프로필을 우회했다. `large`는 가까운 프로필의 `heading.
|
|
37
|
+
2026-10-07 소비 감사에서 두 renderer의 제목 크기·행간·굵기가 foundation으로 확정된 recipe를 직접 읽어 프로필을 우회했다. `large`는 가까운 프로필의 `heading.level3`, `medium`은 `typography.titleLarge`를 읽고 프로필이 없으면 기존 recipe를 유지한다. 시각 역할은 `headingLevel`과 독립이며 display 서체·제목 의미·보조 행동·초점은 바꾸지 않는다.
|
|
37
38
|
|
|
38
39
|
프로필 연결은 1.15.0 이후 미게시 변경이며, 기본 배포 계약과 실제 Native 기기 검증은 구분한다.
|
package/docs/usage/README.md
CHANGED
|
@@ -97,7 +97,7 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
|
|
|
97
97
|
| [FollowProfileCard](components/follow-profile-card.md) | 구성/정보 표시 | 프로필 카드에서 팔로우·팔로잉 전환과 팔로워 수를 실제 mutation 결과와 함께 보여 줄 때 쓴다. | 1.21.0 | Web · Native | 개별 API 계약 참조 | 배포 |
|
|
98
98
|
| [Form](components/form.md) | 입력 | 여러 Field를 한 화면에 쌓고 한 번에 제출할 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
99
99
|
| [Grid](components/grid.md) | 레이아웃 | 카드·타일처럼 같은 모양의 자식을 창 크기에 따라 열 수를 바꿔 배치할 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
100
|
-
| [Heading](components/heading.md) | 글자와 아이콘 | 자리를 모르는
|
|
100
|
+
| [Heading](components/heading.md) | 글자와 아이콘 | 자리를 모르는 제목 글자 하나가 필요할 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
101
101
|
| [HistogramExplorer](components/histogram-explorer.md) | 구성/정보 표시 | 실제 관측값 배열의 분포를 동폭 구간 막대로 보여 주고, 사용자가 구간 수를 바꿔 볼 때 쓴다. | 1.21.0 | Web · Native | 개별 API 계약 참조 | 배포 |
|
|
102
102
|
| [Icon](components/icon.md) | 글자와 아이콘 | HJM semantic 이름(`search`, `back`, `chevronEnd`, `notifications` 등 43개)으로 고르는 그림 기호에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
103
103
|
| [IconButton](components/icon-button.md) | 동작 | 보이는 글자 없이 아이콘만으로 표시하는 행동에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
package/docs/usage/SELECTION.md
CHANGED
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
| 화면 나가기(뒤로·닫기·설정 나가기) | `ScreenLayout`(과 이를 쓰는 화면)의 `exit` — 헤더 시작 쪽 아이콘 버튼, 글자 없음 | 본문·하단의 "나가기" 글자 버튼, 화면마다 다른 화살표 |
|
|
47
47
|
| 버튼 묶음 간격 | 세로로 쌓으면 `gap="xs"`(8), 가로로 나란하면 `gap="sm"`(12) — `actionGroupRecipe` | 세로 버튼 사이 12 이상 |
|
|
48
48
|
| 간격 | `Stack`·`ScreenLayout`·recipe 간격 | 임의 `margin`·빈 `View` |
|
|
49
|
+
| 화면 진입 시 입력 포커스 | `ScreenLayout`(과 이를 쓰는 화면)·`Sheet`·`Dialog`의 `initialFocus`, 주 입력 지정은 칸의 `initialFocusTarget`([진입 포커스](components/screen-layout.md#진입-포커스)) | 앱별 `autoFocus`·지연 `ref.focus()`, 대화·댓글 화면의 작성창 자동 포커스 |
|
|
49
50
|
|
|
50
51
|
리뷰 기준: 위 "쓰지 않을 것" 패턴이 보이면 예제 조립 문제인지 컴포넌트 recipe 문제인지 먼저 가리고, 원인 쪽(예제 또는 HJM 컴포넌트)을 고친다.
|
|
51
52
|
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
- 단계: 컴포넌트
|
|
4
4
|
- 상태: 배포
|
|
5
5
|
- 지원: Web · Native
|
|
6
|
-
- 적용: 1.12.1
|
|
7
|
-
- 검토일: 2026-10-
|
|
6
|
+
- 적용: 1.12.1 (`safeAreaBottom`·링크 `target`/`rel`/`accessibilityHint` 1.28.1)
|
|
7
|
+
- 검토일: 2026-10-17
|
|
8
8
|
- 근거: [로그인 화면 골격](../../auth-screen.md), [1.4 제품 채택 가이드](../../product-adoption-1.4.md), recipe `authScreenRecipe`(`src/auth-screen.ts`), 포트폴리오 상위 기준 루트 `docs/LOGIN_SCREEN_STANDARD.md`(LS)
|
|
9
9
|
- 스토리북: `배포/컴포넌트/레이아웃/로그인 화면`
|
|
10
10
|
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
|
|
13
13
|
로그인·가입 진입 화면의 배치에 쓴다. 위 영역(`hero` + `main`)은 남는 세로 공간에서 가운데 정렬되고,
|
|
14
14
|
`footer`(동의 고지·정책 링크)는 바닥에 붙는다. 내용이 화면보다 길면 스크롤로 바뀐다.
|
|
15
|
-
|
|
15
|
+
`AuthScreenLayout`은 배치를 소유하고, 히어로 문구와 동의 고지·정책 링크는 같은 모듈의 `AuthScreenHero`·`AuthScreenFooter`가
|
|
16
|
+
글자 단계·색·간격을 소유한다. 제품은 문자열·마크·링크 목적지만 넘긴다.
|
|
16
17
|
|
|
17
18
|
## 쓰지 않을 때
|
|
18
19
|
|
|
@@ -25,33 +26,43 @@
|
|
|
25
26
|
|
|
26
27
|
| 이름 | 역할 | Web | Native |
|
|
27
28
|
| --- | --- | --- | --- |
|
|
28
|
-
| `AuthScreenLayout` | 기본 | `@hjmds/react`, `/auth-screen` | `@hjmds/react-native`, `/auth-screen` |
|
|
29
|
+
| `AuthScreenLayout` | 기본(배치) | `@hjmds/react`, `/auth-screen` | `@hjmds/react-native`, `/auth-screen` |
|
|
30
|
+
| `AuthScreenHero` | `hero` 슬롯: 마크·eyebrow·태그라인·설명 | 같음 | 같음 |
|
|
31
|
+
| `AuthScreenFooter` | `footer` 슬롯: 동의 고지·정책 링크(·구분) | 같음, 링크 `href`(+`target`·`rel`) 또는 `onClick` | 같음, 링크 `onPress` |
|
|
29
32
|
|
|
30
33
|
## 최소 사용 예
|
|
31
34
|
|
|
32
35
|
```tsx
|
|
33
36
|
// Web
|
|
34
|
-
import { AuthScreenLayout } from "@hjmds/react/auth-screen";
|
|
37
|
+
import { AuthScreenFooter, AuthScreenHero, AuthScreenLayout } from "@hjmds/react/auth-screen";
|
|
35
38
|
|
|
36
39
|
<AuthScreenLayout
|
|
37
40
|
mainCard
|
|
38
41
|
{...(pending ? { pendingLabel: t("auth.pending") } : {})}
|
|
39
|
-
hero={<
|
|
42
|
+
hero={<AuthScreenHero mark={<ProductMark />} title={t("auth.tagline")} description={t("auth.description")} />}
|
|
40
43
|
main={<ProviderButtons />} // pending이어도 같은 목록
|
|
41
|
-
footer={<
|
|
44
|
+
footer={<AuthScreenFooter notice={t("auth.consent")} links={[
|
|
45
|
+
// 새 탭에서 여는 문서: target만 주면 rel은 "noreferrer noopener"가 기본이다.
|
|
46
|
+
{ label: t("legal.privacy"), href: privacyUrl, target: "_blank", accessibilityHint: t("legal.opensNewTab") },
|
|
47
|
+
{ label: t("legal.terms"), href: termsUrl, target: "_blank", accessibilityHint: t("legal.opensNewTab") },
|
|
48
|
+
]} />}
|
|
42
49
|
/>
|
|
43
50
|
```
|
|
44
51
|
|
|
45
52
|
```tsx
|
|
46
53
|
// Native
|
|
47
|
-
import { AuthScreenLayout } from "@hjmds/react-native/auth-screen";
|
|
54
|
+
import { AuthScreenFooter, AuthScreenHero, AuthScreenLayout } from "@hjmds/react-native/auth-screen";
|
|
48
55
|
|
|
49
56
|
<AuthScreenLayout
|
|
50
57
|
mainCard
|
|
51
58
|
{...(pending ? { pendingLabel: t("auth.pending") } : {})}
|
|
52
|
-
hero={<
|
|
59
|
+
hero={<AuthScreenHero mark={<ProductMark />} title={t("auth.tagline")} description={t("auth.description")} />}
|
|
53
60
|
main={<ProviderButtons />}
|
|
54
|
-
footer={<
|
|
61
|
+
footer={<AuthScreenFooter links={[
|
|
62
|
+
{ label: t("legal.privacy"), onPress: openPrivacy, accessibilityHint: t("legal.opensInBrowser") },
|
|
63
|
+
{ label: t("legal.terms"), onPress: openTerms, accessibilityHint: t("legal.opensInBrowser") },
|
|
64
|
+
]} />}
|
|
65
|
+
safeAreaBottom={useSafeAreaInsets().bottom} // 바깥 SafeAreaView bottom·paddingBottom 래퍼 대신
|
|
55
66
|
testID="login-screen"
|
|
56
67
|
/>
|
|
57
68
|
```
|
|
@@ -69,6 +80,11 @@ import { AuthScreenLayout } from "@hjmds/react-native/auth-screen";
|
|
|
69
80
|
| Web `as` | `main` · `section` | `main` | 최대 폭은 416이다 |
|
|
70
81
|
| `layoutStyle` | margin·width·flex·`alignSelf` | — | 바깥 배치 전용(Web·Native 모두) |
|
|
71
82
|
| Native `testID` | `string` | — | 뿌리 `ScrollView` 식별 |
|
|
83
|
+
| Native `safeAreaBottom` | 0 이상의 유한수 | `0` | 화면이 닿는 창 아래 가장자리의 safe area(`useSafeAreaInsets().bottom`, 이미 아래를 들인 host 안이면 0). 아래 여백이 `max(paddingBlock, inset)` — `regular` max(40, inset), `compact` max(20, inset) — 으로 한 번만 들어간다(ScreenLayout과 같은 규칙, 합산 없음). 키보드가 열리면 paddingBlock만. 음수·무한대는 `RangeError` |
|
|
84
|
+
| `AuthScreenHero` `title` · `eyebrow` · `description` · `mark` · `headingLevel` | 문자열 · `ReactNode` · `1`~`3` | `headingLevel` 1 | 제목은 필수이고 빈 문자열이면 `TypeError`. 밀도는 바깥 `AuthScreenLayout`을 따른다 |
|
|
85
|
+
| `AuthScreenFooter` `links` · `notice` · `children` | 링크 배열(1개 이상) · `ReactNode` | — | 빈 링크 목록·빈 라벨은 `TypeError`(LS-04: 약관 링크는 로그인 전 같은 화면에 있어야 한다). `children`은 링크 아래 제품 내용 |
|
|
86
|
+
| 링크 `accessibilityHint` | 문자열 | — | 라벨 뒤에 읽히는 보조 안내(예: 제품이 번역한 "브라우저에서 열림"). Native `accessibilityHint`, Web 숨긴 설명(`aria-describedby`). 보이는 라벨은 그대로 링크 이름이다. 공백만 있으면 `TypeError` |
|
|
87
|
+
| Web 링크 `target` · `rel` | anchor 속성 | — | `href`가 있을 때만. `target="_blank"`이면 `rel` 기본값 `noreferrer noopener`(Link와 같음) |
|
|
72
88
|
|
|
73
89
|
## 배치
|
|
74
90
|
|
|
@@ -76,8 +92,10 @@ import { AuthScreenLayout } from "@hjmds/react-native/auth-screen";
|
|
|
76
92
|
| --- | --- | --- |
|
|
77
93
|
| 크기 | 화면 전체(Web `min-block-size: 100dvh`, Native `ScrollView flex: 1`). 위 블록과 footer는 최대 폭 416, 마크 72(`markSize`, `radius.lg`), 정책 링크 최소 44(`footerMinTouchTarget`), mainCard 모서리 profile의 `radius.lg`(neutral/미지정 16) | `authScreenRecipe`, `.hjm-auth-screen*` |
|
|
78
94
|
| 간격 | `regular` / `compact`: 바깥 좌우 `layout.pagePadding.regular` 20 / `.compact` 16, 바깥 위아래 `spacing.xxxl` 40 / `spacing.lg` 20, hero 안 `spacing.md` 16 / `spacing.xs` 8, hero↔main `spacing.xl` 24 / `spacing.md` 16, 위 블록↔footer 최소 `spacing.xl` 24 / `spacing.md` 16. mainCard 안쪽 `spacing.md` 16 | `authScreenRecipe.densities`·`mainCard` |
|
|
95
|
+
| 히어로 문구 | 마크 ↔ 문구 묶음은 hero 간격. eyebrow `label` 12/18·500·브랜드색, 아래 `spacing.xxs` 4 → 태그라인 `regular` `typography.heading` 24/32 · `compact` `titleLarge` 20/28, 굵기는 제목 단계 500, 기본 글자색, 실제 heading 요소(Native `header`) → 설명 `body` 14/20·400·보조색(`muted`), 위 `spacing.xs` 8 | `authScreenRecipe.hero`, `densities.*.titleVariant` |
|
|
96
|
+
| 동의 고지·정책 링크 | 고지 `caption` 11/16 흐린 색, 링크 줄과 `spacing.xxs` 4. 링크 `caption`·브랜드색·밑줄, 링크 사이 `linkGap` 8 + `·` + 8(보이는 글자 간격 약 19). 각 링크 높이 44(배경 없음), 가로 터치 폭은 구분점 앞까지 보이지 않게 넓힘(Web 가상 요소, Native `hitSlop`) | `authScreenRecipe.footer`, `.hjm-auth-screen__link` |
|
|
79
97
|
| 순서·정렬 | 위→아래 hero(가운데 정렬 문구) → main(폭 꽉 채움) → footer(동의 고지·정책 링크). 위 블록은 남는 세로 공간에서 가운데, footer는 바닥에 붙는다. 진행 중 로딩은 main 카드 가운데에 겹친다 | `.hjm-auth-screen__block`(`justify-content: center`), `.hjm-auth-screen__pending` |
|
|
80
|
-
| 고정·스크롤 | 고정 영역이 없다. 넘치면 화면 전체가 스크롤한다.
|
|
98
|
+
| 고정·스크롤 | 고정 영역이 없다. 넘치면 화면 전체가 스크롤한다. 위 safe area는 host(TopBar·SafeAreaView top)가, 아래는 레이아웃이 `max(paddingBlock, 하단 inset)`으로 맡는다 — Web `env(safe-area-inset-bottom)`, Native `safeAreaBottom`. Native 키보드 inset은 자동 조정이 처리한다 | `react-native/src/auth-screen.tsx`, `.hjm-auth-screen` |
|
|
81
99
|
| 좁은 폭·큰 글자 | 키 작은 화면·큰 글자에서는 `compact`를 고른다. 그래도 넘치면 스크롤되며 footer는 맨 아래로 밀린다 | `authScreenRecipe.densities` |
|
|
82
100
|
|
|
83
101
|
```text
|
|
@@ -104,6 +122,8 @@ import { AuthScreenLayout } from "@hjmds/react-native/auth-screen";
|
|
|
104
122
|
- `pendingLabel`은 스크린리더 안내로만 쓰인다. 로딩 아래에 보이는 문구를 따로 두지 않는다.
|
|
105
123
|
완료·취소·실패 시 prop을 제거한다. 취소는 실패 문장 없이 버튼만 복구한다(LS-09).
|
|
106
124
|
- `pendingLabel`은 기존 카드에서 전환하는 상태다. 제공자 목록 최초 조회의 로딩을 대신하지 않는다.
|
|
125
|
+
- 히어로에 `Top`(화면 첫 제목, large = display 32/40)이나 직접 조립한 `Text`를 넣지 않는다. `AuthScreenHero`를 쓴다.
|
|
126
|
+
정책 링크를 ghost `Button` + Stack gap으로 만들지 않는다 — 버튼 안쪽 여백이 간격에 더해진다. `AuthScreenFooter`를 쓴다.
|
|
107
127
|
- 슬롯 내용은 전부 제품 소유다: 마크 자산, 모든 문구(i18n 키), 제공자 목록과 순서, 정책 링크 목적지,
|
|
108
128
|
심사자 폼 노출 조건. 제공자 로고를 HJM에 넣지 않는다([AuthProviderButton](auth-provider-button.md)).
|
|
109
129
|
- iOS 앱의 제공자 목록에는 Apple이 있어야 한다(LS-02). 심사자 이메일 폼은 서버 env와 `review=1` 두 조건이
|
|
@@ -119,9 +139,13 @@ import { AuthScreenLayout } from "@hjmds/react-native/auth-screen";
|
|
|
119
139
|
| 배치·식별 | `layoutStyle`, `className`, HTML 속성 | `layoutStyle`, `testID` |
|
|
120
140
|
| 진행 중 숨김 | `inert` + `aria-hidden` + `visibility: hidden`, 로딩은 `role="status"` | 터치·접근성 제외 + 불투명도 0, 로딩은 `progressbar` + live region |
|
|
121
141
|
| 키보드 | 브라우저 기본 | 키보드 inset 자동 조정, 탭 유지(`handled`), iOS `interactive`·Android `on-drag` 내리기 |
|
|
142
|
+
| 아래 안전 영역 | `env(safe-area-inset-bottom)` 자동 | `safeAreaBottom` prop(iOS 자동 content inset 조정은 쓰지 않는다) |
|
|
143
|
+
| 정책 링크 보조 안내 | `aria-describedby` 숨긴 텍스트, `target`·`rel` | `accessibilityHint` |
|
|
122
144
|
|
|
123
145
|
## 함정
|
|
124
146
|
|
|
147
|
+
- Native에서 아래 홈 인디케이터를 `SafeAreaView edges={['bottom']}`·`paddingBottom: insets.bottom` 래퍼로 들이지 말고 `safeAreaBottom`으로 넘긴다. 둘을 같이 주면 inset + paddingBlock이 다시 더해진다.
|
|
148
|
+
- 1.28.0까지 Native는 iOS `contentInsetAdjustmentBehavior="automatic"`으로 위·아래 safe area를 iOS에서만 더했다. 1.28.1부터 위는 host, 아래는 `safeAreaBottom`이다. 위 safe area를 아무도 들이지 않던 화면은 host에서 TopBar `safeAreaTop`이나 SafeAreaView top으로 들인다.
|
|
125
149
|
- Native는 자체로 키보드 스크롤 여백을 준다. 부모가 다시 keyboard avoidance를 하면 여백이 중복될 수 있으니
|
|
126
150
|
심사자 폼처럼 입력칸이 있는 화면은 소비 화면에서 확인한다.
|
|
127
151
|
- 앱 셸이 이미 `<main>`을 가진 Web 화면에서 기본값을 쓰면 main landmark가 중첩된다. `as="section"`을 쓴다.
|
|
@@ -131,3 +155,12 @@ import { AuthScreenLayout } from "@hjmds/react-native/auth-screen";
|
|
|
131
155
|
### 테마 카드 모서리
|
|
132
156
|
|
|
133
157
|
`mainCard`는 가장 가까운 Provider의 `designProfile.tokens.radius.lg`를 양 플랫폼에서 소비한다. 테마가 없으면 기존 recipe의 16을 유지한다. 제공자 버튼은 제공자 브랜드 규격을 유지하며, 처리 중에도 카드와 입력을 제거하지 않는다. 카드 표면과 제공자 브랜드를 같은 radius로 강제하면 제품 테마가 제공자 규격을 덮기 때문에 역할을 나눈다.
|
|
158
|
+
|
|
159
|
+
## 근거
|
|
160
|
+
|
|
161
|
+
- `safeAreaBottom`·링크 `target`/`rel`/`accessibilityHint`(1.28.1): 2026-10-17 1.28 채택에서 다에리·utilverse·spint가 레이아웃을 각자 아래 inset 래퍼로 감쌌고(iOS는 자동 content inset이 이미 더해 합산, Android는 아무도 안 들여 spint가 Android만 덧붙임), 다에리 웹은 정책 문서 새 탭 열기(`target="_blank"`)를, spint는 "브라우저에서 열림" 접근성 안내를 링크 슬롯이 없어 잃었다. ScreenLayout의 max 규칙을 그대로 쓰고 링크에 같은 의미의 보조 안내·anchor 속성을 연다.
|
|
162
|
+
- `AuthScreenHero`·`AuthScreenFooter`와 히어로·링크 수치: 2026-10-17 사용자 피드백("굵기랑 크기 줄여", "간격 줄여").
|
|
163
|
+
예제가 히어로에 `Top large`(display 32/40·600)를 넣어 402 폭에서 태그라인이 두 줄이 됐고, 정책 링크는 ghost Button
|
|
164
|
+
안쪽 여백 12+12에 Stack gap 16이 더해져 글자 사이가 Web 42·Native 40이었다. 슬롯 내용을 앱이 조립하면 같은 실수가
|
|
165
|
+
제품마다 반복되므로 HJM이 글자 단계·간격을 recipe로 소유한다. 변경 후 Web 402에서 태그라인 24/32·500 한 줄,
|
|
166
|
+
링크 글자 간격 약 19, 링크 높이 44를 측정했다. 루트 LS §3도 같은 값으로 갱신했다.
|
|
@@ -79,7 +79,7 @@ const insets = useSafeAreaInsets();
|
|
|
79
79
|
| 간격 | 좌우 `layout.pagePadding.regular` 20, 위 `spacing.sm` 12, 아래 `spacing.sm` 12와 하단 inset 중 큰 값(Web은 `env(safe-area-inset-bottom)`·`safeAreaBottom`도 비교). 설명↔행동, 행동 사이 `spacing.sm` 12. 위 경계는 `border.default` 1px(`stroke.default`), Native는 위로 드리우는 그림자(opacity 0.08, radius 8) | `bottomCtaRecipe`, `.hjm-bottom-cta` |
|
|
80
80
|
| 순서·정렬 | 위→아래 [설명 한 줄(caption, muted)] → 행동 줄. 행동 줄은 [보조][주] 순서로 같은 폭을 나눈다. 주 행동은 하나다 | `react/src/bottom-cta.tsx`, `react-native/src/actions.tsx` |
|
|
81
81
|
| 고정·스크롤 | 본문 스크롤 영역 아래에 붙는다. Web은 `flow`(문서 흐름 끝) 또는 `sticky`(아래 0에 붙음, z-index `layer.sticky` 100). Native는 제품 화면 레이아웃이 스크롤 영역 밖 아래에 둔다. 키보드가 열리는 폼은 `KeyboardAvoiding` 또는 `KeyboardDock`으로 감싼다 | `.hjm-bottom-cta[data-position="sticky"]`, `react-native/src/keyboard-controller.tsx` |
|
|
82
|
-
| `ScreenLayout` `footer` 안 | footer가 위 경계·`screenPatternRecipe.padding` 16·바탕·safe area를 맡으므로 BottomCTA는 바(경계·여백·바탕·그림자·최소 높이)를 그리지 않고 설명과 행동 줄만 그린다. 직접 넘긴 `safeAreaBottom
|
|
82
|
+
| `ScreenLayout` `footer` 안 | footer가 위 경계·`screenPatternRecipe.padding` 16·바탕·safe area를 맡으므로 BottomCTA는 바(경계·여백·바탕·그림자·최소 높이)를 그리지 않고 설명과 행동 줄만 그린다. 버튼 아래 여백은 `max(footer 아래 여백, safeAreaBottom)`이다 — 직접 넘긴 `safeAreaBottom`은 footer가 이미 준 여백을 넘는 만큼만 더한다(16 + 34가 아니라 34). Native는 화면 inset을 ScreenLayout `safeAreaBottom`으로 넘기는 것이 기본이다. 2026-10-17 화면 리뷰: footer 안의 BottomCTA가 경계를 두 겹 긋고 버튼을 한 번 더 들였으며 Native는 footer 안에 그림자 있는 흰 상자를 하나 더 그렸다 | 내부 `ScreenFooterContext`, Web `.hjm-bottom-cta[data-placement="screen-footer"]`, Native `BottomCTA` |
|
|
83
83
|
| 좁은 폭·큰 글자 | Web은 행동 칸 기준 폭 132(`control.minTouchTarget` × 3)보다 좁으면 줄바꿈한다. 큰 글자에서는 세로로 쌓고 주 행동이 위에 온다 | `.hjm-bottom-cta__actions > div`, `.hjm-bottom-cta[data-large-text="true"]` |
|
|
84
84
|
|
|
85
85
|
```text
|
|
@@ -103,7 +103,7 @@ const insets = useSafeAreaInsets();
|
|
|
103
103
|
Native Form은 내장 제출 버튼(`submitLabel` 필수)을 항상 그리므로 Form 대신 필드 + contracts `createFormSubmitSession`
|
|
104
104
|
(`@hjmds/design-contracts/components/form`)으로 제출 세션을 잡고 그 상태를 `primaryAction.loading`에 연결한다.
|
|
105
105
|
- 배치는 `layoutStyle`로만 한다. Native `style`은 제거된 속성이다.
|
|
106
|
-
- 하단 inset을 넘긴다. Native는 자동으로 읽지 않으므로 `safeAreaBottom`이 없으면 홈 인디케이터에 붙는다.
|
|
106
|
+
- 하단 inset을 넘긴다. Native는 자동으로 읽지 않으므로 `safeAreaBottom`이 없으면 홈 인디케이터에 붙는다. ScreenLayout `footer` 안이면 ScreenLayout `safeAreaBottom`에 넘기고, host SafeAreaView의 아래 edge와 같이 쓰지 않는다(inset 위에 footer 16이 더해진다).
|
|
107
107
|
- 키보드가 열리는 폼 화면(Native)은 `KeyboardAvoiding`(`/keyboard`) 또는 `KeyboardDock`(`/keyboard-controller`,
|
|
108
108
|
optional peer `react-native-keyboard-controller` 설치 필요)로 감싼다.
|
|
109
109
|
|
|
@@ -126,3 +126,7 @@ const insets = useSafeAreaInsets();
|
|
|
126
126
|
- Native는 하단 inset을 스스로 읽지 않는다. `safeAreaBottom`을 빠뜨려도 오류가 없고 홈 인디케이터에 붙어 보인다.
|
|
127
127
|
|
|
128
128
|
1.15.0 변경: 프로필의 `shadow.floating` 색·강도·반경을 상속하되, 위 콘텐츠와 겹치는 footer 역할이라 offsetY는 `-abs(offsetY)`로 위쪽에 표시한다. Native의 0-opacity 프로필은 Android elevation도 0이다. 프로필 없는 Web은 기존 무그림자, Native는 기존 위쪽 recipe 그림자를 유지한다. safe area·큰 글자·로딩/실패와 제품 행동 소유권은 바뀌지 않는다.
|
|
129
|
+
|
|
130
|
+
## 근거
|
|
131
|
+
|
|
132
|
+
- footer 안 하단 inset은 합이 아니라 max: 2026-10-17 사용자 보고 "전체적으로 버튼 아래 여백이 많아". footer 16 위에 `safeAreaBottom`을 더해 홈 인디케이터 기기에서 버튼 아래가 50이 됐다. 마지막 컨트롤 아래 여백을 footer 위 여백 16과 같게 두고, 홈 인디케이터가 있으면 max(16, inset)만 둔다([ScreenLayout 근거](screen-layout.md#근거)).
|
|
@@ -65,7 +65,7 @@ Native renderer는 없다.
|
|
|
65
65
|
| --- | --- | --- |
|
|
66
66
|
| 크기 | 내용 폭만 차지하는 한 줄 경로. 글자는 `label` 변형(12/18). 링크 높이가 44에 못 미치므로 터치 중심 화면의 주 탐색으로 쓰지 않는다 | `breadcrumbRecipe.link`·`current` |
|
|
67
67
|
| 간격 | 항목·구분자 사이 `spacing.xxs` 4. 바깥 여백이 없으므로 아래 제목·본문과의 간격은 화면의 세로 [Stack](stack.md)이 정한다 | `breadcrumbRecipe.gap`, `.hjm-breadcrumb` |
|
|
68
|
-
| 순서·정렬 | 페이지 제목 바로 위, 본문 시작 쪽 정렬. 상위 → 현재 순서이고 현재 위치(마지막)는 링크가 아니며
|
|
68
|
+
| 순서·정렬 | 페이지 제목 바로 위, 본문 시작 쪽 정렬. 상위 → 현재 순서이고 현재 위치(마지막)는 링크가 아니며 medium(500)으로 표시한다 | `.hjm-breadcrumb__current` |
|
|
69
69
|
| 고정·스크롤 | 고정되지 않고 본문과 함께 스크롤한다 | `.hjm-breadcrumb__list` |
|
|
70
70
|
| 좁은 폭·큰 글자 | 줄바꿈된다(`flex-wrap: wrap`). 항목 label도 줄바꿈되고 구분자는 줄어들지 않는다 | `.hjm-breadcrumb__list`, `.hjm-breadcrumb__separator { flex-shrink: 0 }` |
|
|
71
71
|
|
|
@@ -79,3 +79,7 @@ Native renderer는 없다.
|
|
|
79
79
|
|
|
80
80
|
- 조상 항목은 일반 `<a href>`로 그린다. 라우터 adapter prop(`renderLink` 등)이 없으므로
|
|
81
81
|
Next.js `Link` 같은 클라이언트 전환을 기대하지 않는다.
|
|
82
|
+
|
|
83
|
+
## 근거
|
|
84
|
+
|
|
85
|
+
2026-10-17 사용자 결정("굵기 좀더 줄여"): 모든 글자 굵기를 한 단계 낮췄다. 큰 숫자·선택 상태 600(큰 제목 단계는 같은 날 폐지), 제목·강조 500, label 500, 본문 400이며 정본은 [타이포그래피](../tokens/typography.md)다.
|
|
@@ -79,6 +79,7 @@ import { ChatScreen, MessageComposer } from "@hjmds/react-native/screens";
|
|
|
79
79
|
|
|
80
80
|
| prop | 값 | 기본값 | 설명 |
|
|
81
81
|
| --- | --- | --- | --- |
|
|
82
|
+
| `initialFocus` | `first-input` · `none` | `none` | 1.28.1. 대화는 기록을 읽는 것이 먼저라 기본으로 작성창에 포커스하지 않는다 — 키보드가 올라오면 읽던 메시지를 가린다. 새 대화 시작처럼 쓰기가 먼저인 화면만 `first-input` |
|
|
82
83
|
| `composer` | `ReactNode` | 필수 | footer에 고정되는 작성창. `ready`와 `empty`에서 보인다(빈 대화의 첫 메시지 입력) |
|
|
83
84
|
| `headerAlignment` | `"center"` · `"start"` | `"center"` | 뒤로·도구 사이에 상대 이름을 가운데 둔다(카카오톡·인스타그램 실측) |
|
|
84
85
|
| `scroll` | `"content"` · `"screen"` | `"content"` | 가상화 목록이 스크롤을 소유하므로 작은 예제 외에는 바꾸지 않는다 |
|
|
@@ -79,7 +79,7 @@ import { CheckboxGroup } from "@hjmds/react-native/inputs";
|
|
|
79
79
|
| --- | --- | --- |
|
|
80
80
|
| 크기 | 폭을 채우는 묶음. 각 행은 [Checkbox](checkbox.md) 규격(최소 높이 `medium` 56 · `small` 44, 표시 24 · 20) | `selectionControlRecipe.sizes`, `.hjm-choice` |
|
|
81
81
|
| 간격 | 그룹 이름(legend)↔항목 `spacing.xs` 8. 항목 사이 세로 `card` `spacing.xs` 8 · `plain` `spacing.xxs` 4 · `grouped` 0, 가로 `card` `spacing.md` 16 · `plain` `spacing.sm` 12 · `grouped` 0. 설명·오류는 `formSupportContract.gap` `spacing.xs` 8 | `selectionGroupRecipe.orientations`, `.hjm-checkbox-group*` |
|
|
82
|
-
| 순서·정렬 | 위→아래 [그룹 이름(
|
|
82
|
+
| 순서·정렬 | 위→아래 [그룹 이름(medium 500)] → [설명] → [항목들] → [오류]. 항목은 시작 쪽 정렬. `grouped`는 항목들을 테두리 1px·`radius.lg` 16 카드 하나에 붙여 담는다 | `selectionGroupRecipe.slots`, `.hjm-checkbox-group[data-presentation="grouped"]` |
|
|
83
83
|
| 고정·스크롤 | 고정 영역이 없다. 목록이 길어도 그룹 안에서 스크롤 영역을 만들지 않는다 | `.hjm-checkbox-group__items` |
|
|
84
84
|
| 좁은 폭·큰 글자 | `horizontal`은 줄바꿈된다(`flex-wrap: wrap`). 좁은 폭·큰 글자에서는 `vertical`을 쓴다 | `.hjm-checkbox-group[data-orientation="horizontal"]` |
|
|
85
85
|
|
|
@@ -102,3 +102,7 @@ import { CheckboxGroup } from "@hjmds/react-native/inputs";
|
|
|
102
102
|
| 그룹 전체 비활성 | `disabled`(fieldset) | `disabled` |
|
|
103
103
|
| 필수·읽기 전용 안내 | `aria-required`·`aria-readonly` | `requiredLabel`·`readOnlyLabel`을 행 hint로 읽음 |
|
|
104
104
|
| 선택 표시 교체 | 없음 | `indicator="none"`, `renderIndicator(item, props)` |
|
|
105
|
+
|
|
106
|
+
## 근거
|
|
107
|
+
|
|
108
|
+
2026-10-17 사용자 결정("굵기 좀더 줄여"): 모든 글자 굵기를 한 단계 낮췄다. 큰 숫자·선택 상태 600(큰 제목 단계는 같은 날 폐지), 제목·강조 500, label 500, 본문 400이며 정본은 [타이포그래피](../tokens/typography.md)다.
|
|
@@ -124,4 +124,5 @@ Web에서 외부 상태 변경으로 닫힐 때 내부에 있던 초점은 trigg
|
|
|
124
124
|
|
|
125
125
|
## 근거
|
|
126
126
|
|
|
127
|
+
- `edge` 축을 두지 않는다: 2026-10-17 1.27.2 채택 점검. 포트폴리오 사이트 헤더 끝 메뉴 트리거(402)의 펼침 표시 칸 끝은 화면 끝에서 36이었는데, 트리거 자체는 좌우 padding 0이고 표시 칸 끝이 트리거 끝과 같다(Storybook 측정: 표시 칸 끝 = 트리거 끝 = 루트 끝). 36은 제품이 TopBar(자기 여백 16)를 `Container gutter="regular"`(20) 안에 둔 결과다. IconButton/Button `edge`는 frameless 컨트롤의 안쪽 padding만큼 바깥으로 내미는 규칙인데 이 트리거는 내밀 padding이 없어 `edge`는 아무것도 바꾸지 못한다. 위치는 제품의 겹친 여백에서 고친다.
|
|
127
128
|
- 2026-10-17 구성 리뷰: 텍스트 표시 ▸가 약 8px로 그려져 트리거가 정적 문구처럼 보였다. 그래서 두 플랫폼 모두 glyph.sm 20 chevron 아이콘으로 바꿨다.
|
|
@@ -119,6 +119,8 @@ threadHeader도 댓글과 함께 숨겨지므로 항상 보여야 하는 안내
|
|
|
119
119
|
|
|
120
120
|
## 꼭 지킬 것
|
|
121
121
|
|
|
122
|
+
- 진입 포커스(`initialFocus`)는 기본 `none`이다(1.28.1). 댓글은 읽는 것이 먼저라 작성창 키보드가 스레드를 가리면 안 된다. 답글 쓰기로 바로 들어가는 화면만 `initialFocus="first-input"`을 준다.
|
|
123
|
+
|
|
122
124
|
- id는 비어 있지 않고 유일해야 하며, 답글의 `parentId`는 `items` 안의 **최상위** 댓글이어야 한다. 아니면 렌더 중
|
|
123
125
|
`TypeError`가 난다. 답글의 답글은 표현하지 않으므로 제품이 최상위로 평탄화한다.
|
|
124
126
|
- 모든 문구·시각·좋아요 수 라벨은 제품 i18n에서 만든다. `likeCountLabel`이 빈 문자열이면 그리지 않는다.
|
|
@@ -113,6 +113,7 @@ await handle.closed; // portal 제거·초점 복구 뒤. 다음 오버레이는
|
|
|
113
113
|
|
|
114
114
|
| prop | 값 | 기본값 | 설명 |
|
|
115
115
|
| --- | --- | --- | --- |
|
|
116
|
+
| `initialFocus` | `single-input` · `first-input` · `none` | `single-input` | 1.28.1. 열린 뒤(Native: 표시·진입 애니메이션 뒤, Web: 포커스 이동 시점) 포커스할 텍스트 입력. 기본은 텍스트 입력이 **하나뿐일 때만** 그 칸(이름 바꾸기·메모 한 줄), 여럿이면 기존 규칙(Web 첫 포커스 가능 요소, Native 접근성 포커스) 그대로. `first-input`은 `initialFocusTarget` 칸, 없으면 첫 칸. `initialFocusRef`(Web)가 있으면 그쪽이 이긴다. 열 때마다 다시 고른다 |
|
|
116
117
|
| `size` | `small` · `medium` · `large` | `medium` | Native recipe 최대 폭 320 · 420 · 640. Web 최대 폭 28rem · 36rem · 48rem(아래 배치 표) |
|
|
117
118
|
| `dismissible` | `true` · `false` | `true` | `false`면 닫기 버튼과 바깥·Escape/back 닫기가 사라진다 |
|
|
118
119
|
| `busy` | `true` · `false` | `false` | 비동기 작업 중에는 `open`을 유지하고 `busy`로 반복 행동과 닫기를 막는다 |
|
|
@@ -91,6 +91,7 @@ import { TextField } from "@hjmds/react-native/inputs";
|
|
|
91
91
|
|
|
92
92
|
| prop | 값 | 기본값 | 설명 |
|
|
93
93
|
| --- | --- | --- | --- |
|
|
94
|
+
| `initialFocus` | `first-input` · `none` | `first-input` | 1.28.1. 작성·편집이 화면의 목적이라 진입 후 첫 입력(또는 `initialFocusTarget` 칸)에 한 번 포커스한다. 읽고 고칠 내용이 먼저인 편집은 `none` |
|
|
94
95
|
| `dirty` | `boolean` | 필수 | `true`에서 닫기를 누르면 `discard` 확인창을 띄우고, 확인하면 `cancel.onAction`을 부른다. `false`면 바로 부른다 |
|
|
95
96
|
| `submit` | `ScreenFlowAction`(`{ label, onAction(), disabled?, pending? }`) | 필수 | `pending`이면 loading·비활성. 그동안 닫기 버튼도 비활성. 본문 `state`가 `ready`가 아니면 저장 비활성 |
|
|
96
97
|
| `cancel` | `ScreenFlowAction` | 필수 | 헤더 시작 쪽 × 아이콘 닫기 버튼(`ScreenLayout` `exit` `close`). `label`은 보조기술 이름이다. 2026-10-17부터 글자 버튼 대신 아이콘 |
|
|
@@ -85,14 +85,14 @@ import { EffectSurface } from "@hjmds/react-native/effect-surface";
|
|
|
85
85
|
| --- | --- | --- |
|
|
86
86
|
| 크기 | 기본 여백은 없다(`padding="none"`). 크기는 children과 `padding`이 정하고 장식 레이어가 그 영역 전체(`absolute`, inset 0)를 덮는다. 넘치는 장식은 `radius` 모서리 안에서 잘린다(`overflow: hidden`). 안에 둔 Surface와 같은 기본 radius라 둥근 카드 모서리 밖으로 장식이 비치지 않는다. 배경은 테마 `bg`다. 높이를 늘리려면 children 높이, Web `layoutStyle`·Native `style`의 높이로 정한다. 장식 레이어는 배치에 참여하지 않는다 | `react/src/effect-surface.tsx`, `react-native/src/effect-surface.tsx` |
|
|
87
87
|
| 간격 | 안쪽 여백은 `padding`(예: `lg` 20), 내용 사이 간격은 children 쪽 [Stack](stack.md)으로 준다. raw 요소에 padding을 직접 주지 않는다 | `react/src/effect-surface.tsx` |
|
|
88
|
-
| 순서·정렬 | children은 일반 레이아웃이다. [Stack](stack.md)으로 eyebrow → [Heading](heading.md) → 소개 → [Button](button.md)을 쌓는다(Showcase 랜딩 예는 `gap="lg"` 20, Heading `
|
|
88
|
+
| 순서·정렬 | children은 일반 레이아웃이다. [Stack](stack.md)으로 eyebrow → [Heading](heading.md) → 소개 → [Button](button.md)을 쌓는다(Showcase 랜딩 예는 `gap="lg"` 20, Heading `level3` 24·h1) | `showcase/web/src/patterns/Landing.stories.tsx` |
|
|
89
89
|
| 고정·스크롤 | 화면 위쪽 히어로 하나에만 둔다. 같은 화면에 여러 개를 겹치거나 목록 행마다 두지 않는다 | — |
|
|
90
90
|
| 좁은 폭·큰 글자 | — | — |
|
|
91
91
|
|
|
92
92
|
```text
|
|
93
93
|
┌────────────────────────────┐ ← EffectSurface(배경 bg + mesh/glow/grain, 터치 없음)
|
|
94
94
|
│ eyebrow │
|
|
95
|
-
│
|
|
95
|
+
│ 제목(Heading level3, 24) │ ← children: 일반 레이아웃(Stack gap lg 20)
|
|
96
96
|
│ 소개 문장 │
|
|
97
97
|
│ [시작하기] primary │
|
|
98
98
|
└────────────────────────────┘
|
|
@@ -70,6 +70,7 @@ import { Field } from "@hjmds/react-native/forms";
|
|
|
70
70
|
|
|
71
71
|
| prop | 값 | 기본값 | 설명 |
|
|
72
72
|
| --- | --- | --- | --- |
|
|
73
|
+
| `initialFocusTarget` | `boolean` | — | 1.28.1. 둘러싼 화면·시트가 진입 포커스를 켰을 때 `true`면 이 칸을 고르고, `false`면 절대 고르지 않는다. 생략하면 트리 순서대로 후보다. 비활성·읽기 전용 칸은 고르지 않는다. 앱별 `autoFocus`·지연 `ref.focus()` 대신 쓴다([진입 포커스](screen-layout.md#진입-포커스)). TextField·TextArea·SearchField·PasswordField 공통 |
|
|
73
74
|
| `variant`(`TextField`) | `surface` · `inset` | `surface` | — |
|
|
74
75
|
| `shape`(`TextField`) | `medium` · `large` · `full` | `medium` | — |
|
|
75
76
|
| `align`(`TextField`) | `start` · `center` | `start` | `center`는 닉네임·코드처럼 짧은 한 값 |
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
|
|
11
11
|
## 언제 쓰나
|
|
12
12
|
|
|
13
|
-
자리를 모르는
|
|
14
|
-
카드·표·빈 상태 안의 제목처럼
|
|
15
|
-
|
|
13
|
+
자리를 모르는 제목 글자 하나가 필요할 때 쓴다. 랜딩 대표 제목, 결과 화면의 제목,
|
|
14
|
+
카드·표·빈 상태 안의 제목처럼 문서 제목 단계를 실제로 내야 하는 자리다. 크기는 `Text`와 같은
|
|
15
|
+
단계(최대 24px)를 쓴다. 주변 여백은 갖지 않는다. 담는 블록이 정한다.
|
|
16
16
|
|
|
17
17
|
## 쓰지 않을 때
|
|
18
18
|
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
// Web
|
|
35
35
|
import { Heading } from "@hjmds/react/heading";
|
|
36
36
|
|
|
37
|
-
<Heading level="
|
|
37
|
+
<Heading level="level3" semanticLevel={2}>{t("result.title")}</Heading>
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
```tsx
|
|
@@ -48,23 +48,23 @@ import { Heading } from "@hjmds/react-native/heading";
|
|
|
48
48
|
|
|
49
49
|
| prop | 값 | 기본값 | 설명 |
|
|
50
50
|
| --- | --- | --- | --- |
|
|
51
|
-
| `level`(필수) | `
|
|
51
|
+
| `level`(필수) | `level3` 24px/500 · `level4` 20px/500 · `level5` 18px/500. `level1`·`level2`는 사용 중단된 이름으로 `level3`과 같은 24px/500이다 | — | recipe 기본은 `level3`이지만 두 renderer 모두 prop을 필수로 받는다. 가장 큰 단계는 24px다 |
|
|
52
52
|
| `semanticLevel` | 1~6 | `level`의 숫자 | 크게 보이지만 구조상 h4인 카드 제목처럼 시각 크기와 문서 단계가 다르면 둘을 따로 적는다 |
|
|
53
53
|
| `layoutStyle` | 배치 key(margin·폭·정렬 등) | — | 바깥 배치만. 크기·줄 높이·굵기·색은 받지 않는다 |
|
|
54
54
|
|
|
55
55
|
- 범위 밖 값은 `TypeError`로 거부된다.
|
|
56
56
|
- 위 크기는 프로필 없는 기본값이다. [디자인 프로필](../../design-profile.md)의
|
|
57
|
-
`tokens.heading
|
|
58
|
-
`
|
|
59
|
-
- `defineHjmDesignProfile`에서 `tokens.typography.heading/titleLarge/title`을 바꾸면
|
|
60
|
-
|
|
61
|
-
값이 우선한다.
|
|
57
|
+
`tokens.heading`으로 시각 단계를 지정하면 양 renderer가 따르며, `semanticLevel`은 그대로다.
|
|
58
|
+
어느 단계도 `typography.heading` 24px를 넘으면 `RangeError`로 거부된다. 크기를 맞추려고 문서 단계를 바꾸지 않는다.
|
|
59
|
+
- `defineHjmDesignProfile`에서 `tokens.typography.heading/titleLarge/title`을 바꾸면
|
|
60
|
+
level1~3/4/5가 그 값을 따른다. 같은 단계에 `tokens.heading`도 지정하면 명시한 heading
|
|
61
|
+
값이 우선한다.
|
|
62
62
|
|
|
63
63
|
## 배치
|
|
64
64
|
|
|
65
65
|
| 항목 | 값 | 근거 |
|
|
66
66
|
| --- | --- | --- |
|
|
67
|
-
| 크기 | 줄 높이: `
|
|
67
|
+
| 크기 | 줄 높이: `level3` 32 · `level4` 28 · `level5` 26 (`level1`·`level2`는 32) | `foundations.ts` `heading`, `typography` |
|
|
68
68
|
| 간격 | Heading은 margin이 0이다(Web `.hjm-heading`). 위아래 간격은 감싸는 Stack·Section의 `gap`이 정한다 | `styles.css` `.hjm-heading` |
|
|
69
69
|
| 순서·정렬 | 한 블록에 Heading은 하나를 맨 위에 둔다 | — |
|
|
70
70
|
| 고정·스크롤 | — | — |
|
|
@@ -88,7 +88,9 @@ import { Heading } from "@hjmds/react-native/heading";
|
|
|
88
88
|
|
|
89
89
|
## 근거
|
|
90
90
|
|
|
91
|
-
2026-10-17 사용자
|
|
91
|
+
2026-10-17 사용자 결정("글자 너무 커", "큰 글자 정책은 없애"): 큰 글자 단계를 폐지했다. Native Storybook 서비스 소개 화면의 히어로 제목(level1 40px)과 구획 제목(level2 32px)이 지나치게 컸던 것이 계기다. `level1`·`level2`는 기존 소비 코드가 깨지지 않도록 이름만 남기고 `level3`과 같은 24/32/500으로 고정했으며 새 코드에서 쓰지 않는다. 위계는 `level3`(24) → `level4`(20) → `level5`(18)로 만들고, 문서 구조는 `semanticLevel`로 적는다. `Top size="large"` 제목도 `heading.level3` 24px다. 브루탈리즘 프리셋은 굵기만 800으로 명시하고 크기는 공통 단계를 따른다.
|
|
92
|
+
|
|
93
|
+
같은 날 앞선 굵기 결정: 제목 단계(`level3`~`level5`)는 500(medium)이며 Native는 `fonts="pretendard"`일 때 `Pretendard-Medium` face로 그린다. 이전의 큰 제목 600(semibold) 단계는 큰 글자 단계와 함께 없어졌다.
|
|
92
94
|
|
|
93
95
|
## 함정
|
|
94
96
|
|
|
@@ -68,6 +68,7 @@ import { ListDetailScreen } from "@hjmds/react-native/screen-flows";
|
|
|
68
68
|
| `back` | `ScreenFlowAction`(`{ label, onAction(), disabled?, pending? }`) | 필수 | 상세 pane 헤더 시작 쪽 ‹ 아이콘 버튼(`ScreenLayout` `exit` `back`). `label`은 보조기술 이름이고 글자는 보이지 않는다. 2026-10-17부터 글자 버튼 대신 아이콘이며 Native `detail.leading`(제품 뒤로 아이콘 대체)은 제거됐다 |
|
|
69
69
|
| `refresh` | `ScreenFlowAction` | 없음 | 목록 pane `actions` 자리의 ghost 버튼. 주면 `actions`를 덮는다 |
|
|
70
70
|
| `loadMore` | `ScreenFlowAction` | 없음 | 목록 pane footer의 ghost 버튼 |
|
|
71
|
+
| `safeAreaBottom` (Native) | 0 이상의 유한수 | `0` | 목록·상세 pane **둘 다**의 ScreenLayout에 넘긴다(두 pane이 같은 창 아래에 닿는다). 1.28.0까지 상세 pane에는 전달되지 않아 상세 본문 끝이 홈 인디케이터 아래에 놓였다(야잘알, 2026-10-17). Web은 `env()`로 자동 |
|
|
71
72
|
| `layoutStyle` | `HjmCompositionStyleProp` | 없음 | Web·Native 모두 목록·상세 pane을 함께 담는 **바깥 틀**에 적용한다(안쪽 목록 ScreenLayout이 아니다). 목록/상세 전환과 상관없이 같은 루트에 남는다(1.13.0) |
|
|
72
73
|
| 나머지 | `ScreenLayout`과 같음(`children`·`footer` 제외) | — | 목록 pane에만 적용된다(`title` 필수, `state`, `scroll` 등) |
|
|
73
74
|
|