@godxjp/ui 18.4.0 → 18.6.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 (159) hide show
  1. package/README.md +16 -1
  2. package/dist/app/theme-axes.d.ts +11 -3
  3. package/dist/app/theme-axes.js +2 -1
  4. package/dist/components/charts/chart-frame.d.ts +17 -3
  5. package/dist/components/charts/chart-frame.js +7 -1
  6. package/dist/components/charts/chart-summary.d.ts +21 -0
  7. package/dist/components/charts/chart-summary.js +32 -5
  8. package/dist/components/charts/compact-bar-trend.d.ts +30 -0
  9. package/dist/components/charts/compact-bar-trend.js +74 -0
  10. package/dist/components/charts/index.d.ts +6 -0
  11. package/dist/components/charts/index.js +2 -0
  12. package/dist/components/data-display/badge.d.ts +8 -0
  13. package/dist/components/data-display/badge.js +7 -1
  14. package/dist/components/data-display/data-table.d.ts +40 -4
  15. package/dist/components/data-display/data-table.js +69 -2
  16. package/dist/components/data-display/empty-state.js +1 -1
  17. package/dist/components/data-display/index.d.ts +4 -2
  18. package/dist/components/data-display/index.js +10 -1
  19. package/dist/components/data-display/list-row.d.ts +41 -4
  20. package/dist/components/data-display/list-row.js +23 -3
  21. package/dist/components/data-display/qr-code.d.ts +1 -1
  22. package/dist/components/data-display/service-launcher-card.d.ts +40 -0
  23. package/dist/components/data-display/service-launcher-card.js +97 -0
  24. package/dist/components/data-display/table.d.ts +47 -2
  25. package/dist/components/data-display/table.js +29 -4
  26. package/dist/components/data-entry/command-palette.d.ts +37 -0
  27. package/dist/components/data-entry/command-palette.js +125 -0
  28. package/dist/components/data-entry/command.d.ts +10 -10
  29. package/dist/components/data-entry/index.d.ts +3 -0
  30. package/dist/components/data-entry/index.js +2 -0
  31. package/dist/components/data-entry/input-otp.d.ts +1 -0
  32. package/dist/components/feedback/dialog.d.ts +16 -1
  33. package/dist/components/feedback/dialog.js +46 -33
  34. package/dist/components/feedback/index.d.ts +5 -2
  35. package/dist/components/feedback/index.js +8 -2
  36. package/dist/components/feedback/sheet.d.ts +26 -4
  37. package/dist/components/feedback/sheet.js +54 -7
  38. package/dist/components/feedback/two-factor-setup.d.ts +31 -0
  39. package/dist/components/feedback/two-factor-setup.js +97 -0
  40. package/dist/components/general/index.d.ts +1 -1
  41. package/dist/components/general/logo.d.ts +49 -9
  42. package/dist/components/general/logo.js +67 -14
  43. package/dist/components/layout/app-shell.d.ts +1 -1
  44. package/dist/components/layout/app-shell.js +55 -30
  45. package/dist/components/layout/auth-account-summary.d.ts +5 -0
  46. package/dist/components/layout/auth-account-summary.js +31 -0
  47. package/dist/components/layout/auth-divider.d.ts +7 -0
  48. package/dist/components/layout/auth-divider.js +21 -0
  49. package/dist/components/layout/auth-footer.d.ts +5 -0
  50. package/dist/components/layout/auth-footer.js +24 -0
  51. package/dist/components/layout/auth-identity.d.ts +5 -0
  52. package/dist/components/layout/auth-identity.js +17 -0
  53. package/dist/components/layout/auth-shell.d.ts +9 -1
  54. package/dist/components/layout/auth-shell.js +25 -6
  55. package/dist/components/layout/auth-stack.d.ts +4 -0
  56. package/dist/components/layout/auth-stack.js +8 -0
  57. package/dist/components/layout/centered-shell.d.ts +5 -2
  58. package/dist/components/layout/centered-shell.js +29 -19
  59. package/dist/components/layout/error-surface.d.ts +35 -0
  60. package/dist/components/layout/error-surface.js +155 -0
  61. package/dist/components/layout/flex.d.ts +1 -1
  62. package/dist/components/layout/flex.js +4 -0
  63. package/dist/components/layout/index.d.ts +23 -3
  64. package/dist/components/layout/index.js +21 -2
  65. package/dist/components/layout/legal-document-shell.d.ts +34 -0
  66. package/dist/components/layout/legal-document-shell.js +186 -0
  67. package/dist/components/layout/master-detail.d.ts +24 -0
  68. package/dist/components/layout/master-detail.js +48 -0
  69. package/dist/components/layout/org-switcher.d.ts +9 -0
  70. package/dist/components/layout/org-switcher.js +218 -0
  71. package/dist/components/layout/page-container.d.ts +1 -1
  72. package/dist/components/layout/page-container.js +7 -2
  73. package/dist/components/layout/sidebar-link.d.ts +43 -0
  74. package/dist/components/layout/sidebar-link.js +13 -0
  75. package/dist/components/layout/sidebar.d.ts +42 -5
  76. package/dist/components/layout/sidebar.js +170 -85
  77. package/dist/components/navigation/app-setting-picker.js +17 -6
  78. package/dist/components/navigation/filter-bar.d.ts +12 -3
  79. package/dist/components/navigation/filter-bar.js +8 -2
  80. package/dist/components/navigation/index.d.ts +2 -2
  81. package/dist/components/navigation/index.js +3 -1
  82. package/dist/components/navigation/steps.js +38 -19
  83. package/dist/components/navigation/tabs-scroll.d.ts +70 -0
  84. package/dist/components/navigation/tabs-scroll.js +76 -0
  85. package/dist/components/navigation/tabs.js +47 -23
  86. package/dist/components/ui/avatar.d.ts +2 -1
  87. package/dist/components/ui/avatar.js +2 -1
  88. package/dist/components/ui/input-otp.d.ts +4 -1
  89. package/dist/components/ui/input-otp.js +2 -1
  90. package/dist/components/ui/toggle-group.d.ts +7 -4
  91. package/dist/components/ui/toggle-group.js +32 -21
  92. package/dist/email/brand-mark.d.ts +108 -0
  93. package/dist/email/brand-mark.js +88 -0
  94. package/dist/email/color.d.ts +53 -0
  95. package/dist/email/color.js +37 -0
  96. package/dist/email/geometry.d.ts +120 -0
  97. package/dist/email/geometry.js +90 -0
  98. package/dist/email/index.d.ts +83 -0
  99. package/dist/email/index.js +82 -0
  100. package/dist/email/inline-style.d.ts +27 -0
  101. package/dist/email/inline-style.js +26 -0
  102. package/dist/email/tokens.generated.d.ts +167 -0
  103. package/dist/email/tokens.generated.js +162 -0
  104. package/dist/email/urgency.d.ts +22 -0
  105. package/dist/email/urgency.js +14 -0
  106. package/dist/i18n/messages/en.json +36 -3
  107. package/dist/i18n/messages/ja.json +36 -3
  108. package/dist/i18n/messages/vi.json +36 -3
  109. package/dist/inertia/index.d.ts +50 -0
  110. package/dist/inertia/index.js +5 -0
  111. package/dist/props/components/app.prop.d.ts +11 -1
  112. package/dist/props/components/charts.prop.d.ts +40 -1
  113. package/dist/props/components/data-display.prop.d.ts +75 -1
  114. package/dist/props/components/data-entry.prop.d.ts +6 -0
  115. package/dist/props/components/feedback.prop.d.ts +10 -0
  116. package/dist/props/components/index.d.ts +3 -3
  117. package/dist/props/components/layout.prop.d.ts +560 -10
  118. package/dist/props/components/layout.prop.js +1 -0
  119. package/dist/props/components/navigation.prop.d.ts +24 -2
  120. package/dist/props/registry.d.ts +433 -8
  121. package/dist/props/registry.js +527 -9
  122. package/dist/props/vocabulary/data.prop.d.ts +27 -0
  123. package/dist/props/vocabulary/index.d.ts +3 -3
  124. package/dist/props/vocabulary/interaction.prop.d.ts +10 -1
  125. package/dist/props/vocabulary/layout.prop.d.ts +49 -0
  126. package/dist/styles/base.css +8 -2
  127. package/dist/styles/card-layout.css +161 -7
  128. package/dist/styles/chart-layout.css +97 -0
  129. package/dist/styles/control.css +76 -2
  130. package/dist/styles/data-display-layout.css +78 -6
  131. package/dist/styles/dialog-layout.css +105 -4
  132. package/dist/styles/fonts.css +15 -4
  133. package/dist/styles/index.css +11 -3
  134. package/dist/styles/layout.css +466 -1
  135. package/dist/styles/logo-layout.css +95 -0
  136. package/dist/styles/navigation-layout.css +152 -0
  137. package/dist/styles/shell-layout.css +790 -40
  138. package/dist/styles/table-layout.css +164 -1
  139. package/dist/theme/dxs.canonical.css +77 -0
  140. package/dist/tokens/axes.css +40 -0
  141. package/dist/tokens/base.css +6 -0
  142. package/dist/tokens/components/card.css +48 -0
  143. package/dist/tokens/components/chart.css +47 -0
  144. package/dist/tokens/components/control.css +16 -0
  145. package/dist/tokens/components/data-display.css +13 -0
  146. package/dist/tokens/components/email.css +93 -0
  147. package/dist/tokens/components/error-surface.css +36 -0
  148. package/dist/tokens/components/feedback.css +17 -0
  149. package/dist/tokens/components/legal-document.css +67 -0
  150. package/dist/tokens/components/list-row.css +43 -2
  151. package/dist/tokens/components/logo.css +74 -0
  152. package/dist/tokens/components/navigation.css +28 -0
  153. package/dist/tokens/components/sheet.css +19 -0
  154. package/dist/tokens/components/shell.css +210 -0
  155. package/dist/tokens/components/sidebar.css +33 -0
  156. package/dist/tokens/components/table.css +45 -0
  157. package/dist/tokens/foundation.css +31 -2
  158. package/dist/tokens/semantic/layout.css +69 -2
  159. package/package.json +22 -5
@@ -40,6 +40,17 @@ export type ColumnDefProp<T> = {
40
40
  * separating shadow. Pin at most one column per table.
41
41
  */
42
42
  pin?: "end";
43
+ /**
44
+ * Relative importance of the column, read ONLY by `DataTable preset="action-collection"`
45
+ * (gh#253). It is the SAME contract the `Table` primitive exposes on `TableHead`/`TableCell`:
46
+ * DataTable stamps it onto both cells of the column for you, so the preset can swap the desktop
47
+ * intrinsic widths for the token-owned priority measures (`--table-action-collection-*`) instead
48
+ * of scrolling a five-column approval queue sideways at 390px. Leave the free-text column
49
+ * unmarked — it takes the remaining space. Emitted as `data-priority` only when set, so an
50
+ * ordinary column gains no attribute. Prefer this over `width` under the preset: an explicit
51
+ * `width` utility wins the cascade and defeats the priority measure.
52
+ */
53
+ priority?: TableColumnPriorityProp;
43
54
  };
44
55
  /** Set of selected row IDs. */
45
56
  export type SelectedIdsProp = Set<string>;
@@ -64,3 +75,19 @@ export type HasActiveFiltersProp = boolean;
64
75
  * offset/background are tuned via the `--filter-bar-sticky-*` theme knobs.
65
76
  */
66
77
  export type StickyProp = boolean;
78
+ /**
79
+ * Table named collection preset. `"default"` keeps the plain table (and emits no attribute).
80
+ * `"action-collection"` is the canonical dense approval / action queue: below `collapseBelow`
81
+ * the desktop intrinsic column widths are replaced by token-owned column-PRIORITY measures under
82
+ * `table-layout: fixed` and cells wrap, so every column — including the row actions — stays inside
83
+ * the initial narrow frame. The real `<table>` semantics are never rewritten.
84
+ */
85
+ export type TablePresetProp = "default" | "action-collection";
86
+ /**
87
+ * Relative importance of a table column, used by `preset="action-collection"` to allocate the
88
+ * narrow-frame measure. `"primary"` is the row's subject, `"secondary"` its object/target,
89
+ * `"meta"` a low-priority stamp (dates, ids), `"actions"` the row-action affordance whose measure
90
+ * is reserved first so it can never be pushed outside the viewport. A column with no priority
91
+ * takes the remaining space (the free-text column).
92
+ */
93
+ export type TableColumnPriorityProp = "primary" | "secondary" | "meta" | "actions";
@@ -1,7 +1,7 @@
1
1
  /** Barrel — all vocabulary prop types. */
2
2
  export type { ClassNameProp, ChildrenProp, IdProp, OpenProp, DefaultOpenProp, OnOpenChangeProp, HandlerProp, PendingProp, RequiredProp, DisabledProp, LabelProp, HelperProp, ErrorProp, PlaceholderProp, NameProp, ValueProp, DefaultValueProp, OnValueChangeProp, OnChangeProp, OnClickProp, AsChildProp, WidthProp, } from "./shared.prop.js";
3
3
  export type { TitleProp, SubtitleProp, DescriptionProp, ExtraProp, FooterProp, ActionProp, IconProp, ConfirmLabelProp, CancelLabelProp, ActionsProp, EmptyMessageProp, } from "./content.prop.js";
4
- export type { PageDensityProp, PageContainerVariantProp, CenteredShellWidthProp, TableDensityProp, DensityProp, GapProp, } from "./layout.prop.js";
5
- export type { ButtonVariantProp, ButtonSizeProp, BadgeVariantProp, AppSettingPickerAppearanceProp, ShapeProp, TextSizeProp, TextToneProp, FontWeightProp, HeadingLevelProp, TextAlignProp, SizeProp, FormLayoutProp, BreakpointProp, ConfirmVariantProp, ToneProp, AlertVariantProp, SortDirectionProp, ColumnAlignProp, SortStateProp, RevealDelayProp, } from "./interaction.prop.js";
4
+ export type { PageDensityProp, PageContainerVariantProp, CenteredShellWidthProp, CenteredShellAlignProp, CenteredShellPresetProp, ErrorSurfaceModeProp, ErrorSurfaceStatusProp, AuthShellPresetProp, TableDensityProp, DensityProp, GapProp, } from "./layout.prop.js";
5
+ export type { ButtonVariantProp, ButtonSizeProp, BadgeVariantProp, AppSettingPickerAppearanceProp, ShapeProp, AvatarShapeProp, TextSizeProp, TextToneProp, FontWeightProp, HeadingLevelProp, TextAlignProp, SizeProp, FormLayoutProp, BreakpointProp, ConfirmVariantProp, ToneProp, AlertVariantProp, SortDirectionProp, ColumnAlignProp, SortStateProp, RevealDelayProp, } from "./interaction.prop.js";
6
6
  export type { BreadcrumbItemProp, BreadcrumbProp } from "./navigation.prop.js";
7
- export type { GetRowIdProp, OnRowClickProp, ColumnDefProp, SelectedIdsProp, OnSelectChangeProp, OnTableDensityChangeProp, OnSortChangeProp, OnSearchChangeProp, OnClearFiltersProp, HasActiveFiltersProp, StickyProp, } from "./data.prop.js";
7
+ export type { GetRowIdProp, OnRowClickProp, ColumnDefProp, TablePresetProp, TableColumnPriorityProp, SelectedIdsProp, OnSelectChangeProp, OnTableDensityChangeProp, OnSortChangeProp, OnSearchChangeProp, OnClearFiltersProp, HasActiveFiltersProp, StickyProp, } from "./data.prop.js";
@@ -7,6 +7,15 @@ export type ButtonVariantProp = "default" | "destructive" | "outline" | "dashed"
7
7
  /** Corner shape — maps to the radius tokens (default = control/component radius). Shared by
8
8
  * Button + Badge. `pill` = fully rounded (`--radius-pill`), `sharp` = square (`--radius-sharp`). */
9
9
  export type ShapeProp = "default" | "pill" | "sharp";
10
+ /**
11
+ * Avatar geometry — WHAT the mark represents, not just its corner radius, which is why it is a
12
+ * separate vocabulary from the control `ShapeProp` (`default | pill | sharp`): an entity mark is a
13
+ * ROUNDED rect, a value `ShapeProp` cannot express (its `sharp` = `--radius-sharp` = 0).
14
+ * - `circle` (default) — a person: the fully-round `--radius-pill` identity avatar.
15
+ * - `square` — an organization / service entity mark for an entity header: the compact rounded
16
+ * square on the brand surface (`--avatar-square-*` tokens).
17
+ */
18
+ export type AvatarShapeProp = "circle" | "square";
10
19
  /** Text size — steps of the golden-ratio type scale (NEVER an arbitrary px). `sm` = base. */
11
20
  export type TextSizeProp = "2xs" | "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
12
21
  /** Text colour intent — maps to semantic foreground tokens (no raw palette). */
@@ -29,7 +38,7 @@ export type BadgeVariantProp = "default" | "secondary" | "outline" | "dashed";
29
38
  * `aria-label`, focus ring, keyboard behaviour and a `--control-height` tap target (which is
30
39
  * ≥44px on coarse/touch pointers per Rule #24) — so consumers never hide internal nodes via CSS.
31
40
  */
32
- export type AppSettingPickerAppearanceProp = "labeled" | "icon";
41
+ export type AppSettingPickerAppearanceProp = "labeled" | "icon" | "inline";
33
42
  /** Button size preset. */
34
43
  export type SizeProp = "xs" | "sm" | "md" | "lg";
35
44
  /** Button size preset; icon-only sizes are a documented Button subset. */
@@ -10,7 +10,56 @@ export type DensityProp = "compact" | "default" | "comfortable";
10
10
  export type PageContainerVariantProp = "default" | "narrow" | "flush" | "ghost";
11
11
  /** CenteredShell content-column max-width tier — sm ~32rem, md ~46rem, lg ~64rem. */
12
12
  export type CenteredShellWidthProp = "sm" | "md" | "lg";
13
+ /**
14
+ * CenteredShell content-column BLOCK alignment inside the viewport-height shell.
15
+ * `"start"` (default) keeps the top-aligned flowing/scrolling page shape; `"center"` centres the
16
+ * column in the viewport for a SYSTEM-level standalone surface (a 500/503 error page, a
17
+ * maintenance notice) — the geometry stays package-owned, so no consumer `min-h-dvh` / flex CSS.
18
+ */
19
+ export type CenteredShellAlignProp = "start" | "center";
20
+ /**
21
+ * ErrorSurface shell contract — WHERE the exception surface lives, not how it looks.
22
+ *
23
+ * `"application"` (403 / 404): the failure happened INSIDE the authenticated app, so the surface is
24
+ * the page BODY of the `AppShell` the route already renders. It never reconstructs nav chrome — a
25
+ * component cannot manufacture the sidebar/topbar/user menu, which are consumer-owned; it renders
26
+ * as `AppShell`'s children (usually inside a `PageContainer`) so the shell is PRESERVED and the
27
+ * user is never stranded chrome-less.
28
+ *
29
+ * `"system"` (500 / 503): the app itself failed — session and nav data may be gone — so the surface
30
+ * owns the whole page and renders package-owned viewport-centred geometry
31
+ * (`CenteredShell align="center"`), with no consumer `min-h-dvh` / flex CSS at 1440 / 1024 / 390.
32
+ */
33
+ export type ErrorSurfaceModeProp = "application" | "system";
34
+ /**
35
+ * The HTTP status an ErrorSurface presents. Deliberately closed to the four exception pages every
36
+ * app ships (RFC 9110 §15.5.4 / §15.5.5 / §15.6.1 / §15.6.4); it drives the default icon, tone and
37
+ * the recommended `mode`. A number, never a string — it is the numeric status, and it is rendered
38
+ * with tabular figures.
39
+ */
40
+ export type ErrorSurfaceStatusProp = 403 | 404 | 500 | 503;
41
+ /**
42
+ * AuthShell named flow preset — the page MEASURE contract for a canonical hosted-identity flow
43
+ * (card max-width plus the desktop and mobile page gutters), owned by component tokens.
44
+ * `"default"` keeps the shell's own measure; `"login"` owns SCR-001's stable card anchor for
45
+ * standalone and requester flows; `"device-authorization"` is the 380px device-grant measure with
46
+ * a 5px mobile inline gutter; `"context-selection"` is the 25rem organisation/context picker
47
+ * measure that goes edge-to-edge on mobile; `"account-recovery"` is the 432px SCR-008 measure
48
+ * shared by the password-recovery and sign-in MFA challenge panels (15px mobile inline gutter).
49
+ * Orthogonal to AuthShell's `variant` (which owns control density and heading size) — combine them.
50
+ */
51
+ export type AuthShellPresetProp = "default" | "login" | "device-authorization" | "context-selection" | "account-recovery";
13
52
  /** Shared gap between layout children; components may document subsets. */
14
53
  export type GapProp = "xs" | "sm" | "md" | "lg" | "xl";
15
54
  /** DataTable row density subset. */
16
55
  export type TableDensityProp = Exclude<DensityProp, "default">;
56
+ /**
57
+ * CenteredShell named page preset — the token-owned SHELL geometry of a whole page shape.
58
+ * `"default"` keeps the shell's own box (and emits no attribute at all, so it can match no rule).
59
+ * `"public-landing"` is the PUBLIC marketing / product landing contract: one content measure
60
+ * shared by the header bar, the centred column and the footer, the section rhythm between page
61
+ * sections, the flat (elevation-free) public-surface chrome and the hero heading tier — all owned
62
+ * by `--centered-shell-landing-*` knobs so the landing COMPOSITION (hero, nav, sections, footer —
63
+ * each of which fails the Framework-Component Test) needs no page-local CSS.
64
+ */
65
+ export type CenteredShellPresetProp = "default" | "public-landing";
@@ -14,8 +14,14 @@
14
14
  *
15
15
  * The layout files declare `@layer components` and use `@apply`, so base.css
16
16
  * (which pulls in Tailwind) MUST be imported before them, in the same
17
- * stylesheet. Bundled fonts are opt-in — add `@godxjp/ui/styles/fonts` if you
18
- * want them, or skip it and supply your own via the --font-family-* tokens.
17
+ * stylesheet.
18
+ *
19
+ * Bundled fonts are opt-in — add `@godxjp/ui/styles/fonts` if you want them, or
20
+ * skip it and supply your own via the --font-family-* tokens. When you DO import
21
+ * it, it MUST come AFTER this file: base.css pulls in tokens/foundation.css,
22
+ * whose font-agnostic `:root { --font-sans-base: <system stack> }` sits at the
23
+ * same specificity as the bundle's fill, so the later import wins. Imported
24
+ * first, the bundle is dead weight (issue #210).
19
25
  *
20
26
  * Prefer the all-in-one `@godxjp/ui/styles` entry if you'd rather ship every
21
27
  * component's CSS + the bundled fonts and not think about it.
@@ -56,14 +56,19 @@
56
56
  box-shadow: 0 0 0 1px hsl(var(--primary));
57
57
  }
58
58
 
59
- /* ── Density — override the shell inset/body tokens ──────────────────── */
59
+ /* ── Density — override the shell inset/body tokens ──────────────────────
60
+ * `--card-space-shell-y: initial` re-arms the block axis to follow THIS card's inset again: the
61
+ * `density` prop is an explicit per-instance decision and must beat an ambient shell-level block
62
+ * override (e.g. AuthShell compact), exactly as it did when one inset owned both axes. */
60
63
  [data-slot="card"][data-density="tight"] {
61
64
  --card-space-inset: var(--space-3);
65
+ --card-space-shell-y: initial;
62
66
  --card-space-body-y: var(--space-3);
63
67
  }
64
68
 
65
69
  [data-slot="card"][data-density="cozy"] {
66
70
  --card-space-inset: var(--space-5);
71
+ --card-space-shell-y: initial;
67
72
  --card-space-body-y: var(--space-5);
68
73
  }
69
74
 
@@ -111,7 +116,7 @@
111
116
  }
112
117
 
113
118
  [data-slot="card-header"]:not([data-banded]) {
114
- padding-top: var(--card-space-inset);
119
+ padding-top: var(--card-space-shell-y, var(--card-space-inset));
115
120
  padding-bottom: 0;
116
121
  }
117
122
 
@@ -254,7 +259,7 @@
254
259
  /* Header-only card — terminal slot owns bottom shell */
255
260
  [data-slot="card"]:not(:has([data-slot="card-content"], [data-slot="card-footer"]))
256
261
  [data-slot="card-header"] {
257
- padding-bottom: var(--card-space-inset);
262
+ padding-bottom: var(--card-space-shell-y, var(--card-space-inset));
258
263
  }
259
264
 
260
265
  /* Cover — header below media uses section top, not shell */
@@ -271,18 +276,27 @@
271
276
  padding-top: var(--card-space-body-y);
272
277
  }
273
278
 
274
- /* ── Content ─────────────────────────────────────────────────────────── */
279
+ /* ── Content ─────────────────────────────────────────────────────────────
280
+ * The two axes are INDEPENDENT (gh#232): the inline column reads --card-space-inset, the block
281
+ * shell edge reads --card-space-shell-y (default = the same inset, so the resting render is
282
+ * unchanged). A shell that needs a shorter card without a narrower column — AuthShell compact —
283
+ * now moves the block axis alone instead of hitting a wall at a uniform inset. */
275
284
  [data-slot="card-content"] {
276
285
  padding-inline: var(--card-space-inset);
277
- padding-bottom: var(--card-space-inset);
286
+ padding-bottom: var(--card-space-shell-y, var(--card-space-inset));
278
287
  }
279
288
 
280
289
  [data-slot="card-content"]:not([data-tight]):not([data-solo]) {
281
290
  padding-top: var(--card-space-body-y);
282
291
  }
283
292
 
293
+ /* A solo body owns BOTH block edges (there is no header/footer to carry them), so this is
294
+ * `padding-block`, not just `padding-top` — it overrides the generic bottom edge above.
295
+ * The fallback chain solo-y → shell-y → inset means: a shell that tunes only the general block
296
+ * knob still reaches a solo body, an explicit --card-space-solo-y wins over it, and with neither
297
+ * set the render is the historical inset. */
284
298
  [data-slot="card-content"][data-solo] {
285
- padding-top: var(--card-space-inset);
299
+ padding-block: var(--card-space-solo-y, var(--card-space-shell-y, var(--card-space-inset)));
286
300
  }
287
301
 
288
302
  [data-slot="card-content"][data-flush] {
@@ -343,7 +357,7 @@
343
357
 
344
358
  [data-slot="card-footer"]:not([data-separated]) {
345
359
  padding-top: 0;
346
- padding-bottom: var(--card-space-inset);
360
+ padding-bottom: var(--card-space-shell-y, var(--card-space-inset));
347
361
  }
348
362
 
349
363
  [data-slot="card-footer"][data-separated] {
@@ -474,4 +488,144 @@
474
488
  font-size: var(--stat-card-hint-font-size);
475
489
  line-height: var(--line-height-normal);
476
490
  }
491
+
492
+ /* ── Service launcher — organization-scoped downstream app tile ─────── */
493
+ [data-slot="card"][data-service-launcher] {
494
+ display: flex;
495
+ flex-direction: column;
496
+ gap: var(--card-service-launcher-space-gap);
497
+ padding: var(--card-space-inset);
498
+ }
499
+
500
+ [data-slot="service-launcher-heading"],
501
+ [data-slot="service-launcher-skeleton-heading"] {
502
+ display: flex;
503
+ min-width: 0;
504
+ align-items: center;
505
+ gap: var(--card-service-launcher-space-gap);
506
+ }
507
+
508
+ [data-slot="service-launcher-icon"] {
509
+ display: inline-flex;
510
+ width: var(--card-service-launcher-icon-size);
511
+ height: var(--card-service-launcher-icon-size);
512
+ flex: none;
513
+ align-items: center;
514
+ justify-content: center;
515
+ border-radius: var(--card-service-launcher-icon-radius);
516
+ background: hsl(var(--card-service-launcher-icon-background, var(--accent)));
517
+ color: hsl(var(--card-service-launcher-icon-foreground, var(--primary)));
518
+ }
519
+
520
+ /* Unavailable tile (the consumer supplied a `disabledReason`): the medallion drops its brand
521
+ * wash so an un-launchable service never reads as live. Role-mirror knobs default at the CALL
522
+ * SITE, so a scoped [data-tenant]/.dark override of --muted still reaches it (docs/TOKENS.md). */
523
+ [data-slot="card"][data-service-launcher][data-unavailable] [data-slot="service-launcher-icon"] {
524
+ background: hsl(var(--card-service-launcher-unavailable-icon-background, var(--muted)));
525
+ color: hsl(var(--card-service-launcher-unavailable-icon-foreground, var(--muted-foreground)));
526
+ }
527
+
528
+ [data-slot="service-launcher-icon"] > svg {
529
+ width: var(--card-service-launcher-icon-glyph-size);
530
+ height: var(--card-service-launcher-icon-glyph-size);
531
+ }
532
+
533
+ [data-slot="service-launcher-title"] {
534
+ min-width: 0;
535
+ flex: 1;
536
+ overflow-wrap: anywhere;
537
+ font-size: var(--card-title-font-size);
538
+ font-weight: var(--card-title-font-weight);
539
+ line-height: var(--card-title-line-height);
540
+ }
541
+
542
+ [data-slot="service-launcher-status"] {
543
+ flex: none;
544
+ }
545
+
546
+ [data-slot="service-launcher-description"] {
547
+ flex: 1;
548
+ overflow-wrap: anywhere;
549
+ color: hsl(var(--muted-foreground));
550
+ font-size: var(--card-service-launcher-description-font-size);
551
+ line-height: var(--card-service-launcher-description-line-height);
552
+ }
553
+
554
+ /* Mono metadata ONLY (hostname · plan) — machine identifiers, never prose. */
555
+ [data-slot="service-launcher-metadata"] {
556
+ overflow-wrap: anywhere;
557
+ color: hsl(var(--muted-foreground));
558
+ font-family: var(--font-mono);
559
+ font-size: var(--card-service-launcher-metadata-font-size);
560
+ line-height: var(--card-service-launcher-metadata-line-height);
561
+ }
562
+
563
+ /* The disabled/unavailable reason is a localized SENTENCE — it takes the description's prose
564
+ * rhythm, not the mono metadata treatment (a JA/VI sentence in monospace is unreadable). */
565
+ [data-slot="service-launcher-disabled-reason"] {
566
+ overflow-wrap: anywhere;
567
+ color: hsl(var(--muted-foreground));
568
+ font-size: var(--card-service-launcher-description-font-size);
569
+ line-height: var(--card-service-launcher-description-line-height);
570
+ }
571
+
572
+ [data-slot="service-launcher-action"] > [data-slot="button"] {
573
+ width: 100%;
574
+ }
575
+
576
+ [data-slot="card"][data-service-catalog-cta] {
577
+ min-height: var(--card-service-launcher-cta-min-height);
578
+ display: flex;
579
+ flex-direction: column;
580
+ align-items: center;
581
+ justify-content: center;
582
+ gap: var(--card-service-launcher-space-gap);
583
+ border-style: dashed;
584
+ padding: var(--card-space-inset);
585
+ text-align: center;
586
+ }
587
+
588
+ [data-slot="service-catalog-icon"] {
589
+ width: var(--card-service-launcher-icon-glyph-size);
590
+ height: var(--card-service-launcher-icon-glyph-size);
591
+ color: hsl(var(--muted-foreground));
592
+ }
593
+
594
+ [data-slot="service-catalog-title"] {
595
+ color: hsl(var(--muted-foreground));
596
+ font-size: var(--card-service-launcher-description-font-size);
597
+ line-height: var(--card-service-launcher-description-line-height);
598
+ }
599
+
600
+ [data-slot^="service-launcher-skeleton-"] {
601
+ display: block;
602
+ }
603
+
604
+ [data-slot="service-launcher-skeleton-icon"] {
605
+ width: var(--card-service-launcher-icon-size);
606
+ height: var(--card-service-launcher-icon-size);
607
+ flex: none;
608
+ }
609
+
610
+ [data-slot="service-launcher-skeleton-title"] {
611
+ width: var(--card-service-launcher-skeleton-title-width);
612
+ height: var(--font-size-base);
613
+ }
614
+
615
+ [data-slot="service-launcher-skeleton-status"] {
616
+ width: var(--card-service-launcher-skeleton-status-width);
617
+ height: var(--font-size-xs);
618
+ margin-inline-start: auto;
619
+ }
620
+
621
+ [data-slot="service-launcher-skeleton-description"],
622
+ [data-slot="service-launcher-skeleton-metadata"],
623
+ [data-slot="service-launcher-skeleton-action"] {
624
+ width: 100%;
625
+ height: var(--font-size-xs);
626
+ }
627
+
628
+ [data-slot="service-launcher-skeleton-action"] {
629
+ height: var(--control-height);
630
+ }
477
631
  }
@@ -37,3 +37,100 @@
37
37
  color: hsl(var(--popover-foreground));
38
38
  font-size: var(--font-size-xs);
39
39
  }
40
+
41
+ /* Content that sits BELOW the plot (the activity footer slot) — outside the
42
+ * `role="img"` canvas so links/buttons in it stay reachable and announced. */
43
+ .ui-chart-footer {
44
+ margin-block-start: var(--chart-trend-footer-gap);
45
+ font-size: var(--font-size-xs);
46
+ color: hsl(var(--muted-foreground));
47
+ }
48
+
49
+ /* ─────────────────────────────────────────────────────────────────────────────
50
+ * CompactBarTrend — a dependency-free (no recharts) vertical bar trend.
51
+ * ALL geometry resolves from `--chart-trend-*` tokens; the only inline value is
52
+ * the DATUM itself (`--chart-trend-bar-value`, a 0..1 ratio), so a consumer never
53
+ * writes a height calculation or a colour of its own.
54
+ * ─────────────────────────────────────────────────────────────────────────── */
55
+
56
+ /* `size` → the active plot-height tier. Set on the <figure> as `data-size`. */
57
+ .ui-chart-trend[data-size="xs"] {
58
+ --chart-trend-plot-height: var(--chart-trend-plot-height-xs);
59
+ }
60
+
61
+ .ui-chart-trend[data-size="sm"] {
62
+ --chart-trend-plot-height: var(--chart-trend-plot-height-sm);
63
+ }
64
+
65
+ .ui-chart-trend[data-size="md"] {
66
+ --chart-trend-plot-height: var(--chart-trend-plot-height-md);
67
+ }
68
+
69
+ .ui-chart-trend[data-size="lg"] {
70
+ --chart-trend-plot-height: var(--chart-trend-plot-height-lg);
71
+ }
72
+
73
+ .ui-chart-trend-body {
74
+ display: flex;
75
+ flex-direction: column;
76
+ gap: var(--chart-trend-tick-gap);
77
+ inline-size: 100%;
78
+ min-inline-size: 0;
79
+ }
80
+
81
+ .ui-chart-trend-plot {
82
+ display: flex;
83
+ align-items: flex-end;
84
+ gap: var(--chart-trend-bar-gap);
85
+ block-size: var(--chart-trend-plot-height);
86
+ inline-size: 100%;
87
+ min-inline-size: 0;
88
+ /* Quiet by default (rule #44) — a service opts into a baseline rule with
89
+ * `--chart-trend-baseline-border: 1px solid hsl(var(--border));`. */
90
+ border-block-end: var(--chart-trend-baseline-border, none);
91
+ }
92
+
93
+ .ui-chart-trend-column {
94
+ display: flex;
95
+ flex: 1 1 0;
96
+ align-items: flex-end;
97
+ justify-content: center;
98
+ min-inline-size: 0;
99
+ block-size: 100%;
100
+ }
101
+
102
+ .ui-chart-trend-bar {
103
+ inline-size: 100%;
104
+ max-inline-size: var(--chart-trend-bar-max-width);
105
+ /* Data-driven: the 0..1 ratio arrives as an inline custom property. */
106
+ block-size: max(var(--chart-trend-bar-min-height), calc(var(--chart-trend-bar-value, 0) * 100%));
107
+ border-start-start-radius: var(--chart-trend-bar-radius);
108
+ border-start-end-radius: var(--chart-trend-bar-radius);
109
+ background: var(
110
+ --chart-trend-bar-background,
111
+ hsl(var(--muted-foreground) / var(--chart-trend-bar-background-alpha))
112
+ );
113
+ }
114
+
115
+ .ui-chart-trend-bar[data-emphasized="true"] {
116
+ background: var(--chart-trend-bar-emphasis-background, hsl(var(--primary)));
117
+ }
118
+
119
+ .ui-chart-trend-ticks {
120
+ display: flex;
121
+ gap: var(--chart-trend-bar-gap);
122
+ inline-size: 100%;
123
+ min-inline-size: 0;
124
+ }
125
+
126
+ .ui-chart-trend-tick {
127
+ flex: 1 1 0;
128
+ min-inline-size: 0;
129
+ overflow: hidden;
130
+ font-size: var(--chart-trend-tick-font-size);
131
+ line-height: var(--line-height-tight);
132
+ color: hsl(var(--muted-foreground));
133
+ text-align: center;
134
+ text-overflow: ellipsis;
135
+ white-space: nowrap;
136
+ }
@@ -585,6 +585,52 @@
585
585
  opacity: 0.5;
586
586
  }
587
587
 
588
+ [data-slot="dialog-content"].ui-command-palette {
589
+ top: var(--command-palette-inset-top);
590
+ width: min(
591
+ var(--command-palette-width),
592
+ calc(100vw - (2 * var(--command-palette-viewport-inset)))
593
+ );
594
+ max-width: none;
595
+ transform: translateX(-50%);
596
+ gap: 0;
597
+ padding: 0;
598
+ background: hsl(var(--popover));
599
+ }
600
+
601
+ .ui-command-palette-trigger {
602
+ min-width: 0;
603
+ }
604
+
605
+ .ui-command-palette-label {
606
+ min-width: 0;
607
+ }
608
+
609
+ .ui-command-palette-meta {
610
+ margin-inline-start: auto;
611
+ color: hsl(var(--muted-foreground));
612
+ font-size: var(--font-size-xs);
613
+ }
614
+
615
+ .ui-command-palette-state {
616
+ padding-block: var(--space-6);
617
+ padding-inline: var(--space-4);
618
+ text-align: center;
619
+ color: hsl(var(--muted-foreground));
620
+ font-size: var(--font-size-base);
621
+ }
622
+
623
+ .ui-command-palette-hints {
624
+ display: flex;
625
+ flex-wrap: wrap;
626
+ gap: var(--command-palette-hint-gap);
627
+ border-top: 1px solid hsl(var(--border));
628
+ padding: var(--command-palette-hint-padding-y) var(--command-palette-hint-padding-x);
629
+ color: hsl(var(--muted-foreground));
630
+ font-family: var(--font-mono);
631
+ font-size: var(--font-size-xs);
632
+ }
633
+
588
634
  .ui-search-input {
589
635
  position: relative;
590
636
  }
@@ -680,8 +726,8 @@
680
726
  .ui-otp-slot {
681
727
  position: relative;
682
728
  display: flex;
683
- width: var(--control-height);
684
- height: var(--control-height);
729
+ width: var(--otp-slot-size, var(--control-height));
730
+ height: var(--otp-slot-size, var(--control-height));
685
731
  align-items: center;
686
732
  justify-content: center;
687
733
  font-size: var(--font-size-base);
@@ -699,6 +745,34 @@
699
745
  border-start-end-radius: var(--radius-md);
700
746
  border-end-end-radius: var(--radius-md);
701
747
  }
748
+ .ui-otp-group[data-appearance="grouped"] {
749
+ overflow: hidden;
750
+ border: 1px solid hsl(var(--border));
751
+ border-radius: var(--radius-md);
752
+ background: hsl(var(--background));
753
+ }
754
+ .ui-otp-group[data-appearance="grouped"] .ui-otp-slot {
755
+ border: 0;
756
+ border-radius: 0;
757
+ background: transparent;
758
+ }
759
+ .ui-otp-group[data-appearance="grouped"] .ui-otp-slot[data-active="true"] {
760
+ box-shadow: none;
761
+ }
762
+ .ui-otp-group[data-appearance="grouped"]:has(.ui-otp-slot[data-active="true"]) {
763
+ border-color: hsl(var(--focus-ring-color, var(--ring)));
764
+ box-shadow: 0 0 0 var(--focus-ring-width) hsl(var(--focus-ring-color, var(--ring)));
765
+ }
766
+ .ui-otp-container:has(.ui-otp-input[aria-invalid="true"]) .ui-otp-group[data-appearance="grouped"] {
767
+ border-color: hsl(var(--destructive));
768
+ }
769
+ .ui-otp-container:has(.ui-otp-input[aria-invalid="true"])
770
+ .ui-otp-group[data-appearance="grouped"]:has(.ui-otp-slot[data-active="true"]) {
771
+ box-shadow: 0 0 0 var(--focus-ring-width) hsl(var(--destructive));
772
+ }
773
+ .ui-otp-container:has(.ui-otp-input:disabled) .ui-otp-group[data-appearance="grouped"] {
774
+ background: hsl(var(--muted));
775
+ }
702
776
  .ui-otp-slot[data-active="true"] {
703
777
  z-index: 1;
704
778
  box-shadow: 0 0 0 var(--focus-ring-width) hsl(var(--focus-ring-color, var(--ring)));