@hjmds/design-contracts 1.27.2 → 1.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/dist/activity-chip.d.ts +1 -1
  2. package/dist/activity-chip.js +1 -1
  3. package/dist/activity-chip.js.map +1 -1
  4. package/dist/auth-screen.d.ts +71 -5
  5. package/dist/auth-screen.d.ts.map +1 -1
  6. package/dist/auth-screen.js +60 -2
  7. package/dist/auth-screen.js.map +1 -1
  8. package/dist/base-recipes.d.ts +1 -1
  9. package/dist/base-recipes.js +1 -1
  10. package/dist/base-recipes.js.map +1 -1
  11. package/dist/behaviors.d.ts +1 -1
  12. package/dist/breadcrumb.d.ts +1 -1
  13. package/dist/breadcrumb.js +1 -1
  14. package/dist/breadcrumb.js.map +1 -1
  15. package/dist/calendar.d.ts +1 -1
  16. package/dist/calendar.js +2 -2
  17. package/dist/calendar.js.map +1 -1
  18. package/dist/catalog.d.ts +131 -87
  19. package/dist/catalog.d.ts.map +1 -1
  20. package/dist/component-contracts.d.ts +1 -1
  21. package/dist/component-contracts.js +1 -1
  22. package/dist/component-contracts.js.map +1 -1
  23. package/dist/component-recipes.d.ts +85 -57
  24. package/dist/component-recipes.d.ts.map +1 -1
  25. package/dist/component-recipes.js +95 -60
  26. package/dist/component-recipes.js.map +1 -1
  27. package/dist/conversation-row.d.ts +2 -2
  28. package/dist/conversation-row.d.ts.map +1 -1
  29. package/dist/conversation-row.js +6 -4
  30. package/dist/conversation-row.js.map +1 -1
  31. package/dist/counter-badge-recipe.d.ts +1 -1
  32. package/dist/counter-badge-recipe.js +1 -1
  33. package/dist/counter-badge-recipe.js.map +1 -1
  34. package/dist/data-table.d.ts +4 -4
  35. package/dist/data-table.js +4 -4
  36. package/dist/data-table.js.map +1 -1
  37. package/dist/date-picker.d.ts +1 -1
  38. package/dist/design-profile.d.ts.map +1 -1
  39. package/dist/design-profile.js +19 -16
  40. package/dist/design-profile.js.map +1 -1
  41. package/dist/file-picker.d.ts +1 -1
  42. package/dist/file-picker.js +1 -1
  43. package/dist/file-picker.js.map +1 -1
  44. package/dist/foundations.d.ts +18 -14
  45. package/dist/foundations.d.ts.map +1 -1
  46. package/dist/foundations.js +18 -13
  47. package/dist/foundations.js.map +1 -1
  48. package/dist/heading.d.ts +13 -13
  49. package/dist/heading.js.map +1 -1
  50. package/dist/number-field.d.ts +1 -1
  51. package/dist/otp-field.d.ts +1 -1
  52. package/dist/password-field.d.ts +1 -1
  53. package/dist/result.d.ts +1 -1
  54. package/dist/result.js +2 -2
  55. package/dist/result.js.map +1 -1
  56. package/dist/semantic-colors.d.ts +10 -0
  57. package/dist/semantic-colors.d.ts.map +1 -1
  58. package/dist/semantic-colors.js +6 -0
  59. package/dist/semantic-colors.js.map +1 -1
  60. package/dist/sheet.d.ts +7 -4
  61. package/dist/sheet.d.ts.map +1 -1
  62. package/dist/sheet.js +8 -5
  63. package/dist/sheet.js.map +1 -1
  64. package/dist/side-panel.d.ts +1 -1
  65. package/dist/side-panel.js +2 -2
  66. package/dist/side-panel.js.map +1 -1
  67. package/dist/steps.d.ts +1 -1
  68. package/dist/steps.js +1 -1
  69. package/dist/steps.js.map +1 -1
  70. package/dist/tag.d.ts +1 -1
  71. package/dist/tag.js +1 -1
  72. package/dist/tag.js.map +1 -1
  73. package/dist/timeline.d.ts +1 -1
  74. package/dist/timeline.js +1 -1
  75. package/dist/timeline.js.map +1 -1
  76. package/dist/top.d.ts +4 -4
  77. package/dist/top.d.ts.map +1 -1
  78. package/dist/top.js +3 -1
  79. package/dist/top.js.map +1 -1
  80. package/dist/version.d.ts +1 -1
  81. package/dist/version.js +1 -1
  82. package/dist/version.js.map +1 -1
  83. package/docs/activity-chip.md +5 -1
  84. package/docs/auth-screen.md +2 -0
  85. package/docs/design-profile.md +14 -10
  86. package/docs/generated/component-maturity.md +1 -1
  87. package/docs/generated/renderer-evidence.json +9 -5
  88. package/docs/generated/renderer-evidence.md +1 -1
  89. package/docs/generated/showcase-manifest.json +1 -1
  90. package/docs/heading.md +14 -9
  91. package/docs/sheet.md +2 -0
  92. package/docs/top.md +4 -3
  93. package/docs/usage/README.md +1 -1
  94. package/docs/usage/components/auth-screen-layout.md +26 -9
  95. package/docs/usage/components/bottom-cta.md +6 -2
  96. package/docs/usage/components/breadcrumb.md +5 -1
  97. package/docs/usage/components/checkbox-group.md +5 -1
  98. package/docs/usage/components/collapsible.md +1 -0
  99. package/docs/usage/components/effect-surface.md +2 -2
  100. package/docs/usage/components/heading.md +14 -12
  101. package/docs/usage/components/list-row.md +7 -3
  102. package/docs/usage/components/list.md +5 -4
  103. package/docs/usage/components/notification-item.md +5 -1
  104. package/docs/usage/components/photo-source-sheet.md +1 -1
  105. package/docs/usage/components/screen-layout.md +7 -3
  106. package/docs/usage/components/search-screen.md +1 -1
  107. package/docs/usage/components/section.md +5 -1
  108. package/docs/usage/components/segmented-control.md +1 -1
  109. package/docs/usage/components/sheet.md +17 -13
  110. package/docs/usage/components/skeleton.md +41 -15
  111. package/docs/usage/components/statistic.md +5 -1
  112. package/docs/usage/components/switch.md +3 -2
  113. package/docs/usage/components/tag.md +5 -1
  114. package/docs/usage/components/text.md +6 -4
  115. package/docs/usage/components/top.md +1 -1
  116. package/docs/usage/compositions/adaptive-content.md +5 -1
  117. package/docs/usage/compositions/compound-controls.md +2 -1
  118. package/docs/usage/compositions/design-profile-comparison.md +1 -1
  119. package/docs/usage/compositions/family-drawer.md +5 -3
  120. package/docs/usage/compositions/input-sheet.md +1 -1
  121. package/docs/usage/compositions/interaction-flow-apply.md +3 -3
  122. package/docs/usage/compositions/photo-source.md +3 -3
  123. package/docs/usage/compositions/selection-motion.md +5 -1
  124. package/docs/usage/compositions/visual-foundations.md +5 -4
  125. package/docs/usage/screens/common-login.md +9 -7
  126. package/docs/usage/screens/landing.md +15 -14
  127. package/docs/usage/screens/notification-settings.md +4 -2
  128. package/docs/usage/tokens/typography.md +21 -13
  129. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import { collectionItemContract, fieldFrameContract, floatingSurfaceContract, focusIndicatorContract, formSupportContract, } from "./component-contracts.js";
2
- import { backdrop, breakpoint, control, glyph, layer, largeTextThreshold, layout, fontWeight, motion, motionPreset, opacity, radius, spacing, stroke, } from "./foundations.js";
2
+ import { backdrop, breakpoint, control, glyph, layer, largeTextThreshold, layout, fontWeight, motion, motionPreset, opacity, radius, spacing, stroke, typography, } from "./foundations.js";
3
3
  import { semanticColors } from "./semantic-colors.js";
4
4
  import { tooltipDescriptorDefaults } from "./tooltip.js";
5
5
  import { bottomNavigationRecipeDefaults } from "./bottom-navigation-defaults.js";
@@ -19,14 +19,18 @@ export const textRecipe = {
19
19
  danger: semanticColors.content.danger,
20
20
  inverse: semanticColors.content.inverse,
21
21
  },
22
+ // 2026-10-17 user decisions: the strongest inline emphasis matches the title weight. strong=700 made unread
23
+ // notification titles and labels heavier than every title after titles moved to 600 ("왜 폰트가 진해"), so
24
+ // strong became 600; the second round ("굵기 좀더 줄여") moved titles 600→500 and display headings 700→600,
25
+ // so strong follows the title role (500) and equals `medium`. Semibold 600 is the key-number/selected step.
22
26
  emphasis: {
23
27
  regular: fontWeight.regular,
24
- medium: fontWeight.semibold,
25
- strong: fontWeight.bold,
28
+ medium: fontWeight.medium,
29
+ strong: typography.title.fontWeight,
26
30
  },
27
31
  /**
28
32
  * Variants whose omitted `emphasis` renders the variant's typography weight even without a design profile.
29
- * Label's 500 has no emphasis step (`medium` is 600), and the shared `regular` default drew form labels at
33
+ * Label's 500 equals the `medium` emphasis step, but the shared `regular` default drew form labels at
30
34
  * body weight (2026-10-17 user decision: label 500 on Web and Native). Other variants keep `regular`.
31
35
  */
32
36
  variantWeight: ["label"],
@@ -86,7 +90,7 @@ export const linkRecipe = {
86
90
  variants: {
87
91
  inline: {
88
92
  textVariant: "body",
89
- fontWeight: fontWeight.semibold,
93
+ fontWeight: fontWeight.medium,
90
94
  underline: "always",
91
95
  minHeight: null,
92
96
  minWidth: null,
@@ -94,7 +98,7 @@ export const linkRecipe = {
94
98
  /** 그 자체가 동작이므로 두 축 모두 최소 터치 타깃. 높이만 잡으면 짧은 라벨이 좁아진다. */
95
99
  standalone: {
96
100
  textVariant: "body",
97
- fontWeight: fontWeight.bold,
101
+ fontWeight: fontWeight.medium,
98
102
  underline: "hover",
99
103
  minHeight: control.minTouchTarget,
100
104
  minWidth: control.minTouchTarget,
@@ -218,7 +222,7 @@ export const badgeRecipe = {
218
222
  },
219
223
  radius: "full",
220
224
  borderWidth: stroke.default,
221
- fontWeight: fontWeight.bold,
225
+ fontWeight: fontWeight.medium,
222
226
  };
223
227
  /** Search is a field specialization with stable affordance and clear targets. */
224
228
  export const searchFieldRecipe = {
@@ -320,7 +324,8 @@ export const chipRecipe = {
320
324
  },
321
325
  radius: "full",
322
326
  borderWidth: stroke.default,
323
- label: { fontWeight: fontWeight.semibold, selectedFontWeight: fontWeight.bold },
327
+ // Selected keeps one step above idle (500 → 600) so the state still carries a weight cue on the lighter scale.
328
+ label: { fontWeight: fontWeight.medium, selectedFontWeight: fontWeight.semibold },
324
329
  selectionIndicator: {
325
330
  color: semanticColors.content.brand,
326
331
  glyph: "xs",
@@ -346,15 +351,24 @@ export const dividerRecipe = {
346
351
  * VirtualList) keep exactly this much inline room so the bleed is never cut.
347
352
  */
348
353
  const listRowSurfaceBleed = spacing.xs;
354
+ /** ListRow leading frame and the gap before its text column; the indented separator reuses both. */
355
+ const listRowLeadingSize = 40;
356
+ const listRowGap = spacing.sm;
349
357
  export const listRecipe = {
350
358
  slots: ["root", "item", "separator"],
351
359
  defaults: { separator: "indented" },
360
+ /**
361
+ * Separators are measured from the list's content edge, where the row text sits, and each one belongs to the row
362
+ * above it (iOS cell separator). "full" spans the row surface (content edge ± bleed). "indented" follows that row's
363
+ * text column: it starts at the text — `leadingInsetStart` (leading 40 + gap 12) when the row has a leading
364
+ * element, `insetStart` 0 when it has none — and ends on the row's trailing content edge (0).
365
+ * 2026-10-17 user report: a fixed 52 start cut the line off midway under rows without leading (text at 16, line
366
+ * from 68), and the −8 end ran past the trailing content.
367
+ */
352
368
  separators: {
353
369
  none: null,
354
- // Separators are measured from the list's content edge, where the row text now sits. "full" spans the row
355
- // surface (content edge ± bleed); "indented" starts at the text after leading 40 + gap sm and ends with the surface.
356
- full: { insetStart: -listRowSurfaceBleed, insetEnd: -listRowSurfaceBleed },
357
- indented: { insetStart: 40 + spacing.sm, insetEnd: -listRowSurfaceBleed },
370
+ full: { insetStart: -listRowSurfaceBleed, leadingInsetStart: -listRowSurfaceBleed, insetEnd: -listRowSurfaceBleed },
371
+ indented: { insetStart: 0, leadingInsetStart: listRowLeadingSize + listRowGap, insetEnd: 0 },
358
372
  },
359
373
  // A grouped list paints its own rounded surface and clips to it, so it leaves the row bleed as inline padding:
360
374
  // rows keep their full surface inside the radius and the text stays 8 inside the group edge as before.
@@ -406,15 +420,15 @@ export const listRowRecipe = {
406
420
  * the same negative inline margin, so pressed/selected fills keep 8 of room beyond the text.
407
421
  */
408
422
  surfaceBleed: listRowSurfaceBleed,
409
- gap: spacing.sm,
410
- leadingSize: 40,
423
+ gap: listRowGap,
424
+ leadingSize: listRowLeadingSize,
411
425
  /**
412
426
  * Frame geometry for the leading slot. The size was already declared here but
413
427
  * no renderer bound it, so every consumer re-derived the avatar box (radius,
414
428
  * clipping, centering) in product styles.
415
429
  */
416
430
  leadingShapes: { square: null, circle: "full" },
417
- title: { color: semanticColors.content.body, textVariant: "bodyLarge", fontWeight: fontWeight.bold },
431
+ title: { color: semanticColors.content.body, textVariant: "bodyLarge", fontWeight: typography.title.fontWeight },
418
432
  description: { color: semanticColors.content.secondary, textVariant: "body" },
419
433
  trailing: {
420
434
  textColor: semanticColors.content.secondary,
@@ -437,7 +451,7 @@ export const accordionRecipe = {
437
451
  },
438
452
  paddingHorizontal: spacing.xs,
439
453
  gap: spacing.sm,
440
- title: { color: semanticColors.content.body, textVariant: "bodyLarge", fontWeight: fontWeight.bold },
454
+ title: { color: semanticColors.content.body, textVariant: "bodyLarge", fontWeight: typography.title.fontWeight },
441
455
  indicator: { color: semanticColors.content.secondary, glyph: "sm" },
442
456
  panel: {
443
457
  color: semanticColors.content.body,
@@ -577,7 +591,7 @@ export const selectRecipe = {
577
591
  paddingVertical: spacing.xs,
578
592
  },
579
593
  optionLeading: { color: semanticColors.content.secondary, glyph: "sm" },
580
- optionLabel: { fontWeight: fontWeight.semibold, selectedFontWeight: fontWeight.bold },
594
+ optionLabel: { fontWeight: fontWeight.medium, selectedFontWeight: fontWeight.semibold },
581
595
  selectionIndicator: { color: semanticColors.border.focus, glyph: "sm" },
582
596
  stateMessage: {
583
597
  color: semanticColors.content.secondary,
@@ -648,6 +662,8 @@ export const comboboxRecipe = {
648
662
  states: selectRecipe.states,
649
663
  transition: selectRecipe.transition,
650
664
  };
665
+ /** Block-side inset of the connected track: `container.padding` + `container.borderWidth`. */
666
+ const segmentedTrackInset = spacing.xxs + stroke.subtle;
651
667
  export const segmentedControlRecipe = {
652
668
  // Text navigation reuses radio selection, without cloning a product-only tab engine.
653
669
  text: { gap: spacing.md, indicatorWidth: 16, indicatorHeight: 2, indicatorRadius: radius.full,
@@ -684,16 +700,25 @@ export const segmentedControlRecipe = {
684
700
  padding: spacing.xxs,
685
701
  gap: spacing.xxs,
686
702
  },
703
+ /**
704
+ * `height` is the connected control's outer height (track border + padding included) and matches the Button /
705
+ * IconButton / Chip height of the same size step, so a segmented control and a chip in one row line up.
706
+ * `minHeight` is the option inside the track (height − 2 × (`container.padding` + `container.borderWidth`)) and `hitSlop` extends each
707
+ * option vertically to the 44 touch target. Before 2026-10-17 `minHeight` was the option itself, so the track
708
+ * drew 46/54 next to 36/44 chips and buttons (DiscoveryGallery sort row, user request "높이 맞춰").
709
+ */
687
710
  sizes: {
688
711
  small: {
689
- minHeight: 36,
690
- hitSlop: 4,
712
+ height: control.buttonHeight.small,
713
+ minHeight: control.buttonHeight.small - 2 * segmentedTrackInset,
714
+ hitSlop: (control.minTouchTarget - (control.buttonHeight.small - 2 * segmentedTrackInset)) / 2,
691
715
  paddingHorizontal: spacing.sm,
692
716
  textVariant: "label",
693
717
  },
694
718
  medium: {
695
- minHeight: control.minTouchTarget,
696
- hitSlop: 0,
719
+ height: control.buttonHeight.medium,
720
+ minHeight: control.buttonHeight.medium - 2 * segmentedTrackInset,
721
+ hitSlop: (control.minTouchTarget - (control.buttonHeight.medium - 2 * segmentedTrackInset)) / 2,
697
722
  paddingHorizontal: spacing.md,
698
723
  textVariant: "body",
699
724
  },
@@ -702,7 +727,7 @@ export const segmentedControlRecipe = {
702
727
  radius: "md",
703
728
  gap: spacing.xxs,
704
729
  idleContent: semanticColors.content.secondary,
705
- fontWeight: fontWeight.semibold,
730
+ fontWeight: fontWeight.medium,
706
731
  selectedBackground: semanticColors.canvas,
707
732
  selectedContent: semanticColors.content.brand,
708
733
  /**
@@ -712,7 +737,7 @@ export const segmentedControlRecipe = {
712
737
  */
713
738
  selectedBorder: semanticColors.border.focus,
714
739
  selectedBorderWidth: stroke.strong,
715
- selectedFontWeight: fontWeight.bold,
740
+ selectedFontWeight: fontWeight.semibold,
716
741
  focusRing: semanticColors.border.focus,
717
742
  pressedOpacity: opacity.pressed,
718
743
  disabledOpacity: opacity.disabled,
@@ -773,11 +798,16 @@ export const switchRecipe = {
773
798
  pressedOpacity: opacity.pressed,
774
799
  },
775
800
  rowMinHeight: control.minTouchTarget,
776
- // A switch with a description is a two-line row like any other list row,
777
- // and a product author found that letting content decide the height gave
778
- // neighbouring rows different heights — this pins it to the same value as
779
- // `listRowRecipe.density.comfortable.twoLineMinHeight`.
780
- rowTwoLineMinHeight: 68,
801
+ // A described switch is a two-line row: the copy (body 20 + gap 4 + caption 16 = 40) with the
802
+ // comfortable list-row vertical padding (8) on both sides. The padding is drawn by the row itself
803
+ // so it survives larger text, and the minimum keeps neighbouring rows equal (a product author found
804
+ // content-only height gave them different heights). It used to borrow ListRow's 68, which is sized
805
+ // for a bodyLarge title + body description; on the smaller switch copy that left 14 above and below,
806
+ // and stacked rows read as widely spaced (2026-10-17 user report, notification settings: 84 pitch
807
+ // for 40 of copy, together with a Stack gap the rows must not have).
808
+ rowPaddingVertical: listRowRecipe.density.comfortable.paddingVertical,
809
+ rowTwoLineMinHeight: typography.body.lineHeight + spacing.xxs + typography.caption.lineHeight
810
+ + 2 * listRowRecipe.density.comfortable.paddingVertical,
781
811
  };
782
812
  export const selectionControlRecipe = {
783
813
  slots: [
@@ -853,8 +883,8 @@ export const selectionControlRecipe = {
853
883
  },
854
884
  label: {
855
885
  color: semanticColors.content.body,
856
- fontWeight: fontWeight.semibold,
857
- checkedFontWeight: fontWeight.bold,
886
+ fontWeight: fontWeight.medium,
887
+ checkedFontWeight: fontWeight.semibold,
858
888
  },
859
889
  leading: { size: "md", color: semanticColors.content.secondary },
860
890
  description: { color: semanticColors.content.secondary },
@@ -1018,8 +1048,8 @@ export const bottomNavigationRecipe = {
1018
1048
  },
1019
1049
  },
1020
1050
  label: {
1021
- fontWeight: fontWeight.semibold,
1022
- selectedFontWeight: fontWeight.bold,
1051
+ fontWeight: fontWeight.medium,
1052
+ selectedFontWeight: fontWeight.semibold,
1023
1053
  textAlign: "center",
1024
1054
  wrap: true,
1025
1055
  fixedLines: null,
@@ -1090,7 +1120,7 @@ export const tabsRecipe = {
1090
1120
  },
1091
1121
  gap: spacing.xs,
1092
1122
  icon: { glyph: "xs" },
1093
- label: { fontWeight: fontWeight.semibold, selectedFontWeight: fontWeight.bold },
1123
+ label: { fontWeight: fontWeight.medium, selectedFontWeight: fontWeight.semibold },
1094
1124
  layouts: {
1095
1125
  content: { fitted: false },
1096
1126
  fitted: { fitted: true },
@@ -1129,21 +1159,28 @@ export const noticeRecipe = {
1129
1159
  contentGap: spacing.xxs,
1130
1160
  iconSize: "sm",
1131
1161
  borderWidth: stroke.default,
1132
- title: { textVariant: "body", fontWeight: fontWeight.bold },
1162
+ title: { textVariant: "body", fontWeight: typography.title.fontWeight },
1133
1163
  description: { color: semanticColors.content.body, textVariant: "body" },
1134
1164
  };
1135
1165
  export const skeletonRecipe = {
1136
- slots: ["root"],
1166
+ slots: ["root", "line"],
1137
1167
  // animated는 0.9.12까지 false였다. 정지한 블록은 로딩이 아니라 깨진 화면으로
1138
1168
  // 읽힌다. 전환 근거와 소비 측 migration은
1139
1169
  // .changeset/skeleton-pulse-by-default.md에 있다.
1140
- defaults: { shape: "block", animated: true },
1141
- background: semanticColors.canvas,
1170
+ defaults: { shape: "block", animated: true, lines: 1 },
1171
+ // 2026-10-17 사용자 요청: canvas 채움 + 1px 테두리는 흰 배경에서 거의 보이지 않아 로딩 모양을 알 수 없었다.
1172
+ // 2026-09-29 "넓은 회색 면은 canvas" 방향의 예외로, 자리표시는 채운 면이어야 모양이 읽힌다(skeleton.md 근거).
1173
+ background: semanticColors.surface.placeholder,
1174
+ // `aspectRatio`가 있으면 높이 대신 폭에 비례한다(media). `button`은 medium 버튼 높이·모서리와 같다.
1142
1175
  shapes: {
1143
- block: { radius: "md", defaultHeight: spacing.xxl },
1144
- text: { radius: "sm", defaultHeight: spacing.md },
1145
- circle: { radius: "full", defaultHeight: glyph.xxl },
1146
- },
1176
+ block: { radius: "md", defaultHeight: spacing.xxl, aspectRatio: null },
1177
+ text: { radius: "sm", defaultHeight: spacing.md, aspectRatio: null },
1178
+ circle: { radius: "full", defaultHeight: glyph.xxl, aspectRatio: null },
1179
+ media: { radius: "md", defaultHeight: null, aspectRatio: 16 / 9 },
1180
+ button: { radius: "md", defaultHeight: control.buttonHeight.medium, aspectRatio: null },
1181
+ },
1182
+ // `lines` > 1 (text 전용): 줄 사이 간격과 짧게 끝나는 마지막 줄. 16 막대 + 8 간격 = 본문 큰 글자 줄 높이 24.
1183
+ lines: { gap: spacing.xs, lastLineWidthPercent: 60 },
1147
1184
  animation: {
1148
1185
  duration: motion.slow * 4,
1149
1186
  easing: "standard",
@@ -1162,7 +1199,7 @@ export const emptyStateRecipe = {
1162
1199
  paddingHorizontal: spacing.xl,
1163
1200
  gap: spacing.xs,
1164
1201
  icon: { size: "lg", color: semanticColors.content.decorative },
1165
- title: { textVariant: "body", color: semanticColors.content.primary, fontWeight: fontWeight.semibold },
1202
+ title: { textVariant: "body", color: semanticColors.content.primary, fontWeight: typography.title.fontWeight },
1166
1203
  description: { textVariant: "label", color: semanticColors.content.secondary },
1167
1204
  };
1168
1205
  export const spinnerRecipe = {
@@ -1214,7 +1251,7 @@ export const loadMoreRecipe = {
1214
1251
  radius: "md",
1215
1252
  color: semanticColors.content.brand,
1216
1253
  textVariant: "label",
1217
- fontWeight: fontWeight.bold,
1254
+ fontWeight: fontWeight.medium,
1218
1255
  },
1219
1256
  spinner: { size: "small", tone: "brand" },
1220
1257
  states: {
@@ -1236,7 +1273,7 @@ export const avatarRecipe = {
1236
1273
  background: semanticColors.canvas,
1237
1274
  content: semanticColors.content.secondary,
1238
1275
  border: semanticColors.border.default,
1239
- /** Initials are bold; their size follows the avatar (Web `.hjm-avatar[data-size]` steps). */
1276
+ /** Initials use the strong emphasis step; their size follows the avatar (Web `.hjm-avatar[data-size]` steps). */
1240
1277
  initialsWeight: "strong",
1241
1278
  };
1242
1279
  /**
@@ -1294,18 +1331,20 @@ export const statisticRecipe = {
1294
1331
  },
1295
1332
  label: {
1296
1333
  color: semanticColors.content.secondary,
1297
- fontWeight: fontWeight.semibold,
1334
+ fontWeight: fontWeight.medium,
1298
1335
  },
1299
1336
  value: {
1300
1337
  color: semanticColors.content.primary,
1301
- fontWeight: fontWeight.heavy,
1338
+ // The key number keeps the semibold emphasis step (600, 2026-10-17 lighter scale) at the `heading` size.
1339
+ // It used to borrow the display heading weight, which was removed with the display tier the same day.
1340
+ fontWeight: fontWeight.semibold,
1302
1341
  numericVariant: "tabular",
1303
1342
  maxLines: null,
1304
1343
  },
1305
1344
  affix: {
1306
1345
  color: semanticColors.content.body,
1307
1346
  textVariant: "body",
1308
- fontWeight: fontWeight.semibold,
1347
+ fontWeight: fontWeight.medium,
1309
1348
  },
1310
1349
  hint: {
1311
1350
  color: semanticColors.content.secondary,
@@ -1313,7 +1352,7 @@ export const statisticRecipe = {
1313
1352
  },
1314
1353
  trend: {
1315
1354
  textVariant: "caption",
1316
- fontWeight: fontWeight.bold,
1355
+ fontWeight: fontWeight.medium,
1317
1356
  gap: spacing.xxs,
1318
1357
  marks: {
1319
1358
  up: "trendUp",
@@ -1378,12 +1417,8 @@ export const sheetRecipe = {
1378
1417
  showWhen: "swipe-dismiss-enabled",
1379
1418
  },
1380
1419
  header: { minHeight: control.minTouchTarget, gap: spacing.sm },
1381
- title: {
1382
- color: semanticColors.content.primary,
1383
- textVariant: "title",
1384
- fontWeight: fontWeight.bold,
1385
- gap: spacing.xxs,
1386
- },
1420
+ // No visual `title` region: sheets render the title only as the accessible name (2026-10-17 user decision).
1421
+ // The "title" slot stays because the title is still the dialog's name.
1387
1422
  body: {
1388
1423
  color: semanticColors.content.body,
1389
1424
  textVariant: "body",
@@ -1467,7 +1502,7 @@ export const alertDialogRecipe = {
1467
1502
  title: {
1468
1503
  color: semanticColors.content.primary,
1469
1504
  textVariant: "title",
1470
- fontWeight: fontWeight.bold,
1505
+ fontWeight: typography.title.fontWeight,
1471
1506
  },
1472
1507
  description: {
1473
1508
  color: semanticColors.content.secondary,
@@ -1586,7 +1621,7 @@ export const toastRecipe = {
1586
1621
  title: {
1587
1622
  color: semanticColors.content.primary,
1588
1623
  textVariant: "body",
1589
- fontWeight: fontWeight.bold,
1624
+ fontWeight: typography.title.fontWeight,
1590
1625
  },
1591
1626
  // Secondary copy so the title leads; body-on-body read as one grey block.
1592
1627
  description: {
@@ -1594,7 +1629,7 @@ export const toastRecipe = {
1594
1629
  textVariant: "body",
1595
1630
  },
1596
1631
  // Step dots of a running task: done and current are brand, upcoming is the divider colour; the current
1597
- // label is bold. Labels sit beside each dot so the row reads without colour alone (WCAG 1.4.1).
1632
+ // label is emphasized. Labels sit beside each dot so the row reads without colour alone (WCAG 1.4.1).
1598
1633
  progress: {
1599
1634
  gap: spacing.sm,
1600
1635
  dotSize: 6,
@@ -1614,7 +1649,7 @@ export const toastRecipe = {
1614
1649
  radius: "full",
1615
1650
  align: "start",
1616
1651
  textVariant: "body",
1617
- fontWeight: fontWeight.bold,
1652
+ fontWeight: fontWeight.medium,
1618
1653
  minHeight: control.minTouchTarget,
1619
1654
  paddingHorizontal: spacing.md,
1620
1655
  },
@@ -1683,7 +1718,7 @@ export const topBarRecipe = {
1683
1718
  paddingHorizontal: spacing.md,
1684
1719
  gap: spacing.xs,
1685
1720
  largeTextThreshold,
1686
- title: { textVariant: "bodyLarge", color: semanticColors.content.primary, fontWeight: fontWeight.bold },
1721
+ title: { textVariant: "bodyLarge", color: semanticColors.content.primary, fontWeight: typography.title.fontWeight },
1687
1722
  titleAction: {
1688
1723
  minHeight: control.minTouchTarget,
1689
1724
  minWidth: control.minTouchTarget,
@@ -1744,7 +1779,7 @@ export const sectionRecipe = {
1744
1779
  gap: spacing.xs,
1745
1780
  headerGap: spacing.sm,
1746
1781
  copyGap: spacing.xxs,
1747
- title: { textVariant: "title", color: semanticColors.content.body, fontWeight: fontWeight.bold },
1782
+ title: { textVariant: "title", color: semanticColors.content.body, fontWeight: typography.title.fontWeight },
1748
1783
  description: { textVariant: "caption", color: semanticColors.content.secondary },
1749
1784
  };
1750
1785
  //# sourceMappingURL=component-recipes.js.map