@hjmds/design-contracts 1.33.2 → 1.34.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/tag.d.ts CHANGED
@@ -19,7 +19,7 @@ export type ResolvedTagDescriptor = Readonly<{
19
19
  export declare function validateTagDescriptor(descriptor: TagDescriptor): void;
20
20
  export declare function resolveTagDescriptor(descriptor: TagDescriptor): ResolvedTagDescriptor;
21
21
  /**
22
- * Rectangular, not the pill `radius.full` that `chipRecipe` and `badgeRecipe`
22
+ * Rectangular, not the pill `radius.full` that `chipRecipe`
23
23
  * use, so a static Tag never reads as a pressable Chip or a status Badge at a
24
24
  * glance. No `states` and no `focus` contract: nothing here is ever pressed
25
25
  * or focused.
package/dist/tag.js CHANGED
@@ -31,7 +31,7 @@ export function resolveTagDescriptor(descriptor) {
31
31
  };
32
32
  }
33
33
  /**
34
- * Rectangular, not the pill `radius.full` that `chipRecipe` and `badgeRecipe`
34
+ * Rectangular, not the pill `radius.full` that `chipRecipe`
35
35
  * use, so a static Tag never reads as a pressable Chip or a status Badge at a
36
36
  * glance. No `states` and no `focus` contract: nothing here is ever pressed
37
37
  * or focused.
package/dist/tag.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tag.js","sourceRoot":"","sources":["../src/tag.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n resolveColorReference,\n type ColorReferencePalette,\n} from \"./color-references.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\nimport { fontWeight, spacing, stroke } from \"./foundations.js\";\n\nexport type TagTone = \"neutral\" | \"info\" | \"success\" | \"attention\" | \"brand\";\n\nexport const tagDefaults = {\n tone: \"neutral\",\n} as const satisfies Readonly<{ tone: TagTone }>;\n\n/**\n * A single piece of static metadata (a position, a grade, a season). Tag has\n * no interaction axis at all — no selected, no pressed, no closable. Removing\n * or toggling a tag is deselection, which belongs to `Chip`.\n */\nexport type TagDescriptor = Readonly<{\n label: string;\n tone?: TagTone;\n}>;\n\nexport type ResolvedTagDescriptor = Readonly<{\n label: string;\n tone: TagTone;\n}>;\n\nfunction assertCopy(value: string, field: string): void {\n if (value.trim().length === 0) {\n throw new TypeError(`Tag ${field} must not be empty`);\n }\n}\n\nconst supportedTones: readonly TagTone[] = [\n \"neutral\",\n \"info\",\n \"success\",\n \"attention\",\n \"brand\",\n];\n\nexport function validateTagDescriptor(descriptor: TagDescriptor): void {\n assertCopy(descriptor.label, \"label\");\n if (\n descriptor.tone !== undefined &&\n !supportedTones.includes(descriptor.tone)\n ) {\n throw new TypeError(`Unsupported Tag tone: ${String(descriptor.tone)}`);\n }\n}\n\nexport function resolveTagDescriptor(\n descriptor: TagDescriptor,\n): ResolvedTagDescriptor {\n validateTagDescriptor(descriptor);\n return {\n label: descriptor.label,\n tone: descriptor.tone ?? tagDefaults.tone,\n };\n}\n\n/**\n * Rectangular, not the pill `radius.full` that `chipRecipe` and `badgeRecipe`\n * use, so a static Tag never reads as a pressable Chip or a status Badge at a\n * glance. No `states` and no `focus` contract: nothing here is ever pressed\n * or focused.\n */\nexport const tagRecipe = {\n slots: [\"root\", \"label\"] as const,\n defaults: tagDefaults,\n tones: {\n // Same as badgeRecipe: a canvas plate without a border vanished on the canvas (2026-10-17 review E3).\n neutral: {\n background: semanticColors.canvas,\n content: semanticColors.content.secondary,\n border: semanticColors.border.default,\n },\n brand: {\n background: semanticColors.canvas,\n content: semanticColors.content.brand,\n border: semanticColors.border.default,\n },\n info: {\n background: semanticColors.canvas,\n content: semanticColors.feedback.info.foreground,\n border: semanticColors.feedback.info.border,\n },\n success: {\n background: semanticColors.feedback.success.badgeBackground,\n content: semanticColors.feedback.success.foreground,\n border: semanticColors.feedback.success.border,\n },\n attention: {\n background: semanticColors.feedback.attention.badgeBackground,\n content: semanticColors.feedback.attention.foreground,\n border: semanticColors.feedback.attention.border,\n },\n },\n size: {\n // Static metadata still participates in Dynamic Type. A fixed 20px frame\n // clips the label at accessibility sizes; minHeight preserves the compact\n // base shape while allowing the intrinsic line box to grow.\n minHeight: 20,\n paddingHorizontal: spacing.xxs,\n gap: spacing.xxs,\n textVariant: \"caption\",\n fontWeight: fontWeight.medium,\n },\n radius: \"sm\",\n borderWidth: stroke.default,\n} as const;\n\nexport type ResolvedTagPresentation = Readonly<{\n background: string;\n content: string;\n border: string | null;\n}>;\n\n/** Resolve the same Tag recipe for non-CSS renderers. */\nexport function resolveTagPresentation(\n tone: TagTone,\n palette: ColorReferencePalette,\n): ResolvedTagPresentation {\n const contract = tagRecipe.tones[tone];\n return {\n background: resolveColorReference(contract.background, palette),\n content: resolveColorReference(contract.content, palette),\n border:\n contract.border === null\n ? null\n : resolveColorReference(contract.border, palette),\n };\n}\n"]}
1
+ {"version":3,"file":"tag.js","sourceRoot":"","sources":["../src/tag.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n resolveColorReference,\n type ColorReferencePalette,\n} from \"./color-references.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\nimport { fontWeight, spacing, stroke } from \"./foundations.js\";\n\nexport type TagTone = \"neutral\" | \"info\" | \"success\" | \"attention\" | \"brand\";\n\nexport const tagDefaults = {\n tone: \"neutral\",\n} as const satisfies Readonly<{ tone: TagTone }>;\n\n/**\n * A single piece of static metadata (a position, a grade, a season). Tag has\n * no interaction axis at all — no selected, no pressed, no closable. Removing\n * or toggling a tag is deselection, which belongs to `Chip`.\n */\nexport type TagDescriptor = Readonly<{\n label: string;\n tone?: TagTone;\n}>;\n\nexport type ResolvedTagDescriptor = Readonly<{\n label: string;\n tone: TagTone;\n}>;\n\nfunction assertCopy(value: string, field: string): void {\n if (value.trim().length === 0) {\n throw new TypeError(`Tag ${field} must not be empty`);\n }\n}\n\nconst supportedTones: readonly TagTone[] = [\n \"neutral\",\n \"info\",\n \"success\",\n \"attention\",\n \"brand\",\n];\n\nexport function validateTagDescriptor(descriptor: TagDescriptor): void {\n assertCopy(descriptor.label, \"label\");\n if (\n descriptor.tone !== undefined &&\n !supportedTones.includes(descriptor.tone)\n ) {\n throw new TypeError(`Unsupported Tag tone: ${String(descriptor.tone)}`);\n }\n}\n\nexport function resolveTagDescriptor(\n descriptor: TagDescriptor,\n): ResolvedTagDescriptor {\n validateTagDescriptor(descriptor);\n return {\n label: descriptor.label,\n tone: descriptor.tone ?? tagDefaults.tone,\n };\n}\n\n/**\n * Rectangular, not the pill `radius.full` that `chipRecipe`\n * use, so a static Tag never reads as a pressable Chip or a status Badge at a\n * glance. No `states` and no `focus` contract: nothing here is ever pressed\n * or focused.\n */\nexport const tagRecipe = {\n slots: [\"root\", \"label\"] as const,\n defaults: tagDefaults,\n tones: {\n // Same as badgeRecipe: a canvas plate without a border vanished on the canvas (2026-10-17 review E3).\n neutral: {\n background: semanticColors.canvas,\n content: semanticColors.content.secondary,\n border: semanticColors.border.default,\n },\n brand: {\n background: semanticColors.canvas,\n content: semanticColors.content.brand,\n border: semanticColors.border.default,\n },\n info: {\n background: semanticColors.canvas,\n content: semanticColors.feedback.info.foreground,\n border: semanticColors.feedback.info.border,\n },\n success: {\n background: semanticColors.feedback.success.badgeBackground,\n content: semanticColors.feedback.success.foreground,\n border: semanticColors.feedback.success.border,\n },\n attention: {\n background: semanticColors.feedback.attention.badgeBackground,\n content: semanticColors.feedback.attention.foreground,\n border: semanticColors.feedback.attention.border,\n },\n },\n size: {\n // Static metadata still participates in Dynamic Type. A fixed 20px frame\n // clips the label at accessibility sizes; minHeight preserves the compact\n // base shape while allowing the intrinsic line box to grow.\n minHeight: 20,\n paddingHorizontal: spacing.xxs,\n gap: spacing.xxs,\n textVariant: \"caption\",\n fontWeight: fontWeight.medium,\n },\n radius: \"sm\",\n borderWidth: stroke.default,\n} as const;\n\nexport type ResolvedTagPresentation = Readonly<{\n background: string;\n content: string;\n border: string | null;\n}>;\n\n/** Resolve the same Tag recipe for non-CSS renderers. */\nexport function resolveTagPresentation(\n tone: TagTone,\n palette: ColorReferencePalette,\n): ResolvedTagPresentation {\n const contract = tagRecipe.tones[tone];\n return {\n background: resolveColorReference(contract.background, palette),\n content: resolveColorReference(contract.content, palette),\n border:\n contract.border === null\n ? null\n : resolveColorReference(contract.border, palette),\n };\n}\n"]}
package/dist/version.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  /** Package release shown by documentation surfaces. Kept in sync by a test. */
2
- export declare const designSystemVersion: "1.33.2";
2
+ export declare const designSystemVersion: "1.34.0";
3
3
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  /** Package release shown by documentation surfaces. Kept in sync by a test. */
2
- export const designSystemVersion = "1.33.2";
2
+ export const designSystemVersion = "1.34.0";
3
3
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAiB,CAAC","sourcesContent":["/** Package release shown by documentation surfaces. Kept in sync by a test. */\nexport const designSystemVersion = \"1.33.2\" as const;\n"]}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAiB,CAAC","sourcesContent":["/** Package release shown by documentation surfaces. Kept in sync by a test. */\nexport const designSystemVersion = \"1.34.0\" as const;\n"]}
@@ -1,6 +1,6 @@
1
1
  # Component maturity and evidence manifest
2
2
 
3
- > Generated from `@hjmds/design-contracts` 1.33.2. Do not edit this file directly; run `pnpm contracts:sync`.
3
+ > Generated from `@hjmds/design-contracts` 1.34.0. 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.33.2",
3
+ "designSystemVersion": "1.34.0",
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.33.2",
8
+ "packageVersion": "1.34.0",
9
9
  "activeComponents": 130,
10
10
  "claimedComponents": 130,
11
11
  "completeScenarioComponents": 129,
@@ -6999,7 +6999,7 @@
6999
6999
  },
7000
7000
  "native": {
7001
7001
  "packageName": "@hjmds/react-native",
7002
- "packageVersion": "1.33.2",
7002
+ "packageVersion": "1.34.0",
7003
7003
  "activeComponents": 112,
7004
7004
  "claimedComponents": 112,
7005
7005
  "completeScenarioComponents": 109,
@@ -1,6 +1,6 @@
1
1
  # Renderer evidence coverage
2
2
 
3
- > Generated for HJM 1.33.2. Do not edit directly; run `pnpm evidence:sync`.
3
+ > Generated for HJM 1.34.0. 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
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
- "designSystemVersion": "1.33.2",
3
+ "designSystemVersion": "1.34.0",
4
4
  "packageName": "@hjmds/design-contracts",
5
5
  "components": [
6
6
  {
@@ -89,3 +89,7 @@ Ant의 원시 색상이나 낮은 테두리 대비까지 복제하지 않고, HJ
89
89
  버튼의 배경색은 소비 화면에서 직접 지정하지 않고 `ButtonTone`의 `primary | secondary | ghost | danger | link`로 선택한다.
90
90
  웹과 네이티브는 같은 `buttonRecipe`의 배경·글자·테두리 역할을 해석한다. Create draft는 `primary`를 유지하므로
91
91
  일반 표면의 canvas 전환과 독립적으로 기존 브랜드 채움색을 보존한다.
92
+
93
+ ## 단색 배지 판 (2026-10-18)
94
+
95
+ 사용자가 제시한 오늘야구 경기 상태 배지의 단색 판과 흰 글자를 양 테마에서 유지하도록 `neutralFill`(#4e5968)·`strongFill`(#333d4b)을 추가했다. 기존 textMuted/text는 다크 모드에서 밝아지므로 판에 재사용하지 않는다. 상태색은 기존 `accentFill`과 `dangerFill`, 브랜드는 primary를 사용한다. 일반 본문과 Tag의 색 계약은 유지한다.
@@ -41,7 +41,9 @@
41
41
  | 보조 행동 | 주 행동 아래 같은 폭 secondary, 또는 헤더 `actions` | 본문 중간에 흩어진 버튼, 나가기를 보조 행동으로 두기 |
42
42
  | 상태·결과 안내 | `Notice`, `Badge`, `ListRow` 설명 | 가운데 정렬 일반 글자 |
43
43
  | 예제 전용 도구(실패 예약 등) | "미리보기 도구" 캡션 아래 `Button size="small" tone="ghost"`로 분리 | 제품 행동과 같은 모양·폭 |
44
- | 헤더 제목·추가 기능 | 제목은 기본 가운데(`ScreenLayout`·`TopBar`, 왼쪽 정렬은 선택), 추가 기능은 `actions`에 아이콘 버튼 1~2개로 오른쪽 위(`ScreenFlowAction.icon`, 넘치면 Menu) | 왼쪽 정렬 제목, 헤더의 글자 버튼(가운데 제목을 밀어냄), 본문에 흩어진 도구 버튼 |
44
+ | 헤더 제목·추가 기능 | 하단 탭·상단 탐색과 중복되는 최상위 화면명은 `titleVisibility="hidden"`. 식별에 필요한 대상·하위 작업명은 유지하며 본문과 중복되면 생략. 보이는 제목은 기본 가운데(`ScreenLayout`·`TopBar`, 왼쪽 정렬은 선택), 도구는 `actions` 아이콘 1~2개(넘치면 Menu) | 모든 제목 일괄 숨김, 위치 이름의 반복, 헤더의 글자 도구 버튼 |
45
+ | 상단 탐색과 도구 한 행 | `ScreenLayout.header`에 HJM `TopBar`, `leading`에 HJM `Tabs`/`SegmentedControl` 탐색, `actions`에 아이콘 도구. 탐색이 화면명을 대신한다. 본문 추가 필터는 `notice`/본문 유지 | 제목 행 + 같은 화면을 설명하는 탐색 행, 앱에서 직접 만든 헤더 레이아웃 |
46
+ | 상단 검색·장소 입력 | `SearchScreen`의 `queryLabelVisibility="hidden"`(기본). `queryLabel`은 placeholder와 접근성 이름, 뒤로가기·입력·오른쪽 도구는 공통 행에서 세로 중앙 | 입력 위 안내 label로 행 높이를 늘리기, 아이콘을 label 포함 높이에 맞추기, 일반 폼 label까지 숨기기 |
45
47
  | 도구성 행동(새로고침·추가·편집·삭제·공유·검색·필터·더보기) | `IconButton` + 아이콘, 글자는 `label`(보조기술 이름). HJM 화면은 아이콘 슬롯(`ScreenFlowAction.icon` 등) | 헤더·행·도구 막대의 글자 버튼 |
46
48
  | 화면 나가기(뒤로·닫기·설정 나가기) | `ScreenLayout`(과 이를 쓰는 화면)·`AuthScreenLayout`의 `exit` — 헤더 시작 쪽 아이콘 버튼, 글자 없음. 단계형 작성의 앞 단계는 `EditorScreen` `stepBack`(1.31.0) | 본문·하단의 "나가기" 글자 버튼, 화면마다 다른 화살표, 로그인·작성 화면 위에 따로 얹은 `TopBar` |
47
49
  | 버튼 묶음 간격 | 세로로 쌓으면 `gap="xs"`(8), 가로로 나란하면 `gap="sm"`(12) — `actionGroupRecipe` | 세로 버튼 사이 12 이상 |
@@ -78,6 +80,8 @@
78
80
 
79
81
  ## 근거
80
82
 
83
+ - 상단 제목의 선택·탐색 한 행: 2026-10-18 사용자 결정. Spint의 ‘탐험·설정’ 화면명과 하단 탭이 중복됐다. 번뚝 홈의 왼쪽 탐색·오른쪽 도구 구성을 공통 기준으로 삼되, 대화 상대·상세 대상·하위 작업 이름은 필요한 곳에 남긴다. `header`에 HJM TopBar를 연결하는 것은 공통 구현의 합성이며 제품이 헤더 구조를 복제하는 것과 구분한다.
84
+
81
85
  - 선택 기준·소유권: 2026-10-15 사용자 요청 — 메시지·댓글에서 같은 HJM 부품을 쓰면서도 앱마다 기본 배치와 상태 표시를 다시 조립해 UI가 달라졌다.
82
86
  - 도구성 행동 아이콘 버튼: 2026-10-17 사용자 지시 — '나의 기록' 헤더의 새로고침·새 기록 글자 버튼.
83
87
  - 조립 규칙(필수): 2026-10-16 사용자 지적("UI가 구리다") — `배포/구성/피드백과 복구/변경 저장과 이탈 확인` 예제가 선택을 버튼으로, 켜고 끄기를 글자 버튼으로 흉내 내 선택 상태가 테두리 없는 글자로만 보이고 버튼 폭이 제각각이었다.
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-07
7
+ - 검토일: 2026-10-18
8
8
  - 근거: recipe `badgeRecipe`(`src/component-recipes.ts`)
9
9
  - 스토리북: `배포/컴포넌트/데이터 표시/배지`
10
10
 
@@ -43,14 +43,20 @@ import { Badge } from "@hjmds/react-native/data-display";
43
43
  <Badge tone="success" size="small" label={t("order.status.done")} />
44
44
  ```
45
45
 
46
+ 왼쪽 → 라벨 → 오른쪽 순서로 배치한다. 아이콘에는 배지 글자와 같은 색과 12~14 크기를 전달한다. 아이콘은 장식이며 별도 누르기 동작을 넣지 않는다.
47
+
48
+ ```tsx
49
+ <Badge tone="success" leading={<Trophy size={12} color="currentColor" />} trailing={<Check size={12} color="currentColor" />}>완료</Badge>
50
+ ```
51
+
46
52
  ## 축과 기본값
47
53
 
48
54
  | prop | 값 | 기본값 | 설명 |
49
55
  | --- | --- | --- | --- |
50
- | `tone` | `neutral` · `strong` · `brand` · `info` · `success` · `warning` · `attention` · `danger` | `neutral` | `neutral`·`brand` 판은 캔버스색이라 `border.default` 테두리로 판을 그린다(2026-10-17, 테두리 없이 일반 글자처럼 보였다). `brand`는 판이 중립색이고 글자만 브랜드색이다. 브랜드 틴트는 "선택됨"을 뜻하므로 정적 표시에 쓰지 않는다. `strong`은 `neutral`보다 한 단계 강한 잉크 판이며 승패처럼 한눈에 갈려야 하는 사실에 쓴다 |
56
+ | `tone` | `neutral` · `strong` · `brand` · `info` · `success` · `warning` · `attention` · `danger` | `neutral` | 단색 배경과 흰색 굵은 글자를 사용한다. `neutral`은 회색, `strong`은 더 진한 회색, 나머지는 각 의미의 solid fill이다. `outline`은 투명 배경과 의미색 글자다 |
51
57
  | `variant` | `filled` · `outline` | `filled` | — |
52
58
  | `size` | `small` · `medium` | `medium` | — |
53
- | `leading` | `ReactNode` | — | 라벨 앞 아이콘 자리이며 보조기기에서 숨겨진다 |
59
+ | `leading` · `trailing` | `ReactNode` | — | 라벨 왼쪽·오른쪽 아이콘. 양쪽 동시 사용 가능하며 보조기기에서 숨겨진다 |
54
60
  | Web `children` · Native `label` | `ReactNode` · `string \| number`(필수) | — | 표시 라벨 |
55
61
  | Native `accessibilityLabel` | `string` | `String(label)` | 숫자 라벨에 단위를 붙여 읽힐 때 쓴다 |
56
62
  | `layoutStyle` | margin·width·flex·`alignSelf` | — | 배치 전용. Native `style`·`labelStyle`은 제거된 속성 — layoutStyle 또는 tone/토큰 |
@@ -59,12 +65,12 @@ import { Badge } from "@hjmds/react-native/data-display";
59
65
 
60
66
  ## 배치
61
67
 
62
- Native 모서리는 Provider token에서 recipe 역할을 읽는다. Badge의 `full`은 고정 pill 역할(999)이라 테마가 이를 사각형으로 바꾸지 않는다.
68
+ Web·Native 모두 `radius.sm`(8)의 둥근 사각형을 사용한다. 2026-10-18 사용자가 제시한 오늘야구 경기 상태 배지에 맞춘 공통 표현이다.
63
69
 
64
70
  | 항목 | 값 | 근거 |
65
71
  | --- | --- | --- |
66
- | 크기 | 내용 폭만 차지한다. 최소 높이 `medium` 24 · `small` 20, 모서리 `radius.full`. 누를 수 없는 표시라 44 터치 영역이 필요 없다(누르게 하려면 [Chip](chip.md)) | `badgeRecipe.sizes`, `.hjm-badge` |
67
- | 간격 | 좌우 여백 `medium` `spacing.xs` 8 · `small` `spacing.xxs` 4, 아이콘↔라벨 `spacing.xxs` 4. 여러 개를 나란히 둘 때는 `spacing.xxs` 4~`spacing.xs` 8 | `badgeRecipe.sizes` |
72
+ | 크기 | 내용 폭만 차지한다. 최소 높이 `medium` 24 · `small` 20, 모서리 `radius.sm`. 누를 수 없는 표시라 44 터치 영역이 필요 없다(누르게 하려면 [Chip](chip.md)) | `badgeRecipe.sizes`, `.hjm-badge` |
73
+ | 간격 | 좌우 여백 `medium` `spacing.xs` 8 · `small` `spacing.xs` 8, 아이콘↔라벨 `spacing.xxs` 4. 여러 개를 나란히 둘 때는 `spacing.xxs` 4~`spacing.xs` 8 | `badgeRecipe.sizes` |
68
74
  | 순서·정렬 | 제목·행 이름 옆(끝 쪽) 또는 카드 머리 위에 인라인으로 붙인다. 한 행에 2~3개를 넘기지 않는다 | `.hjm-badge`(`inline-flex`) |
69
75
  | 고정·스크롤 | 고정 영역이 없다 | — |
70
76
  | 좁은 폭·큰 글자 | 라벨이 줄바꿈된다(`overflow-wrap: anywhere`). 말줄임하지 않는다 | `.hjm-badge` |
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-10
7
+ - 검토일: 2026-10-18
8
8
  - 근거: recipe `cardRecipe`(`src/card.ts`), 바탕 `surfaceRecipe`(`src/base-recipes.ts`). 별도 계약 문서는 없다
9
9
  - 스토리북: `배포/컴포넌트/데이터 표시/카드`
10
10
 
@@ -69,9 +69,14 @@ import { Button } from "@hjmds/react-native/actions";
69
69
 
70
70
  ## 축과 기본값
71
71
 
72
+ 2026-10-18 사용자 요청으로 오늘야구 경기일정의 입체 카드 표현을 공통 기본값으로 채택했다.
73
+ Web·Native 모두 기존 `raised` Surface의 테두리·그림자를 재사용하며, 그림자는 바깥 프레임에,
74
+ 콘텐츠의 둥근 모서리 클리핑은 안쪽에 유지한다. 배경 영역인 `Surface`의 기본값은 바꾸지 않는다.
75
+ 기존 평면 표현이 필요한 소비자는 `tone="default"`를 명시한다. 이 변경의 npm 게시와 앱 버전 갱신은 별도다.
76
+
72
77
  | prop | 값 | 기본값 | 설명 |
73
78
  | --- | --- | --- | --- |
74
- | `tone` | `default` · `raised` · `accent` · `sunken` · `subtle`(Surface tone) | `default` | — |
79
+ | `tone` | `default` · `raised` · `accent` · `sunken` · `subtle`(Surface tone) | `raised` | 테두리와 공통 floating shadow로 입체감 표시. 평면 카드는 `tone="default"` 지정 |
75
80
  | `bordered` | `boolean` | `true` | Surface 기본 `false`와 다르다 |
76
81
  | `padding` | spacing 이름 | `md`(16) | — |
77
82
  | `radius` | radius 이름 | `lg`(foundation 16) | 선택한 designProfile의 같은 radius 역할로 frame과 media clip을 함께 변경 |
@@ -84,6 +84,9 @@ import { CommentThreadRow } from "@hjmds/react-native/screen-flows";
84
84
  | 고정·스크롤 | 행은 스크롤하지 않는다. 목록 스크롤·행 간격(`rowGap` 12)·답글 들여쓰기(`replyIndent` 40)는 host가 준다 | `CommentThreadScreen` |
85
85
  | 좁은 폭·큰 글자 | 본문 열이 `min-width: 0`으로 줄며 줄바꿈되고 작성자·행동 줄은 `flexWrap`. 좋아요 열은 줄지 않는다 | `.hjm-comment-row__text`, `flexShrink: 0` |
86
86
 
87
+ 좋아요·더보기 버튼 사이의 추가 간격은 0이다. 각 IconButton의 터치 영역은 유지한다.
88
+ 2026-10-18 Spint 화면에서 두 아이콘이 멀리 떨어져 보인다는 사용자 피드백으로, 버튼 내부 여백에 더해지던 `xxs` 간격을 제거했다. 미게시 변경이다.
89
+
87
90
  ## 꼭 지킬 것
88
91
 
89
92
  - 가상화 host에서 행 사이 간격(`commentThreadRecipe.rowGap`)과 답글 들여쓰기(`replyIndent`)를 직접 줄 때도 recipe 값을 쓴다. 제품 숫자로 다시 정하지 않는다.
@@ -88,11 +88,11 @@ Native popover와 항목 모서리는 Provider token에서 recipe 역할을 해
88
88
 
89
89
  | 항목 | 값 | 근거 |
90
90
  | --- | --- | --- |
91
- | 크기 | 항목 높이 `comfortable` 56(`layout.rowHeight.singleLine`) · `compact` 44(`control.minTouchTarget`). Web 표면 폭 `13.75rem`~`min(24rem, 90vw)`, 높이 최대 `100dvh − 2 × spacing.md`. Native 표면 폭 100%·최대 520, 높이 최대 75% | `menuRecipe.density`, `.hjm-menu__content`, `react-native/src/navigation.tsx` |
91
+ | 크기 | 항목 높이 `comfortable` 56(`layout.rowHeight.singleLine`) · `compact` 44(`control.minTouchTarget`). Web 표면 폭 `13.75rem`~`min(24rem, 90vw)`, 높이 최대 `100dvh − 2 × spacing.md`. Native 표면 폭 100%·최대 320(`menuRecipe.maxWidth`), 높이 최대 75% | `menuRecipe.density`, `.hjm-menu__content`, `react-native/src/navigation.tsx` |
92
92
  | 간격 | 항목 좌우 `spacing.sm` 12, leading·문구·단축키 사이 12. 섹션 제목 좌우 12 · 위아래 `spacing.xs` 8. Web: 트리거와 8(`menuRecipe.sideOffset`), 화면 가장자리 8(`collisionPadding`), 안쪽 `spacing.xs` 8(`menuRecipe.surface.padding`; 2026-10-06까지 4, 1.13.0 변경), 표면·항목 radius `radius.md` 12, 구분선 위아래 4 · 좌우 8. Native: 바깥 여백·안쪽 `spacing.md` 16, 제목·목록·닫기 사이 12, radius `radius.lg` 16 | `collectionItemContract`, `menuRecipe.sectionLabel`·`surface`, `.hjm-menu__*`, `useAnchoredPopup` |
93
- | 순서·정렬 | 일반 행동을 위에, `danger` 행동은 맨 아래. Web `align="start"`(기본)는 트리거 시작 끝, `end`는 끝 끝에 맞춘다(행 끝 ⋯은 `end`). Native는 위→아래 제목 → 항목 → 닫기 버튼(`secondary`) | `menuRecipe`, `src/overlays.tsx` |
93
+ | 순서·정렬 | 일반 행동을 위에, `danger` 행동은 맨 아래. Web `align="start"`(기본)는 트리거 시작 끝, `end`는 끝 끝에 맞춘다(행 끝 ⋯은 `end`). Native는 위→아래 가운데 정렬한 보조 제목(`label`, muted) → 항목 → 닫기 버튼(`ghost`) | `menuRecipe`, `src/overlays.tsx` |
94
94
  | 고정·스크롤 | Web: 트리거에 붙는 portal(z-index `layer.dropdown` 400), 아래가 모자라면 위로 뒤집고 가로는 화면 안으로 민다. 넘치면 표면 안 스크롤. Native: `Modal`로 화면 **가운데**, scrim을 누르면 닫히고 항목 목록만 스크롤 | `useAnchoredPopup`, `react-native/src/navigation.tsx`(`Menu`) |
95
- | 좁은 폭·큰 글자 | Web 폭 상한 `90vw`, 항목 문구는 줄바꿈된다. Native 높이 상한 75%를 넘으면 목록이 스크롤된다 | `.hjm-menu__content`, `maxHeight: "75%"` |
95
+ | 좁은 폭·큰 글자 | Web 폭 상한 `90vw`, 항목 문구는 줄바꿈된다. Native 높이 상한 75%를 넘으면 목록이 스크롤된다. 짧은 목록은 내용 높이만 사용한다(`flexGrow: 0`) | `.hjm-menu__content`, `maxHeight: "75%"` |
96
96
 
97
97
  ```text
98
98
  Web (트리거 아래, 앵커) Native (Modal, 화면 가운데)
@@ -103,9 +103,9 @@ Web (트리거 아래, 앵커) Native (Modal, 화면 가운데)
103
103
  │ 편집 │ ← 56 │░ │ 편집 │ ░│ ← 항목 56
104
104
  │ 공유 │ │░ │ 공유 │ ░│ (스크롤)
105
105
  │─────────────────│ │░ │ 삭제 (danger) │ ░│
106
- │ 삭제 (danger) │ │░ │ [ 닫기 ] │ ░│ ← secondary
106
+ │ 삭제 (danger) │ │░ │ [ 닫기 ] │ ░│ ← ghost
107
107
  └─────────────────┘ │░ └─────────────────────────┘ ░│
108
- 13.75rem ~ 24rem │░ 폭 ≤520 · 높이 ≤75% · 여백 16│
108
+ 13.75rem ~ 24rem │░ 폭 ≤320 · 높이 ≤75% · 여백 16│
109
109
  └───────────────────────────────┘
110
110
  ```
111
111
 
@@ -130,3 +130,5 @@ Web (트리거 아래, 앵커) Native (Modal, 화면 가운데)
130
130
  | 표면 | 트리거에 붙는 portal(`portalContainer`) | `Modal` |
131
131
  | 항목 label | `ReactNode`(+`textValue`) | `string` |
132
132
  | 배치 | `className`, `layoutStyle` | `layoutStyle`(`style`은 제거된 속성) |
133
+
134
+ 2026-10-18 스핀트 댓글 메뉴 사진에서 단일 행동에도 넓은 폭과 빈 공간이 생겼다. Native 메뉴 폭을 기존 recipe 상한에 맞추고 목록의 기본 확장을 없앴으며, 닫기는 조용한 행동으로 분리했다. 사용자 후속 요청으로 닫기 위 구분선은 제거했다.
@@ -70,9 +70,10 @@ import { ScreenLayout } from "@hjmds/react-native/screens";
70
70
  | --- | --- | --- | --- |
71
71
  | `keyboardAvoidance` (Native) | `boolean` | `true`(미게시) | 키보드와 실제로 겹치는 만큼 footer·본문을 올리고 포커스된 입력칸을 스크롤해 보인다. `KeyboardAvoiding`·키보드 처리 Sheet/Dialog 안에서는 건너뛴다 |
72
72
  | `title` | `string` | 필수 | 화면 제목. `header`를 주면 접근성 이름으로만 쓴다 |
73
+ | `titleVisibility` | `"visible"` · `"hidden"` | `"visible"` | 하단 탭·상단 탐색으로 이미 식별되는 최상위 화면명은 `hidden`. 대상·하위 작업 이름은 식별에 필요하고 본문과 중복되지 않을 때 유지. 기본값이나 뒤로가기 유무로 목적지를 추정하지 않으며 제품이 맥락에 맞게 선택한다. `title` 값은 유지한다 |
73
74
  | `titleBadge` | `ReactNode` | 없음 | 기본 제목 바로 옆 배지. 값·의미는 제품, 간격·정렬은 HJM. `header` 교체 또는 제목 숨김이면 그리지 않는다. 1.23.0 추가 API |
74
75
  | `titleLeading` | `ReactNode` | 없음 | 미게시(1.30.0 이후). 제목 앞 식별 표시(팀 로고·도구 아트워크). 장식이라 보조기술에서 숨기고 제목이 heading·접근성 이름으로 남는다. `titleVisibility="hidden"`이면 표시가 제목 자리에 혼자 서고 `title`을 이름으로 하는 heading(level 1)이 된다. 이때도 아이콘 전용 48 띠가 아니라 제목 띠 52를 쓴다. 노드 크기는 제품이 주되 헤더 띠 안에 들어가야 한다. `header` 교체 시 그리지 않는다. `ChatScreen`은 같은 자리를 `avatar`로 받는다 |
75
- | `header` | `ReactNode` | 없음 | 기존 navigator 헤더 유지용. 주면 기본 헤더(`exit`·제목·`actions`)를 그리지 않는다 |
76
+ | `header` | `ReactNode` | 없음 | 기존 navigator 헤더 또는 HJM `TopBar` 합성. 상단 탐색은 `TopBar.leading`에 HJM 탐색 컨트롤, 도구는 `TopBar.actions`에 주어 한 행을 만든다. 주면 기본 헤더(`exit`·제목·`actions`)를 그리지 않으므로 뒤로가기·도구도 해당 TopBar에 연결한다. 앱 자체 헤더 복제용 슬롯이 아니다 |
76
77
  | `description` | `string` | 없음 | 2026-10-18 사용자 결정으로 상단 보조 문구는 표시하지 않는다. 기존 호출의 타입 호환을 위해 prop만 유지하며 다음 major 정리에서 제거한다. 필요한 설명은 본문 콘텐츠에 둔다 |
77
78
  | `exit` | `{ kind: "back" \| "close"; label; onClick(Web) \| onPress(Native); disabled? }` | 없음 | 화면 나가기(뒤로·닫기·"설정 나가기")를 헤더 시작 쪽 아이콘 버튼으로 고정한다. 글자는 보이지 않고 `label`은 보조기술 이름이다. `back`은 ‹(RTL 반전), `close`는 ×. 헤더 시작 쪽에는 이 버튼 외의 자유 슬롯이 없다. 미게시(1.26.1 이후) |
78
79
  | `actions` | `ReactNode` | 없음 | 제목 뒤(도구) 슬롯. 시작 쪽 `leading` 슬롯은 2026-10-17 제거됐다 — 제품 사용처가 모두 직접 만든 뒤로 버튼이었다 |
@@ -133,7 +134,8 @@ import { ScreenLayout } from "@hjmds/react-native/screens";
133
134
 
134
135
  ## 꼭 지킬 것
135
136
 
136
- - **헤더 제목은 기본 가운데, 추가 기능 버튼은 오른쪽 위.** `headerAlignment` 기본값은 `"center"`이고 `"start"`는 큰 제목이 필요한 화면에서만 고른다(2026-10-17 사용자 지시: 가운데가 기본, 강제는 아님).
137
+ - **중복된 화면명은 숨기고, 대상·작업 식별에 필요한 제목은 유지한다.** 하단 탭·상단 탐색이 화면을 이미 설명하면 `titleVisibility="hidden"`으로 둔다. 제목·식별 자산·뒤로가기·도구가 모두 없으면 기본 헤더는 그리지 않는다. 내용 첫머리에서 이미 대상·작업을 식별할 수 있으면 같은 제목을 반복하지 않는다. 2026-10-18 사용자 결정이며 모든 제목을 숨기는 기본값 변경이 아니다.
138
+ - **보이는 헤더 제목은 기본 가운데, 추가 기능 버튼은 오른쪽 위.** `headerAlignment` 기본값은 `"center"`이고 `"start"`도 선택할 수 있다(2026-10-17 사용자 지시: 가운데가 기본, 강제는 아님). 상단 탐색이 있으면 HJM TopBar를 `header`에 합성해 왼쪽 탐색·오른쪽 도구 한 행으로 두고 별도 제목 줄을 만들지 않는다.
137
139
  - **나가기는 `exit` 하나로 헤더 시작 쪽에 둔다(필수).** 본문·하단의 "나가기"·"닫기"·"뒤로"·"취소하고 나가기" 글자 버튼, 화면마다 다른 화살표(`←` 글자, 제품 아이콘)로 만든 뒤로 버튼을 두지 않는다. 2026-10-17 사용자 지시: `변경 저장과 이탈 확인` 예제의 "설정 나가기"가 본문 버튼으로 떠 있었고, 제품마다 뒤로 버튼 모양이 달랐다(야잘알 자체 `BackButton`, 예제의 `←`·`ChevronLeft`). 변경이 있을 때의 확인 대화상자는 제품이 `exit` 콜백에서 띄운다. 기존 navigator 헤더를 쓰는 화면은 그 헤더의 뒤로 버튼이 같은 자리를 맡는다.
138
140
 
139
141
  - `headerAlignment="center"` + `titleVisibility="hidden"`이고 설명·사용자 지정 헤더가 없으면 아이콘 헤더는 최소 48pt, 세로 여백 2pt다(값의 정본은 `screenHeaderRecipe`). 2026-10-14 야잘알의 상단 버튼–탭 사이 빈 공간 피드백으로 제목용 높이를 줄였다. 제목이 보이는 가운데 헤더는 최소 52pt, 세로 여백 4pt다(2026-10-17 사용자 지시 "상단 여백 줄여": 60pt가 iOS 내비게이션 바보다 높았다). `notice`가 없을 때만 본문 상단 여백을 최대 4pt로 줄이며, `notice`에 둔 탭과 본문 카테고리 사이 기본 16pt 간격은 유지한다. 좌우 여백·하단 여백·터치 영역은 그대로다.
@@ -67,7 +67,7 @@ import { SearchField } from "@hjmds/react-native/inputs";
67
67
  query={query}
68
68
  onQueryChange={setQuery}
69
69
  onSearch={runSearch}
70
- queryField={<SearchField label={t("search.query")} clearLabel={t("common.clearSearch")}
70
+ queryField={<SearchField accessibilityLabel={t("search.query")} placeholder={t("search.query")} clearLabel={t("common.clearSearch")}
71
71
  busyLabel={t("common.searching")} value={query} onValueChange={setQuery} busy={isFetching} />}
72
72
  >
73
73
  <ProductResults items={results} />
@@ -139,7 +139,7 @@ import { SearchScreen } from "@hjmds/react-native/screen-flows";
139
139
  | prop | 값 | 기본값 | 설명 |
140
140
  | --- | --- | --- | --- |
141
141
  | `query` | `string` | 필수 | 제어형 검색어 |
142
- | `queryLabel` | `string` | 필수 | 기본 입력의 label |
142
+ | `queryLabel` | `string` | 필수 | 기본 검색 입력의 placeholder·접근성 이름. 입력값이 있어도 접근성 이름은 유지한다 |
143
143
  | `onQueryChange` | `(value: string) => void` | 필수 | 입력·지우기(`""`) |
144
144
  | `onSearch` | `(query: string, context: { signal: AbortSignal }) => void` | 필수 | `debounceMs` 뒤 호출. 값이 바뀌면 이전 타이머를 지우고 이전 신호를 abort한다 |
145
145
  | `queryClearLabel` | `string` | `queryField`가 없으면 필수 | 기본 입력의 지우기 버튼 이름. 비어 있으면 `TypeError` |
@@ -149,7 +149,7 @@ import { SearchScreen } from "@hjmds/react-native/screen-flows";
149
149
  | `filtersRole` | `narrow` · `scope` | `narrow` | 1.32.0, 미게시. 필터 줄(`filters`·`filterSheet.trigger`)이 결과에 하는 일. `narrow`는 결과를 좁히기만 해서 적용한 필터 없이 0건이면 검색어 탓이고 어떤 필터도 결과를 되살리지 못하므로 줄을 숨긴다. `scope`는 무엇을 찾을지 고르는 필터(장소명/주소, 지역, 지도 영역)라 적용한 필터가 없어도 바꾸면 결과가 생길 수 있으므로 0건에도 줄을 남긴다. 판정은 두 renderer가 공유하는 `resolveSearchFilterRailVisible`(`@hjmds/design-contracts/screen-patterns`) |
150
150
  | `onSubmit` | `(query: string) => void` | 없음 | 확정 신호. 모든 확정에서 키보드를 닫는다(Native `Keyboard.dismiss()`, Web은 고른 항목이면 결과 영역으로 포커스, 1.13.1 변경) — 아래 플랫폼 차이. 기본 입력의 Web Enter(`enterKeyHint="search"`, IME 조합 중 Enter는 무시)·Native 키보드 검색 키(`returnKeyType="search"`, `onSubmitEditing`)와 SearchScreen이 그린 확정 행·제안·최근·추천 검색어가 모두 여기로 온다. 앞뒤 공백을 지운 값이고 공백만이면 부르지 않는다. 고른 값이 입력과 다르면 먼저 `onQueryChange`. 대기 중인 `onSearch` debounce는 그대로 둔다. `queryField`를 주면 그 입력의 키는 연결하지 않는다 |
151
151
  | `committedQuery` | `string` | 없음 | 결과를 보여 줄 확정 검색어. 주면 두 단계 검색(`onSubmit` 필수, 타입). 단계: 검색어 공백 = 검색 전, 확정값과 다름 = 입력 중, 같음 = 결과(앞뒤 공백 무시). `children`은 결과 단계에만 그린다 |
152
- | `queryLabelVisibility` | `visible` · `hidden` | `visible` | `hidden`이면 보이는 label 없이 `queryLabel`을 접근성 이름(Web `aria-label`, Native `accessibilityLabel`)과 placeholder로 쓴다 |
152
+ | `queryLabelVisibility` | `visible` · `hidden` | `hidden` | `hidden`이면 보이는 label 없이 `queryLabel`을 접근성 이름(Web `aria-label`, Native `accessibilityLabel`)과 placeholder로 쓴다 |
153
153
  | `searching` · `searchingLabel` | `boolean` · `string` | 없음 | 기본 입력의 진행 표시(Web `loading`, Native `busy`+`busyLabel`). 함께 주거나 함께 생략(타입). 켜져 있으면 `searchingLabel`을 낭독한다 |
154
154
  | `recentQueries` | `{ items, title, clearAllLabel, onClearAll(), removeLabel(query), onRemove(query), icon?, removeIcon?, maxVisible? }` | 없음 | 검색 전 본문 맨 위. 행 = 확정, 행 끝 × = `onRemove`, 제목 끝 = `onClearAll`. 기본 5행(`searchScreenRecipe.recentVisible`) |
155
155
  | `suggestedQueries` | `{ title, items: string[] }` | 없음 | 검색 전(최근 검색 아래)과 검색어 때문인 0건 아래 Chip 줄. 누르면 확정 |
@@ -216,7 +216,7 @@ import { SearchScreen } from "@hjmds/react-native/screen-flows";
216
216
 
217
217
  ## 함정
218
218
 
219
- - 기본값(`queryLabelVisibility="visible"`)은 label을 보이게 그려 고정 영역을 1배 약 22, 2배 약 40 더 쓴다(2026-10-06 실측). 검색만 하는 화면은 `hidden`을 쓴다.
219
+ - 2026-10-18 사용자 결정: 기본값은 `queryLabelVisibility="hidden"`이다. 상단 검색·장소 입력의 안내는 placeholder로 두고 `queryLabel`은 접근성 이름으로 계속 유지한다. 선택의창에서 보이는 label 때문에 오른쪽 지도 아이콘이 입력칸보다 위로 보였던 것이 계기다. `titleVisibility="hidden"`이고 별도 `header`가 없으면 뒤로가기·입력·오른쪽 도구를 같은 행의 세로 중앙에 둔다. 보이는 제목이나 기존 navigator 헤더를 유지하는 경우에도 검색 입력의 기본 안내는 placeholder다. 일반 폼 필드의 label 정책은 바꾸지 않는다.
220
220
  - 1.12.1 이하 Native에서는 `searching`인 동안 SearchField `busy`가 입력을 무시해 그동안 친 글자가 사라졌다. 다음 릴리스부터 두 플랫폼 모두 입력을 받으므로 입력 중 제안 조회에도 켤 수 있다. 1.12.1 이하에 머무는 Native 제품은 확정 결과 요청에만 켠다.
221
221
  - Native 필터 시트는 닫힘 애니메이션이 끝난 뒤에 다시 열린다(Sheet 계약). 닫자마자 `open`을 켜도 바로 보이지 않을 수 있다.
222
222
  - Web `filters` 안에 `Stack wrap`을 넣어도 `scroll`에서는 한 줄이다(자식 폭을 `max-content`로 잡는다).
@@ -10,6 +10,8 @@
10
10
 
11
11
  ## 언제 쓰나
12
12
 
13
+ 2026-10-18 전사 기준: 하단 탭·상단 탐색과 중복되는 최상위 화면명은 생략한다. 탐색이 필요하면 제목 없이 `leading`에 HJM 탐색 컨트롤, `actions`에 아이콘 도구를 두고 `ScreenLayout.header`에 연결한다. 상세 대상·하위 작업 제목은 식별에 필요하고 본문과 중복되지 않을 때 유지한다. 제목만 있는 빈 행을 남기지 않으며, 내용·탐색·뒤로가기·도구가 모두 없으면 TopBar 자체를 렌더하지 않는다. [선택 기준](../SELECTION.md)을 따른다.
14
+
13
15
  화면 맨 위에 붙는 **크롬**에 쓴다. 뒤로가기·닫기(`exit`), 짧은 화면 이름(`title`), 소수의 행동
14
16
  (`actions`/`trailing`)을 담는다. 스크롤과 무관한 자리이며 safe area 위쪽 여백을 처리한다.
15
17
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hjmds/design-contracts",
3
- "version": "1.33.2",
3
+ "version": "1.34.0",
4
4
  "description": "Renderer-neutral design contracts, tokens, recipes, and behaviors shared by HJM products.",
5
5
  "license": "MIT",
6
6
  "repository": {