@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
@@ -649,6 +649,40 @@ with no call site in the two pages that are its proof, which is the tier-2 mista
649
649
  warns about. The token stands on its own as the vocabulary a composition caps its shell with, the
650
650
  way `--gradient-hero` does.
651
651
 
652
+ **CLOSED in gh#839, and the refusal above still stands.** Leaving the centred column to "the
653
+ composition writes it" was measured and found wanting: three unrelated pages hand-wrote the SAME
654
+ four declarations — `marketing-page.tsx` as an inline `style` constant, `acme-website.tsx` as
655
+ `.tx-shell`, `futurelastic-web.tsx` as `.fl-shell` — which `docs/TOKENS.md` calls tier 1, and two of
656
+ the three spelled it as a page-local CSS class. The answer was not a `Section` component (it fails
657
+ C1–C7 the way Hero does — `docs/COMPOSITION-VS-COMPONENT.md` §3.1 carries the ledger) and not
658
+ `PageContainer`, which still cannot be consumed by a full-bleed page. It is **`Flex measure`**
659
+ (`narrow | medium | wide`, reading `--page-measure-*`), on the primitive a `<section>` CAN put
660
+ inside itself. It centres and caps and nothing else: `pad` keeps the gutter, so a band is
661
+ `<section>` around one `<Flex direction="col" measure="wide" pad={{ inline: 6, block: 20 }}>`.
662
+
663
+ | step | reader |
664
+ | ---------------------------------- | -------------------------------------------------------------- |
665
+ | `--space-section-band` (80px) | `pad={{ block: 20 }}` — `GapStepProp` gained 20/24 |
666
+ | `--space-section-hero` (96px) | `pad={{ block: 24 }}` |
667
+ | `--page-measure-{narrow,med,wide}` | `Flex measure` — and `PageContainer measure` for the first two |
668
+
669
+ **No compact rung for the band steps, decided on measurement.** At 320×568 the hero band is 96px
670
+ at both edges = 33.8% of the first screen, and the `<h1>` still lands fully above the fold (top
671
+ 195px, bottom 315px). Three reasons not to add one: (1) 96px is already the NARROW rung of the
672
+ industry pair — Tailwind UI's marketing sections are `py-24 sm:py-32`, i.e. 96px on a phone and
673
+ 128px from `sm` up, so the missing step is at the wide end, not the narrow one; (2) a numeric `pad`
674
+ step is a VALUE by contract (`pad={3}` is 12px at every width — `GapStepProp`'s own docstring says
675
+ so), and making 20/24 responsive would make them the two steps whose number lies; (3) a
676
+ `--space-section-band-compact` token would have nowhere to be flipped: every `-active`/`-compact`
677
+ pair in this repo switches inside a COMPONENT's scope (`.ui-page-container`,
678
+ `.ui-centered-shell[data-preset="public-landing"]`), and a plain `<section>` has no such scope — so
679
+ it would ship as vocabulary with no caller, the exact defect gh#839 exists to stop.
680
+ `--centered-shell-landing-main-padding-block-compact` is not the counter-example it looks like: it
681
+ is 40px → 24px of a SHELL's `main` padding, not a marketing band, and it lives inside that shell's
682
+ own scope. **The real capability the band wants is a responsive object form for `pad`**, the one
683
+ `direction` and `ResponsiveGrid columns` already take (`pad={{ block: { base: 12, md: 20 } }}`) —
684
+ that is its own issue, and a much larger one than a token rung.
685
+
652
686
  ---
653
687
 
654
688
  ## 6. The animation and motion story
@@ -30,26 +30,34 @@
30
30
  *
31
31
  * ── WHAT IS STILL HAND-WRITTEN, AND WHY (the finding this page exists to produce) ───────────────
32
32
  *
33
- * Zero classes and zero literals did NOT mean zero CSS. Six things below are inline `style`
34
- * objects because no PROP reaches them. Each one is a named gap, in the order a reader meets them:
33
+ * Zero classes and zero literals did NOT mean zero CSS. Six things below WERE inline `style`
34
+ * objects because no PROP reached them. Each one is a named gap, in the order a reader meets them;
35
+ * gh#839 closed the first two:
35
36
  *
36
- * 1. THE SECTION BAND — `paddingBlock: var(--space-section-band | --space-section-hero)`.
37
- * PARTLY CLOSED by gh#839: the numeric `GapProp` ladder now reaches the two band steps, so
37
+ * 1. THE SECTION BAND — was `paddingBlock: var(--space-section-band | --space-section-hero)`.
38
+ * CLOSED by gh#839: the numeric `GapProp` ladder reaches the two band steps, so
38
39
  * `pad={{ block: 20 }}` (80px) and `pad={{ block: 24 }}` (96px) state the rhythm through the
39
- * public API. The bands below still use an inline `style` because each one ALSO needs the
40
- * centred inner column (`SHELL`), and nothing owns "full-bleed outside, measured column
41
- * inside" — that half of gh#839 is open. Neither step has a compact variant either:
42
- * `--space-section-hero` measures 96px at 1440 and at 320, where the package's own landing
43
- * preset drops its band via `--centered-shell-landing-main-padding-block-compact`.
44
- * 2. THE CENTRED COLUMN — `marginInline: auto` + `inlineSize: 100%` +
40
+ * public API, and every band below now does. Neither step has a compact variant and gh#839
41
+ * decided it should not: `--space-section-hero` measures 96px at 1440 AND at 320, which is
42
+ * deliberate — Tailwind UI's own marketing sections are `py-24 sm:py-32`, i.e. 96px is the
43
+ * NARROW rung of the industry pair and the desktop one is the step above. A numeric `pad`
44
+ * step is a VALUE (`pad={3}` is 12px everywhere); making one of them responsive would break
45
+ * that contract for the whole ladder. `--centered-shell-landing-main-padding-block-compact`
46
+ * is not the counter-example it looks like: 40px → 24px is a SHELL's main padding, not a
47
+ * marketing band, and it switches inside `.ui-centered-shell`'s own scope, which a plain
48
+ * `<section>` has no equivalent of.
49
+ * 2. THE CENTRED COLUMN — was `marginInline: auto` + `inlineSize: 100%` +
45
50
  * `maxInlineSize: var(--page-measure-wide)` + `paddingInline: var(--space-6)`, repeated at
46
- * every band. gh#831 minted `--page-measure-wide` and deliberately did not add
47
- * `PageContainer measure="wide"`, reasoning that a marketing page is full-bleed sections with
48
- * a centred inner column and `PageContainer` owns neither. The reasoning holds; its
49
- * consequence is that NOTHING owns the centred inner column.
50
- * 3. THE STICKY BAR — `position: sticky` + `insetBlockStart` + `zIndex`. `Affix` does not exist
51
- * here (`docs/roadmap/website-components.md` §5.2 proposed `--affix-inset-block-start` and
52
- * `--affix-z-index`; neither shipped) and `Topbar` has no `sticky` prop.
51
+ * every band. CLOSED by gh#839 as `Flex measure="wide"` + `pad={{ inline: 6 }}`. gh#831 minted
52
+ * `--page-measure-wide` and deliberately did not add `PageContainer measure="wide"`, reasoning
53
+ * that a marketing page is full-bleed sections with a centred inner column and `PageContainer`
54
+ * owns neither. That reasoning still holds and was not reopened — the prop went to `Flex`, the
55
+ * primitive a full-bleed `<section>` can actually put inside itself, where it has seven call
56
+ * sites on this page. `measure` adds NO gutter: `pad` owns that, and one band with two padding
57
+ * owners is how a measure and its inset drift apart.
58
+ * 3. THE STICKY BAR — `position: sticky` + `insetBlockStart` + `zIndex`. `Affix` has since
59
+ * shipped (28.8.0, with the measured placeholder that removes the 62px page jump) and this
60
+ * page has not yet been moved onto it; `Topbar` still has no `sticky` prop.
53
61
  * 4. THE GLASS — `--topbar-background-alpha` / `--topbar-backdrop-blur-size` as inline custom
54
62
  * properties. They are `initial` by design, so a call site MUST set them, and that is correct.
55
63
  * This used to read `--space-2`, borrowing a token that means something else, because the
@@ -155,20 +163,9 @@ import { useTranslation } from "@godxjp/ui/i18n";
155
163
  import { Flex, ResponsiveGrid, Separator, Topbar } from "@godxjp/ui/layout";
156
164
  import { AppSettingPicker } from "@godxjp/ui/navigation";
157
165
 
158
- /* ── The five inline-style constants named in the docblock. Every value is a token reference; no
159
- number with a unit appears anywhere in this file. ─────────────────────────────────────────── */
160
-
161
- /** (2) The centred inner column. Nothing in the package owns this shape yet. */
162
- const SHELL: CSSProperties = {
163
- marginInline: "auto",
164
- inlineSize: "100%",
165
- maxInlineSize: "var(--page-measure-wide)",
166
- paddingInline: "var(--space-6)",
167
- };
168
-
169
- /** (1) The section rhythm gh#831 named and no prop reads. */
170
- const BAND: CSSProperties = { paddingBlock: "var(--space-section-band)" };
171
- const HERO_BAND: CSSProperties = { paddingBlock: "var(--space-section-hero)" };
166
+ /* ── The inline-style constants named in the docblock. Every value is a token reference; no number
167
+ with a unit appears anywhere in this file. Gaps 1 and 2 no longer have one: `Flex measure` and
168
+ `Flex pad` state the band through props (gh#839). ──────────────────────────────────────────── */
172
169
 
173
170
  /** (5) The halo host and the halo layer. */
174
171
  const GLOW_HOST: CSSProperties = { position: "relative", overflow: "hidden" };
@@ -446,7 +443,13 @@ export default function MarketingPage() {
446
443
  className="ui-brand-glow"
447
444
  style={{ ...GLOW_LAYER, ...HERO_GLOW }}
448
445
  />
449
- <div style={{ ...SHELL, ...HERO_BAND, ...ABOVE_GLOW }}>
446
+ <Flex
447
+ direction="col"
448
+ gap="none"
449
+ measure="wide"
450
+ pad={{ inline: 6, block: 24 }}
451
+ style={ABOVE_GLOW}
452
+ >
450
453
  <ResponsiveGrid columns={{ base: 1, lg: 2 }} gap="xl" align="start">
451
454
  <Reveal>
452
455
  <Flex direction="col" gap="lg">
@@ -532,12 +535,12 @@ export default function MarketingPage() {
532
535
  </Card>
533
536
  </Reveal>
534
537
  </ResponsiveGrid>
535
- </div>
538
+ </Flex>
536
539
  </section>
537
540
 
538
541
  {/* ── LOGO WALL ────────────────────────────────────────────────────────────────────── */}
539
542
  <section aria-labelledby="logos-title">
540
- <div style={{ ...SHELL, ...BAND }}>
543
+ <Flex direction="col" gap="none" measure="wide" pad={{ inline: 6, block: 20 }}>
541
544
  <Flex direction="col" gap="lg">
542
545
  <Flex direction="col" gap="xs">
543
546
  <Heading level={2} size="lg" id="logos-title">
@@ -567,13 +570,13 @@ export default function MarketingPage() {
567
570
  ))}
568
571
  </Marquee>
569
572
  </Flex>
570
- </div>
573
+ </Flex>
571
574
  </section>
572
575
 
573
576
  {/* ── FEATURE GRID ─────────────────────────────────────────────────────────────────── */}
574
577
  <section id="solutions" aria-labelledby="features-title">
575
578
  <Flex as="div" direction="col" gap="none" surface="muted">
576
- <div style={{ ...SHELL, ...BAND }}>
579
+ <Flex direction="col" gap="none" measure="wide" pad={{ inline: 6, block: 20 }}>
577
580
  <Flex direction="col" gap="xl">
578
581
  <Flex direction="col" gap="xs">
579
582
  <Heading level={2} size="3xl" weight="bold" id="features-title">
@@ -613,13 +616,13 @@ export default function MarketingPage() {
613
616
  ))}
614
617
  </ResponsiveGrid>
615
618
  </Flex>
616
- </div>
619
+ </Flex>
617
620
  </Flex>
618
621
  </section>
619
622
 
620
623
  {/* ── TESTIMONIALS ─────────────────────────────────────────────────────────────────── */}
621
624
  <section aria-labelledby="quotes-title">
622
- <div style={{ ...SHELL, ...BAND }}>
625
+ <Flex direction="col" gap="none" measure="wide" pad={{ inline: 6, block: 20 }}>
623
626
  <Flex direction="col" gap="lg">
624
627
  <Heading level={2} size="3xl" weight="bold" id="quotes-title">
625
628
  {t("marketingShowcase.testimonials.title")}
@@ -673,13 +676,13 @@ export default function MarketingPage() {
673
676
  <CarouselDots />
674
677
  </Carousel>
675
678
  </Flex>
676
- </div>
679
+ </Flex>
677
680
  </section>
678
681
 
679
682
  {/* ── PRICING ──────────────────────────────────────────────────────────────────────── */}
680
683
  <section id="pricing" aria-labelledby="pricing-title">
681
684
  <Flex as="div" direction="col" gap="none" surface="muted">
682
- <div style={{ ...SHELL, ...BAND }}>
685
+ <Flex direction="col" gap="none" measure="wide" pad={{ inline: 6, block: 20 }}>
683
686
  <Flex direction="col" gap="xl">
684
687
  <Flex direction="col" gap="md">
685
688
  <Heading level={2} size="3xl" weight="bold" id="pricing-title">
@@ -774,7 +777,7 @@ export default function MarketingPage() {
774
777
  })}
775
778
  </ResponsiveGrid>
776
779
  </Flex>
777
- </div>
780
+ </Flex>
778
781
  </Flex>
779
782
  </section>
780
783
 
@@ -785,7 +788,13 @@ export default function MarketingPage() {
785
788
  className="ui-brand-glow"
786
789
  style={{ ...GLOW_LAYER, ...CTA_GLOW }}
787
790
  />
788
- <div style={{ ...SHELL, ...BAND, ...ABOVE_GLOW }}>
791
+ <Flex
792
+ direction="col"
793
+ gap="none"
794
+ measure="wide"
795
+ pad={{ inline: 6, block: 20 }}
796
+ style={ABOVE_GLOW}
797
+ >
789
798
  <Reveal on="view">
790
799
  <Flex direction="col" gap="md" align="center">
791
800
  <Heading level={2} size="4xl" weight="bold" align="center" id="cta-title">
@@ -810,14 +819,14 @@ export default function MarketingPage() {
810
819
  </Text>
811
820
  </Flex>
812
821
  </Reveal>
813
- </div>
822
+ </Flex>
814
823
  </section>
815
824
  </main>
816
825
 
817
826
  {/* ── FOOTER ───────────────────────────────────────────────────────────────────────── */}
818
827
  <footer>
819
828
  <Flex as="div" direction="col" gap="none" surface="muted">
820
- <div style={{ ...SHELL, ...BAND }}>
829
+ <Flex direction="col" gap="none" measure="wide" pad={{ inline: 6, block: 20 }}>
821
830
  <Flex direction="col" gap="lg">
822
831
  {/* ResponsiveGrid.Item span — the asymmetric footer both siblings hand-wrote a
823
832
  `grid-template-columns: 1.4fr 1fr 1fr 1fr` for. It has shipped the whole time. */}
@@ -877,7 +886,7 @@ export default function MarketingPage() {
877
886
  </Flex>
878
887
  </Flex>
879
888
  </Flex>
880
- </div>
889
+ </Flex>
881
890
  </Flex>
882
891
  </footer>
883
892
  </Flex>
@@ -36,6 +36,7 @@ import {
36
36
  import { Button, Text } from "@godxjp/ui/general";
37
37
  import { Flex, PageContainer } from "@godxjp/ui/layout";
38
38
  import { Pagination } from "@godxjp/ui/navigation";
39
+ import { useTranslation } from "@godxjp/ui/i18n";
39
40
  import {
40
41
  Select,
41
42
  SelectContent,
@@ -91,8 +92,13 @@ function makeRows(count: number, prefix = "ATT"): Attendance[] {
91
92
  date: `2026-06-${String((i % 28) + 1).padStart(2, "0")}`,
92
93
  employee: NAMES[i % NAMES.length],
93
94
  dept: DEPTS[i % DEPTS.length],
94
- clockIn: `0${startHour}:${["02", "58", "31", "07"][i % 4]}`,
95
- clockOut: ot ? `${18 + Math.floor(ot / 60)}:${String(ot % 60).padStart(2, "0")}` : "18:00",
95
+ // `0${startHour}` hard-coded the leading zero and printed `010:07` at startHour 10 —
96
+ // the hand-built formatting this repo's own rules forbid. `padStart` on the hour, the
97
+ // same way the line above already does it for the date.
98
+ clockIn: `${String(startHour).padStart(2, "0")}:${["02", "58", "31", "07"][i % 4]}`,
99
+ clockOut: ot
100
+ ? `${String(18 + Math.floor(ot / 60)).padStart(2, "0")}:${String(ot % 60).padStart(2, "0")}`
101
+ : "18:00",
96
102
  overtime: ot,
97
103
  status: statuses[i % statuses.length],
98
104
  };
@@ -102,20 +108,38 @@ function makeRows(count: number, prefix = "ATT"): Attendance[] {
102
108
  const minutesFmt = (m: number) => (m === 0 ? "—" : `${m}分`);
103
109
 
104
110
  // Columns are shared between the cards (pure data — no per-card state inside).
111
+ /* EVERY column declares a width, not just the first (gh#849).
112
+ *
113
+ * Measured at 2000px before this, as empty pixels between one cell's ink and the next's:
114
+ *
115
+ * 部署 → 出勤 521 残業 → 状態 57
116
+ * 従業員 → 部署 411 勤務日 → 従業員 40
117
+ * 退勤 → 残業 280
118
+ *
119
+ * A 521px hole beside a 40px seam, in one row. Only `勤務日` carried a width, so
120
+ * `table-layout: auto` sized the rest from content and dumped ALL the slack of a 1952px card
121
+ * into the two free-text columns — and because those are start-aligned while the measures are
122
+ * end-aligned, the slack opened as craters BETWEEN columns rather than inside them.
123
+ *
124
+ * With a width on every column the engine distributes the excess proportionally instead, so the
125
+ * gaps scale together and no single one dominates. The numbers are a ratio, not a measurement:
126
+ * the two free-text columns get the larger share because they are the ones that actually wrap. */
105
127
  const columns: ColumnDef<Attendance>[] = [
106
128
  { key: "date", header: "勤務日", width: "w-28" },
107
- { key: "employee", header: "従業員" },
108
- { key: "dept", header: "部署", hiddenOnMobile: true },
129
+ { key: "employee", header: "従業員", width: "w-44" },
130
+ { key: "dept", header: "部署", hiddenOnMobile: true, width: "w-36" },
109
131
  {
110
132
  key: "clockIn",
111
133
  header: "出勤",
112
134
  align: "right",
135
+ width: "w-24",
113
136
  render: (row) => <Text tabular>{row.clockIn}</Text>,
114
137
  },
115
138
  {
116
139
  key: "clockOut",
117
140
  header: "退勤",
118
141
  align: "right",
142
+ width: "w-24",
119
143
  render: (row) => <Text tabular>{row.clockOut}</Text>,
120
144
  },
121
145
  {
@@ -123,12 +147,17 @@ const columns: ColumnDef<Attendance>[] = [
123
147
  header: "残業",
124
148
  align: "right",
125
149
  hiddenOnMobile: true,
150
+ width: "w-24",
126
151
  render: (row) => <Text tabular>{minutesFmt(row.overtime)}</Text>,
127
152
  },
128
153
  {
129
154
  key: "status",
130
155
  header: "状態",
131
- align: "center",
156
+ /* END, not CENTER (gh#849). The status column carries the table's right edge; centring a
157
+ * ~90px pill in it put the badges 102px inboard while the header count, the column headers
158
+ * and the pagination all landed on 17px. */
159
+ align: "right",
160
+ width: "w-32",
132
161
  render: (row) => <Badge status={row.status}>{STATUS_LABEL[row.status]}</Badge>,
133
162
  },
134
163
  ];
@@ -138,6 +167,8 @@ const columns: ColumnDef<Attendance>[] = [
138
167
  const NUMBERED_DATA = makeRows(83, "OFS");
139
168
 
140
169
  function NumberedPaginationCard() {
170
+ const { t } = useTranslation();
171
+ const fmt = useFormatters();
141
172
  const [page, setPage] = React.useState(2); // start mid-set so prev/next are both live
142
173
  const [pageSize, setPageSize] = React.useState(10);
143
174
 
@@ -150,7 +181,7 @@ function NumberedPaginationCard() {
150
181
  <CardTitle level={2}>番号付き + 件数選択</CardTitle>
151
182
  <CardAction>
152
183
  <Text size="xs" tone="muted" tabular>
153
- 全 {NUMBERED_DATA.length} 件
184
+ {t("showcase.pagination.recordCount", { count: NUMBERED_DATA.length })}
154
185
  </Text>
155
186
  </CardAction>
156
187
  </CardHeader>
@@ -164,7 +195,9 @@ function NumberedPaginationCard() {
164
195
  pageSize={pageSize}
165
196
  pageSizeOptions={[10, 20, 50]}
166
197
  showSizeChanger
167
- showTotal={(total, [from, to]) => `${from}–${to} / ${total} 件`}
198
+ showTotal={(total, [from, to]) =>
199
+ `${fmt.number(from)}–${fmt.number(to)} / ${t("showcase.pagination.recordCount", { count: total })}`
200
+ }
168
201
  onValueChange={(nextPage, nextSize) => {
169
202
  setPage(nextPage);
170
203
  setPageSize(nextSize);
@@ -181,6 +214,8 @@ const LOADMORE_DATA = makeRows(42, "LDM");
181
214
  const LOADMORE_STEP = 8;
182
215
 
183
216
  function LoadMoreCard() {
217
+ const { t } = useTranslation();
218
+ const fmt = useFormatters();
184
219
  // Mid-load state at rest: some rows already revealed, more remaining.
185
220
  const [visible, setVisible] = React.useState(LOADMORE_STEP * 2);
186
221
  const [loading, setLoading] = React.useState(false);
@@ -205,7 +240,8 @@ function LoadMoreCard() {
205
240
  <CardTitle level={2}>もっと読む</CardTitle>
206
241
  <CardAction>
207
242
  <Text size="xs" tone="muted" tabular>
208
- {rows.length} / {LOADMORE_DATA.length} 件表示
243
+ {fmt.number(rows.length)} /{" "}
244
+ {t("showcase.pagination.recordCount", { count: LOADMORE_DATA.length })}
209
245
  </Text>
210
246
  </CardAction>
211
247
  </CardHeader>
@@ -225,11 +261,13 @@ function LoadMoreCard() {
225
261
  {!done && <ChevronDown aria-hidden="true" />}
226
262
  {done
227
263
  ? "すべて表示しました"
228
- : `さらに ${Math.min(LOADMORE_STEP, remaining)} 件を読み込む`}
264
+ : t("showcase.pagination.loadMore", {
265
+ count: Math.min(LOADMORE_STEP, remaining),
266
+ })}
229
267
  </Button>
230
268
  {!done && (
231
269
  <Text size="xs" tone="muted" tabular>
232
- 残り {remaining} 件
270
+ {t("showcase.pagination.recordCount", { count: remaining })}
233
271
  </Text>
234
272
  )}
235
273
  </Flex>
@@ -242,11 +280,11 @@ function LoadMoreCard() {
242
280
 
243
281
  // Time-series 勤怠 logs grouped by month — offset paging is meaningless here,
244
282
  // so we use cursor first/next (DataTable.Pagination) + a period Select to jump.
245
- const PERIODS = [
246
- { value: "2026-06", label: "2026年 6月" },
247
- { value: "2026-05", label: "2026年 5月" },
248
- { value: "2026-04", label: "2026年 4月" },
249
- ] as const;
283
+ /* The VALUE is data; the LABEL is a formatting decision and belongs to the active locale.
284
+ * Hard-coded "2026年 6月" rendered beside the library's own "Đầu" / "Tiếp" — two languages in one
285
+ * row, which is what the owner photographed. `Intl.DateTimeFormat` gives 「2026年6月」, "June 2026"
286
+ * and "tháng 6 năm 2026" from the same value. */
287
+ const PERIODS = [{ value: "2026-06" }, { value: "2026-05" }, { value: "2026-04" }] as const;
250
288
 
251
289
  const PERIOD_DATA: Record<string, Attendance[]> = {
252
290
  "2026-06": makeRows(7, "CUR-06"),
@@ -255,6 +293,8 @@ const PERIOD_DATA: Record<string, Attendance[]> = {
255
293
  };
256
294
 
257
295
  function CursorPeriodCard() {
296
+ const { t } = useTranslation();
297
+ const fmt = useFormatters();
258
298
  const [period, setPeriod] = React.useState<string>("2026-06");
259
299
  // cursor === id of the last row of the previous page; undefined = first page.
260
300
  const [cursor, setCursor] = React.useState<string | undefined>(undefined);
@@ -284,7 +324,7 @@ function CursorPeriodCard() {
284
324
  <SelectContent>
285
325
  {PERIODS.map((p) => (
286
326
  <SelectItem key={p.value} value={p.value}>
287
- {p.label}
327
+ {fmt.month(p.value)}
288
328
  </SelectItem>
289
329
  ))}
290
330
  </SelectContent>
@@ -294,10 +334,30 @@ function CursorPeriodCard() {
294
334
  <CardContent flush>
295
335
  <DataTable data={rows} columns={columns} getRowId={(row) => row.id} density="compact" />
296
336
  </CardContent>
297
- <CardContent className="border-t">
337
+ {/* `DataTable.Pagination` owns its own insets ON BOTH AXES, because it is normally the lone
338
+ * child of a FLUSH container. Here it shares a padded `CardContent` with the period label,
339
+ * so both stack.
340
+ *
341
+ * The first pass at this zeroed only the INLINE knob and declared it done — a two-axis
342
+ * defect fixed on one axis, which looks fixed from the side you were measuring. The block
343
+ * one kept stacking: 16px from the CardContent plus 7.36px of the slot's own, so this
344
+ * footer sat 27px off the last row while the other two cards sat at 20px.
345
+ *
346
+ * Zeroing both hands the insets to the container that already supplies them; that is what
347
+ * these knobs are for, and an instance override is the right tier for one element in one
348
+ * arrangement (docs/CUSTOMER-THEMING.md, tier 4). */}
349
+ <CardContent
350
+ className="border-t"
351
+ style={
352
+ {
353
+ "--table-pagination-padding-x": "0",
354
+ "--table-pagination-padding-y": "0",
355
+ } as React.CSSProperties
356
+ }
357
+ >
298
358
  <Flex direction="row" align="center" justify="between" wrap gap="sm">
299
359
  <Text size="xs" tone="muted" tabular>
300
- {PERIODS.find((p) => p.value === period)?.label} · {all.length} 件
360
+ {fmt.month(period)} · {t("showcase.pagination.recordCount", { count: all.length })}
301
361
  </Text>
302
362
  <DataTable.Pagination
303
363
  cursor={lastVisibleId}
@@ -310,6 +370,27 @@ function CursorPeriodCard() {
310
370
  );
311
371
  }
312
372
 
373
+ /* COUNTS AND MONTHS FOLLOW THE ACTIVE LOCALE, and this page used to hard-code them.
374
+ *
375
+ * The library's own chrome is localized — the page-size Select renders "10 / trang" under `vi` —
376
+ * so a page that hard-codes `件` puts two languages in one row and they can never agree. That is
377
+ * exactly what the owner photographed: "11–20 / 83 件" beside "10 / trang".
378
+ *
379
+ * `Intl.NumberFormat` groups the number for the locale; the unit word comes from the library's own
380
+ * message catalogue, which already carries a CLDR plural map per language. Nothing here is a
381
+ * hand-built string. */
382
+ const useFormatters = () => {
383
+ const { locale } = useTranslation();
384
+ return React.useMemo(() => {
385
+ const number = new Intl.NumberFormat(locale);
386
+ const month = new Intl.DateTimeFormat(locale, { year: "numeric", month: "long" });
387
+ return {
388
+ number: (n: number) => number.format(n),
389
+ month: (iso: string) => month.format(new Date(`${iso}-01T00:00:00`)),
390
+ };
391
+ }, [locale]);
392
+ };
393
+
313
394
  export default function Demo() {
314
395
  return (
315
396
  <PageContainer
@@ -302,7 +302,13 @@ export default function ThemeCustomizationShowcase() {
302
302
  const [corner, setCorner] = React.useState("default");
303
303
  const [custom, setCustom] = React.useState("#0F62FE");
304
304
  const [regions, setRegions] = React.useState<string[]>(["JP", "VN"]);
305
- const [labels, setLabels] = React.useState<string[]>(["本番", "ổn định", UNBREAKABLE]);
305
+ /* UNBREAKABLE FIRST, ON PURPOSE (gh#840). With `maxTagCount={2}` the tag in third place is the
306
+ * one that collapses into `+N` — so while the 71-character id sat last, the chip that painted
307
+ * outside the control was never actually drawn on this page, and `maxTagTextLength` had nothing
308
+ * to demonstrate. Held first, the row now draws all three of the issue's answers at once: the
309
+ * long id truncated to its ceiling (full value in `title` and in the remover's name), a normal
310
+ * chip beside it, and a `+1` that names the tag behind it. */
311
+ const [labels, setLabels] = React.useState<string[]>([UNBREAKABLE, "本番", "ổn định"]);
306
312
  const [plan, setPlan] = React.useState("standard");
307
313
  const [channels, setChannels] = React.useState<string[]>(["email"]);
308
314
  const [cadence, setCadence] = React.useState("weekly");
@@ -565,7 +571,19 @@ function ComponentsBoard(props: {
565
571
  } = props;
566
572
 
567
573
  return (
568
- <ResponsiveGrid columns={{ base: 1, sm: 2, lg: 3 }} gap="lg" align="start">
574
+ /* PADDED HERE, because nothing above this pads it (gh#841 follow-up). The board sits in
575
+ `<CardContent flush>` so the tab STRIP can meet the card edge — which is right, a line
576
+ variant's underline should run edge to edge — and `bodied` is a no-op on `variant="line"`
577
+ by design (`tabs.tsx`: `const bodiedCard = card && Boolean(bodied)`). So the body has no
578
+ inline inset from either side, and the form sat 1px from the card border: measured label
579
+ at x=25 against a card at x=24. `pad` on the board is the composition answer — the same
580
+ step `CardContent` would have used. */
581
+ <ResponsiveGrid
582
+ columns={{ base: 1, sm: 2, lg: 3 }}
583
+ gap="lg"
584
+ align="start"
585
+ pad={{ inline: "lg", block: "md" }}
586
+ >
569
587
  {/* 1 · text entry */}
570
588
  <Flex direction="col" gap="md">
571
589
  <FormField
@@ -618,6 +636,11 @@ function ComponentsBoard(props: {
618
636
  value={labels}
619
637
  onValueChange={setLabels}
620
638
  maxTagCount={2}
639
+ /* The ceiling the 71-character id is measured against (gh#840). 16 is the widest cut
640
+ that still leaves room for a second chip and the `+1` on one row at 375px; the value
641
+ is untouched — it stays in the chip's `title` and in the remover's accessible name,
642
+ which is the only reason truncating an identifier is allowed at all. */
643
+ maxTagTextLength={16}
621
644
  placeholder={t("themeShowcase.form.labelsPlaceholder")}
622
645
  />
623
646
  </FormField>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@godxjp/ui",
3
- "version": "28.9.0",
4
- "godxUiMcp": "28.9.0",
3
+ "version": "28.12.0",
4
+ "godxUiMcp": "28.12.0",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",
@@ -23,6 +23,7 @@
23
23
  "description": "@godxjp/ui — shared React UI framework (shadcn + Radix + Tailwind v4).",
24
24
  "files": [
25
25
  "LICENSE",
26
+ "agent",
26
27
  "dist",
27
28
  "scripts/ui-audit.mjs",
28
29
  "README.md",
@@ -387,12 +388,12 @@
387
388
  "gen:frame-coverage-ledger": "node scripts/gen-frame-coverage-ledger.mjs",
388
389
  "gen:registry": "node scripts/gen-registry.mjs",
389
390
  "init-agent": "node scripts/init-agent-kit.mjs",
390
- "lint": "eslint .",
391
+ "lint": "eslint . --cache --cache-strategy content",
391
392
  "lint:fix": "eslint . --fix",
392
393
  "postinstall": "node scripts/postinstall.mjs",
393
394
  "prepublishOnly": "pnpm run build",
394
395
  "preview": "node preview/scripts/kill-port.mjs && vite --config preview/vite.config.ts --port 6008 --strictPort",
395
- "preview:build": "vite build --config preview/vite.config.ts && node scripts/gen-registry.mjs",
396
+ "preview:build": "vite build --config preview/vite.config.ts && node scripts/gen-registry.mjs && node scripts/copy-agent-catalog.mjs",
396
397
  "regen": "node scripts/regen-generated.mjs",
397
398
  "release": "node scripts/release.mjs",
398
399
  "ship:surface": "pnpm regen && pnpm verify:ci:static && pnpm check:frame-contracts",
@@ -413,13 +414,15 @@
413
414
  "verify": "pnpm typecheck && pnpm lint && pnpm format && pnpm build && pnpm preview:build && pnpm check:example-imports && pnpm check:core-isolation && pnpm check:no-consumer-coupling && pnpm check:prop-vocabulary && pnpm check:token-tiers && pnpm check:token-width-wins && pnpm check:no-external-assets && pnpm check:token-scale-bypass && pnpm check:radix-surface && pnpm check:dist-tokens-resolve && pnpm check:no-hardcoded-geometry && pnpm check:no-hardcoded-css-values && pnpm check:disclosure-duplication && pnpm check:no-inline-magic-numbers && pnpm check:no-tailwind-class-assertions && pnpm check:control-sizing && pnpm check:rtl && pnpm check:typography && pnpm check:mcp-lockstep && pnpm check:mcp-sync && pnpm check:doc-prop-existence && pnpm check:mcp-catalog-coverage && pnpm check:mcp-orphans && pnpm check:mcp-pattern-imports && pnpm check:audit-sync && pnpm check:frame-coverage && pnpm test",
414
415
  "verify:browser": "pnpm check:contrast && pnpm check:text-ink-clip && pnpm check:frame-overflow && pnpm check:font-fallback-metrics && pnpm check:visual-audit",
415
416
  "verify:ci": "pnpm verify:ci:static && pnpm check:frame-contracts && pnpm test",
416
- "verify:ci:static": "pnpm build && pnpm check:packed-public-contract && pnpm typecheck && pnpm typecheck:docs && pnpm lint && pnpm preview:build && pnpm check:registry && pnpm check:example-imports && pnpm check:core-isolation && pnpm check:no-consumer-coupling && pnpm check:use-client && pnpm check:prop-vocabulary && pnpm check:token-tiers && pnpm check:token-width-wins && pnpm check:no-external-assets && pnpm check:token-scale-bypass && pnpm check:no-antd-runtime && pnpm check:radix-surface && pnpm check:dist-tokens-resolve && pnpm check:no-hardcoded-geometry && pnpm check:no-hardcoded-css-values && pnpm check:disclosure-duplication && pnpm check:no-inline-magic-numbers && pnpm check:no-tailwind-class-assertions && pnpm check:control-sizing && pnpm check:rtl && pnpm check:typography && pnpm check:mcp-lockstep && pnpm check:measurement-contract && pnpm check:mcp-token-sync && pnpm check:email-token-sync && pnpm check:mcp-sync && pnpm check:doc-prop-existence && pnpm check:mcp-catalog-coverage && pnpm check:mcp-orphans && pnpm check:mcp-catalog-completeness && pnpm check:mcp-pattern-imports && pnpm check:audit-sync && pnpm run audit && pnpm check:frame-coverage-report && pnpm check:gate-coverage && pnpm check:mcp-prop-sync && pnpm check:catalog-contradictions && pnpm check:catalog-snippets && pnpm check:absorbed-names && pnpm check:agent-catalog",
417
+ "verify:ci:static:gates": "pnpm build && pnpm check:packed-public-contract && pnpm typecheck && pnpm typecheck:docs && pnpm lint && pnpm preview:build && pnpm check:registry && pnpm check:example-imports && pnpm check:core-isolation && pnpm check:no-consumer-coupling && pnpm check:use-client && pnpm check:prop-vocabulary && pnpm check:token-tiers && pnpm check:token-width-wins && pnpm check:no-external-assets && pnpm check:token-scale-bypass && pnpm check:no-antd-runtime && pnpm check:radix-surface && pnpm check:dist-tokens-resolve && pnpm check:no-hardcoded-geometry && pnpm check:no-hardcoded-css-values && pnpm check:disclosure-duplication && pnpm check:no-inline-magic-numbers && pnpm check:no-tailwind-class-assertions && pnpm check:control-sizing && pnpm check:rtl && pnpm check:typography && pnpm check:mcp-lockstep && pnpm check:measurement-contract && pnpm check:mcp-token-sync && pnpm check:email-token-sync && pnpm check:mcp-sync && pnpm check:doc-prop-existence && pnpm check:mcp-catalog-coverage && pnpm check:mcp-orphans && pnpm check:mcp-catalog-completeness && pnpm check:mcp-pattern-imports && pnpm check:audit-sync && pnpm run audit && pnpm check:frame-coverage-report && pnpm check:gate-coverage && pnpm check:mcp-prop-sync && pnpm check:catalog-contradictions && pnpm check:catalog-snippets && pnpm check:absorbed-names && pnpm check:agent-catalog",
418
+ "verify:ci:static": "node scripts/run-gate-list.mjs verify:ci:static:gates",
417
419
  "verify:publish-tree": "pnpm build && pnpm check:packed-public-contract && pnpm check:use-client && pnpm check:dist-tokens-resolve",
418
420
  "verify:release": "pnpm verify:static && pnpm check:frame-contracts && pnpm check:frame-coverage",
419
421
  "verify:static": "pnpm build && pnpm check:packed-public-contract && pnpm typecheck && pnpm typecheck:docs && pnpm lint && pnpm preview:build && pnpm check:example-imports && pnpm check:core-isolation && pnpm check:no-consumer-coupling && pnpm check:use-client && pnpm check:prop-vocabulary && pnpm check:token-tiers && pnpm check:token-scale-bypass && pnpm check:radix-surface && pnpm check:dist-tokens-resolve && pnpm check:no-hardcoded-geometry && pnpm check:no-hardcoded-css-values && pnpm check:disclosure-duplication && pnpm check:no-inline-magic-numbers && pnpm check:no-tailwind-class-assertions && pnpm check:control-sizing && pnpm check:rtl && pnpm check:typography && pnpm check:mcp-lockstep && pnpm check:mcp-sync && pnpm check:doc-prop-existence && pnpm check:mcp-catalog-coverage && pnpm check:mcp-orphans && pnpm check:mcp-pattern-imports && pnpm check:audit-sync && pnpm check:mcp-prop-sync && pnpm check:contrast && pnpm check:visual-audit && pnpm test",
420
422
  "visual-audit": "node scripts/visual-audit.mjs",
421
423
  "gen:brand": "node scripts/gen-brand.mjs",
422
424
  "check:absorbed-names": "node scripts/check-absorbed-names.mjs",
425
+ "check:control-glyph-tier": "node scripts/check-control-glyph-tier.mjs",
423
426
  "check:frame-overflow": "node scripts/check-frame-overflow.mjs",
424
427
  "check:frame-token-wins": "node scripts/check-frame-token-wins.mjs",
425
428
  "check:frame-token-scope": "node scripts/check-frame-token-scope.mjs"