@godxjp/ui 18.15.4 → 19.0.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 (145) hide show
  1. package/dist/app/date-format-labels.d.ts +1 -1
  2. package/dist/app/time-format-labels.d.ts +1 -1
  3. package/dist/components/data-display/badge.d.ts +1 -1
  4. package/dist/components/data-display/card.d.ts +4 -13
  5. package/dist/components/data-display/card.js +2 -15
  6. package/dist/components/data-display/data-table.d.ts +3 -8
  7. package/dist/components/data-display/data-table.js +106 -68
  8. package/dist/components/data-display/descriptions.js +34 -5
  9. package/dist/components/data-display/empty-state.js +1 -1
  10. package/dist/components/data-display/permission-matrix.js +7 -7
  11. package/dist/components/data-display/popover.js +2 -9
  12. package/dist/components/data-display/qr-code.d.ts +1 -1
  13. package/dist/components/data-display/scroll-area.d.ts +3 -1
  14. package/dist/components/data-display/scroll-area.js +150 -16
  15. package/dist/components/data-display/table.js +12 -3
  16. package/dist/components/data-entry/branch-scope-picker.js +4 -4
  17. package/dist/components/data-entry/calendar.js +15 -24
  18. package/dist/components/data-entry/cascader.js +67 -71
  19. package/dist/components/data-entry/checkbox.js +9 -1
  20. package/dist/components/data-entry/command.d.ts +6 -6
  21. package/dist/components/data-entry/date-picker.js +28 -15
  22. package/dist/components/data-entry/date-range-picker.js +10 -10
  23. package/dist/components/data-entry/form-field.js +7 -3
  24. package/dist/components/data-entry/index.d.ts +2 -1
  25. package/dist/components/data-entry/input.js +8 -19
  26. package/dist/components/data-entry/label.js +4 -2
  27. package/dist/components/data-entry/month-picker.js +16 -15
  28. package/dist/components/data-entry/month-range-picker.js +17 -16
  29. package/dist/components/data-entry/radio.js +6 -1
  30. package/dist/components/data-entry/search-select.js +29 -29
  31. package/dist/components/data-entry/select.js +15 -18
  32. package/dist/components/data-entry/switch.js +7 -1
  33. package/dist/components/data-entry/textarea.d.ts +56 -0
  34. package/dist/components/data-entry/textarea.js +103 -40
  35. package/dist/components/data-entry/time-picker.js +52 -52
  36. package/dist/components/data-entry/toggle-group.d.ts +1 -0
  37. package/dist/components/data-entry/toggle.d.ts +1 -1
  38. package/dist/components/data-entry/transfer.js +14 -15
  39. package/dist/components/data-entry/tree-select.js +16 -22
  40. package/dist/components/data-entry/upload-crop-dialog.js +4 -4
  41. package/dist/components/data-entry/upload.d.ts +1 -1
  42. package/dist/components/data-entry/upload.js +46 -46
  43. package/dist/components/feedback/alert.js +7 -14
  44. package/dist/components/feedback/dialog.d.ts +23 -32
  45. package/dist/components/feedback/dialog.js +16 -13
  46. package/dist/components/feedback/sheet.d.ts +1 -1
  47. package/dist/components/feedback/sheet.js +14 -14
  48. package/dist/components/feedback/skeleton.js +13 -10
  49. package/dist/components/feedback/sonner.d.ts +1 -1
  50. package/dist/components/feedback/sonner.js +17 -8
  51. package/dist/components/feedback/tooltip.js +1 -1
  52. package/dist/components/general/activity.d.ts +44 -0
  53. package/dist/components/general/activity.js +65 -0
  54. package/dist/components/general/button.d.ts +2 -2
  55. package/dist/components/general/button.js +11 -24
  56. package/dist/components/general/index.d.ts +2 -0
  57. package/dist/components/general/index.js +2 -0
  58. package/dist/components/layout/app-shell.js +5 -3
  59. package/dist/components/layout/auth-divider.d.ts +9 -2
  60. package/dist/components/layout/auth-divider.js +7 -11
  61. package/dist/components/layout/index.d.ts +1 -0
  62. package/dist/components/layout/page-container.d.ts +1 -1
  63. package/dist/components/layout/page-container.js +32 -20
  64. package/dist/components/layout/resizable.d.ts +12 -12
  65. package/dist/components/layout/separator.d.ts +1 -0
  66. package/dist/components/layout/service-role-panel.js +47 -42
  67. package/dist/components/layout/sidebar.js +13 -1
  68. package/dist/components/layout/split-pane.d.ts +12 -1
  69. package/dist/components/layout/split-pane.js +13 -4
  70. package/dist/components/navigation/app-setting-picker.js +5 -14
  71. package/dist/components/navigation/dropdown-menu.js +10 -16
  72. package/dist/components/navigation/menubar.d.ts +3 -6
  73. package/dist/components/navigation/pagination-utils.d.ts +15 -2
  74. package/dist/components/navigation/pagination-utils.js +27 -21
  75. package/dist/components/navigation/pagination.d.ts +4 -4
  76. package/dist/components/navigation/pagination.js +2 -2
  77. package/dist/components/navigation/steps.js +31 -67
  78. package/dist/components/navigation/tabs.js +17 -5
  79. package/dist/components/query/infinite-query-state.js +2 -2
  80. package/dist/components/ui/accordion.d.ts +1 -1
  81. package/dist/components/ui/avatar.d.ts +1 -1
  82. package/dist/components/ui/avatar.js +29 -10
  83. package/dist/components/ui/input-otp.d.ts +8 -6
  84. package/dist/components/ui/rating.js +1 -1
  85. package/dist/components/ui/separator.d.ts +27 -2
  86. package/dist/components/ui/separator.js +42 -11
  87. package/dist/components/ui/toggle-group.d.ts +15 -5
  88. package/dist/components/ui/toggle-group.js +43 -18
  89. package/dist/components/ui/toggle.d.ts +66 -5
  90. package/dist/components/ui/toggle.js +64 -9
  91. package/dist/i18n/messages/en.json +8 -0
  92. package/dist/i18n/messages/ja.json +8 -0
  93. package/dist/i18n/messages/vi.json +8 -0
  94. package/dist/lib/control-styles.d.ts +14 -0
  95. package/dist/lib/control-styles.js +2 -0
  96. package/dist/props/components/data-display.prop.d.ts +85 -3
  97. package/dist/props/components/data-entry.prop.d.ts +17 -0
  98. package/dist/props/components/general.prop.d.ts +52 -1
  99. package/dist/props/components/layout.prop.d.ts +137 -2
  100. package/dist/props/registry.d.ts +116 -5
  101. package/dist/props/registry.js +165 -4
  102. package/dist/props/vocabulary/index.d.ts +2 -2
  103. package/dist/props/vocabulary/interaction.prop.d.ts +15 -0
  104. package/dist/props/vocabulary/layout.prop.d.ts +8 -0
  105. package/dist/styles/alert-layout.css +69 -3
  106. package/dist/styles/badge-layout.css +2 -2
  107. package/dist/styles/base.css +6 -0
  108. package/dist/styles/card-layout.css +25 -8
  109. package/dist/styles/control.css +1150 -29
  110. package/dist/styles/data-display-layout.css +185 -11
  111. package/dist/styles/data-entry-layout.css +352 -1
  112. package/dist/styles/density.css +2 -2
  113. package/dist/styles/dialog-layout.css +203 -4
  114. package/dist/styles/focus-ring.css +155 -0
  115. package/dist/styles/form-layout.css +50 -5
  116. package/dist/styles/index.css +1 -0
  117. package/dist/styles/layout.css +412 -23
  118. package/dist/styles/motion.css +178 -0
  119. package/dist/styles/navigation-layout.css +398 -22
  120. package/dist/styles/shell-layout.css +137 -39
  121. package/dist/styles/table-layout.css +153 -22
  122. package/dist/styles/toggle.css +59 -0
  123. package/dist/tokens/base.css +5 -0
  124. package/dist/tokens/components/activity.css +64 -0
  125. package/dist/tokens/components/badge.css +7 -0
  126. package/dist/tokens/components/card.css +14 -10
  127. package/dist/tokens/components/chart.css +1 -1
  128. package/dist/tokens/components/control.css +358 -8
  129. package/dist/tokens/components/data-display.css +75 -0
  130. package/dist/tokens/components/data-entry.css +25 -0
  131. package/dist/tokens/components/descriptions.css +22 -0
  132. package/dist/tokens/components/feedback.css +143 -4
  133. package/dist/tokens/components/form.css +16 -0
  134. package/dist/tokens/components/legal-document.css +5 -1
  135. package/dist/tokens/components/navigation.css +109 -0
  136. package/dist/tokens/components/scroll-area.css +21 -0
  137. package/dist/tokens/components/separator.css +52 -0
  138. package/dist/tokens/components/shell.css +132 -20
  139. package/dist/tokens/components/table.css +52 -3
  140. package/dist/tokens/components/toggle.css +82 -0
  141. package/dist/tokens/components/upload.css +107 -0
  142. package/dist/tokens/foundation.css +171 -10
  143. package/dist/tokens/semantic/layout.css +120 -1
  144. package/package.json +84 -68
  145. package/scripts/ui-audit.mjs +51 -2
@@ -10,6 +10,25 @@
10
10
  * modal Dialog. Defaults resolve to rgb(0 0 0 / 0.2) (sheet) and rgb(0 0 0 / 0.3) (dialog). */
11
11
  --sheet-overlay-background: initial;
12
12
  --sheet-overlay-alpha: 40%;
13
+ /* Sheet chrome rhythm (#319). --sheet-pad-x/-y already governed the panel inset, but the gaps
14
+ * INSIDE the chrome — header stack, title/subtitle pair, the extra-slot row — were literal, so
15
+ * a service could retune the sheet's outer padding and still be stuck with the inner rhythm. */
16
+ --sheet-shadow: var(--shadow-lg);
17
+ --sheet-header-space-gap: 0.375rem;
18
+ --sheet-title-block-space-gap: var(--space-1);
19
+ --sheet-title-row-space-gap: var(--space-3);
20
+ --sheet-extra-space-gap: var(--space-2);
21
+ --sheet-footer-space-gap: var(--space-2);
22
+ --sheet-title-font-size: var(--font-size-lg);
23
+ --sheet-description-font-size: var(--font-size-sm);
24
+ /* The scroll region keeps a sliver of padding so a focused control's ring is never clipped at
25
+ * the scroll edges — retune together with --focus-ring-width. */
26
+ --sheet-body-space-block: var(--space-1);
27
+ /* Close button: offset from the panel corner, and the inline room the header reserves so a
28
+ * title never runs under it. */
29
+ --sheet-close-offset: var(--space-4);
30
+ --sheet-close-rest-alpha: 0.7;
31
+ --sheet-header-close-space-inline-end: var(--space-8);
13
32
  --dialog-width-default: 32rem;
14
33
  --dialog-viewport-inset: 1.5rem;
15
34
  --dialog-overlay-background: initial;
@@ -21,6 +40,26 @@
21
40
  --dialog-space-inset: var(--dialog-space-y) var(--dialog-space-x);
22
41
  --dialog-space-gap: var(--space-stack-md);
23
42
  --dialog-close-space-offset: var(--space-4);
43
+ /* Dialog chrome rhythm (#319) — the exact mirror of the --sheet-* set above, because Dialog's
44
+ * header markup is byte-identical to Sheet's. --dialog-space-x/-y already governed the panel
45
+ * inset, but every gap INSIDE the chrome (header stack, title/subtitle pair, the extra-slot row)
46
+ * was a literal on the component, so a service could retune the outer padding and still be stuck
47
+ * with the inner rhythm. Defaults are the values the literals resolved to. */
48
+ --dialog-header-space-gap: var(--space-stack-xs);
49
+ --dialog-title-row-space-gap: var(--space-3);
50
+ --dialog-title-block-space-gap: var(--space-1);
51
+ --dialog-extra-space-gap: var(--space-2);
52
+ /* Inline room the header reserves so a long title or the extra slot never runs under the
53
+ * absolutely-positioned close button; retune together with --dialog-close-space-offset.
54
+ * Mirrors --sheet-header-close-space-inline-end. */
55
+ --dialog-header-close-space-inline-end: var(--space-8);
56
+ /* Close button rest opacity — quiet at rest, full on hover (rule #44). Was a hard 0.7 baked into
57
+ * dialog-layout.css with no way for a theme to make the ✕ louder. Mirrors
58
+ * --sheet-close-rest-alpha so the two overlay siblings retune together. */
59
+ --dialog-close-rest-alpha: 0.7;
60
+ /* Type-to-confirm challenge: the step-up failure line under the input. Its size + ink were
61
+ * `text-sm text-destructive` on the component, i.e. unreachable from a theme. */
62
+ --dialog-step-up-error-font-size: var(--font-size-sm);
24
63
  /* Alert corner radius (gh#268 — rule #45): a full-width Alert often sits in the
25
64
  * same page column as a Card — so the DEFAULT IS the Card radius (gh#282); a service re-tunes by
26
65
  * overriding this once. Default keeps the historical --radius-md. */
@@ -29,17 +68,38 @@
29
68
  --alert-space-gap: var(--space-inline-md);
30
69
  --alert-inner-space-gap: var(--space-stack-sm);
31
70
  --alert-dismiss-space-offset: var(--space-3);
71
+ /* Leading tone glyph (the ⚠/✓/ⓘ at the start of the strip). It was a bare `1.25rem` in
72
+ * alert-layout.css with no token at all, so the ONLY ways to resize it were `!important` or a
73
+ * forked stylesheet — the two things the icon axis's tier 2 exists to abolish (gh#326). Same
74
+ * step, so nothing moves; a service now scopes `--alert-icon-size` to one Alert instead.
75
+ * Deliberately NOT --scaling-multiplied: the literal it replaces did not track density. */
76
+ --alert-icon-size: var(--icon-size-lg);
77
+ /* Dismiss ✕ — rest opacity (quiet at rest, full on hover, rule #44) and glyph size. The rest
78
+ * alpha was a hard 0.7 in alert-layout.css whose `:hover` companion lived on the COMPONENT as
79
+ * `hover:opacity-100`, so the two halves of one affordance sat in two files and neither was
80
+ * themeable. Mirrors --sheet-close-rest-alpha. The icon size carries a raw rem, not
81
+ * var(--space-N): it replaces a flat Tailwind `size-4` step and must not start tracking the
82
+ * density axis. */
83
+ --alert-dismiss-rest-alpha: 0.7;
84
+ --alert-dismiss-icon-size: var(--icon-size-md);
32
85
  /* Soft (subtle) semantic tint ratios — themeable so a service can hit its exact spec
33
86
  * (a brand's success-bg/-border are often more present than the faint 5%/30% default). */
34
87
  --alert-bg-alpha: 0.05;
35
88
  --alert-border-alpha: 0.3;
36
89
  /* BANNER — the page-level Alert treatment (gh#255). Every constant a service would want to
37
- * match to its page grid is a knob (rule #45): a banner that must keep the app's rounded card
38
- * language sets --banner-radius, one that rules its top edge too sets --banner-border-width.
90
+ * match to its page grid is a knob (rule #45).
91
+ *
92
+ * --banner-radius and --banner-border-width are NOT declared here (gh#327). They used to be,
93
+ * and both copies were dead: components/banner.css declares the same two names at the same
94
+ * `:root`, base.css imports banner AFTER feedback, so banner won every time. Radius agreed at
95
+ * `0`, but border-width did not — this file said `0` and banner.css says `1px`, and `1px` is
96
+ * what alert-layout.css actually paints as the strip's block-end hairline. So anyone who read
97
+ * THIS file for the default was told the strip has no rule when it has one, and the MCP token
98
+ * catalog carried both entries with the two conflicting values. banner.css owns the strip
99
+ * geometry; these two live there and only there.
100
+ *
39
101
  * The inline inset tracks the live page gutter, so a banner mounted above a PageContainer lines
40
102
  * its text up with the page title instead of sitting at a second, unrelated margin. */
41
- --banner-radius: 0;
42
- --banner-border-width: 0;
43
103
  --banner-border-block-end-width: 1px;
44
104
  --banner-space-block: var(--space-3);
45
105
  /* Inline inset tracks the page gutter so a banner's text lines up with the page title.
@@ -73,6 +133,12 @@
73
133
  * Defaults = hsl(var(--muted-foreground)) glyph · hsl(var(--muted)) fill. */
74
134
  --empty-state-icon-foreground: initial;
75
135
  --empty-state-icon-tint: initial;
136
+ /* Medallion box + the glyph inside it. Both were literal (`width: 3rem` in the stylesheet, a
137
+ * `size-6` utility on the icon), so a service could not scale the empty-state mark to its own
138
+ * page rhythm — and the two must move TOGETHER or the glyph stops sitting centred in its
139
+ * circle, which is exactly the kind of pair rule #45 exists to keep tunable as one. */
140
+ --empty-state-icon-size: var(--icon-size-4xl);
141
+ --empty-state-icon-glyph-size: var(--icon-size-xl);
76
142
  --skeleton-row-gap: var(--space-stack-sm);
77
143
  --skeleton-cell-gap: var(--space-inline-lg);
78
144
  --skeleton-card-inset: var(--space-section-active);
@@ -81,4 +147,77 @@
81
147
  * a scoped theme (a :root binding to a role var freezes at :root). A service tints the shimmer to
82
148
  * its surface (rule #44) without forking the keyframes. Default = hsl(var(--muted)). */
83
149
  --skeleton-background: initial;
150
+ /* Skeleton bar heights + widths (#319). These carry RAW rem, not var(--space-N): the literals
151
+ * they replace were plain Tailwind steps, which are not multiplied by --scaling. Routing them
152
+ * through the spacing scale would silently make the loading state track the density axis while
153
+ * the loaded content it stands in for does not. Names say what each bar STANDS IN FOR, so a
154
+ * service retuning its type scale knows which one to move. */
155
+ --skeleton-block-height: 1rem;
156
+ --skeleton-caption-height: 0.75rem;
157
+ --skeleton-title-height: 1.75rem;
158
+ --skeleton-label-width: 6rem;
159
+ --skeleton-detail-value-max-width: 28rem;
160
+ --skeleton-stat-value-width: 8rem;
161
+ --skeleton-stat-caption-width: 5rem;
162
+
163
+ /* QUERY LIFECYCLE FOOTERS — the two rows `<InfiniteQueryState>` appends under a paginated feed:
164
+ * the "load more" button row and the "loading more…" caption. Both gaps were Tailwind literals
165
+ * on the component (`pt-4` / `pt-2`), so a service running a denser (or airier) feed could not
166
+ * align them to its own vertical rhythm without forking the component (rule #45). They live in
167
+ * the feedback tier because these ARE query feedback surfaces and there is no query stylesheet.
168
+ * Defaults = var(--space-stack-md) (16px) and var(--space-stack-sm) (8px) — the exact values the
169
+ * Tailwind steps resolved to, so adopting this changes nothing until a theme opts in. */
170
+ --query-load-more-space-block-start: var(--space-stack-md);
171
+ --query-loading-more-space-block-start: var(--space-stack-sm);
172
+
173
+ /* TOOLTIP — the transient label surface. Every constant here was a Tailwind literal baked into
174
+ * the component (`max-w-xs px-2 py-1 rounded-md text-xs shadow-md`), so a service could not
175
+ * retune tooltip density or measure without forking the component (rule #45). Defaults reproduce
176
+ * the previous look exactly, so adopting this changes nothing until a theme opts in. */
177
+ --tooltip-max-width: 20rem;
178
+ --tooltip-space-inline: var(--space-2);
179
+ --tooltip-space-block: var(--space-1);
180
+ --tooltip-radius: var(--radius);
181
+ --tooltip-font-size: var(--font-size-xs);
182
+ /* Raised-surface depth — role-mirror knob, so a flat service theme sets `none` once. */
183
+ --tooltip-shadow: var(--shadow-md);
184
+ /* Surface colours — `initial` so the popover roles re-resolve at the CALL SITE under a scoped
185
+ * [data-tenant]/.dark theme (docs/TOKENS.md · "Role-mirror knobs MUST be `initial`").
186
+ * Defaults = hsl(var(--popover)) fill · hsl(var(--popover-foreground)) text. */
187
+ --tooltip-background: initial;
188
+ --tooltip-foreground: initial;
189
+ --tooltip-border-color: initial;
190
+
191
+ /* POPOVER — the interactive overlay panel. Same story as Tooltip: `w-72 rounded-md p-4 shadow-md`
192
+ * were literals, so a service could not align the panel to its own grid or width scale. */
193
+ --popover-width: 18rem;
194
+ --popover-space-inset: var(--space-4);
195
+ --popover-radius: var(--radius);
196
+ --popover-shadow: var(--shadow-md);
197
+ --popover-header-space-gap: var(--space-1);
198
+ --popover-header-font-size: var(--font-size-sm);
199
+ /* Role-mirror knobs — `initial`, defaults resolve at the call site (see Tooltip above). */
200
+ --popover-surface-background: initial;
201
+ --popover-surface-foreground: initial;
202
+ --popover-surface-border-color: initial;
203
+
204
+ /* TOAST (Sonner) — the status glyph in the toast's leading slot.
205
+ *
206
+ * WHY A KNOB AND NOT A UTILITY: sonner renders the toast body itself and takes the five status
207
+ * glyphs through ONE `icons={{ success, info, warning, error, loading }}` config prop. That prop
208
+ * is all-or-nothing — a consumer who wants a different glyph size must re-declare all five icons,
209
+ * re-importing lucide and re-deriving the aria wiring. Routing the size through a token makes it
210
+ * a one-line theme override instead (rule #45).
211
+ *
212
+ * Raw rem, not var(--space-N): it replaces a flat Tailwind `size-4` step, and the 16px box sonner
213
+ * gives `[data-icon]` is itself fixed — scaling the glyph with density alone would overflow it.
214
+ *
215
+ * NAMESPACE NOTE: sonner publishes its own `--toast-*` custom properties (--toast-icon-margin-*,
216
+ * --toast-svg-margin-*, --toast-button-margin-*, --toast-close-button-*). Neither name below
217
+ * collides with those; keep it that way when adding to this group. */
218
+ --toast-icon-size: var(--icon-size-md);
219
+ /* Viewport gutter of the mobile toast stack. Sonner passes `mobileOffset` straight into inline
220
+ * CSS, so a var() string resolves normally. Flat 16px on purpose: this is a fixed inset from the
221
+ * device edge (thumb reach / safe area), not a density-scaled gap inside a surface. */
222
+ --toast-mobile-offset: 16px;
84
223
  }
@@ -31,4 +31,20 @@
31
31
  * which only reaches DIRECT children — dead in the common `Form > CardContent > FormField*`
32
32
  * composition every real Save-button form needs), so it holds at any DOM depth relative to Form. */
33
33
  --form-field-row-gap: var(--space-3); /* 12px */
34
+
35
+ /* Row rhythm between the ROWS of a `<Form columns={n}>` grid (gh#304). The grid path cannot use
36
+ * the margin above — a per-item margin inside a grid double-counts the track gap AND leaves the
37
+ * first item of row 1 unshifted while its row-mates drop, so row 1's columns end up misaligned.
38
+ * The rhythm therefore rides the grid's own `row-gap`, and it DEFAULTS TO THE SAME
39
+ * --form-field-row-gap the stacked path uses so `columns={1}` — and every `columns={n}` form once
40
+ * it collapses to one column on a narrow container — is pixel-identical to a Form with no
41
+ * `columns` at all. Retune this alone only to give multi-column forms a looser row rhythm than
42
+ * stacked ones; retune --form-field-row-gap to move both together. */
43
+ --form-grid-row-gap: var(--form-field-row-gap); /* 12px */
44
+
45
+ /* Gutter between the COLUMNS of a `<Form columns={n}>` grid (gh#304). Was ResponsiveGrid's
46
+ * generic 16px stack gap with no way to say otherwise; it is a form's inter-field gutter and a
47
+ * service aligning forms to its design grid needs it as a knob (rule #45). The default keeps the
48
+ * historical 16px, so nothing moves unless a theme opts in. */
49
+ --form-grid-column-gap: var(--space-4); /* 16px */
34
50
  }
@@ -21,6 +21,10 @@
21
21
  /* ── Geometry ─────────────────────────────────────────────────────────────── */
22
22
  /* Readable document measure. Caps the header, the section column and the footer so
23
23
  * a 1440px viewport still reads at ~75-90 characters per line. */
24
+ /* Section anchor ring needs a gap so the mark does not touch the heading
25
+ * (outline form — styles/focus-ring.css). */
26
+ --legal-document-section-focus-ring-offset: var(--space-1);
27
+
24
28
  --legal-document-measure-max-width: 46rem;
25
29
  /* Rail ↔ document column gutter (two-column mode only). */
26
30
  --legal-document-column-gap: var(--space-10);
@@ -37,7 +41,7 @@
37
41
  --legal-document-toc-font-size: var(--font-size-xs);
38
42
  /* Leading marker on the active contents entry — a non-colour affordance so the
39
43
  * active state is never colour-only (WCAG 1.4.1). */
40
- --legal-document-toc-marker-width: 2px;
44
+ --legal-document-toc-marker-width: var(--stroke-md);
41
45
  /* Gap between the `documentNavigation` slot and the contents list inside the rail. */
42
46
  --legal-document-nav-gap: var(--space-4);
43
47
  --legal-document-header-gap: var(--space-2);
@@ -7,7 +7,19 @@
7
7
  * localized labels (ja 100/ページ) never clip; this token only keeps short labels from
8
8
  * collapsing below the control rhythm. */
9
9
  --pagination-size-width: 5.5rem;
10
+ /* Inline padding on a page button. The button keeps min-width: var(--control-height), so this
11
+ * only takes effect once a number outgrows that box. Deliberately --space-1 rather than
12
+ * --space-2: at the wider value a two-digit label (14.8px of text + 2 * 8px + 2px border) came
13
+ * to 32.8px and pushed the common 1–2 digit buttons off square by 0.8px. */
14
+ --pagination-page-padding-x: var(--space-1);
10
15
  --pagination-total-font-size: var(--font-size-sm);
16
+ /* Chevron glyph inside the `simple` form's Prev/Next Buttons (#319). Those are size="sm"
17
+ * Buttons, so `.ui-button--sm svg` would draw them at --control-icon-size-sm; the component
18
+ * pinned them larger with a literal `size-4` class instead, because in simple mode the chevron
19
+ * IS the control — there is no label beside it to carry the affordance. The knob keeps that
20
+ * deliberate override themeable (rule #45) and tracks the default control-icon tier, so it now
21
+ * follows density like every other control glyph instead of staying a flat 1rem. */
22
+ --pagination-icon-size: var(--control-icon-size);
11
23
  --filter-bar-gap: var(--space-3);
12
24
  --filter-bar-padding-y: var(--space-2);
13
25
  --filter-label-font-size: var(--font-size-xs);
@@ -51,6 +63,28 @@
51
63
  --tabs-indicator-size: 2px;
52
64
  --tabs-indicator-offset: 0px;
53
65
 
66
+ /* TABS BOX GEOMETRY (#319) — the strip↔panel gap and the whole `line` trigger box were Tailwind
67
+ * scale literals on tabs.tsx, so a service could only retune the tab rhythm by forking the
68
+ * component (rule #45).
69
+ * Every default below is a RAW rem/px, deliberately NOT a --space-* step: the classes they
70
+ * replace (`gap-2`, `p-0`, `rounded-none`, `px-4`, `py-2`) read Tailwind's own --spacing grid,
71
+ * which this repo does NOT density-scale, so a --space-* default would silently resize the tabs
72
+ * under a compact/comfortable density. A service that WANTS a knob to follow density simply
73
+ * points it at var(--space-2) itself. */
74
+
75
+ /* Gap between the tab strip and the panel below it. */
76
+ --tabs-root-gap: 0.5rem;
77
+ /* Inset of the `line` strip. The default/pill strip keeps its own padding box; the line strip is
78
+ * a flat underlined rail, so its default is the quietest value — none (rule #44). Raise it to
79
+ * give the rail breathing room above its hairline. */
80
+ --tabs-list-line-space-inset: 0px;
81
+ /* `line` trigger box. Square corners because the selected state is the token-owned ::after bar,
82
+ * never a pill; the hit box is wider/taller than the pill trigger so the underline spans a real
83
+ * column of the rail. */
84
+ --tabs-trigger-line-radius: 0px;
85
+ --tabs-trigger-line-padding-x: 1rem;
86
+ --tabs-trigger-line-padding-y: 0.5rem;
87
+
54
88
  /* Menu item hover/highlight tint — `initial` so the --accent default re-resolves at the call site
55
89
  * under a scoped theme (a :root binding to a role var freezes at :root).
56
90
  * Defaults = hsl(var(--accent)) fill · hsl(var(--accent-foreground)) text. */
@@ -78,4 +112,79 @@
78
112
  --filter-bar-filter-width: var(--filter-picker-width-md);
79
113
  --filter-bar-chip-gap: var(--space-2);
80
114
  --filter-bar-section-gap: var(--space-2);
115
+
116
+ /* MENU SURFACES — one row rhythm shared by ContextMenu, Menubar and DropdownMenu. All three are
117
+ * the same Radix popup surface; the row height was a literal `2rem` in the CSS and DropdownMenu
118
+ * had not been converted at all, so it carried the whole box as Tailwind literals on the
119
+ * component (#319). A service tunes the menu rhythm once here instead of three times. */
120
+ --menu-item-height: var(--band-height-md);
121
+ --menu-item-radius: var(--radius-sm);
122
+ --menu-item-space-inline: var(--space-2);
123
+ --menu-item-space-gap: var(--space-2);
124
+ --menu-item-font-size: var(--font-size-base);
125
+ /* Inset rows (a checkbox/radio row without its own indicator) reserve the indicator column so
126
+ * labels stay aligned with their checked siblings. */
127
+ --menu-item-inset-space-inline-start: var(--space-8);
128
+ --menu-indicator-size: 0.875rem;
129
+ --menu-indicator-inset-inline-start: var(--space-2);
130
+ --menu-icon-size: var(--icon-size-md);
131
+ --menu-content-space-inset: var(--space-1);
132
+ --menu-content-min-width: 10rem;
133
+ /* DropdownMenu is anchored to a small trigger, so it opens narrower than a context menu. */
134
+ --dropdown-content-min-width: 8rem;
135
+ --menu-separator-space-block: var(--space-1);
136
+ --menu-separator-space-inline: calc(var(--space-1) * -1);
137
+
138
+ /* NAVIGATION MENU — the disclosure chevron on a top-level trigger. It was a bare `0.9rem` in
139
+ * navigation-layout.css: no token, so unreachable from an app (gh#326), and 14.4px, so off the
140
+ * icon scale AND off the pixel grid. A stroked chevron drawn into a 14.4px box lands its path
141
+ * on half pixels at 1x, which is a rendering defect rather than a rounding preference, so this
142
+ * SNAPS to the nearest step — --icon-size-sm, 14px, −0.4px. The nearest step upward is 16px
143
+ * (+1.6px) and would also fight the `opacity: 0.7` that makes this chevron deliberately quiet.
144
+ * `sm` rather than `md` is the same call the other quiet chevrons in the system already made
145
+ * (--month-picker-separator-icon-size, --steps-inline-separator-size). NOT
146
+ * --scaling-multiplied: the literal it replaces did not track density. */
147
+ --navigation-menu-trigger-icon-size: var(--icon-size-sm);
148
+
149
+ /* STEPS — the full (non-inline) variant. `--steps-inline-*` already covers the compact inline
150
+ * form; the marker, connector and text rhythm of the main variant were still literal on the
151
+ * component (#319), so a service could not resize the dot, retighten the vertical run, or move
152
+ * the horizontal connector to match its own grid. */
153
+ --steps-dot-size: 0.625rem;
154
+ --steps-dot-process-ring-width: var(--stroke-xl);
155
+ --steps-marker-border-width: var(--stroke-md);
156
+ --steps-marker-font-size: var(--font-size-sm);
157
+ --steps-marker-icon-size: var(--icon-size-md);
158
+ --steps-wait-icon-size: var(--icon-size-xs);
159
+ --steps-title-font-size: var(--font-size-sm);
160
+ --steps-title-font-size-compact: var(--font-size-xs);
161
+ --steps-subtitle-font-size: var(--font-size-xs);
162
+ /* Vertical run: gap between marker and text, and the drop to the next step. */
163
+ --steps-vertical-space-gap: var(--space-3);
164
+ --steps-vertical-space-block-end: var(--space-8);
165
+ --steps-vertical-text-space-block-start: var(--space-1);
166
+ /* Horizontal run: the text sits under the marker, and the connector spans the gap between
167
+ * two markers — inset by the marker radius on each side so it never runs under a marker. */
168
+ --steps-horizontal-text-space-block-start: var(--space-2);
169
+ --steps-horizontal-text-space-inline: var(--space-2);
170
+ --steps-connector-inset: 1.25rem;
171
+ --steps-connector-space-block-start: var(--space-4);
172
+
173
+ /* APP SETTING PICKER — per-kind trigger widths (#319). Each picker is sized to the longest
174
+ * value it can show: a timezone name is far wider than a theme name. These were literal
175
+ * `sm:w-*` steps in a lookup table, so a service whose locale renders longer labels (a German
176
+ * timezone list, a Japanese density label) could not widen just the one that overflows. */
177
+ --app-setting-picker-locale-width: 10rem;
178
+ --app-setting-picker-timezone-width: 14rem;
179
+ --app-setting-picker-date-format-width: 11rem;
180
+ --app-setting-picker-time-format-width: 11rem;
181
+ --app-setting-picker-theme-width: 9rem;
182
+ --app-setting-picker-brand-width: 11rem;
183
+ --app-setting-picker-density-width: 10rem;
184
+ --app-setting-picker-font-size-width: 9rem;
185
+ /* Below this the trigger hugs its content instead of taking the per-kind width, so a picker
186
+ * dropped into a narrow topbar never stretches the bar (gh#165). */
187
+ --app-setting-picker-width-breakpoint: 40rem;
188
+ --app-setting-picker-icon-size: var(--icon-size-md);
189
+ --app-setting-picker-icon-rest-alpha: 0.7;
81
190
  }
@@ -0,0 +1,21 @@
1
+ /* SCROLL AREA — the bottom-anchoring contract (gh#311).
2
+ *
3
+ * The rail geometry (--scroll-area-bar-size / --scroll-area-bar-padding / --scroll-area-thumb-radius)
4
+ * lives with the other data-display knobs in ./data-display.css, because it is styled from
5
+ * styles/data-display-layout.css. What lives HERE is the one constant that is read by JavaScript
6
+ * rather than by CSS, so it needs no layout rule to hang off.
7
+ */
8
+ :root {
9
+ /* How close to the bottom edge the reader still counts as "following the stream" for
10
+ * `<ScrollArea anchor="bottom">`. Inside this band new content keeps the viewport pinned to the
11
+ * newest item; one pixel beyond it the reader is reading history and anchoring NEVER moves them
12
+ * again until they come back (WCAG 3.2.5 — no change of context on request of the machine).
13
+ *
14
+ * It is a rule #45 knob because the right distance is a function of the row height a service
15
+ * renders: one line of a dense audit log is ~20px, a chat bubble with an avatar is ~64px, and
16
+ * "one row from the bottom" is what the reader actually means. Expressed in rem on purpose — it
17
+ * then tracks the user's font size, so the band is still one row at 200% zoom (WCAG 1.4.4)
18
+ * instead of collapsing to a third of a row. The `anchorOffset` prop overrides it per instance;
19
+ * px/rem/em are all accepted. */
20
+ --scroll-area-anchor-offset: 3rem;
21
+ }
@@ -0,0 +1,52 @@
1
+ /* Separator component tokens (gh#308) — the tokenized rule AND the LABELLED rule that a `label`
2
+ * interrupts: a message stream's day divider, an unread "new messages" watermark, an auth
3
+ * conjunction. Before #308 the only labelled rule in the library was the auth-scoped AuthDivider,
4
+ * whose rule baked `height: 1px` into shell-layout.css and whose type ramp lived on the
5
+ * `--auth-shell-divider-*` layer — so a service that retuned its login divider silently retuned
6
+ * every day divider in its chat. These knobs are the generic layer; AuthDivider now re-points them.
7
+ *
8
+ * Chrome defaults to the QUIETEST state (rule #44): one hairline, a muted xs label, no surrounding
9
+ * padding. A service opts IN to a louder watermark from its theme, never by forking CSS. */
10
+ :root {
11
+ /* Rule weight, both orientations and both halves of a labelled rule. */
12
+ --separator-rule-size: var(--stroke-hairline);
13
+ /* Rule colour. Role-mirror knob → declared `initial` so the --border default re-resolves at the
14
+ * CALL SITE under a scoped [data-tenant] / .dark theme (docs/TOKENS.md — the :root freeze rule).
15
+ * Documented default = hsl(var(--border)). */
16
+ --separator-rule-color: initial;
17
+ /* Gap between the label and each rule half. `initial` so the default re-resolves at the call
18
+ * site: --space-* is density-scaled and re-declared inside a `.ui-density-*` subtree, so a
19
+ * :root binding would freeze the gap at the :root density. Documented default = var(--space-3). */
20
+ --separator-label-gap: initial;
21
+ /* Length of the SHORT rule half when labelAlign is `start` / `end` — the label's inset from the
22
+ * inline edge (the Slack/Mattermost stream convention). It is a grid track on the INLINE axis,
23
+ * so it flips with the writing direction under dir="rtl" with no extra rule. `initial` for the
24
+ * same density reason as the gap. Documented default = var(--space-6). */
25
+ --separator-label-inset: initial;
26
+ /* Label type ramp (#46) — steps of the modular scale, never a literal. `xs` is the quiet
27
+ * stream/divider voice; --separator-label-line-height keeps ja/vi descenders and tone marks
28
+ * intact (cf. #254); --separator-label-font-weight lets a service raise an unread watermark
29
+ * without touching the day divider. */
30
+ --separator-label-font-size: var(--font-size-xs);
31
+ --separator-label-line-height: var(--line-height-normal);
32
+ --separator-label-font-weight: var(--font-weight-medium);
33
+ /* Label colour. Role-mirror → `initial`; documented default = hsl(var(--muted-foreground)). */
34
+ --separator-label-color: initial;
35
+ /* Per-tone rule + label colours. `tone` re-points BOTH the rule and the label — never the rule
36
+ * alone — so an attention rule such as an unread watermark is not colour-only (WCAG 1.4.1) and
37
+ * survives forced-colors. All role-mirror → `initial`; documented defaults are the matching
38
+ * semantic role. `muted` deliberately mirrors the quiet default so a service can still split the
39
+ * two voices later without a breaking change. */
40
+ --separator-tone-muted-rule-color: initial;
41
+ --separator-tone-muted-label-color: initial;
42
+ --separator-tone-primary-rule-color: initial;
43
+ --separator-tone-primary-label-color: initial;
44
+ --separator-tone-success-rule-color: initial;
45
+ --separator-tone-success-label-color: initial;
46
+ --separator-tone-warning-rule-color: initial;
47
+ --separator-tone-warning-label-color: initial;
48
+ --separator-tone-destructive-rule-color: initial;
49
+ --separator-tone-destructive-label-color: initial;
50
+ --separator-tone-info-rule-color: initial;
51
+ --separator-tone-info-label-color: initial;
52
+ }