@godxjp/ui 28.3.1 → 28.5.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 (32) hide show
  1. package/README.md +58 -0
  2. package/dist/app/theme-axes.js +6 -0
  3. package/dist/components/data-display/scroll-area.js +2 -0
  4. package/dist/components/data-entry/calendar.d.ts +5 -0
  5. package/dist/components/data-entry/calendar.js +6 -2
  6. package/dist/components/data-entry/date-picker.js +12 -0
  7. package/dist/components/data-entry/index.d.ts +3 -0
  8. package/dist/components/data-entry/index.js +5 -0
  9. package/dist/components/data-entry/time-picker.d.ts +1 -1
  10. package/dist/components/data-entry/time-picker.js +8 -1
  11. package/dist/contracts/measurement.json +1 -1
  12. package/dist/props/components/data-display.prop.d.ts +22 -0
  13. package/dist/props/components/data-entry.prop.d.ts +14 -0
  14. package/dist/props/registry.d.ts +9 -0
  15. package/dist/props/registry.js +11 -0
  16. package/dist/styles/base.css +5 -3
  17. package/dist/styles/card-layout.css +11 -6
  18. package/dist/styles/control.css +19 -10
  19. package/dist/styles/data-display-layout.css +44 -18
  20. package/dist/styles/data-entry-layout.css +3 -3
  21. package/dist/styles/table-layout.css +9 -6
  22. package/dist/styles/text-layout.css +1 -1
  23. package/dist/theme/famgia.service.css +2 -4
  24. package/dist/tokens/components/control.css +4 -3
  25. package/dist/tokens/components/scroll-area.css +3 -0
  26. package/dist/tokens/derived.css +20 -0
  27. package/dist/tokens/foundation.css +6 -13
  28. package/docs/CUSTOMER-THEMING.md +82 -3
  29. package/docs/FRAME-COVERAGE-REPORT.md +1 -1
  30. package/docs/data-display/scroll-area.tsx +6 -2
  31. package/docs/data-entry/upload-crop-dialog.tsx +110 -0
  32. package/package.json +99 -92
package/README.md CHANGED
@@ -12,6 +12,64 @@ npm i @godxjp/ui
12
12
 
13
13
  ---
14
14
 
15
+ ## Using this from an AI agent
16
+
17
+ The catalog — 165 components, 1616 tokens, 47 cardinal rules — is published in two forms from one
18
+ source. **Entry point for either: [`AGENTS.md`](AGENTS.md).**
19
+
20
+ ### If your agent can run a process
21
+
22
+ Claude Code · Codex CLI · Cursor · any MCP client:
23
+
24
+ ```bash
25
+ npx @godxjp/ui sync-rules
26
+ ```
27
+
28
+ Writes `.mcp.json`, `CLAUDE.md` and `.ai/rules/` into the consumer repo and wires
29
+ `@godxjp/ui-mcp`. Prefer this: it is searchable, it drills down instead of dumping, and it is
30
+ **locked to the version on disk** — the failure it prevents is an agent being told a prop does not
31
+ exist because the catalog was two minors behind (gh#789).
32
+
33
+ ### If your agent can only fetch URLs
34
+
35
+ ChatGPT on the web, Claude.ai, or any client without a local process. The same data is served as
36
+ static files straight from this public repo — no hosting, no deploy step:
37
+
38
+ | file | size | what it is |
39
+ | ----------------------------------------------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------ |
40
+ | [`agent/START-HERE.md`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/START-HERE.md) | 6 KB | **read first** — self-contained: the four rules, the token override model, a page that passes review |
41
+ | [`agent/llms.txt`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/llms.txt) | 2 KB | the [llms.txt](https://llmstxt.org/) entry point |
42
+ | [`agent/index.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/index.json) | 2 KB | manifest: version, counts, every file's URL |
43
+ | [`agent/components-index.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json) | 42 KB | all 165 as name + group + tagline — **fetch this first** |
44
+ | [`agent/components/<Name>.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components/Select.json) | 1–32 KB | one file per component, each with its `importPath` — **this is the route to take** |
45
+ | [`agent/components.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components.json) | 1.1 MB | all 165 entries in one file — most URL fetchers truncate this silently; prefer the per-component files |
46
+ | [`agent/tokens.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/tokens.json) | 0.6 MB | every design token, its value and why it exists |
47
+ | [`agent/vocabulary.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/vocabulary.json) | 7 KB | the controlled prop vocabulary |
48
+ | [`agent/rules.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/rules.json) | 17 KB | the 47 cardinal rules |
49
+
50
+ Pasteable bootstrap:
51
+
52
+ > Read https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/START-HERE.md and follow it.
53
+ > Then fetch .../agent/components-index.json to choose components, and
54
+ > .../agent/components/<Name>.json for the props of each one you chose. Tell me which catalog
55
+ > version you read.
56
+
57
+ **Pin to the version you installed.** Every URL above tracks `main`. Swap `main` for the matching
58
+ tag — `https://raw.githubusercontent.com/godx-jp/godxjp-ui/v28.4.0/agent/...` — because a catalog
59
+ newer than your package describes props you do not have, an older one hides props you do, and
60
+ neither failure announces itself. Pinned URLs only resolve for releases whose tag contains
61
+ `agent/`; on a 404 the release predates this catalog, so read `main` and compare
62
+ `index.json` → `version` against what you installed.
63
+
64
+ ### Why static files and not a hosted MCP endpoint
65
+
66
+ GitHub serves files; it does not run servers. MCP over the network needs a process handling POST
67
+ requests, which no amount of Pages or raw hosting provides. A hosted HTTP MCP would work and would
68
+ need real hosting, ops and an auth decision — so the static lane ships first, carrying the same
69
+ data at no operational cost. Regenerate both from the one source with `pnpm gen:agent-catalog`.
70
+
71
+ ---
72
+
15
73
  ## Role & boundary — read this first
16
74
 
17
75
  This package is **the single source of UI truth**. It is shared, versioned infrastructure, which means two things are non-negotiable:
@@ -68,6 +68,12 @@ function applyPrimaryColor(root, color, foreground) {
68
68
  "--primary-active": "initial",
69
69
  "--primary-border": "initial",
70
70
  "--control-outline": "initial",
71
+ /* The brand TEXT roles derive from the seed too (gh#664), so they are reset for the same
72
+ * reason: an ancestor theme that pinned a link colour for the previous brand would otherwise
73
+ * outrank the seed just set, and a re-tinted product would keep the old brand's links. */
74
+ "--text-link": "initial",
75
+ "--text-brand": "initial",
76
+ "--text-primary": "initial",
71
77
  "--primary-hover-channels": `var(--primary-hover-${polarity}-channels)`,
72
78
  "--primary-active-channels": `var(--primary-active-${polarity}-channels)`,
73
79
  "--ring": hsl(rgb)
@@ -120,6 +120,7 @@ const ScrollArea = React.forwardRef(
120
120
  anchorOffset,
121
121
  onAnchoredChange,
122
122
  orientation = "vertical",
123
+ scrollbar = "auto",
123
124
  ...props
124
125
  }, ref) => {
125
126
  const [viewport, setViewport] = React.useState(null);
@@ -153,6 +154,7 @@ const ScrollArea = React.forwardRef(
153
154
  "data-slot": "scroll-area-viewport",
154
155
  "data-anchor": anchor,
155
156
  "data-orientation": orientation,
157
+ "data-scrollbar": scrollbar,
156
158
  className: cn("ui-scroll-area", className),
157
159
  ...props,
158
160
  children: /* @__PURE__ */ jsx("div", { "data-slot": "scroll-area-content", className: "ui-scroll-area-content", children })
@@ -1,4 +1,9 @@
1
1
  import * as React from "react";
2
+ import { DayButton, dateMatchModifiers } from "react-day-picker";
3
+ import type { DateRange, Modifiers } from "react-day-picker";
4
+ export { DayButton, dateMatchModifiers };
5
+ export type { DateRange, Modifiers };
6
+ export type { DayPickerProps, DayButtonProps, Matcher, CalendarDay, DateLib, } from "react-day-picker";
2
7
  import type { CalendarProp } from "../../props/components/data-entry.prop.js";
3
8
  export type { CalendarProp, CalendarProp as CalendarProps, CalendarCellRenderProp, CalendarFooterProp, } from "../../props/components/data-entry.prop.js";
4
9
  export declare function Calendar({ className, classNames, showOutsideDays, "aria-label": ariaLabel, labels, showToday, showClose, onClose, footer, width, bordered, cellRender, month: monthProp, onMonthChange, locale: localeProp, ...props }: CalendarProp): React.JSX.Element;
@@ -187,7 +187,9 @@ function Calendar({
187
187
  * So the original node is passed in and the consumer decorates around it.
188
188
  */
189
189
  ...cellRender ? {
190
- DayButton: (dayButtonProps) => /* @__PURE__ */ jsx(Fragment, { children: cellRender(dayButtonProps.day.date, { originNode: /* @__PURE__ */ jsx(DayButton, { ...dayButtonProps }) }) })
190
+ DayButton: (dayButtonProps) => /* @__PURE__ */ jsx(Fragment, { children: cellRender(dayButtonProps.day.date, {
191
+ originNode: /* @__PURE__ */ jsx(DayButton, { ...dayButtonProps })
192
+ }) })
191
193
  } : {},
192
194
  /*
193
195
  * A consumer's `components` MERGE with ours; they must not replace the object.
@@ -205,5 +207,7 @@ function Calendar({
205
207
  );
206
208
  }
207
209
  export {
208
- Calendar
210
+ Calendar,
211
+ DayButton,
212
+ dateMatchModifiers
209
213
  };
@@ -70,6 +70,7 @@ function DatePicker(props) {
70
70
  defaultOpen = false,
71
71
  onOpenChange,
72
72
  inputReadOnly,
73
+ width,
73
74
  preserveInvalidOnBlur,
74
75
  placement = "bottom-start",
75
76
  renderExtraFooter,
@@ -580,11 +581,16 @@ function DatePicker(props) {
580
581
  "data-size": size,
581
582
  "data-status": status,
582
583
  "data-variant": variant,
584
+ "data-width": width,
583
585
  "aria-disabled": allDisabled ? true : void 0,
584
586
  "data-disabled": allDisabled ? "" : void 0,
585
587
  "data-state": open ? "open" : "closed",
586
588
  className: cn(
587
589
  "ui-control ui-control-composite-field",
590
+ // `bounded` emits NO utility on purpose — control.css owns it, and a utility here
591
+ // would win the layer order and kill the token (gh#366, gh#375, gh#799).
592
+ width === "auto" && "w-auto",
593
+ width === "full" && "w-full",
588
594
  "aria-invalid:border-destructive",
589
595
  CONTROL_VARIANT_CHROME_CLASS[variant ?? "outlined"],
590
596
  CONTROL_STATUS_CHROME_CLASS,
@@ -610,6 +616,12 @@ function DatePicker(props) {
610
616
  /* @__PURE__ */ jsx(
611
617
  Input,
612
618
  {
619
+ "data-width": width,
620
+ className: cn(
621
+ width === "auto" && "w-auto",
622
+ width === "full" && "w-full",
623
+ width === "bounded" && "w-[var(--control-bounded-width)] max-w-full"
624
+ ),
613
625
  id: rootId,
614
626
  name: displayIsCanonical ? resolvedName : "",
615
627
  ref,
@@ -42,6 +42,7 @@ export type { SelectProp, SelectProp as SelectProps } from "./select.js";
42
42
  export type { SearchSelectOptionProp as SelectOption, SearchSelectLoadParamsProp as SelectLoadParams, SearchSelectLoadResultProp as SelectLoadResult, } from "./search-select.js";
43
43
  export { UPLOAD_LIST_IGNORE, Upload, collectUploadCommitActions, createUploadItem, useUploadDraft, } from "./upload.js";
44
44
  export type { UploadProps, UploadFileItem, UploadVariant, UploadCommitAction } from "./upload.js";
45
+ export { UploadCropDialog } from "./upload-crop-dialog.js";
45
46
  export { Cascader } from "./cascader.js";
46
47
  export type { CascaderProps, TreeOption, TreeFieldNames } from "./cascader.js";
47
48
  export { TreeSelect, SHOW_CHILD, SHOW_PARENT, SHOW_ALL } from "./tree-select.js";
@@ -72,3 +73,5 @@ export type { UploadRequestContext, UploadResult, UploadRejection } from "./uplo
72
73
  export type { UploadItemActions } from "./upload-types.js";
73
74
  export { Attachments } from "./attachments.js";
74
75
  export type { AttachmentsProp, AttachmentsProps, AttachmentsItemProp, AttachmentsPlaceholderProp, AttachmentsOverflowProp, AttachmentsRefProp, } from "./attachments.js";
76
+ export { DayButton, dateMatchModifiers } from "./calendar.js";
77
+ export type { DateRange, Modifiers, DayPickerProps, DayButtonProps, Matcher, CalendarDay, DateLib, } from "./calendar.js";
@@ -39,6 +39,7 @@ import {
39
39
  createUploadItem,
40
40
  useUploadDraft
41
41
  } from "./upload.js";
42
+ import { UploadCropDialog } from "./upload-crop-dialog.js";
42
43
  import { Cascader } from "./cascader.js";
43
44
  import { TreeSelect, SHOW_CHILD, SHOW_PARENT, SHOW_ALL } from "./tree-select.js";
44
45
  import { Transfer } from "./transfer.js";
@@ -60,6 +61,7 @@ import { Segmented } from "./segmented.js";
60
61
  import { BranchScopePicker } from "./branch-scope-picker.js";
61
62
  import { TimeRangePicker } from "./time-range-picker.js";
62
63
  import { Attachments } from "./attachments.js";
64
+ import { DayButton, dateMatchModifiers } from "./calendar.js";
63
65
  export {
64
66
  Attachments,
65
67
  BranchScopePicker,
@@ -78,6 +80,7 @@ export {
78
80
  CommandList,
79
81
  CommandPalette,
80
82
  DatePicker,
83
+ DayButton,
81
84
  Field,
82
85
  Form,
83
86
  FormErrors,
@@ -125,8 +128,10 @@ export {
125
128
  TreeSelect,
126
129
  UPLOAD_LIST_IGNORE,
127
130
  Upload,
131
+ UploadCropDialog,
128
132
  collectUploadCommitActions,
129
133
  createUploadItem,
134
+ dateMatchModifiers,
130
135
  useFormLayout,
131
136
  usePasswordStrength,
132
137
  useUploadDraft
@@ -6,4 +6,4 @@ export type { TimePickerDisabledTimeProp, TimePickerProp, TimePickerProp as Time
6
6
  * canonical): form-submittable (give it a `name`), screen-reader friendly, and e2e-testable by
7
7
  * filling the input.
8
8
  */
9
- export declare function TimePicker({ value: controlledValue, defaultValue, onValueChange, placeholder, disabled, className, id, name, minuteStep, hourStep, changeOnScroll, secondStep, showSeconds: showSecondsProp, use12Hours, format, open: openProp, defaultOpen, onOpenChange, inputReadOnly, preserveInvalidOnBlur, placement, renderExtraFooter, size, status, variant, ref, disabledTime, hideDisabledOptions, showNow, needConfirm, allowClear, ...ariaProps }: TimePickerProp): React.JSX.Element;
9
+ export declare function TimePicker({ value: controlledValue, defaultValue, onValueChange, placeholder, disabled, className, id, name, minuteStep, hourStep, changeOnScroll, secondStep, showSeconds: showSecondsProp, use12Hours, format, open: openProp, defaultOpen, onOpenChange, inputReadOnly, width, preserveInvalidOnBlur, placement, renderExtraFooter, size, status, variant, ref, disabledTime, hideDisabledOptions, showNow, needConfirm, allowClear, ...ariaProps }: TimePickerProp): React.JSX.Element;
@@ -377,6 +377,7 @@ function TimePicker({
377
377
  defaultOpen = false,
378
378
  onOpenChange,
379
379
  inputReadOnly,
380
+ width,
380
381
  preserveInvalidOnBlur,
381
382
  placement = "bottom-end",
382
383
  renderExtraFooter,
@@ -428,6 +429,7 @@ function TimePicker({
428
429
  /* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsx("div", { className: cn("relative", className), children: /* @__PURE__ */ jsx(
429
430
  Input,
430
431
  {
432
+ "data-width": width,
431
433
  id,
432
434
  ref,
433
435
  size,
@@ -445,7 +447,12 @@ function TimePicker({
445
447
  "aria-haspopup": "dialog",
446
448
  "aria-controls": open ? dialogId : void 0,
447
449
  ...fieldA11y,
448
- className: "tabular-nums",
450
+ className: cn(
451
+ "tabular-nums",
452
+ width === "auto" && "w-auto",
453
+ width === "full" && "w-full",
454
+ width === "bounded" && "w-[var(--control-bounded-width)] max-w-full"
455
+ ),
449
456
  trailingIcon: /* @__PURE__ */ jsx("span", { className: "ui-time-picker-affix", children: showClear ? /* @__PURE__ */ jsx(
450
457
  "button",
451
458
  {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$comment": "AUTO-GENERATED by scripts/gen-measurement-contract.mjs — do not edit. Read this instead of guessing: docs/MEASUREMENT-CONTRACT.md.",
3
- "version": "28.3.1",
3
+ "version": "28.5.0",
4
4
  "targetSize": {
5
5
  "standard": "WCAG 2.2 SC 2.5.8 Target Size (Minimum), level AA — 24×24 CSS px",
6
6
  "min": 24,
@@ -540,6 +540,11 @@ export type PermissionMatrixProp = {
540
540
  * live stream (chat, log tail, streaming response, activity feed).
541
541
  */
542
542
  export type ScrollAreaAnchorProp = "none" | "bottom";
543
+ /**
544
+ * Whether the scrollbar is always drawn, or left to the platform (gh#798). `auto` is the platform
545
+ * decision and the default; `always` forces a classic, space-occupying bar.
546
+ */
547
+ export type ScrollAreaScrollbarProp = "auto" | "always";
543
548
  /** @see ScrollArea */
544
549
  /**
545
550
  * @see ScrollArea — which AXES scroll. This is not decoration: it IS the `overflow` the element
@@ -570,6 +575,23 @@ export type ScrollAreaProp = {
570
575
  */
571
576
  viewportRef?: React.Ref<HTMLDivElement>;
572
577
  /** Edge the viewport sticks to as content grows. Default `none` (inert). */
578
+ /**
579
+ * Whether the scrollbar is drawn ALWAYS or only when the platform feels like it (gh#798).
580
+ *
581
+ * Default `auto` — today's behaviour, unchanged: the browser decides, styled from the
582
+ * `--scroll-area-*` tokens. On macOS/iPadOS with the system default "Show scroll bars: when
583
+ * scrolling", that decision is an OVERLAY bar, which exists only while the reader is already
584
+ * scrolling — so the tokens style a bar nobody sees, and a wide area does not LOOK scrollable.
585
+ * Measured by a consumer: clientWidth 727, scrollWidth 3476, and `offsetHeight - clientHeight`
586
+ * of 2px (the border, twice) — no bar occupies layout at all. A manager reading a 31-column
587
+ * roster said it showed "only five days".
588
+ *
589
+ * `always` forces a classic, space-occupying bar via `::-webkit-scrollbar`, which Chrome and
590
+ * Safari honour even in overlay mode, painted from the same tokens. Reach for it where the
591
+ * affordance IS the information — a wide table, a roster, a board. Leave it `auto` for a chat
592
+ * stream, where a permanent bar is noise (gh#311).
593
+ */
594
+ scrollbar?: ScrollAreaScrollbarProp;
573
595
  anchor?: ScrollAreaAnchorProp;
574
596
  /**
575
597
  * Distance in px from the bottom edge inside which the reader still counts as "at the bottom"
@@ -770,6 +770,20 @@ export type CalendarFooterProp = {
770
770
  };
771
771
  /** Shared picker chrome. Placement uses logical start/end so RTL follows the locale. */
772
772
  export type PickerChromeProp = {
773
+ /**
774
+ * How the field takes its inline space, exactly as `Select` means it (gh#799).
775
+ *
776
+ * CONSUMER-RULES §5 says a control outside a form takes `width="auto"`, and `Select` has carried
777
+ * `width` since gh#375 — the pickers did not, so the one control in a toolbar with a KNOWN
778
+ * content width was the one that could not be sized. Measured in a consumer toolbar at 1440px:
779
+ * a month picker showing `2026/09` took 275px, against the 144px box it had before the migration,
780
+ * because with no prop it falls to the flex default and stretches.
781
+ *
782
+ * `auto` shrink-wraps to the display format; `full` fills the line; `bounded` holds one width
783
+ * while the value's length varies (its width is owned by the `[data-width="bounded"]` rule in
784
+ * control.css, NOT by a utility — see the note there).
785
+ */
786
+ width?: ControlWidthProp;
773
787
  open?: boolean;
774
788
  defaultOpen?: boolean;
775
789
  onOpenChange?: (open: boolean) => void;
@@ -1810,6 +1810,15 @@ export declare const COMPONENT_PROP_REGISTRY: {
1810
1810
  readonly reason: 'Edge the scrolling viewport sticks to as content grows ("none" | "bottom"). A closed union, not a boolean pair — the third edge ("top", for an inverted feed) is a value, not another flag.';
1811
1811
  }];
1812
1812
  };
1813
+ readonly ScrollAreaScrollbarProp: {
1814
+ readonly group: "data-display";
1815
+ readonly file: "components/data-display.prop.ts";
1816
+ readonly vocabulary: readonly [{
1817
+ readonly field: "scrollbar";
1818
+ readonly local: true;
1819
+ readonly reason: 'Whether the scrollbar is always drawn or left to the platform ("auto" | "always"). Not a width or a colour — those are the --scroll-area-* tokens; this names WHO decides the bar exists, because on macOS/iPadOS the platform decides "only while scrolling" and a wide area then never looks scrollable (gh#798).';
1820
+ }];
1821
+ };
1813
1822
  readonly ScrollAreaOrientationProp: {
1814
1823
  readonly group: "data-display";
1815
1824
  readonly file: "components/data-display.prop.ts";
@@ -2203,6 +2203,17 @@ const COMPONENT_PROP_REGISTRY = {
2203
2203
  }
2204
2204
  ]
2205
2205
  },
2206
+ ScrollAreaScrollbarProp: {
2207
+ group: "data-display",
2208
+ file: "components/data-display.prop.ts",
2209
+ vocabulary: [
2210
+ {
2211
+ field: "scrollbar",
2212
+ local: true,
2213
+ reason: 'Whether the scrollbar is always drawn or left to the platform ("auto" | "always"). Not a width or a colour \u2014 those are the --scroll-area-* tokens; this names WHO decides the bar exists, because on macOS/iPadOS the platform decides "only while scrolling" and a wide area then never looks scrollable (gh#798).'
2214
+ }
2215
+ ]
2216
+ },
2206
2217
  ScrollAreaOrientationProp: {
2207
2218
  group: "data-display",
2208
2219
  file: "components/data-display.prop.ts",
@@ -74,7 +74,9 @@
74
74
  --color-attention: hsl(var(--attention));
75
75
  --color-attention-foreground: hsl(var(--attention-foreground));
76
76
 
77
- --color-primary-strong: hsl(var(--text-primary));
77
+ --color-primary-strong: hsl(
78
+ var(--text-primary, from hsl(var(--primary)) var(--text-primary-channels))
79
+ );
78
80
  --color-success-strong: hsl(var(--text-success));
79
81
  --color-warning-strong: hsl(var(--text-warning));
80
82
  --color-info-strong: hsl(var(--text-info));
@@ -97,8 +99,8 @@
97
99
 
98
100
  --color-foreground-tertiary: hsl(var(--text-tertiary));
99
101
  --color-foreground-disabled: hsl(var(--text-disabled));
100
- --color-link: hsl(var(--text-link));
101
- --color-brand: hsl(var(--text-brand));
102
+ --color-link: hsl(var(--text-link, from hsl(var(--primary)) var(--text-link-channels)));
103
+ --color-brand: hsl(var(--text-brand, from hsl(var(--primary)) var(--text-brand-channels)));
102
104
 
103
105
  --color-primary-hover: hsl(
104
106
  var(--primary-hover, from hsl(var(--primary)) var(--primary-hover-channels))
@@ -55,27 +55,32 @@
55
55
  }
56
56
 
57
57
  [data-slot="card"][data-accent="primary"] {
58
- --card-accent-color: hsl(var(--mark-primary));
58
+ --card-accent-color: hsl(
59
+ var(
60
+ --mark-primary,
61
+ var(--text-primary, from hsl(var(--primary)) var(--text-primary-channels))
62
+ )
63
+ );
59
64
  }
60
65
 
61
66
  [data-slot="card"][data-accent="success"] {
62
- --card-accent-color: hsl(var(--mark-success));
67
+ --card-accent-color: hsl(var(--mark-success, var(--text-success)));
63
68
  }
64
69
 
65
70
  [data-slot="card"][data-accent="warning"] {
66
- --card-accent-color: hsl(var(--mark-warning));
71
+ --card-accent-color: hsl(var(--mark-warning, var(--text-warning)));
67
72
  }
68
73
 
69
74
  [data-slot="card"][data-accent="info"] {
70
- --card-accent-color: hsl(var(--mark-info));
75
+ --card-accent-color: hsl(var(--mark-info, var(--text-info)));
71
76
  }
72
77
 
73
78
  [data-slot="card"][data-accent="attention"] {
74
- --card-accent-color: hsl(var(--mark-attention));
79
+ --card-accent-color: hsl(var(--mark-attention, var(--attention)));
75
80
  }
76
81
 
77
82
  [data-slot="card"][data-accent="destructive"] {
78
- --card-accent-color: hsl(var(--mark-destructive));
83
+ --card-accent-color: hsl(var(--mark-destructive, var(--text-error)));
79
84
  }
80
85
 
81
86
  [data-slot="card"][data-accent] {
@@ -999,7 +999,7 @@
999
999
  .ui-otp-container:has(.ui-otp-input[data-status="warning"]) .ui-otp-slot,
1000
1000
  .ui-otp-container:has(.ui-otp-input[data-status="warning"])
1001
1001
  .ui-otp-group[data-appearance="grouped"] {
1002
- border-color: var(--control-status-warning-border-color);
1002
+ border-color: var(--control-status-warning-border-color, hsl(var(--text-warning)));
1003
1003
  }
1004
1004
 
1005
1005
  .ui-otp-container:has(.ui-otp-input[readonly]) .ui-otp-slot {
@@ -1326,7 +1326,9 @@
1326
1326
  cursor: not-allowed;
1327
1327
  }
1328
1328
 
1329
- .ui-control-trigger[data-width="bounded"] {
1329
+ .ui-control-trigger[data-width="bounded"],
1330
+
1331
+ .ui-control[data-width="bounded"] {
1330
1332
  inline-size: var(--control-bounded-width);
1331
1333
  max-inline-size: 100%;
1332
1334
  }
@@ -1369,10 +1371,10 @@
1369
1371
  }
1370
1372
 
1371
1373
  .ui-control-surface[data-status="warning"] {
1372
- border-color: var(--control-status-warning-border-color);
1374
+ border-color: var(--control-status-warning-border-color, hsl(var(--text-warning)));
1373
1375
  --focus-ring-color: var(--warning);
1374
1376
  --focus-outline-color: var(--warning);
1375
- --focus-ring-glow-color: var(--control-status-warning-outline-color);
1377
+ --focus-ring-glow-color: var(--control-status-warning-outline-color, var(--text-warning));
1376
1378
  }
1377
1379
 
1378
1380
  .ui-number-input[data-size="sm"] {
@@ -2325,8 +2327,15 @@
2325
2327
 
2326
2328
  .ui-calendar[data-width="full"] .ui-calendar-day-button {
2327
2329
  inline-size: 100%;
2328
-
2329
2330
  max-inline-size: none;
2331
+ block-size: auto;
2332
+ min-block-size: var(--control-height);
2333
+
2334
+ flex-direction: column;
2335
+ align-items: stretch;
2336
+ justify-content: flex-start;
2337
+ text-align: start;
2338
+ white-space: normal;
2330
2339
  }
2331
2340
 
2332
2341
  .ui-calendar-month {
@@ -2433,7 +2442,7 @@
2433
2442
 
2434
2443
  .ui-calendar .ui-calendar-day.day-today:not(.day-selected) .ui-calendar-day-button {
2435
2444
  box-shadow: inset 0 0 0 1px hsl(var(--primary));
2436
- color: hsl(var(--text-primary));
2445
+ color: hsl(var(--text-primary, from hsl(var(--primary)) var(--text-primary-channels)));
2437
2446
  font-weight: var(--font-weight-medium);
2438
2447
  }
2439
2448
 
@@ -2462,7 +2471,7 @@
2462
2471
 
2463
2472
  .ui-calendar .ui-calendar-day.day-today:not(:has(button)) {
2464
2473
  box-shadow: inset 0 0 0 1px hsl(var(--primary));
2465
- color: hsl(var(--text-primary));
2474
+ color: hsl(var(--text-primary, from hsl(var(--primary)) var(--text-primary-channels)));
2466
2475
  font-weight: var(--font-weight-medium);
2467
2476
  border-radius: var(--calendar-day-radius);
2468
2477
  }
@@ -2797,8 +2806,8 @@
2797
2806
 
2798
2807
  .ui-control[data-status="warning"],
2799
2808
  .ui-control-multiline[data-status="warning"] {
2800
- --focus-ring-color: var(--control-status-warning-outline-color);
2801
- --focus-outline-color: var(--control-status-warning-outline-color);
2809
+ --focus-ring-color: var(--control-status-warning-outline-color, var(--text-warning));
2810
+ --focus-outline-color: var(--control-status-warning-outline-color, var(--text-warning));
2802
2811
  --focus-ring-glow-color: var(--control-status-warning-glow-color);
2803
2812
  --focus-ring-glow-alpha: var(--control-status-warning-glow-alpha);
2804
2813
  }
@@ -2824,7 +2833,7 @@
2824
2833
  }
2825
2834
 
2826
2835
  .ui-control-count[data-exceeded="true"] {
2827
- color: hsl(var(--control-count-exceeded-color));
2836
+ color: hsl(var(--control-count-exceeded-color, var(--text-error)));
2828
2837
  }
2829
2838
 
2830
2839
  .ui-textarea-count {
@@ -182,19 +182,19 @@
182
182
  .ui-progress-bar {
183
183
  height: 100%;
184
184
  border-radius: inherit;
185
- background: var(--progress-fill-background, hsl(var(--mark-success)));
185
+ background: var(--progress-fill-background, hsl(var(--mark-success, var(--text-success))));
186
186
  }
187
187
 
188
188
  .ui-progress[data-tone="warning"] .ui-progress-bar {
189
- background: hsl(var(--mark-warning));
189
+ background: hsl(var(--mark-warning, var(--text-warning)));
190
190
  }
191
191
 
192
192
  .ui-progress[data-tone="destructive"] .ui-progress-bar {
193
- background: hsl(var(--mark-destructive));
193
+ background: hsl(var(--mark-destructive, var(--text-error)));
194
194
  }
195
195
 
196
196
  .ui-progress[data-over] .ui-progress-bar {
197
- background-color: hsl(var(--mark-destructive));
197
+ background-color: hsl(var(--mark-destructive, var(--text-error)));
198
198
  background-image: repeating-linear-gradient(
199
199
  45deg,
200
200
  hsl(var(--destructive-foreground) / 0.4) 0,
@@ -223,15 +223,15 @@
223
223
  }
224
224
 
225
225
  .ui-progress-segment[data-tone="success"] {
226
- background: hsl(var(--mark-success));
226
+ background: hsl(var(--mark-success, var(--text-success)));
227
227
  }
228
228
 
229
229
  .ui-progress-segment[data-tone="warning"] {
230
- background: hsl(var(--mark-warning));
230
+ background: hsl(var(--mark-warning, var(--text-warning)));
231
231
  }
232
232
 
233
233
  .ui-progress-segment[data-tone="destructive"] {
234
- background: hsl(var(--mark-destructive));
234
+ background: hsl(var(--mark-destructive, var(--text-error)));
235
235
  }
236
236
 
237
237
  .ui-progress-label {
@@ -273,17 +273,17 @@
273
273
  }
274
274
 
275
275
  .ui-progress-ring-indicator {
276
- stroke: var(--progress-fill-background, hsl(var(--mark-success)));
276
+ stroke: var(--progress-fill-background, hsl(var(--mark-success, var(--text-success))));
277
277
 
278
278
  stroke-linecap: butt;
279
279
  }
280
280
 
281
281
  .ui-progress[data-tone="warning"] .ui-progress-ring-indicator {
282
- stroke: hsl(var(--mark-warning));
282
+ stroke: hsl(var(--mark-warning, var(--text-warning)));
283
283
  }
284
284
 
285
285
  .ui-progress[data-tone="destructive"] .ui-progress-ring-indicator {
286
- stroke: hsl(var(--mark-destructive));
286
+ stroke: hsl(var(--mark-destructive, var(--text-error)));
287
287
  }
288
288
 
289
289
  .ui-progress-ring-label {
@@ -320,23 +320,28 @@
320
320
  }
321
321
 
322
322
  .ui-legend-swatch[data-tone="default"] {
323
- background: hsl(var(--mark-primary));
323
+ background: hsl(
324
+ var(
325
+ --mark-primary,
326
+ var(--text-primary, from hsl(var(--primary)) var(--text-primary-channels))
327
+ )
328
+ );
324
329
  }
325
330
 
326
331
  .ui-legend-swatch[data-tone="success"] {
327
- background: hsl(var(--mark-success));
332
+ background: hsl(var(--mark-success, var(--text-success)));
328
333
  }
329
334
 
330
335
  .ui-legend-swatch[data-tone="warning"] {
331
- background: hsl(var(--mark-warning));
336
+ background: hsl(var(--mark-warning, var(--text-warning)));
332
337
  }
333
338
 
334
339
  .ui-legend-swatch[data-tone="destructive"] {
335
- background: hsl(var(--mark-destructive));
340
+ background: hsl(var(--mark-destructive, var(--text-error)));
336
341
  }
337
342
 
338
343
  .ui-legend-swatch[data-tone="info"] {
339
- background: hsl(var(--mark-info));
344
+ background: hsl(var(--mark-info, var(--text-info)));
340
345
  }
341
346
 
342
347
  .ui-legend-swatch[data-tone="muted"],
@@ -386,11 +391,11 @@
386
391
  }
387
392
 
388
393
  .ui-feature-list-item[data-state="included"] .ui-feature-list-mark {
389
- color: hsl(var(--mark-success));
394
+ color: hsl(var(--mark-success, var(--text-success)));
390
395
  }
391
396
 
392
397
  .ui-feature-list-item[data-state="limited"] .ui-feature-list-mark {
393
- color: hsl(var(--mark-warning));
398
+ color: hsl(var(--mark-warning, var(--text-warning)));
394
399
  }
395
400
 
396
401
  .ui-feature-list-item[data-state="excluded"] .ui-feature-list-label {
@@ -958,6 +963,24 @@
958
963
  overflow: auto;
959
964
  }
960
965
 
966
+ .ui-scroll-area[data-scrollbar="always"] {
967
+ scrollbar-width: auto;
968
+ }
969
+
970
+ .ui-scroll-area[data-scrollbar="always"]::-webkit-scrollbar {
971
+ inline-size: var(--scroll-area-always-bar-size);
972
+ block-size: var(--scroll-area-always-bar-size);
973
+ }
974
+
975
+ .ui-scroll-area[data-scrollbar="always"]::-webkit-scrollbar-track {
976
+ background: var(--scroll-area-track-color, transparent);
977
+ }
978
+
979
+ .ui-scroll-area[data-scrollbar="always"]::-webkit-scrollbar-thumb {
980
+ background: var(--scroll-area-thumb-color, hsl(var(--border)));
981
+ border-radius: var(--scroll-area-always-thumb-radius);
982
+ }
983
+
961
984
  .ui-scroll-area:is([data-orientation="horizontal"], [data-orientation="both"])
962
985
  > .ui-scroll-area-content {
963
986
  inline-size: max-content;
@@ -1051,7 +1074,10 @@
1051
1074
 
1052
1075
  .ui-code-block [data-code-token="link"],
1053
1076
  .ui-prose pre [data-code-token="link"] {
1054
- color: var(--code-block-token-link-color, hsl(var(--text-link)));
1077
+ color: var(
1078
+ --code-block-token-link-color,
1079
+ hsl(var(--text-link, from hsl(var(--primary)) var(--text-link-channels)))
1080
+ );
1055
1081
  text-decoration-line: underline;
1056
1082
 
1057
1083
  text-underline-offset: var(--text-link-underline-offset);