@godxjp/ui 31.28.4 → 31.29.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 (61) hide show
  1. package/agent/START-HERE.md +5 -5
  2. package/agent/components/AppLauncher.json +5 -0
  3. package/agent/components/Avatar.json +1 -0
  4. package/agent/components/Dialog.json +3 -1
  5. package/agent/components/ResponsiveGrid.json +7 -0
  6. package/agent/components/Sidebar.json +5 -0
  7. package/agent/components/SortableList.json +78 -0
  8. package/agent/components/TopbarItem.json +1 -1
  9. package/agent/components-index.json +5 -0
  10. package/agent/components.json +100 -2
  11. package/agent/index.json +6 -6
  12. package/agent/llms.txt +6 -6
  13. package/agent/tokens.json +74 -2
  14. package/dist/components/data-entry/index.d.ts +2 -0
  15. package/dist/components/data-entry/index.js +2 -0
  16. package/dist/components/data-entry/sortable-list.d.ts +10 -0
  17. package/dist/components/data-entry/sortable-list.js +237 -0
  18. package/dist/components/feedback/dialog.d.ts +8 -2
  19. package/dist/components/feedback/dialog.js +8 -1
  20. package/dist/components/feedback/sheet.js +1 -1
  21. package/dist/components/layout/app-launcher.d.ts +1 -1
  22. package/dist/components/layout/app-launcher.js +18 -14
  23. package/dist/components/layout/responsive-grid.d.ts +17 -2
  24. package/dist/components/layout/responsive-grid.js +17 -4
  25. package/dist/components/layout/sidebar.d.ts +1 -1
  26. package/dist/components/layout/sidebar.js +3 -0
  27. package/dist/components/layout/topbar-item.js +8 -2
  28. package/dist/components/ui/avatar.d.ts +3 -1
  29. package/dist/components/ui/avatar.js +2 -1
  30. package/dist/contracts/measurement.json +1 -1
  31. package/dist/i18n/messages/en.json +8 -0
  32. package/dist/i18n/messages/ja.json +8 -0
  33. package/dist/i18n/messages/vi.json +8 -0
  34. package/dist/props/components/data-display.prop.d.ts +9 -0
  35. package/dist/props/components/data-entry.prop.d.ts +34 -0
  36. package/dist/props/components/feedback.prop.d.ts +6 -1
  37. package/dist/props/components/layout.prop.d.ts +22 -0
  38. package/dist/props/registry.d.ts +33 -2
  39. package/dist/props/registry.js +44 -1
  40. package/dist/styles/control.css +50 -0
  41. package/dist/styles/data-display-layout.css +4 -0
  42. package/dist/styles/dialog-layout.css +14 -1
  43. package/dist/styles/layers.json +6 -1
  44. package/dist/styles/layout.css +29 -7
  45. package/dist/styles/shell-layout.css +26 -0
  46. package/dist/tokens/components/control.css +5 -0
  47. package/dist/tokens/components/feedback.css +6 -0
  48. package/dist/tokens/components/shell.css +6 -0
  49. package/dist/tokens/semantic/layout.css +1 -4
  50. package/docs/FRAME-COVERAGE-REPORT.md +3 -2
  51. package/docs/data-display/avatar.tsx +32 -0
  52. package/docs/data-entry/sortable-list.tsx +76 -0
  53. package/docs/feedback/dialog.tsx +37 -1
  54. package/docs/i18n/messages/en.json +51 -0
  55. package/docs/i18n/messages/ja.json +51 -0
  56. package/docs/i18n/messages/vi.json +51 -0
  57. package/docs/layout/app-launcher.tsx +17 -1
  58. package/docs/layout/responsive-grid.tsx +23 -0
  59. package/docs/layout/sidebar.tsx +24 -0
  60. package/docs/layout/topbar-item.tsx +11 -0
  61. package/package.json +2 -2
@@ -1,7 +1,7 @@
1
1
  /** Feedback component prop types — @see docs/COMPONENTS.md#feedback */
2
2
  import type * as React from "react";
3
3
  import type { QueryErrorCategory } from "../../lib/query-error.js";
4
- import type { AlertVariantProp, AvatarShapeProp, CalloutKindProp, CancelLabelProp, ChildrenProp, ClassNameProp, ConfirmLabelProp, ConfirmVariantProp, DefaultOpenProp, DescriptionProp, HandlerProp, IconProp, OpenProp, OnOpenChangeProp, PendingProp, ShapeProp, SizeProp, ToneProp, TitleProp } from "../vocabulary/index.js";
4
+ import type { AlertVariantProp, AvatarShapeProp, CalloutKindProp, CancelLabelProp, ChildrenProp, ClassNameProp, ConfirmLabelProp, ConfirmVariantProp, DefaultOpenProp, DescriptionProp, HandlerProp, IconProp, OpenProp, OnOpenChangeProp, PendingProp, ShapeProp, SizeProp, ToneProp, TitleProp, WidthProp } from "../vocabulary/index.js";
5
5
  /**
6
6
  * @see Dialog — the compound modal ROOT.
7
7
  *
@@ -38,6 +38,11 @@ export type DialogContentProp = {
38
38
  showClose?: boolean;
39
39
  /** Semantic class for the scrim the surface owns (RAC nests overlay → modal → dialog). */
40
40
  overlayClassName?: ClassNameProp;
41
+ /**
42
+ * Panel width (antd Modal `width`; same vocabulary as SheetContent `width`): number = px, string
43
+ * = any CSS length. Capped at the viewport minus `--dialog-viewport-inset`. Default 32rem.
44
+ */
45
+ width?: WidthProp;
41
46
  className?: ClassNameProp;
42
47
  children?: ChildrenProp;
43
48
  };
@@ -1430,6 +1430,16 @@ export type AppLauncherProp = {
1430
1430
  */
1431
1431
  side?: "top" | "right" | "bottom" | "left";
1432
1432
  align?: "start" | "center" | "end";
1433
+ /**
1434
+ * Secondary links UNDER the grid — "Request access", "All apps", "Add apps": the row Google
1435
+ * Workspace, Okta and Microsoft My Apps put below their launcher grid (gh#1172). Pass the links
1436
+ * themselves (`<a>` or a router link); the launcher draws the row (a hairline above it, the
1437
+ * small type step, `--app-launcher-footer-*`) outside the scrolling grid, so it stays in view and
1438
+ * comes after the tiles in focus order. Rendered in every state — loading, error and empty
1439
+ * included — because "request access" is most needed when there is nothing to open. Not a tile:
1440
+ * a link dressed as an app reads as an app.
1441
+ */
1442
+ footer?: ReactNode;
1433
1443
  open?: boolean;
1434
1444
  onOpenChange?: (open: boolean) => void;
1435
1445
  className?: ClassNameProp;
@@ -1463,6 +1473,14 @@ export type SidebarProp = {
1463
1473
  * not belong here — see AppShell's `navRail` for where it goes and why.
1464
1474
  */
1465
1475
  brand?: ReactNode | ((collapsed: boolean) => ReactNode);
1476
+ /**
1477
+ * The rail's ONE primary action ("New issue", "+ Create", "New page"), rendered between `brand`
1478
+ * and the scrolling nav and never scrolling with it. Put exactly one primary control here: a
1479
+ * `Button` (variant primary) full width when expanded, icon-only with an `aria-label` + tooltip
1480
+ * when collapsed. It takes a function for the same reason `brand` and `footer` do: it receives the
1481
+ * EFFECTIVE collapsed value, so the mobile drawer (which never collapses) gets the full button.
1482
+ */
1483
+ action?: ReactNode | ((collapsed: boolean) => ReactNode);
1466
1484
  collapsed?: boolean;
1467
1485
  children?: ChildrenProp;
1468
1486
  /**
@@ -1620,6 +1638,10 @@ export type TopbarItemProp = Omit<React.ButtonHTMLAttributes<HTMLButtonElement>,
1620
1638
  *
1621
1639
  * The count is not an accessible name: give the cell an `aria-label` that says what the number
1622
1640
  * means (`aria-label={t("topbar.notifications.unread", { count })}`).
1641
+ *
1642
+ * Drawn under `asChild` too (gh#1171): the pill goes inside the child element, after its own
1643
+ * children, so a bell that is a link (`<TopbarItem asChild badge={3}><a …/></TopbarItem>`) keeps
1644
+ * its count.
1623
1645
  */
1624
1646
  badge?: ReactNode;
1625
1647
  /**
@@ -2017,6 +2017,11 @@ export declare const COMPONENT_PROP_REGISTRY: {
2017
2017
  readonly file: "components/data-display.prop.ts";
2018
2018
  readonly vocabulary: readonly [];
2019
2019
  };
2020
+ readonly AvatarImageFitProp: {
2021
+ readonly group: "data-display";
2022
+ readonly file: "components/data-display.prop.ts";
2023
+ readonly vocabulary: readonly [];
2024
+ };
2020
2025
  readonly BadgeProp: {
2021
2026
  readonly group: "data-display";
2022
2027
  readonly file: "components/data-display.prop.ts";
@@ -2428,7 +2433,7 @@ export declare const COMPONENT_PROP_REGISTRY: {
2428
2433
  readonly DialogContentProp: {
2429
2434
  readonly group: "feedback";
2430
2435
  readonly file: "components/feedback.prop.ts";
2431
- readonly vocabulary: readonly ["ConfirmVariantProp", "ClassNameProp", "ChildrenProp", {
2436
+ readonly vocabulary: readonly ["ConfirmVariantProp", "ClassNameProp", "ChildrenProp", "WidthProp", {
2432
2437
  readonly field: "showCloseButton";
2433
2438
  readonly local: true;
2434
2439
  readonly reason: 'Corner ✕ opt-out; defaults to false under variant="destructive".';
@@ -3312,13 +3317,39 @@ export declare const COMPONENT_PROP_REGISTRY: {
3312
3317
  readonly reason: "antd `onDrop` info: true when it lands as a sibling, false inside.";
3313
3318
  }];
3314
3319
  };
3320
+ readonly SortableListItemProp: {
3321
+ readonly group: "data-entry";
3322
+ readonly file: "components/data-entry.prop.ts";
3323
+ readonly vocabulary: readonly ["ValueProp", "LabelProp", "DisabledProp"];
3324
+ };
3325
+ readonly SortableListProp: {
3326
+ readonly group: "data-entry";
3327
+ readonly file: "components/data-entry.prop.ts";
3328
+ readonly vocabulary: readonly ["ValueProp", "DefaultValueProp", "OnValueChangeProp", "DisabledProp", "IdProp", "ClassNameProp", {
3329
+ readonly field: "items";
3330
+ readonly local: true;
3331
+ readonly reason: "The rows to order — key + spoken label + per-item disabled; not select options (nothing is chosen, the order is the value).";
3332
+ }, {
3333
+ readonly field: "renderItem";
3334
+ readonly local: true;
3335
+ readonly reason: "Draws an item's content beside the grip, with its index and whether it is held.";
3336
+ }, {
3337
+ readonly field: "layout";
3338
+ readonly local: true;
3339
+ readonly reason: "list (stacked rows) vs grid (auto-filled tiles) — the same keyboard order either way; not OrientationProp, a grid is neither axis.";
3340
+ }, {
3341
+ readonly field: "aria-label";
3342
+ readonly local: true;
3343
+ readonly reason: "Accessible name of the list.";
3344
+ }];
3345
+ };
3315
3346
  readonly EmojiPickerProp: {
3316
3347
  readonly group: "data-entry";
3317
3348
  readonly file: "components/data-entry.prop.ts";
3318
3349
  readonly vocabulary: readonly ["ValueProp", "DefaultValueProp", "OnValueChangeProp", "OpenProp", "DefaultOpenProp", "OnOpenChangeProp", "SizeProp", "DisabledProp", "IdProp", "ClassNameProp", {
3319
3350
  readonly field: "removable";
3320
3351
  readonly local: true;
3321
- readonly reason: "Whether \"remove icon\" is offered while a value is set — a picker for an OPTIONAL icon (gh#1164), not AllowClearProp's inline ✕ on a field.";
3352
+ readonly reason: 'Whether "remove icon" is offered while a value is set — a picker for an OPTIONAL icon (gh#1164), not AllowClearProp\'s inline ✕ on a field.';
3322
3353
  }, {
3323
3354
  readonly field: "recentsKey";
3324
3355
  readonly local: true;
@@ -2417,6 +2417,11 @@ const COMPONENT_PROP_REGISTRY = {
2417
2417
  file: "components/data-display.prop.ts",
2418
2418
  vocabulary: []
2419
2419
  },
2420
+ AvatarImageFitProp: {
2421
+ group: "data-display",
2422
+ file: "components/data-display.prop.ts",
2423
+ vocabulary: []
2424
+ },
2420
2425
  BadgeProp: {
2421
2426
  group: "data-display",
2422
2427
  file: "components/data-display.prop.ts",
@@ -2850,6 +2855,7 @@ const COMPONENT_PROP_REGISTRY = {
2850
2855
  "ConfirmVariantProp",
2851
2856
  "ClassNameProp",
2852
2857
  "ChildrenProp",
2858
+ "WidthProp",
2853
2859
  {
2854
2860
  field: "showCloseButton",
2855
2861
  local: true,
@@ -3917,6 +3923,39 @@ const COMPONENT_PROP_REGISTRY = {
3917
3923
  }
3918
3924
  ]
3919
3925
  },
3926
+ SortableListItemProp: {
3927
+ group: "data-entry",
3928
+ file: "components/data-entry.prop.ts",
3929
+ vocabulary: ["ValueProp", "LabelProp", "DisabledProp"]
3930
+ },
3931
+ SortableListProp: {
3932
+ group: "data-entry",
3933
+ file: "components/data-entry.prop.ts",
3934
+ vocabulary: [
3935
+ "ValueProp",
3936
+ "DefaultValueProp",
3937
+ "OnValueChangeProp",
3938
+ "DisabledProp",
3939
+ "IdProp",
3940
+ "ClassNameProp",
3941
+ {
3942
+ field: "items",
3943
+ local: true,
3944
+ reason: "The rows to order \u2014 key + spoken label + per-item disabled; not select options (nothing is chosen, the order is the value)."
3945
+ },
3946
+ {
3947
+ field: "renderItem",
3948
+ local: true,
3949
+ reason: "Draws an item's content beside the grip, with its index and whether it is held."
3950
+ },
3951
+ {
3952
+ field: "layout",
3953
+ local: true,
3954
+ reason: "list (stacked rows) vs grid (auto-filled tiles) \u2014 the same keyboard order either way; not OrientationProp, a grid is neither axis."
3955
+ },
3956
+ { field: "aria-label", local: true, reason: "Accessible name of the list." }
3957
+ ]
3958
+ },
3920
3959
  EmojiPickerProp: {
3921
3960
  group: "data-entry",
3922
3961
  file: "components/data-entry.prop.ts",
@@ -3946,7 +3985,11 @@ const COMPONENT_PROP_REGISTRY = {
3946
3985
  local: true,
3947
3986
  reason: "Replaces the default trigger button (a page icon drawn large, a reaction chip) \u2014 a ReactNode slot, not a TriggerProp mode."
3948
3987
  },
3949
- { field: "aria-label", local: true, reason: "Accessible name of the trigger and the popover." }
3988
+ {
3989
+ field: "aria-label",
3990
+ local: true,
3991
+ reason: "Accessible name of the trigger and the popover."
3992
+ }
3950
3993
  ]
3951
3994
  },
3952
3995
  TreeProp: {
@@ -1026,6 +1026,56 @@
1026
1026
  background-color: hsl(var(--accent));
1027
1027
  }
1028
1028
 
1029
+ .ui-sortable-list-items {
1030
+ display: flex;
1031
+ flex-direction: column;
1032
+ gap: var(--sortable-list-gap);
1033
+ margin: 0;
1034
+ padding: 0;
1035
+ list-style: none;
1036
+ }
1037
+
1038
+ .ui-sortable-list[data-layout="grid"] .ui-sortable-list-items {
1039
+ display: grid;
1040
+ grid-template-columns: repeat(
1041
+ auto-fill,
1042
+ minmax(min(100%, var(--sortable-list-grid-item-min-inline-size)), 1fr)
1043
+ );
1044
+ }
1045
+
1046
+ .ui-sortable-list-item {
1047
+ display: flex;
1048
+ min-inline-size: 0;
1049
+ align-items: center;
1050
+ gap: var(--sortable-list-gap);
1051
+ border: var(--stroke-hairline) solid hsl(var(--border));
1052
+ border-radius: var(--sortable-list-item-radius);
1053
+ background: hsl(var(--card));
1054
+ padding: var(--sortable-list-item-padding);
1055
+ }
1056
+
1057
+ .ui-sortable-list-item[data-dragging] {
1058
+ position: relative;
1059
+ z-index: 1;
1060
+ border-color: hsl(var(--ring));
1061
+ box-shadow: var(--shadow-md);
1062
+ }
1063
+
1064
+ .ui-sortable-list-content {
1065
+ min-inline-size: 0;
1066
+ flex: 1 1 0%;
1067
+ }
1068
+
1069
+ .ui-sortable-list-handle {
1070
+ flex-shrink: 0;
1071
+ cursor: grab;
1072
+ touch-action: none;
1073
+ }
1074
+
1075
+ .ui-sortable-list-item[data-dragging] .ui-sortable-list-handle {
1076
+ cursor: grabbing;
1077
+ }
1078
+
1029
1079
  .ui-emoji-picker-grid {
1030
1080
  display: flex;
1031
1081
  flex-direction: column;
@@ -72,6 +72,10 @@
72
72
  object-fit: cover;
73
73
  }
74
74
 
75
+ .ui-avatar-image[data-fit="contain"] {
76
+ object-fit: contain;
77
+ }
78
+
75
79
  .ui-avatar-fallback {
76
80
  display: flex;
77
81
  width: 100%;
@@ -47,7 +47,12 @@
47
47
  top: 50%;
48
48
  z-index: var(--overlay-z-index);
49
49
  display: grid;
50
- width: min(var(--dialog-width-default), calc(100% - var(--dialog-viewport-inset)));
50
+ width: min(
51
+ var(--dialog-width, var(--dialog-width-default)),
52
+ calc(100% - var(--dialog-viewport-inset))
53
+ );
54
+
55
+ --tw-animation-duration: var(--dialog-motion-duration, var(--duration-fast));
51
56
  max-width: none;
52
57
  transform: translate(-50%, -50%);
53
58
  gap: var(--dialog-space-gap);
@@ -424,6 +429,14 @@
424
429
  inset: 0;
425
430
  }
426
431
 
432
+ .ui-sheet-panel[data-state="open"] {
433
+ --tw-animation-duration: var(--sheet-enter-duration, var(--duration-fast));
434
+ }
435
+
436
+ .ui-sheet-panel[data-state="closed"] {
437
+ --tw-animation-duration: var(--sheet-exit-duration, var(--duration-fast));
438
+ }
439
+
427
440
  .ui-sheet-panel {
428
441
 
429
442
  background-color: var(--sheet-surface-background, hsl(var(--background)));
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "31.28.4",
2
+ "version": "31.29.0",
3
3
  "generatedBy": "scripts/gen-style-layers.mjs — do not edit by hand; run `pnpm gen:style-layers`",
4
4
  "base": "base.css",
5
5
  "fonts": "fonts.css",
@@ -1035,6 +1035,11 @@
1035
1035
  "control.css"
1036
1036
  ]
1037
1037
  },
1038
+ "SortableList": {
1039
+ "layers": [
1040
+ "control.css"
1041
+ ]
1042
+ },
1038
1043
  "SpaceCompact": {
1039
1044
  "layers": [
1040
1045
  "layout.css"
@@ -138,7 +138,10 @@
138
138
  min-inline-size: 0;
139
139
  }
140
140
 
141
- .ui-space-compact[data-orientation="horizontal"][data-full-width="true"] > [data-slot="space-compact-item"]:not(:has(.ui-control, .ui-control-multiline, .ui-tag-input)) {
141
+ .ui-space-compact[data-orientation="horizontal"][data-full-width="true"]
142
+ > [data-slot="space-compact-item"]:not(
143
+ :has(.ui-control, .ui-control-multiline, .ui-tag-input)
144
+ ) {
142
145
  flex: none;
143
146
  }
144
147
 
@@ -146,16 +149,21 @@
146
149
  flex-direction: column;
147
150
  }
148
151
 
149
- .ui-space-compact[data-orientation="vertical"][data-full-width="true"] > [data-slot="space-compact-item"] {
152
+ .ui-space-compact[data-orientation="vertical"][data-full-width="true"]
153
+ > [data-slot="space-compact-item"] {
150
154
  min-inline-size: auto;
151
155
  min-block-size: 0;
152
156
  }
153
157
 
154
- .ui-space-compact[data-orientation="horizontal"] > [data-slot="space-compact-item"] + [data-slot="space-compact-item"] {
158
+ .ui-space-compact[data-orientation="horizontal"]
159
+ > [data-slot="space-compact-item"]
160
+ + [data-slot="space-compact-item"] {
155
161
  margin-inline-start: calc(var(--control-border-width, var(--stroke-hairline)) * -1);
156
162
  }
157
163
 
158
- .ui-space-compact[data-orientation="vertical"] > [data-slot="space-compact-item"] + [data-slot="space-compact-item"] {
164
+ .ui-space-compact[data-orientation="vertical"]
165
+ > [data-slot="space-compact-item"]
166
+ + [data-slot="space-compact-item"] {
159
167
  margin-block-start: calc(var(--control-border-width, var(--stroke-hairline)) * -1);
160
168
  }
161
169
 
@@ -175,12 +183,14 @@
175
183
  z-index: 1;
176
184
  }
177
185
 
178
- .ui-space-compact[data-orientation="horizontal"] > [data-slot="space-compact-item"]:not(:first-child) {
186
+ .ui-space-compact[data-orientation="horizontal"]
187
+ > [data-slot="space-compact-item"]:not(:first-child) {
179
188
  --input-radius-start: 0;
180
189
  --control-trigger-radius-start: 0;
181
190
  }
182
191
 
183
- .ui-space-compact[data-orientation="horizontal"] > [data-slot="space-compact-item"]:not(:last-child) {
192
+ .ui-space-compact[data-orientation="horizontal"]
193
+ > [data-slot="space-compact-item"]:not(:last-child) {
184
194
  --input-radius-end: 0;
185
195
  --control-trigger-radius-end: 0;
186
196
  }
@@ -410,6 +420,10 @@
410
420
  align-items: start;
411
421
  }
412
422
 
423
+ .ui-responsive-grid[data-dense] {
424
+ grid-auto-flow: row dense;
425
+ }
426
+
413
427
  .ui-responsive-grid[data-align="start"] {
414
428
  align-items: start;
415
429
  }
@@ -926,7 +940,10 @@
926
940
 
927
941
  .ui-page-container[data-header-scale="display"] .ui-page-title,
928
942
  .ui-page-container[data-header-scale="display"] .ui-page-header .ui-page-title {
929
- font-size: var(--page-title-font-size-display);
943
+ font-size: var(
944
+ --page-title-font-size-display,
945
+ var(--font-size-4xl, calc(var(--font-size-display) / var(--font-size-display-ratio)))
946
+ );
930
947
  font-weight: var(--font-weight-semibold);
931
948
  line-height: var(--page-title-line-height-display);
932
949
  text-wrap: balance;
@@ -1966,22 +1983,27 @@
1966
1983
  .ui-master-detail[data-rail-width="wide"] {
1967
1984
  --master-detail-rail-size: var(--master-detail-rail-wide);
1968
1985
  }
1986
+
1969
1987
  .ui-responsive-grid-item {
1970
1988
  grid-column: span min(var(--responsive-grid-base, 1), var(--responsive-grid-item-base, 1));
1989
+ grid-row: span var(--responsive-grid-item-row-base, 1);
1971
1990
  }
1972
1991
  @container responsive-grid (min-width: 40rem) {
1973
1992
  .ui-responsive-grid-item {
1974
1993
  grid-column: span min(var(--responsive-grid-sm, 1), var(--responsive-grid-item-sm, 1));
1994
+ grid-row: span var(--responsive-grid-item-row-sm, 1);
1975
1995
  }
1976
1996
  }
1977
1997
  @container responsive-grid (min-width: 48rem) {
1978
1998
  .ui-responsive-grid-item {
1979
1999
  grid-column: span min(var(--responsive-grid-md, 1), var(--responsive-grid-item-md, 1));
2000
+ grid-row: span var(--responsive-grid-item-row-md, 1);
1980
2001
  }
1981
2002
  }
1982
2003
  @container responsive-grid (min-width: 64rem) {
1983
2004
  .ui-responsive-grid-item {
1984
2005
  grid-column: span min(var(--responsive-grid-lg, 1), var(--responsive-grid-item-lg, 1));
2006
+ grid-row: span var(--responsive-grid-item-row-lg, 1);
1985
2007
  }
1986
2008
  }
1987
2009
  .ui-separator[data-label-size="2xs"] .ui-separator-label {
@@ -1504,6 +1504,21 @@
1504
1504
  font-weight: var(--font-weight-medium);
1505
1505
  }
1506
1506
 
1507
+ .ui-app-launcher-footer {
1508
+ display: flex;
1509
+ flex-wrap: wrap;
1510
+ align-items: center;
1511
+ gap: var(--app-launcher-footer-gap);
1512
+ margin-block-start: var(--app-launcher-footer-space-block);
1513
+ padding-block-start: var(--app-launcher-footer-space-block);
1514
+ border-block-start: var(--stroke-hairline) solid hsl(var(--border));
1515
+ font-size: var(--app-launcher-footer-font-size);
1516
+ }
1517
+
1518
+ .ui-app-launcher-launchpad .ui-app-launcher-footer {
1519
+ justify-content: center;
1520
+ }
1521
+
1507
1522
  .ui-app-launcher-state {
1508
1523
  display: flex;
1509
1524
  min-height: var(--app-launcher-state-min-height);
@@ -1606,6 +1621,17 @@
1606
1621
  height: var(--sidebar-product-caret-icon-size);
1607
1622
  }
1608
1623
 
1624
+ .sb-action {
1625
+ display: flex;
1626
+ flex-shrink: 0;
1627
+ flex-direction: column;
1628
+ padding: var(--sidebar-action-padding);
1629
+ }
1630
+
1631
+ .sb-root[data-collapsed="true"] .sb-action {
1632
+ align-items: center;
1633
+ }
1634
+
1609
1635
  .sb-nav-scroll {
1610
1636
  flex: 1;
1611
1637
  min-height: 0;
@@ -470,6 +470,11 @@
470
470
  );
471
471
  --emoji-picker-glyph-size: var(--icon-size-lg);
472
472
  --emoji-picker-grid-max-block-size: 16rem;
473
+
474
+ --sortable-list-gap: var(--space-2);
475
+ --sortable-list-item-padding: var(--space-2);
476
+ --sortable-list-item-radius: var(--control-radius);
477
+ --sortable-list-grid-item-min-inline-size: 12rem;
473
478
  }
474
479
 
475
480
  @media (pointer: coarse) {
@@ -29,6 +29,12 @@
29
29
  --sheet-close-rest-alpha: 0.7;
30
30
  --sheet-header-close-space-inline-end: var(--space-8);
31
31
  --dialog-width-default: 32rem;
32
+
33
+ --dialog-width: initial;
34
+
35
+ --dialog-motion-duration: initial;
36
+ --sheet-enter-duration: initial;
37
+ --sheet-exit-duration: initial;
32
38
  --dialog-viewport-inset: 1.5rem;
33
39
  --dialog-overlay-background: initial;
34
40
 
@@ -139,6 +139,8 @@
139
139
 
140
140
  --sidebar-nav-item-radius: initial;
141
141
  --sidebar-nav-scroll-padding: var(--space-3) var(--space-2);
142
+
143
+ --sidebar-action-padding: var(--space-3) var(--space-2) 0;
142
144
  --sidebar-section-gap: var(--space-4);
143
145
  --sidebar-section-label-padding-x: var(--space-2);
144
146
  --sidebar-section-label-padding-bottom: var(--space-1);
@@ -240,6 +242,10 @@
240
242
  --app-launcher-group-label-color: initial;
241
243
  --app-launcher-state-min-height: 8rem;
242
244
 
245
+ --app-launcher-footer-space-block: var(--space-2);
246
+ --app-launcher-footer-gap: var(--space-3);
247
+ --app-launcher-footer-font-size: var(--font-size-sm, var(--font-size-base));
248
+
243
249
  --app-launcher-launchpad-backdrop-background: initial;
244
250
  --app-launcher-launchpad-backdrop-blur-size: var(--blur-lg);
245
251
  --app-launcher-launchpad-backdrop-alpha: 55%;
@@ -91,10 +91,7 @@
91
91
 
92
92
  --page-title-font-size-chrome: var(--heading-h3, var(--font-size-base));
93
93
 
94
- --page-title-font-size-display: var(
95
- --font-size-4xl,
96
- calc(var(--font-size-display) / var(--font-size-display-ratio))
97
- );
94
+ --page-title-font-size-display: initial;
98
95
  --page-title-line-height-display: 1.4;
99
96
 
100
97
  --page-icon-size: var(--icon-size-4xl);
@@ -3,8 +3,8 @@
3
3
  > Generated by `scripts/frame-coverage.mjs` (issue #163). Do not edit by hand — run `pnpm check:frame-coverage`.
4
4
  > Standard: [FRAME-COVERAGE-STANDARD.md](./FRAME-COVERAGE-STANDARD.md). A missing dimension is **UNTESTED**, never a pass.
5
5
 
6
- - Public components: **184**
7
- - With ≥1 frame: **160** · zero-frame: **24**
6
+ - Public components: **185**
7
+ - With ≥1 frame: **161** · zero-frame: **24**
8
8
  - Fully covered (every contract axis declared): **0**
9
9
 
10
10
  ## UNTESTED — no `/frame/**` route
@@ -195,6 +195,7 @@ Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
195
195
  | PieChart | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
196
196
  | CommandPalette | ✓ | N/A | · | · | · | · | · | · | · | · |
197
197
  | EmojiPicker | ✓ | · | · | · | · | · | · | · | · | · |
198
+ | SortableList | ✓ | N/A | · | · | · | · | · | · | · | · |
198
199
  | TwoFactorSetup | ✓ | N/A | · | · | · | · | · | · | · | · |
199
200
  | AuthDivider | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
200
201
  | AuthFooter | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
@@ -11,9 +11,11 @@ import {
11
11
  } from "@godxjp/ui/data-display";
12
12
  import { Button, Text } from "@godxjp/ui/general";
13
13
  import { Flex, PageContainer } from "@godxjp/ui/layout";
14
+ import { useTranslation } from "@godxjp/ui/i18n";
14
15
  import { Building2, KeyRound, ShieldCheck, Sparkles } from "lucide-react";
15
16
 
16
17
  import portraitAmber from "../assets/portrait-amber.svg";
18
+ import shotLandscape from "../assets/shot-landscape.svg";
17
19
  import portraitClay from "../assets/portrait-clay.svg";
18
20
  import portraitIndigo from "../assets/portrait-indigo.svg";
19
21
  import portraitIris from "../assets/portrait-iris.svg";
@@ -39,9 +41,39 @@ import portraitSlate from "../assets/portrait-slate.svg";
39
41
  * Composed only from real @godxjp/ui components.
40
42
  */
41
43
  export default function Demo() {
44
+ const { t } = useTranslation();
42
45
  return (
43
46
  <PageContainer title="Avatar" subtitle="Identity image with a readable fallback">
44
47
  <Flex direction="col" gap="lg">
48
+ <Card>
49
+ <CardHeader>
50
+ <CardTitle level={2}>{t("avatarDocs.fitTitle")}</CardTitle>
51
+ <CardDescription>{t("avatarDocs.fitDescription")}</CardDescription>
52
+ </CardHeader>
53
+ <CardContent>
54
+ {/* gh#1176: a wide logo — cover crops it, contain shows it whole. */}
55
+ <Flex direction="row" wrap align="center" gap="md">
56
+ <Flex direction="col" align="center" gap="xs">
57
+ <Avatar shape="square" size="lg">
58
+ <AvatarImage src={shotLandscape} alt={t("avatarDocs.logo")} />
59
+ <AvatarFallback>AC</AvatarFallback>
60
+ </Avatar>
61
+ <Text size="sm" tone="muted">
62
+ cover
63
+ </Text>
64
+ </Flex>
65
+ <Flex direction="col" align="center" gap="xs">
66
+ <Avatar shape="square" size="lg">
67
+ <AvatarImage src={shotLandscape} alt={t("avatarDocs.logo")} fit="contain" />
68
+ <AvatarFallback>AC</AvatarFallback>
69
+ </Avatar>
70
+ <Text size="sm" tone="muted">
71
+ contain
72
+ </Text>
73
+ </Flex>
74
+ </Flex>
75
+ </CardContent>
76
+ </Card>
45
77
  <Card>
46
78
  <CardHeader>
47
79
  <CardTitle level={2}>Image + fallback</CardTitle>
@@ -0,0 +1,76 @@
1
+ import * as React from "react";
2
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@godxjp/ui/data-display";
3
+ import { SortableList } from "@godxjp/ui/data-entry";
4
+ import { Text } from "@godxjp/ui/general";
5
+ import { useTranslation } from "@godxjp/ui/i18n";
6
+ import { Flex, PageContainer } from "@godxjp/ui/layout";
7
+
8
+ /**
9
+ * SortableList (gh#1173) — a desktop editor's widget order (tiles) and a settings list of tabs,
10
+ * reordered by dragging a grip or by the keyboard, every step announced.
11
+ */
12
+ export default function Demo() {
13
+ const { t, locale } = useTranslation();
14
+ const list = new Intl.ListFormat(locale, { style: "narrow", type: "unit" });
15
+ const ordinal = new Intl.NumberFormat(locale);
16
+ const widgets = [
17
+ { value: "calendar", label: t("sortableListDocs.calendar") },
18
+ { value: "mail", label: t("sortableListDocs.mail") },
19
+ { value: "tasks", label: t("sortableListDocs.tasks") },
20
+ { value: "notes", label: t("sortableListDocs.notes") },
21
+ ];
22
+ const tabs = [
23
+ { value: "overview", label: t("sortableListDocs.overview") },
24
+ { value: "members", label: t("sortableListDocs.members") },
25
+ { value: "billing", label: t("sortableListDocs.billing"), disabled: true },
26
+ { value: "audit", label: t("sortableListDocs.audit") },
27
+ ];
28
+ const [order, setOrder] = React.useState(widgets.map((w) => w.value));
29
+ return (
30
+ <PageContainer title="SortableList" subtitle={t("sortableListDocs.subtitle")}>
31
+ <Flex direction="col" gap="lg">
32
+ <Card>
33
+ <CardHeader>
34
+ <CardTitle level={2}>{t("sortableListDocs.widgetsTitle")}</CardTitle>
35
+ <CardDescription>{t("sortableListDocs.widgetsDescription")}</CardDescription>
36
+ </CardHeader>
37
+ <CardContent>
38
+ <Flex direction="col" gap="sm">
39
+ <SortableList
40
+ aria-label={t("sortableListDocs.widgetsTitle")}
41
+ layout="grid"
42
+ items={widgets}
43
+ value={order}
44
+ onValueChange={setOrder}
45
+ renderItem={(item, { index }) => (
46
+ <Flex direction="col" gap="xs">
47
+ <Text weight="medium">{item.label}</Text>
48
+ <Text size="sm" tone="muted">
49
+ {t("sortableListDocs.slot", { position: ordinal.format(index + 1) })}
50
+ </Text>
51
+ </Flex>
52
+ )}
53
+ />
54
+ <Text size="sm" tone="muted">
55
+ {t("sortableListDocs.saved", {
56
+ order: list.format(
57
+ order.map((key) => widgets.find((w) => w.value === key)?.label ?? key),
58
+ ),
59
+ })}
60
+ </Text>
61
+ </Flex>
62
+ </CardContent>
63
+ </Card>
64
+ <Card>
65
+ <CardHeader>
66
+ <CardTitle level={2}>{t("sortableListDocs.tabsTitle")}</CardTitle>
67
+ <CardDescription>{t("sortableListDocs.tabsDescription")}</CardDescription>
68
+ </CardHeader>
69
+ <CardContent>
70
+ <SortableList aria-label={t("sortableListDocs.tabsTitle")} items={tabs} />
71
+ </CardContent>
72
+ </Card>
73
+ </Flex>
74
+ </PageContainer>
75
+ );
76
+ }