@godxjp/ui 19.0.0 → 19.2.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 (445) hide show
  1. package/README.md +25 -128
  2. package/dist/app/app-provider.d.ts +1 -1
  3. package/dist/app/app-provider.js +3 -0
  4. package/dist/app/theme-axes.d.ts +4 -15
  5. package/dist/app/timezones.d.ts +1 -4
  6. package/dist/components/charts/area-chart.d.ts +2 -3
  7. package/dist/components/charts/bar-chart.d.ts +2 -3
  8. package/dist/components/charts/chart-frame.d.ts +7 -16
  9. package/dist/components/charts/chart-summary.d.ts +2 -2
  10. package/dist/components/charts/compact-bar-trend.d.ts +9 -24
  11. package/dist/components/charts/index.d.ts +1 -6
  12. package/dist/components/data-display/badge.d.ts +2 -12
  13. package/dist/components/data-display/card.d.ts +21 -41
  14. package/dist/components/data-display/carousel.d.ts +1 -2
  15. package/dist/components/data-display/code-block.d.ts +4 -0
  16. package/dist/components/data-display/code-block.js +24 -0
  17. package/dist/components/data-display/data-table.d.ts +15 -37
  18. package/dist/components/data-display/data-table.js +1 -3
  19. package/dist/components/data-display/descriptions.d.ts +2 -6
  20. package/dist/components/data-display/descriptions.js +0 -2
  21. package/dist/components/data-display/index.d.ts +4 -0
  22. package/dist/components/data-display/index.js +4 -0
  23. package/dist/components/data-display/list-row.d.ts +3 -22
  24. package/dist/components/data-display/permission-matrix.d.ts +7 -20
  25. package/dist/components/data-display/progress.d.ts +1 -1
  26. package/dist/components/data-display/prose.d.ts +4 -0
  27. package/dist/components/data-display/prose.js +20 -0
  28. package/dist/components/data-display/service-launcher-card.d.ts +3 -10
  29. package/dist/components/data-display/table.d.ts +20 -80
  30. package/dist/components/data-display/table.js +0 -3
  31. package/dist/components/data-display/timeline.d.ts +3 -5
  32. package/dist/components/data-entry/branch-scope-picker.d.ts +2 -19
  33. package/dist/components/data-entry/branch-scope-picker.js +0 -1
  34. package/dist/components/data-entry/calendar.d.ts +3 -2
  35. package/dist/components/data-entry/calendar.js +58 -3
  36. package/dist/components/data-entry/cascader.js +3 -1
  37. package/dist/components/data-entry/checkbox-group.js +12 -5
  38. package/dist/components/data-entry/checkbox.js +30 -21
  39. package/dist/components/data-entry/command-palette.d.ts +8 -22
  40. package/dist/components/data-entry/date-picker.d.ts +4 -5
  41. package/dist/components/data-entry/date-picker.js +12 -3
  42. package/dist/components/data-entry/date-range-picker.d.ts +1 -1
  43. package/dist/components/data-entry/date-range-picker.js +17 -6
  44. package/dist/components/data-entry/form-errors.d.ts +7 -18
  45. package/dist/components/data-entry/form-field.d.ts +1 -1
  46. package/dist/components/data-entry/form-field.js +15 -6
  47. package/dist/components/data-entry/form.d.ts +1 -9
  48. package/dist/components/data-entry/input.d.ts +16 -14
  49. package/dist/components/data-entry/input.js +13 -3
  50. package/dist/components/data-entry/month-picker.js +6 -4
  51. package/dist/components/data-entry/month-range-picker.d.ts +4 -6
  52. package/dist/components/data-entry/month-range-picker.js +11 -5
  53. package/dist/components/data-entry/number-input.d.ts +1 -5
  54. package/dist/components/data-entry/number-input.js +1 -0
  55. package/dist/components/data-entry/radio.js +19 -6
  56. package/dist/components/data-entry/search-select.d.ts +3 -5
  57. package/dist/components/data-entry/search-select.js +8 -2
  58. package/dist/components/data-entry/select.d.ts +15 -2
  59. package/dist/components/data-entry/select.js +57 -10
  60. package/dist/components/data-entry/switch.js +9 -2
  61. package/dist/components/data-entry/textarea.d.ts +10 -16
  62. package/dist/components/data-entry/textarea.js +8 -1
  63. package/dist/components/data-entry/time-picker.d.ts +3 -5
  64. package/dist/components/data-entry/tree-select.js +3 -1
  65. package/dist/components/data-entry/upload-types.d.ts +1 -5
  66. package/dist/components/data-entry/use-upload-draft.d.ts +0 -4
  67. package/dist/components/feedback/dialog.d.ts +6 -15
  68. package/dist/components/feedback/overlay-header-tone.d.ts +1 -1
  69. package/dist/components/feedback/sheet.d.ts +3 -10
  70. package/dist/components/feedback/sheet.js +1 -1
  71. package/dist/components/feedback/sonner.js +2 -3
  72. package/dist/components/general/activity.d.ts +4 -30
  73. package/dist/components/general/button.js +1 -10
  74. package/dist/components/general/logo.d.ts +6 -28
  75. package/dist/components/general/reveal.d.ts +3 -9
  76. package/dist/components/general/typography.d.ts +1 -3
  77. package/dist/components/layout/account-chip.d.ts +8 -0
  78. package/dist/components/layout/account-chip.js +45 -0
  79. package/dist/components/layout/auth-divider.d.ts +2 -9
  80. package/dist/components/layout/auth-shell.d.ts +1 -14
  81. package/dist/components/layout/breadcrumb.d.ts +1 -5
  82. package/dist/components/layout/centered-shell.d.ts +4 -18
  83. package/dist/components/layout/error-surface.d.ts +11 -28
  84. package/dist/components/layout/error-surface.js +4 -1
  85. package/dist/components/layout/index.d.ts +2 -0
  86. package/dist/components/layout/index.js +2 -0
  87. package/dist/components/layout/legal-document-shell.d.ts +5 -28
  88. package/dist/components/layout/master-detail.d.ts +2 -18
  89. package/dist/components/layout/page-container.js +0 -1
  90. package/dist/components/layout/responsive-grid.d.ts +6 -1
  91. package/dist/components/layout/responsive-grid.js +9 -2
  92. package/dist/components/layout/service-role-panel.d.ts +7 -19
  93. package/dist/components/layout/sidebar.d.ts +5 -27
  94. package/dist/components/layout/sidebar.js +0 -1
  95. package/dist/components/layout/split-pane.d.ts +0 -7
  96. package/dist/components/layout/topbar.d.ts +1 -14
  97. package/dist/components/navigation/app-setting-picker.js +1 -3
  98. package/dist/components/navigation/filter-bar.d.ts +1 -6
  99. package/dist/components/navigation/pagination-utils.d.ts +2 -12
  100. package/dist/components/navigation/tabs-scroll.d.ts +2 -36
  101. package/dist/components/navigation/tabs.js +4 -8
  102. package/dist/components/query/data-state.d.ts +2 -2
  103. package/dist/components/ui/credential-reveal.d.ts +1 -4
  104. package/dist/components/ui/hover-card.d.ts +1 -2
  105. package/dist/components/ui/separator.d.ts +2 -15
  106. package/dist/components/ui/toggle-group.d.ts +1 -5
  107. package/dist/components/ui/toggle.d.ts +6 -31
  108. package/dist/email/brand-mark.d.ts +6 -34
  109. package/dist/email/color.d.ts +7 -17
  110. package/dist/email/geometry.d.ts +4 -8
  111. package/dist/email/index.d.ts +4 -48
  112. package/dist/email/inline-style.d.ts +3 -9
  113. package/dist/email/urgency.d.ts +2 -7
  114. package/dist/form/form-context.d.ts +2 -5
  115. package/dist/form/form-root.d.ts +1 -2
  116. package/dist/i18n/messages/en.json +4 -0
  117. package/dist/i18n/messages/ja.json +4 -0
  118. package/dist/i18n/messages/vi.json +4 -0
  119. package/dist/inertia/index.d.ts +2 -10
  120. package/dist/lib/control-styles.d.ts +10 -21
  121. package/dist/lib/control-styles.js +4 -6
  122. package/dist/lib/datetime/format-date.d.ts +1 -4
  123. package/dist/lib/field-a11y.d.ts +31 -36
  124. package/dist/lib/field-a11y.js +16 -2
  125. package/dist/lib/format.d.ts +4 -8
  126. package/dist/lib/hooks.d.ts +6 -12
  127. package/dist/lib/permission-grid.d.ts +3 -18
  128. package/dist/lib/query-error.d.ts +3 -3
  129. package/dist/props/components/app.prop.d.ts +19 -27
  130. package/dist/props/components/charts.prop.d.ts +2 -3
  131. package/dist/props/components/data-display.prop.d.ts +48 -72
  132. package/dist/props/components/data-entry.prop.d.ts +41 -30
  133. package/dist/props/components/form.prop.d.ts +2 -6
  134. package/dist/props/components/general.prop.d.ts +11 -27
  135. package/dist/props/components/layout.prop.d.ts +89 -260
  136. package/dist/props/components/navigation.prop.d.ts +27 -42
  137. package/dist/props/registry.d.ts +31 -0
  138. package/dist/props/registry.js +33 -0
  139. package/dist/props/vocabulary/data.prop.d.ts +18 -49
  140. package/dist/props/vocabulary/index.d.ts +1 -1
  141. package/dist/props/vocabulary/interaction.prop.d.ts +10 -12
  142. package/dist/props/vocabulary/layout.prop.d.ts +8 -30
  143. package/dist/props/vocabulary/shared.prop.d.ts +3 -2
  144. package/dist/styles/alert-layout.css +1 -32
  145. package/dist/styles/badge-layout.css +4 -18
  146. package/dist/styles/base.css +14 -113
  147. package/dist/styles/card-layout.css +6 -128
  148. package/dist/styles/chart-layout.css +2 -17
  149. package/dist/styles/control.css +380 -458
  150. package/dist/styles/core.css +21 -0
  151. package/dist/styles/data-display-layout.css +175 -94
  152. package/dist/styles/data-entry-layout.css +0 -45
  153. package/dist/styles/density.css +1 -30
  154. package/dist/styles/dialog-layout.css +5 -69
  155. package/dist/styles/focus-ring.css +13 -87
  156. package/dist/styles/fonts.css +1 -31
  157. package/dist/styles/form-layout.css +7 -91
  158. package/dist/styles/index.css +2 -20
  159. package/dist/styles/layout.css +38 -456
  160. package/dist/styles/logo-layout.css +1 -35
  161. package/dist/styles/motion.css +3 -42
  162. package/dist/styles/navigation-layout.css +3 -50
  163. package/dist/styles/shell-layout.css +37 -403
  164. package/dist/styles/table-layout.css +16 -274
  165. package/dist/styles/text-layout.css +7 -21
  166. package/dist/styles/toggle.css +1 -23
  167. package/dist/theme/dxs.canonical.css +1 -55
  168. package/dist/theme/example.service.css +1 -26
  169. package/dist/theme/famgia.service.css +5 -15
  170. package/dist/tokens/axes.css +3 -40
  171. package/dist/tokens/base.css +1 -4
  172. package/dist/tokens/components/activity.css +6 -42
  173. package/dist/tokens/components/badge.css +4 -13
  174. package/dist/tokens/components/banner.css +4 -9
  175. package/dist/tokens/components/card.css +32 -88
  176. package/dist/tokens/components/chart.css +9 -26
  177. package/dist/tokens/components/control.css +37 -222
  178. package/dist/tokens/components/data-display.css +28 -79
  179. package/dist/tokens/components/data-entry.css +5 -19
  180. package/dist/tokens/components/descriptions.css +7 -27
  181. package/dist/tokens/components/email.css +38 -84
  182. package/dist/tokens/components/error-surface.css +2 -18
  183. package/dist/tokens/components/feedback.css +27 -124
  184. package/dist/tokens/components/form.css +13 -45
  185. package/dist/tokens/components/legal-document.css +16 -41
  186. package/dist/tokens/components/list-row.css +11 -36
  187. package/dist/tokens/components/logo.css +2 -57
  188. package/dist/tokens/components/navigation.css +16 -94
  189. package/dist/tokens/components/scroll-area.css +2 -18
  190. package/dist/tokens/components/separator.css +8 -30
  191. package/dist/tokens/components/sheet.css +3 -14
  192. package/dist/tokens/components/shell.css +53 -307
  193. package/dist/tokens/components/sidebar.css +5 -22
  194. package/dist/tokens/components/table.css +19 -140
  195. package/dist/tokens/components/toggle.css +3 -65
  196. package/dist/tokens/components/upload.css +7 -43
  197. package/dist/tokens/foundation.css +108 -383
  198. package/dist/tokens/semantic/layout.css +33 -207
  199. package/docs/CANONICAL-CONTRACTS.md +206 -0
  200. package/docs/COMPONENTS.md +157 -0
  201. package/docs/COMPOSITION-VS-COMPONENT.md +105 -0
  202. package/docs/CONSUMER-RULES.md +14 -0
  203. package/docs/CUSTOMER-THEMING.md +162 -0
  204. package/docs/DATETIME.md +69 -0
  205. package/docs/DEVELOPMENT.md +173 -0
  206. package/docs/FORMS.md +143 -0
  207. package/docs/FRAME-A11Y-CI.md +267 -0
  208. package/docs/FRAME-COVERAGE-LEDGER.md +123 -0
  209. package/docs/FRAME-COVERAGE-REPORT.md +140 -0
  210. package/docs/FRAME-COVERAGE-STANDARD.md +65 -0
  211. package/docs/FRAME-COVERAGE.md +29 -0
  212. package/docs/PROPS-REGISTRY.md +61 -0
  213. package/docs/PROPS-VOCABULARY.md +100 -0
  214. package/docs/README.md +60 -0
  215. package/docs/REGISTRY.md +70 -0
  216. package/docs/SPACING.md +56 -0
  217. package/docs/STANDARDS-vocabulary-tokens.md +48 -0
  218. package/docs/TESTING.md +98 -0
  219. package/docs/TOKENS.md +347 -0
  220. package/docs/asset-modules.d.ts +46 -0
  221. package/docs/assets/cover-terrain.svg +6 -0
  222. package/docs/assets/portrait-amber.svg +5 -0
  223. package/docs/assets/portrait-clay.svg +5 -0
  224. package/docs/assets/portrait-indigo.svg +5 -0
  225. package/docs/assets/portrait-iris.svg +5 -0
  226. package/docs/assets/portrait-moss.svg +5 -0
  227. package/docs/assets/portrait-plum.svg +5 -0
  228. package/docs/assets/portrait-slate.svg +5 -0
  229. package/docs/charts/compact-bar-trend.tsx +328 -0
  230. package/docs/charts/size-tiers.tsx +192 -0
  231. package/docs/data-display/accordion.tsx +99 -0
  232. package/docs/data-display/avatar.tsx +355 -0
  233. package/docs/data-display/badge.tsx +247 -0
  234. package/docs/data-display/card/examples/detail-panel.tsx +106 -0
  235. package/docs/data-display/card/index.md +3 -0
  236. package/docs/data-display/card/index.tsx +285 -0
  237. package/docs/data-display/carousel.tsx +156 -0
  238. package/docs/data-display/charts.tsx +198 -0
  239. package/docs/data-display/code-block.tsx +90 -0
  240. package/docs/data-display/collapsible.tsx +162 -0
  241. package/docs/data-display/credential-reveal.tsx +145 -0
  242. package/docs/data-display/data-table/examples/approval-queue.tsx +212 -0
  243. package/docs/data-display/data-table/examples/grid-features.tsx +158 -0
  244. package/docs/data-display/data-table/examples/list-page.tsx +175 -0
  245. package/docs/data-display/data-table/index.md +5 -0
  246. package/docs/data-display/data-table/index.tsx +295 -0
  247. package/docs/data-display/descriptions.tsx +143 -0
  248. package/docs/data-display/empty-state.tsx +164 -0
  249. package/docs/data-display/hover-card.tsx +181 -0
  250. package/docs/data-display/list-row.tsx +420 -0
  251. package/docs/data-display/permission-matrix.tsx +184 -0
  252. package/docs/data-display/popover-touch.tsx +27 -0
  253. package/docs/data-display/popover.tsx +245 -0
  254. package/docs/data-display/progress.tsx +84 -0
  255. package/docs/data-display/prose.tsx +105 -0
  256. package/docs/data-display/qr-code.tsx +68 -0
  257. package/docs/data-display/scroll-area.tsx +271 -0
  258. package/docs/data-display/service-launcher-card.tsx +145 -0
  259. package/docs/data-display/stat-card.tsx +99 -0
  260. package/docs/data-display/table.tsx +251 -0
  261. package/docs/data-display/timeline.tsx +112 -0
  262. package/docs/data-display/touch-actions.tsx +36 -0
  263. package/docs/data-display/tree-list.tsx +107 -0
  264. package/docs/data-entry/branch-scope-picker.tsx +123 -0
  265. package/docs/data-entry/calendar.tsx +180 -0
  266. package/docs/data-entry/cascader.tsx +378 -0
  267. package/docs/data-entry/checkbox.tsx +256 -0
  268. package/docs/data-entry/color-picker.tsx +117 -0
  269. package/docs/data-entry/command-palette.tsx +179 -0
  270. package/docs/data-entry/command.tsx +262 -0
  271. package/docs/data-entry/country-picker-recipe.tsx +170 -0
  272. package/docs/data-entry/date-picker.tsx +191 -0
  273. package/docs/data-entry/date-range-picker.tsx +149 -0
  274. package/docs/data-entry/form/examples/employee-settings.tsx +267 -0
  275. package/docs/data-entry/form/examples/invoice-form.tsx +387 -0
  276. package/docs/data-entry/form-field/examples/a11y-contract.tsx +240 -0
  277. package/docs/data-entry/form-field/examples/create-form.tsx +105 -0
  278. package/docs/data-entry/form-field/index.md +8 -0
  279. package/docs/data-entry/form-field/index.tsx +326 -0
  280. package/docs/data-entry/form-inertia.tsx +80 -0
  281. package/docs/data-entry/form-root.tsx +102 -0
  282. package/docs/data-entry/form.tsx +724 -0
  283. package/docs/data-entry/input-otp.tsx +231 -0
  284. package/docs/data-entry/input.tsx +114 -0
  285. package/docs/data-entry/label.tsx +100 -0
  286. package/docs/data-entry/month-picker.tsx +101 -0
  287. package/docs/data-entry/month-range-picker.tsx +115 -0
  288. package/docs/data-entry/number-input.tsx +240 -0
  289. package/docs/data-entry/password-input.tsx +106 -0
  290. package/docs/data-entry/password-strength.tsx +97 -0
  291. package/docs/data-entry/radio-group.tsx +95 -0
  292. package/docs/data-entry/rating.tsx +87 -0
  293. package/docs/data-entry/search-input.tsx +120 -0
  294. package/docs/data-entry/select-async-contracts.tsx +108 -0
  295. package/docs/data-entry/select-matrix.tsx +204 -0
  296. package/docs/data-entry/select.tsx +369 -0
  297. package/docs/data-entry/slider.tsx +130 -0
  298. package/docs/data-entry/switch.tsx +67 -0
  299. package/docs/data-entry/tag-input.tsx +108 -0
  300. package/docs/data-entry/textarea.tsx +209 -0
  301. package/docs/data-entry/time-picker.tsx +99 -0
  302. package/docs/data-entry/toggle-count.tsx +293 -0
  303. package/docs/data-entry/toggle-group.tsx +225 -0
  304. package/docs/data-entry/toggle.tsx +127 -0
  305. package/docs/data-entry/transfer.tsx +97 -0
  306. package/docs/data-entry/tree-select.tsx +204 -0
  307. package/docs/data-entry/upload.tsx +184 -0
  308. package/docs/feedback/alert-dialog.tsx +270 -0
  309. package/docs/feedback/alert.tsx +248 -0
  310. package/docs/feedback/banner.tsx +183 -0
  311. package/docs/feedback/danger-confirm.tsx +88 -0
  312. package/docs/feedback/dialog-touch.tsx +24 -0
  313. package/docs/feedback/dialog.tsx +193 -0
  314. package/docs/feedback/sheet.tsx +410 -0
  315. package/docs/feedback/skeleton.tsx +111 -0
  316. package/docs/feedback/toast.tsx +135 -0
  317. package/docs/feedback/tooltip.tsx +171 -0
  318. package/docs/feedback/two-factor-setup.tsx +68 -0
  319. package/docs/foundation/_email-specimen.ts +274 -0
  320. package/docs/foundation/colors.tsx +202 -0
  321. package/docs/foundation/density.tsx +232 -0
  322. package/docs/foundation/email-tokens.tsx +306 -0
  323. package/docs/foundation/radius-shadow.tsx +152 -0
  324. package/docs/foundation/spacing.tsx +211 -0
  325. package/docs/foundation/typography.tsx +147 -0
  326. package/docs/general/activity.tsx +224 -0
  327. package/docs/general/button/examples/destructive-confirm.tsx +90 -0
  328. package/docs/general/button/examples/form-actions.tsx +85 -0
  329. package/docs/general/button/examples/toolbar.tsx +160 -0
  330. package/docs/general/button/index.md +12 -0
  331. package/docs/general/button/index.tsx +211 -0
  332. package/docs/general/logo.tsx +131 -0
  333. package/docs/general/reveal.tsx +41 -0
  334. package/docs/general/typography.tsx +152 -0
  335. package/docs/layout/account-chip.tsx +72 -0
  336. package/docs/layout/admin-collection.tsx +122 -0
  337. package/docs/layout/app-shell.tsx +374 -0
  338. package/docs/layout/aspect-ratio.tsx +119 -0
  339. package/docs/layout/auth-account-summary.tsx +103 -0
  340. package/docs/layout/auth-divider.tsx +28 -0
  341. package/docs/layout/auth-footer.tsx +37 -0
  342. package/docs/layout/auth-identity.tsx +36 -0
  343. package/docs/layout/auth-recovery/examples/mfa-challenge.md +41 -0
  344. package/docs/layout/auth-recovery/examples/mfa-challenge.tsx +254 -0
  345. package/docs/layout/auth-recovery/examples/mobile-390.md +25 -0
  346. package/docs/layout/auth-recovery/examples/mobile-390.tsx +82 -0
  347. package/docs/layout/auth-recovery/examples/password-recovery.md +24 -0
  348. package/docs/layout/auth-recovery/examples/password-recovery.tsx +197 -0
  349. package/docs/layout/auth-recovery/examples/wrapping-ja-en-vi.md +27 -0
  350. package/docs/layout/auth-recovery/examples/wrapping-ja-en-vi.tsx +156 -0
  351. package/docs/layout/auth-recovery/index.md +140 -0
  352. package/docs/layout/auth-recovery/index.tsx +96 -0
  353. package/docs/layout/auth-shell-context.tsx +103 -0
  354. package/docs/layout/auth-shell-device.tsx +112 -0
  355. package/docs/layout/auth-shell-registration.tsx +335 -0
  356. package/docs/layout/auth-shell-variants.tsx +139 -0
  357. package/docs/layout/auth-shell.tsx +59 -0
  358. package/docs/layout/auth-stack.tsx +40 -0
  359. package/docs/layout/centered-shell.tsx +102 -0
  360. package/docs/layout/error-surface/examples/application-403.tsx +84 -0
  361. package/docs/layout/error-surface/examples/application-404.tsx +77 -0
  362. package/docs/layout/error-surface/examples/system-500.tsx +47 -0
  363. package/docs/layout/error-surface/examples/system-503.tsx +48 -0
  364. package/docs/layout/error-surface/index.md +153 -0
  365. package/docs/layout/error-surface/index.tsx +239 -0
  366. package/docs/layout/flex.tsx +262 -0
  367. package/docs/layout/legal-document-shell/_data.ts +110 -0
  368. package/docs/layout/legal-document-shell/examples/desktop-1440.md +12 -0
  369. package/docs/layout/legal-document-shell/examples/desktop-1440.tsx +83 -0
  370. package/docs/layout/legal-document-shell/examples/mobile-390.md +22 -0
  371. package/docs/layout/legal-document-shell/examples/mobile-390.tsx +70 -0
  372. package/docs/layout/legal-document-shell/examples/tablet-1024.md +3 -0
  373. package/docs/layout/legal-document-shell/examples/tablet-1024.tsx +84 -0
  374. package/docs/layout/legal-document-shell/examples/wrapping-ja-en-vi.md +18 -0
  375. package/docs/layout/legal-document-shell/examples/wrapping-ja-en-vi.tsx +61 -0
  376. package/docs/layout/legal-document-shell/index.md +51 -0
  377. package/docs/layout/legal-document-shell/index.tsx +101 -0
  378. package/docs/layout/master-detail.tsx +387 -0
  379. package/docs/layout/org-switcher.tsx +131 -0
  380. package/docs/layout/page-container.tsx +946 -0
  381. package/docs/layout/resizable-panel.tsx +419 -0
  382. package/docs/layout/responsive-grid.tsx +235 -0
  383. package/docs/layout/separator.tsx +327 -0
  384. package/docs/layout/service-role-panel.tsx +144 -0
  385. package/docs/layout/sidebar.tsx +586 -0
  386. package/docs/layout/split-pane.tsx +451 -0
  387. package/docs/layout/topbar.tsx +347 -0
  388. package/docs/navigation/app-setting-picker.tsx +141 -0
  389. package/docs/navigation/breadcrumb.tsx +128 -0
  390. package/docs/navigation/context-menu.tsx +117 -0
  391. package/docs/navigation/dropdown-menu.tsx +213 -0
  392. package/docs/navigation/filter-bar.tsx +227 -0
  393. package/docs/navigation/menubar.tsx +141 -0
  394. package/docs/navigation/navigation-menu.tsx +165 -0
  395. package/docs/navigation/pagination.tsx +241 -0
  396. package/docs/navigation/steps.tsx +263 -0
  397. package/docs/navigation/tabs-rtl.tsx +28 -0
  398. package/docs/navigation/tabs.tsx +308 -0
  399. package/docs/navigation/toolbar.tsx +339 -0
  400. package/docs/providers/app-provider.tsx +191 -0
  401. package/docs/providers/format-date.tsx +230 -0
  402. package/docs/query/button-refetch.tsx +160 -0
  403. package/docs/query/data-state.tsx +192 -0
  404. package/docs/query/infinite-query-state.tsx +93 -0
  405. package/docs/query/mutation-feedback.tsx +70 -0
  406. package/docs/query/prefetch-link.tsx +262 -0
  407. package/docs/query/touch-actions.tsx +68 -0
  408. package/docs/showcase/acme-portal.tsx +441 -0
  409. package/docs/showcase/acme-website.tsx +455 -0
  410. package/docs/showcase/case1-warehouse-dashboard.tsx +343 -0
  411. package/docs/showcase/case2-employee-me.tsx +441 -0
  412. package/docs/showcase/case3-approval-workflow.tsx +527 -0
  413. package/docs/showcase/case4-login.tsx +307 -0
  414. package/docs/showcase/case5-shift-calendar.tsx +795 -0
  415. package/docs/showcase/case6-agency-handy.tsx +1072 -0
  416. package/docs/showcase/futurelastic-web.tsx +390 -0
  417. package/docs/showcase/org-switcher.tsx +313 -0
  418. package/docs/showcase/permission-matrix.tsx +343 -0
  419. package/docs/showcase/public-landing.tsx +210 -0
  420. package/docs/showcase/settings-account-sections.tsx +559 -0
  421. package/docs/showcase/settings-security-mfa.tsx +145 -0
  422. package/docs/showcase/table-approval-queue.tsx +199 -0
  423. package/docs/showcase/table-bulk-actions.tsx +460 -0
  424. package/docs/showcase/table-compact-kintone.tsx +438 -0
  425. package/docs/showcase/table-conditional-format.tsx +367 -0
  426. package/docs/showcase/table-crud-list.tsx +544 -0
  427. package/docs/showcase/table-density.tsx +340 -0
  428. package/docs/showcase/table-expandable-rows.tsx +471 -0
  429. package/docs/showcase/table-filter-chips.tsx +461 -0
  430. package/docs/showcase/table-footer-totals.tsx +311 -0
  431. package/docs/showcase/table-grouped-subtotals.tsx +388 -0
  432. package/docs/showcase/table-master-detail.tsx +479 -0
  433. package/docs/showcase/table-pagination.tsx +320 -0
  434. package/docs/showcase/table-states.tsx +363 -0
  435. package/docs/showcase/table-sticky-columns.tsx +373 -0
  436. package/docs/showcase/table-tree-rows.tsx +444 -0
  437. package/docs/showcase/table-view-tabs.tsx +488 -0
  438. package/package.json +10 -11
  439. package/scripts/_agent-setup.mjs +16 -2
  440. package/scripts/audit-hook.mjs +3 -13
  441. package/scripts/init-agent-kit.mjs +1 -14
  442. package/scripts/postinstall.mjs +12 -12
  443. package/scripts/ui-audit.mjs +93 -31
  444. package/scripts/visual-audit-rules.mjs +51 -7
  445. package/scripts/visual-audit.mjs +118 -47
@@ -1,15 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * postinstall runs when a consumer installs @godxjp/ui. Auto-registers the godx-ui
4
- * MCP server in the consumer's `.mcp.json` so the agent gets live access to the
5
- * component catalog + audit rules WITHOUT any manual step. Non-destructive (only adds
6
- * a missing server entry) and guarded so it never runs in CI or in the library's own repo.
7
- *
8
- * The heavier forcing-kit (the auto-audit PostToolUse hook + the workflow mandate) is
9
- * left to an EXPLICIT `npx @godxjp/ui init-agent`, since hooks change the dev loop and
10
- * should be opted into, not forced on install. Set GODXJP_UI_SKIP_SETUP=1 to disable.
3
+ * Auto-registers the godx-ui MCP server in the consumer's `.mcp.json` so the agent gets live
4
+ * access to the component catalog + audit rules WITHOUT any manual step. Non-destructive (only
5
+ * adds a missing server entry) and guarded so it never runs in CI or in the library's own repo.
11
6
  */
12
- import { ensureMcpJson, shouldSkip } from "./_agent-setup.mjs";
7
+ import { ensureClaudeMd, ensureMcpJson, shouldSkip, writeWorkflowMd } from "./_agent-setup.mjs";
13
8
 
14
9
  const root = process.env.INIT_CWD || process.cwd();
15
10
 
@@ -18,11 +13,16 @@ if (skip) process.exit(0); // silent: CI / opt-out / self-install / no consumer
18
13
 
19
14
  try {
20
15
  const r = ensureMcpJson(root);
21
- if (r === "present") process.exit(0); // already configured stay quiet
16
+ // The mandate is plain text the agent reads every turn (CLAUDE.md block + workflow file). It
17
+ // changes nothing in the dev loop, so it is installed by default: an agent that never saw the
18
+ // but no mandate). Only the hooks — which DO change the loop — stay behind `init-agent`.
19
+ const md = ensureClaudeMd(root);
20
+ const wf = writeWorkflowMd(root);
21
+ if (r === "present" && md === "present" && !wf) process.exit(0); // already configured — stay quiet
22
22
  console.log(
23
- `\n @godxjp/ui → registered the godx-ui MCP in .mcp.json (${r}).\n` +
23
+ `\n @godxjp/ui → MCP in .mcp.json (${r}); workflow mandate in CLAUDE.md (${md}).\n` +
24
24
  " Your agent now has live component + audit guidance. Restart it to pick up the MCP.\n" +
25
- " For the full forcing-kit (auto-audit on every edit + workflow mandate):\n" +
25
+ " For auto-audit on every edit (PostToolUse + SessionStart hooks):\n" +
26
26
  " npx @godxjp/ui init-agent\n",
27
27
  );
28
28
  } catch {
@@ -1,20 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * godxjp-ui audit — the design system's own linter for consumer UI code.
4
- *
5
- * Enforces the UI-standardization rules that ship with @godxjp/ui
6
- * (see the app's .claude/skills/frontend-design/rules/ui-standardization.md):
7
- * use the design system, never hand-roll. Inspired by `@google/design.md lint`
8
- * — structured, agent-readable findings.
9
- *
10
- * Runs from the CONSUMER's repo root and scans its hand-written UI
11
- * (components / pages / layouts) — never the framework or generated code.
12
- *
13
- * Usage (from the consuming app):
14
- * node packages/godx-ui/scripts/ui-audit.mjs # human report; exit 1 on error
15
- * node packages/godx-ui/scripts/ui-audit.mjs --format json
16
- * node packages/godx-ui/scripts/ui-audit.mjs --quiet # errors only
17
- * node packages/godx-ui/scripts/ui-audit.mjs src/ui lib/ # custom scan dirs
3
+ * godxjp-ui audit — the design system's own linter for consumer UI code. Enforces the
4
+ * UI-standardization rules that ship with @godxjp/ui (see the app's
5
+ * .claude/skills/frontend-design/rules/ui-standardization.md): use the design system, never
6
+ * hand-roll.
18
7
  */
19
8
  import { readdirSync, readFileSync, statSync } from "node:fs";
20
9
  import { isAbsolute, join, relative } from "node:path";
@@ -54,6 +43,37 @@ const EMOJI_FLAG = /\p{Regional_Indicator}/u;
54
43
  * CSS Logical Properties L1 (W3C) · HTML Living Standard (WHATWG).
55
44
  */
56
45
  const RULES = [
46
+ {
47
+ id: "no-utility-spacing",
48
+ scope: "consumer",
49
+ severity: "error",
50
+ // Any Tailwind spacing step on the consumer's own markup. Arbitrary values ([13px]) have their
51
+ // own rule; this is README rule 3, documented for a long time and never enforced: rhythm
52
+ // comes from Flex gap / ResponsiveGrid / PageContainer, never from gap-*/p-*/m-* utilities.
53
+ test: /\b(?:gap|gap-x|gap-y|p|px|py|pt|pb|ps|pe|pl|pr|m|mx|my|mt|mb|ms|me|ml|mr)-(?:\d+(?:\.\d+)?|px)\b/,
54
+ message:
55
+ "No Tailwind spacing utilities (gap-*/p-*/m-*) for layout — space siblings with <Flex gap> / <ResponsiveGrid>; sections of a page are spaced by <PageContainer> (docs/CONSUMER-RULES.md §3).",
56
+ },
57
+ {
58
+ id: "no-utility-layout",
59
+ scope: "consumer",
60
+ severity: "error",
61
+ // A hand-rolled flex/grid container. The primitives exist precisely so a row is <Flex>, a
62
+ // stack is <Flex direction="col">, a grid is <ResponsiveGrid> — token gaps, RTL, density.
63
+ test: /className=(?:"[^"]*|'[^']*|\{`[^`]*)(?<![\w-])(?:flex|inline-flex|grid|inline-grid)(?![\w-])/,
64
+ message:
65
+ 'No hand-rolled flex/grid — use <Flex> (row), <Flex direction="col"> (stack) or <ResponsiveGrid> (docs/CONSUMER-RULES.md §3).',
66
+ },
67
+ {
68
+ id: "no-hand-rolled-surface",
69
+ scope: "consumer",
70
+ severity: "warn",
71
+ // rounded + border/bg on the consumer's own element = a fake Card / Badge / Avatar / ListRow
72
+ // that drifts from the real ones (a 38px account pill beside 32px controls).
73
+ test: /className=(?:"[^"]*|'[^']*|\{`[^`]*)\brounded(?:-(?:full|sm|md|lg|xl|2xl))?\b[^"'`]*\b(?:border|bg-)/,
74
+ message:
75
+ "Hand-rolled surface (rounded + border/bg) — use Card, Badge, Avatar, ListRow, Descriptions or EmptyState so height, padding and radius come from the tokens (docs/CONSUMER-RULES.md §4).",
76
+ },
57
77
  {
58
78
  id: "no-space-xy",
59
79
  severity: "error",
@@ -138,7 +158,6 @@ const RULES = [
138
158
  // Tailwind class; this catches one in a design-system KNOB the app sets itself
139
159
  // (`style={{ "--card-space-inset": "13px" }}`), which is the sanctioned per-instance override
140
160
  // route and therefore the one place an app can silently leave the scale. Framework-side the same
141
- // check runs over `src/tokens/**` as `pnpm check:token-scale-bypass` (gh#332).
142
161
  //
143
162
  // Only axes that HAVE named steps are flagged — space, font-size, radius, icon-size. width /
144
163
  // height / size / offset have no scale yet, so a number there is the only thing to write and is
@@ -333,26 +352,30 @@ const RULES = [
333
352
  * so the executable rules and the agent-facing docs never drift.
334
353
  */
335
354
  if (args.includes("--rules")) {
336
- process.stdout.write(
337
- JSON.stringify(
338
- RULES.map((r) => ({
339
- id: r.id,
340
- severity: r.severity,
341
- standard: r.standard ?? null,
342
- message: r.message,
343
- })),
344
- null,
345
- 2,
346
- ) + "\n",
355
+ await new Promise((resolve) =>
356
+ process.stdout.write(
357
+ JSON.stringify(
358
+ RULES.map((r) => ({
359
+ id: r.id,
360
+ severity: r.severity,
361
+ standard: r.standard ?? null,
362
+ message: r.message,
363
+ })),
364
+ null,
365
+ 2,
366
+ ) + "\n",
367
+ resolve,
368
+ ),
347
369
  );
370
+ // Exit only once the pipe has drained (a bare process.exit() cut a 367 KB report at 64 KB),
371
+ // and never fall through to the scan, whose summary line would corrupt the JSON.
348
372
  process.exit(0);
349
373
  }
350
374
 
351
375
  /**
352
376
  * Blank out `//` line comments and block comments (incl. JSDoc) — replacing them with spaces and
353
377
  * preserving newlines so line numbers stay accurate — while KEEPING string/template literals (the
354
- * className values we actually want to scan). Prevents false positives like a doc-comment that
355
- * literally says "Never a raw <input>".
378
+ * className values we actually want to scan).
356
379
  */
357
380
  function stripComments(src) {
358
381
  let out = "";
@@ -464,6 +487,25 @@ const CARD_FLUSH =
464
487
  const BARE_FIELD =
465
488
  /<(?:label|Label)\b[^>]*>[\s\S]{0,240}?<\/(?:label|Label)>\s*<(?:Input|Select|Textarea|NumberInput|SearchInput|SearchSelect|DatePicker|DateRangePicker|TimePicker|MonthPicker|MonthRangePicker|Cascader|TreeSelect|input)\b/g;
466
489
 
490
+ // Two <Card> siblings written back to back at the same indentation with nothing between them.
491
+ // Direct children of PageContainer are spaced by the page; anywhere else they touch.
492
+ const SIBLING_CARDS = /^([ \t]*)<\/Card>\s*\n\1<Card\b/gm;
493
+
494
+ /**
495
+ * `scope: "consumer"` rules ban utilities on a CONSUMER's own markup. Inside the library repo the
496
+ * utilities are the design system itself (that is where `flex`/`gap-*` are supposed to live), so
497
+ * those rules are skipped when the CWD package is @godxjp/ui — unless `--consumer` forces them.
498
+ */
499
+ const SELF = (() => {
500
+ try {
501
+ return JSON.parse(readFileSync(join(CWD, "package.json"), "utf8")).name === "@godxjp/ui";
502
+ } catch {
503
+ return false;
504
+ }
505
+ })();
506
+ const ACTIVE_RULES =
507
+ SELF && !args.includes("--consumer") ? RULES.filter((r) => r.scope !== "consumer") : RULES;
508
+
467
509
  const findings = [];
468
510
  for (const dir of SCAN_DIRS) {
469
511
  for (const file of walk(isAbsolute(dir) ? dir : join(CWD, dir))) {
@@ -473,7 +515,7 @@ for (const dir of SCAN_DIRS) {
473
515
  const scanContent = stripComments(content); // comments blanked; strings + line numbers kept
474
516
  const scanLines = scanContent.split("\n");
475
517
  scanLines.forEach((line, i) => {
476
- for (const rule of RULES) {
518
+ for (const rule of ACTIVE_RULES) {
477
519
  if (
478
520
  rule.test.test(line) &&
479
521
  !isSuppressed(rule.id, origLines[i], origLines[i - 1]) &&
@@ -505,6 +547,25 @@ for (const dir of SCAN_DIRS) {
505
547
  snippet: match[0].replace(/\s+/g, " ").slice(0, 120),
506
548
  });
507
549
  }
550
+ if (
551
+ !(SELF && !args.includes("--consumer")) &&
552
+ !/<(?:Flex|ResponsiveGrid|PageContainer)\b/.test(scanContent)
553
+ ) {
554
+ for (const match of scanContent.matchAll(SIBLING_CARDS)) {
555
+ const lineNo = scanContent.slice(0, match.index).split("\n").length + 1;
556
+ if (isSuppressed("sibling-cards-need-flex", origLines[lineNo - 1], origLines[lineNo - 2]))
557
+ continue;
558
+ findings.push({
559
+ file: rel,
560
+ line: lineNo,
561
+ rule: "sibling-cards-need-flex",
562
+ severity: "warn",
563
+ message:
564
+ 'Adjacent <Card> siblings with no layout owner — wrap them in <Flex direction="col" gap="lg"> or <ResponsiveGrid> (inside PageContainer the page spaces them for you).',
565
+ snippet: match[0].replace(/\s+/g, " ").slice(0, 120),
566
+ });
567
+ }
568
+ }
508
569
  for (const match of scanContent.matchAll(BARE_FIELD)) {
509
570
  const lineNo = scanContent.slice(0, match.index).split("\n").length;
510
571
  if (
@@ -560,4 +621,5 @@ if (asJson) {
560
621
  }
561
622
  }
562
623
 
563
- process.exit(errors.length > 0 ? 1 : 0);
624
+ // See the note above --rules: exitCode, so a large JSON report drains fully.
625
+ process.exitCode = errors.length > 0 ? 1 : 0;
@@ -1,12 +1,8 @@
1
1
  /**
2
2
  * Visual-audit decision logic — PURE functions, zero browser/DOM dependency.
3
- *
4
- * `scripts/visual-audit.mjs` collects measurements from a REAL rendered page
5
- * (Playwright) and feeds them here; the browser glue stays thin and these rules
6
- * are fully unit-testable without a browser. The same functions are injected into
7
- * the page for in-browser heuristics.
8
- *
9
- * Each rule cites the international standard it enforces — see VISUAL_RULES.
3
+ * `scripts/visual-audit.mjs` collects measurements from a REAL rendered page (Playwright) and
4
+ * feeds them here; the browser glue stays thin and these rules are fully unit-testable without a
5
+ * browser.
10
6
  */
11
7
 
12
8
  /** Catalog (agent-facing mirror lives in mcp/src/data/visual-rules.ts; kept in sync by a guard). */
@@ -39,6 +35,34 @@ export const VISUAL_RULES = [
39
35
  standard: "Unicode UTS #51 · WCAG 2.2 SC 1.1.1",
40
36
  fix: "Remove emoji from rendered product text; use quiet i18n copy + a Lucide icon + a Badge tone for status.",
41
37
  },
38
+ {
39
+ id: "css-layers-missing",
40
+ severity: "error",
41
+ category: "layout",
42
+ standard: "@godxjp/ui styles contract (styles / styles/core are the only entries)",
43
+ fix: "A component rendered without its layer: import `@godxjp/ui/styles` (or `styles/core` without fonts) instead of cherry-picking *-layout.css files. Naked menus and unsized Select rows are this.",
44
+ },
45
+ {
46
+ id: "control-height-mismatch",
47
+ severity: "error",
48
+ category: "layout",
49
+ standard: "@godxjp/ui control tier (--control-height) · Nielsen consistency heuristic",
50
+ fix: "Every control in one row (Flex row, PageContainer extra, footer) must share --control-height. Replace the odd one with the real primitive (Avatar/Button/Badge) instead of a hand-rolled pill; never restyle a control's height.",
51
+ },
52
+ {
53
+ id: "sibling-card-gap",
54
+ severity: "error",
55
+ category: "layout",
56
+ standard: "@godxjp/ui spacing scale (docs/SPACING.md)",
57
+ fix: 'Adjacent Cards must be separated by at least one space step — wrap them in <Flex direction="col" gap> / <ResponsiveGrid>, or put them directly in PageContainer, which spaces them.',
58
+ },
59
+ {
60
+ id: "row-content-starved",
61
+ severity: "warn",
62
+ category: "layout",
63
+ standard: "WCAG 2.2 SC 1.4.10 reflow (content must not be clipped to hide it)",
64
+ fix: 'Text in a control row is truncated to a few characters because a sibling takes the whole width (a w-full SelectTrigger). Give the Select width="auto" or move it out of the row.',
65
+ },
42
66
  {
43
67
  id: "alert-controls-misplaced",
44
68
  severity: "warn",
@@ -114,6 +138,26 @@ export function alertControlIssues(m) {
114
138
  return issues;
115
139
  }
116
140
 
141
+ /** Layers whose probe rule did not resolve. @param {{layer:string, ok:boolean}[]} probes */
142
+ export function missingCssLayers(probes) {
143
+ return probes.filter((p) => !p.ok).map((p) => p.layer);
144
+ }
145
+
146
+ /** Rows whose controls disagree on height. @param {{name:string, heights:number[]}[]} rows */
147
+ export function controlHeightMismatches(rows) {
148
+ return rows.filter((r) => new Set(r.heights).size > 1);
149
+ }
150
+
151
+ /** Adjacent card pairs closer than the minimum gap. @param {{gap:number}[]} pairs */
152
+ export function tightCardPairs(pairs, min = 8) {
153
+ return pairs.filter((p) => p.gap < min);
154
+ }
155
+
156
+ /** Row texts squeezed below a readable width by a sibling. @param {{text:string, visible:number, needed:number}[]} texts */
157
+ export function starvedRowTexts(texts) {
158
+ return texts.filter((t) => t.needed > 40 && t.visible < Math.min(t.needed, 60));
159
+ }
160
+
117
161
  export function findVisualRule(id) {
118
162
  return VISUAL_RULES.find((r) => r.id === id);
119
163
  }
@@ -1,36 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * godxjp-ui VISUAL audit — the runtime counterpart to the static ui-audit.mjs.
4
- *
5
- * Static audit (ui-audit.mjs) reads SOURCE with regexes — zero deps, fast, runs on
6
- * every save. This audit drives a REAL browser (Playwright) over the running app and
7
- * runs axe-core + computed-style/layout heuristics, catching what source can't see:
8
- * colour contrast, target size, OKLCH chroma of a rendered accent, emoji that survived
9
- * to the DOM, and a mis-laid-out notification banner. Warnings by default (agent
10
- * guidance) — pass --strict to exit non-zero. Run it BEFORE an AI/human visual review.
11
- *
12
- * Playwright + @axe-core/playwright are OPTIONAL peer deps — installed only by apps that
13
- * run this audit, so the static audit and the library stay browser-free.
14
- *
15
- * TESTED peer range (see TESTED_VERSIONS + README "Runtime visual audit"):
16
- * playwright >=1.55 <2 (tested 1.61.1)
17
- * @axe-core/playwright >=4.10 <5 (tested 4.12.1)
18
- * axe-core >=4.10 <5 (tested 4.12.1)
19
- *
20
- * Usage (from the consuming app, against its running dev/preview server):
21
- * node node_modules/@godxjp/ui/scripts/visual-audit.mjs http://localhost:5173 /invoices /settings
22
- * node node_modules/@godxjp/ui/scripts/visual-audit.mjs http://localhost:5173 --format json
23
- * node node_modules/@godxjp/ui/scripts/visual-audit.mjs --strict http://localhost:5173 # CI gate
24
- * PLAYWRIGHT_CHROMIUM_EXECUTABLE=/usr/bin/chromium node …/visual-audit.mjs http://… # system browser
25
- *
26
- * JSON contract (--format json ALWAYS emits valid JSON, even on bootstrap failure):
27
- * { status: "ok" | "partial" | "error",
28
- * tool: <TESTED_VERSIONS>,
29
- * summary: { errors, warnings, pages, infrastructureErrors } | null,
30
- * findings: [ … product findings … ],
31
- * errors: [ … infrastructure errors (bootstrap / navigate / analyze) … ] }
32
- * `status: "ok"` ONLY when every page was audited with zero infrastructure errors —
33
- * so a tool/bootstrap failure can NEVER be misread as "zero violations".
3
+ * godxjp-ui VISUAL audit — the runtime counterpart to the static ui-audit.mjs. Static audit
4
+ * (ui-audit.mjs) reads SOURCE with regexes — zero deps, fast, runs on every save.
34
5
  */
35
6
  import { pathToFileURL } from "node:url";
36
7
  import {
@@ -40,14 +11,15 @@ import {
40
11
  isUndersizedTarget,
41
12
  hasEmoji,
42
13
  alertControlIssues,
14
+ missingCssLayers,
15
+ controlHeightMismatches,
16
+ tightCardPairs,
17
+ starvedRowTexts,
43
18
  } from "./visual-audit-rules.mjs";
44
19
 
45
20
  /**
46
- * Peer versions this audit is tested against. Surfaced in the JSON `tool` field, the
47
- * README, and the MCP `list_visual_checks` command so agents pin a compatible range.
48
- * Playwright 1.55+ is required for the `browser.newContext()` / `context.newPage()`
49
- * flow that @axe-core/playwright 4.10+ expects (older `browser.newPage()` throws
50
- * "Please use browser.newContext()").
21
+ * Peer versions this audit is tested against. Surfaced in the JSON `tool` field, the README, and
22
+ * the MCP `list_visual_checks` command so agents pin a compatible range.
51
23
  */
52
24
  export const TESTED_VERSIONS = {
53
25
  playwright: { range: ">=1.55 <2", tested: "1.61.1" },
@@ -68,10 +40,8 @@ export function buildFinding(ruleId, url, message) {
68
40
  }
69
41
 
70
42
  /**
71
- * Assemble the machine-readable result (pure). `findings` are PRODUCT results; `errors`
72
- * are INFRASTRUCTURE failures (a page that would not load, axe that would not inject).
73
- * `status` is "ok" only when no infra error occurred, "error" when nothing could be
74
- * audited, "partial" when some pages ran and some failed.
43
+ * Assemble the machine-readable result (pure). `findings` are PRODUCT results; `errors` are
44
+ * INFRASTRUCTURE failures (a page that would not load, axe that would not inject).
75
45
  */
76
46
  export function buildResult({ findings = [], errors = [], pages = 0 } = {}) {
77
47
  const productErrors = findings.filter((f) => f.severity === "error");
@@ -93,11 +63,7 @@ export function buildResult({ findings = [], errors = [], pages = 0 } = {}) {
93
63
  };
94
64
  }
95
65
 
96
- /**
97
- * Assemble a BOOTSTRAP failure result (pure) — missing peers, no browser, bad args.
98
- * `summary` is null (never `{ warnings: 0 }`) so a consumer cannot read a bootstrap
99
- * failure as a clean pass.
100
- */
66
+ /** Assemble a BOOTSTRAP failure result (pure) — missing peers, no browser, bad args. */
101
67
  export function buildBootstrapError(message, hint) {
102
68
  return {
103
69
  status: "error",
@@ -195,7 +161,80 @@ function collectInPage() {
195
161
  });
196
162
  }
197
163
 
198
- return { accents, targets, text, alerts };
164
+ // Layer completeness one probe per layer, each checks a rule only that layer sets.
165
+ const probe = (layer, className, prop, bad) => {
166
+ const el = document.createElement("div");
167
+ el.className = className;
168
+ el.style.position = "absolute";
169
+ el.style.visibility = "hidden";
170
+ document.body.appendChild(el);
171
+ const v = getComputedStyle(el)[prop];
172
+ el.remove();
173
+ return { layer, ok: v !== bad && v !== "" };
174
+ };
175
+ const layers = [
176
+ probe("control", "ui-button", "borderRadius", "0px"),
177
+ probe("navigation-layout", "ui-dropdown-menu-content", "borderTopWidth", "0px"),
178
+ probe("card-layout", "ui-card", "borderTopWidth", "0px"),
179
+ probe("layout", "ui-page-container", "display", "block"),
180
+ probe("dialog-layout", "ui-dialog-content", "position", "static"),
181
+ probe("form-layout", "ui-form-field", "display", "inline"),
182
+ ];
183
+ // Control rows — every control in a row shares the control tier height.
184
+ const rows = [];
185
+ for (const row of document.querySelectorAll(
186
+ ".ui-flex[data-direction=row], .ui-page-header-extra, .ui-page-footer .ui-flex, [data-slot=card-bar]",
187
+ )) {
188
+ if (!visible(row)) continue;
189
+ const controls = [...row.children].filter(
190
+ (c) =>
191
+ visible(c) &&
192
+ (c.matches(
193
+ "button, [role=combobox], [role=button], input, [data-slot=avatar], .ui-control, .ui-button",
194
+ ) ||
195
+ c.querySelector(":scope > button, :scope > [role=combobox]")),
196
+ );
197
+ if (controls.length < 2) continue;
198
+ rows.push({
199
+ name: (row.className || row.tagName).toString().slice(0, 40),
200
+ heights: controls.map((c) => Math.round(c.getBoundingClientRect().height)),
201
+ });
202
+ }
203
+ // Sibling cards — the gap between consecutive cards in one parent.
204
+ const cardPairs = [];
205
+ const seen = new Set();
206
+ for (const card of document.querySelectorAll(".ui-card, [data-slot=card]")) {
207
+ const parent = card.parentElement;
208
+ if (!parent || seen.has(parent)) continue;
209
+ seen.add(parent);
210
+ const cards = [...parent.children].filter(
211
+ (c) => c.matches(".ui-card, [data-slot=card]") && visible(c),
212
+ );
213
+ for (let i = 1; i < cards.length; i++) {
214
+ const a = cards[i - 1].getBoundingClientRect();
215
+ const b = cards[i].getBoundingClientRect();
216
+ const gap =
217
+ b.top >= a.bottom - 1 ? Math.round(b.top - a.bottom) : Math.round(b.left - a.right);
218
+ cardPairs.push({ gap });
219
+ }
220
+ }
221
+ // Starved text — a text node in a control row squeezed far below what it needs.
222
+ const rowTexts = [];
223
+ for (const row of document.querySelectorAll(
224
+ ".ui-flex[data-direction=row], .ui-page-footer .ui-flex",
225
+ )) {
226
+ for (const t of row.querySelectorAll("[data-slot=text], span, label")) {
227
+ if (!visible(t) || t.children.length) continue;
228
+ const cs = getComputedStyle(t);
229
+ if (cs.overflow !== "hidden" && cs.textOverflow !== "ellipsis") continue;
230
+ rowTexts.push({
231
+ text: (t.textContent || "").trim().slice(0, 30),
232
+ visible: Math.round(t.clientWidth),
233
+ needed: Math.round(t.scrollWidth),
234
+ });
235
+ }
236
+ }
237
+ return { accents, targets, text, alerts, layers, rows, cardPairs, rowTexts };
199
238
  }
200
239
  /* c8 ignore stop */
201
240
 
@@ -223,7 +262,6 @@ async function loadDeps() {
223
262
  /**
224
263
  * Drive the browser over every target and gather findings + infra errors. Cleanup
225
264
  * (page/context/browser) is guaranteed via finally on BOTH success and failure.
226
- * A launch failure is re-tagged as a bootstrap error so it surfaces as `status:error`.
227
265
  */
228
266
  /* c8 ignore start — the browser-driving glue; exercised by scripts/visual-audit-smoke.mjs in CI. */
229
267
  async function audit(targets, { chromium, AxeBuilder }) {
@@ -296,6 +334,39 @@ async function audit(targets, { chromium, AxeBuilder }) {
296
334
  buildFinding("emoji-rendered", url, `emoji rendered in product text: ${found}`),
297
335
  );
298
336
  }
337
+ for (const layer of missingCssLayers(m.layers || []))
338
+ findings.push(
339
+ buildFinding(
340
+ "css-layers-missing",
341
+ url,
342
+ `component layer "${layer}" is not loaded — its rules do not resolve`,
343
+ ),
344
+ );
345
+ for (const r of controlHeightMismatches(m.rows || []))
346
+ findings.push(
347
+ buildFinding(
348
+ "control-height-mismatch",
349
+ url,
350
+ `row "${r.name}" mixes control heights ${[...new Set(r.heights)].join("/")}px`,
351
+ ),
352
+ );
353
+ const tight = tightCardPairs(m.cardPairs || []);
354
+ if (tight.length)
355
+ findings.push(
356
+ buildFinding(
357
+ "sibling-card-gap",
358
+ url,
359
+ `${tight.length} adjacent Card pair(s) closer than 8px (min gap ${Math.min(...tight.map((p) => p.gap))}px)`,
360
+ ),
361
+ );
362
+ for (const t of starvedRowTexts(m.rowTexts || []))
363
+ findings.push(
364
+ buildFinding(
365
+ "row-content-starved",
366
+ url,
367
+ `text "${t.text}" squeezed to ${t.visible}px of ${t.needed}px by a sibling in its row`,
368
+ ),
369
+ );
299
370
  m.alerts.forEach((al, i) => {
300
371
  for (const issue of alertControlIssues(al))
301
372
  findings.push(