@godxjp/ui 19.1.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 (436) hide show
  1. package/README.md +25 -128
  2. package/dist/app/theme-axes.d.ts +4 -15
  3. package/dist/app/timezones.d.ts +1 -4
  4. package/dist/components/charts/area-chart.d.ts +2 -3
  5. package/dist/components/charts/bar-chart.d.ts +2 -3
  6. package/dist/components/charts/chart-frame.d.ts +7 -16
  7. package/dist/components/charts/chart-summary.d.ts +2 -2
  8. package/dist/components/charts/compact-bar-trend.d.ts +9 -24
  9. package/dist/components/charts/index.d.ts +1 -6
  10. package/dist/components/data-display/badge.d.ts +2 -12
  11. package/dist/components/data-display/card.d.ts +21 -41
  12. package/dist/components/data-display/carousel.d.ts +1 -2
  13. package/dist/components/data-display/code-block.d.ts +4 -0
  14. package/dist/components/data-display/code-block.js +24 -0
  15. package/dist/components/data-display/data-table.d.ts +15 -37
  16. package/dist/components/data-display/data-table.js +1 -3
  17. package/dist/components/data-display/descriptions.d.ts +2 -6
  18. package/dist/components/data-display/descriptions.js +0 -2
  19. package/dist/components/data-display/index.d.ts +4 -0
  20. package/dist/components/data-display/index.js +4 -0
  21. package/dist/components/data-display/list-row.d.ts +3 -22
  22. package/dist/components/data-display/permission-matrix.d.ts +7 -20
  23. package/dist/components/data-display/progress.d.ts +1 -1
  24. package/dist/components/data-display/prose.d.ts +4 -0
  25. package/dist/components/data-display/prose.js +20 -0
  26. package/dist/components/data-display/service-launcher-card.d.ts +3 -10
  27. package/dist/components/data-display/table.d.ts +20 -80
  28. package/dist/components/data-display/table.js +0 -3
  29. package/dist/components/data-display/timeline.d.ts +3 -5
  30. package/dist/components/data-entry/branch-scope-picker.d.ts +2 -19
  31. package/dist/components/data-entry/branch-scope-picker.js +0 -1
  32. package/dist/components/data-entry/calendar.d.ts +3 -2
  33. package/dist/components/data-entry/calendar.js +58 -3
  34. package/dist/components/data-entry/command-palette.d.ts +8 -22
  35. package/dist/components/data-entry/date-picker.d.ts +4 -5
  36. package/dist/components/data-entry/date-picker.js +6 -1
  37. package/dist/components/data-entry/date-range-picker.d.ts +1 -1
  38. package/dist/components/data-entry/date-range-picker.js +8 -3
  39. package/dist/components/data-entry/form-errors.d.ts +7 -18
  40. package/dist/components/data-entry/form-field.js +1 -5
  41. package/dist/components/data-entry/form.d.ts +1 -9
  42. package/dist/components/data-entry/input.d.ts +16 -14
  43. package/dist/components/data-entry/input.js +3 -0
  44. package/dist/components/data-entry/month-picker.js +2 -2
  45. package/dist/components/data-entry/month-range-picker.d.ts +4 -6
  46. package/dist/components/data-entry/month-range-picker.js +2 -2
  47. package/dist/components/data-entry/number-input.d.ts +1 -5
  48. package/dist/components/data-entry/number-input.js +1 -0
  49. package/dist/components/data-entry/radio.js +0 -1
  50. package/dist/components/data-entry/search-select.d.ts +2 -4
  51. package/dist/components/data-entry/select.d.ts +11 -6
  52. package/dist/components/data-entry/select.js +4 -2
  53. package/dist/components/data-entry/switch.js +0 -1
  54. package/dist/components/data-entry/textarea.d.ts +10 -16
  55. package/dist/components/data-entry/time-picker.d.ts +3 -5
  56. package/dist/components/data-entry/upload-types.d.ts +1 -5
  57. package/dist/components/data-entry/use-upload-draft.d.ts +0 -4
  58. package/dist/components/feedback/dialog.d.ts +6 -15
  59. package/dist/components/feedback/overlay-header-tone.d.ts +1 -1
  60. package/dist/components/feedback/sheet.d.ts +3 -10
  61. package/dist/components/feedback/sheet.js +1 -1
  62. package/dist/components/feedback/sonner.js +2 -3
  63. package/dist/components/general/activity.d.ts +4 -30
  64. package/dist/components/general/button.js +1 -10
  65. package/dist/components/general/logo.d.ts +6 -28
  66. package/dist/components/general/reveal.d.ts +3 -9
  67. package/dist/components/general/typography.d.ts +1 -3
  68. package/dist/components/layout/account-chip.d.ts +8 -0
  69. package/dist/components/layout/account-chip.js +45 -0
  70. package/dist/components/layout/auth-divider.d.ts +2 -9
  71. package/dist/components/layout/auth-shell.d.ts +1 -14
  72. package/dist/components/layout/breadcrumb.d.ts +1 -5
  73. package/dist/components/layout/centered-shell.d.ts +4 -18
  74. package/dist/components/layout/error-surface.d.ts +11 -28
  75. package/dist/components/layout/error-surface.js +4 -1
  76. package/dist/components/layout/index.d.ts +2 -0
  77. package/dist/components/layout/index.js +2 -0
  78. package/dist/components/layout/legal-document-shell.d.ts +5 -28
  79. package/dist/components/layout/master-detail.d.ts +2 -18
  80. package/dist/components/layout/page-container.js +0 -1
  81. package/dist/components/layout/responsive-grid.d.ts +6 -1
  82. package/dist/components/layout/responsive-grid.js +9 -2
  83. package/dist/components/layout/service-role-panel.d.ts +7 -19
  84. package/dist/components/layout/sidebar.d.ts +5 -27
  85. package/dist/components/layout/sidebar.js +0 -1
  86. package/dist/components/layout/split-pane.d.ts +0 -7
  87. package/dist/components/layout/topbar.d.ts +1 -14
  88. package/dist/components/navigation/app-setting-picker.js +1 -3
  89. package/dist/components/navigation/filter-bar.d.ts +1 -6
  90. package/dist/components/navigation/pagination-utils.d.ts +2 -12
  91. package/dist/components/navigation/tabs-scroll.d.ts +2 -36
  92. package/dist/components/navigation/tabs.js +4 -8
  93. package/dist/components/query/data-state.d.ts +2 -2
  94. package/dist/components/ui/credential-reveal.d.ts +1 -4
  95. package/dist/components/ui/hover-card.d.ts +1 -2
  96. package/dist/components/ui/separator.d.ts +2 -15
  97. package/dist/components/ui/toggle-group.d.ts +1 -5
  98. package/dist/components/ui/toggle.d.ts +6 -31
  99. package/dist/email/brand-mark.d.ts +6 -34
  100. package/dist/email/color.d.ts +7 -17
  101. package/dist/email/geometry.d.ts +4 -8
  102. package/dist/email/index.d.ts +4 -48
  103. package/dist/email/inline-style.d.ts +3 -9
  104. package/dist/email/urgency.d.ts +2 -7
  105. package/dist/form/form-context.d.ts +2 -5
  106. package/dist/form/form-root.d.ts +1 -2
  107. package/dist/i18n/messages/en.json +4 -0
  108. package/dist/i18n/messages/ja.json +4 -0
  109. package/dist/i18n/messages/vi.json +4 -0
  110. package/dist/inertia/index.d.ts +2 -10
  111. package/dist/lib/control-styles.d.ts +10 -21
  112. package/dist/lib/control-styles.js +4 -6
  113. package/dist/lib/datetime/format-date.d.ts +1 -4
  114. package/dist/lib/field-a11y.d.ts +11 -68
  115. package/dist/lib/field-a11y.js +0 -1
  116. package/dist/lib/format.d.ts +4 -8
  117. package/dist/lib/hooks.d.ts +6 -12
  118. package/dist/lib/permission-grid.d.ts +3 -18
  119. package/dist/lib/query-error.d.ts +3 -3
  120. package/dist/props/components/app.prop.d.ts +15 -38
  121. package/dist/props/components/charts.prop.d.ts +2 -3
  122. package/dist/props/components/data-display.prop.d.ts +48 -72
  123. package/dist/props/components/data-entry.prop.d.ts +35 -40
  124. package/dist/props/components/form.prop.d.ts +2 -6
  125. package/dist/props/components/general.prop.d.ts +11 -27
  126. package/dist/props/components/layout.prop.d.ts +89 -260
  127. package/dist/props/components/navigation.prop.d.ts +27 -42
  128. package/dist/props/registry.d.ts +31 -0
  129. package/dist/props/registry.js +33 -0
  130. package/dist/props/vocabulary/data.prop.d.ts +18 -49
  131. package/dist/props/vocabulary/index.d.ts +1 -1
  132. package/dist/props/vocabulary/interaction.prop.d.ts +10 -12
  133. package/dist/props/vocabulary/layout.prop.d.ts +8 -30
  134. package/dist/props/vocabulary/shared.prop.d.ts +3 -2
  135. package/dist/styles/alert-layout.css +1 -32
  136. package/dist/styles/badge-layout.css +4 -18
  137. package/dist/styles/base.css +14 -113
  138. package/dist/styles/card-layout.css +6 -128
  139. package/dist/styles/chart-layout.css +2 -17
  140. package/dist/styles/control.css +380 -458
  141. package/dist/styles/core.css +21 -0
  142. package/dist/styles/data-display-layout.css +175 -94
  143. package/dist/styles/data-entry-layout.css +0 -45
  144. package/dist/styles/density.css +1 -30
  145. package/dist/styles/dialog-layout.css +5 -69
  146. package/dist/styles/focus-ring.css +13 -87
  147. package/dist/styles/fonts.css +1 -31
  148. package/dist/styles/form-layout.css +7 -91
  149. package/dist/styles/index.css +2 -20
  150. package/dist/styles/layout.css +38 -456
  151. package/dist/styles/logo-layout.css +1 -35
  152. package/dist/styles/motion.css +3 -42
  153. package/dist/styles/navigation-layout.css +3 -50
  154. package/dist/styles/shell-layout.css +37 -403
  155. package/dist/styles/table-layout.css +16 -274
  156. package/dist/styles/text-layout.css +7 -21
  157. package/dist/styles/toggle.css +1 -23
  158. package/dist/theme/dxs.canonical.css +1 -55
  159. package/dist/theme/example.service.css +1 -26
  160. package/dist/theme/famgia.service.css +5 -15
  161. package/dist/tokens/axes.css +3 -40
  162. package/dist/tokens/base.css +1 -4
  163. package/dist/tokens/components/activity.css +6 -42
  164. package/dist/tokens/components/badge.css +4 -13
  165. package/dist/tokens/components/banner.css +4 -9
  166. package/dist/tokens/components/card.css +32 -88
  167. package/dist/tokens/components/chart.css +9 -26
  168. package/dist/tokens/components/control.css +37 -222
  169. package/dist/tokens/components/data-display.css +28 -79
  170. package/dist/tokens/components/data-entry.css +5 -19
  171. package/dist/tokens/components/descriptions.css +7 -27
  172. package/dist/tokens/components/email.css +38 -84
  173. package/dist/tokens/components/error-surface.css +2 -18
  174. package/dist/tokens/components/feedback.css +27 -124
  175. package/dist/tokens/components/form.css +13 -45
  176. package/dist/tokens/components/legal-document.css +16 -41
  177. package/dist/tokens/components/list-row.css +11 -36
  178. package/dist/tokens/components/logo.css +2 -57
  179. package/dist/tokens/components/navigation.css +16 -94
  180. package/dist/tokens/components/scroll-area.css +2 -18
  181. package/dist/tokens/components/separator.css +8 -30
  182. package/dist/tokens/components/sheet.css +3 -14
  183. package/dist/tokens/components/shell.css +53 -307
  184. package/dist/tokens/components/sidebar.css +5 -22
  185. package/dist/tokens/components/table.css +19 -140
  186. package/dist/tokens/components/toggle.css +3 -65
  187. package/dist/tokens/components/upload.css +7 -43
  188. package/dist/tokens/foundation.css +108 -383
  189. package/dist/tokens/semantic/layout.css +33 -207
  190. package/docs/CANONICAL-CONTRACTS.md +206 -0
  191. package/docs/COMPONENTS.md +157 -0
  192. package/docs/COMPOSITION-VS-COMPONENT.md +105 -0
  193. package/docs/CONSUMER-RULES.md +14 -0
  194. package/docs/CUSTOMER-THEMING.md +162 -0
  195. package/docs/DATETIME.md +69 -0
  196. package/docs/DEVELOPMENT.md +173 -0
  197. package/docs/FORMS.md +143 -0
  198. package/docs/FRAME-A11Y-CI.md +267 -0
  199. package/docs/FRAME-COVERAGE-LEDGER.md +123 -0
  200. package/docs/FRAME-COVERAGE-REPORT.md +140 -0
  201. package/docs/FRAME-COVERAGE-STANDARD.md +65 -0
  202. package/docs/FRAME-COVERAGE.md +29 -0
  203. package/docs/PROPS-REGISTRY.md +61 -0
  204. package/docs/PROPS-VOCABULARY.md +100 -0
  205. package/docs/README.md +60 -0
  206. package/docs/REGISTRY.md +70 -0
  207. package/docs/SPACING.md +56 -0
  208. package/docs/STANDARDS-vocabulary-tokens.md +48 -0
  209. package/docs/TESTING.md +98 -0
  210. package/docs/TOKENS.md +347 -0
  211. package/docs/asset-modules.d.ts +46 -0
  212. package/docs/assets/cover-terrain.svg +6 -0
  213. package/docs/assets/portrait-amber.svg +5 -0
  214. package/docs/assets/portrait-clay.svg +5 -0
  215. package/docs/assets/portrait-indigo.svg +5 -0
  216. package/docs/assets/portrait-iris.svg +5 -0
  217. package/docs/assets/portrait-moss.svg +5 -0
  218. package/docs/assets/portrait-plum.svg +5 -0
  219. package/docs/assets/portrait-slate.svg +5 -0
  220. package/docs/charts/compact-bar-trend.tsx +328 -0
  221. package/docs/charts/size-tiers.tsx +192 -0
  222. package/docs/data-display/accordion.tsx +99 -0
  223. package/docs/data-display/avatar.tsx +355 -0
  224. package/docs/data-display/badge.tsx +247 -0
  225. package/docs/data-display/card/examples/detail-panel.tsx +106 -0
  226. package/docs/data-display/card/index.md +3 -0
  227. package/docs/data-display/card/index.tsx +285 -0
  228. package/docs/data-display/carousel.tsx +156 -0
  229. package/docs/data-display/charts.tsx +198 -0
  230. package/docs/data-display/code-block.tsx +90 -0
  231. package/docs/data-display/collapsible.tsx +162 -0
  232. package/docs/data-display/credential-reveal.tsx +145 -0
  233. package/docs/data-display/data-table/examples/approval-queue.tsx +212 -0
  234. package/docs/data-display/data-table/examples/grid-features.tsx +158 -0
  235. package/docs/data-display/data-table/examples/list-page.tsx +175 -0
  236. package/docs/data-display/data-table/index.md +5 -0
  237. package/docs/data-display/data-table/index.tsx +295 -0
  238. package/docs/data-display/descriptions.tsx +143 -0
  239. package/docs/data-display/empty-state.tsx +164 -0
  240. package/docs/data-display/hover-card.tsx +181 -0
  241. package/docs/data-display/list-row.tsx +420 -0
  242. package/docs/data-display/permission-matrix.tsx +184 -0
  243. package/docs/data-display/popover-touch.tsx +27 -0
  244. package/docs/data-display/popover.tsx +245 -0
  245. package/docs/data-display/progress.tsx +84 -0
  246. package/docs/data-display/prose.tsx +105 -0
  247. package/docs/data-display/qr-code.tsx +68 -0
  248. package/docs/data-display/scroll-area.tsx +271 -0
  249. package/docs/data-display/service-launcher-card.tsx +145 -0
  250. package/docs/data-display/stat-card.tsx +99 -0
  251. package/docs/data-display/table.tsx +251 -0
  252. package/docs/data-display/timeline.tsx +112 -0
  253. package/docs/data-display/touch-actions.tsx +36 -0
  254. package/docs/data-display/tree-list.tsx +107 -0
  255. package/docs/data-entry/branch-scope-picker.tsx +123 -0
  256. package/docs/data-entry/calendar.tsx +180 -0
  257. package/docs/data-entry/cascader.tsx +378 -0
  258. package/docs/data-entry/checkbox.tsx +256 -0
  259. package/docs/data-entry/color-picker.tsx +117 -0
  260. package/docs/data-entry/command-palette.tsx +179 -0
  261. package/docs/data-entry/command.tsx +262 -0
  262. package/docs/data-entry/country-picker-recipe.tsx +170 -0
  263. package/docs/data-entry/date-picker.tsx +191 -0
  264. package/docs/data-entry/date-range-picker.tsx +149 -0
  265. package/docs/data-entry/form/examples/employee-settings.tsx +267 -0
  266. package/docs/data-entry/form/examples/invoice-form.tsx +387 -0
  267. package/docs/data-entry/form-field/examples/a11y-contract.tsx +240 -0
  268. package/docs/data-entry/form-field/examples/create-form.tsx +105 -0
  269. package/docs/data-entry/form-field/index.md +8 -0
  270. package/docs/data-entry/form-field/index.tsx +326 -0
  271. package/docs/data-entry/form-inertia.tsx +80 -0
  272. package/docs/data-entry/form-root.tsx +102 -0
  273. package/docs/data-entry/form.tsx +724 -0
  274. package/docs/data-entry/input-otp.tsx +231 -0
  275. package/docs/data-entry/input.tsx +114 -0
  276. package/docs/data-entry/label.tsx +100 -0
  277. package/docs/data-entry/month-picker.tsx +101 -0
  278. package/docs/data-entry/month-range-picker.tsx +115 -0
  279. package/docs/data-entry/number-input.tsx +240 -0
  280. package/docs/data-entry/password-input.tsx +106 -0
  281. package/docs/data-entry/password-strength.tsx +97 -0
  282. package/docs/data-entry/radio-group.tsx +95 -0
  283. package/docs/data-entry/rating.tsx +87 -0
  284. package/docs/data-entry/search-input.tsx +120 -0
  285. package/docs/data-entry/select-async-contracts.tsx +108 -0
  286. package/docs/data-entry/select-matrix.tsx +204 -0
  287. package/docs/data-entry/select.tsx +369 -0
  288. package/docs/data-entry/slider.tsx +130 -0
  289. package/docs/data-entry/switch.tsx +67 -0
  290. package/docs/data-entry/tag-input.tsx +108 -0
  291. package/docs/data-entry/textarea.tsx +209 -0
  292. package/docs/data-entry/time-picker.tsx +99 -0
  293. package/docs/data-entry/toggle-count.tsx +293 -0
  294. package/docs/data-entry/toggle-group.tsx +225 -0
  295. package/docs/data-entry/toggle.tsx +127 -0
  296. package/docs/data-entry/transfer.tsx +97 -0
  297. package/docs/data-entry/tree-select.tsx +204 -0
  298. package/docs/data-entry/upload.tsx +184 -0
  299. package/docs/feedback/alert-dialog.tsx +270 -0
  300. package/docs/feedback/alert.tsx +248 -0
  301. package/docs/feedback/banner.tsx +183 -0
  302. package/docs/feedback/danger-confirm.tsx +88 -0
  303. package/docs/feedback/dialog-touch.tsx +24 -0
  304. package/docs/feedback/dialog.tsx +193 -0
  305. package/docs/feedback/sheet.tsx +410 -0
  306. package/docs/feedback/skeleton.tsx +111 -0
  307. package/docs/feedback/toast.tsx +135 -0
  308. package/docs/feedback/tooltip.tsx +171 -0
  309. package/docs/feedback/two-factor-setup.tsx +68 -0
  310. package/docs/foundation/_email-specimen.ts +274 -0
  311. package/docs/foundation/colors.tsx +202 -0
  312. package/docs/foundation/density.tsx +232 -0
  313. package/docs/foundation/email-tokens.tsx +306 -0
  314. package/docs/foundation/radius-shadow.tsx +152 -0
  315. package/docs/foundation/spacing.tsx +211 -0
  316. package/docs/foundation/typography.tsx +147 -0
  317. package/docs/general/activity.tsx +224 -0
  318. package/docs/general/button/examples/destructive-confirm.tsx +90 -0
  319. package/docs/general/button/examples/form-actions.tsx +85 -0
  320. package/docs/general/button/examples/toolbar.tsx +160 -0
  321. package/docs/general/button/index.md +12 -0
  322. package/docs/general/button/index.tsx +211 -0
  323. package/docs/general/logo.tsx +131 -0
  324. package/docs/general/reveal.tsx +41 -0
  325. package/docs/general/typography.tsx +152 -0
  326. package/docs/layout/account-chip.tsx +72 -0
  327. package/docs/layout/admin-collection.tsx +122 -0
  328. package/docs/layout/app-shell.tsx +374 -0
  329. package/docs/layout/aspect-ratio.tsx +119 -0
  330. package/docs/layout/auth-account-summary.tsx +103 -0
  331. package/docs/layout/auth-divider.tsx +28 -0
  332. package/docs/layout/auth-footer.tsx +37 -0
  333. package/docs/layout/auth-identity.tsx +36 -0
  334. package/docs/layout/auth-recovery/examples/mfa-challenge.md +41 -0
  335. package/docs/layout/auth-recovery/examples/mfa-challenge.tsx +254 -0
  336. package/docs/layout/auth-recovery/examples/mobile-390.md +25 -0
  337. package/docs/layout/auth-recovery/examples/mobile-390.tsx +82 -0
  338. package/docs/layout/auth-recovery/examples/password-recovery.md +24 -0
  339. package/docs/layout/auth-recovery/examples/password-recovery.tsx +197 -0
  340. package/docs/layout/auth-recovery/examples/wrapping-ja-en-vi.md +27 -0
  341. package/docs/layout/auth-recovery/examples/wrapping-ja-en-vi.tsx +156 -0
  342. package/docs/layout/auth-recovery/index.md +140 -0
  343. package/docs/layout/auth-recovery/index.tsx +96 -0
  344. package/docs/layout/auth-shell-context.tsx +103 -0
  345. package/docs/layout/auth-shell-device.tsx +112 -0
  346. package/docs/layout/auth-shell-registration.tsx +335 -0
  347. package/docs/layout/auth-shell-variants.tsx +139 -0
  348. package/docs/layout/auth-shell.tsx +59 -0
  349. package/docs/layout/auth-stack.tsx +40 -0
  350. package/docs/layout/centered-shell.tsx +102 -0
  351. package/docs/layout/error-surface/examples/application-403.tsx +84 -0
  352. package/docs/layout/error-surface/examples/application-404.tsx +77 -0
  353. package/docs/layout/error-surface/examples/system-500.tsx +47 -0
  354. package/docs/layout/error-surface/examples/system-503.tsx +48 -0
  355. package/docs/layout/error-surface/index.md +153 -0
  356. package/docs/layout/error-surface/index.tsx +239 -0
  357. package/docs/layout/flex.tsx +262 -0
  358. package/docs/layout/legal-document-shell/_data.ts +110 -0
  359. package/docs/layout/legal-document-shell/examples/desktop-1440.md +12 -0
  360. package/docs/layout/legal-document-shell/examples/desktop-1440.tsx +83 -0
  361. package/docs/layout/legal-document-shell/examples/mobile-390.md +22 -0
  362. package/docs/layout/legal-document-shell/examples/mobile-390.tsx +70 -0
  363. package/docs/layout/legal-document-shell/examples/tablet-1024.md +3 -0
  364. package/docs/layout/legal-document-shell/examples/tablet-1024.tsx +84 -0
  365. package/docs/layout/legal-document-shell/examples/wrapping-ja-en-vi.md +18 -0
  366. package/docs/layout/legal-document-shell/examples/wrapping-ja-en-vi.tsx +61 -0
  367. package/docs/layout/legal-document-shell/index.md +51 -0
  368. package/docs/layout/legal-document-shell/index.tsx +101 -0
  369. package/docs/layout/master-detail.tsx +387 -0
  370. package/docs/layout/org-switcher.tsx +131 -0
  371. package/docs/layout/page-container.tsx +946 -0
  372. package/docs/layout/resizable-panel.tsx +419 -0
  373. package/docs/layout/responsive-grid.tsx +235 -0
  374. package/docs/layout/separator.tsx +327 -0
  375. package/docs/layout/service-role-panel.tsx +144 -0
  376. package/docs/layout/sidebar.tsx +586 -0
  377. package/docs/layout/split-pane.tsx +451 -0
  378. package/docs/layout/topbar.tsx +347 -0
  379. package/docs/navigation/app-setting-picker.tsx +141 -0
  380. package/docs/navigation/breadcrumb.tsx +128 -0
  381. package/docs/navigation/context-menu.tsx +117 -0
  382. package/docs/navigation/dropdown-menu.tsx +213 -0
  383. package/docs/navigation/filter-bar.tsx +227 -0
  384. package/docs/navigation/menubar.tsx +141 -0
  385. package/docs/navigation/navigation-menu.tsx +165 -0
  386. package/docs/navigation/pagination.tsx +241 -0
  387. package/docs/navigation/steps.tsx +263 -0
  388. package/docs/navigation/tabs-rtl.tsx +28 -0
  389. package/docs/navigation/tabs.tsx +308 -0
  390. package/docs/navigation/toolbar.tsx +339 -0
  391. package/docs/providers/app-provider.tsx +191 -0
  392. package/docs/providers/format-date.tsx +230 -0
  393. package/docs/query/button-refetch.tsx +160 -0
  394. package/docs/query/data-state.tsx +192 -0
  395. package/docs/query/infinite-query-state.tsx +93 -0
  396. package/docs/query/mutation-feedback.tsx +70 -0
  397. package/docs/query/prefetch-link.tsx +262 -0
  398. package/docs/query/touch-actions.tsx +68 -0
  399. package/docs/showcase/acme-portal.tsx +441 -0
  400. package/docs/showcase/acme-website.tsx +455 -0
  401. package/docs/showcase/case1-warehouse-dashboard.tsx +343 -0
  402. package/docs/showcase/case2-employee-me.tsx +441 -0
  403. package/docs/showcase/case3-approval-workflow.tsx +527 -0
  404. package/docs/showcase/case4-login.tsx +307 -0
  405. package/docs/showcase/case5-shift-calendar.tsx +795 -0
  406. package/docs/showcase/case6-agency-handy.tsx +1072 -0
  407. package/docs/showcase/futurelastic-web.tsx +390 -0
  408. package/docs/showcase/org-switcher.tsx +313 -0
  409. package/docs/showcase/permission-matrix.tsx +343 -0
  410. package/docs/showcase/public-landing.tsx +210 -0
  411. package/docs/showcase/settings-account-sections.tsx +559 -0
  412. package/docs/showcase/settings-security-mfa.tsx +145 -0
  413. package/docs/showcase/table-approval-queue.tsx +199 -0
  414. package/docs/showcase/table-bulk-actions.tsx +460 -0
  415. package/docs/showcase/table-compact-kintone.tsx +438 -0
  416. package/docs/showcase/table-conditional-format.tsx +367 -0
  417. package/docs/showcase/table-crud-list.tsx +544 -0
  418. package/docs/showcase/table-density.tsx +340 -0
  419. package/docs/showcase/table-expandable-rows.tsx +471 -0
  420. package/docs/showcase/table-filter-chips.tsx +461 -0
  421. package/docs/showcase/table-footer-totals.tsx +311 -0
  422. package/docs/showcase/table-grouped-subtotals.tsx +388 -0
  423. package/docs/showcase/table-master-detail.tsx +479 -0
  424. package/docs/showcase/table-pagination.tsx +320 -0
  425. package/docs/showcase/table-states.tsx +363 -0
  426. package/docs/showcase/table-sticky-columns.tsx +373 -0
  427. package/docs/showcase/table-tree-rows.tsx +444 -0
  428. package/docs/showcase/table-view-tabs.tsx +488 -0
  429. package/package.json +9 -11
  430. package/scripts/_agent-setup.mjs +16 -2
  431. package/scripts/audit-hook.mjs +3 -13
  432. package/scripts/init-agent-kit.mjs +1 -14
  433. package/scripts/postinstall.mjs +12 -12
  434. package/scripts/ui-audit.mjs +93 -31
  435. package/scripts/visual-audit-rules.mjs +51 -7
  436. package/scripts/visual-audit.mjs +118 -47
@@ -1,7 +1,4 @@
1
- /*
2
- * APP SHELL — canonical chrome layout for admin surfaces.
3
- * Slot-based composition: <AppShell sidebar={<Sidebar />} topbar={<Topbar />}>.
4
- */
1
+
5
2
 
6
3
  @layer components {
7
4
  .app-root {
@@ -13,8 +10,6 @@
13
10
  "sidebar topbar"
14
11
  "sidebar main"
15
12
  "sidebar footer";
16
- /* Rail width is a knob, never a literal (rule #45) — a service on a different design grid sets
17
- * --app-shell-sidebar-width once (e.g. 255px) instead of forking this rule. */
18
13
  grid-template-columns: var(--app-shell-sidebar-width) minmax(0, 1fr);
19
14
  grid-template-rows: var(--app-shell-bar-height) minmax(0, 1fr) auto;
20
15
  height: 100vh;
@@ -25,13 +20,6 @@
25
20
  grid-template-columns: var(--app-shell-rail-width) minmax(0, 1fr);
26
21
  }
27
22
 
28
- /* topbarSpan="full" — the bar runs edge to edge and the rail starts beneath it, for products
29
- * whose bar carries space-level chrome (global search, account, notifications) that outranks the
30
- * current section instead of belonging to it. Only the row assignment changes: both track sizes
31
- * stay token-owned, so --app-shell-sidebar-width and the collapsed rail still apply untouched.
32
- * Below the 900px breakpoint the grid already collapses to a single column, which IS this
33
- * arrangement, so no narrow override is needed here — only the docked mode, which rebuilds the
34
- * two-track grid, needs restating (see the breakpoint block). */
35
23
  .app-root[data-topbar-span="full"] {
36
24
  grid-template-areas:
37
25
  "topbar topbar"
@@ -39,35 +27,10 @@
39
27
  "sidebar footer";
40
28
  }
41
29
 
42
- /* NO TOP BAR AT ALL — `topbar`, `topbarLeft`, `topbarRight` and `logo` all undefined. AppShell
43
- * renders no `<header class="app-topbar">` in that state (the mobile drawer trigger is the one
44
- * exception, below) and publishes it as ONE attribute here, so the geometry is decided by the
45
- * grid element itself rather than by `:has()` on whatever the consumer nested inside.
46
- *
47
- * The row has to go with the header. An empty --app-shell-bar-height track still costs a fixed
48
- * 48px, and the header still painted a bottom border over hsl(var(--card)) — so a chat / mail /
49
- * IDE shell, whose PAGE header is the top row, showed two rows of chrome (~48 + ~60px) over
50
- * exactly the region that needs the height most.
51
- *
52
- * `auto`, not a `0` literal, is what makes one attribute enough for every branch: an absent row
53
- * measures 0, a `display:none` header measures 0, and at narrow widths — where the header comes
54
- * back to carry the hamburger — the row sizes itself from that header's own min-height. So the
55
- * breakpoint block needs no restatement of these rows, which is the trap this file has already
56
- * paid for once: the deleted 768px block re-declared grid-template-rows with a `3rem` literal
57
- * and defeated --app-shell-bar-height below 768px only (see the note on the one @media block).
58
- * `[data-topbar-span="full"]` re-declares grid-template-AREAS only, so it inherits this row
59
- * template untouched and its named-but-empty "topbar topbar" row is 0 as well. */
60
30
  .app-root[data-topbar="none"] {
61
31
  grid-template-rows: auto minmax(0, 1fr) auto;
62
32
  }
63
33
 
64
- /* The one thing that may still live in a bar-less shell: AppShell's OWN hamburger. Below 900px
65
- * the docked sidebar is hidden, and dropping the bar there too would leave the shell with no
66
- * reachable navigation at all — a worse failure than the double chrome above (gh#165). So when a
67
- * drawer exists the header is still rendered, carrying the trigger and nothing else, and it is
68
- * THIS rule, not the empty row, that keeps it off the wide layout: `display:none` removes its
69
- * border, its background and its height together. The min-height is inert here and load-bearing
70
- * at narrow widths, where it is the only thing giving the `auto` row a height. */
71
34
  .app-root[data-topbar="none"] > .app-topbar {
72
35
  display: none;
73
36
  min-height: var(--app-shell-bar-height);
@@ -80,35 +43,21 @@
80
43
  min-width: 0;
81
44
  min-height: 0;
82
45
  flex-direction: column;
83
- /* Same ring-headroom contract as the topbar family (gh#291): nav rows are
84
- * flush with the rail's inline edges, so a plain `hidden` clipped their
85
- * focus ring on three sides. `clip` + margin (the margin is honoured only
86
- * when BOTH axes clip) lets the ring paint over the rail border, while the
87
- * rail still contains its content. Inner scroll regions are unaffected —
88
- * `clip` creates no scroll container, exactly like `hidden`. */
46
+
89
47
  overflow: clip;
90
48
  overflow-clip-margin: var(--focus-ring-clip-margin);
91
49
  border-inline-end: 1px solid hsl(var(--border));
92
50
  background: hsl(var(--card));
93
- /* Opt-in brand-chrome wash — invisible by default (--sidebar-gradient: none). */
51
+
94
52
  background-image: var(--sidebar-gradient);
95
53
  color: hsl(var(--foreground));
96
54
  }
97
55
 
98
56
  .app-topbar {
99
57
  grid-area: topbar;
100
- /* Persistent navigation chrome stays a FIXED size regardless of density — the
101
- * AppShell adds `.ui-scale-fixed` to this <header> (resets the whole size-token
102
- * set to baseline; --scaling:1 alone is not enough — see foundation.css). */
103
58
  display: flex;
104
59
  min-width: 0;
105
- /* Same both-axes-clip + margin contract as .ui-topbar (gh#291): Chromium
106
- * honours overflow-clip-margin ONLY when BOTH axes are `clip` — a
107
- * single-axis clip silently drops the margin, which would cut a flush
108
- * control's ring whenever a theme sets --app-shell-bar-inset to 0. The 8px
109
- * margin also covers the vertical ring of coarse-pointer 44px controls,
110
- * which the 3.5rem coarse bar height already keeps inside the bar. Safari
111
- * falls back to `visible` with the .ui-topbar family below. */
60
+
112
61
  overflow: clip;
113
62
  overflow-clip-margin: var(--focus-ring-clip-margin);
114
63
  align-items: center;
@@ -116,7 +65,7 @@
116
65
  padding-inline: var(--app-shell-bar-inset);
117
66
  border-bottom: 1px solid hsl(var(--border));
118
67
  background: hsl(var(--card));
119
- /* Opt-in brand-chrome wash — invisible by default (--topbar-gradient: none). */
68
+
120
69
  background-image: var(--topbar-gradient);
121
70
  }
122
71
 
@@ -124,38 +73,16 @@
124
73
  grid-area: main;
125
74
  min-width: 0;
126
75
  overflow: auto;
127
- /* Column flow, so a page that asks to FILL can actually be bounded by the
128
- * region instead of merely starting at its top. With the default `block`
129
- * flow a `--fill` container could only be `min-height: 100%`, i.e. it grew
130
- * past the region and pushed the scroll back out here — which is why a chat
131
- * transcript slid UNDER its own sticky composer. Ordinary pages are
132
- * unaffected: their container still has `min-height: 100%`, still grows,
133
- * and this element still scrolls it. */
76
+
134
77
  display: flex;
135
78
  flex-direction: column;
136
- /* Ambient brand glow hook — invisible by default (--gradient-glow: none); a service paints a
137
- * radial/linear brand wash behind the whole content area by configuring the token alone. */
79
+
138
80
  background-image: var(--gradient-glow);
139
- /* Sole scroll/clip boundary for the page area. `overflow:auto` alone does NOT reliably
140
- * establish a paint-containment boundary in Chrome: tall nested scrollers (a two-pane
141
- * `grid` of long lists, a nested `overflow-y:auto` region) — or any absolutely-positioned
142
- * descendant (Radix form-fallback bubbles, `.sr-only` nodes) — can leak their clipped
143
- * extent into the DOCUMENT scroll, so the window scrolls past the content into a
144
- * full-viewport void below the fold even though `.app-root` is exactly 100vh.
145
- * `contain:paint` makes `.app-main` the real clip boundary + containing block, so its
146
- * content can never extend the document scroll height (gh#104 — same shell-containment
147
- * family as the gh#103 body-stretch void). */
81
+
148
82
  contain: paint;
149
83
  background-color: var(--app-shell-main-background);
150
84
  }
151
85
 
152
- /* `.app-main` carries tabindex="0" (axe scrollable-region-focusable, dbad118), so keyboard
153
- * focus can land on the region. The UA default outline is suppressed and the indicator is
154
- * token-gated (gh#276): `--region-focus-ring-width` defaults to 0 — OFF — because a
155
- * control-strength frame around the whole content area reads as a glitch (`:focus-visible`
156
- * promotes on any keypress after a click-focus, so mouse users see it constantly). A service
157
- * that needs the visible keyboard-scroll affordance (WCAG 2.4.7) opts in via the token;
158
- * INSET because `.app-main` is the shell's clip boundary (`contain: paint`). */
159
86
  .app-main:focus-visible {
160
87
  outline: none;
161
88
  box-shadow: inset 0 0 0 var(--region-focus-ring-width)
@@ -173,11 +100,6 @@
173
100
  max-width: var(--app-shell-page-max-width);
174
101
  }
175
102
 
176
- /*
177
- * AUTH SHELL — centred auth/login page shell (brand bar + centred card + footer).
178
- * Comfortable control density + a larger auth heading are scoped to the shell so login/mfa/reset
179
- * forms read at the right size without a consumer scope class. Everything reads a token knob.
180
- */
181
103
  .ui-auth-shell {
182
104
  --control-height: var(--auth-shell-control-height);
183
105
  --heading-h1: var(--auth-shell-heading-size);
@@ -220,22 +142,12 @@
220
142
  --auth-shell-main-padding: var(--auth-shell-canonical-main-padding);
221
143
  }
222
144
 
223
- /*
224
- * NAMED FLOW PRESETS (gh#217 / gh#220) — orthogonal to `variant` and declared AFTER it (equal
225
- * specificity, so source order wins) so a preset re-measures the page while `variant="canonical"`
226
- * keeps owning control density and heading size. A preset owns the card measure + page gutters,
227
- * so the consumer never overrides `--auth-shell-card-max-width` by hand.
228
- */
229
145
  .ui-auth-shell[data-preset] .ui-auth-shell-card {
230
146
  display: flex;
231
147
  flex-direction: column;
232
148
  gap: var(--auth-shell-card-stack-gap);
233
149
  }
234
150
 
235
- /* Login is an anchored three-part composition: AuthIdentity · Card · AuthFooter. The fixed
236
- * identity track absorbs absent, one-line and wrapped two-line requester states while its
237
- * content remains visually adjacent to the Card. This keeps real requester data intact without
238
- * allowing its line count to move the canonical SCR-001 card anchor (gh#237). */
239
151
  .ui-auth-shell[data-preset="login"] {
240
152
  --auth-shell-card-max-width: var(--auth-shell-login-card-max-width);
241
153
  --auth-shell-main-align: flex-start;
@@ -277,9 +189,6 @@
277
189
  text-align: center;
278
190
  }
279
191
 
280
- /* Device-authorization also owns its CODE FIELD (gh#12): the preset's subject is the code, so
281
- * the slot box comes from the preset instead of the generic square control tier. Scoped on the
282
- * shell so every OTP inside the device flow inherits it; a per-instance override still wins. */
283
192
  .ui-auth-shell[data-preset="device-authorization"] {
284
193
  --auth-shell-card-max-width: var(--auth-shell-device-card-max-width);
285
194
  --auth-shell-main-padding: var(--auth-shell-device-main-padding);
@@ -293,20 +202,11 @@
293
202
  --auth-shell-card-stack-gap: var(--auth-shell-context-card-stack-gap);
294
203
  }
295
204
 
296
- /* Account-recovery preset (gh#233) — one 432px measure for BOTH SCR-008 panels (password
297
- * recovery and the sign-in MFA challenge). Title + description live INSIDE the bordered surface
298
- * here, so the panel is `Card > CardHeader(CardTitle + CardDescription)` with no `AuthIdentity`
299
- * mark above it — the preset only re-measures the page, it never dictates content. */
300
205
  .ui-auth-shell[data-preset="account-recovery"] {
301
206
  --auth-shell-card-max-width: var(--auth-shell-recovery-card-max-width);
302
207
  --auth-shell-main-padding: var(--auth-shell-recovery-main-padding);
303
208
  }
304
209
 
305
- /* Registration preset (gh#256) — the sign-up measure. `flex-start` is the load-bearing line: a
306
- * registration card is the tallest surface in the hosted-identity set, and a vertically CENTRED
307
- * tall card overflows ABOVE the scroll origin on a short viewport, putting its first field out of
308
- * reach. Start-aligned, the block-start inset is ordinary page padding the user scrolls past, and
309
- * the block-end inset is the footer clearance the form keeps at the end of a long scroll. */
310
210
  .ui-auth-shell[data-preset="registration"] {
311
211
  --auth-shell-card-max-width: var(--auth-shell-registration-card-max-width);
312
212
  --auth-shell-main-align: flex-start;
@@ -316,11 +216,6 @@
316
216
  --auth-shell-card-stack-gap: var(--auth-shell-registration-card-stack-gap);
317
217
  }
318
218
 
319
- /* Fixed identity track — the mechanism `login` proved (gh#237), applied to the identity block
320
- * wherever it sits in the column rather than through a grid row, so the preset does not constrain
321
- * how many sections the page stacks. Content aligns to the slot END, so absent / one-line /
322
- * wrapped two-line identity copy changes only the empty space ABOVE it and never moves the card
323
- * off its canonical anchor. */
324
219
  .ui-auth-shell[data-preset="registration"] .ui-auth-shell-card > .ui-auth-identity {
325
220
  block-size: var(--auth-shell-registration-identity-slot-block-size);
326
221
  justify-content: flex-end;
@@ -391,7 +286,6 @@
391
286
  overflow: hidden;
392
287
  color: hsl(var(--foreground));
393
288
  font-size: var(--auth-account-summary-email-font-size);
394
- /* Declares its own line box because it CLIPS — see gh#254. */
395
289
  line-height: var(--auth-account-summary-email-line-height);
396
290
  font-weight: var(--font-weight-medium);
397
291
  text-overflow: ellipsis;
@@ -430,32 +324,12 @@
430
324
  }
431
325
  }
432
326
 
433
- /* INTENTIONALLY UNLAYERED (like density.css) — the Label primitive carries `text-sm leading-none`
434
- * Tailwind utility classes, and the utilities layer is declared after `components`, so a
435
- * components-layer declaration of the same properties can NEVER win regardless of specificity.
436
- * That left `--auth-shell-field-label-font-size` / `--auth-shell-field-label-line-height` dead and
437
- * the canonical field label rendering as a 14px/1.0 line box instead of the canonical 12px/1.5 =
438
- * 18px, sitting the email input 4px high (gh#263). Unlayered author CSS out-cascades every layer;
439
- * the two tokens remain the public tuning knobs. */
440
327
  .ui-auth-shell[data-variant="canonical"] [data-slot="form-field-label"] [data-slot="label"] {
441
328
  font-size: var(--auth-shell-field-label-font-size);
442
329
  line-height: var(--auth-shell-field-label-line-height);
443
330
  }
444
331
 
445
332
  @layer components {
446
- /*
447
- * Compact auth card density — the two card axes are wired to SEPARATE knobs (gh#232), so a
448
- * service can shorten the canonical Login card without narrowing its column (or vice versa):
449
- * inline column ← --auth-shell-compact-card-inset → --card-space-inset
450
- * block padding ← --auth-shell-card-padding-block-compact → --card-space-shell-y
451
- * header↔body ← --auth-shell-card-body-gap-compact → --card-space-body-y
452
- * solo body ← --auth-shell-card-padding-block-compact → --card-space-solo-y
453
- * The block knob is `initial` at :root, so its default resolves HERE against the live inset —
454
- * canonical output is byte-identical to pre-gh#232 while the knob is now actually consumed.
455
- * --card-space-solo-y is mapped EXPLICITLY as well as via the shell-y fallback: a solo body
456
- * takes both block edges symmetrically, and naming it here keeps that contract legible at the
457
- * one place a reader looks for the compact card's spacing.
458
- */
459
333
  .ui-auth-shell[data-density="compact"] .ui-auth-shell-card {
460
334
  --card-space-inset: var(--auth-shell-compact-card-inset);
461
335
  --card-space-shell-y: var(
@@ -470,13 +344,6 @@
470
344
  --card-space-gap: var(--auth-shell-card-gap-compact);
471
345
  }
472
346
 
473
- /* AuthDivider is the auth-scoped PRESET over the labelled `Separator` (gh#308). It no longer
474
- * re-implements the grid, the rule or the accessible-name contract — it only re-points
475
- * Separator's knobs at the `--auth-shell-divider-*` layer, so the auth micro-scale (11px, not
476
- * the stream's xs) and the auth rule/label colours stay exactly where a service already tunes
477
- * them, while a day divider in a message stream is no longer coupled to a login form.
478
- * `--separator-label-font-weight` is pinned to normal because the auth conjunction has always
479
- * inherited the body weight, whereas the generic labelled rule defaults to medium. */
480
347
  .ui-auth-divider {
481
348
  --separator-label-gap: var(--auth-shell-divider-gap);
482
349
  --separator-label-font-size: var(--auth-shell-divider-label-font-size);
@@ -497,9 +364,6 @@
497
364
  var(--auth-shell-login-main-padding-block-end);
498
365
  }
499
366
 
500
- /* Declared after the canonical mobile inset so a preset's mobile gutter wins at equal
501
- * specificity: device-authorization = 5px inline (card x=5, width=380 at 390px);
502
- * context-selection = edge-to-edge (0 inline). */
503
367
  .ui-auth-shell[data-preset="device-authorization"] {
504
368
  --auth-shell-main-padding: var(--auth-shell-device-main-padding-mobile);
505
369
  }
@@ -508,17 +372,10 @@
508
372
  --auth-shell-main-padding: var(--auth-shell-context-main-padding-mobile);
509
373
  }
510
374
 
511
- /* account-recovery = 15px inline at 390 (panel x=15, width=360). This is a DECIDED mobile
512
- * contract — the supplied 390 canonical reference is a desktop 2x2 composite that overflows
513
- * horizontally and is not a valid source (gh#233). */
514
375
  .ui-auth-shell[data-preset="account-recovery"] {
515
376
  --auth-shell-main-padding: var(--auth-shell-recovery-main-padding-mobile);
516
377
  }
517
378
 
518
- /* registration = 15px inline at 390 (card x=15, width=360 — the same page rhythm as Login, so
519
- * sign-in → sign-up never jumps on a phone). The block insets tighten but the footer clearance
520
- * stays deliberately larger than a bare gutter: at 390 the form is at its tallest and the
521
- * consent/legal footer must not sit flush against the submit button. */
522
379
  .ui-auth-shell[data-preset="registration"] {
523
380
  --auth-shell-main-padding: var(--auth-shell-registration-main-padding-block-start-mobile)
524
381
  var(--auth-shell-registration-main-padding-inline-mobile)
@@ -526,14 +383,6 @@
526
383
  }
527
384
  }
528
385
 
529
- /*
530
- * CENTERED SHELL — authenticated, no-sidebar, centred-column page shell (hosted-ID "My Page",
531
- * account / self-service, standalone settings). A flex column over the same brand-wash surface as
532
- * the app/auth shells: a padded top bar that MIRRORS `.app-topbar` chrome (fixed height + inline
533
- * padding + border + backdrop) but WITHOUT a sidebar grid, a scrollable `main` whose content is a
534
- * width-tiered centred column that is TOP-aligned (sections flow + scroll, never vertically
535
- * centred like the auth card), and an optional footer. Every knob reads a token.
536
- */
537
386
  .ui-centered-shell {
538
387
  display: flex;
539
388
  min-height: 100dvh;
@@ -551,7 +400,7 @@
551
400
  padding-inline: var(--centered-shell-bar-padding-x);
552
401
  border-bottom: 1px solid hsl(var(--border));
553
402
  background: hsl(var(--card));
554
- /* Opt-in brand-chrome wash — shares the topbar hook, invisible by default (none). */
403
+
555
404
  background-image: var(--topbar-gradient);
556
405
  }
557
406
 
@@ -561,9 +410,9 @@
561
410
  flex: 1 1 auto;
562
411
  flex-direction: column;
563
412
  overflow: auto;
564
- /* Ambient brand glow hook — shares the app-main hook, invisible by default (none). */
413
+
565
414
  background-image: var(--gradient-glow);
566
- /* Sole scroll/clip boundary for the page area (same containment rationale as `.app-main`). */
415
+
567
416
  contain: paint;
568
417
  padding: var(--centered-shell-main-padding);
569
418
  }
@@ -571,9 +420,7 @@
571
420
  .ui-centered-shell-column {
572
421
  width: 100%;
573
422
  margin-inline: auto;
574
- /* Block offset knob — `0` (top-aligned page, the default) or `auto` (viewport-centred system
575
- * surface via `align="center"`). Auto margins absorb the free space of the flex `main`, and
576
- * collapse to 0 when the content overflows, so tall content still scrolls from the top. */
423
+
577
424
  margin-block: var(--centered-shell-column-offset-block);
578
425
  max-width: var(--centered-shell-width-md);
579
426
  }
@@ -597,6 +444,20 @@
597
444
  color: hsl(var(--muted-foreground));
598
445
  }
599
446
 
447
+ @media (max-width: 720px) {
448
+ .ui-centered-shell-bar {
449
+ padding-inline: var(--centered-shell-bar-padding-x-compact);
450
+ }
451
+
452
+ .ui-centered-shell-main {
453
+ padding-inline: var(--centered-shell-main-padding-inline-compact);
454
+ }
455
+
456
+ .ui-centered-shell-footer {
457
+ padding-inline: var(--centered-shell-footer-padding-inline-compact);
458
+ }
459
+ }
460
+
600
461
  .app-topbar-rail {
601
462
  display: flex;
602
463
  min-width: 0;
@@ -617,22 +478,12 @@
617
478
  display: none;
618
479
  }
619
480
 
620
- /* AppShell-owned mobile drawer trigger — hidden on desktop, shown once the docked sidebar
621
- * collapses out at 900px, THE ONE AppShell breakpoint (gh#165, gh#259). NOT Tailwind's `lg`
622
- * (1024px): this comment said "below lg" for six minors while the shipped rule below and the
623
- * hamburger's own `max-[900px]:inline-flex` both fired at 900 — see the block comment on the
624
- * `@media (width <= 56.25rem)` rule for why 900 is the canonical value. The drawer nav itself is
625
- * a focus-trapped Sheet. */
626
481
  .app-mobile-nav-trigger {
627
482
  display: none;
628
483
  flex: 0 0 auto;
629
484
  margin-inline-end: var(--space-1);
630
485
  }
631
486
 
632
- /* Drawer scrim = a SHARE of the ONE shared semantic scrim (--overlay-background), resolved at the
633
- * call site so a scoped [data-tenant]/.dark override of --overlay-background reaches the portaled
634
- * drawer (docs/TOKENS.md · "Role-mirror knobs MUST be `initial`"). Default resolves to the same
635
- * rgb(0 0 0 / 0.2) it has always been; setting --app-shell-mobile-nav-background still wins. */
636
487
  [data-slot="sheet-overlay"].app-mobile-nav-overlay {
637
488
  background-color: var(
638
489
  --app-shell-mobile-nav-background,
@@ -648,13 +499,6 @@
648
499
  overscroll-behavior: contain;
649
500
  }
650
501
 
651
- /* The drawer nav reuses the docked Sidebar node; its `.sb-root { display: contents }` flattens
652
- * into the scrollable SheetBody so sections/footer flow naturally in the panel.
653
- * The body's INLINE inset is tokenized on the component itself
654
- * (`px-[var(--app-shell-mobile-nav-inset)]` in AppShell, which wins over SheetBody's generic
655
- * `px-[var(--sheet-pad-x)]` because both are utilities) — the nav owns its edge instead of being
656
- * double-padded (gh#211). Do NOT set `padding-inline` here: this file is `@layer components`, so
657
- * a Tailwind utility on the element always wins. */
658
502
  .app-mobile-nav-body {
659
503
  display: flex;
660
504
  flex-direction: column;
@@ -672,21 +516,6 @@
672
516
  font-size: var(--font-size-base);
673
517
  }
674
518
 
675
- /*
676
- * THE ONE AppShell breakpoint — 900px (56.25rem), the DXS canonical value. It is stated in exactly
677
- * two places and they must agree: this rule and `max-[900px]:inline-flex` on the hamburger in
678
- * `app-shell.tsx` (a CSS @media cannot read a custom property, so the trigger's visibility has to
679
- * repeat the number; making it a public knob needs a `data-shell-mode` attribute — tracked in the
680
- * #213 audit, out of scope here).
681
- *
682
- * There used to be a SECOND, conflicting `@media (max-width: 768px)` block further down that also
683
- * restructured `.app-root`. It (a) dropped the `"footer"` grid area and set `.app-footer{display:
684
- * none}`, silently deleting whatever a consumer passed to AppShell's `footer` slot on a phone, and
685
- * (b) re-declared the grid ROW template with a 3rem literal, overriding --app-shell-bar-height so
686
- * a themed bar height was ignored below 768px only. Between 768 and 900 the sidebar was hidden by
687
- * THIS rule while the grid still reserved a sidebar track — incoherent. Both are gone; the
688
- * footer row and the bar-height token now hold at every width.
689
- */
690
519
  @media (width <= 56.25rem) {
691
520
  .app-root,
692
521
  .app-root[data-collapsed="true"] {
@@ -701,12 +530,6 @@
701
530
  display: none;
702
531
  }
703
532
 
704
- /* Bar-less shell (data-topbar="none") at narrow widths: the bar comes back for the hamburger
705
- * ALONE — the only route to navigation once the docked rail is hidden. The `auto` row picks up
706
- * its height from the header's min-height, so no grid-template-rows is restated here, and none
707
- * may be: the one-breakpoint guard forbids a rows declaration inside this block precisely
708
- * because the last one hard-coded 3rem. Docked mode never reaches this rule — it renders no
709
- * drawer, so a bar-less docked shell has no header to show. */
710
533
  .app-root[data-topbar="none"] > .app-topbar {
711
534
  display: flex;
712
535
  }
@@ -719,9 +542,6 @@
719
542
  display: inline-flex;
720
543
  }
721
544
 
722
- /* Some approved application contracts retain their navigation rail at narrow widths. This is
723
- * an explicit AppShell mode, never a consumer media-query fork: the same sidebar node, footer,
724
- * active items and token-owned rail width remain in the canonical two-track grid. */
725
545
  .app-root[data-responsive-navigation="docked"] {
726
546
  grid-template-areas:
727
547
  "sidebar topbar"
@@ -734,8 +554,6 @@
734
554
  grid-template-columns: var(--app-shell-rail-width) minmax(0, 1fr);
735
555
  }
736
556
 
737
- /* Docked mode rebuilds the two-track grid at narrow widths, which would otherwise undo
738
- * topbarSpan="full" exactly where the bar's space-level controls matter most. */
739
557
  .app-root[data-responsive-navigation="docked"][data-topbar-span="full"] {
740
558
  grid-template-areas:
741
559
  "topbar topbar"
@@ -747,24 +565,11 @@
747
565
  display: flex;
748
566
  }
749
567
 
750
- /* (There is deliberately no `padding-inline` undo for docked mode any more. It existed to
751
- * cancel a bar inset that stepped on the SHELL's 900px restructure; since gh#330 the inset
752
- * steps on the PAGE's 720px step instead, and a docked page tightens its own gutters at that
753
- * same step — so undoing the bar here would re-open the misalignment the step exists to
754
- * keep closed.) */
755
568
  .app-root[data-responsive-navigation="docked"] .app-mobile-nav-trigger {
756
569
  display: none;
757
570
  }
758
571
  }
759
572
 
760
- /* The compact step of the HORIZONTAL PAGE-INSET AXIS (gh#330). It is deliberately NOT the shell's
761
- * 900px restructure breakpoint: the bar's inset is not a fact about whether a rail is docked, it
762
- * is a fact about where the page's content edge is — and the page steps its gutters to
763
- * --space-page-compact-x at exactly `(max-width: 720px)` (see styles/layout.css ·
764
- * `.ui-page-container`). Stated with the identical condition so the two sides can never drift
765
- * apart again; the shell's own restructure block above still owns everything that IS about the
766
- * rail. This block mentions `.app-topbar` only, so the one-breakpoint `.app-root` contract
767
- * (gh#213) is untouched. */
768
573
  @media (max-width: 720px) {
769
574
  .app-topbar {
770
575
  padding-inline: var(--app-shell-bar-inset-compact);
@@ -775,10 +580,6 @@
775
580
  display: contents;
776
581
  }
777
582
 
778
- /*
779
- * ORG SWITCHER — shared Console/Admin tenant control. Widths, insets and state geometry stay
780
- * inside the library so a consumer never needs `w-64` or descendant-selector overrides.
781
- */
782
583
  .ui-org-switcher {
783
584
  width: 100%;
784
585
  min-width: 0;
@@ -920,7 +721,6 @@
920
721
  background: hsl(var(--accent));
921
722
  }
922
723
 
923
- /* Non-interactive brand header — no click target, so no pointer/hover affordance and no caret. */
924
724
  .sb-product-static,
925
725
  .sb-product-static:hover {
926
726
  cursor: default;
@@ -992,16 +792,6 @@
992
792
  margin-top: var(--sidebar-section-gap);
993
793
  }
994
794
 
995
- /* The section label is the ONE piece of sidebar text with no colour knob.
996
- * Every other one has a variable with this same fallback — item, hover,
997
- * active, icon — and the sidebar is paintable by design (`--sidebar-gradient`
998
- * is a published knob). So a consumer that paints it can recolour every label
999
- * except this heading, which stays muted-foreground and lands on whatever
1000
- * they painted: measured at 1.50 against a mid-tone blue, where 11px text
1001
- * needs 4.5.
1002
- *
1003
- * The fallback is the old value, so nothing changes for a sidebar that does
1004
- * not paint itself. */
1005
795
  .sb-section-label {
1006
796
  padding: 0 var(--sidebar-section-label-padding-x) var(--sidebar-section-label-padding-bottom);
1007
797
  color: var(--sidebar-section-label-foreground, hsl(var(--muted-foreground)));
@@ -1030,9 +820,7 @@
1030
820
  border: 0;
1031
821
  border-radius: var(--sidebar-nav-item-radius);
1032
822
  background: transparent;
1033
- /* gh#228 — the row/label foreground is its own knob (default = the muted role, unchanged). The
1034
- * icon reads a SEPARATE knob on `.sb-icon` below, so a service can darken 16px nav icons
1035
- * without re-tinting every muted text. */
823
+
1036
824
  color: var(--sidebar-nav-item-foreground, hsl(var(--muted-foreground)));
1037
825
  cursor: pointer;
1038
826
  font-family: inherit;
@@ -1041,18 +829,6 @@
1041
829
  text-align: start;
1042
830
  }
1043
831
 
1044
- /* Every nav row is a CONTROL, so it owes a visible keyboard indicator (WCAG 2.4.7) — and it
1045
- * has to be the design system's, not the UA's. Measured in Chromium before this rule: the
1046
- * first Tab into the shell landed on a rail item drawing Chrome's own
1047
- * `outline: rgb(0,95,204) auto 1px`, a blue that belongs to no theme and matches nothing else
1048
- * on the page, while every Button beside it drew the token ring. On a Slack-style shell that
1049
- * is the FIRST thing a keyboard user sees.
1050
- *
1051
- * Same shape as `.app-main:focus-visible` above — suppress the UA outline, draw a token ring —
1052
- * but NOT gated behind an opt-in width the way the region ring is. A whole-content-area frame
1053
- * reads as a glitch and defaults off; a 2px ring hugging one 32px row is exactly the
1054
- * affordance, so it reads the global `--focus-ring-width` and is on by default. `box-shadow`
1055
- * rather than `outline` so the ring follows `--sidebar-nav-item-radius`. */
1056
832
  .sb-nav-item:focus-visible {
1057
833
  outline: none;
1058
834
  box-shadow: 0 0 0 var(--focus-ring-width) hsl(var(--focus-ring-color, var(--ring)));
@@ -1063,9 +839,6 @@
1063
839
  color: var(--sidebar-nav-item-hover-foreground, hsl(var(--foreground)));
1064
840
  }
1065
841
 
1066
- /* Active item reads its own bg/fg tokens (defaults = the hover look) so a service can give the
1067
- * selected nav row a brand treatment — e.g. a gold tint + gold text on a navy sidebar — without
1068
- * forking CSS. Defaults are byte-identical to the hover state. */
1069
842
  .sb-nav-item[data-active="true"] {
1070
843
  background: var(--sidebar-item-active-background, hsl(var(--accent)));
1071
844
  color: var(--sidebar-item-active-foreground, hsl(var(--foreground)));
@@ -1079,11 +852,7 @@
1079
852
  flex: 0 0 auto;
1080
853
  line-height: 0;
1081
854
  place-items: center;
1082
- /* gh#228 — the nav ICON owns its own foreground knob; the default `currentColor` resolves to the
1083
- * row colour, so the resting look is byte-identical to before. A service sets
1084
- * `--sidebar-nav-icon-foreground` alone to get canonical darker icons beside muted labels — no
1085
- * page-local selector, no global `--muted-foreground` re-tint. Colour only: size/geometry stay
1086
- * on --sidebar-nav-icon-size. */
855
+
1087
856
  color: var(--sidebar-nav-icon-foreground, currentColor);
1088
857
  }
1089
858
 
@@ -1098,19 +867,12 @@
1098
867
  min-width: 0;
1099
868
  flex: 1;
1100
869
  overflow: hidden;
1101
- /* gh#254 — MUST NOT inherit the row's `line-height: 1`. This element clips, so its line box is
1102
- * its clip box, and a 1em box is shorter than the font's ascent+descent: every descender and
1103
- * Vietnamese tone mark below the baseline gets sheared off. Knob, not constant (rule #45). */
870
+
1104
871
  line-height: var(--sidebar-nav-item-line-height);
1105
872
  text-overflow: ellipsis;
1106
873
  white-space: nowrap;
1107
874
  }
1108
875
 
1109
- /* The nav row's count pill. Its COLOUR is a knob pair, and the resting values are exactly the
1110
- * literals it carried before (rule #44/#45) — `initial` at the token tier so the role defaults
1111
- * re-resolve under a scoped theme, resolved here at the call site. Before the knobs the only
1112
- * way to emphasise a row was to nest a `<Badge>` INSIDE `badge`, which produced two stacked
1113
- * pills: a 37.11×19.14 `.sb-badge` wrapping a 25.11×19.14 `<Badge>` with its own border. */
1114
876
  .sb-badge {
1115
877
  display: inline-flex;
1116
878
  min-width: 1.125rem;
@@ -1125,12 +887,6 @@
1125
887
  padding-inline: 0.375rem;
1126
888
  }
1127
889
 
1128
- /* `badgeTone="destructive"` — the count is ADDRESSED TO YOU (an @mention, a direct message),
1129
- * not merely unread. Present-when-on (rule #44): a neutral row emits no `data-tone` at all, so
1130
- * this rule cannot reach it and the default pill is byte-identical. GEOMETRY IS UNTOUCHED — the
1131
- * tone moves two colours and nothing else, which is the whole point of the axis: the row's
1132
- * height, the pill's radius and its 0.375rem inline pad stay on the base rule, so a mention row
1133
- * and an unread row still line up in the same column. */
1134
890
  .sb-badge[data-tone="destructive"] {
1135
891
  background: var(--sidebar-badge-destructive-background, hsl(var(--destructive)));
1136
892
  color: var(--sidebar-badge-destructive-foreground, hsl(var(--destructive-foreground)));
@@ -1142,7 +898,6 @@
1142
898
  padding: var(--space-2);
1143
899
  }
1144
900
 
1145
- /* Collapsible group trigger chevron (rotates when open). */
1146
901
  .sb-nav-group-trigger .sb-chevron {
1147
902
  margin-inline-start: auto;
1148
903
  flex: 0 0 auto;
@@ -1154,8 +909,6 @@
1154
909
  transform: rotate(180deg);
1155
910
  }
1156
911
 
1157
- /* Nested rows (submenu) — left guide line + dot markers, design `.sb-nest`.
1158
- * Active child reads as primary-colored text, never a filled block. */
1159
912
  .sb-nav-sub {
1160
913
  display: flex;
1161
914
  flex-direction: column;
@@ -1168,8 +921,7 @@
1168
921
  .sb-nav-item--sub {
1169
922
  height: 1.75rem;
1170
923
  font-size: var(--sidebar-nav-sub-font-size);
1171
- /* Same row knob as a top-level row (gh#228) — one override themes the whole nav, sub rows
1172
- * included. Default unchanged. */
924
+
1173
925
  color: var(--sidebar-nav-item-foreground, hsl(var(--muted-foreground)));
1174
926
  }
1175
927
 
@@ -1199,11 +951,6 @@
1199
951
  opacity: 1;
1200
952
  }
1201
953
 
1202
- /* gh#228 — nav ICON state knobs. Declared AFTER the row-state rules so they win at equal
1203
- * specificity, in the same order the row states resolve (hover → active → disabled). Each falls
1204
- * back to the base icon knob and finally to `currentColor` (= the row colour for that state), so
1205
- * the defaults are byte-identical to the pre-gh#228 rendering and setting only
1206
- * `--sidebar-nav-icon-foreground` themes every state at once. Colour only — no geometry. */
1207
954
  .sb-nav-item:hover .sb-icon {
1208
955
  color: var(
1209
956
  --sidebar-nav-icon-hover-foreground,
@@ -1218,9 +965,6 @@
1218
965
  );
1219
966
  }
1220
967
 
1221
- /* Disabled rows (`aria-disabled="true"`, set by SidebarItem for item.disabled). Defaults resolve
1222
- * to the RESTING row/icon colour, so a disabled row reads as inert in every state; a service dims
1223
- * it further with the two disabled knobs. */
1224
968
  .sb-nav-item[aria-disabled="true"] {
1225
969
  color: var(
1226
970
  --sidebar-nav-item-disabled-foreground,
@@ -1235,8 +979,6 @@
1235
979
  );
1236
980
  }
1237
981
 
1238
- /* Collapsed-rail flyout — PORTALED (Radix Popover) so it escapes the sidebar's overflow and is
1239
- * never clipped: names a leaf item (tooltip) or reveals a group's submenu on hover/focus. */
1240
982
  .sb-flyout-pop {
1241
983
  display: flex;
1242
984
  width: auto;
@@ -1254,7 +996,6 @@
1254
996
  white-space: nowrap;
1255
997
  }
1256
998
 
1257
- /* Footer user — circular avatar with live dot + overflow menu, design `.sb-user`. */
1258
999
  .sb-user {
1259
1000
  display: flex;
1260
1001
  width: 100%;
@@ -1377,30 +1118,6 @@
1377
1118
  gap: 2px;
1378
1119
  }
1379
1120
 
1380
- /* Topbar — a pure slot bar. start (inline-start) · center (grows, centered) · end (inline-end).
1381
- * The library owns ONLY this positioning; every control inside is the consumer's to compose.
1382
- *
1383
- * SHRINK CONTRACT (gh#226) — a hosted console packs an intrinsically wide brand/tenant string
1384
- * into `start` and a fixed-width search trigger into `center`; at a compact desktop width
1385
- * (1024px minus a 16rem sidebar) that content used to push `end` (locale picker, user menu) past
1386
- * the bar and out of the viewport, and leak a horizontal document scroll. The contract is:
1387
- * root — never wider than its AppShell grid/flex allocation (`max-width:100%` +
1388
- * `min-width:0`, so it shrinks whether the parent is a grid track or a flex line);
1389
- * start — shrinks first, its content clipped INSIDE the slot (never spills over center/end);
1390
- * center — `flex-basis:0`, so it yields all of its box before `start` gives up a pixel;
1391
- * end — `flex:0 0 auto`, so it keeps its natural width and stays anchored inline-end.
1392
- * Slots clip with `clip`, NOT `hidden`: a `hidden` box is still a scroll container, so focusing a
1393
- * clipped control would scroll the slot and shove its leading content out of view. The clip
1394
- * margin keeps the focus ring of an edge control paintable (WCAG 2.4.11 / 2.4.13).
1395
- *
1396
- * BOX KNOBS (rule #45) — --topbar-height / --topbar-inset / --topbar-gap. Their defaults are the
1397
- * QUIET ones (`auto` / `0` / the previous 8px), i.e. byte-identical rendering to before they
1398
- * existed: inside AppShell the `.app-topbar` grid row still owns the height and inset. A service
1399
- * that mounts <Topbar> DIRECTLY on a page (no AppShell) now sizes it from the theme —
1400
- * `--topbar-height: 3.5rem; --topbar-inset: var(--space-4)` — instead of an app-local class.
1401
- * `--topbar-gap` re-rhythms the whole bar at once: it is both the gap BETWEEN the three clusters
1402
- * and the gap INSIDE each of them, so a denser bar stays internally consistent.
1403
- * The shrink contract above is unaffected — none of these touch flex/overflow. */
1404
1121
  .ui-topbar {
1405
1122
  display: flex;
1406
1123
  height: var(--topbar-height);
@@ -1411,17 +1128,7 @@
1411
1128
  align-items: center;
1412
1129
  gap: var(--topbar-gap);
1413
1130
  padding-inline: var(--topbar-inset);
1414
- /* Ring headroom contract (gh#291), measured empirically:
1415
- * - Chromium honours overflow-clip-margin ONLY when BOTH axes are `clip`;
1416
- * a single-axis clip silently ignores the margin, and a flush-edge
1417
- * control's ring vanishes on the clipped axis.
1418
- * - `hidden` can never work here (no clip-margin, and pairing it with
1419
- * `visible` computes to `auto`, which still clips).
1420
- * - The margin must be a bare var() — Chromium drops calc() at parse time.
1421
- * So: clip BOTH axes and let the margin carry the ring on every side. The
1422
- * gh#226 shrink/truncation contract is intact (content clips 8px outside
1423
- * the box). Safari (clip yes, clip-margin no) falls back to `visible`
1424
- * below. */
1131
+
1425
1132
  overflow: clip;
1426
1133
  overflow-clip-margin: var(--focus-ring-clip-margin);
1427
1134
  }
@@ -1432,17 +1139,10 @@
1432
1139
  min-width: 0;
1433
1140
  align-items: center;
1434
1141
  gap: var(--topbar-gap);
1435
- /* Same both-axes-clip + margin contract as .ui-topbar above (gh#291): the
1436
- * 8px margin covers the 3px ring on every side of a flush-edge control;
1437
- * single-axis clip would silently drop the margin in Chromium. */
1438
1142
  overflow: clip;
1439
1143
  overflow-clip-margin: var(--focus-ring-clip-margin);
1440
1144
  }
1441
- /* Safari implements `overflow: clip` but NOT overflow-clip-margin, so the
1442
- * both-axes clip above would eat every flush-edge focus ring there with no
1443
- * margin to save it. Trade slot containment for ring visibility on that
1444
- * engine only — the .app-topbar horizontal clip still prevents bar blowout
1445
- * and document scroll. */
1145
+
1446
1146
  @supports not (overflow-clip-margin: 1px) {
1447
1147
  .app-sidebar,
1448
1148
  .app-topbar,
@@ -1461,7 +1161,6 @@
1461
1161
  min-width: 0;
1462
1162
  flex: 0 1 auto;
1463
1163
  overflow: hidden;
1464
- /* Declares its own line box because it CLIPS — see gh#254. */
1465
1164
  line-height: var(--topbar-start-truncate-line-height);
1466
1165
  text-overflow: ellipsis;
1467
1166
  white-space: nowrap;
@@ -1476,11 +1175,6 @@
1476
1175
  margin-inline-start: auto;
1477
1176
  }
1478
1177
 
1479
- /* COLLISION CONTRACT (gh#244) — with a docked 16rem sidebar, 901–1100px viewports do not have
1480
- * enough room for a breadcrumb/title, full search trigger and end utilities. The center slot is
1481
- * optional by definition, so remove it before it can cover either owned edge. This also gives
1482
- * phones the same deterministic allocation. A consumer with a real compact center presentation
1483
- * can opt it back in through the public token instead of a page-local media query. */
1484
1178
  @media (width <= 68.75rem) {
1485
1179
  .ui-topbar-center {
1486
1180
  display: var(--topbar-center-compact-display);
@@ -1516,7 +1210,6 @@
1516
1210
  color: hsl(var(--muted-foreground));
1517
1211
  }
1518
1212
 
1519
- /* Non-interactive brand chip — no menu/handler, so no pointer/hover affordance and no caret. */
1520
1213
  .tb-chip-static,
1521
1214
  .tb-chip-static:hover {
1522
1215
  border-color: transparent;
@@ -1540,9 +1233,7 @@
1540
1233
  min-width: 0;
1541
1234
  overflow: hidden;
1542
1235
  font-weight: 500;
1543
- /* gh#254 — same trap as `.sb-label`: this clips, so it must own a line box tall enough for the
1544
- * font's descenders. `.tb-chip` uses `font: inherit`, so without this the chip would shear its
1545
- * glyphs the moment a service set a tight line-height on the bar. */
1236
+
1546
1237
  line-height: 1.5;
1547
1238
  text-overflow: ellipsis;
1548
1239
  white-space: nowrap;
@@ -1560,11 +1251,7 @@
1560
1251
 
1561
1252
  .tb-search {
1562
1253
  display: flex;
1563
- /* Fills the whole `Topbar` center slot by default (gh#296) — `--topbar-search-max-width`
1564
- * defaults to `none`, so nothing caps it short of `end`. A consumer opting into the older
1565
- * fixed-width, centered look sets the token to an explicit length instead of overriding this
1566
- * rule. `width: 100%` + `max-width: none` is the pair that makes both cases work from ONE
1567
- * token: a set length caps it, an unset one (`none`) doesn't. */
1254
+
1568
1255
  width: 100%;
1569
1256
  max-width: var(--topbar-search-max-width);
1570
1257
  height: var(--control-height-sm);
@@ -1637,12 +1324,6 @@
1637
1324
  background: hsl(var(--attention));
1638
1325
  }
1639
1326
 
1640
- /* TOPBAR CHIP/SEARCH DENSITY ONLY — this is NOT a second shell breakpoint. The `.app-root`
1641
- * restructure that used to live here (a 768px duplicate of the 900px rule that also deleted the
1642
- * footer row and re-declared the grid rows with a 3rem literal) is gone; see the note on the one
1643
- * `@media (width <= 56.25rem)` block above. What remains is purely the optional `.tb-*` console
1644
- * chrome collapsing to icon-only on a phone, which is independent of where the shell switches to
1645
- * its drawer. */
1646
1327
  @media (max-width: 768px) {
1647
1328
  .tb-chip-sep,
1648
1329
  .tb-search .kbd {
@@ -1660,15 +1341,6 @@
1660
1341
  }
1661
1342
  }
1662
1343
 
1663
- /*
1664
- * REDUCED MOTION for the AppShell mobile drawer (WCAG 2.3.3 / 2.2.2). The drawer is a Sheet, so it
1665
- * animates via Tailwind `data-[state]:animate-in|animate-out` + `slide-in-from-*` utilities.
1666
- * DELIBERATELY UNLAYERED: Tailwind v4 emits those into `@layer utilities`, which beats any rule in
1667
- * `@layer components` regardless of specificity — this gate was previously written inside the
1668
- * components layer above and could never win. Same reasoning as the overlay gate in
1669
- * dialog-layout.css (which also covers this drawer via [data-slot="sheet-*"]); kept here as well so
1670
- * a consumer importing only `styles/shell-layout` still gets it.
1671
- */
1672
1344
  @media (prefers-reduced-motion: reduce) {
1673
1345
  .app-mobile-nav-overlay,
1674
1346
  .app-mobile-nav-drawer {
@@ -1677,18 +1349,6 @@
1677
1349
  }
1678
1350
  }
1679
1351
 
1680
- /*
1681
- * ERROR SURFACE — the 403 / 404 / 500 / 503 exception surface (gh#221, gh#251).
1682
- *
1683
- * Geometry is PACKAGE-OWNED at 1440 / 1024 / 390: a consumer never writes `min-h-dvh`, a
1684
- * flex-centring class or a media query for an error page. In `system` mode the viewport centring
1685
- * comes from `CenteredShell align="center"` (which the component renders itself); this block owns
1686
- * the surface block that is identical in BOTH modes — measure, rhythm, metadata list.
1687
- *
1688
- * The narrow step is a CONTAINER query, not a viewport one (the SplitPane precedent, gh#165): the
1689
- * surface reads its OWN width, so it compacts correctly whether the shrinking came from a 390px
1690
- * phone or from an application shell whose sidebar is expanded on a laptop.
1691
- */
1692
1352
  @layer components {
1693
1353
  .ui-error-surface {
1694
1354
  container: error-surface / inline-size;
@@ -1697,7 +1357,7 @@
1697
1357
  max-width: var(--error-surface-max-width);
1698
1358
  flex-direction: column;
1699
1359
  align-items: center;
1700
- /* Logical, so the surface stays centred and its text start-aligns correctly under `dir="rtl"`. */
1360
+
1701
1361
  margin-inline: auto;
1702
1362
  gap: var(--error-surface-gap);
1703
1363
  padding-block: var(--error-surface-padding-block);
@@ -1710,9 +1370,6 @@
1710
1370
  padding-block-end: var(--error-surface-brand-gap);
1711
1371
  }
1712
1372
 
1713
- /* The EmptyState body already owns its own vertical rhythm and description measure
1714
- * (--empty-state-*); the surface only removes the duplicated block padding so the two spacing
1715
- * systems do not stack into a hole between the status code and the icon. */
1716
1373
  .ui-error-surface .ui-error-surface-body {
1717
1374
  width: 100%;
1718
1375
  padding-block: 0;
@@ -1727,7 +1384,6 @@
1727
1384
  width: 100%;
1728
1385
  flex-direction: column;
1729
1386
  gap: var(--error-surface-meta-gap);
1730
- /* Chrome is a token, default quiet (rule #44) — `none` unless a service opts in. */
1731
1387
  border-block-start: var(--error-surface-meta-border);
1732
1388
  padding-block-start: var(--error-surface-meta-padding-block);
1733
1389
  margin: 0;
@@ -1749,10 +1405,6 @@
1749
1405
  max-width: var(--error-surface-progress-max-width);
1750
1406
  }
1751
1407
 
1752
- /*
1753
- * NARROW STEP (the 390 case). Reclaim vertical space and stop a long ja / vi metadata row from
1754
- * wrapping into a ragged two-column shape: each label stacks over its value, centred.
1755
- */
1756
1408
  @container error-surface (width < 30rem) {
1757
1409
  .ui-error-surface {
1758
1410
  padding-block: var(--error-surface-padding-block-compact);
@@ -1766,24 +1418,11 @@
1766
1418
  }
1767
1419
 
1768
1420
  @layer components {
1769
- /* ── CenteredShell · `preset="public-landing"` (gh#252) ──────────────────────────────────────
1770
- * A PUBLIC landing page (marketing header · hero · sections · legal footer) is a COMPOSITION,
1771
- * not a framework component (docs/COMPOSITION-VS-COMPONENT.md: Hero / Navbar / Footer / Pricing
1772
- * all FAIL the Framework-Component Test). What the consumer must NOT own is the SHELL geometry —
1773
- * the content measure, the header/footer measure alignment, the section rhythm and the flat
1774
- * public-surface chrome. This preset owns all of it through `--centered-shell-landing-*` knobs,
1775
- * so a landing composes real primitives with ZERO page-local CSS and zero descendant selectors.
1776
- *
1777
- * `preset="default"` emits NO `data-preset` attribute at all, so none of this can match and every
1778
- * existing CenteredShell keeps its exact box (the gh#231 inert-default contract). */
1421
+
1779
1422
  .ui-centered-shell[data-preset="public-landing"] {
1780
1423
  --centered-shell-main-padding: var(--centered-shell-landing-main-padding-block)
1781
1424
  var(--centered-shell-landing-inset-inline);
1782
- /* The hero title is a real `Heading level={1}`; the preset re-points the h1 tier the same way
1783
- * `.ui-auth-shell` re-points it — never a consumer font-size. */
1784
1425
  --heading-h1: var(--centered-shell-landing-heading-size);
1785
- /* Flat public surface: every Card in the subtree loses its elevation from ONE knob instead of
1786
- * a consumer `.landing .card { box-shadow: none }` descendant selector. */
1787
1426
  --card-shadow: var(--centered-shell-landing-card-shadow);
1788
1427
  background: hsl(var(--centered-shell-landing-background, var(--background)));
1789
1428
  }
@@ -1792,13 +1431,10 @@
1792
1431
  display: flex;
1793
1432
  max-width: var(--centered-shell-landing-max-width);
1794
1433
  flex-direction: column;
1795
- /* Section rhythm — the consumer stacks plain `<section>` layout elements and never sets a gap. */
1434
+
1796
1435
  gap: var(--centered-shell-landing-section-gap);
1797
1436
  }
1798
1437
 
1799
- /* Header and footer share the column's measure WITHOUT a consumer max-width wrapper: the inline
1800
- * padding is the page gutter until the viewport is wider than the measure, then it grows so the
1801
- * bar content starts on exactly the same inline edge as the centred column. */
1802
1438
  .ui-centered-shell[data-preset="public-landing"] .ui-centered-shell-bar,
1803
1439
  .ui-centered-shell[data-preset="public-landing"] .ui-centered-shell-footer {
1804
1440
  padding-inline: max(
@@ -1812,8 +1448,6 @@
1812
1448
  text-align: start;
1813
1449
  }
1814
1450
 
1815
- /* Compact step (the 390px acceptance width sits here): the gutter, the section rhythm and the
1816
- * hero tier switch to their compact knobs. One media query, owned by the package. */
1817
1451
  @media (width < 40rem) {
1818
1452
  .ui-centered-shell[data-preset="public-landing"] {
1819
1453
  --centered-shell-landing-inset-inline: var(--centered-shell-landing-inset-inline-compact);