@godxjp/ui 19.2.0 → 19.4.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 (144) hide show
  1. package/dist/components/data-display/badge.d.ts +26 -2
  2. package/dist/components/data-display/badge.js +20 -5
  3. package/dist/components/data-display/index.d.ts +2 -0
  4. package/dist/components/data-display/index.js +2 -0
  5. package/dist/components/data-display/popover.d.ts +13 -1
  6. package/dist/components/data-display/popover.js +3 -1
  7. package/dist/components/data-display/table.d.ts +19 -2
  8. package/dist/components/data-display/table.js +4 -1
  9. package/dist/components/data-display/timeline-grid.d.ts +18 -0
  10. package/dist/components/data-display/timeline-grid.js +222 -0
  11. package/dist/components/data-entry/cascader.js +4 -6
  12. package/dist/components/data-entry/checkbox.js +1 -1
  13. package/dist/components/data-entry/search-select.js +5 -7
  14. package/dist/components/data-entry/select.js +32 -28
  15. package/dist/components/data-entry/tree-select.js +4 -6
  16. package/dist/components/general/inert-background.d.ts +5 -0
  17. package/dist/components/general/inert-background.js +48 -0
  18. package/dist/components/layout/auth-shell.d.ts +4 -2
  19. package/dist/components/layout/auth-shell.js +7 -1
  20. package/dist/components/layout/centered-shell.d.ts +2 -2
  21. package/dist/components/layout/flex.d.ts +1 -1
  22. package/dist/components/layout/flex.js +2 -1
  23. package/dist/components/layout/index.d.ts +3 -0
  24. package/dist/components/layout/index.js +2 -0
  25. package/dist/components/layout/mobile-shell.d.ts +21 -0
  26. package/dist/components/layout/mobile-shell.js +43 -0
  27. package/dist/components/layout/sidebar-link.d.ts +1 -1
  28. package/dist/components/navigation/context-menu.js +13 -9
  29. package/dist/components/navigation/dropdown-menu.js +17 -13
  30. package/dist/inertia/index.d.ts +1 -1
  31. package/dist/lib/variants.d.ts +3 -3
  32. package/dist/lib/variants.js +1 -0
  33. package/dist/props/components/data-display.prop.d.ts +72 -0
  34. package/dist/props/components/data-entry.prop.d.ts +1 -1
  35. package/dist/props/components/layout.prop.d.ts +93 -6
  36. package/dist/props/registry.d.ts +113 -2
  37. package/dist/props/registry.js +155 -1
  38. package/dist/props/vocabulary/data.prop.d.ts +6 -0
  39. package/dist/props/vocabulary/index.d.ts +3 -3
  40. package/dist/props/vocabulary/layout.prop.d.ts +17 -2
  41. package/dist/props/vocabulary/shared.prop.d.ts +7 -0
  42. package/dist/styles/badge-layout.css +15 -0
  43. package/dist/styles/control.css +18 -1
  44. package/dist/styles/data-display-layout.css +167 -0
  45. package/dist/styles/layout.css +7 -0
  46. package/dist/styles/shell-layout.css +148 -0
  47. package/dist/styles/table-layout.css +23 -4
  48. package/dist/tokens/components/badge.css +8 -0
  49. package/dist/tokens/components/control.css +1 -0
  50. package/dist/tokens/components/data-display.css +20 -0
  51. package/dist/tokens/components/shell.css +22 -0
  52. package/dist/tokens/components/table.css +2 -0
  53. package/docs/SPACING.md +19 -6
  54. package/docs/charts/compact-bar-trend.tsx +5 -6
  55. package/docs/data-display/avatar.tsx +7 -9
  56. package/docs/data-display/badge.tsx +37 -1
  57. package/docs/data-display/card/index.tsx +6 -4
  58. package/docs/data-display/carousel.tsx +8 -4
  59. package/docs/data-display/data-table/examples/approval-queue.tsx +1 -1
  60. package/docs/data-display/data-table/examples/grid-features.tsx +1 -1
  61. package/docs/data-display/data-table/index.tsx +1 -1
  62. package/docs/data-display/permission-matrix.tsx +15 -11
  63. package/docs/data-display/popover.tsx +2 -1
  64. package/docs/data-display/scroll-area.tsx +108 -76
  65. package/docs/data-display/table.tsx +2 -2
  66. package/docs/data-display/timeline-grid.tsx +119 -0
  67. package/docs/data-entry/branch-scope-picker.tsx +1 -1
  68. package/docs/data-entry/calendar.tsx +1 -1
  69. package/docs/data-entry/checkbox.tsx +1 -1
  70. package/docs/data-entry/country-picker-recipe.tsx +11 -6
  71. package/docs/data-entry/form-field/index.tsx +33 -29
  72. package/docs/data-entry/form-root.tsx +1 -1
  73. package/docs/data-entry/input-otp.tsx +1 -1
  74. package/docs/data-entry/password-input.tsx +1 -5
  75. package/docs/data-entry/select.tsx +7 -7
  76. package/docs/data-entry/textarea.tsx +3 -3
  77. package/docs/data-entry/toggle-count.tsx +1 -1
  78. package/docs/feedback/alert.tsx +1 -1
  79. package/docs/feedback/banner.tsx +1 -1
  80. package/docs/feedback/sheet.tsx +112 -102
  81. package/docs/feedback/skeleton.tsx +2 -2
  82. package/docs/foundation/colors.tsx +8 -6
  83. package/docs/foundation/email-tokens.tsx +83 -66
  84. package/docs/foundation/radius-shadow.tsx +13 -11
  85. package/docs/general/activity.tsx +1 -1
  86. package/docs/layout/app-shell.tsx +2 -2
  87. package/docs/layout/aspect-ratio.tsx +30 -14
  88. package/docs/layout/auth-account-summary.tsx +1 -1
  89. package/docs/layout/auth-recovery/examples/mobile-390.tsx +1 -1
  90. package/docs/layout/auth-recovery/examples/password-recovery.tsx +0 -2
  91. package/docs/layout/auth-shell-context.tsx +1 -1
  92. package/docs/layout/auth-shell-device.tsx +2 -2
  93. package/docs/layout/auth-shell-registration.tsx +1 -1
  94. package/docs/layout/auth-shell.tsx +1 -1
  95. package/docs/layout/flex.tsx +6 -2
  96. package/docs/layout/master-detail.tsx +2 -2
  97. package/docs/layout/mobile-shell.tsx +101 -0
  98. package/docs/layout/page-container.tsx +15 -7
  99. package/docs/layout/responsive-grid.tsx +12 -4
  100. package/docs/layout/separator.tsx +2 -2
  101. package/docs/layout/service-role-panel.tsx +1 -1
  102. package/docs/layout/sidebar.tsx +175 -152
  103. package/docs/layout/split-pane.tsx +45 -28
  104. package/docs/layout/topbar.tsx +19 -23
  105. package/docs/navigation/app-setting-picker.tsx +5 -4
  106. package/docs/navigation/breadcrumb.tsx +12 -5
  107. package/docs/navigation/context-menu.tsx +10 -4
  108. package/docs/navigation/filter-bar.tsx +2 -2
  109. package/docs/navigation/navigation-menu.tsx +89 -96
  110. package/docs/navigation/pagination.tsx +2 -2
  111. package/docs/navigation/steps.tsx +13 -11
  112. package/docs/navigation/tabs.tsx +27 -21
  113. package/docs/navigation/toolbar.tsx +15 -15
  114. package/docs/providers/format-date.tsx +2 -2
  115. package/docs/showcase/acme-portal.tsx +41 -28
  116. package/docs/showcase/acme-website.tsx +67 -61
  117. package/docs/showcase/case1-warehouse-dashboard.tsx +46 -31
  118. package/docs/showcase/case2-employee-me.tsx +33 -36
  119. package/docs/showcase/case3-approval-workflow.tsx +34 -32
  120. package/docs/showcase/case4-login.tsx +95 -101
  121. package/docs/showcase/case5-shift-calendar.tsx +182 -281
  122. package/docs/showcase/case6-agency-handy.tsx +514 -470
  123. package/docs/showcase/futurelastic-web.tsx +32 -29
  124. package/docs/showcase/org-switcher.tsx +20 -13
  125. package/docs/showcase/permission-matrix.tsx +20 -17
  126. package/docs/showcase/public-landing.tsx +1 -1
  127. package/docs/showcase/settings-account-sections.tsx +2 -2
  128. package/docs/showcase/settings-security-mfa.tsx +3 -3
  129. package/docs/showcase/table-approval-queue.tsx +1 -1
  130. package/docs/showcase/table-bulk-actions.tsx +49 -52
  131. package/docs/showcase/table-conditional-format.tsx +9 -9
  132. package/docs/showcase/table-crud-list.tsx +4 -4
  133. package/docs/showcase/table-density.tsx +49 -42
  134. package/docs/showcase/table-expandable-rows.tsx +112 -100
  135. package/docs/showcase/table-filter-chips.tsx +78 -71
  136. package/docs/showcase/table-footer-totals.tsx +30 -23
  137. package/docs/showcase/table-grouped-subtotals.tsx +31 -25
  138. package/docs/showcase/table-master-detail.tsx +27 -24
  139. package/docs/showcase/table-pagination.tsx +34 -27
  140. package/docs/showcase/table-states.tsx +42 -34
  141. package/docs/showcase/table-sticky-columns.tsx +11 -7
  142. package/docs/showcase/table-tree-rows.tsx +22 -15
  143. package/docs/showcase/table-view-tabs.tsx +12 -14
  144. package/package.json +7 -7
@@ -1,7 +1,7 @@
1
1
  /** Layout component prop types — @see docs/COMPONENTS.md#layout */
2
2
  import type * as React from "react";
3
3
  import type { ComponentType, ReactNode, SVGProps } from "react";
4
- import type { BreadcrumbProp, TitleProp, SubtitleProp, StatusProp, ExtraProp, FooterProp, PageDensityProp, PageContainerVariantProp, CenteredShellWidthProp, CenteredShellAlignProp, CenteredShellPresetProp, ErrorSurfaceModeProp, ErrorSurfaceStatusProp, AuthShellPresetProp, OrientationProp, TextAlignProp, TextToneProp, BreakpointProp, GapProp, ClassNameProp, ChildrenProp, IdProp, DisabledProp, DescriptionProp, ActionProp, IconProp, HeadingLevelProp, ToneProp } from "../vocabulary/index.js";
4
+ import type { BreadcrumbProp, TitleProp, SubtitleProp, StatusProp, ExtraProp, FooterProp, PageDensityProp, PageContainerVariantProp, CenteredShellWidthProp, CenteredShellAlignProp, CenteredShellPresetProp, ErrorSurfaceModeProp, ErrorSurfaceStatusProp, AuthShellPresetProp, MobileShellHeightProp, OrientationProp, TextAlignProp, TextToneProp, BreakpointProp, GapProp, ClassNameProp, ChildrenProp, IdProp, DisabledProp, DescriptionProp, ActionProp, IconProp, HeadingLevelProp, ToneProp } from "../vocabulary/index.js";
5
5
  import type { EmptyStateToneProp } from "./data-display.prop.js";
6
6
  /**
7
7
  * Arrangement of the page header's title band and its `extra` slot below the 640px step.
@@ -102,6 +102,14 @@ export type FlexAlignProp = "start" | "center" | "end" | "stretch" | "baseline";
102
102
  export type FlexJustifyProp = "start" | "center" | "end" | "between" | "around" | "evenly";
103
103
  /** @see Flex */
104
104
  export type FlexProp = React.HTMLAttributes<HTMLDivElement> & {
105
+ /**
106
+ * Render element — `div` (default) or `span` when the Flex sits in a PHRASING context and a
107
+ * `<div>` would be invalid HTML: inside a `TabsTrigger`/`PopoverTrigger`/`Button` (all of which
108
+ * render a `<button>`, whose content model is phrasing content only), inside a `<label>`, or
109
+ * inside a `<p>`. Same closed shape as `ListRow`'s `as` — it swaps the TAG, nothing else: the
110
+ * `.ui-flex` rules carry `display: flex`, so the box is identical either way (gh#354).
111
+ */
112
+ as?: "div" | "span";
105
113
  direction?: FlexDirectionProp;
106
114
  gap?: GapProp;
107
115
  align?: FlexAlignProp;
@@ -226,6 +234,13 @@ export type AuthShellProp = {
226
234
  children: ReactNode;
227
235
  /** Brand bar slot pinned to the top (e.g. a `<Logo>` / product mark). */
228
236
  brand?: ReactNode;
237
+ /**
238
+ * Page-level controls pinned to the TOP-RIGHT of the same banner row as `brand` — a locale
239
+ * picker, a theme toggle, a "need help?" link. These belong to the PAGE, not to the auth form,
240
+ * which is why they sit in the bar rather than inside the card. The banner renders as soon as
241
+ * `brand` OR `actions` is present, so an actions-only bar is a legal shape.
242
+ */
243
+ actions?: ActionProp;
229
244
  /** Footer slot pinned to the bottom (legal links, locale switch, support). */
230
245
  footer?: ReactNode;
231
246
  /**
@@ -242,6 +257,16 @@ export type AuthShellProp = {
242
257
  * canonical 1440x900, 1024x900 and 390x844 viewports.
243
258
  */
244
259
  preset?: AuthShellPresetProp;
260
+ /**
261
+ * Inline MEASURE of the shell's content slot. `"default"` keeps the single auth card (24rem, or
262
+ * the canonical variant's 22.5rem). `"wide"` opens the slot to
263
+ * `--auth-shell-wide-card-max-width` so a SPLIT login fits — a brand/marketing panel beside the
264
+ * auth card, the shape that previously had no shell and reached for `CenteredShell`. The wide
265
+ * slot centres itself with auto margins, so a tall two-column layout starts at the top instead
266
+ * of overflowing above the scroll origin. Ignored under a `preset`: a preset already owns its
267
+ * flow geometry.
268
+ */
269
+ measure?: "default" | "wide";
245
270
  /**
246
271
  * Vertical density scoped to auth-card descendants. The canonical variant defaults to
247
272
  * `"compact"`; the default variant defaults to `"comfortable"`.
@@ -249,6 +274,68 @@ export type AuthShellProp = {
249
274
  density?: "comfortable" | "compact";
250
275
  className?: ClassNameProp;
251
276
  };
277
+ /**
278
+ * @see MobileShell — the HANDHELD app shell: a status band, an app bar, ONE scroll region, a
279
+ * sticky action bar and a bottom tab bar, in that fixed order.
280
+ *
281
+ * It is the fourth root shell, and it exists because the other three cannot express a phone app:
282
+ * `AppShell` REQUIRES a sidebar (its bar is a grid area beside the nav rail), `AuthShell` is the
283
+ * UNAUTHENTICATED root and centres a ~24rem card, and `CenteredShell` is a scrolling DOCUMENT —
284
+ * its `main` scrolls the page, which is exactly what a handheld app must not do. Composing one out
285
+ * of `Card` + `ui-card-inset*` (what docs/showcase/case6 did before gh#354) reproduces the look
286
+ * and none of the two behaviours that matter on a real device:
287
+ *
288
+ * 1. The shell is the only scroll container. The root is exactly one screen tall, so the DOCUMENT
289
+ * never scrolls and the tab bar never slides away under a collapsing URL bar.
290
+ * 2. Every band pads itself out of `env(safe-area-inset-*)`, so the notch never covers the app
291
+ * bar and the home indicator never covers the primary verb.
292
+ *
293
+ * Layout-only, like the other shells: it paints chrome and owns geometry, and delegates motion to
294
+ * `Reveal`. Never nest it inside another shell — it is a ROOT.
295
+ */
296
+ export type MobileShellProp = {
297
+ /**
298
+ * The scrolling screen body — the ONLY scroll container in the shell, and the only elastic band.
299
+ * Everything else is fixed chrome, so a long list scrolls under a stationary app bar and tab bar.
300
+ */
301
+ children: ReactNode;
302
+ /**
303
+ * App bar (banner) pinned to the top: the screen title plus its inline actions. When there is no
304
+ * `statusBar` this band absorbs the top safe-area inset itself. Omit → no banner.
305
+ *
306
+ * On a screen with a MODE (multi-select, search, edit) swap the whole node rather than stacking a
307
+ * second strip under it — replacing the bar's contents is the platform pattern on both iOS and
308
+ * Android, and it keeps one bar to read instead of two.
309
+ */
310
+ header?: ReactNode;
311
+ /**
312
+ * The band that sits IN the OS status-bar strip above the app bar — the carrier/clock row of a
313
+ * `display-mode: standalone` PWA, or the simulated one in a device-frame preview. It absorbs the
314
+ * top safe-area inset when present. Omit it in an ordinary browser tab, where the OS already owns
315
+ * that strip: the header then takes the inset.
316
+ */
317
+ statusBar?: ReactNode;
318
+ /**
319
+ * The sticky action bar pinned above the tab bar — the screen's primary verb (Scan, Save, Hand
320
+ * over) and at most one secondary. It sits OUTSIDE the scroll region, so it is always reachable
321
+ * without `position: sticky` and without a scroll-padding hack, and it takes the home-indicator
322
+ * inset whenever no tab bar follows it.
323
+ */
324
+ actions?: ActionProp;
325
+ /**
326
+ * Bottom tab bar (navigation) — the app's top-level destinations. Its children TILE: equal width,
327
+ * no seam, no page gutter, so the consumer never hand-rolls a grid with a column count. Always
328
+ * the last band, so it owns the home-indicator inset.
329
+ */
330
+ tabBar?: ReactNode;
331
+ /**
332
+ * Where the shell's one-screen height comes from. `"viewport"` (default) is the real app —
333
+ * exactly `100dvh`, document never scrolls. `"fill"` fills a BOUNDED parent instead (a
334
+ * device-frame preview, a phone view embedded in a wider page).
335
+ */
336
+ height?: MobileShellHeightProp;
337
+ className?: ClassNameProp;
338
+ };
252
339
  /**
253
340
  * @see Separator — the tokenized rule, optionally INTERRUPTED by a localized label.
254
341
  *
@@ -256,7 +343,7 @@ export type AuthShellProp = {
256
343
  * what they have always been: an inert Radix rule, `decorative` by default, nothing announced.
257
344
  * With a `label` the root becomes the three-cell grid `rule · label · rule` and, because
258
345
  * "new messages" is CONTENT rather than decoration, a real `role="separator"` carrying the label
259
- * as its accessible name (gh#308). Every constant — rule weight, label gap, label inset, label
346
+ * as its accessible name. Every constant — rule weight, label gap, label inset, label
260
347
  * type ramp, rule and label colour per tone — is a `--separator-*` component token (rules #44/#45),
261
348
  * so a service retunes a day divider or an unread watermark from its theme and never forks CSS.
262
349
  */
@@ -284,7 +371,7 @@ export type SeparatorProp = Omit<React.HTMLAttributes<HTMLDivElement>, "children
284
371
  decorative?: boolean;
285
372
  className?: ClassNameProp;
286
373
  };
287
- /** @see AuthDivider — the auth-scoped preset over `Separator label` (gh#308). */
374
+ /** @see AuthDivider — the auth-scoped preset over `Separator label`. */
288
375
  export type AuthDividerProp = {
289
376
  /** Short localized conjunction rendered between the two separator rules (for example, "or"). */
290
377
  label: string;
@@ -577,7 +664,7 @@ export type SidebarLinkComponentProp = ComponentType<SidebarLinkProp>;
577
664
  * @deprecated Prefer `Sidebar.linkComponent` (or `SidebarItem asChild`), where the library composes
578
665
  * the row and the consumer supplies only the element. `renderItem` leaves row CONTENT to the
579
666
  * consumer, which is how a `<Link>{item.label}</Link>` silently dropped every icon and badge
580
- * (gh#213). Spreading `rowProps` — including its `children` — now yields the canonical row.
667
+ *. Spreading `rowProps` — including its `children` — now yields the canonical row.
581
668
  */
582
669
  export type SidebarRenderItemProp = {
583
670
  className: string;
@@ -662,7 +749,7 @@ export type SidebarProp = {
662
749
  collapsed?: boolean;
663
750
  children?: ChildrenProp;
664
751
  /**
665
- * THE framework-router contract (gh#213). Supply only the LINK ELEMENT TYPE; the Sidebar still
752
+ * THE framework-router contract. Supply only the LINK ELEMENT TYPE; the Sidebar still
666
753
  * composes the row — icon slot, label, badge, `data-active`/`aria-current`, the icon-only
667
754
  * collapsed rail and its tooltip name — and passes it as {@link SidebarLinkProp} `children`.
668
755
  * Used for every row that carries an `href`: top-level leaves, submenu children, collapsed-rail
@@ -690,7 +777,7 @@ export type SidebarProp = {
690
777
  *
691
778
  * Legacy escape hatch: return a SINGLE interactive element and the Sidebar merges the row styling
692
779
  * + active state onto it via Slot. Because row CONTENT stayed consumer-authored, a
693
- * `<Link>{item.label}</Link>` silently dropped every icon and badge (the gh#213 production
780
+ * `<Link>{item.label}</Link>` silently dropped every icon and badge (the production
694
781
  * regression). `rowProps` now also carries the composed `children`, so spreading it restores the
695
782
  * canonical row.
696
783
  */
@@ -114,6 +114,11 @@ export declare const VOCABULARY_REGISTRY: {
114
114
  readonly category: "shared";
115
115
  readonly description: "Radix asChild polymorphism";
116
116
  };
117
+ readonly FlushProp: {
118
+ readonly file: "vocabulary/shared.prop.ts";
119
+ readonly category: "shared";
120
+ readonly description: "Content owns its own inset — the container drops its padding so the child reaches the frame edge (CardContent/CardFooter flush, TableCell flush, PopoverContent flush)";
121
+ };
117
122
  readonly WidthProp: {
118
123
  readonly file: "vocabulary/shared.prop.ts";
119
124
  readonly category: "shared";
@@ -209,6 +214,11 @@ export declare const VOCABULARY_REGISTRY: {
209
214
  readonly category: "layout";
210
215
  readonly description: "CenteredShell column block alignment — start (flowing page), center (system surface)";
211
216
  };
217
+ readonly MobileShellHeightProp: {
218
+ readonly file: "vocabulary/layout.prop.ts";
219
+ readonly category: "layout";
220
+ readonly description: "MobileShell block-size contract — viewport (100dvh root, the real app) | fill (fills a bounded parent, e.g. a device-frame preview)";
221
+ };
212
222
  readonly CenteredShellPresetProp: {
213
223
  readonly file: "vocabulary/layout.prop.ts";
214
224
  readonly category: "layout";
@@ -224,6 +234,11 @@ export declare const VOCABULARY_REGISTRY: {
224
234
  readonly category: "data";
225
235
  readonly description: "Table column priority for the action-collection preset — primary | secondary | meta | actions (unset = takes the remaining space)";
226
236
  };
237
+ readonly TableCellIndentProp: {
238
+ readonly file: "vocabulary/data.prop.ts";
239
+ readonly category: "data";
240
+ readonly description: "TableCell hierarchy depth — indent = --table-cell-space-x + depth x --table-cell-indent-space-step (tree rows, grouped detail rows)";
241
+ };
227
242
  readonly ErrorSurfaceModeProp: {
228
243
  readonly file: "vocabulary/layout.prop.ts";
229
244
  readonly category: "layout";
@@ -540,6 +555,10 @@ export declare const COMPONENT_PROP_REGISTRY: {
540
555
  readonly group: "layout";
541
556
  readonly file: "components/layout.prop.ts";
542
557
  readonly vocabulary: readonly [{
558
+ readonly field: "as";
559
+ readonly local: true;
560
+ readonly reason: "Closed tag seam (div | span) so a Flex stays valid HTML inside a phrasing-only parent such as the <button> a TabsTrigger renders — same shape as ListRow's `as` (gh#354).";
561
+ }, {
543
562
  readonly field: "direction";
544
563
  readonly local: true;
545
564
  readonly reason: "Flex-specific axis control.";
@@ -648,7 +667,28 @@ export declare const COMPONENT_PROP_REGISTRY: {
648
667
  readonly field: "density";
649
668
  readonly local: true;
650
669
  readonly reason: "Auth-specific vertical-density scope for card descendants.";
651
- }, "AuthShellPresetProp"];
670
+ }, {
671
+ readonly field: "measure";
672
+ readonly local: true;
673
+ readonly reason: "Auth-shell content-slot measure; wide is the split brand-panel login.";
674
+ }, "ActionProp", "AuthShellPresetProp"];
675
+ };
676
+ readonly MobileShellProp: {
677
+ readonly group: "layout";
678
+ readonly file: "components/layout.prop.ts";
679
+ readonly vocabulary: readonly ["ChildrenProp", "ClassNameProp", "ActionProp", "MobileShellHeightProp", {
680
+ readonly field: "header";
681
+ readonly local: true;
682
+ readonly reason: "MobileShell app-bar band — the screen title row; it absorbs the top safe-area inset when no statusBar precedes it.";
683
+ }, {
684
+ readonly field: "statusBar";
685
+ readonly local: true;
686
+ readonly reason: "MobileShell OS status-bar band — standalone/PWA chrome or a device-frame preview; owns the top safe-area inset.";
687
+ }, {
688
+ readonly field: "tabBar";
689
+ readonly local: true;
690
+ readonly reason: "MobileShell bottom navigation band — tiling equal-width destinations; owns the home-indicator inset.";
691
+ }];
652
692
  };
653
693
  readonly SeparatorProp: {
654
694
  readonly group: "layout";
@@ -1160,7 +1200,15 @@ export declare const COMPONENT_PROP_REGISTRY: {
1160
1200
  readonly BadgeProp: {
1161
1201
  readonly group: "data-display";
1162
1202
  readonly file: "components/data-display.prop.ts";
1163
- readonly vocabulary: readonly ["BadgeVariantProp", "ShapeProp", "ToneProp", "ChildrenProp", "ClassNameProp"];
1203
+ readonly vocabulary: readonly [{
1204
+ readonly field: "as";
1205
+ readonly local: true;
1206
+ readonly reason: "Closed tag seam (div | span) so a chip stays valid HTML inside a phrasing-only parent such as the <button> a TabsTrigger renders — same shape as ListRow's `as` (gh#354).";
1207
+ }, "BadgeVariantProp", "ShapeProp", "ToneProp", "ChildrenProp", "ClassNameProp", {
1208
+ readonly field: "color";
1209
+ readonly local: true;
1210
+ readonly reason: "The entity's OWN colour as a CSS colour string — DATA a person picked in a settings screen (a status, an issue type, a tag), a third axis beside `variant` (structure) and `tone` (meaning). No vocabulary type fits: ToneProp is the closed semantic set, and this value is open by definition. Washed into --badge-tint-surface rather than filled, because no foreground clears WCAG AA against every colour a picker can produce.";
1211
+ }];
1164
1212
  };
1165
1213
  readonly DataTableProp: {
1166
1214
  readonly group: "data-display";
@@ -1721,6 +1769,69 @@ export declare const COMPONENT_PROP_REGISTRY: {
1721
1769
  readonly file: "components/data-display/timeline.tsx";
1722
1770
  readonly vocabulary: readonly ["ClassNameProp"];
1723
1771
  };
1772
+ readonly TimelineGridColumnProp: {
1773
+ readonly group: "data-display";
1774
+ readonly file: "components/data-display.prop.ts";
1775
+ readonly vocabulary: readonly ["IdProp", "LabelProp", "DescriptionProp", {
1776
+ readonly field: "current";
1777
+ readonly local: true;
1778
+ readonly reason: "Marks the column as now/today — it carries the tint and hosts the grid's now marker.";
1779
+ }];
1780
+ };
1781
+ readonly TimelineGridEventProp: {
1782
+ readonly group: "data-display";
1783
+ readonly file: "components/data-display.prop.ts";
1784
+ readonly vocabulary: readonly ["IdProp", "TitleProp", "DescriptionProp", {
1785
+ readonly field: "columnId";
1786
+ readonly local: true;
1787
+ readonly reason: "Id of the column the event belongs to (see TimelineGridColumnProp.id).";
1788
+ }, {
1789
+ readonly field: "start";
1790
+ readonly local: true;
1791
+ readonly reason: 'Start clock time in the column\'s own day, "HH:MM" — no date, no timezone.';
1792
+ }, {
1793
+ readonly field: "end";
1794
+ readonly local: true;
1795
+ readonly reason: 'End clock time, "HH:MM"; at or before start means the event continues into the next day.';
1796
+ }, {
1797
+ readonly field: "color";
1798
+ readonly local: true;
1799
+ readonly reason: "The record's own colour, washed into the block exactly like Badge `color` — decorative, never the only signal.";
1800
+ }];
1801
+ };
1802
+ readonly TimelineGridProp: {
1803
+ readonly group: "data-display";
1804
+ readonly file: "components/data-display.prop.ts";
1805
+ readonly vocabulary: readonly ["LabelProp", "ClassNameProp", "IdProp", {
1806
+ readonly field: "columns";
1807
+ readonly local: true;
1808
+ readonly reason: "Consumer-supplied columns in render order (see TimelineGridColumnProp).";
1809
+ }, {
1810
+ readonly field: "events";
1811
+ readonly local: true;
1812
+ readonly reason: "Consumer-supplied events (see TimelineGridEventProp).";
1813
+ }, {
1814
+ readonly field: "start";
1815
+ readonly local: true;
1816
+ readonly reason: 'First clock time on the axis, "HH:MM"; defaults to the earliest event on the hour so a block can only fall outside a PINNED axis.';
1817
+ }, {
1818
+ readonly field: "end";
1819
+ readonly local: true;
1820
+ readonly reason: 'Last clock time on the axis, "HH:MM"; defaults to the latest event on the hour.';
1821
+ }, {
1822
+ readonly field: "interval";
1823
+ readonly local: true;
1824
+ readonly reason: "Hours between hour rules and axis labels (default 1).";
1825
+ }, {
1826
+ readonly field: "now";
1827
+ readonly local: true;
1828
+ readonly reason: 'Current clock time, "HH:MM" — draws the now marker in the columns marked current.';
1829
+ }, {
1830
+ readonly field: "onEventSelect";
1831
+ readonly local: true;
1832
+ readonly reason: "Block click handler carrying the event; its presence turns every block into a real button.";
1833
+ }];
1834
+ };
1724
1835
  readonly TreeListProp: {
1725
1836
  readonly group: "data-display";
1726
1837
  readonly file: "components/data-display/tree-list.tsx";
@@ -110,6 +110,11 @@ const VOCABULARY_REGISTRY = {
110
110
  category: "shared",
111
111
  description: "Radix asChild polymorphism"
112
112
  },
113
+ FlushProp: {
114
+ file: "vocabulary/shared.prop.ts",
115
+ category: "shared",
116
+ description: "Content owns its own inset \u2014 the container drops its padding so the child reaches the frame edge (CardContent/CardFooter flush, TableCell flush, PopoverContent flush)"
117
+ },
113
118
  WidthProp: {
114
119
  file: "vocabulary/shared.prop.ts",
115
120
  category: "shared",
@@ -207,6 +212,11 @@ const VOCABULARY_REGISTRY = {
207
212
  category: "layout",
208
213
  description: "CenteredShell column block alignment \u2014 start (flowing page), center (system surface)"
209
214
  },
215
+ MobileShellHeightProp: {
216
+ file: "vocabulary/layout.prop.ts",
217
+ category: "layout",
218
+ description: "MobileShell block-size contract \u2014 viewport (100dvh root, the real app) | fill (fills a bounded parent, e.g. a device-frame preview)"
219
+ },
210
220
  CenteredShellPresetProp: {
211
221
  file: "vocabulary/layout.prop.ts",
212
222
  category: "layout",
@@ -222,6 +232,11 @@ const VOCABULARY_REGISTRY = {
222
232
  category: "data",
223
233
  description: "Table column priority for the action-collection preset \u2014 primary | secondary | meta | actions (unset = takes the remaining space)"
224
234
  },
235
+ TableCellIndentProp: {
236
+ file: "vocabulary/data.prop.ts",
237
+ category: "data",
238
+ description: "TableCell hierarchy depth \u2014 indent = --table-cell-space-x + depth x --table-cell-indent-space-step (tree rows, grouped detail rows)"
239
+ },
225
240
  ErrorSurfaceModeProp: {
226
241
  file: "vocabulary/layout.prop.ts",
227
242
  category: "layout",
@@ -546,6 +561,11 @@ const COMPONENT_PROP_REGISTRY = {
546
561
  group: "layout",
547
562
  file: "components/layout.prop.ts",
548
563
  vocabulary: [
564
+ {
565
+ field: "as",
566
+ local: true,
567
+ reason: "Closed tag seam (div | span) so a Flex stays valid HTML inside a phrasing-only parent such as the <button> a TabsTrigger renders \u2014 same shape as ListRow's `as` (gh#354)."
568
+ },
549
569
  { field: "direction", local: true, reason: "Flex-specific axis control." },
550
570
  "GapProp",
551
571
  { field: "align", local: true, reason: "Flex-specific align-items keyword subset." },
@@ -656,9 +676,40 @@ const COMPONENT_PROP_REGISTRY = {
656
676
  local: true,
657
677
  reason: "Auth-specific vertical-density scope for card descendants."
658
678
  },
679
+ {
680
+ field: "measure",
681
+ local: true,
682
+ reason: "Auth-shell content-slot measure; wide is the split brand-panel login."
683
+ },
684
+ "ActionProp",
659
685
  "AuthShellPresetProp"
660
686
  ]
661
687
  },
688
+ MobileShellProp: {
689
+ group: "layout",
690
+ file: "components/layout.prop.ts",
691
+ vocabulary: [
692
+ "ChildrenProp",
693
+ "ClassNameProp",
694
+ "ActionProp",
695
+ "MobileShellHeightProp",
696
+ {
697
+ field: "header",
698
+ local: true,
699
+ reason: "MobileShell app-bar band \u2014 the screen title row; it absorbs the top safe-area inset when no statusBar precedes it."
700
+ },
701
+ {
702
+ field: "statusBar",
703
+ local: true,
704
+ reason: "MobileShell OS status-bar band \u2014 standalone/PWA chrome or a device-frame preview; owns the top safe-area inset."
705
+ },
706
+ {
707
+ field: "tabBar",
708
+ local: true,
709
+ reason: "MobileShell bottom navigation band \u2014 tiling equal-width destinations; owns the home-indicator inset."
710
+ }
711
+ ]
712
+ },
662
713
  SeparatorProp: {
663
714
  group: "layout",
664
715
  file: "components/layout.prop.ts",
@@ -1345,7 +1396,23 @@ const COMPONENT_PROP_REGISTRY = {
1345
1396
  BadgeProp: {
1346
1397
  group: "data-display",
1347
1398
  file: "components/data-display.prop.ts",
1348
- vocabulary: ["BadgeVariantProp", "ShapeProp", "ToneProp", "ChildrenProp", "ClassNameProp"]
1399
+ vocabulary: [
1400
+ {
1401
+ field: "as",
1402
+ local: true,
1403
+ reason: "Closed tag seam (div | span) so a chip stays valid HTML inside a phrasing-only parent such as the <button> a TabsTrigger renders \u2014 same shape as ListRow's `as` (gh#354)."
1404
+ },
1405
+ "BadgeVariantProp",
1406
+ "ShapeProp",
1407
+ "ToneProp",
1408
+ "ChildrenProp",
1409
+ "ClassNameProp",
1410
+ {
1411
+ field: "color",
1412
+ local: true,
1413
+ reason: "The entity's OWN colour as a CSS colour string \u2014 DATA a person picked in a settings screen (a status, an issue type, a tag), a third axis beside `variant` (structure) and `tone` (meaning). No vocabulary type fits: ToneProp is the closed semantic set, and this value is open by definition. Washed into --badge-tint-surface rather than filled, because no foreground clears WCAG AA against every colour a picker can produce."
1414
+ }
1415
+ ]
1349
1416
  },
1350
1417
  DataTableProp: {
1351
1418
  group: "data-display",
@@ -1905,6 +1972,93 @@ const COMPONENT_PROP_REGISTRY = {
1905
1972
  file: "components/data-display/timeline.tsx",
1906
1973
  vocabulary: ["ClassNameProp"]
1907
1974
  },
1975
+ TimelineGridColumnProp: {
1976
+ group: "data-display",
1977
+ file: "components/data-display.prop.ts",
1978
+ vocabulary: [
1979
+ "IdProp",
1980
+ "LabelProp",
1981
+ "DescriptionProp",
1982
+ {
1983
+ field: "current",
1984
+ local: true,
1985
+ reason: "Marks the column as now/today \u2014 it carries the tint and hosts the grid's now marker."
1986
+ }
1987
+ ]
1988
+ },
1989
+ TimelineGridEventProp: {
1990
+ group: "data-display",
1991
+ file: "components/data-display.prop.ts",
1992
+ vocabulary: [
1993
+ "IdProp",
1994
+ "TitleProp",
1995
+ "DescriptionProp",
1996
+ {
1997
+ field: "columnId",
1998
+ local: true,
1999
+ reason: "Id of the column the event belongs to (see TimelineGridColumnProp.id)."
2000
+ },
2001
+ {
2002
+ field: "start",
2003
+ local: true,
2004
+ reason: `Start clock time in the column's own day, "HH:MM" \u2014 no date, no timezone.`
2005
+ },
2006
+ {
2007
+ field: "end",
2008
+ local: true,
2009
+ reason: 'End clock time, "HH:MM"; at or before start means the event continues into the next day.'
2010
+ },
2011
+ {
2012
+ field: "color",
2013
+ local: true,
2014
+ reason: "The record's own colour, washed into the block exactly like Badge `color` \u2014 decorative, never the only signal."
2015
+ }
2016
+ ]
2017
+ },
2018
+ TimelineGridProp: {
2019
+ group: "data-display",
2020
+ file: "components/data-display.prop.ts",
2021
+ vocabulary: [
2022
+ "LabelProp",
2023
+ "ClassNameProp",
2024
+ "IdProp",
2025
+ {
2026
+ field: "columns",
2027
+ local: true,
2028
+ reason: "Consumer-supplied columns in render order (see TimelineGridColumnProp)."
2029
+ },
2030
+ {
2031
+ field: "events",
2032
+ local: true,
2033
+ reason: "Consumer-supplied events (see TimelineGridEventProp)."
2034
+ },
2035
+ {
2036
+ field: "start",
2037
+ local: true,
2038
+ reason: 'First clock time on the axis, "HH:MM"; defaults to the earliest event on the hour so a block can only fall outside a PINNED axis.'
2039
+ },
2040
+ {
2041
+ field: "end",
2042
+ local: true,
2043
+ reason: 'Last clock time on the axis, "HH:MM"; defaults to the latest event on the hour.'
2044
+ },
2045
+ {
2046
+ field: "interval",
2047
+ local: true,
2048
+ reason: "Hours between hour rules and axis labels (default 1)."
2049
+ },
2050
+ {
2051
+ field: "now",
2052
+ local: true,
2053
+ reason: 'Current clock time, "HH:MM" \u2014 draws the now marker in the columns marked current.'
2054
+ },
2055
+ {
2056
+ field: "onEventSelect",
2057
+ local: true,
2058
+ reason: "Block click handler carrying the event; its presence turns every block into a real button."
2059
+ }
2060
+ ]
2061
+ },
1908
2062
  TreeListProp: {
1909
2063
  group: "data-display",
1910
2064
  file: "components/data-display/tree-list.tsx",
@@ -87,3 +87,9 @@ export type TablePresetProp = "default" | "action-collection" | "stacked-record-
87
87
  * is reserved first so it can never be pushed outside the viewport.
88
88
  */
89
89
  export type TableColumnPriorityProp = "primary" | "secondary" | "meta" | "actions";
90
+ /**
91
+ * Hierarchy depth a table cell renders at. `0` sits on the column's own text axis and every
92
+ * further level adds one `--table-cell-indent-space-step`, so a tree row or a grouped detail row
93
+ * expresses its level instead of hand-rolling `style={{ paddingInlineStart }}` at the call site.
94
+ */
95
+ export type TableCellIndentProp = number;
@@ -1,7 +1,7 @@
1
1
  /** Barrel — all vocabulary prop types. */
2
- export type { ClassNameProp, ChildrenProp, IdProp, OpenProp, DefaultOpenProp, OnOpenChangeProp, HandlerProp, PendingProp, RequiredProp, DisabledProp, LabelProp, HelperProp, ErrorProp, ErrorBagProp, PlaceholderProp, NameProp, ValueProp, DefaultValueProp, OnValueChangeProp, OnChangeProp, OnClickProp, AsChildProp, WidthProp, ControlWidthProp, } from "./shared.prop.js";
2
+ export type { ClassNameProp, ChildrenProp, IdProp, OpenProp, DefaultOpenProp, OnOpenChangeProp, HandlerProp, PendingProp, RequiredProp, DisabledProp, LabelProp, HelperProp, ErrorProp, ErrorBagProp, PlaceholderProp, NameProp, ValueProp, DefaultValueProp, OnValueChangeProp, OnChangeProp, OnClickProp, AsChildProp, FlushProp, WidthProp, ControlWidthProp, } from "./shared.prop.js";
3
3
  export type { TitleProp, SubtitleProp, StatusProp, DescriptionProp, ExtraProp, FooterProp, ActionProp, IconProp, ConfirmLabelProp, CancelLabelProp, ActionsProp, EmptyMessageProp, } from "./content.prop.js";
4
- export type { PageDensityProp, PageContainerVariantProp, CenteredShellWidthProp, CenteredShellAlignProp, CenteredShellPresetProp, ErrorSurfaceModeProp, ErrorSurfaceStatusProp, AuthShellPresetProp, OrientationProp, TableDensityProp, DensityProp, GapProp, } from "./layout.prop.js";
4
+ export type { PageDensityProp, PageContainerVariantProp, CenteredShellWidthProp, CenteredShellAlignProp, CenteredShellPresetProp, ErrorSurfaceModeProp, ErrorSurfaceStatusProp, AuthShellPresetProp, MobileShellHeightProp, OrientationProp, TableDensityProp, DensityProp, GapProp, } from "./layout.prop.js";
5
5
  export type { ButtonVariantProp, ButtonSizeProp, BadgeVariantProp, AppSettingPickerAppearanceProp, ShapeProp, AvatarShapeProp, TextSizeProp, TextToneProp, FontWeightProp, HeadingLevelProp, TextAlignProp, SizeProp, FormLayoutProp, DescriptionsLayoutProp, BreakpointProp, ConfirmVariantProp, ToneProp, AlertVariantProp, SortDirectionProp, ColumnAlignProp, SortStateProp, RevealDelayProp, ActivityVariantProp, ActivityAnnounceProp, } from "./interaction.prop.js";
6
6
  export type { BreadcrumbItemProp, BreadcrumbProp } from "./navigation.prop.js";
7
- export type { GetRowIdProp, OnRowClickProp, ColumnDefProp, TablePresetProp, TableColumnPriorityProp, SelectedIdsProp, OnSelectChangeProp, OnTableDensityChangeProp, OnSortChangeProp, OnSearchChangeProp, OnClearFiltersProp, HasActiveFiltersProp, StickyProp, } from "./data.prop.js";
7
+ export type { GetRowIdProp, OnRowClickProp, ColumnDefProp, TablePresetProp, TableColumnPriorityProp, TableCellIndentProp, SelectedIdsProp, OnSelectChangeProp, OnTableDensityChangeProp, OnSortChangeProp, OnSearchChangeProp, OnClearFiltersProp, HasActiveFiltersProp, StickyProp, } from "./data.prop.js";
@@ -17,6 +17,15 @@ export type CenteredShellWidthProp = "sm" | "md" | "lg";
17
17
  * maintenance notice) — the geometry stays package-owned, so no consumer `min-h-dvh` / flex CSS.
18
18
  */
19
19
  export type CenteredShellAlignProp = "start" | "center";
20
+ /**
21
+ * MobileShell block-size contract — WHERE the handheld shell gets its one-screen height, the one
22
+ * question a bounded shell cannot answer for itself.
23
+ * `"viewport"` (default) is the real app: the shell is exactly `100dvh`, the document never
24
+ * scrolls, and the chrome bands stay on screen while the URL bar collapses. `"fill"` fills a
25
+ * BOUNDED parent instead — a device-frame preview, or a phone view embedded in a wider page —
26
+ * where a viewport-tall root would overflow its frame. Nothing else differs between the two.
27
+ */
28
+ export type MobileShellHeightProp = "viewport" | "fill";
20
29
  /**
21
30
  * ErrorSurface shell contract — WHERE the exception surface lives, not how it looks.
22
31
  * `"application"` (403 / 404): the failure happened INSIDE the authenticated app, so the surface
@@ -43,8 +52,14 @@ export type ErrorSurfaceStatusProp = 400 | 403 | 404 | 500 | 503 | (number & {})
43
52
  * scroll origin, and the only preset with a footer-clearance knob of its own.
44
53
  */
45
54
  export type AuthShellPresetProp = "default" | "login" | "registration" | "device-authorization" | "context-selection" | "account-recovery";
46
- /** Shared gap between layout children; components may document subsets. */
47
- export type GapProp = "xs" | "sm" | "md" | "lg" | "xl";
55
+ /**
56
+ * Shared gap between layout children; components may document subsets.
57
+ * `"none"` is a DELIBERATE zero, not the absence of a value: two lines that read as ONE block —
58
+ * a name over its role, a weekday over its date, a tab bar with no seam between its triggers.
59
+ * Without it those stacks had to carry `gap="xs"`, which is a visual change forced by a missing
60
+ * step rather than by design (gh#354).
61
+ */
62
+ export type GapProp = "none" | "xs" | "sm" | "md" | "lg" | "xl";
48
63
  /** DataTable row density subset. */
49
64
  export type TableDensityProp = Exclude<DensityProp, "default">;
50
65
  /**
@@ -50,6 +50,13 @@ export type OnChangeProp = React.ChangeEventHandler<HTMLInputElement>;
50
50
  export type OnClickProp = React.MouseEventHandler<HTMLButtonElement>;
51
51
  /** Radix/shadcn `asChild` polymorphism — render as child element. */
52
52
  export type AsChildProp = boolean;
53
+ /**
54
+ * The slot's CONTENT owns its own inset: the container drops its padding so the child reaches the
55
+ * frame edge — a full-bleed table inside a Card, an expanded detail panel inside a table cell, a
56
+ * Command list inside a Popover. It is the sanctioned replacement for the `p-0` utility a consumer
57
+ * would otherwise write at the call site.
58
+ */
59
+ export type FlushProp = boolean;
53
60
  /**
54
61
  * An explicit layout dimension (NOT the `SizeProp` control-height tier). A `number` is treated as
55
62
  * px; a `string` is any CSS length (`"32rem"`, `"90vw"`, `"50%"`).
@@ -25,6 +25,21 @@
25
25
  }
26
26
  }
27
27
 
28
+ [data-slot="badge"][data-tinted] {
29
+
30
+ background-color: color-mix(
31
+ in srgb,
32
+ var(--badge-tint-surface),
33
+ var(--badge-color) var(--badge-tint-fill)
34
+ );
35
+ border-color: color-mix(
36
+ in srgb,
37
+ var(--badge-tint-surface),
38
+ var(--badge-color) var(--badge-tint-edge)
39
+ );
40
+ color: var(--badge-tint-foreground);
41
+ }
42
+
28
43
  [data-slot="badge-icon"] {
29
44
  width: var(--badge-icon-size);
30
45
  height: var(--badge-icon-size);
@@ -368,7 +368,7 @@
368
368
  width: var(--checkbox-size);
369
369
  height: var(--checkbox-size);
370
370
  flex-shrink: 0;
371
- border: 1px solid hsl(var(--primary));
371
+ border: var(--checkbox-border-width) solid hsl(var(--primary));
372
372
  color: hsl(var(--primary));
373
373
  background: transparent;
374
374
  }
@@ -999,6 +999,23 @@
999
999
  --control-height: var(--control-height-sm);
1000
1000
  }
1001
1001
 
1002
+ .ui-control-trigger {
1003
+ cursor: pointer;
1004
+ }
1005
+
1006
+ .ui-control-trigger:disabled,
1007
+ .ui-control-trigger[data-disabled] {
1008
+ cursor: not-allowed;
1009
+ }
1010
+
1011
+ .ui-control[data-size="sm"] {
1012
+ --control-height: var(--control-height-sm);
1013
+ }
1014
+
1015
+ .ui-control[data-size="lg"] {
1016
+ --control-height: var(--control-height-lg);
1017
+ }
1018
+
1002
1019
  .ui-number-input[data-size="sm"] {
1003
1020
  --control-height: var(--control-height-sm);
1004
1021
  }