@godxjp/ui 16.5.0 → 16.7.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.
- package/dist/components/data-display/badge.d.ts +6 -1
- package/dist/components/data-display/badge.js +2 -0
- package/dist/components/data-entry/input.d.ts +12 -0
- package/dist/components/data-entry/input.js +17 -2
- package/dist/components/general/typography.d.ts +1 -0
- package/dist/components/general/typography.js +2 -1
- package/dist/lib/control-styles.d.ts +5 -0
- package/dist/lib/control-styles.js +2 -0
- package/dist/props/components/data-display.prop.d.ts +2 -1
- package/dist/props/components/data-entry.prop.d.ts +4 -0
- package/dist/props/components/general.prop.d.ts +2 -0
- package/dist/styles/alert-layout.css +1 -1
- package/dist/styles/card-layout.css +13 -9
- package/dist/styles/control.css +12 -12
- package/dist/styles/data-display-layout.css +13 -13
- package/dist/styles/data-entry-layout.css +2 -2
- package/dist/styles/index.css +2 -1
- package/dist/styles/layout.css +14 -1
- package/dist/styles/navigation-layout.css +6 -6
- package/dist/styles/shell-layout.css +6 -6
- package/dist/styles/table-layout.css +12 -6
- package/dist/styles/text-layout.css +9 -4
- package/dist/tokens/components/badge.css +10 -0
- package/dist/tokens/components/card.css +79 -0
- package/dist/tokens/components/control.css +92 -0
- package/dist/tokens/components/data-display.css +34 -0
- package/dist/tokens/components/data-entry.css +6 -0
- package/dist/tokens/components/descriptions.css +9 -0
- package/dist/tokens/components/feedback.css +39 -0
- package/dist/tokens/components/form.css +11 -0
- package/dist/tokens/components/list-row.css +11 -0
- package/dist/tokens/components/navigation.css +21 -0
- package/dist/tokens/components/shell.css +35 -0
- package/dist/tokens/components/table.css +26 -0
- package/dist/tokens/foundation.css +23 -6
- package/dist/tokens/semantic/layout.css +54 -0
- package/package.json +1 -1
|
@@ -2,7 +2,12 @@ import * as React from "react";
|
|
|
2
2
|
import { type VariantProps } from "class-variance-authority";
|
|
3
3
|
import type { ShapeProp, ToneProp } from "../../props/vocabulary";
|
|
4
4
|
export type BadgeVariant = "default" | "secondary" | "outline" | "dashed";
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Badge tones extend the shared status `ToneProp` with a brand `primary` tone — a SOFT brand pill
|
|
7
|
+
* (tinted fill + brand text), the dashboard "role pill" pattern. The status tones stay status-only;
|
|
8
|
+
* for a SOLID brand fill use `variant="default"`.
|
|
9
|
+
*/
|
|
10
|
+
export type BadgeTone = ToneProp | "primary";
|
|
6
11
|
declare const badgeVariants: (props?: ({
|
|
7
12
|
variant?: "default" | "outline" | "dashed" | "secondary" | null | undefined;
|
|
8
13
|
shape?: "default" | "pill" | "sharp" | null | undefined;
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
toneInfoClass,
|
|
18
18
|
toneMutedClass,
|
|
19
19
|
toneNeutralClass,
|
|
20
|
+
tonePrimaryClass,
|
|
20
21
|
toneSuccessClass,
|
|
21
22
|
toneWarningClass
|
|
22
23
|
} from "../../lib/control-styles";
|
|
@@ -65,6 +66,7 @@ const badgeVariants = cva(
|
|
|
65
66
|
);
|
|
66
67
|
const badgeToneClass = {
|
|
67
68
|
default: void 0,
|
|
69
|
+
primary: cn("border-transparent", tonePrimaryClass),
|
|
68
70
|
success: cn("border-transparent", toneSuccessClass),
|
|
69
71
|
warning: cn("border-transparent", toneWarningClass),
|
|
70
72
|
destructive: cn("border-transparent", toneDestructiveClass),
|
|
@@ -4,6 +4,12 @@ export type InputProps = React.InputHTMLAttributes<HTMLInputElement> & {
|
|
|
4
4
|
allowClear?: boolean;
|
|
5
5
|
/** Called after the field is cleared via the inline ✕. */
|
|
6
6
|
onClear?: () => void;
|
|
7
|
+
/**
|
|
8
|
+
* A leading affordance pinned inside the start of the field — e.g. a mail or
|
|
9
|
+
* search icon (the common auth/search pattern). Decorative by default; the
|
|
10
|
+
* input keeps the keyboard focus. Pairs with `trailingIcon`/`allowClear`.
|
|
11
|
+
*/
|
|
12
|
+
leadingIcon?: React.ReactNode;
|
|
7
13
|
/**
|
|
8
14
|
* A trailing affordance pinned inside the field — e.g. a calendar / clock popover
|
|
9
15
|
* trigger button. ONE trailing icon shows at a time: when `allowClear` and the field
|
|
@@ -16,6 +22,12 @@ export declare const Input: React.ForwardRefExoticComponent<React.InputHTMLAttri
|
|
|
16
22
|
allowClear?: boolean;
|
|
17
23
|
/** Called after the field is cleared via the inline ✕. */
|
|
18
24
|
onClear?: () => void;
|
|
25
|
+
/**
|
|
26
|
+
* A leading affordance pinned inside the start of the field — e.g. a mail or
|
|
27
|
+
* search icon (the common auth/search pattern). Decorative by default; the
|
|
28
|
+
* input keeps the keyboard focus. Pairs with `trailingIcon`/`allowClear`.
|
|
29
|
+
*/
|
|
30
|
+
leadingIcon?: React.ReactNode;
|
|
19
31
|
/**
|
|
20
32
|
* A trailing affordance pinned inside the field — e.g. a calendar / clock popover
|
|
21
33
|
* trigger button. ONE trailing icon shows at a time: when `allowClear` and the field
|
|
@@ -18,6 +18,7 @@ const Input = React.forwardRef(
|
|
|
18
18
|
type,
|
|
19
19
|
allowClear = false,
|
|
20
20
|
onClear,
|
|
21
|
+
leadingIcon,
|
|
21
22
|
trailingIcon,
|
|
22
23
|
value,
|
|
23
24
|
defaultValue,
|
|
@@ -58,7 +59,7 @@ const Input = React.forwardRef(
|
|
|
58
59
|
setHasText(false);
|
|
59
60
|
onClear?.();
|
|
60
61
|
};
|
|
61
|
-
if (!allowClear && trailingIcon == null) {
|
|
62
|
+
if (!allowClear && trailingIcon == null && leadingIcon == null) {
|
|
62
63
|
return /* @__PURE__ */ jsx(
|
|
63
64
|
"input",
|
|
64
65
|
{
|
|
@@ -86,6 +87,15 @@ const Input = React.forwardRef(
|
|
|
86
87
|
}
|
|
87
88
|
) : trailingIcon;
|
|
88
89
|
return /* @__PURE__ */ jsxs("span", { "data-slot": "input-affix-wrapper", className: "relative inline-flex w-full items-center", children: [
|
|
90
|
+
leadingIcon != null ? /* @__PURE__ */ jsx(
|
|
91
|
+
"span",
|
|
92
|
+
{
|
|
93
|
+
"data-slot": "input-leading",
|
|
94
|
+
"aria-hidden": "true",
|
|
95
|
+
className: "text-muted-foreground pointer-events-none absolute inset-y-0 start-2 inline-flex items-center",
|
|
96
|
+
children: leadingIcon
|
|
97
|
+
}
|
|
98
|
+
) : null,
|
|
89
99
|
/* @__PURE__ */ jsx(
|
|
90
100
|
"input",
|
|
91
101
|
{
|
|
@@ -95,7 +105,12 @@ const Input = React.forwardRef(
|
|
|
95
105
|
value,
|
|
96
106
|
defaultValue,
|
|
97
107
|
onChange: handleChange,
|
|
98
|
-
className: cn(
|
|
108
|
+
className: cn(
|
|
109
|
+
inputBaseClass,
|
|
110
|
+
leadingIcon != null && "ps-9",
|
|
111
|
+
(showClear || trailingIcon != null) && "pe-9",
|
|
112
|
+
className
|
|
113
|
+
),
|
|
99
114
|
...props
|
|
100
115
|
}
|
|
101
116
|
),
|
|
@@ -27,4 +27,5 @@ export declare const Heading: React.ForwardRefExoticComponent<Omit<React.HTMLAtt
|
|
|
27
27
|
tone?: import("../../props").TextToneProp;
|
|
28
28
|
align?: import("../../props").TextAlignProp;
|
|
29
29
|
truncate?: boolean;
|
|
30
|
+
weight?: import("../../props").FontWeightProp;
|
|
30
31
|
} & React.RefAttributes<HTMLHeadingElement>>;
|
|
@@ -28,12 +28,13 @@ const Text = React.forwardRef(
|
|
|
28
28
|
);
|
|
29
29
|
Text.displayName = "Text";
|
|
30
30
|
const Heading = React.forwardRef(
|
|
31
|
-
({ level = 2, as, tone = "default", align, truncate, className, ...props }, ref) => React.createElement(as ?? `h${level}`, {
|
|
31
|
+
({ level = 2, as, tone = "default", align, truncate, weight = "medium", className, ...props }, ref) => React.createElement(as ?? `h${level}`, {
|
|
32
32
|
ref,
|
|
33
33
|
"data-slot": "heading",
|
|
34
34
|
"data-level": level,
|
|
35
35
|
"data-tone": tone,
|
|
36
36
|
"data-align": align,
|
|
37
|
+
"data-weight": weight,
|
|
37
38
|
"data-truncate": truncate ? "" : void 0,
|
|
38
39
|
className: cn("ui-heading", className),
|
|
39
40
|
...props
|
|
@@ -23,6 +23,11 @@ export declare const tableCellPaddingClass = "py-[length:var(--table-cell-paddin
|
|
|
23
23
|
export declare const toneSuccessClass = "border-success/30 bg-success/10 text-success-strong";
|
|
24
24
|
export declare const toneWarningClass = "border-warning/30 bg-warning/10 text-warning-strong";
|
|
25
25
|
export declare const toneInfoClass = "border-info/30 bg-info/10 text-info-strong";
|
|
26
|
+
/** Soft BRAND pill — a tinted primary tone (border/fill keep the brand role; the TEXT uses the
|
|
27
|
+
* AA-strong brand colour `text-primary-strong`, darker than the fill, so a small brand label clears
|
|
28
|
+
* WCAG AA on the soft tint — `text-primary` alone is only 4.04:1 in light). For a SOLID brand fill
|
|
29
|
+
* use the Badge `default` variant instead. */
|
|
30
|
+
export declare const tonePrimaryClass = "border-primary/30 bg-primary/10 text-primary-strong";
|
|
26
31
|
export declare const toneDestructiveClass = "border-destructive/30 bg-destructive/10 text-error-strong";
|
|
27
32
|
export declare const toneMutedClass = "border-border bg-muted text-muted-foreground";
|
|
28
33
|
export declare const toneNeutralClass = "border-border bg-muted text-muted-foreground";
|
|
@@ -11,6 +11,7 @@ const tableCellPaddingClass = "py-[length:var(--table-cell-padding-y)]";
|
|
|
11
11
|
const toneSuccessClass = "border-success/30 bg-success/10 text-success-strong";
|
|
12
12
|
const toneWarningClass = "border-warning/30 bg-warning/10 text-warning-strong";
|
|
13
13
|
const toneInfoClass = "border-info/30 bg-info/10 text-info-strong";
|
|
14
|
+
const tonePrimaryClass = "border-primary/30 bg-primary/10 text-primary-strong";
|
|
14
15
|
const toneDestructiveClass = "border-destructive/30 bg-destructive/10 text-error-strong";
|
|
15
16
|
const toneMutedClass = "border-border bg-muted text-muted-foreground";
|
|
16
17
|
const toneNeutralClass = "border-border bg-muted text-muted-foreground";
|
|
@@ -29,6 +30,7 @@ export {
|
|
|
29
30
|
toneInfoClass,
|
|
30
31
|
toneMutedClass,
|
|
31
32
|
toneNeutralClass,
|
|
33
|
+
tonePrimaryClass,
|
|
32
34
|
toneSuccessClass,
|
|
33
35
|
toneWarningClass
|
|
34
36
|
};
|
|
@@ -23,7 +23,8 @@ export type DescriptionsItemProp = {
|
|
|
23
23
|
/** @see Badge */
|
|
24
24
|
export type BadgeProp = {
|
|
25
25
|
variant?: "default" | "secondary" | "outline";
|
|
26
|
-
tone
|
|
26
|
+
/** Status tones plus a brand `primary` tone (soft brand pill); solid brand = `variant="default"`. */
|
|
27
|
+
tone?: ToneProp | "primary";
|
|
27
28
|
status?: string;
|
|
28
29
|
icon?: React.ComponentType<{
|
|
29
30
|
className?: string;
|
|
@@ -15,6 +15,10 @@ export type InputProp = React.InputHTMLAttributes<HTMLInputElement> & {
|
|
|
15
15
|
allowClear?: boolean;
|
|
16
16
|
/** Called after the field is cleared via the inline ✕. */
|
|
17
17
|
onClear?: () => void;
|
|
18
|
+
/** A leading affordance pinned inside the start of the field (e.g. a mail/lock icon). */
|
|
19
|
+
leadingIcon?: React.ReactNode;
|
|
20
|
+
/** A trailing affordance pinned inside the end of the field (replaced by the clear ✕ when `allowClear` + value). */
|
|
21
|
+
trailingIcon?: React.ReactNode;
|
|
18
22
|
};
|
|
19
23
|
/** @see Textarea */
|
|
20
24
|
export type TextareaProp = React.TextareaHTMLAttributes<HTMLTextAreaElement> & {
|
|
@@ -28,6 +28,8 @@ export type HeadingProp = Omit<React.HTMLAttributes<HTMLHeadingElement>, "color"
|
|
|
28
28
|
tone?: TextToneProp;
|
|
29
29
|
align?: TextAlignProp;
|
|
30
30
|
truncate?: boolean;
|
|
31
|
+
/** Weight (system canon: 400 · 500 · 700). Default `medium` — set `bold` for an emphasised title. */
|
|
32
|
+
weight?: FontWeightProp;
|
|
31
33
|
};
|
|
32
34
|
/** @see Button */
|
|
33
35
|
export type ButtonProp = React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
@@ -27,13 +27,15 @@
|
|
|
27
27
|
* `[data-accent]` rail-width override below can win within the same cascade layer. */
|
|
28
28
|
border-width: 1px;
|
|
29
29
|
border-style: solid;
|
|
30
|
-
border-color: hsl(var(--card-border));
|
|
30
|
+
border-color: hsl(var(--card-border, var(--border)));
|
|
31
31
|
border-radius: var(--card-radius);
|
|
32
32
|
/* Fill = base card colour with an opt-in role tint washed over it (--card-tint default
|
|
33
|
-
* transparent → byte-identical at rest). Tint layer sits ABOVE the base colour.
|
|
33
|
+
* transparent → byte-identical at rest). Tint layer sits ABOVE the base colour.
|
|
34
|
+
* --card-background defaults to the LIVE --card role at the call site (re-resolves under a
|
|
35
|
+
* scoped theme); a service overrides --card-background to win. */
|
|
34
36
|
background:
|
|
35
37
|
linear-gradient(var(--card-tint), var(--card-tint)),
|
|
36
|
-
hsl(var(--card-background));
|
|
38
|
+
hsl(var(--card-background, var(--card)));
|
|
37
39
|
color: hsl(var(--card-foreground));
|
|
38
40
|
/* Resting elevation + opt-in brand glow — both quiet no-ops by default so a service theme can
|
|
39
41
|
* lift (--card-shadow) and/or glow (--card-glow) every card with no markup change. */
|
|
@@ -126,7 +128,7 @@
|
|
|
126
128
|
align-items: center;
|
|
127
129
|
gap: var(--space-inline-md);
|
|
128
130
|
padding: var(--card-space-header-y) var(--card-space-inset);
|
|
129
|
-
border-block: 1px solid hsl(var(--card-border));
|
|
131
|
+
border-block: 1px solid hsl(var(--card-border, var(--border)));
|
|
130
132
|
}
|
|
131
133
|
|
|
132
134
|
[data-slot="card-bar"]:first-child {
|
|
@@ -154,8 +156,10 @@
|
|
|
154
156
|
}
|
|
155
157
|
|
|
156
158
|
.ui-card-header--banded {
|
|
157
|
-
border-bottom: var(--card-header-border-bottom);
|
|
158
|
-
background-color: hsl(
|
|
159
|
+
border-bottom: var(--card-header-border-bottom, 1px solid hsl(var(--card-border, var(--border))));
|
|
160
|
+
background-color: hsl(
|
|
161
|
+
var(--card-header-background, var(--muted)) / var(--card-header-background-alpha)
|
|
162
|
+
);
|
|
159
163
|
}
|
|
160
164
|
|
|
161
165
|
[data-slot="card-header"][data-banded] {
|
|
@@ -343,7 +347,7 @@
|
|
|
343
347
|
}
|
|
344
348
|
|
|
345
349
|
[data-slot="card-footer"][data-separated] {
|
|
346
|
-
border-top: 1px solid hsl(var(--card-border));
|
|
350
|
+
border-top: 1px solid hsl(var(--card-border, var(--border)));
|
|
347
351
|
/* Divided band → symmetric vertical padding (border-aware), in sync with a banded header. */
|
|
348
352
|
padding-block: var(--card-space-divided-y);
|
|
349
353
|
justify-content: flex-end;
|
|
@@ -401,8 +405,8 @@
|
|
|
401
405
|
height: var(--stat-card-icon-size);
|
|
402
406
|
margin-bottom: var(--stat-card-gap);
|
|
403
407
|
border-radius: var(--stat-card-icon-radius);
|
|
404
|
-
background: var(--stat-card-icon-background);
|
|
405
|
-
color: var(--stat-card-icon-foreground);
|
|
408
|
+
background: var(--stat-card-icon-background, hsl(var(--primary) / 0.1));
|
|
409
|
+
color: var(--stat-card-icon-foreground, hsl(var(--primary)));
|
|
406
410
|
}
|
|
407
411
|
|
|
408
412
|
[data-slot="stat-card-icon"] svg {
|
package/dist/styles/control.css
CHANGED
|
@@ -50,13 +50,13 @@
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
.ui-toggle[data-state="on"] {
|
|
53
|
-
background: var(--toggle-on-background);
|
|
53
|
+
background: var(--toggle-on-background, hsl(var(--primary)));
|
|
54
54
|
color: hsl(var(--primary-foreground));
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
.ui-toggle:focus-visible {
|
|
58
58
|
outline: none;
|
|
59
|
-
box-shadow: 0 0 0 3px hsl(var(--focus-ring-color) / 0.35);
|
|
59
|
+
box-shadow: 0 0 0 3px hsl(var(--focus-ring-color, var(--ring)) / 0.35);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
.ui-toggle:disabled,
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
|
|
86
86
|
.ui-button:focus-visible {
|
|
87
87
|
outline: none;
|
|
88
|
-
box-shadow: 0 0 0 var(--focus-ring-width) hsl(var(--focus-ring-color));
|
|
88
|
+
box-shadow: 0 0 0 var(--focus-ring-width) hsl(var(--focus-ring-color, var(--ring)));
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
.ui-button:disabled {
|
|
@@ -287,7 +287,7 @@
|
|
|
287
287
|
.ui-slider-thumb:focus-visible,
|
|
288
288
|
.ui-switch:focus-visible {
|
|
289
289
|
outline: none;
|
|
290
|
-
box-shadow: 0 0 0 var(--focus-ring-width) hsl(var(--focus-ring-color));
|
|
290
|
+
box-shadow: 0 0 0 var(--focus-ring-width) hsl(var(--focus-ring-color, var(--ring)));
|
|
291
291
|
}
|
|
292
292
|
|
|
293
293
|
.ui-checkbox:disabled,
|
|
@@ -299,7 +299,7 @@
|
|
|
299
299
|
}
|
|
300
300
|
|
|
301
301
|
.ui-checkbox[data-state="checked"] {
|
|
302
|
-
background: var(--checkbox-checked-background);
|
|
302
|
+
background: var(--checkbox-checked-background, hsl(var(--primary)));
|
|
303
303
|
color: hsl(var(--primary-foreground));
|
|
304
304
|
}
|
|
305
305
|
|
|
@@ -336,7 +336,7 @@
|
|
|
336
336
|
}
|
|
337
337
|
|
|
338
338
|
.ui-switch[data-state="checked"] {
|
|
339
|
-
background: var(--switch-checked-background);
|
|
339
|
+
background: var(--switch-checked-background, hsl(var(--primary)));
|
|
340
340
|
}
|
|
341
341
|
|
|
342
342
|
.ui-switch[data-state="unchecked"] {
|
|
@@ -374,13 +374,13 @@
|
|
|
374
374
|
flex-grow: 1;
|
|
375
375
|
overflow: hidden;
|
|
376
376
|
border-radius: var(--radius-pill);
|
|
377
|
-
background: var(--slider-track-background);
|
|
377
|
+
background: var(--slider-track-background, hsl(var(--primary) / 0.2));
|
|
378
378
|
}
|
|
379
379
|
|
|
380
380
|
.ui-slider-range {
|
|
381
381
|
position: absolute;
|
|
382
382
|
height: 100%;
|
|
383
|
-
background: var(--slider-range-background);
|
|
383
|
+
background: var(--slider-range-background, hsl(var(--primary)));
|
|
384
384
|
}
|
|
385
385
|
|
|
386
386
|
.ui-slider-thumb {
|
|
@@ -656,7 +656,7 @@
|
|
|
656
656
|
}
|
|
657
657
|
.ui-otp-slot[data-active="true"] {
|
|
658
658
|
z-index: 1;
|
|
659
|
-
box-shadow: 0 0 0 var(--focus-ring-width) hsl(var(--focus-ring-color));
|
|
659
|
+
box-shadow: 0 0 0 var(--focus-ring-width) hsl(var(--focus-ring-color, var(--ring)));
|
|
660
660
|
}
|
|
661
661
|
.ui-otp-container:has(.ui-otp-input[aria-invalid="true"]) .ui-otp-slot {
|
|
662
662
|
border-color: hsl(var(--destructive));
|
|
@@ -727,7 +727,7 @@
|
|
|
727
727
|
fill: currentColor;
|
|
728
728
|
}
|
|
729
729
|
.ui-rating-star:focus-visible {
|
|
730
|
-
outline: var(--focus-ring-width) solid hsl(var(--focus-ring-color));
|
|
730
|
+
outline: var(--focus-ring-width) solid hsl(var(--focus-ring-color, var(--ring)));
|
|
731
731
|
outline-offset: 2px;
|
|
732
732
|
border-radius: var(--radius-sm);
|
|
733
733
|
}
|
|
@@ -745,8 +745,8 @@
|
|
|
745
745
|
background: hsl(var(--background));
|
|
746
746
|
}
|
|
747
747
|
.ui-tag-input:focus-within {
|
|
748
|
-
box-shadow: 0 0 0 var(--focus-ring-width) hsl(var(--focus-ring-color));
|
|
749
|
-
border-color: hsl(var(--focus-ring-color));
|
|
748
|
+
box-shadow: 0 0 0 var(--focus-ring-width) hsl(var(--focus-ring-color, var(--ring)));
|
|
749
|
+
border-color: hsl(var(--focus-ring-color, var(--ring)));
|
|
750
750
|
}
|
|
751
751
|
.ui-tag-input-disabled {
|
|
752
752
|
opacity: 0.6;
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
/* Tint washes over the role background (default transparent = no-op, rule #44). */
|
|
13
13
|
background:
|
|
14
14
|
linear-gradient(var(--avatar-tint), var(--avatar-tint)),
|
|
15
|
-
var(--avatar-background);
|
|
15
|
+
var(--avatar-background, hsl(var(--muted)));
|
|
16
16
|
color: hsl(var(--muted-foreground));
|
|
17
17
|
font-size: var(--font-size-base);
|
|
18
18
|
font-weight: var(--font-weight-medium);
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
height: 100%;
|
|
31
31
|
align-items: center;
|
|
32
32
|
justify-content: center;
|
|
33
|
-
background: var(--avatar-background);
|
|
33
|
+
background: var(--avatar-background, hsl(var(--muted)));
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
.ui-progress {
|
|
@@ -43,13 +43,13 @@
|
|
|
43
43
|
height: 0.5rem;
|
|
44
44
|
overflow: hidden;
|
|
45
45
|
border-radius: var(--radius-pill);
|
|
46
|
-
background: var(--progress-track-background);
|
|
46
|
+
background: var(--progress-track-background, hsl(var(--secondary)));
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
.ui-progress-bar {
|
|
50
50
|
height: 100%;
|
|
51
51
|
border-radius: inherit;
|
|
52
|
-
background: var(--progress-fill-background);
|
|
52
|
+
background: var(--progress-fill-background, hsl(var(--success)));
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
.ui-progress[data-tone="warning"] .ui-progress-bar {
|
|
@@ -78,8 +78,8 @@
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
.ui-tree-item[data-active="true"] {
|
|
81
|
-
border-color: var(--tree-item-active-border);
|
|
82
|
-
background: var(--tree-item-active-background);
|
|
81
|
+
border-color: var(--tree-item-active-border, hsl(var(--primary) / 0.3));
|
|
82
|
+
background: var(--tree-item-active-background, hsl(var(--primary) / 0.05));
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
.ui-tree-item[data-depth="1"] {
|
|
@@ -154,7 +154,7 @@
|
|
|
154
154
|
place-items: center;
|
|
155
155
|
border: 1px solid transparent;
|
|
156
156
|
border-radius: var(--radius-pill);
|
|
157
|
-
background: var(--timeline-dot-done-background);
|
|
157
|
+
background: var(--timeline-dot-done-background, hsl(var(--success)));
|
|
158
158
|
color: hsl(var(--success-foreground));
|
|
159
159
|
font-size: var(--timeline-note-font-size);
|
|
160
160
|
font-weight: var(--font-weight-medium);
|
|
@@ -163,14 +163,14 @@
|
|
|
163
163
|
|
|
164
164
|
/* done → success fill. */
|
|
165
165
|
.ui-timeline-dot[data-status="done"] {
|
|
166
|
-
background: var(--timeline-dot-done-background);
|
|
166
|
+
background: var(--timeline-dot-done-background, hsl(var(--success)));
|
|
167
167
|
color: hsl(var(--success-foreground));
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
/* current → primary fill + ring (also the legacy data-current shorthand). */
|
|
171
171
|
.ui-timeline-dot[data-status="current"],
|
|
172
172
|
.ui-timeline-dot[data-current="true"] {
|
|
173
|
-
background: var(--timeline-dot-current-background);
|
|
173
|
+
background: var(--timeline-dot-current-background, hsl(var(--primary)));
|
|
174
174
|
color: hsl(var(--primary-foreground));
|
|
175
175
|
box-shadow: 0 0 0 3px hsl(var(--primary) / 0.18);
|
|
176
176
|
}
|
|
@@ -205,7 +205,7 @@
|
|
|
205
205
|
|
|
206
206
|
/* The segment below a done/current step reads as travelled (completed). */
|
|
207
207
|
.ui-timeline-line[data-completed="true"] {
|
|
208
|
-
background: var(--timeline-line-completed-background);
|
|
208
|
+
background: var(--timeline-line-completed-background, hsl(var(--primary)));
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
.ui-timeline-head {
|
|
@@ -259,7 +259,7 @@
|
|
|
259
259
|
text-decoration: underline;
|
|
260
260
|
}
|
|
261
261
|
.ui-accordion-trigger:focus-visible {
|
|
262
|
-
outline: var(--focus-ring-width) solid hsl(var(--focus-ring-color));
|
|
262
|
+
outline: var(--focus-ring-width) solid hsl(var(--focus-ring-color, var(--ring)));
|
|
263
263
|
outline-offset: 2px;
|
|
264
264
|
border-radius: var(--radius-sm);
|
|
265
265
|
}
|
|
@@ -386,7 +386,7 @@
|
|
|
386
386
|
}
|
|
387
387
|
|
|
388
388
|
.ui-carousel-dot:focus-visible {
|
|
389
|
-
outline: var(--focus-ring-width) solid hsl(var(--focus-ring-color));
|
|
389
|
+
outline: var(--focus-ring-width) solid hsl(var(--focus-ring-color, var(--ring)));
|
|
390
390
|
outline-offset: 2px;
|
|
391
391
|
}
|
|
392
392
|
|
|
@@ -444,7 +444,7 @@
|
|
|
444
444
|
align-items: flex-start;
|
|
445
445
|
}
|
|
446
446
|
[data-slot="list-row"]:not(:last-child) {
|
|
447
|
-
border-block-end: var(--list-row-border);
|
|
447
|
+
border-block-end: var(--list-row-border, 1px solid hsl(var(--border)));
|
|
448
448
|
}
|
|
449
449
|
[data-slot="list-row-leading"] {
|
|
450
450
|
display: inline-flex;
|
|
@@ -83,8 +83,8 @@
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
.ui-time-input:focus-visible {
|
|
86
|
-
border-color: hsl(var(--focus-ring-color));
|
|
87
|
-
box-shadow: 0 0 0 3px hsl(var(--focus-ring-color) / 0.3);
|
|
86
|
+
border-color: hsl(var(--focus-ring-color, var(--ring)));
|
|
87
|
+
box-shadow: 0 0 0 3px hsl(var(--focus-ring-color, var(--ring)) / 0.3);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
.ui-time-input[aria-invalid="true"] {
|
package/dist/styles/index.css
CHANGED
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
@source inline("rounded-{xs,xl,2xl}");
|
|
43
43
|
@source inline("text-2xs");
|
|
44
44
|
@source inline("text-{3xl,4xl,5xl}");
|
|
45
|
-
@source inline("text-{success,warning,info,error}-strong");
|
|
45
|
+
@source inline("text-{primary,success,warning,info,error}-strong");
|
|
46
46
|
/* `.js` matters: the published package ships only compiled JS under dist (no
|
|
47
47
|
* .tsx sources), and this file is copied verbatim into dist/styles — without
|
|
48
48
|
* it Tailwind finds no component classes in consumers (unstyled popovers and
|
|
@@ -103,6 +103,7 @@
|
|
|
103
103
|
--color-attention-foreground: hsl(var(--attention-foreground));
|
|
104
104
|
/* AA-strong status TEXT colours (text-{success,warning,info,error}-strong) — darker than the
|
|
105
105
|
* fills so small coloured labels clear WCAG AA on white; flip light on the dark theme. */
|
|
106
|
+
--color-primary-strong: hsl(var(--text-primary));
|
|
106
107
|
--color-success-strong: hsl(var(--text-success));
|
|
107
108
|
--color-warning-strong: hsl(var(--text-warning));
|
|
108
109
|
--color-info-strong: hsl(var(--text-info));
|
package/dist/styles/layout.css
CHANGED
|
@@ -23,6 +23,16 @@
|
|
|
23
23
|
overflow: hidden;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/* Decorative brand SPOTLIGHT backdrop — the token-driven radial halo behind a hero / auth card,
|
|
27
|
+
* replacing a hand-authored `radial-gradient(... hsl(var(--primary)/.18) ...)`. Apply to an
|
|
28
|
+
* `aria-hidden` layer (e.g. an absolutely-positioned full-bleed div behind the card). All geometry
|
|
29
|
+
* and tint come from the `--brand-glow*` tokens, so a theme retints/repositions it with no markup
|
|
30
|
+
* change. `pointer-events:none` keeps the backdrop from stealing clicks from the card above it. */
|
|
31
|
+
.ui-brand-glow {
|
|
32
|
+
background-image: var(--brand-glow);
|
|
33
|
+
pointer-events: none;
|
|
34
|
+
}
|
|
35
|
+
|
|
26
36
|
.ui-flex,
|
|
27
37
|
.ui-stack-xs {
|
|
28
38
|
display: flex;
|
|
@@ -497,7 +507,10 @@
|
|
|
497
507
|
width: 3rem;
|
|
498
508
|
height: 3rem;
|
|
499
509
|
border-radius: var(--radius-pill);
|
|
500
|
-
|
|
510
|
+
/* Medallion fill + glyph colour read their knobs, defaulting to the LIVE --muted /
|
|
511
|
+
* --muted-foreground roles at the call site (re-resolves under a scoped theme). */
|
|
512
|
+
background-color: var(--empty-state-icon-tint, hsl(var(--muted)));
|
|
513
|
+
color: var(--empty-state-icon-foreground, hsl(var(--muted-foreground)));
|
|
501
514
|
}
|
|
502
515
|
}
|
|
503
516
|
|
|
@@ -55,8 +55,8 @@
|
|
|
55
55
|
.ui-menubar-sub-trigger:hover,
|
|
56
56
|
.ui-navigation-menu-link:hover,
|
|
57
57
|
.ui-navigation-menu-item:hover {
|
|
58
|
-
background: var(--menubar-item-hover-background);
|
|
59
|
-
color: var(--menubar-item-hover-foreground);
|
|
58
|
+
background: var(--menubar-item-hover-background, hsl(var(--accent)));
|
|
59
|
+
color: var(--menubar-item-hover-foreground, hsl(var(--accent-foreground)));
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
.ui-context-menu-item[data-state="open"],
|
|
@@ -68,8 +68,8 @@
|
|
|
68
68
|
.ui-menubar-sub-trigger[data-state="open"],
|
|
69
69
|
.ui-navigation-menu-link[data-highlighted],
|
|
70
70
|
.ui-navigation-menu-item[data-focus-visible] {
|
|
71
|
-
background: var(--menubar-item-hover-background);
|
|
72
|
-
color: var(--menubar-item-hover-foreground);
|
|
71
|
+
background: var(--menubar-item-hover-background, hsl(var(--accent)));
|
|
72
|
+
color: var(--menubar-item-hover-foreground, hsl(var(--accent-foreground)));
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
.ui-context-menu-item[data-variant="destructive"],
|
|
@@ -184,8 +184,8 @@
|
|
|
184
184
|
|
|
185
185
|
.ui-menubar-trigger[data-state="open"],
|
|
186
186
|
.ui-menubar-sub-trigger[data-state="open"] {
|
|
187
|
-
background: var(--menubar-item-hover-background);
|
|
188
|
-
color: var(--menubar-item-hover-foreground);
|
|
187
|
+
background: var(--menubar-item-hover-background, hsl(var(--accent)));
|
|
188
|
+
color: var(--menubar-item-hover-foreground, hsl(var(--accent-foreground)));
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
.ui-navigation-menu {
|
|
@@ -270,8 +270,8 @@
|
|
|
270
270
|
* selected nav row a brand treatment — e.g. a gold tint + gold text on a navy sidebar — without
|
|
271
271
|
* forking CSS. Defaults are byte-identical to the hover state. */
|
|
272
272
|
.sb-nav-item[data-active="true"] {
|
|
273
|
-
background: var(--sidebar-item-active-background);
|
|
274
|
-
color: var(--sidebar-item-active-foreground);
|
|
273
|
+
background: var(--sidebar-item-active-background, hsl(var(--accent)));
|
|
274
|
+
color: var(--sidebar-item-active-foreground, hsl(var(--foreground)));
|
|
275
275
|
font-weight: var(--font-weight-medium);
|
|
276
276
|
}
|
|
277
277
|
|
|
@@ -362,12 +362,12 @@
|
|
|
362
362
|
|
|
363
363
|
.sb-nav-item--sub[data-active="true"] {
|
|
364
364
|
background: transparent;
|
|
365
|
-
color: var(--sidebar-item-active-color);
|
|
365
|
+
color: var(--sidebar-item-active-color, hsl(var(--primary)));
|
|
366
366
|
font-weight: 500;
|
|
367
367
|
}
|
|
368
368
|
|
|
369
369
|
.sb-nav-item--sub[data-active="true"]::before {
|
|
370
|
-
background: var(--sidebar-item-active-tint);
|
|
370
|
+
background: var(--sidebar-item-active-tint, hsl(var(--primary)));
|
|
371
371
|
opacity: 1;
|
|
372
372
|
}
|
|
373
373
|
|
|
@@ -417,7 +417,7 @@
|
|
|
417
417
|
}
|
|
418
418
|
|
|
419
419
|
.sb-user:focus-visible {
|
|
420
|
-
box-shadow: 0 0 0 var(--focus-ring-width) hsl(var(--focus-ring-color) / 0.45);
|
|
420
|
+
box-shadow: 0 0 0 var(--focus-ring-width) hsl(var(--focus-ring-color, var(--ring)) / 0.45);
|
|
421
421
|
}
|
|
422
422
|
|
|
423
423
|
.sb-user-avatar {
|
|
@@ -660,7 +660,7 @@
|
|
|
660
660
|
}
|
|
661
661
|
|
|
662
662
|
.tb-icon-btn:focus-visible {
|
|
663
|
-
box-shadow: 0 0 0 var(--focus-ring-width) hsl(var(--focus-ring-color) / 0.45);
|
|
663
|
+
box-shadow: 0 0 0 var(--focus-ring-width) hsl(var(--focus-ring-color, var(--ring)) / 0.45);
|
|
664
664
|
}
|
|
665
665
|
|
|
666
666
|
.tb-bell {
|
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
padding: var(--table-cell-padding-y) var(--table-cell-space-x);
|
|
9
9
|
text-align: start;
|
|
10
10
|
vertical-align: middle;
|
|
11
|
-
background: var(--table-header-background);
|
|
11
|
+
background: var(--table-header-background, hsl(var(--muted)));
|
|
12
12
|
font-size: var(--table-head-font-size);
|
|
13
13
|
font-weight: var(--font-weight-medium);
|
|
14
|
-
color: var(--table-header-foreground);
|
|
14
|
+
color: var(--table-header-foreground, hsl(var(--muted-foreground)));
|
|
15
15
|
white-space: nowrap;
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
/* Zebra striping (opt-in via the `striped` prop). Selected rows keep their
|
|
98
98
|
* own emphasis, so they are excluded from the stripe. */
|
|
99
99
|
.ui-data-table-surface[data-striped] tbody tr:nth-child(even):not([data-state="selected"]) {
|
|
100
|
-
background-color: var(--table-row-striped-background);
|
|
100
|
+
background-color: var(--table-row-striped-background, hsl(var(--muted) / 0.4));
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
/* Empty-state cell hosts the built-in EmptyState (which brings its own padding). */
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
[data-slot="card-content"][data-flush] .ui-data-table-toolbar {
|
|
113
|
-
border-bottom: 1px solid hsl(var(--card-border));
|
|
113
|
+
border-bottom: 1px solid hsl(var(--card-border, var(--border)));
|
|
114
114
|
padding: var(--card-space-header-y) var(--card-space-inset);
|
|
115
115
|
}
|
|
116
116
|
|
|
@@ -195,10 +195,16 @@
|
|
|
195
195
|
/* Mirror the row's hover / selected tint — a translucent muted wash layered
|
|
196
196
|
* over the opaque base, so the pinned cell matches the rest of its row. */
|
|
197
197
|
.ui-data-table-surface tbody tr:hover > .ui-data-table-pin-end {
|
|
198
|
-
background-image: linear-gradient(
|
|
198
|
+
background-image: linear-gradient(
|
|
199
|
+
var(--table-row-hover-background, hsl(var(--muted) / 0.5)),
|
|
200
|
+
var(--table-row-hover-background, hsl(var(--muted) / 0.5))
|
|
201
|
+
);
|
|
199
202
|
}
|
|
200
203
|
|
|
201
204
|
.ui-data-table-surface tbody tr[data-state="selected"] > .ui-data-table-pin-end {
|
|
202
|
-
background-image: linear-gradient(
|
|
205
|
+
background-image: linear-gradient(
|
|
206
|
+
var(--table-row-selected-background, hsl(var(--muted) / 0.3)),
|
|
207
|
+
var(--table-row-selected-background, hsl(var(--muted) / 0.3))
|
|
208
|
+
);
|
|
203
209
|
}
|
|
204
210
|
}
|
|
@@ -33,14 +33,19 @@
|
|
|
33
33
|
font-size: var(--font-size-2xl);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
/* Weight — the dxs-kintai canon (400 body · 500 heading/label · 700 emphasis). No 600.
|
|
37
|
-
|
|
36
|
+
/* Weight — the dxs-kintai canon (400 body · 500 heading/label · 700 emphasis). No 600.
|
|
37
|
+
* Shared by text + heading; the two-attribute heading selectors outrank the base heading
|
|
38
|
+
* rule below, so `<Heading weight="bold">` renders a semantic, emphasised title. */
|
|
39
|
+
[data-slot="text"][data-weight="regular"],
|
|
40
|
+
[data-slot="heading"][data-weight="regular"] {
|
|
38
41
|
font-weight: var(--font-weight-normal);
|
|
39
42
|
}
|
|
40
|
-
[data-slot="text"][data-weight="medium"]
|
|
43
|
+
[data-slot="text"][data-weight="medium"],
|
|
44
|
+
[data-slot="heading"][data-weight="medium"] {
|
|
41
45
|
font-weight: var(--font-weight-medium);
|
|
42
46
|
}
|
|
43
|
-
[data-slot="text"][data-weight="bold"]
|
|
47
|
+
[data-slot="text"][data-weight="bold"],
|
|
48
|
+
[data-slot="heading"][data-weight="bold"] {
|
|
44
49
|
font-weight: var(--font-weight-bold);
|
|
45
50
|
}
|
|
46
51
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* Badge component tokens. */
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
--badge-space-gap: var(--space-inline-xs);
|
|
5
|
+
--badge-space-x: var(--space-2);
|
|
6
|
+
--badge-space-y: var(--space-1);
|
|
7
|
+
/* Small-by-design (badge/pill/counter). A knob (rule #45) so a service can
|
|
8
|
+
* re-tune badge text without touching the global --font-size-xs step. */
|
|
9
|
+
--badge-font-size: var(--font-size-xs);
|
|
10
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/* Card component tokens: card chrome derives from semantic layout tokens. */
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
/* Horizontal inset of every slot (header / content / footer) + the resting top/bottom
|
|
5
|
+
* shell padding. This is the column the title, body and footer all align to. */
|
|
6
|
+
--card-space-inset: var(--space-section-active);
|
|
7
|
+
/* Vertical padding of a BANDED header band (top = bottom). Drives --card-space-divided-y. */
|
|
8
|
+
--card-space-header-y: var(--space-stack-sm);
|
|
9
|
+
/* Gap between the header and the body, and the body's own top padding — the breathing
|
|
10
|
+
* room under a title before content begins. */
|
|
11
|
+
--card-space-body-y: var(--space-section-active);
|
|
12
|
+
/* Vertical padding of a SEPARATED footer band (top = bottom). Drives --card-space-divided-y. */
|
|
13
|
+
--card-space-footer-y: var(--space-stack-sm);
|
|
14
|
+
/* DIVIDED-section vertical padding (rule #44/#45). A header/footer that carries a divider
|
|
15
|
+
* border (banded header, separated footer) reads as its own band, so it pads SYMMETRICALLY
|
|
16
|
+
* top+bottom — distinct from a plain header that flows into the body (top inset, no bottom).
|
|
17
|
+
* One themeable knob keeps the header- and footer-band rhythm in sync; a service theme tunes
|
|
18
|
+
* the band density here instead of forking per-slot CSS. */
|
|
19
|
+
--card-space-divided-y: var(--card-space-header-y);
|
|
20
|
+
/* Vertical gap between stacked items WITHIN a slot (e.g. title ↕ description in the header). */
|
|
21
|
+
--card-space-gap: var(--space-stack-xs);
|
|
22
|
+
--card-title-font-size: var(--font-size-base);
|
|
23
|
+
--card-title-line-height: var(--line-height-tight);
|
|
24
|
+
--card-title-font-weight: var(--font-weight-semibold);
|
|
25
|
+
--card-description-font-size: var(--font-size-sm);
|
|
26
|
+
--card-description-line-height: var(--line-height-normal);
|
|
27
|
+
/* Card fill + edge — opt-in knobs that DEFAULT to the live --card / --border roles. Declared
|
|
28
|
+
* `initial` (not `var(--card)`) so the default re-resolves at the call site under a scoped theme:
|
|
29
|
+
* a :root binding to a role var freezes at the :root value and a scoped `[data-tenant]` override of
|
|
30
|
+
* the role never reaches it (see docs/STANDARDS-vocabulary-tokens.md · "role-mirror knobs"). A
|
|
31
|
+
* service still overrides the knob directly (--card-background: …) to win over the role default. */
|
|
32
|
+
--card-background: initial; /* default = hsl(var(--card)) */
|
|
33
|
+
--card-border: initial; /* default = hsl(var(--border)) */
|
|
34
|
+
/* Banded-header fill — role-tintable (rule #45): a service points this at any role,
|
|
35
|
+
* e.g. --card-header-background: var(--primary), and tunes --card-header-background-alpha for
|
|
36
|
+
* the wash strength. Default = the live --muted role (resolved at the call site). */
|
|
37
|
+
--card-header-background: initial; /* default = hsl(var(--muted)) */
|
|
38
|
+
--card-header-background-alpha: 0.55;
|
|
39
|
+
/* Banded-header divider — tokenised (rule #44) so a service theme can make it
|
|
40
|
+
* dashed / heavier / none without forking CSS. Pair with
|
|
41
|
+
* --card-header-background-alpha: 0 for a quiet borderless-band header.
|
|
42
|
+
* Default = 1px solid hsl(var(--card-border)) (resolved at the call site). */
|
|
43
|
+
--card-header-border-bottom: initial;
|
|
44
|
+
--card-radius: var(--radius);
|
|
45
|
+
/* Resting elevation — quiet by default (rule #44): cards are flat (1px border, no shadow) in the
|
|
46
|
+
* dxs-kintai baseline. A service that wants lifted cards sets this to an elevation token once,
|
|
47
|
+
* e.g. --card-shadow: var(--shadow-sm), and every Card picks up the shadow with no markup change. */
|
|
48
|
+
--card-shadow: 0 0 0 0 transparent;
|
|
49
|
+
/* Brand glow layer — invisible no-op at rest (rule #44). Paired AFTER --card-shadow in the
|
|
50
|
+
* surface box-shadow so a service can wash every card with the global glow, e.g.
|
|
51
|
+
* --card-glow: var(--shadow-glow), with no markup change. */
|
|
52
|
+
--card-glow: 0 0 0 0 transparent;
|
|
53
|
+
/* Fill tint — subtle role wash over the card background (default transparent = invisible).
|
|
54
|
+
* Painted as an overlay so a service sets --card-tint: hsl(var(--primary) / 0.04) once. */
|
|
55
|
+
--card-tint: transparent;
|
|
56
|
+
/* Accent edge — width of the semantic leading-edge stripe (data-accent).
|
|
57
|
+
* Tokenised (rule #44) so a service theme can re-tune it without forking CSS.
|
|
58
|
+
* The slot padding compensation in card-layout.css subtracts the same token,
|
|
59
|
+
* so content stays aligned on the shell whatever the rail width. */
|
|
60
|
+
--card-accent-rail-width: 6px;
|
|
61
|
+
--stat-card-label-font-size: var(--font-size-xs);
|
|
62
|
+
--stat-card-label-font-weight: var(--font-weight-medium);
|
|
63
|
+
--stat-card-label-letter-spacing: 0.04em;
|
|
64
|
+
--stat-card-value-font-size: var(--font-size-2xl);
|
|
65
|
+
--stat-card-value-line-height: 1.1;
|
|
66
|
+
--stat-card-value-font-weight: var(--font-weight-semibold);
|
|
67
|
+
--stat-card-hint-font-size: var(--font-size-xs);
|
|
68
|
+
--stat-card-gap: var(--space-stack-xs);
|
|
69
|
+
--stat-card-icon-size: 2.25rem;
|
|
70
|
+
--stat-card-icon-glyph-size: 1.25rem;
|
|
71
|
+
--stat-card-icon-radius: var(--radius-md);
|
|
72
|
+
/* Medallion tint — soft brand wash + brand glyph by default; a service retints by overriding
|
|
73
|
+
* --primary or these tokens directly (rule #44/#45). `initial` so the --primary default
|
|
74
|
+
* re-resolves at the call site under a scoped theme (no :root freeze).
|
|
75
|
+
* Defaults = hsl(var(--primary) / 0.1) fill · hsl(var(--primary)) glyph. */
|
|
76
|
+
--stat-card-icon-background: initial;
|
|
77
|
+
--stat-card-icon-foreground: initial;
|
|
78
|
+
--stat-card-delta-font-size: var(--font-size-xs);
|
|
79
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/* Control primitive tokens: heights, horizontal padding, adjacent control sizes. */
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
--control-height-compact: 1.75rem;
|
|
5
|
+
--control-height-default: 2rem;
|
|
6
|
+
--control-height-comfortable: 2.75rem;
|
|
7
|
+
--control-padding-x-compact: var(--space-2);
|
|
8
|
+
--control-padding-x-default: var(--space-3);
|
|
9
|
+
--control-padding-x-comfortable: var(--space-4);
|
|
10
|
+
|
|
11
|
+
--control-height: calc(var(--control-height-default) * var(--scaling));
|
|
12
|
+
/* Adjacent control sizes, derived from the active --control-height. The ±step
|
|
13
|
+
* is scaled too so the whole control ladder stays proportional under --scaling. */
|
|
14
|
+
--control-height-sm: calc(var(--control-height) - calc(0.25rem * var(--scaling)));
|
|
15
|
+
--control-height-lg: calc(var(--control-height) + calc(0.25rem * var(--scaling)));
|
|
16
|
+
--control-height-xs: calc(var(--control-height) - calc(0.5rem * var(--scaling)));
|
|
17
|
+
--control-padding-x: var(--control-padding-x-default);
|
|
18
|
+
--control-gap: var(--space-inline-sm);
|
|
19
|
+
--control-gap-sm: var(--space-inline-xs);
|
|
20
|
+
--control-radius: var(--radius);
|
|
21
|
+
--control-icon-size: calc(1rem * var(--scaling));
|
|
22
|
+
--control-icon-size-sm: calc(0.875rem * var(--scaling));
|
|
23
|
+
--control-focus-ring-width: var(--focus-ring-width);
|
|
24
|
+
|
|
25
|
+
--checkbox-size: calc(1rem * var(--scaling));
|
|
26
|
+
--checkbox-size-compact: 0.875rem;
|
|
27
|
+
--checkbox-size-comfortable: 1.125rem;
|
|
28
|
+
--choice-gap: var(--space-inline-sm);
|
|
29
|
+
--choice-group-gap-x: var(--space-6);
|
|
30
|
+
--choice-group-gap-y: var(--space-3);
|
|
31
|
+
--choice-description-gap: 0.125rem;
|
|
32
|
+
--choice-control-offset: 0.125rem;
|
|
33
|
+
|
|
34
|
+
--switch-width: calc(2.25rem * var(--scaling));
|
|
35
|
+
--switch-width-compact: 2rem;
|
|
36
|
+
--switch-width-comfortable: 2.5rem;
|
|
37
|
+
--switch-height: calc(1.25rem * var(--scaling));
|
|
38
|
+
--switch-height-compact: 1.125rem;
|
|
39
|
+
--switch-height-comfortable: 1.375rem;
|
|
40
|
+
--switch-thumb-size: calc(1rem * var(--scaling));
|
|
41
|
+
--switch-thumb-size-compact: 0.875rem;
|
|
42
|
+
--switch-thumb-size-comfortable: 1.125rem;
|
|
43
|
+
--switch-thumb-translate: calc(1rem * var(--scaling));
|
|
44
|
+
--switch-thumb-translate-compact: 0.875rem;
|
|
45
|
+
--switch-thumb-translate-comfortable: 1.125rem;
|
|
46
|
+
|
|
47
|
+
--slider-track-height: 0.375rem;
|
|
48
|
+
--slider-thumb-size: 1rem;
|
|
49
|
+
|
|
50
|
+
/* Checked/on/active fills — `initial` so the --primary default re-resolves at the call site
|
|
51
|
+
* under a scoped theme (a :root binding to var(--primary) freezes at the :root value and a scoped
|
|
52
|
+
* [data-tenant] override of --primary never reaches it). A service retints the "selected" state
|
|
53
|
+
* by overriding these directly. Defaults = hsl(var(--primary)) · slider track 0.2α. */
|
|
54
|
+
--checkbox-checked-background: initial;
|
|
55
|
+
--switch-checked-background: initial;
|
|
56
|
+
--toggle-on-background: initial;
|
|
57
|
+
--slider-track-background: initial;
|
|
58
|
+
--slider-range-background: initial;
|
|
59
|
+
|
|
60
|
+
--color-picker-input-width: 6.5rem;
|
|
61
|
+
|
|
62
|
+
--command-list-max-height: min(300px, 50vh);
|
|
63
|
+
--command-input-padding-x: var(--space-3);
|
|
64
|
+
--command-group-padding: var(--space-1);
|
|
65
|
+
--command-item-padding-y: var(--space-2);
|
|
66
|
+
--command-item-padding-x: var(--space-2);
|
|
67
|
+
--search-input-edge-inset: var(--space-3);
|
|
68
|
+
--search-input-start-padding: calc(
|
|
69
|
+
var(--search-input-edge-inset) + var(--control-icon-size) + var(--control-gap)
|
|
70
|
+
);
|
|
71
|
+
--search-input-end-padding: calc(
|
|
72
|
+
var(--search-input-edge-inset) + var(--control-icon-size) + var(--control-gap)
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
--choice-description-font-size: var(--font-size-xs);
|
|
76
|
+
--color-picker-hex-font-size: var(--font-size-xs);
|
|
77
|
+
--command-group-heading-font-size: var(--font-size-xs);
|
|
78
|
+
--search-input-label-font-size: var(--font-size-xs);
|
|
79
|
+
--tag-input-chip-font-size: var(--font-size-xs);
|
|
80
|
+
--toggle-sm-font-size: var(--font-size-xs);
|
|
81
|
+
--button-sm-font-size: var(--font-size-xs);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* Rule #24 — on touch devices (coarse pointer) interactive controls keep a ≥44px tap target
|
|
85
|
+
* regardless of density; desktop (fine pointer) keeps the compact heights above. --control-height
|
|
86
|
+
* resolves through these via var(), so inputs/buttons/selects/table rows all bump together. */
|
|
87
|
+
@media (pointer: coarse) {
|
|
88
|
+
:root {
|
|
89
|
+
--control-height-compact: 2.75rem;
|
|
90
|
+
--control-height-default: 2.75rem;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/* Data-display component tokens — small-by-design text knobs (rule #45/#46). */
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
--progress-label-font-size: var(--font-size-xs);
|
|
5
|
+
--tree-item-title-font-size: var(--font-size-xs);
|
|
6
|
+
--tree-item-description-font-size: var(--font-size-xs);
|
|
7
|
+
--timeline-note-font-size: var(--font-size-xs);
|
|
8
|
+
|
|
9
|
+
/* Avatar surface — `initial` so the --muted default re-resolves at the call site under a scoped
|
|
10
|
+
theme (a :root binding to a role var freezes at :root; a scoped role override never reaches it).
|
|
11
|
+
A service re-tints the placeholder fill once (e.g. --avatar-background: hsl(var(--accent))).
|
|
12
|
+
Default = hsl(var(--muted)). */
|
|
13
|
+
--avatar-background: initial;
|
|
14
|
+
/* Optional role wash over the avatar (default transparent = invisible, rule #44).
|
|
15
|
+
Painted as an overlay so a service sets --avatar-tint: hsl(var(--primary) / 0.08). */
|
|
16
|
+
--avatar-tint: transparent;
|
|
17
|
+
|
|
18
|
+
/* Progress track + fill — `initial` so the role defaults re-resolve under a scoped theme.
|
|
19
|
+
Track reads --secondary, fill reads --success; a service re-tones once.
|
|
20
|
+
Defaults = hsl(var(--secondary)) track · hsl(var(--success)) fill. */
|
|
21
|
+
--progress-track-background: initial;
|
|
22
|
+
--progress-fill-background: initial;
|
|
23
|
+
|
|
24
|
+
/* Timeline accents — `initial` so the dot/line role defaults re-resolve under a scoped theme.
|
|
25
|
+
Defaults = hsl(var(--success)) done · hsl(var(--primary)) current/line. */
|
|
26
|
+
--timeline-dot-done-background: initial;
|
|
27
|
+
--timeline-dot-current-background: initial;
|
|
28
|
+
--timeline-line-completed-background: initial;
|
|
29
|
+
|
|
30
|
+
/* Tree active item — border + soft bg tint over the --primary role. `initial` so the default
|
|
31
|
+
re-resolves under a scoped theme. Defaults = hsl(var(--primary) / 0.3) border · 0.05 fill. */
|
|
32
|
+
--tree-item-active-border: initial;
|
|
33
|
+
--tree-item-active-background: initial;
|
|
34
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/* Descriptions component tokens. */
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
/* Width of the label column when <Descriptions layout="horizontal">. Labels align to this
|
|
5
|
+
* shared column so the values line up (the horizontal-detail look, mirroring <Form layout>).
|
|
6
|
+
* A rem value gives a fixed aligned column; set `max-content` to size each label to its text.
|
|
7
|
+
* (rule #44/#45 — a service theme tunes it here instead of forking CSS.) */
|
|
8
|
+
--descriptions-label-width: 8rem;
|
|
9
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/* Feedback primitive tokens: dialog, alert, empty state. */
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
/* Dialog inset defaults to the shared global chrome tokens (override --space-chrome-* once for the
|
|
5
|
+
* whole system, or --dialog-space-x/-y for dialogs only). */
|
|
6
|
+
--dialog-space-x: var(--space-chrome-x);
|
|
7
|
+
--dialog-space-y: var(--space-chrome-y);
|
|
8
|
+
--dialog-space-inset: var(--dialog-space-y) var(--dialog-space-x);
|
|
9
|
+
--dialog-space-gap: var(--space-stack-md);
|
|
10
|
+
--dialog-close-space-offset: var(--space-4);
|
|
11
|
+
--alert-space-inset: var(--space-section-active);
|
|
12
|
+
--alert-space-gap: var(--space-inline-md);
|
|
13
|
+
--alert-inner-space-gap: var(--space-stack-sm);
|
|
14
|
+
--alert-dismiss-space-offset: var(--space-3);
|
|
15
|
+
/* Soft (subtle) semantic tint ratios — themeable so a service can hit its exact spec
|
|
16
|
+
* (a brand's success-bg/-border are often more present than the faint 5%/30% default). */
|
|
17
|
+
--alert-bg-alpha: 0.05;
|
|
18
|
+
--alert-border-alpha: 0.3;
|
|
19
|
+
/* Brand glow layer for the raised dialog/sheet panel — invisible no-op at rest (rule #44).
|
|
20
|
+
* Paired AFTER --shadow-lg in the surface box-shadow so a service can wash the overlay with the
|
|
21
|
+
* global glow, e.g. --dialog-content-glow: var(--shadow-glow), with no markup change. */
|
|
22
|
+
--dialog-content-glow: 0 0 0 0 transparent;
|
|
23
|
+
--empty-state-space-y: var(--space-10);
|
|
24
|
+
--empty-state-space-x: var(--space-6);
|
|
25
|
+
/* EmptyState icon medallion colour — `initial` so the role defaults re-resolve at the call site
|
|
26
|
+
* under a scoped theme (rule #44). A service recolours the glyph (--empty-state-icon-foreground)
|
|
27
|
+
* or washes the medallion fill (--empty-state-icon-tint) without forking.
|
|
28
|
+
* Defaults = hsl(var(--muted-foreground)) glyph · hsl(var(--muted)) fill. */
|
|
29
|
+
--empty-state-icon-foreground: initial;
|
|
30
|
+
--empty-state-icon-tint: initial;
|
|
31
|
+
--skeleton-row-gap: var(--space-stack-sm);
|
|
32
|
+
--skeleton-cell-gap: var(--space-inline-lg);
|
|
33
|
+
--skeleton-card-inset: var(--space-section-active);
|
|
34
|
+
--skeleton-radius: var(--radius);
|
|
35
|
+
/* Skeleton placeholder fill — `initial` so the --muted default re-resolves at the call site under
|
|
36
|
+
* a scoped theme (a :root binding to a role var freezes at :root). A service tints the shimmer to
|
|
37
|
+
* its surface (rule #44) without forking the keyframes. Default = hsl(var(--muted)). */
|
|
38
|
+
--skeleton-background: initial;
|
|
39
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/* Form layout tokens: horizontal-layout label column geometry. */
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
/* Width of the label column in horizontal/inline layout. A service theme sets
|
|
5
|
+
* this once (e.g. 110px) to align every form to its design grid; the Form/
|
|
6
|
+
* FormField `labelWidth` prop overrides per form/field. */
|
|
7
|
+
--form-label-width: max-content;
|
|
8
|
+
|
|
9
|
+
/* Column gap between the label and its control in horizontal/inline layout. */
|
|
10
|
+
--form-label-gap: var(--space-4); /* 16px */
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/* ListRow component tokens — a single-line entity row for short lists inside a Card
|
|
2
|
+
* (sessions / API tokens / linked accounts / passkeys …). Sits in a flush CardContent;
|
|
3
|
+
* rows separate with a quiet divider (#44 — chrome defaults to the calm semantic border). */
|
|
4
|
+
:root {
|
|
5
|
+
--list-row-padding-y: var(--space-3);
|
|
6
|
+
--list-row-padding-x: var(--space-4);
|
|
7
|
+
--list-row-gap: var(--space-3);
|
|
8
|
+
/* Row divider — `initial` so the --border default re-resolves at the call site under a scoped
|
|
9
|
+
* theme (a :root binding to a role var freezes at :root). Default = 1px solid hsl(var(--border)). */
|
|
10
|
+
--list-row-border: initial;
|
|
11
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/* Navigation primitive tokens: pagination, filters, compact pickers. */
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
--pagination-gap: var(--space-inline-sm);
|
|
5
|
+
--pagination-item-gap: var(--space-inline-xs);
|
|
6
|
+
--pagination-size-width: 5.5rem;
|
|
7
|
+
--pagination-total-font-size: var(--font-size-sm);
|
|
8
|
+
--filter-bar-gap: var(--space-3);
|
|
9
|
+
--filter-bar-padding-y: var(--space-2);
|
|
10
|
+
--filter-label-font-size: var(--font-size-xs);
|
|
11
|
+
--filter-picker-width-sm: 11rem;
|
|
12
|
+
--filter-picker-width-md: 14rem;
|
|
13
|
+
--breadcrumb-font-size: var(--font-size-xs);
|
|
14
|
+
--menubar-shortcut-font-size: var(--font-size-xs);
|
|
15
|
+
|
|
16
|
+
/* Menu item hover/highlight tint — `initial` so the --accent default re-resolves at the call site
|
|
17
|
+
* under a scoped theme (a :root binding to a role var freezes at :root).
|
|
18
|
+
* Defaults = hsl(var(--accent)) fill · hsl(var(--accent-foreground)) text. */
|
|
19
|
+
--menubar-item-hover-background: initial;
|
|
20
|
+
--menubar-item-hover-foreground: initial;
|
|
21
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/* Shell (sidebar / topbar / kbd) component tokens — small-by-design text
|
|
2
|
+
* knobs (rule #45/#46). A service re-tunes chrome text without moving the
|
|
3
|
+
* global scale. */
|
|
4
|
+
|
|
5
|
+
:root {
|
|
6
|
+
--sidebar-section-label-font-size: var(--font-size-2xs);
|
|
7
|
+
--sidebar-product-tenant-font-size: var(--font-size-2xs);
|
|
8
|
+
--sidebar-badge-font-size: var(--font-size-2xs);
|
|
9
|
+
--sidebar-user-role-font-size: var(--font-size-2xs);
|
|
10
|
+
--sidebar-nav-sub-font-size: var(--font-size-xs);
|
|
11
|
+
--sidebar-flyout-title-font-size: var(--font-size-xs);
|
|
12
|
+
--topbar-chip-icon-font-size: var(--font-size-2xs);
|
|
13
|
+
--kbd-font-size: var(--font-size-2xs);
|
|
14
|
+
--sidebar-logo-mark-font-size: var(--font-size-xs);
|
|
15
|
+
--sidebar-avatar-font-size: var(--font-size-2xs);
|
|
16
|
+
--sidebar-user-name-font-size: var(--font-size-xs);
|
|
17
|
+
|
|
18
|
+
/* Brand-chrome gradient hooks — opt-in, invisible by default. A service paints
|
|
19
|
+
* the sidebar/topbar surface by setting these to a gradient (no-op = none). */
|
|
20
|
+
--sidebar-gradient: none;
|
|
21
|
+
--topbar-gradient: none;
|
|
22
|
+
|
|
23
|
+
/* Sidebar active-item tint/marker — `initial` so the role defaults re-resolve at the call site
|
|
24
|
+
* under a scoped theme (a :root binding to a role var freezes at :root; a scoped role override
|
|
25
|
+
* never reaches it). A service re-tunes the active sub-item accent without forking CSS.
|
|
26
|
+
* Defaults = hsl(var(--primary)) marker/tint. */
|
|
27
|
+
--sidebar-item-active-color: initial;
|
|
28
|
+
--sidebar-item-active-tint: initial;
|
|
29
|
+
/* Main nav-item active row — defaults mirror the hover state (accent bg, foreground text); a
|
|
30
|
+
* service overrides these to brand the selected row (e.g. a gold tint + gold text on a navy
|
|
31
|
+
* sidebar). `initial` so the defaults re-resolve under a scoped theme.
|
|
32
|
+
* Defaults = hsl(var(--accent)) fill · hsl(var(--foreground)) text. */
|
|
33
|
+
--sidebar-item-active-background: initial;
|
|
34
|
+
--sidebar-item-active-foreground: initial;
|
|
35
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/* Table component tokens: row height, cell padding. */
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
--table-row-height-compact: 1.75rem;
|
|
5
|
+
--table-row-height-default: 2rem;
|
|
6
|
+
--table-row-height-comfortable: 2.75rem;
|
|
7
|
+
--table-row-height: calc(var(--table-row-height-default) * var(--scaling));
|
|
8
|
+
--table-cell-padding-y: var(--space-2);
|
|
9
|
+
--table-cell-space-x: var(--control-padding-x);
|
|
10
|
+
--table-head-font-size: var(--font-size-xs);
|
|
11
|
+
/* Header band — its OWN bg + fg knobs (decoupled from --secondary). Declared `initial` so the
|
|
12
|
+
* default re-resolves to the LIVE --muted / --muted-foreground roles at the call site: a :root
|
|
13
|
+
* binding to a role var freezes at the :root value and a scoped [data-tenant] role override never
|
|
14
|
+
* reaches it. A brand sets both header tokens together to keep band/text contrast.
|
|
15
|
+
* Defaults = hsl(var(--muted)) band · hsl(var(--muted-foreground)) text. */
|
|
16
|
+
--table-header-background: initial;
|
|
17
|
+
--table-header-foreground: initial;
|
|
18
|
+
/* Inline-end shadow that lifts a pinned (sticky) action column off the body it scrolls over. */
|
|
19
|
+
--table-pin-shadow: -6px 0 6px -5px hsl(var(--foreground) / 0.12);
|
|
20
|
+
/* Row-state tint washes — translucent muted over the opaque base. `initial` so the --muted
|
|
21
|
+
* default re-resolves under a scoped theme; a service retints by reading another role (e.g.
|
|
22
|
+
* --primary). Defaults = hsl(var(--muted) / 0.4 striped · 0.5 hover · 0.3 selected). */
|
|
23
|
+
--table-row-striped-background: initial;
|
|
24
|
+
--table-row-hover-background: initial;
|
|
25
|
+
--table-row-selected-background: initial;
|
|
26
|
+
}
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
/* Status TEXT colours — darker than the fill roles so small coloured text (a StatCard delta, an
|
|
41
41
|
* outline status badge label) clears WCAG AA 4.5:1 on white. The fill roles (--success/--warning/
|
|
42
42
|
* --info) stay the brighter wa-iro for badges/bars; only TEXT reads these via text-*-strong. */
|
|
43
|
+
--text-primary: 204 100% 30%; /* deep brand blue — AA brand text on the soft primary tint */
|
|
43
44
|
--text-success: 152 84% 22%; /* deep 若竹 — AA green text on white */
|
|
44
45
|
--text-warning: 36 100% 28%; /* dark 山吹 amber — yellow can never pass as text */
|
|
45
46
|
--text-info: 221 70% 38%; /* deep 群青 — AA blue text on white */
|
|
@@ -107,15 +108,30 @@
|
|
|
107
108
|
* — the CTA then carries a brand glow with no component change. */
|
|
108
109
|
--shadow-glow: 0 0 0 0 transparent;
|
|
109
110
|
|
|
111
|
+
/* Brand SPOTLIGHT — a decorative radial brand halo for hero / auth backdrops (the `.ui-brand-glow`
|
|
112
|
+
* utility paints this as a background-image on an aria-hidden layer). Every knob is a token, so a
|
|
113
|
+
* service retints (`--brand-glow-color`), softens (`--brand-glow-alpha`), resizes
|
|
114
|
+
* (`--brand-glow-size`) or repositions (`--brand-glow-position`) the halo with no markup change;
|
|
115
|
+
* override the whole `--brand-glow` for a bespoke gradient. Default: a soft primary glow up top. */
|
|
116
|
+
--brand-glow-color: var(--primary);
|
|
117
|
+
--brand-glow-alpha: 0.18;
|
|
118
|
+
--brand-glow-size: 60% 50%;
|
|
119
|
+
--brand-glow-position: 50% 0%;
|
|
120
|
+
--brand-glow: radial-gradient(
|
|
121
|
+
ellipse var(--brand-glow-size) at var(--brand-glow-position),
|
|
122
|
+
hsl(var(--brand-glow-color) / var(--brand-glow-alpha)),
|
|
123
|
+
transparent 70%
|
|
124
|
+
);
|
|
125
|
+
|
|
110
126
|
/* FOCUS RING — the two global knobs for the keyboard-focus affordance (WCAG 2.4.7 / 2.4.11).
|
|
111
127
|
* `--focus-ring-color` is the themeable hue (HSL components, defaults to --ring) and
|
|
112
128
|
* `--focus-ring-width` the standard thickness; override either ONCE — even scoped under a
|
|
113
|
-
* `[data-tenant]` — to retint or resize EVERY focus ring.
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
* hsl(var(--focus-ring-color))`,
|
|
117
|
-
*
|
|
118
|
-
--focus-ring-color:
|
|
129
|
+
* `[data-tenant]` — to retint or resize EVERY focus ring. `--focus-ring-color` is declared
|
|
130
|
+
* `initial` (NOT `var(--ring)`): a :root binding to a role var would freeze at the :root --ring
|
|
131
|
+
* and a scoped [data-tenant] override of --ring alone would never reach it. Every :focus-visible
|
|
132
|
+
* rule reads it with the live-role fallback `hsl(var(--focus-ring-color, var(--ring)))`, so the
|
|
133
|
+
* default re-resolves at the element that paints the ring while an explicit override still wins. */
|
|
134
|
+
--focus-ring-color: initial; /* default = the live --ring role */
|
|
119
135
|
--focus-ring-width: 2px;
|
|
120
136
|
|
|
121
137
|
/* GRADIENTS — opt-in decorative fills exposed at the token layer so a service can paint a hero
|
|
@@ -345,6 +361,7 @@
|
|
|
345
361
|
--text-link: 204 90% 66%;
|
|
346
362
|
--text-brand: 204 90% 66%;
|
|
347
363
|
/* Status text on the dark spine reads LIGHT (AA on dark surfaces). */
|
|
364
|
+
--text-primary: 204 90% 64%; /* lifted brand blue — AA on the dark primary tint */
|
|
348
365
|
--text-success: 146 55% 64%;
|
|
349
366
|
--text-warning: 44 95% 62%;
|
|
350
367
|
--text-info: 221 70% 72%;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/* Layout primitive tokens: page, section, stack, inline. */
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
/* Semantic spacing — ONE coherent 4px grid (Tailwind/Material/Ant standard). All steps reference
|
|
5
|
+
* the linear `--space-*` scale (NOT the golden φ scale — golden ratio is for modular SIZE scales
|
|
6
|
+
* like type/radius, not the spacing grid; mixing the two left an incoherent density rhythm). The
|
|
7
|
+
* φ tokens remain available as an optional golden-layout utility, just not the spacing baseline. */
|
|
8
|
+
--space-page-x: var(--space-6); /* 24px — page gutter */
|
|
9
|
+
--space-page-y: var(--space-6); /* 24px */
|
|
10
|
+
--space-section: var(--space-4); /* 16px */
|
|
11
|
+
--space-stack-xs: var(--space-1); /* 4px */
|
|
12
|
+
--space-stack-sm: var(--space-2); /* 8px */
|
|
13
|
+
--space-stack-md: var(--space-4); /* 16px */
|
|
14
|
+
--space-stack-lg: var(--space-6); /* 24px */
|
|
15
|
+
--space-stack-xl: var(--space-10); /* 40px */
|
|
16
|
+
--space-inline-xs: var(--space-1); /* 4px */
|
|
17
|
+
--space-inline-sm: var(--space-2); /* 8px */
|
|
18
|
+
--space-inline-md: var(--space-3); /* 12px */
|
|
19
|
+
--space-inline-lg: var(--space-4); /* 16px */
|
|
20
|
+
|
|
21
|
+
--space-page-active-x: var(--space-page-x);
|
|
22
|
+
--space-page-active-y: var(--space-page-y);
|
|
23
|
+
--space-section-active: var(--space-section);
|
|
24
|
+
|
|
25
|
+
/* Modal scrim — the single backdrop colour shared by EVERY overlay (Dialog, AlertDialog, Sheet,
|
|
26
|
+
* Drawer). Themeable (rule #44) so a service tints the wash to its brand once, e.g. a navy
|
|
27
|
+
* `--overlay-background: rgb(12 26 49 / .55)`, instead of forking each overlay. */
|
|
28
|
+
--overlay-background: rgb(0 0 0 / 0.5);
|
|
29
|
+
|
|
30
|
+
/* Full-bleed rule under the PageContainer header. OFF by default — the title/body
|
|
31
|
+
* gap already separates them; a divider is a service-theme opt-in:
|
|
32
|
+
* `--page-header-divider: 1px solid hsl(var(--border));` */
|
|
33
|
+
--page-header-divider: none;
|
|
34
|
+
|
|
35
|
+
/* Bottom inset of the page header, sized so title→body distance (this pad + the
|
|
36
|
+
* --space-section-active container gap) EQUALS the page's top padding — the title
|
|
37
|
+
* band reads vertically balanced (24 above / 24 below by default), instead of the
|
|
38
|
+
* old fixed 16px pad that left 24 above vs 32 below. */
|
|
39
|
+
--page-header-pad-bottom: calc(var(--space-page-active-y) - var(--space-section-active));
|
|
40
|
+
|
|
41
|
+
/* Section "chrome" padding — the SHARED inset for header / body / footer of every overlay-ish
|
|
42
|
+
* surface (Dialog · Sheet · Drawer · Card). One global knob: a service sets these once instead of
|
|
43
|
+
* per-component. Vertical is tightened (16 vs the old 24) and density-aware (see density.css);
|
|
44
|
+
* horizontal stays 24 (Ant standard). Per-component tokens (--sheet-pad-*, --dialog-space-inset…)
|
|
45
|
+
* default to these and only override when a surface genuinely needs to differ. */
|
|
46
|
+
--space-chrome-x: var(--space-6); /* 24px — horizontal inset */
|
|
47
|
+
--space-chrome-y: var(--space-4); /* 16px — vertical inset (header/footer/body) */
|
|
48
|
+
--space-chrome-gap: var(--space-stack-sm); /* gap between header ↔ body ↔ footer */
|
|
49
|
+
|
|
50
|
+
/* The ONE vertical gap between a field's LABEL and its CONTENT — same everywhere a label sits above
|
|
51
|
+
* a control or a value (FormField, Descriptions, Form, any label-above stack). Single source so the
|
|
52
|
+
* whole system stays in sync (gh feedback: Descriptions was ~0px while FormField was 8px). */
|
|
53
|
+
--field-label-gap: var(--space-2); /* 8px */
|
|
54
|
+
}
|