@hjmds/design-contracts 1.3.5 → 1.5.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/catalog.d.ts +44 -47
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +13 -13
- package/dist/catalog.js.map +1 -1
- package/dist/colors.js +1 -1
- package/dist/colors.js.map +1 -1
- package/dist/component-recipes.d.ts +6 -0
- package/dist/component-recipes.d.ts.map +1 -1
- package/dist/component-recipes.js +6 -0
- package/dist/component-recipes.js.map +1 -1
- package/dist/design-system-provider.d.ts +4 -3
- package/dist/design-system-provider.d.ts.map +1 -1
- package/dist/design-system-provider.js +4 -3
- package/dist/design-system-provider.js.map +1 -1
- package/dist/palette-contrast.d.ts +19 -0
- package/dist/palette-contrast.d.ts.map +1 -0
- package/dist/palette-contrast.js +46 -0
- package/dist/palette-contrast.js.map +1 -0
- package/dist/showcase.d.ts.map +1 -1
- package/dist/showcase.js +3 -1
- package/dist/showcase.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/brand-boundary.md +89 -0
- package/docs/catalog-freeze.json +120 -0
- package/docs/consumer-policy.md +22 -15
- package/docs/design-system-provider.md +4 -4
- package/docs/generated/component-maturity.md +19 -19
- package/docs/generated/renderer-evidence.json +2275 -1342
- package/docs/generated/renderer-evidence.md +178 -178
- package/docs/generated/showcase-manifest.json +41 -51
- package/docs/product-adoption-1.4.md +48 -0
- package/docs/sheet.md +50 -0
- package/docs/stable-core.md +18 -1
- package/docs/tag.md +7 -8
- package/docs/theme-palette.md +12 -0
- package/docs/theming.md +17 -22
- package/package.json +7 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
|
-
"designSystemVersion": "1.
|
|
3
|
+
"designSystemVersion": "1.5.0",
|
|
4
4
|
"packageName": "@hjmds/design-contracts",
|
|
5
5
|
"components": [
|
|
6
6
|
{
|
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
"name": "Text",
|
|
10
10
|
"category": "foundation",
|
|
11
11
|
"platform": "shared",
|
|
12
|
-
"contractStatus": "
|
|
12
|
+
"contractStatus": "stable",
|
|
13
13
|
"surfaces": {
|
|
14
|
-
"web": "
|
|
15
|
-
"native": "
|
|
14
|
+
"web": "stable",
|
|
15
|
+
"native": "stable"
|
|
16
16
|
},
|
|
17
17
|
"requirements": [
|
|
18
18
|
{
|
|
@@ -144,10 +144,10 @@
|
|
|
144
144
|
"name": "Icon",
|
|
145
145
|
"category": "foundation",
|
|
146
146
|
"platform": "shared",
|
|
147
|
-
"contractStatus": "
|
|
147
|
+
"contractStatus": "stable",
|
|
148
148
|
"surfaces": {
|
|
149
|
-
"web": "
|
|
150
|
-
"native": "
|
|
149
|
+
"web": "stable",
|
|
150
|
+
"native": "stable"
|
|
151
151
|
},
|
|
152
152
|
"requirements": [
|
|
153
153
|
{
|
|
@@ -161,7 +161,6 @@
|
|
|
161
161
|
"scenarios": [
|
|
162
162
|
"default",
|
|
163
163
|
"dark",
|
|
164
|
-
"long-copy",
|
|
165
164
|
"large-text",
|
|
166
165
|
"rtl",
|
|
167
166
|
"reduced-motion",
|
|
@@ -173,7 +172,6 @@
|
|
|
173
172
|
"scenarios": [
|
|
174
173
|
"default",
|
|
175
174
|
"dark",
|
|
176
|
-
"long-copy",
|
|
177
175
|
"large-text",
|
|
178
176
|
"rtl",
|
|
179
177
|
"reduced-motion",
|
|
@@ -257,7 +255,6 @@
|
|
|
257
255
|
"scenarios": [
|
|
258
256
|
"default",
|
|
259
257
|
"dark",
|
|
260
|
-
"long-copy",
|
|
261
258
|
"large-text",
|
|
262
259
|
"rtl",
|
|
263
260
|
"reduced-motion",
|
|
@@ -269,7 +266,6 @@
|
|
|
269
266
|
"scenarios": [
|
|
270
267
|
"default",
|
|
271
268
|
"dark",
|
|
272
|
-
"long-copy",
|
|
273
269
|
"large-text",
|
|
274
270
|
"rtl",
|
|
275
271
|
"reduced-motion",
|
|
@@ -336,10 +332,10 @@
|
|
|
336
332
|
"name": "Stack",
|
|
337
333
|
"category": "layout",
|
|
338
334
|
"platform": "shared",
|
|
339
|
-
"contractStatus": "
|
|
335
|
+
"contractStatus": "stable",
|
|
340
336
|
"surfaces": {
|
|
341
|
-
"web": "
|
|
342
|
-
"native": "
|
|
337
|
+
"web": "stable",
|
|
338
|
+
"native": "stable"
|
|
343
339
|
},
|
|
344
340
|
"requirements": [
|
|
345
341
|
{
|
|
@@ -384,10 +380,10 @@
|
|
|
384
380
|
"name": "Container",
|
|
385
381
|
"category": "layout",
|
|
386
382
|
"platform": "shared",
|
|
387
|
-
"contractStatus": "
|
|
383
|
+
"contractStatus": "stable",
|
|
388
384
|
"surfaces": {
|
|
389
|
-
"web": "
|
|
390
|
-
"native": "
|
|
385
|
+
"web": "stable",
|
|
386
|
+
"native": "stable"
|
|
391
387
|
},
|
|
392
388
|
"requirements": [
|
|
393
389
|
{
|
|
@@ -743,10 +739,10 @@
|
|
|
743
739
|
"name": "IconButton",
|
|
744
740
|
"category": "action",
|
|
745
741
|
"platform": "shared",
|
|
746
|
-
"contractStatus": "
|
|
742
|
+
"contractStatus": "stable",
|
|
747
743
|
"surfaces": {
|
|
748
|
-
"web": "
|
|
749
|
-
"native": "
|
|
744
|
+
"web": "stable",
|
|
745
|
+
"native": "stable"
|
|
750
746
|
},
|
|
751
747
|
"requirements": [
|
|
752
748
|
{
|
|
@@ -760,7 +756,6 @@
|
|
|
760
756
|
"scenarios": [
|
|
761
757
|
"default",
|
|
762
758
|
"dark",
|
|
763
|
-
"long-copy",
|
|
764
759
|
"large-text",
|
|
765
760
|
"rtl",
|
|
766
761
|
"reduced-motion",
|
|
@@ -772,7 +767,6 @@
|
|
|
772
767
|
"scenarios": [
|
|
773
768
|
"default",
|
|
774
769
|
"dark",
|
|
775
|
-
"long-copy",
|
|
776
770
|
"large-text",
|
|
777
771
|
"rtl",
|
|
778
772
|
"reduced-motion",
|
|
@@ -3137,10 +3131,10 @@
|
|
|
3137
3131
|
"name": "Badge",
|
|
3138
3132
|
"category": "data-display",
|
|
3139
3133
|
"platform": "shared",
|
|
3140
|
-
"contractStatus": "
|
|
3134
|
+
"contractStatus": "stable",
|
|
3141
3135
|
"surfaces": {
|
|
3142
|
-
"web": "
|
|
3143
|
-
"native": "
|
|
3136
|
+
"web": "stable",
|
|
3137
|
+
"native": "stable"
|
|
3144
3138
|
},
|
|
3145
3139
|
"requirements": [
|
|
3146
3140
|
{
|
|
@@ -3233,10 +3227,10 @@
|
|
|
3233
3227
|
"name": "Card",
|
|
3234
3228
|
"category": "data-display",
|
|
3235
3229
|
"platform": "shared",
|
|
3236
|
-
"contractStatus": "
|
|
3230
|
+
"contractStatus": "stable",
|
|
3237
3231
|
"surfaces": {
|
|
3238
|
-
"web": "
|
|
3239
|
-
"native": "
|
|
3232
|
+
"web": "stable",
|
|
3233
|
+
"native": "stable"
|
|
3240
3234
|
},
|
|
3241
3235
|
"requirements": [
|
|
3242
3236
|
{
|
|
@@ -3895,10 +3889,10 @@
|
|
|
3895
3889
|
"name": "Tag",
|
|
3896
3890
|
"category": "data-display",
|
|
3897
3891
|
"platform": "shared",
|
|
3898
|
-
"contractStatus": "
|
|
3892
|
+
"contractStatus": "stable",
|
|
3899
3893
|
"surfaces": {
|
|
3900
|
-
"web": "
|
|
3901
|
-
"native": "
|
|
3894
|
+
"web": "stable",
|
|
3895
|
+
"native": "stable"
|
|
3902
3896
|
},
|
|
3903
3897
|
"requirements": [
|
|
3904
3898
|
{
|
|
@@ -4028,10 +4022,10 @@
|
|
|
4028
4022
|
"name": "Notice",
|
|
4029
4023
|
"category": "feedback",
|
|
4030
4024
|
"platform": "shared",
|
|
4031
|
-
"contractStatus": "
|
|
4025
|
+
"contractStatus": "stable",
|
|
4032
4026
|
"surfaces": {
|
|
4033
|
-
"web": "
|
|
4034
|
-
"native": "
|
|
4027
|
+
"web": "stable",
|
|
4028
|
+
"native": "stable"
|
|
4035
4029
|
},
|
|
4036
4030
|
"requirements": [
|
|
4037
4031
|
{
|
|
@@ -4076,10 +4070,10 @@
|
|
|
4076
4070
|
"name": "Progress",
|
|
4077
4071
|
"category": "feedback",
|
|
4078
4072
|
"platform": "shared",
|
|
4079
|
-
"contractStatus": "
|
|
4073
|
+
"contractStatus": "stable",
|
|
4080
4074
|
"surfaces": {
|
|
4081
|
-
"web": "
|
|
4082
|
-
"native": "
|
|
4075
|
+
"web": "stable",
|
|
4076
|
+
"native": "stable"
|
|
4083
4077
|
},
|
|
4084
4078
|
"requirements": [
|
|
4085
4079
|
{
|
|
@@ -4124,10 +4118,10 @@
|
|
|
4124
4118
|
"name": "Spinner",
|
|
4125
4119
|
"category": "feedback",
|
|
4126
4120
|
"platform": "shared",
|
|
4127
|
-
"contractStatus": "
|
|
4121
|
+
"contractStatus": "stable",
|
|
4128
4122
|
"surfaces": {
|
|
4129
|
-
"web": "
|
|
4130
|
-
"native": "
|
|
4123
|
+
"web": "stable",
|
|
4124
|
+
"native": "stable"
|
|
4131
4125
|
},
|
|
4132
4126
|
"requirements": [
|
|
4133
4127
|
{
|
|
@@ -4141,7 +4135,6 @@
|
|
|
4141
4135
|
"scenarios": [
|
|
4142
4136
|
"default",
|
|
4143
4137
|
"dark",
|
|
4144
|
-
"long-copy",
|
|
4145
4138
|
"large-text",
|
|
4146
4139
|
"rtl",
|
|
4147
4140
|
"reduced-motion",
|
|
@@ -4153,7 +4146,6 @@
|
|
|
4153
4146
|
"scenarios": [
|
|
4154
4147
|
"default",
|
|
4155
4148
|
"dark",
|
|
4156
|
-
"long-copy",
|
|
4157
4149
|
"large-text",
|
|
4158
4150
|
"rtl",
|
|
4159
4151
|
"reduced-motion",
|
|
@@ -4172,10 +4164,10 @@
|
|
|
4172
4164
|
"name": "Skeleton",
|
|
4173
4165
|
"category": "feedback",
|
|
4174
4166
|
"platform": "shared",
|
|
4175
|
-
"contractStatus": "
|
|
4167
|
+
"contractStatus": "stable",
|
|
4176
4168
|
"surfaces": {
|
|
4177
|
-
"web": "
|
|
4178
|
-
"native": "
|
|
4169
|
+
"web": "stable",
|
|
4170
|
+
"native": "stable"
|
|
4179
4171
|
},
|
|
4180
4172
|
"requirements": [
|
|
4181
4173
|
{
|
|
@@ -4189,7 +4181,6 @@
|
|
|
4189
4181
|
"scenarios": [
|
|
4190
4182
|
"default",
|
|
4191
4183
|
"dark",
|
|
4192
|
-
"long-copy",
|
|
4193
4184
|
"large-text",
|
|
4194
4185
|
"rtl",
|
|
4195
4186
|
"reduced-motion",
|
|
@@ -4201,7 +4192,6 @@
|
|
|
4201
4192
|
"scenarios": [
|
|
4202
4193
|
"default",
|
|
4203
4194
|
"dark",
|
|
4204
|
-
"long-copy",
|
|
4205
4195
|
"large-text",
|
|
4206
4196
|
"rtl",
|
|
4207
4197
|
"reduced-motion",
|
|
@@ -4543,7 +4533,7 @@
|
|
|
4543
4533
|
],
|
|
4544
4534
|
"documentation": {
|
|
4545
4535
|
"generated": "docs/generated/component-maturity.md",
|
|
4546
|
-
"authored":
|
|
4536
|
+
"authored": "docs/sheet.md"
|
|
4547
4537
|
}
|
|
4548
4538
|
},
|
|
4549
4539
|
{
|
|
@@ -4796,10 +4786,10 @@
|
|
|
4796
4786
|
"name": "DesignSystemProvider",
|
|
4797
4787
|
"category": "provider",
|
|
4798
4788
|
"platform": "shared",
|
|
4799
|
-
"contractStatus": "
|
|
4789
|
+
"contractStatus": "stable",
|
|
4800
4790
|
"surfaces": {
|
|
4801
|
-
"web": "
|
|
4802
|
-
"native": "
|
|
4791
|
+
"web": "stable",
|
|
4792
|
+
"native": "stable"
|
|
4803
4793
|
},
|
|
4804
4794
|
"requirements": [
|
|
4805
4795
|
{
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# 1.4 제품 채택 가이드
|
|
2
|
+
|
|
3
|
+
2026-09-23 소비 감사에서 기존 HJM 대신 같은 설정·로그인·Sheet를 제품마다 조립하는 사례를
|
|
4
|
+
확인했다. 새 도메인 컴포넌트를 추가하는 대신 실제 우회를 없앨 수 있는 API와 조합을 보완한다.
|
|
5
|
+
|
|
6
|
+
## 로그인
|
|
7
|
+
|
|
8
|
+
`AuthScreenLayout`의 hero/main/footer를 제품의 기존 내용으로 채운다. 로고, 제공자 목록,
|
|
9
|
+
심사자 폼 노출 조건, 동의와 정책 링크는 제품 소유다. 제공자 로고를 HJM 패키지로 옮기지 않는다.
|
|
10
|
+
Native의 외부 배치는 `layoutStyle`, UI 식별은 `testID`를 사용한다. 키보드가 열린 입력 폼에서도
|
|
11
|
+
버튼 터치를 받으며 iOS의 키보드 스크롤 여백을 제공한다. 긴 내용은 축소하지 않고 스크롤한다.
|
|
12
|
+
부모도 keyboard avoidance를 한다면 같은 clearance를 중복 적용하지 않도록 소비 화면에서 검증한다.
|
|
13
|
+
Web의 상위 앱 셸에 이미 main landmark가 있으면 `as="section"`을 사용한다. 이 옵션은
|
|
14
|
+
다에리 WebShell의 main 안에 로그인 레이아웃을 채택할 때 중첩 main을 피하기 위해 추가했다.
|
|
15
|
+
|
|
16
|
+
## 설정 한 행
|
|
17
|
+
|
|
18
|
+
`Switch presentation="row"`에 label/description/checked/onCheckedChange를 전달하면 행 전체가
|
|
19
|
+
한 개의 switch다. 다른 Pressable이나 button으로 감싸지 않는다. 표시 이름과 설명은 접근성에서도
|
|
20
|
+
분리하고, Web의 기존 aria-describedby가 있으면 설명을 함께 연결한다. 공통 large-text 기준(160%)에서는
|
|
21
|
+
설명 아래로 track을 내려 좁은 텍스트 열을 만들지 않는다. DOM의 순서 차이는 키보드 tab stop을
|
|
22
|
+
추가하지 않는다.
|
|
23
|
+
|
|
24
|
+
Web의 기본은 기존 inline, Native의 기본은 기존 row다. 제품 간 같은 설정 화면은 presentation을
|
|
25
|
+
명시한다. 기존 ListRow의 trailing control만 필요한 경우 `labelVisibility="hidden"`을 유지할 수 있다.
|
|
26
|
+
그 경우 ListRow에는 별도 onPress를 두지 않는다.
|
|
27
|
+
|
|
28
|
+
## 상태 화면
|
|
29
|
+
|
|
30
|
+
상태를 하나의 error boolean으로 뭉치지 않는다. 아래 조합은 제품 adapter에 두며 데이터 조회나
|
|
31
|
+
재시도 로직을 HJM에 넣지 않는다.
|
|
32
|
+
|
|
33
|
+
| 상태 | 조합 | 제품 책임 |
|
|
34
|
+
| --- | --- | --- |
|
|
35
|
+
| 최초 로딩 | Skeleton + 기다리는 대상 설명 | 실제 화면 높이/형태, 중복 announcement 방지 |
|
|
36
|
+
| 빈 결과 | EmptyState + 다음 행동 | 검색 0건과 데이터 미생성을 구분 |
|
|
37
|
+
| 구역 실패 | Notice + 재시도 | 다른 정상 구역은 유지 |
|
|
38
|
+
| 이전 데이터 있음 | 기존 콘텐츠 + Notice | stale 배지, 새로고침, 중복 재시도 방지 |
|
|
39
|
+
| 전체 흐름 종료 | Result | 다음 목적지와 결과 문구 |
|
|
40
|
+
|
|
41
|
+
## 호환성과 채택 검증
|
|
42
|
+
|
|
43
|
+
세 패키지의 exact version, dependency/lock/contract/catalog를 함께 갱신한다. 기존 beta의
|
|
44
|
+
status를 자동으로 stable로 바꾸지 않는다. 실제 쓰는 beta는 소비 ADR/evidence에 연결한다.
|
|
45
|
+
Native raw style API는 아직 호환 지원이며 [소비 정책](consumer-policy.md)의 다음 major 절차를 따른다.
|
|
46
|
+
|
|
47
|
+
Sheet 이관은 [입력 화면 계약](sheet.md)을 따른다. 각 제품은 typecheck/build/관련 동작 테스트와
|
|
48
|
+
실제 화면 검증을 구별해 기록한다. 패키지 테스트 통과가 소비자·배포·기기 완료를 뜻하지 않는다.
|
package/docs/sheet.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Sheet 입력 화면과 가용 영역
|
|
2
|
+
|
|
3
|
+
## 문제와 결정
|
|
4
|
+
|
|
5
|
+
2026-09-23 소비 조사에서 BurnTok 모바일 `AppSheet.tsx:93`은 제목을 수직 정렬하기 위해
|
|
6
|
+
ReactNode를 `as unknown as string`으로 전달했다. Web도 `.hjm-sheet__header`를 덮어썼다.
|
|
7
|
+
같은 어댑터가 키보드 높이, top inset, 본문 maxHeight를 별도로 보정하고 있었다.
|
|
8
|
+
|
|
9
|
+
1.4는 제목·닫기 컨트롤을 헤더 중앙에 정렬한다. 제목은 여전히 문자열이다. 임의의 제목 slot을
|
|
10
|
+
추가하면 접근성 이름과 표시가 서로 어긋날 수 있어 기본 renderer 정렬을 고치는 쪽을 택했다.
|
|
11
|
+
|
|
12
|
+
Native Sheet는 `keyboardAvoidance`와 `scrollable`을 제공한다. 둘 다 기본 false다.
|
|
13
|
+
이미 키보드를 처리하거나 FlatList를 넣는 소비자를 minor에서 이중 처리하지 않기 위한 선택이다.
|
|
14
|
+
입력 폼은 두 옵션을 함께 켜고 제품의 키보드 listener·강제 maxHeight·중첩 ScrollView를 제거한다.
|
|
15
|
+
|
|
16
|
+
## 계약과 플랫폼 번역
|
|
17
|
+
|
|
18
|
+
- 제목/닫기와 footer는 스크롤 본문 바깥에 둔다. body만 줄어들고 스크롤한다.
|
|
19
|
+
- top safe area는 화면의 가용 높이를 제한한다. bottom sheet 내부에 노치 높이를 더하지 않는다.
|
|
20
|
+
- Native modal의 실제 onLayout 높이를 사용한다. Android adjustResize가 이미 줄인 높이에
|
|
21
|
+
키보드 높이를 한 번 더 빼지 않는다.
|
|
22
|
+
- 열린 키보드 위에 Sheet가 나타나도 `Keyboard.metrics()`로 현재 상태를 읽는다.
|
|
23
|
+
- iOS frame change, Android didShow/didHide를 구독하고 종료 시 해제한다.
|
|
24
|
+
- 화면 폭보다 좁은 floating keyboard에는 화면 전체를 밀어 올리지 않는다.
|
|
25
|
+
- docked keyboard가 있으면 home indicator inset을 키보드 위에 중복 추가하지 않는다.
|
|
26
|
+
- Web은 기존 scroll body·viewport CSS·focus trap을 유지하며 헤더 정렬을 함께 고친다.
|
|
27
|
+
- busy, close reason, Modal dismiss completion, focus return 계약은 유지한다.
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
<Sheet open={open} title="설정" closeLabel="닫기"
|
|
31
|
+
keyboardAvoidance scrollable safeAreaInsets={insets}
|
|
32
|
+
onOpenChange={setOpen} footer={<Button onPress={save}>저장</Button>}>
|
|
33
|
+
<TextField label="이름" value={name} onValueChange={setName} />
|
|
34
|
+
</Sheet>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`scrollable` 본문 안에 가상화 목록을 중첩하지 않는다. 목록 자체가 스크롤 소유자인 경우
|
|
38
|
+
`scrollable={false}`로 두고 목록의 flex/keyboard 연결을 제품 adapter에서 담당한다.
|
|
39
|
+
|
|
40
|
+
## 증거와 남은 확인
|
|
41
|
+
|
|
42
|
+
- `packages/react-native/test/sheet-viewport.test.tsx`: 키보드 사전 표시, frame 변경, resize,
|
|
43
|
+
floating keyboard, listener 해제, footer/body 경계, safe area와 title 이름.
|
|
44
|
+
- `packages/react-native/test/modal-lifecycle-fallback.test.tsx`: 종료/후속 surface/focus 회귀.
|
|
45
|
+
- `packages/react/test/sheet-layout.browser.test.tsx`: 320px의 제목/닫기 기하와 100/200% 글자.
|
|
46
|
+
- 위 Native proof는 host mock이다. 소비 앱 Device Hub 및 Android의 실제 키보드 동작은
|
|
47
|
+
별도로 기록하며 mock 통과를 기기 증거로 삼지 않는다.
|
|
48
|
+
|
|
49
|
+
참조: [React Native Keyboard](https://reactnative.dev/docs/keyboard),
|
|
50
|
+
[ScrollView](https://reactnative.dev/docs/scrollview).
|
package/docs/stable-core.md
CHANGED
|
@@ -1,4 +1,21 @@
|
|
|
1
|
-
# Stable Core
|
|
1
|
+
# Stable Core
|
|
2
|
+
|
|
3
|
+
## 1.5.0 승격
|
|
4
|
+
|
|
5
|
+
다음 13개를 stable로 올렸습니다: `Text`, `Icon`, `Stack`, `Container`, `DesignSystemProvider`,
|
|
6
|
+
`IconButton`, `Badge`, `Card`, `Tag`, `Notice`, `Progress`, `Spinner`, `Skeleton`.
|
|
7
|
+
|
|
8
|
+
- 근거: 1.5.0에서 renderer 시나리오 증거를 이름뿐인 검사에서 실제 검사로 바꿨습니다
|
|
9
|
+
(Web `test/scenario-matrix.browser.test.tsx`, Native `test/scenario-matrix.test.tsx`). 두 renderer에서
|
|
10
|
+
요구 시나리오가 모두 통과하고 세 제품 이상이 쓰는 컴포넌트만 올렸습니다.
|
|
11
|
+
- 이 전환으로 Web의 "모든 시나리오 증거 완비"는 33개에서 16개로 줄었다가 승격 대상 보강 뒤
|
|
12
|
+
24개가 됐습니다. 줄어든 것은 이전 수치가 과대 표시였기 때문입니다.
|
|
13
|
+
- 아이콘·로딩 표시·구분선처럼 보이는 글자 슬롯이 없는 컴포넌트는 long-copy 요구에서 뺐습니다
|
|
14
|
+
(`showcase.ts`의 `textlessComponentNames`).
|
|
15
|
+
- keyboard·platform-parity를 요구하는 컴포넌트(Checkbox, Switch, Dialog, Sheet 등)는 그 증거가
|
|
16
|
+
아직 없어 beta로 남습니다. 생성된 evidence 문서의 "Missing required scenarios"가 남은 일입니다.
|
|
17
|
+
|
|
18
|
+
## 0.8 첫 stable slice
|
|
2
19
|
|
|
3
20
|
첫 renderer stable slice는 `Surface`, `Button`, `Field`, `TextArea`다. 이 네 컴포넌트는
|
|
4
21
|
계약이 이미 stable이고 Web/RN renderer가 같은 public intent를 실행한다.
|
package/docs/tag.md
CHANGED
|
@@ -47,11 +47,10 @@ recipe가 안정화되면 그쪽에 위임하고, 이 계약에는 넣지 않습
|
|
|
47
47
|
|
|
48
48
|
## 현재 maturity와 남은 증거
|
|
49
49
|
|
|
50
|
-
Tag contract와 Web/Native surface는 모두 **
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
환경 증거까지 연결한 뒤 stable 승격을 검토합니다.
|
|
50
|
+
Tag contract와 Web/Native surface는 모두 **stable**입니다(1.5.0). 두 first-party renderer의
|
|
51
|
+
scenario matrix가 dark(라이트 전용 색 누수 없음), 2배 글자, RTL, 모션 줄이기, 접근 이름,
|
|
52
|
+
긴 문구 줄바꿈을 실제 계산 스타일로 검사하고 모두 통과했습니다. Web은 실제 브라우저에서
|
|
53
|
+
배치까지, Native는 test renderer의 style 값까지 봅니다.
|
|
54
|
+
|
|
55
|
+
stable은 전체 환경 인증을 뜻하지 않습니다. screen reader 실사용과 실제 device screenshot은
|
|
56
|
+
제품 기기 QA의 범위입니다.
|
package/docs/theme-palette.md
CHANGED
|
@@ -50,3 +50,15 @@ contracts·tree-select까지 연쇄로 초과).
|
|
|
50
50
|
형태로 보이는지(3:1). 기존 AA 검사가 못 잡던 축이다.
|
|
51
51
|
- `keeps neutral surfaces and text in one low-saturation family per theme` — 중성 역할의
|
|
52
52
|
채도 상한 30%. 강조 역할(`primary`·`contentBrand`·`danger`·`surfaceAccent`)은 제외한다.
|
|
53
|
+
|
|
54
|
+
## 라이트 `textSub`를 AA로 올렸다 (2026-09-26)
|
|
55
|
+
|
|
56
|
+
브랜드 팔레트 대비 검사([brand-boundary.md](./brand-boundary.md))를 만들면서 기본 팔레트에 같은 기준을 돌렸더니
|
|
57
|
+
라이트 `textSub` `#6b7684`가 `surface`(`#f2f4f6`) 위에서 **4.19:1**로 본문 AA(4.5)에 못 미쳤다. `textSub`는
|
|
58
|
+
`Text tone="subtle"`의 실제 글자색이라 장식 등급으로 볼 수 없다. 같은 색상·채도에서 명도만 내린 `#65707d`
|
|
59
|
+
(`surface` 4.57:1, `bg` 5.04:1)로 바꿨다. 텍스트 램프 순서(`textMuted` > `textSub` > `textWeak`)는 유지된다.
|
|
60
|
+
모펀은 같은 이유로 이미 `textSub`를 `#626E7D`로 보정해 쓰고 있었다.
|
|
61
|
+
|
|
62
|
+
`borderControl`은 `#6b7684`를 그대로 둔다. 비텍스트 기준(3:1)이고 `surface` 위 4.19:1로 충분하다.
|
|
63
|
+
|
|
64
|
+
**버린 대안:** 검사에서 `textSub`를 본문 기준에서 빼기. 실제 글자에 쓰이는 색을 기준에서 빼면 검사가 존재 이유를 잃는다.
|
package/docs/theming.md
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
HJM은 `theme`(light/dark/system) 같은 **환경**과, 그 환경이 해석된 **값**을 분리해서
|
|
4
4
|
받는다. 제품 브랜드색은 값 쪽에 넣는다. 이 문서는 새 제품이 처음 부딪히는 그 경로만
|
|
5
|
-
설명한다.
|
|
6
|
-
|
|
5
|
+
설명한다. **무엇을 어디까지 바꿀 수 있는지와 대비 검사 규칙은 [brand-boundary.md](./brand-boundary.md)가
|
|
6
|
+
단일 원본이다.** 팔레트를 어떻게 고를지는 [theme-palette.md](./theme-palette.md), 색의 의미 구분은
|
|
7
|
+
[identity.md](./identity.md)에 있다.
|
|
7
8
|
|
|
8
9
|
## 두 가지 사용 방식
|
|
9
10
|
|
|
@@ -52,29 +53,23 @@ function ProductProvider({ preference, systemDark, children }) {
|
|
|
52
53
|
}
|
|
53
54
|
```
|
|
54
55
|
|
|
55
|
-
React Native는 `HjmNativeProvider`가 같은 `value`를 받는다.
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
React Native는 `HjmNativeProvider`가 같은 `value`를 받는다. `value`를 넘기면 Provider가 OS 설정 관찰을
|
|
57
|
+
멈추므로, 위 예시처럼 system theme 등의 신호를 제품이 구독해 resolver에 넣는다. 실제 사용 예는 BurnTok의
|
|
58
|
+
`apps/web/src/components/ThemeProvider.tsx`(경계선 두 key)와 `apps/mobile/src/components/ThemeProvider.tsx`
|
|
59
|
+
(경계선 두 key + 표면 두 key)다.
|
|
60
|
+
|
|
61
|
+
팔레트를 바꾸면 제품 테스트에서 대비 검사를 돌린다.
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { checkBrandPaletteContrast } from "@hjmds/design-contracts/palette-contrast";
|
|
65
|
+
|
|
66
|
+
expect(checkBrandPaletteContrast(PRODUCT_BRAND_PALETTE)).toEqual({ light: [], dark: [] });
|
|
67
|
+
```
|
|
58
68
|
|
|
59
69
|
## 덮어도 되는 것과 아닌 것
|
|
60
70
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
| `primary` / `onPrimary` / `contentBrand` | 권장 | 브랜드의 자리다. 주 행동과 현재 위치를 이 색이 말한다 |
|
|
64
|
-
| `borderControl` / `focus` | 선택 | 브랜드 채도가 높으면 포커스 대비를 맞추기 위해 함께 조정한다 |
|
|
65
|
-
| `border` / `borderControl` | 선택 | 제품 경계선 밀도가 다를 때. BurnTok이 이 둘만 주입한다 |
|
|
66
|
-
| `bg` / `surface` / `text*` 중성 계열 | 신중히 | 대비 검증이 붙어 있다. 바꾸면 라이트·다크 양쪽에서 4.5:1을 다시 확인한다 |
|
|
67
|
-
| `danger` / `success` / `warning` / `info` | 비권장 | 상태색을 브랜드색으로 바꾸면 "오류"와 "브랜드"가 같은 색이 된다 |
|
|
68
|
-
| 컴포넌트별 색 | 불가 | recipe가 semantic key만 읽는다. 컴포넌트 하나만 다른 색이 되면 그것은 제품 예외지 테마가 아니다 |
|
|
69
|
-
|
|
70
|
-
## 하지 말아야 할 세 가지
|
|
71
|
-
|
|
72
|
-
1. **CSS로 `.hjm-*` 클래스를 덮거나 `--hjm-color-*`를 인라인 style로 재정의하지 않는다.** 그 순간 업그레이드마다 깨진다. 필요한 것이
|
|
73
|
-
semantic key로 표현되지 않으면 그것은 계약 공백이고, 우회가 아니라 이슈로 올린다.
|
|
74
|
-
2. **recipe 값을 읽어 인라인 스타일로 다시 싣지 않는다.** 렌더러가 이미 그 값을
|
|
75
|
-
칠한다. 제품이 다시 실으면 두 벌이 생기고 한쪽만 갱신된다.
|
|
76
|
-
3. **제공자 브랜드색을 팔레트에 넣지 않는다.** 소셜 로그인 색은 테마가 아니라 남의
|
|
77
|
-
자산이다 — [AuthProviderButton](./provider-button.md)이 그 자리를 갖는다.
|
|
71
|
+
규칙은 [brand-boundary.md](./brand-boundary.md)에 있다. 요약하면 `brandPalette`의 17개 semantic key만 바꿀 수 있고,
|
|
72
|
+
상태 강조색과 컴포넌트별 색은 바꿀 수 없으며, `.hjm-*`·`--hjm-*` CSS 재정의는 지원하는 경로가 아니다.
|
|
78
73
|
|
|
79
74
|
## 어댑터를 두는 이유
|
|
80
75
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hjmds/design-contracts",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Renderer-neutral design contracts, tokens, recipes, and behaviors shared by HJM products.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -46,6 +46,12 @@
|
|
|
46
46
|
"import": "./dist/color-references.js",
|
|
47
47
|
"default": "./dist/color-references.js"
|
|
48
48
|
},
|
|
49
|
+
"./palette-contrast": {
|
|
50
|
+
"types": "./dist/palette-contrast.d.ts",
|
|
51
|
+
"react-native": "./dist/palette-contrast.js",
|
|
52
|
+
"import": "./dist/palette-contrast.js",
|
|
53
|
+
"default": "./dist/palette-contrast.js"
|
|
54
|
+
},
|
|
49
55
|
"./responsive": {
|
|
50
56
|
"types": "./dist/responsive.d.ts",
|
|
51
57
|
"react-native": "./dist/responsive.js",
|