@godxjp/ui 28.9.0 → 28.12.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 (244) hide show
  1. package/agent/START-HERE.md +193 -0
  2. package/agent/anti-ai-tells.json +158 -0
  3. package/agent/components/Accordion.json +60 -0
  4. package/agent/components/AccountChip.json +59 -0
  5. package/agent/components/Actions.json +78 -0
  6. package/agent/components/Activity.json +80 -0
  7. package/agent/components/Affix.json +78 -0
  8. package/agent/components/Alert.json +65 -0
  9. package/agent/components/AlertDialog.json +109 -0
  10. package/agent/components/AlertDialogRoot.json +52 -0
  11. package/agent/components/Anchor.json +119 -0
  12. package/agent/components/AppLauncher.json +95 -0
  13. package/agent/components/AppProvider.json +105 -0
  14. package/agent/components/AppSettingPicker.json +88 -0
  15. package/agent/components/AppSettingToggle.json +70 -0
  16. package/agent/components/AppShell.json +159 -0
  17. package/agent/components/AreaChart.json +105 -0
  18. package/agent/components/AspectRatio.json +38 -0
  19. package/agent/components/Attachments.json +77 -0
  20. package/agent/components/AuthAccountSummary.json +64 -0
  21. package/agent/components/AuthDivider.json +35 -0
  22. package/agent/components/AuthFooter.json +48 -0
  23. package/agent/components/AuthIdentity.json +42 -0
  24. package/agent/components/AuthShell.json +108 -0
  25. package/agent/components/AuthStack.json +21 -0
  26. package/agent/components/Avatar.json +89 -0
  27. package/agent/components/Badge.json +96 -0
  28. package/agent/components/Banner.json +48 -0
  29. package/agent/components/BarChart.json +108 -0
  30. package/agent/components/BranchScopePicker.json +89 -0
  31. package/agent/components/Breadcrumb.json +54 -0
  32. package/agent/components/Button.json +133 -0
  33. package/agent/components/Calendar.json +259 -0
  34. package/agent/components/Callout.json +46 -0
  35. package/agent/components/Card.json +112 -0
  36. package/agent/components/CardBar.json +49 -0
  37. package/agent/components/CardContent.json +51 -0
  38. package/agent/components/Carousel.json +50 -0
  39. package/agent/components/Cascader.json +209 -0
  40. package/agent/components/CenteredShell.json +66 -0
  41. package/agent/components/ChatBubble.json +100 -0
  42. package/agent/components/ChatBubbleList.json +64 -0
  43. package/agent/components/ChatComposer.json +160 -0
  44. package/agent/components/ChatSuggestion.json +86 -0
  45. package/agent/components/Checkbox.json +68 -0
  46. package/agent/components/CheckboxGroup.json +96 -0
  47. package/agent/components/CodeBlock.json +64 -0
  48. package/agent/components/Collapsible.json +74 -0
  49. package/agent/components/ColorPicker.json +87 -0
  50. package/agent/components/Command.json +168 -0
  51. package/agent/components/CommandPalette.json +84 -0
  52. package/agent/components/CompactBarTrend.json +101 -0
  53. package/agent/components/Conversations.json +82 -0
  54. package/agent/components/CredentialReveal.json +93 -0
  55. package/agent/components/DataState.json +79 -0
  56. package/agent/components/DataTable.json +268 -0
  57. package/agent/components/DatePicker.json +275 -0
  58. package/agent/components/Descriptions.json +67 -0
  59. package/agent/components/Dialog.json +78 -0
  60. package/agent/components/DraggablePanel.json +106 -0
  61. package/agent/components/DropdownMenu.json +102 -0
  62. package/agent/components/EmptyState.json +83 -0
  63. package/agent/components/ErrorSurface.json +128 -0
  64. package/agent/components/FeatureList.json +43 -0
  65. package/agent/components/Field.json +64 -0
  66. package/agent/components/FilterBar.json +99 -0
  67. package/agent/components/Flex.json +153 -0
  68. package/agent/components/FloatButton.json +91 -0
  69. package/agent/components/Form.json +87 -0
  70. package/agent/components/FormErrors.json +51 -0
  71. package/agent/components/FormField.json +137 -0
  72. package/agent/components/FormFieldArray.json +39 -0
  73. package/agent/components/FormFieldControl.json +129 -0
  74. package/agent/components/FormRoot.json +122 -0
  75. package/agent/components/Heading.json +61 -0
  76. package/agent/components/HoverCard.json +55 -0
  77. package/agent/components/Icon.json +60 -0
  78. package/agent/components/InfiniteQueryState.json +58 -0
  79. package/agent/components/Input.json +122 -0
  80. package/agent/components/InputOTP.json +106 -0
  81. package/agent/components/Label.json +43 -0
  82. package/agent/components/LegalDocumentShell.json +102 -0
  83. package/agent/components/Legend.json +42 -0
  84. package/agent/components/LineChart.json +103 -0
  85. package/agent/components/Link.json +41 -0
  86. package/agent/components/ListRow.json +92 -0
  87. package/agent/components/Logo.json +85 -0
  88. package/agent/components/Marquee.json +91 -0
  89. package/agent/components/Masonry.json +82 -0
  90. package/agent/components/MasterDetail.json +95 -0
  91. package/agent/components/MegaMenu.json +120 -0
  92. package/agent/components/MobileShell.json +73 -0
  93. package/agent/components/NavList.json +63 -0
  94. package/agent/components/NumberInput.json +158 -0
  95. package/agent/components/OrgSwitcher.json +89 -0
  96. package/agent/components/OverlayPortalProvider.json +42 -0
  97. package/agent/components/PageContainer.json +181 -0
  98. package/agent/components/Pagination.json +132 -0
  99. package/agent/components/Paragraph.json +40 -0
  100. package/agent/components/PasswordInput.json +79 -0
  101. package/agent/components/PasswordStrength.json +51 -0
  102. package/agent/components/PermissionMatrix.json +81 -0
  103. package/agent/components/PieChart.json +99 -0
  104. package/agent/components/Popover.json +110 -0
  105. package/agent/components/PrefetchLink.json +65 -0
  106. package/agent/components/Progress.json +79 -0
  107. package/agent/components/Prose.json +57 -0
  108. package/agent/components/QrCode.json +62 -0
  109. package/agent/components/Radio.json +98 -0
  110. package/agent/components/RadioGroup.json +91 -0
  111. package/agent/components/RangeTimeline.json +80 -0
  112. package/agent/components/Rating.json +92 -0
  113. package/agent/components/ResizablePanel.json +69 -0
  114. package/agent/components/ResponsiveGrid.json +77 -0
  115. package/agent/components/Reveal.json +70 -0
  116. package/agent/components/ScrollArea.json +104 -0
  117. package/agent/components/SearchInput.json +98 -0
  118. package/agent/components/Segmented.json +96 -0
  119. package/agent/components/Select.json +397 -0
  120. package/agent/components/Separator.json +86 -0
  121. package/agent/components/ServiceCatalogCta.json +46 -0
  122. package/agent/components/ServiceLauncherCard.json +93 -0
  123. package/agent/components/ServiceRolePanel.json +83 -0
  124. package/agent/components/Sheet.json +85 -0
  125. package/agent/components/Sidebar.json +118 -0
  126. package/agent/components/Skeleton.json +57 -0
  127. package/agent/components/SkeletonArticle.json +71 -0
  128. package/agent/components/SkeletonAvatar.json +50 -0
  129. package/agent/components/SkeletonButton.json +57 -0
  130. package/agent/components/SkeletonForm.json +52 -0
  131. package/agent/components/SkeletonImage.json +37 -0
  132. package/agent/components/SkeletonInput.json +51 -0
  133. package/agent/components/SkeletonNode.json +42 -0
  134. package/agent/components/SkeletonRows.json +49 -0
  135. package/agent/components/SkeletonTable.json +45 -0
  136. package/agent/components/Slider.json +160 -0
  137. package/agent/components/SplitPane.json +66 -0
  138. package/agent/components/StatCard.json +83 -0
  139. package/agent/components/Steps.json +95 -0
  140. package/agent/components/Swatch.json +41 -0
  141. package/agent/components/Switch.json +81 -0
  142. package/agent/components/Table.json +112 -0
  143. package/agent/components/Tabs.json +158 -0
  144. package/agent/components/TagInput.json +105 -0
  145. package/agent/components/Text.json +201 -0
  146. package/agent/components/Textarea.json +126 -0
  147. package/agent/components/ThoughtChain.json +76 -0
  148. package/agent/components/Thumbnail.json +70 -0
  149. package/agent/components/TimePicker.json +200 -0
  150. package/agent/components/TimeRangePicker.json +90 -0
  151. package/agent/components/Timeline.json +47 -0
  152. package/agent/components/TimelineGrid.json +92 -0
  153. package/agent/components/Title.json +67 -0
  154. package/agent/components/Toaster.json +42 -0
  155. package/agent/components/Toggle.json +90 -0
  156. package/agent/components/ToggleGroup.json +102 -0
  157. package/agent/components/Toolbar.json +120 -0
  158. package/agent/components/Tooltip.json +110 -0
  159. package/agent/components/Topbar.json +83 -0
  160. package/agent/components/TopbarItem.json +79 -0
  161. package/agent/components/Transfer.json +141 -0
  162. package/agent/components/Tree.json +185 -0
  163. package/agent/components/TreeSelect.json +232 -0
  164. package/agent/components/TwoFactorSetup.json +79 -0
  165. package/agent/components/Typography.json +42 -0
  166. package/agent/components/Upload.json +221 -0
  167. package/agent/components/UploadCropDialog.json +60 -0
  168. package/agent/components/VisuallyHidden.json +20 -0
  169. package/agent/components/Welcome.json +65 -0
  170. package/agent/components/formatDate.json +46 -0
  171. package/agent/components/inertiaUpload.json +32 -0
  172. package/agent/components/useZodForm.json +39 -0
  173. package/agent/components-index.json +884 -0
  174. package/agent/components.json +15515 -0
  175. package/agent/index.json +56 -0
  176. package/agent/llms.txt +32 -0
  177. package/agent/patterns/account-recovery-settings.json +19 -0
  178. package/agent/patterns/async-data-state.json +20 -0
  179. package/agent/patterns/auth-recovery-panels.json +29 -0
  180. package/agent/patterns/badge-coloring.json +14 -0
  181. package/agent/patterns/common-fixes.json +16 -0
  182. package/agent/patterns/confirm-destructive.json +11 -0
  183. package/agent/patterns/data-table-page.json +18 -0
  184. package/agent/patterns/deferred-loading.json +12 -0
  185. package/agent/patterns/error-pages.json +28 -0
  186. package/agent/patterns/inertia-detail-page.json +13 -0
  187. package/agent/patterns/inertia-list-page.json +15 -0
  188. package/agent/patterns/inertia-persistent-layout.json +14 -0
  189. package/agent/patterns/organization-memberships.json +19 -0
  190. package/agent/patterns/page-sections.json +18 -0
  191. package/agent/patterns/settings-page-responsive.json +18 -0
  192. package/agent/patterns/settings-section-rows.json +23 -0
  193. package/agent/patterns/signup-form.json +13 -0
  194. package/agent/patterns/topbar-account-chip.json +18 -0
  195. package/agent/patterns/transactional-email.json +22 -0
  196. package/agent/patterns-index.json +323 -0
  197. package/agent/patterns.json +342 -0
  198. package/agent/rules.json +237 -0
  199. package/agent/tokens.json +8427 -0
  200. package/agent/vocabulary.json +198 -0
  201. package/dist/components/data-display/service-launcher-card.d.ts +19 -0
  202. package/dist/components/data-display/service-launcher-card.js +14 -1
  203. package/dist/components/data-entry/attachments.js +77 -33
  204. package/dist/components/data-entry/input.js +8 -1
  205. package/dist/components/layout/flex.d.ts +2 -2
  206. package/dist/components/layout/flex.js +2 -0
  207. package/dist/components/ui/avatar.d.ts +1 -18
  208. package/dist/components/ui/avatar.js +1 -36
  209. package/dist/components/ui/tag-input.d.ts +10 -0
  210. package/dist/components/ui/tag-input.js +35 -2
  211. package/dist/contracts/measurement.json +1 -1
  212. package/dist/i18n/messages/en.json +190 -1
  213. package/dist/i18n/messages/ja.json +188 -1
  214. package/dist/i18n/messages/vi.json +188 -1
  215. package/dist/lib/image-loading-status.d.ts +25 -0
  216. package/dist/lib/image-loading-status.js +41 -0
  217. package/dist/props/components/data-entry.prop.d.ts +21 -2
  218. package/dist/props/components/layout.prop.d.ts +42 -0
  219. package/dist/props/registry.d.ts +14 -1
  220. package/dist/props/registry.js +18 -1
  221. package/dist/styles/card-layout.css +10 -4
  222. package/dist/styles/control.css +33 -4
  223. package/dist/styles/data-display-layout.css +1 -1
  224. package/dist/styles/data-entry-layout.css +245 -2
  225. package/dist/styles/layout.css +17 -0
  226. package/dist/styles/navigation-layout.css +3 -1
  227. package/dist/styles/shell-layout.css +2 -0
  228. package/dist/styles/table-layout.css +50 -9
  229. package/dist/tokens/components/attachments.css +18 -9
  230. package/dist/tokens/components/segmented.css +7 -3
  231. package/dist/tokens/components/table.css +2 -1
  232. package/docs/COMPOSITION-VS-COMPONENT.md +41 -0
  233. package/docs/DESIGN-AUTHORITY.md +52 -0
  234. package/docs/DEVELOPMENT.md +81 -6
  235. package/docs/assets/service-mark-rose.svg +6 -0
  236. package/docs/assets/service-mark-teal.svg +5 -0
  237. package/docs/data-display/service-launcher-card.tsx +232 -92
  238. package/docs/data-entry/tag-input.tsx +37 -0
  239. package/docs/layout/flex.tsx +40 -0
  240. package/docs/roadmap/website-components.md +34 -0
  241. package/docs/showcase/marketing-page.tsx +54 -45
  242. package/docs/showcase/table-pagination.tsx +99 -18
  243. package/docs/showcase/theme-customization.tsx +25 -2
  244. package/package.json +8 -5
@@ -88,12 +88,14 @@
88
88
 
89
89
  .ui-table-row[data-tone] > [data-slot="table-cell"]:first-child {
90
90
  border-inline-start: var(--table-row-tone-rail-width) solid hsl(var(--table-row-tone-color));
91
- padding-inline-start: calc(var(--table-cell-space-x) - var(--table-row-tone-rail-width));
91
+ padding-inline-start: calc(
92
+ var(--table-cell-space-x, var(--space-section-active)) - var(--table-row-tone-rail-width)
93
+ );
92
94
  }
93
95
 
94
96
  [data-slot="table-head"] {
95
97
  height: var(--table-row-height);
96
- padding: var(--table-cell-padding-y) var(--table-cell-space-x);
98
+ padding: var(--table-cell-padding-y) var(--table-cell-space-x, var(--space-section-active));
97
99
  text-align: start;
98
100
  vertical-align: middle;
99
101
  background: var(--table-header-background, hsl(var(--muted)));
@@ -113,7 +115,7 @@
113
115
  }
114
116
 
115
117
  [data-slot="table-cell"] {
116
- padding: var(--table-cell-padding-y) var(--table-cell-space-x);
118
+ padding: var(--table-cell-padding-y) var(--table-cell-space-x, var(--space-section-active));
117
119
  vertical-align: middle;
118
120
 
119
121
  white-space: nowrap;
@@ -146,7 +148,7 @@
146
148
 
147
149
  [data-slot="table-cell"][data-indent] {
148
150
  padding-inline-start: calc(
149
- var(--table-cell-space-x) + var(--table-cell-indent-level, 0) *
151
+ var(--table-cell-space-x, var(--space-section-active)) + var(--table-cell-indent-level, 0) *
150
152
  var(--table-cell-indent-space-step)
151
153
  );
152
154
  }
@@ -207,7 +209,10 @@
207
209
  justify-content: flex-end;
208
210
  gap: var(--space-inline-sm);
209
211
  padding-block: var(--table-pagination-padding-y, var(--space-stack-sm));
210
- padding-inline: var(--table-pagination-padding-x, var(--table-cell-space-x));
212
+ padding-inline: var(
213
+ --table-pagination-padding-x,
214
+ var(--table-cell-space-x, var(--space-section-active))
215
+ );
211
216
  }
212
217
 
213
218
  .ui-data-table-pagination--numbered {
@@ -634,7 +639,16 @@
634
639
  white-space: normal;
635
640
  }
636
641
 
637
- [data-collapse-below="sm"] [data-slot="table"]:has([data-slot="table-head"]:nth-child(7)) {
642
+ [data-collapse-below="sm"]
643
+ [data-slot="table"]:has(
644
+ [data-slot="table-head"]:nth-child(7),
645
+ [data-slot="table-head"][data-priority="primary"]
646
+ ~ [data-slot="table-head"][data-priority="primary"],
647
+ [data-slot="table-head"][data-priority="secondary"]
648
+ ~ [data-slot="table-head"][data-priority="secondary"],
649
+ [data-slot="table-head"][data-priority="meta"]
650
+ ~ [data-slot="table-head"][data-priority="meta"]
651
+ ) {
638
652
  --table-action-collection-primary-width: var(--table-action-collection-primary-width-floor);
639
653
  --table-action-collection-secondary-width: var(
640
654
  --table-action-collection-secondary-width-floor
@@ -664,7 +678,16 @@
664
678
  white-space: normal;
665
679
  }
666
680
 
667
- [data-collapse-below="md"] [data-slot="table"]:has([data-slot="table-head"]:nth-child(7)) {
681
+ [data-collapse-below="md"]
682
+ [data-slot="table"]:has(
683
+ [data-slot="table-head"]:nth-child(7),
684
+ [data-slot="table-head"][data-priority="primary"]
685
+ ~ [data-slot="table-head"][data-priority="primary"],
686
+ [data-slot="table-head"][data-priority="secondary"]
687
+ ~ [data-slot="table-head"][data-priority="secondary"],
688
+ [data-slot="table-head"][data-priority="meta"]
689
+ ~ [data-slot="table-head"][data-priority="meta"]
690
+ ) {
668
691
  --table-action-collection-primary-width: var(--table-action-collection-primary-width-floor);
669
692
  --table-action-collection-secondary-width: var(
670
693
  --table-action-collection-secondary-width-floor
@@ -694,7 +717,16 @@
694
717
  white-space: normal;
695
718
  }
696
719
 
697
- [data-collapse-below="lg"] [data-slot="table"]:has([data-slot="table-head"]:nth-child(7)) {
720
+ [data-collapse-below="lg"]
721
+ [data-slot="table"]:has(
722
+ [data-slot="table-head"]:nth-child(7),
723
+ [data-slot="table-head"][data-priority="primary"]
724
+ ~ [data-slot="table-head"][data-priority="primary"],
725
+ [data-slot="table-head"][data-priority="secondary"]
726
+ ~ [data-slot="table-head"][data-priority="secondary"],
727
+ [data-slot="table-head"][data-priority="meta"]
728
+ ~ [data-slot="table-head"][data-priority="meta"]
729
+ ) {
698
730
  --table-action-collection-primary-width: var(--table-action-collection-primary-width-floor);
699
731
  --table-action-collection-secondary-width: var(
700
732
  --table-action-collection-secondary-width-floor
@@ -724,7 +756,16 @@
724
756
  white-space: normal;
725
757
  }
726
758
 
727
- [data-collapse-below="xl"] [data-slot="table"]:has([data-slot="table-head"]:nth-child(7)) {
759
+ [data-collapse-below="xl"]
760
+ [data-slot="table"]:has(
761
+ [data-slot="table-head"]:nth-child(7),
762
+ [data-slot="table-head"][data-priority="primary"]
763
+ ~ [data-slot="table-head"][data-priority="primary"],
764
+ [data-slot="table-head"][data-priority="secondary"]
765
+ ~ [data-slot="table-head"][data-priority="secondary"],
766
+ [data-slot="table-head"][data-priority="meta"]
767
+ ~ [data-slot="table-head"][data-priority="meta"]
768
+ ) {
728
769
  --table-action-collection-primary-width: var(--table-action-collection-primary-width-floor);
729
770
  --table-action-collection-secondary-width: var(
730
771
  --table-action-collection-secondary-width-floor
@@ -1,15 +1,24 @@
1
1
 
2
2
  :root {
3
+
3
4
  --attachments-gap: var(--space-2);
4
- --attachments-card-size: var(--upload-tile-size);
5
- --attachments-card-radius: var(--upload-tile-radius);
6
- --attachments-card-gap: var(--upload-tile-space-gap);
7
- --attachments-placeholder-space-inset: var(--upload-dropzone-space-inset);
8
- --attachments-placeholder-radius: var(--upload-dropzone-radius);
9
- --attachments-placeholder-icon-size: var(--upload-dropzone-icon-size);
10
- --attachments-placeholder-title-font-size: var(--font-size-sm, var(--font-size-base));
11
- --attachments-placeholder-description-font-size: var(--upload-dropzone-meta-font-size);
12
- --attachments-placeholder-gap: var(--space-2);
5
+
6
+ --attachments-card-size: 16.75rem;
7
+ --attachments-card-block-size: 4.25rem;
8
+ --attachments-card-radius: var(--radius-lg);
9
+ --attachments-card-gap: var(--space-3);
10
+
11
+ --attachments-placeholder-space-inset: var(--space-4);
12
+ --attachments-placeholder-radius: var(
13
+ --radius-lg
14
+ );
15
+
16
+ --attachments-placeholder-icon-size: var(--icon-size-3xl);
17
+ --attachments-placeholder-title-font-size: var(--font-size-base);
18
+ --attachments-placeholder-description-font-size: var(--font-size-sm, var(--font-size-base));
19
+ --attachments-placeholder-gap: var(--space-1);
20
+
13
21
  --attachments-drop-overlay-background: initial;
22
+
14
23
  --attachments-remove-icon-size: var(--upload-remove-icon-size);
15
24
  }
@@ -4,7 +4,7 @@
4
4
 
5
5
  --segmented-track-padding: calc(var(--space-1) / 2);
6
6
 
7
- --segmented-track-background: var(--muted);
7
+ --segmented-track-background: initial;
8
8
 
9
9
  --segmented-track-radius: var(--radius);
10
10
  --segmented-item-radius: var(--radius-md);
@@ -27,8 +27,12 @@
27
27
  --segmented-item-hover-background: initial;
28
28
  --segmented-item-active-background: var(--secondary);
29
29
 
30
- --segmented-item-selected-background: var(--background);
31
- --segmented-item-selected-shadow: var(--shadow-sm);
30
+ --segmented-item-selected-background: initial;
31
+ --segmented-item-selected-shadow: var(--shadow-md);
32
+
33
+ --segmented-item-selected-font-weight: var(--font-weight-medium);
34
+
35
+ --segmented-item-selected-border-color: initial;
32
36
 
33
37
  --segmented-count-min-width: var(--button-count-min-width);
34
38
  --segmented-count-space-inline: var(--button-count-space-inline);
@@ -6,7 +6,8 @@
6
6
  --table-row-height-comfortable: var(--band-height-xl);
7
7
  --table-row-height: calc(var(--table-row-height-default) * var(--scaling));
8
8
  --table-cell-padding-y: var(--space-2);
9
- --table-cell-space-x: var(--control-padding-x);
9
+
10
+ --table-cell-space-x: initial;
10
11
 
11
12
  --table-cell-indent-space-step: var(--space-4);
12
13
 
@@ -84,6 +84,7 @@ new UI need
84
84
  | **PricingTable** / feature grid | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | **Composition** — `ResponsiveGrid` + `Card` |
85
85
  | Dashboard **page layout** | ❌ | ❌ | ❌ | ❌ | ➖ | ❌ | ❌ | **Composition** — `AppShell` + `PageContainer` + `ResponsiveGrid` |
86
86
  | "Icon medallion" | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | **Composition** — `Avatar` (square) + a Lucide glyph |
87
+ | **Section** / **Band** (full-bleed + measured column) | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | **Composition** — `<section>` + `Flex measure` + `pad`; see §3.1 |
87
88
 
88
89
  `✅ pass · ❌ fail · ➖ borderline`. **StatCard** is the instructive borderline: C2 is weak (it owns little behavior), but it is a universal KPI tile with a controlled API, fully tokenized, broadly reused — so it earns its place. A **Hero** fails six of seven; it is unambiguously a composition.
89
90
 
@@ -106,6 +107,46 @@ C2 rather than C7.
106
107
 
107
108
  **ServiceLauncherCard is the ONE recorded exception, and it is recorded so that it stays one.** It shipped before the test was run against it, and when the test was run it came back with two hard FAILs: **C2** — no state, no keyboard handling, no focus management, its only ARIA three static attributes — and **C3** — its own imports are `Card` + `CardContent` + `Badge` + a Lucide glyph, so "could I build this right now from primitives?" is yes. By §2 that makes it a composition pattern. It is kept anyway because `src/components/layout/app-launcher.tsx` consumes it: it is an internal building block of a component that **does** pass, so the question was never "should it exist" but "should it be PUBLIC", and removing a public export is breaking. Keeping it public was the cheaper call and the ledger lives on gh#814 and at the top of `src/components/data-display/service-launcher-card.tsx`. **Consumers should compose `Card` + `Badge` for a service tile** — reach for `ServiceLauncherCard` only to match `AppLauncher`'s own tiles — and this row is not precedent for adding another static tile to `src/components/`.
108
109
 
110
+ ### 3.1 "Full-bleed outside, measured column inside" — the case that was answered with a PROP (gh#839)
111
+
112
+ This row exists because the question it settles was asked the other way round, and the right answer
113
+ turned out to be neither "add a component" nor "do nothing".
114
+
115
+ **A `Section` component is refused, and it is refused for Hero's reasons.** It owns no state, no
116
+ keyboard, no focus and no ARIA a `<section aria-labelledby>` does not already have (**C2**); it is
117
+ `<section>` + `Flex` + padding + a max-width, i.e. buildable today (**C3**); its API would be the
118
+ grab-bag `band` / `measure` / `glow` / `surface` / `bleed` that **C4** names by name; and a JP
119
+ business-software library would be shipping a marketing band to every consumer (**C7**). One
120
+ criterion of seven passes. Nothing about that is close.
121
+
122
+ **But "do nothing" was also refused, and that is the part worth remembering.** The doctrine's own
123
+ remedy for a composition is a TOKEN (§4.3), and `--page-measure-wide` shipped in 28.8.0 — so on
124
+ paper the gap was already closed. It was not. A token is only closed when something can READ it,
125
+ and three unrelated pages had each hand-written the identical four declarations to do so:
126
+ `docs/showcase/marketing-page.tsx` as an inline `style` constant, `acme-website.tsx` as `.tx-shell`,
127
+ `futurelastic-web.tsx` as `.fl-shell` — one shape, three spellings, two of them page-local CSS
128
+ classes on the pages whose entire claim is that they need none. `docs/TOKENS.md` calls a value that
129
+ appears in more than one place tier 1, and the same logic reads on a SHAPE.
130
+
131
+ **The third move is a prop on a primitive that already passes the test.** `Flex measure` (narrow |
132
+ medium | wide) centres the box and caps it at `var(--page-measure-*)`; it adds no gutter, because
133
+ `pad` owns that. A full-bleed band is now `<section>` — carrying the edge-to-edge paint — around one
134
+ `<Flex direction="col" measure="wide" pad={{ inline: 6, block: 20 }}>`. Measured on
135
+ `marketing-page.tsx`: seven bands, 7 inline `style` spreads and 3 `CSSProperties` constants gone,
136
+ the metric that page defends (0 bespoke classes, 0 raw px/rem literals) unchanged at 0/0, and the
137
+ geometry byte-identical (1440: section 1440px, column 1152px, band 96/80px, gutter 24px).
138
+
139
+ **`PageContainer measure="wide"` stays refused, and for the original reason.** A marketing page is
140
+ full-bleed `<section>`s; `PageContainer` owns page padding and a header/toolbar/footer scaffold, so
141
+ the two pages that are the prop's proof still cannot consume it. The prop went to the primitive
142
+ those pages CAN put inside a `<section>`, which is the distinction the earlier deferral was missing
143
+ rather than a reversal of it.
144
+
145
+ **The rule this leaves behind:** when a composition repeats a shape, ask whether an existing
146
+ primitive is missing ONE AXIS before you ask whether a new component is missing. A component that
147
+ fails C1–C7 and a prop that closes the same gap are not the same proposal, and the test above is
148
+ only asking about the first.
149
+
109
150
  ---
110
151
 
111
152
  ## 4. How to build a composition pattern correctly
@@ -449,6 +449,58 @@ expandable.expandedRowKeys` / `onExpandedRowsChange`. This library already mappe
449
449
  (`role="toolbar"`, `aria-pressed`); the prop there only keeps the last item selected.
450
450
  Divergence from Radix's markup is the point of the change, not a side effect of it.
451
451
 
452
+ - **`Input addonBefore` / `addonAfter` stay, although antd DEPRECATED them (gh#841).** Verified in
453
+ `ant-design@master` rather than on the docs page: `components/input/Input.tsx` carries
454
+ `@deprecated Use Space.Compact instead` on both props and fires a dev-time
455
+ `devUseWarning('Input').deprecated(…)` for each, while still rendering them; both locale tables
456
+ strike the rows through and the `addon` demo is flagged `debug`, so it no longer appears on the
457
+ published page. The deprecation is a REFACTOR onto `Space.Compact`, and this library has no
458
+ `Space` — so following it would delete the joined control and put nothing in its place, which is
459
+ a breaking change bought for nothing. `addonBefore` IS the joined control here: it renders
460
+ `.ui-input-group`, and gh#841 squared its seam. The steer that IS followed is the one antd's own
461
+ live surface gives: a GLYPH belongs in `prefix`, inside the field, where there is one box, one
462
+ border and no seam; an addon is for a label segment. Written on the prop
463
+ (`src/props/components/data-entry.prop.ts`) and in the MCP catalog. Revisit if a `Space`/compact
464
+ primitive is ever ported — then antd's steer is followable and this becomes a migration.
465
+
466
+ - **`Attachments` is Ant Design X's card and tray, with five measured departures (gh#855).** The
467
+ component had shipped with NO stylesheet at all — fifteen slot classes, one rule, twelve tokens
468
+ read by nothing — so the port below is the first time it followed anything. Geometry was taken
469
+ from `@ant-design/x@2.9.0` (`es/attachments/style/index.js` for root/placeholder/drop-area,
470
+ `es/file-card/style/index.js` for list/card/overflow/remove), not from a screenshot. What was
471
+ NOT followed, and why:
472
+
473
+ 1. **The card's 268px is a CEILING, not a fixed width.** Ant X writes `width: 268` flat. At the
474
+ 320px viewport this repo sweeps, 268 plus the page gutter spills, and `check:frame-overflow`
475
+ is the gate that would have to be baselined around it. `inline-size` + `max-inline-size: 100%`
476
+ keeps Ant's box everywhere it fits and gives ground only where the alternative is a clip.
477
+ 2. **One leading box for a thumbnail AND a glyph.** Ant X has two card types — a 268x68 overview
478
+ card and a separate 68x68 image card — so a tray mixing a `.png` with a `.pdf` gets two
479
+ shapes. This library already decided that question for `Upload` (gh#720: "the image rows draw
480
+ their previewUrl as a thumbnail and every other row draws the glyph for its kind, on one row
481
+ height"), and a second answer in the same group would be the defect.
482
+ 3. **The remove ✕ is always visible, and in flow.** Ant X pins it absolutely outside the card
483
+ corner at `opacity: 0` until the card is hovered. A hover-only control has no touch
484
+ equivalent — worse, an `opacity: 0` button is still hit-testable, so a tablet user taps a
485
+ control they cannot see. `.ui-upload-tile-remove` already made this call. Always-visible in
486
+ turn removes the reason to position it outside the box, so it is the row's trailing item.
487
+ 4. **The placeholder mark is the icon scale's 40px step, not `fontSizeHeading2` (30px).** 30 is
488
+ not one of the nine steps gh#326 fixed, and its neighbours are 24 and 36 — equidistant. The
489
+ token keeps the value the frozen table in `src/tokens/__tests__/icon-size-scale.test.ts`
490
+ already records for it, which is `--upload-dropzone-icon-size`: the mark a reader meets this
491
+ surface beside. Same reasoning holds `--attachments-remove-icon-size` at 14px against Ant's
492
+ `fontSizeLG` = 16.
493
+ 5. **The placeholder carries a dashed border at rest; Ant X's is `transparent`.** Ant X's
494
+ placeholder sits INSIDE a `Sender`, which already draws the boundary. Here `Attachments` is
495
+ also used standalone, where a target with no edge is not a target — so it takes
496
+ `.ui-upload-dropzone`'s dashed edge.
497
+
498
+ Not ported, and named here so the gap is a decision rather than an oversight: Ant X's 8px
499
+ `::before`/`::after` scroll-edge gradients and their `ping-start`/`ping-end` state (they need a
500
+ scroll listener, and the clipped card already reads as "more this way"), and its full-screen
501
+ drop area — `getDropContainer` positions the layer `fixed`, but activation is still the
502
+ control's own dragenter, not the document's.
503
+
452
504
  **A knob that only a fork could reach is not parity either.** antd's `components`,
453
505
  `filterDropdown`, `classNames`/`styles` semantic maps and `prefixCls` all exist to let a consumer
454
506
  replace the rendered markup. This library answers that layer with tokens (cardinal rule #45), so
@@ -112,16 +112,91 @@ A change isn't done until its documentation reflects it:
112
112
 
113
113
  ---
114
114
 
115
- ## 5. Verify before finishing
115
+ ## 5. Verify before finishing — scope the checks to the DIFF
116
+
117
+ > **Read §5.0 before you run anything.** This section was rewritten after the owner watched a
118
+ > two-file change take six minutes of gates and asked, correctly, _"bản chất chỉ sửa rất ít, check
119
+ > diff đéo được hay sao? có phải release đâu mà run hết thế?"_ — it is not a release, so stop
120
+ > running a release's worth of checks.
121
+
122
+ ### 5.0 MEASURE FIRST, THEN COMPLAIN — the real cost table
123
+
124
+ Every number below was measured on this repo, on an M-series Mac, warm. **Measure again before you
125
+ believe them; a cost you assumed is how you end up optimising the wrong thing.**
126
+
127
+ | command | cost | scope |
128
+ | -------------------------- | --------------- | ------------------------------------------ |
129
+ | `check:token-tiers` | **0.2s** | whole repo |
130
+ | `audit` | **0.5s** | whole repo (~600 files) |
131
+ | `typecheck:docs` | **0.7s** | whole `docs/` program |
132
+ | `typecheck` | **1.3s** | whole `src/` program |
133
+ | `build` | **1.5s** | whole package |
134
+ | `preview:build` | **1.9s** | whole preview |
135
+ | `lint` | **12.9s** | whole repo |
136
+ | **`check:frame-overflow`** | **52s** (was 339s before it was parallelised, gh#851) | **199 frames x 2 viewports, in a browser** |
137
+
138
+ **The lesson is the opposite of the intuition.** "Typecheck the whole system" sounds wasteful and
139
+ costs **1.3 seconds** — typescript-7 is the native compiler, there is nothing to save there, and
140
+ scoping it to changed files would cost more in ceremony than it saves in wall clock. Meanwhile the
141
+ one command that _does_ cost real time is the browser sweep, and it is the one that looks innocent
142
+ in a shell script.
143
+
144
+ `check:frame-overflow` alone was **~96% of the wall clock** of a full local pass. It ran all 398
145
+ navigations down a SINGLE page, in series. It now runs a pool of one page per core (capped at 8):
146
+ **339s -> 52.3s, 6.5x**, byte-identical output, and a planted overflow is still caught at both
147
+ viewports by both. `FRAME_CONCURRENCY=1` restores the serial run for debugging.
148
+
149
+ > **Never argue about the cost of a gate you have not timed.** Time it:
150
+ > `st=$(date +%s); pnpm <gate> >/dev/null 2>&1; echo $(( $(date +%s) - st ))s`
151
+
152
+ ### 5.1 What to run, by what you touched
153
+
154
+ Find the row that matches your diff. Run that row. Do not run the rows below it.
155
+
156
+ | you changed | run | why |
157
+ | ---------------------------------- | ------------------------------------------------------------------------------------------------ | --------------------------------------------------------- |
158
+ | a `.css` file only | `audit` · `check:token-tiers` · the component's own tests · `check:frame-overflow --only <slug>` | no TS changed, so `typecheck` has nothing to say |
159
+ | a `.tsx`/`.ts` in `src/` | `typecheck` · `lint` · `audit` · that group's tests | 1.3s + 12.9s, both worth it |
160
+ | a file in `docs/` | `typecheck:docs` · `audit` · `check:example-imports` | `typecheck` covers `src/`, not `docs/` |
161
+ | a token in `src/tokens/` | + `check:token-tiers` · `src/tokens/__tests__` · `pnpm regen` twice (gh#847) | frozen tier tables go stale instantly |
162
+ | a public export / prop | + `pnpm ship:surface` | 8 stacked generators; the list is derived, not remembered |
163
+ | anything that moves LAYOUT | `check:frame-overflow --only <slug>` while you work; the FULL sweep once, before the PR | 17.5s vs 339s |
164
+ | opening the PR / cutting a release | the full sweep, `verify:ci:static`, the full suite in CI | the only place "run everything" is right |
165
+
166
+ ### 5.2 `--only` — the diff-scoped browser sweep
167
+
168
+ Added in gh#851 for exactly this. The sweep had no filter at all: 199 frames or nothing.
169
+
170
+ ```bash
171
+ pnpm check:frame-overflow --only table # 9 frames, 17.5s
172
+ pnpm check:frame-overflow --only '/^data-entry-(switch|segmented)/' # 3 frames
173
+ pnpm check:frame-overflow # all 199, 52s
174
+ ```
175
+
176
+ Measured: `--only table` is **17.5s against the full sweep's 52.3s**. Both matter — the pool cut
177
+ the full sweep 6.5x, and `--only` cuts what is left to the frames a diff can actually reach.
178
+
179
+ Two guards, both deliberate:
180
+
181
+ - **`--only` refuses `--update-baseline`.** A partial sweep does not know whether the frames it
182
+ skipped are still clean, and recording that as the whole truth is how a baseline stops meaning
183
+ anything. Rebaselining needs the full sweep.
184
+ - **Its success line says `PARTIAL, not a release gate`** and prints how many frames it skipped, so
185
+ a filtered green can never be pasted into a PR as if it were the real thing.
186
+
187
+ ### 5.3 The commands, for reference
116
188
 
117
189
  ```bash
118
- pnpm lint # eslint — self-contained flat config
119
- pnpm typecheck # tsc --noEmit
190
+ pnpm lint # eslint — self-contained flat config 12.9s
191
+ pnpm typecheck # tsc --noEmit over src/ 1.3s
192
+ pnpm typecheck:docs # tsc --noEmit over docs/ 0.7s
193
+ pnpm audit # godxjp-ui-audit — 0 errors for touched files 0.5s
194
+ pnpm check:mcp-sync # MCP registry <-> library export drift guard
120
195
  pnpm vitest run src/components/<group>/__tests__ --maxWorkers=2 # ONLY what you touched
196
+ pnpm check:frame-overflow --only <slug> # the frames your change can reach ~18s
197
+ pnpm check:frame-overflow # all 199 — pre-PR and CI only 52s
198
+ pnpm preview:build # integration test: examples + docs must build 1.9s
121
199
  pnpm test # FULL suite — CI only, never from an agent loop
122
- pnpm preview:build # integration test: examples + docs must build — at most once, pre-PR
123
- pnpm audit # godxjp-ui-audit — 0 errors for touched files
124
- pnpm check:mcp-sync # MCP registry ↔ library export drift guard
125
200
  pnpm check:frame-axe # WCAG 2.2 AA over every frame — LOCAL ONLY, never in CI (FRAME-A11Y-CI.md)
126
201
  ```
127
202
 
@@ -0,0 +1,6 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="240" height="96" viewBox="0 0 240 96" role="img">
2
+ <rect width="240" height="96" rx="12" fill="hsl(348 32% 42%)"/>
3
+ <rect x="24" y="30" width="36" height="36" rx="8" fill="hsl(348 32% 88%)"/>
4
+ <rect x="76" y="36" width="140" height="10" rx="5" fill="hsl(348 32% 88%)"/>
5
+ <rect x="76" y="54" width="96" height="10" rx="5" fill="hsl(348 32% 74%)"/>
6
+ </svg>
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img">
2
+ <rect width="128" height="128" rx="28" fill="hsl(178 38% 34%)"/>
3
+ <path d="M40 78 L64 34 L88 78 Z" fill="hsl(178 38% 88%)"/>
4
+ <circle cx="64" cy="90" r="9" fill="hsl(178 38% 88%)"/>
5
+ </svg>