@godxjp/ui 18.7.0 → 18.9.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/card.d.ts +27 -3
- package/dist/components/data-display/card.js +4 -1
- package/dist/components/data-display/data-table.js +2 -3
- package/dist/components/data-entry/command-palette.d.ts +42 -1
- package/dist/components/data-entry/command-palette.js +39 -3
- package/dist/components/data-entry/input-otp.d.ts +1 -1
- package/dist/components/navigation/steps.d.ts +2 -2
- package/dist/components/navigation/steps.js +4 -2
- package/dist/components/ui/avatar.d.ts +1 -1
- package/dist/components/ui/avatar.js +2 -1
- package/dist/components/ui/input-otp.d.ts +15 -3
- package/dist/components/ui/input-otp.js +2 -1
- package/dist/email/tokens.generated.d.ts +2 -2
- package/dist/email/tokens.generated.js +2 -2
- package/dist/props/components/data-display.prop.d.ts +16 -0
- package/dist/props/components/data-entry.prop.d.ts +7 -0
- package/dist/props/components/navigation.prop.d.ts +9 -0
- package/dist/props/registry.d.ts +16 -1
- package/dist/props/registry.js +16 -1
- package/dist/styles/base.css +28 -0
- package/dist/styles/card-layout.css +49 -13
- package/dist/styles/control.css +12 -2
- package/dist/styles/data-display-layout.css +16 -0
- package/dist/styles/navigation-layout.css +8 -3
- package/dist/styles/shell-layout.css +10 -1
- package/dist/styles/table-layout.css +27 -0
- package/dist/tokens/components/card.css +12 -0
- package/dist/tokens/components/control.css +16 -0
- package/dist/tokens/components/data-display.css +12 -0
- package/dist/tokens/components/email.css +8 -2
- package/dist/tokens/components/navigation.css +9 -0
- package/dist/tokens/components/shell.css +27 -4
- package/dist/tokens/components/table.css +21 -0
- package/package.json +2 -2
|
@@ -3,9 +3,21 @@ import { type VariantProps } from "class-variance-authority";
|
|
|
3
3
|
import type { LucideIcon } from "lucide-react";
|
|
4
4
|
import type { HeadingLevelProp } from "../../props/vocabulary/index.js";
|
|
5
5
|
type CardSize = "md" | "compact";
|
|
6
|
-
/** Semantic
|
|
7
|
-
*
|
|
6
|
+
/** Semantic accent tone. `accentPlacement` decides WHERE it is drawn — a leading-edge
|
|
7
|
+
* stripe (default) or the full perimeter. */
|
|
8
8
|
type CardAccent = "primary" | "success" | "warning" | "info" | "attention" | "destructive";
|
|
9
|
+
/**
|
|
10
|
+
* Where the semantic `accent` tone is drawn.
|
|
11
|
+
*
|
|
12
|
+
* - `"edge"` (default) — the leading-edge stripe on `border-inline-start` only, at the
|
|
13
|
+
* `--card-accent-rail-width` measure (6px). The classic "this row needs a look" rail.
|
|
14
|
+
* - `"perimeter"` — a full attention border around the WHOLE card in the same semantic
|
|
15
|
+
* tone, measured by `--card-accent-perimeter-width` + `--card-accent-perimeter-ring-width`.
|
|
16
|
+
* This is what `variant="featured"` does, except the tone is yours instead of `--primary`,
|
|
17
|
+
* so a card can shout "action required" (`accent="attention"`) or "this failed"
|
|
18
|
+
* (`accent="destructive"`) without borrowing the brand colour.
|
|
19
|
+
*/
|
|
20
|
+
type CardAccentPlacement = "edge" | "perimeter";
|
|
9
21
|
/** Surface fill — plain card, muted band, borderless outline, or emphasized featured ring. */
|
|
10
22
|
type CardVariant = "default" | "muted" | "outline" | "featured";
|
|
11
23
|
/** Padding density — base 16px · tight 12px · cozy 20px. */
|
|
@@ -16,6 +28,11 @@ declare const cardVariants: (props?: ({
|
|
|
16
28
|
export type CardProps = React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof cardVariants> & {
|
|
17
29
|
size?: CardSize;
|
|
18
30
|
accent?: CardAccent;
|
|
31
|
+
/**
|
|
32
|
+
* Where `accent` is drawn — `"edge"` (default, the leading-edge stripe) or `"perimeter"`
|
|
33
|
+
* (a full attention border in the accent tone). Inert without `accent`.
|
|
34
|
+
*/
|
|
35
|
+
accentPlacement?: CardAccentPlacement;
|
|
19
36
|
variant?: CardVariant;
|
|
20
37
|
density?: CardDensity;
|
|
21
38
|
};
|
|
@@ -24,6 +41,11 @@ export declare const Card: React.ForwardRefExoticComponent<React.HTMLAttributes<
|
|
|
24
41
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string> & {
|
|
25
42
|
size?: CardSize;
|
|
26
43
|
accent?: CardAccent;
|
|
44
|
+
/**
|
|
45
|
+
* Where `accent` is drawn — `"edge"` (default, the leading-edge stripe) or `"perimeter"`
|
|
46
|
+
* (a full attention border in the accent tone). Inert without `accent`.
|
|
47
|
+
*/
|
|
48
|
+
accentPlacement?: CardAccentPlacement;
|
|
27
49
|
variant?: CardVariant;
|
|
28
50
|
density?: CardDensity;
|
|
29
51
|
} & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -136,9 +158,11 @@ export type StatCardProps = React.HTMLAttributes<HTMLDivElement> & VariantProps<
|
|
|
136
158
|
inverse?: boolean;
|
|
137
159
|
/** Semantic leading-edge rail (Card accent) — flags a KPI needing attention. */
|
|
138
160
|
accent?: CardAccent;
|
|
161
|
+
/** Where `accent` is drawn — `"edge"` (default rail) or `"perimeter"` (full attention border). */
|
|
162
|
+
accentPlacement?: CardAccentPlacement;
|
|
139
163
|
};
|
|
140
164
|
/** KPI / stat tile — token-driven layout aligned to dashboard KPI cards. */
|
|
141
|
-
export declare function StatCard({ label, value, hint, icon: Icon, delta, layout, align, inverse, accent, className, size, ...props }: StatCardProps): React.JSX.Element;
|
|
165
|
+
export declare function StatCard({ label, value, hint, icon: Icon, delta, layout, align, inverse, accent, accentPlacement, className, size, ...props }: StatCardProps): React.JSX.Element;
|
|
142
166
|
/** Header actions slot — pair with `CardHeader className="flex flex-row …"`. */
|
|
143
167
|
export declare const CardAction: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
|
|
144
168
|
export {};
|
|
@@ -12,7 +12,7 @@ const cardVariants = cva("group/card", {
|
|
|
12
12
|
defaultVariants: { size: "md" }
|
|
13
13
|
});
|
|
14
14
|
const Card = React.forwardRef(
|
|
15
|
-
({ className, size = "md", accent, variant, density, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
15
|
+
({ className, size = "md", accent, accentPlacement, variant, density, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
16
16
|
"div",
|
|
17
17
|
{
|
|
18
18
|
ref,
|
|
@@ -20,6 +20,7 @@ const Card = React.forwardRef(
|
|
|
20
20
|
"data-slot": "card",
|
|
21
21
|
"data-size": size === "compact" ? "compact" : void 0,
|
|
22
22
|
"data-accent": accent,
|
|
23
|
+
"data-accent-placement": accentPlacement === "perimeter" ? "perimeter" : void 0,
|
|
23
24
|
"data-variant": variant && variant !== "default" ? variant : void 0,
|
|
24
25
|
"data-density": density,
|
|
25
26
|
...props
|
|
@@ -108,6 +109,7 @@ function StatCard({
|
|
|
108
109
|
align = "start",
|
|
109
110
|
inverse = false,
|
|
110
111
|
accent,
|
|
112
|
+
accentPlacement,
|
|
111
113
|
className,
|
|
112
114
|
size = "compact",
|
|
113
115
|
...props
|
|
@@ -118,6 +120,7 @@ function StatCard({
|
|
|
118
120
|
{
|
|
119
121
|
size: size ?? "compact",
|
|
120
122
|
accent,
|
|
123
|
+
accentPlacement,
|
|
121
124
|
className: cn("ui-stat-card", className),
|
|
122
125
|
"data-stat-card": "",
|
|
123
126
|
"data-stat-layout": layout,
|
|
@@ -636,9 +636,8 @@ DataTable.Content = function DataTableContent() {
|
|
|
636
636
|
),
|
|
637
637
|
children: col.render ? col.render(original) : (() => {
|
|
638
638
|
const v = original[col.key];
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
return "\u2014";
|
|
639
|
+
const text = v == null || !(typeof v === "string" || typeof v === "number") ? "\u2014" : String(v);
|
|
640
|
+
return /* @__PURE__ */ jsx("span", { "data-slot": "table-cell-text", children: text });
|
|
642
641
|
})()
|
|
643
642
|
},
|
|
644
643
|
col.key
|
|
@@ -29,9 +29,50 @@ export type CommandPaletteProps = {
|
|
|
29
29
|
open?: boolean;
|
|
30
30
|
defaultOpen?: boolean;
|
|
31
31
|
onOpenChange?: (open: boolean) => void;
|
|
32
|
+
/**
|
|
33
|
+
* Controlled search-box query. Pairs with `onSearchChange`; leave it out for the uncontrolled
|
|
34
|
+
* palette (seeded by `defaultSearch`). Same idiom as `SearchSelect`'s `search`.
|
|
35
|
+
*/
|
|
36
|
+
search?: string;
|
|
37
|
+
/** Initial uncontrolled query, and the value the palette resets to when it closes. Default `""`. */
|
|
38
|
+
defaultSearch?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Fires on every keystroke with the current query — the seam a server-backed group needs to run
|
|
41
|
+
* search-as-you-type. Also fires with `defaultSearch` when the palette closes.
|
|
42
|
+
*/
|
|
43
|
+
onSearchChange?: (query: string) => void;
|
|
44
|
+
/**
|
|
45
|
+
* Whether the palette filters `groups` itself (cmdk's client-side fuzzy match). Set `false` when
|
|
46
|
+
* the QUERY is answered by a server and `groups` already holds the matches — otherwise every row
|
|
47
|
+
* is scored a second time against the same string and late-arriving matches are dropped.
|
|
48
|
+
*
|
|
49
|
+
* It also decides who owns the empty node; see the component doc block. Default `true`.
|
|
50
|
+
*/
|
|
51
|
+
shouldFilter?: boolean;
|
|
32
52
|
loading?: boolean;
|
|
33
53
|
error?: React.ReactNode;
|
|
34
54
|
trigger?: React.ReactNode;
|
|
35
55
|
shortcut?: boolean;
|
|
36
56
|
};
|
|
37
|
-
|
|
57
|
+
/**
|
|
58
|
+
* CommandPalette — the ⌘K search dialog.
|
|
59
|
+
*
|
|
60
|
+
* ## The empty-state contract (gh#412)
|
|
61
|
+
*
|
|
62
|
+
* `labels.empty` renders under exactly one rule, and which mechanism decides follows `shouldFilter`:
|
|
63
|
+
*
|
|
64
|
+
* - **`shouldFilter` (default, client-side)** — cmdk owns it: the palette holds items, the query
|
|
65
|
+
* matches none of them. Unchanged.
|
|
66
|
+
* - **`shouldFilter={false}` (server-side)** — the PALETTE owns it, derived synchronously from
|
|
67
|
+
* props: the empty node renders when `groups` carries no items, and never while `loading` or
|
|
68
|
+
* `error` is set. cmdk's own empty node cannot be trusted here, because it is driven by a
|
|
69
|
+
* scheduled count of the items that have REGISTERED in the DOM, not by what the consumer knows —
|
|
70
|
+
* so an async group that populates a frame late flips it on and off and any assertion over it
|
|
71
|
+
* races. Reading `groups` instead makes "did we render the empty state?" a pure function of the
|
|
72
|
+
* props at that render, which is what a contract test can assert.
|
|
73
|
+
*
|
|
74
|
+
* The practical consequence for search-as-you-type: hold `loading` true for the whole in-flight
|
|
75
|
+
* window. While it is true the palette shows `labels.loading` and never claims "no results" —
|
|
76
|
+
* a request that has not answered yet is not an empty result.
|
|
77
|
+
*/
|
|
78
|
+
export declare function CommandPalette({ groups, labels, onSelect, open: controlledOpen, defaultOpen, onOpenChange, search: controlledSearch, defaultSearch, onSearchChange, shouldFilter, loading, error, trigger, shortcut, }: CommandPaletteProps): React.JSX.Element;
|
|
@@ -19,14 +19,20 @@ function CommandPalette({
|
|
|
19
19
|
open: controlledOpen,
|
|
20
20
|
defaultOpen = false,
|
|
21
21
|
onOpenChange,
|
|
22
|
+
search: controlledSearch,
|
|
23
|
+
defaultSearch = "",
|
|
24
|
+
onSearchChange,
|
|
25
|
+
shouldFilter = true,
|
|
22
26
|
loading = false,
|
|
23
27
|
error,
|
|
24
28
|
trigger,
|
|
25
29
|
shortcut = true
|
|
26
30
|
}) {
|
|
27
31
|
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen);
|
|
32
|
+
const [uncontrolledSearch, setUncontrolledSearch] = React.useState(defaultSearch);
|
|
28
33
|
const shortcutRestoreFocusRef = React.useRef(null);
|
|
29
34
|
const open = controlledOpen ?? uncontrolledOpen;
|
|
35
|
+
const search = controlledSearch ?? uncontrolledSearch;
|
|
30
36
|
const setOpen = React.useCallback(
|
|
31
37
|
(next) => {
|
|
32
38
|
if (controlledOpen === void 0) {
|
|
@@ -36,6 +42,22 @@ function CommandPalette({
|
|
|
36
42
|
},
|
|
37
43
|
[controlledOpen, onOpenChange]
|
|
38
44
|
);
|
|
45
|
+
const setSearch = React.useCallback(
|
|
46
|
+
(next) => {
|
|
47
|
+
if (controlledSearch === void 0) {
|
|
48
|
+
setUncontrolledSearch(next);
|
|
49
|
+
}
|
|
50
|
+
onSearchChange?.(next);
|
|
51
|
+
},
|
|
52
|
+
[controlledSearch, onSearchChange]
|
|
53
|
+
);
|
|
54
|
+
const previousOpenRef = React.useRef(open);
|
|
55
|
+
React.useEffect(() => {
|
|
56
|
+
const wasOpen = previousOpenRef.current;
|
|
57
|
+
previousOpenRef.current = open;
|
|
58
|
+
if (!wasOpen || open || search === defaultSearch) return;
|
|
59
|
+
setSearch(defaultSearch);
|
|
60
|
+
}, [open, search, defaultSearch, setSearch]);
|
|
39
61
|
React.useEffect(() => {
|
|
40
62
|
if (!shortcut) return;
|
|
41
63
|
const handleKeyDown = (event) => {
|
|
@@ -50,6 +72,7 @@ function CommandPalette({
|
|
|
50
72
|
document.addEventListener("keydown", handleKeyDown);
|
|
51
73
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
52
74
|
}, [open, setOpen, shortcut]);
|
|
75
|
+
const hasItems = React.useMemo(() => groups.some((group) => group.items.length > 0), [groups]);
|
|
53
76
|
const triggerNode = trigger ?? /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", className: "ui-command-palette-trigger", children: [
|
|
54
77
|
/* @__PURE__ */ jsx(Search, { "aria-hidden": "true" }),
|
|
55
78
|
/* @__PURE__ */ jsx("span", { children: labels.open }),
|
|
@@ -79,10 +102,23 @@ function CommandPalette({
|
|
|
79
102
|
children: [
|
|
80
103
|
/* @__PURE__ */ jsx(Dialog.Title, { className: "sr-only", children: labels.title }),
|
|
81
104
|
/* @__PURE__ */ jsx(Dialog.Description, { id: "ui-command-palette-description", className: "sr-only", children: labels.description }),
|
|
82
|
-
/* @__PURE__ */ jsxs(Command, { label: labels.title, children: [
|
|
83
|
-
/* @__PURE__ */ jsx(
|
|
105
|
+
/* @__PURE__ */ jsxs(Command, { label: labels.title, shouldFilter, children: [
|
|
106
|
+
/* @__PURE__ */ jsx(
|
|
107
|
+
CommandInput,
|
|
108
|
+
{
|
|
109
|
+
autoFocus: true,
|
|
110
|
+
value: search,
|
|
111
|
+
onValueChange: setSearch,
|
|
112
|
+
placeholder: labels.placeholder,
|
|
113
|
+
"aria-label": labels.placeholder
|
|
114
|
+
}
|
|
115
|
+
),
|
|
84
116
|
/* @__PURE__ */ jsx(CommandList, { "aria-busy": loading, children: loading ? /* @__PURE__ */ jsx("div", { className: "ui-command-palette-state", role: "status", children: labels.loading }) : error ? /* @__PURE__ */ jsx("div", { className: "ui-command-palette-state", role: "alert", children: error }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
85
|
-
/* @__PURE__ */ jsx(CommandEmpty, { children: labels.empty })
|
|
117
|
+
shouldFilter ? /* @__PURE__ */ jsx(CommandEmpty, { children: labels.empty }) : hasItems ? null : (
|
|
118
|
+
// Same node cmdk would have rendered (class + role), decided from `groups`
|
|
119
|
+
// instead of from its scheduled DOM-registration count — see the doc block.
|
|
120
|
+
/* @__PURE__ */ jsx("div", { className: "ui-command-empty", role: "presentation", children: labels.empty })
|
|
121
|
+
),
|
|
86
122
|
groups.map((group) => /* @__PURE__ */ jsx(CommandGroup, { heading: group.label, children: group.items.map((item) => /* @__PURE__ */ jsxs(
|
|
87
123
|
CommandItem,
|
|
88
124
|
{
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } from "../ui/input-otp.js";
|
|
2
|
-
export type { InputOTPGroupAppearanceProp, InputOTPGroupProp, InputOTPGroupProps, } from "../ui/input-otp.js";
|
|
2
|
+
export type { InputOTPAlignProp, InputOTPGroupAppearanceProp, InputOTPGroupProp, InputOTPGroupProps, } from "../ui/input-otp.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import type { StepsProp } from "../../props/components/navigation.prop.js";
|
|
3
|
-
export type { StepsProp, StepsProp as StepsProps, StepItemProp, StepStatusProp, } from "../../props/components/navigation.prop.js";
|
|
4
|
-
export declare function Steps({ items, value: current, defaultValue, status: currentStatus, orientation, type, size, titlePlacement, onValueChange, className, }: StepsProp): React.JSX.Element;
|
|
3
|
+
export type { StepsProp, StepsProp as StepsProps, StepItemProp, StepStatusProp, StepsSeparatorProp, } from "../../props/components/navigation.prop.js";
|
|
4
|
+
export declare function Steps({ items, value: current, defaultValue, status: currentStatus, orientation, type, size, titlePlacement, separator, onValueChange, className, }: StepsProp): React.JSX.Element;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { Check, ChevronRight, Circle, Loader2, X } from "lucide-react";
|
|
3
|
+
import { ArrowRight, Check, ChevronRight, Circle, Loader2, X } from "lucide-react";
|
|
4
4
|
import { useTranslation } from "../../i18n/use-translation.js";
|
|
5
5
|
import { cn } from "../../lib/utils.js";
|
|
6
6
|
import { controlIconClass } from "../../lib/control-styles.js";
|
|
@@ -61,6 +61,7 @@ function Steps({
|
|
|
61
61
|
type = "default",
|
|
62
62
|
size = "md",
|
|
63
63
|
titlePlacement = "horizontal",
|
|
64
|
+
separator = "chevron",
|
|
64
65
|
onValueChange,
|
|
65
66
|
className
|
|
66
67
|
}) {
|
|
@@ -69,6 +70,7 @@ function Steps({
|
|
|
69
70
|
const isVertical = orientation === "vertical";
|
|
70
71
|
const compact = size === "sm";
|
|
71
72
|
const inline = type === "inline";
|
|
73
|
+
const SeparatorIcon = separator === "arrow" ? ArrowRight : ChevronRight;
|
|
72
74
|
return /* @__PURE__ */ jsx(
|
|
73
75
|
"ol",
|
|
74
76
|
{
|
|
@@ -151,7 +153,7 @@ function Steps({
|
|
|
151
153
|
}
|
|
152
154
|
) : /* @__PURE__ */ jsx("span", { className: stepClassName, children: stepInner }),
|
|
153
155
|
inline && index < items.length - 1 ? /* @__PURE__ */ jsx(
|
|
154
|
-
|
|
156
|
+
SeparatorIcon,
|
|
155
157
|
{
|
|
156
158
|
className: "ui-steps-inline-separator rtl:rotate-180",
|
|
157
159
|
"aria-hidden": "true"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
|
3
3
|
import type { AvatarProp } from "../../props/components/data-display.prop.js";
|
|
4
|
-
export declare const Avatar: React.ForwardRefExoticComponent<Omit<AvatarPrimitive.AvatarProps & React.RefAttributes<HTMLSpanElement>, "ref"> & Pick<AvatarProp, "shape"> & React.RefAttributes<HTMLSpanElement>>;
|
|
4
|
+
export declare const Avatar: React.ForwardRefExoticComponent<Omit<AvatarPrimitive.AvatarProps & React.RefAttributes<HTMLSpanElement>, "ref"> & Pick<AvatarProp, "shape" | "appearance"> & React.RefAttributes<HTMLSpanElement>>;
|
|
5
5
|
export declare const AvatarImage: React.ForwardRefExoticComponent<Omit<AvatarPrimitive.AvatarImageProps & React.RefAttributes<HTMLImageElement>, "ref"> & React.RefAttributes<HTMLImageElement>>;
|
|
6
6
|
export declare const AvatarFallback: React.ForwardRefExoticComponent<Omit<AvatarPrimitive.AvatarFallbackProps & React.RefAttributes<HTMLSpanElement>, "ref"> & React.RefAttributes<HTMLSpanElement>>;
|
|
@@ -3,12 +3,13 @@ import { jsx } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
|
5
5
|
import { cn } from "../../lib/utils.js";
|
|
6
|
-
const Avatar = React.forwardRef(({ className, shape = "circle", ...props }, ref) => /* @__PURE__ */ jsx(
|
|
6
|
+
const Avatar = React.forwardRef(({ className, shape = "circle", appearance = "default", ...props }, ref) => /* @__PURE__ */ jsx(
|
|
7
7
|
AvatarPrimitive.Root,
|
|
8
8
|
{
|
|
9
9
|
ref,
|
|
10
10
|
"data-slot": "avatar",
|
|
11
11
|
"data-shape": shape === "square" ? "square" : void 0,
|
|
12
|
+
"data-appearance": appearance === "tinted" ? "tinted" : void 0,
|
|
12
13
|
className: cn("ui-avatar", className),
|
|
13
14
|
...props
|
|
14
15
|
}
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
|
|
2
|
+
import type { InputOTPAlignProp } from "../../props/components/data-entry.prop.js";
|
|
3
|
+
/**
|
|
4
|
+
* InputOTP — the code-challenge field.
|
|
5
|
+
*
|
|
6
|
+
* `align` positions the whole row (groups + separators) on the main axis: `start` (default,
|
|
7
|
+
* unchanged), `center` — the canonical auth challenge — or `end`. The attribute lands on the
|
|
8
|
+
* hidden input because `input-otp` owns the container element; `.ui-otp-container` reads it back
|
|
9
|
+
* through `:has()`, exactly as it already does for the invalid and disabled states. A service that
|
|
10
|
+
* wants every code field centred sets `--otp-container-align` once instead.
|
|
11
|
+
*/
|
|
12
|
+
export declare const InputOTP: React.ForwardRefExoticComponent<((Omit<Omit<React.InputHTMLAttributes<HTMLInputElement>, "onChange" | "value" | "maxLength" | "textAlign" | "onComplete" | "pushPasswordManagerStrategy" | "pasteTransformer" | "containerClassName" | "noScriptCSSFallback"> & {
|
|
3
13
|
value?: string;
|
|
4
14
|
onChange?: (newValue: string) => unknown;
|
|
5
15
|
maxLength: number;
|
|
@@ -25,8 +35,10 @@ export declare const InputOTP: React.ForwardRefExoticComponent<(Omit<Omit<React.
|
|
|
25
35
|
} & {
|
|
26
36
|
render?: never;
|
|
27
37
|
children: React.ReactNode;
|
|
28
|
-
} & React.RefAttributes<HTMLInputElement>, "ref">) &
|
|
29
|
-
|
|
38
|
+
} & React.RefAttributes<HTMLInputElement>, "ref">) & {
|
|
39
|
+
align?: InputOTPAlignProp;
|
|
40
|
+
}) & React.RefAttributes<HTMLInputElement>>;
|
|
41
|
+
export type { InputOTPAlignProp, InputOTPGroupAppearanceProp, InputOTPGroupProp, InputOTPGroupProp as InputOTPGroupProps, } from "../../props/components/data-entry.prop.js";
|
|
30
42
|
export declare const InputOTPGroup: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & {
|
|
31
43
|
appearance?: import("./input-otp.js").InputOTPGroupAppearanceProp;
|
|
32
44
|
} & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -4,11 +4,12 @@ import * as React from "react";
|
|
|
4
4
|
import { OTPInput, OTPInputContext } from "input-otp";
|
|
5
5
|
import { Minus } from "lucide-react";
|
|
6
6
|
import { cn } from "../../lib/utils.js";
|
|
7
|
-
const InputOTP = React.forwardRef(({ className, containerClassName, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
7
|
+
const InputOTP = React.forwardRef(({ className, containerClassName, align, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
8
8
|
OTPInput,
|
|
9
9
|
{
|
|
10
10
|
ref,
|
|
11
11
|
"data-slot": "input-otp",
|
|
12
|
+
"data-align": align && align !== "start" ? align : void 0,
|
|
12
13
|
containerClassName: cn("ui-otp-container", containerClassName),
|
|
13
14
|
className: cn("ui-otp-input", className),
|
|
14
15
|
...props
|
|
@@ -146,8 +146,8 @@ export declare const EMAIL_GEOMETRY_SOURCE: {
|
|
|
146
146
|
readonly "--email-heading-font-size": "17px";
|
|
147
147
|
readonly "--email-heading-line-height": "1.7";
|
|
148
148
|
readonly "--email-heading-font-weight": "500";
|
|
149
|
-
readonly "--email-cta-height": "
|
|
150
|
-
readonly "--email-cta-line-height": "
|
|
149
|
+
readonly "--email-cta-height": "44px";
|
|
150
|
+
readonly "--email-cta-line-height": "44px";
|
|
151
151
|
readonly "--email-cta-padding-x": "16px";
|
|
152
152
|
readonly "--email-cta-radius": "6px";
|
|
153
153
|
readonly "--email-cta-font-size": "14px";
|
|
@@ -136,8 +136,8 @@ const EMAIL_GEOMETRY_SOURCE = {
|
|
|
136
136
|
"--email-heading-font-size": "17px",
|
|
137
137
|
"--email-heading-line-height": "1.7",
|
|
138
138
|
"--email-heading-font-weight": "500",
|
|
139
|
-
"--email-cta-height": "
|
|
140
|
-
"--email-cta-line-height": "
|
|
139
|
+
"--email-cta-height": "44px",
|
|
140
|
+
"--email-cta-line-height": "44px",
|
|
141
141
|
"--email-cta-padding-x": "16px",
|
|
142
142
|
"--email-cta-radius": "6px",
|
|
143
143
|
"--email-cta-font-size": "14px",
|
|
@@ -62,9 +62,25 @@ export type DescriptionsItemProp = {
|
|
|
62
62
|
*/
|
|
63
63
|
export type AvatarProp = React.ComponentPropsWithoutRef<"span"> & {
|
|
64
64
|
shape?: AvatarShapeProp;
|
|
65
|
+
appearance?: AvatarAppearanceProp;
|
|
65
66
|
className?: ClassNameProp;
|
|
66
67
|
children?: ChildrenProp;
|
|
67
68
|
};
|
|
69
|
+
/**
|
|
70
|
+
* Avatar fill treatment.
|
|
71
|
+
*
|
|
72
|
+
* - `"default"` — the identity fill: `--muted` for a person, the solid brand mark for
|
|
73
|
+
* `shape="square"`.
|
|
74
|
+
* - `"tinted"` — the **capability medallion**: a soft role wash behind a role-coloured glyph.
|
|
75
|
+
* This is the plate a capability/feature icon sits on (`shape="square" appearance="tinted"` for
|
|
76
|
+
* the canonical rounded square). It exists because the medallion is a composition — `Avatar` +
|
|
77
|
+
* a Lucide glyph, per docs/COMPOSITION-VS-COMPONENT.md — but its *tint* was not reachable from
|
|
78
|
+
* a token, so consumers were re-deriving `hsl(var(--primary) / 0.1)` in page CSS or giving up
|
|
79
|
+
* and rendering a bare glyph. Retune with `--avatar-tinted-*`.
|
|
80
|
+
*
|
|
81
|
+
* @see Avatar
|
|
82
|
+
*/
|
|
83
|
+
export type AvatarAppearanceProp = "default" | "tinted";
|
|
68
84
|
/** @see Badge */
|
|
69
85
|
export type BadgeProp = {
|
|
70
86
|
variant?: "default" | "secondary" | "outline";
|
|
@@ -16,6 +16,13 @@ export type InputOTPGroupAppearanceProp = "slots" | "grouped";
|
|
|
16
16
|
export type InputOTPGroupProp = React.HTMLAttributes<HTMLDivElement> & {
|
|
17
17
|
appearance?: InputOTPGroupAppearanceProp;
|
|
18
18
|
};
|
|
19
|
+
/**
|
|
20
|
+
* Main-axis alignment of the whole code row (groups + separators) inside its container.
|
|
21
|
+
* `start` is the historical default. A centred challenge is the common auth case and used to
|
|
22
|
+
* force every consumer to wrap `.ui-otp-container` in their own flex-centring div.
|
|
23
|
+
* @see InputOTP
|
|
24
|
+
*/
|
|
25
|
+
export type InputOTPAlignProp = "start" | "center" | "end";
|
|
19
26
|
/** @see Input */
|
|
20
27
|
export type InputProp = React.InputHTMLAttributes<HTMLInputElement> & {
|
|
21
28
|
/** Show an inline ✕ that clears the field while it holds text (default false). */
|
|
@@ -144,9 +144,18 @@ export type StepsProp = {
|
|
|
144
144
|
type?: "default" | "dot" | "inline";
|
|
145
145
|
size?: "md" | "sm";
|
|
146
146
|
titlePlacement?: "horizontal" | "vertical";
|
|
147
|
+
/**
|
|
148
|
+
* The glyph between inline steps (`type="inline"` only). `chevron` (default, `›`) is the
|
|
149
|
+
* breadcrumb-flavoured original. `arrow` (`→`) is the canonical hosted-identity progression
|
|
150
|
+
* marker: a chevron reads as "drill into", an arrow reads as "then" — which is what a step row
|
|
151
|
+
* means. Ignored by every other `type`.
|
|
152
|
+
*/
|
|
153
|
+
separator?: StepsSeparatorProp;
|
|
147
154
|
onValueChange?: (value: number) => void;
|
|
148
155
|
className?: ClassNameProp;
|
|
149
156
|
};
|
|
157
|
+
/** @see Steps — inline separator glyph. */
|
|
158
|
+
export type StepsSeparatorProp = "chevron" | "arrow";
|
|
150
159
|
/** Tab pane — Ant Design `items` entry. */
|
|
151
160
|
export type TabItemProp = {
|
|
152
161
|
value: string;
|
package/dist/props/registry.d.ts
CHANGED
|
@@ -1063,6 +1063,11 @@ export declare const COMPONENT_PROP_REGISTRY: {
|
|
|
1063
1063
|
readonly file: "components/data-display.prop.ts";
|
|
1064
1064
|
readonly vocabulary: readonly ["AvatarShapeProp", "ChildrenProp", "ClassNameProp"];
|
|
1065
1065
|
};
|
|
1066
|
+
readonly AvatarAppearanceProp: {
|
|
1067
|
+
readonly group: "data-display";
|
|
1068
|
+
readonly file: "components/data-display.prop.ts";
|
|
1069
|
+
readonly vocabulary: readonly [];
|
|
1070
|
+
};
|
|
1066
1071
|
readonly BadgeProp: {
|
|
1067
1072
|
readonly group: "data-display";
|
|
1068
1073
|
readonly file: "components/data-display.prop.ts";
|
|
@@ -1474,6 +1479,11 @@ export declare const COMPONENT_PROP_REGISTRY: {
|
|
|
1474
1479
|
readonly file: "components/data-entry.prop.ts";
|
|
1475
1480
|
readonly vocabulary: readonly [];
|
|
1476
1481
|
};
|
|
1482
|
+
readonly InputOTPAlignProp: {
|
|
1483
|
+
readonly group: "data-entry";
|
|
1484
|
+
readonly file: "components/data-entry.prop.ts";
|
|
1485
|
+
readonly vocabulary: readonly [];
|
|
1486
|
+
};
|
|
1477
1487
|
readonly StepItemProp: {
|
|
1478
1488
|
readonly group: "navigation";
|
|
1479
1489
|
readonly file: "components/navigation.prop.ts";
|
|
@@ -1484,6 +1494,11 @@ export declare const COMPONENT_PROP_REGISTRY: {
|
|
|
1484
1494
|
readonly file: "components/navigation.prop.ts";
|
|
1485
1495
|
readonly vocabulary: readonly [];
|
|
1486
1496
|
};
|
|
1497
|
+
readonly StepsSeparatorProp: {
|
|
1498
|
+
readonly group: "navigation";
|
|
1499
|
+
readonly file: "components/navigation.prop.ts";
|
|
1500
|
+
readonly vocabulary: readonly [];
|
|
1501
|
+
};
|
|
1487
1502
|
readonly TabsProp: {
|
|
1488
1503
|
readonly group: "navigation";
|
|
1489
1504
|
readonly file: "components/navigation.prop.ts";
|
|
@@ -1532,7 +1547,7 @@ export declare const COMPONENT_PROP_REGISTRY: {
|
|
|
1532
1547
|
readonly CommandPaletteProp: {
|
|
1533
1548
|
readonly group: "data-entry";
|
|
1534
1549
|
readonly file: "components/data-entry/command-palette.tsx";
|
|
1535
|
-
readonly vocabulary: readonly ["OpenProp", "DefaultOpenProp", "OnOpenChangeProp"];
|
|
1550
|
+
readonly vocabulary: readonly ["OpenProp", "DefaultOpenProp", "OnOpenChangeProp", "OnSearchChangeProp"];
|
|
1536
1551
|
};
|
|
1537
1552
|
readonly TwoFactorSetupProp: {
|
|
1538
1553
|
readonly group: "feedback";
|
package/dist/props/registry.js
CHANGED
|
@@ -1224,6 +1224,11 @@ const COMPONENT_PROP_REGISTRY = {
|
|
|
1224
1224
|
file: "components/data-display.prop.ts",
|
|
1225
1225
|
vocabulary: ["AvatarShapeProp", "ChildrenProp", "ClassNameProp"]
|
|
1226
1226
|
},
|
|
1227
|
+
AvatarAppearanceProp: {
|
|
1228
|
+
group: "data-display",
|
|
1229
|
+
file: "components/data-display.prop.ts",
|
|
1230
|
+
vocabulary: []
|
|
1231
|
+
},
|
|
1227
1232
|
BadgeProp: {
|
|
1228
1233
|
group: "data-display",
|
|
1229
1234
|
file: "components/data-display.prop.ts",
|
|
@@ -1626,12 +1631,22 @@ const COMPONENT_PROP_REGISTRY = {
|
|
|
1626
1631
|
file: "components/data-entry.prop.ts",
|
|
1627
1632
|
vocabulary: []
|
|
1628
1633
|
},
|
|
1634
|
+
InputOTPAlignProp: {
|
|
1635
|
+
group: "data-entry",
|
|
1636
|
+
file: "components/data-entry.prop.ts",
|
|
1637
|
+
vocabulary: []
|
|
1638
|
+
},
|
|
1629
1639
|
StepItemProp: {
|
|
1630
1640
|
group: "navigation",
|
|
1631
1641
|
file: "components/navigation.prop.ts",
|
|
1632
1642
|
vocabulary: ["TitleProp", "SubtitleProp", "DescriptionProp", "IconProp", "DisabledProp"]
|
|
1633
1643
|
},
|
|
1634
1644
|
StepStatusProp: { group: "navigation", file: "components/navigation.prop.ts", vocabulary: [] },
|
|
1645
|
+
StepsSeparatorProp: {
|
|
1646
|
+
group: "navigation",
|
|
1647
|
+
file: "components/navigation.prop.ts",
|
|
1648
|
+
vocabulary: []
|
|
1649
|
+
},
|
|
1635
1650
|
TabsProp: {
|
|
1636
1651
|
group: "navigation",
|
|
1637
1652
|
file: "components/navigation.prop.ts",
|
|
@@ -1682,7 +1697,7 @@ const COMPONENT_PROP_REGISTRY = {
|
|
|
1682
1697
|
CommandPaletteProp: {
|
|
1683
1698
|
group: "data-entry",
|
|
1684
1699
|
file: "components/data-entry/command-palette.tsx",
|
|
1685
|
-
vocabulary: ["OpenProp", "DefaultOpenProp", "OnOpenChangeProp"]
|
|
1700
|
+
vocabulary: ["OpenProp", "DefaultOpenProp", "OnOpenChangeProp", "OnSearchChangeProp"]
|
|
1686
1701
|
},
|
|
1687
1702
|
TwoFactorSetupProp: {
|
|
1688
1703
|
group: "feedback",
|
package/dist/styles/base.css
CHANGED
|
@@ -28,6 +28,34 @@
|
|
|
28
28
|
* ───────────────────────────────────────────────────────────────────────── */
|
|
29
29
|
@import "tailwindcss";
|
|
30
30
|
|
|
31
|
+
/* ── THE LAYER CONTRACT (gh#412) ───────────────────────────────────────────
|
|
32
|
+
* `@import "tailwindcss"` establishes `theme, base, components, utilities`. Everything this
|
|
33
|
+
* package styles lives in `@layer components`, which is EARLIER than `utilities` — so a Tailwind
|
|
34
|
+
* utility a component itself emits silently outranks the package rule that is supposed to own the
|
|
35
|
+
* same property, no matter how specific that rule is. That is not a specificity bug you can fix
|
|
36
|
+
* with another selector; layer order beats specificity outright.
|
|
37
|
+
*
|
|
38
|
+
* It shipped as a real defect: `<table class="… text-sm">` (utilities) vs the collection preset's
|
|
39
|
+
* `font-size: var(--table-action-collection-font-size-compact)` (components) — the compact type
|
|
40
|
+
* tier never applied at ANY width, so the documented token was dead and a 5–6 character Japanese
|
|
41
|
+
* label could not fit the narrow-frame column measure (WCAG 2.2 SC 1.4.10, and only ever visible
|
|
42
|
+
* in Japanese). Two independent consumers reported it.
|
|
43
|
+
*
|
|
44
|
+
* `godxjp-ui-responsive` is declared HERE, after Tailwind, so it is the LAST layer and outranks
|
|
45
|
+
* `utilities`. It is reserved for one thing: RESPONSIVE RE-POINTS — the container/media blocks
|
|
46
|
+
* that must win over a component's own static utility for the layout contract to hold. Nothing
|
|
47
|
+
* static belongs in it; a rule that does not sit inside a `@container`/`@media` query goes in
|
|
48
|
+
* `@layer components` like everything else.
|
|
49
|
+
*
|
|
50
|
+
* What this layer canNOT do — and the rule consumers must follow: an UNLAYERED consumer rule
|
|
51
|
+
* outranks EVERY layer, including this one. So app CSS must theme this package through TOKENS on
|
|
52
|
+
* a wrapper, never by writing its own selectors against `[data-slot]` / `[data-priority]`
|
|
53
|
+
* elements. A consumer that does write such a rule must put it in a layer
|
|
54
|
+
* (`@layer components { … }`), or it will silently kill the responsive re-points the package
|
|
55
|
+
* makes — a column measured to 0px and wrapped one character per line is what that looks like.
|
|
56
|
+
* See docs/TOKENS.md · "The layer contract". */
|
|
57
|
+
@layer godxjp-ui-responsive;
|
|
58
|
+
|
|
31
59
|
/* Per-language font wiring — token-only, NO hardcoded faces. Each locale reads
|
|
32
60
|
* an OPTIONAL per-lang slot token (empty by default) and falls back to the
|
|
33
61
|
* base sans. Consumers switch a locale's face by setting its slot, e.g.
|
|
@@ -51,9 +51,16 @@
|
|
|
51
51
|
background: transparent;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
/* `featured` = the brand-toned perimeter. Its colour is a role-mirror knob (rule #45): declared
|
|
55
|
+
* `initial` in the token layer so the --primary default resolves HERE, at the call site, and a
|
|
56
|
+
* scoped [data-tenant]/.dark override of --primary still reaches it. A service that wants the
|
|
57
|
+
* featured ring in a non-brand tone overrides --card-featured-border-color; a card that wants a
|
|
58
|
+
* SEMANTIC perimeter (attention / destructive / success …) uses
|
|
59
|
+
* `accent="…" accentPlacement="perimeter"` instead — same geometry, tone-owned. */
|
|
54
60
|
[data-slot="card"][data-variant="featured"] {
|
|
55
|
-
border-color: hsl(var(--primary));
|
|
56
|
-
box-shadow: 0 0 0
|
|
61
|
+
border-color: var(--card-featured-border-color, hsl(var(--primary)));
|
|
62
|
+
box-shadow: 0 0 0 var(--card-featured-ring-width)
|
|
63
|
+
var(--card-featured-border-color, hsl(var(--primary)));
|
|
57
64
|
}
|
|
58
65
|
|
|
59
66
|
/* ── Density — override the shell inset/body tokens ──────────────────────
|
|
@@ -72,33 +79,39 @@
|
|
|
72
79
|
--card-space-body-y: var(--space-5);
|
|
73
80
|
}
|
|
74
81
|
|
|
75
|
-
/* ── Accent
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
82
|
+
/* ── Accent tone — ONE resolved colour per semantic accent ─────────────
|
|
83
|
+
* `--card-accent-color` is RESOLVED from the card's own `data-accent`, so both placements read
|
|
84
|
+
* the same value and a new tone is one line. It is deliberately NOT a service knob: it is set on
|
|
85
|
+
* the element, so a :root / [data-tenant] override could never reach it. Retint the ROLE
|
|
86
|
+
* (--attention, --success, …) — that is what a role is for. */
|
|
80
87
|
[data-slot="card"][data-accent="primary"] {
|
|
81
|
-
|
|
88
|
+
--card-accent-color: hsl(var(--primary));
|
|
82
89
|
}
|
|
83
90
|
|
|
84
91
|
[data-slot="card"][data-accent="success"] {
|
|
85
|
-
|
|
92
|
+
--card-accent-color: hsl(var(--success));
|
|
86
93
|
}
|
|
87
94
|
|
|
88
95
|
[data-slot="card"][data-accent="warning"] {
|
|
89
|
-
|
|
96
|
+
--card-accent-color: hsl(var(--warning));
|
|
90
97
|
}
|
|
91
98
|
|
|
92
99
|
[data-slot="card"][data-accent="info"] {
|
|
93
|
-
|
|
100
|
+
--card-accent-color: hsl(var(--info));
|
|
94
101
|
}
|
|
95
102
|
|
|
96
103
|
[data-slot="card"][data-accent="attention"] {
|
|
97
|
-
|
|
104
|
+
--card-accent-color: hsl(var(--attention));
|
|
98
105
|
}
|
|
99
106
|
|
|
100
107
|
[data-slot="card"][data-accent="destructive"] {
|
|
101
|
-
|
|
108
|
+
--card-accent-color: hsl(var(--destructive));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* ── Accent placement `edge` (DEFAULT) — semantic left stripe (token width), content on shell ─ */
|
|
112
|
+
[data-slot="card"][data-accent] {
|
|
113
|
+
border-inline-start-width: var(--card-accent-rail-width);
|
|
114
|
+
border-inline-start-color: var(--card-accent-color);
|
|
102
115
|
}
|
|
103
116
|
|
|
104
117
|
[data-slot="card"][data-accent] > [data-slot="card-header"],
|
|
@@ -107,6 +120,29 @@
|
|
|
107
120
|
padding-inline-start: calc(var(--card-space-inset) - var(--card-accent-rail-width));
|
|
108
121
|
}
|
|
109
122
|
|
|
123
|
+
/* ── Accent placement `perimeter` — the full attention border (gh#12) ───
|
|
124
|
+
* The whole edge carries the semantic tone instead of one rail, so a card can read as
|
|
125
|
+
* "action required" / "failed" at a glance without borrowing `--primary` the way
|
|
126
|
+
* `variant="featured"` does. Same optical weight as `featured` (border + 1px ring), tone-owned.
|
|
127
|
+
* Because the rail is gone, the slot padding compensation above is undone — content returns to
|
|
128
|
+
* the normal shell column, so switching placement never shifts the text. */
|
|
129
|
+
[data-slot="card"][data-accent][data-accent-placement="perimeter"] {
|
|
130
|
+
border-width: var(--card-accent-perimeter-width);
|
|
131
|
+
border-color: var(--card-accent-color);
|
|
132
|
+
box-shadow:
|
|
133
|
+
0 0 0 var(--card-accent-perimeter-ring-width) var(--card-accent-color),
|
|
134
|
+
var(--card-shadow),
|
|
135
|
+
var(--card-glow);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
[data-slot="card"][data-accent][data-accent-placement="perimeter"] > [data-slot="card-header"],
|
|
139
|
+
[data-slot="card"][data-accent][data-accent-placement="perimeter"]
|
|
140
|
+
> [data-slot="card-content"]:not([data-flush]),
|
|
141
|
+
[data-slot="card"][data-accent][data-accent-placement="perimeter"]
|
|
142
|
+
> [data-slot="card-footer"]:not([data-flush]) {
|
|
143
|
+
padding-inline-start: var(--card-space-inset);
|
|
144
|
+
}
|
|
145
|
+
|
|
110
146
|
/* ── Header ──────────────────────────────────────────────────────────── */
|
|
111
147
|
[data-slot="card-header"] {
|
|
112
148
|
display: flex;
|
package/dist/styles/control.css
CHANGED
|
@@ -714,8 +714,18 @@
|
|
|
714
714
|
.ui-otp-container {
|
|
715
715
|
display: flex;
|
|
716
716
|
align-items: center;
|
|
717
|
+
justify-content: var(--otp-container-align, flex-start);
|
|
717
718
|
gap: var(--space-2);
|
|
718
719
|
}
|
|
720
|
+
/* `align` on InputOTP lands on the hidden <input> (input-otp owns the container element), so the
|
|
721
|
+
* container reads it through :has() — the same mechanism this file already uses for the invalid
|
|
722
|
+
* and disabled states. Specificity puts the per-instance prop above the theme token. */
|
|
723
|
+
.ui-otp-container:has(.ui-otp-input[data-align="center"]) {
|
|
724
|
+
justify-content: center;
|
|
725
|
+
}
|
|
726
|
+
.ui-otp-container:has(.ui-otp-input[data-align="end"]) {
|
|
727
|
+
justify-content: flex-end;
|
|
728
|
+
}
|
|
719
729
|
.ui-otp-input:disabled {
|
|
720
730
|
cursor: not-allowed;
|
|
721
731
|
}
|
|
@@ -726,8 +736,8 @@
|
|
|
726
736
|
.ui-otp-slot {
|
|
727
737
|
position: relative;
|
|
728
738
|
display: flex;
|
|
729
|
-
width: var(--otp-slot-size, var(--control-height));
|
|
730
|
-
height: var(--otp-slot-size, var(--control-height));
|
|
739
|
+
width: var(--otp-slot-inline-size, var(--otp-slot-size, var(--control-height)));
|
|
740
|
+
height: var(--otp-slot-block-size, var(--otp-slot-size, var(--control-height)));
|
|
731
741
|
align-items: center;
|
|
732
742
|
justify-content: center;
|
|
733
743
|
font-size: var(--font-size-base);
|
|
@@ -30,6 +30,22 @@
|
|
|
30
30
|
color: var(--avatar-square-foreground, hsl(var(--primary-foreground)));
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
/* CAPABILITY MEDALLION — `<Avatar appearance="tinted">` (gh#12). The tinted companion of the
|
|
34
|
+
* solid entity mark: same box, same radius, a soft role wash instead of a solid fill and the
|
|
35
|
+
* glyph in the role itself. Declared AFTER the square rule (equal specificity) so it retints
|
|
36
|
+
* either shape. The glyph rule is scoped to this appearance ON PURPOSE — a global
|
|
37
|
+
* `.ui-avatar svg` would outrank the per-call-site icon classes existing avatars already use. */
|
|
38
|
+
.ui-avatar[data-appearance="tinted"] {
|
|
39
|
+
--avatar-background: var(--avatar-tinted-background, hsl(var(--primary) / 0.1));
|
|
40
|
+
color: var(--avatar-tinted-foreground, hsl(var(--primary)));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.ui-avatar[data-appearance="tinted"] svg {
|
|
44
|
+
inline-size: var(--avatar-tinted-glyph-size);
|
|
45
|
+
block-size: var(--avatar-tinted-glyph-size);
|
|
46
|
+
flex: none;
|
|
47
|
+
}
|
|
48
|
+
|
|
33
49
|
.ui-avatar-image {
|
|
34
50
|
width: 100%;
|
|
35
51
|
height: 100%;
|
|
@@ -394,10 +394,15 @@
|
|
|
394
394
|
box-shadow: 0 0 0 var(--focus-ring-width) hsl(var(--focus-ring-color, var(--ring)));
|
|
395
395
|
}
|
|
396
396
|
|
|
397
|
+
/* The step NUMBER carries the emphasis. Both knobs exist because the canonical hosted-identity
|
|
398
|
+
* row marks the step with an accent TINT at normal weight, while the original marked it with
|
|
399
|
+
* bold at the inherited colour — a service picks its own emphasis instead of forking this rule
|
|
400
|
+
* (rule #45). Defaults are the original bold + inherited colour, so nothing moves. */
|
|
397
401
|
.ui-steps-inline-index {
|
|
398
402
|
flex: none;
|
|
399
403
|
font-variant-numeric: tabular-nums;
|
|
400
|
-
font-weight: var(--font-weight
|
|
404
|
+
font-weight: var(--steps-inline-index-font-weight);
|
|
405
|
+
color: var(--steps-inline-index-color, currentColor);
|
|
401
406
|
}
|
|
402
407
|
|
|
403
408
|
.ui-steps-inline-title {
|
|
@@ -418,8 +423,7 @@
|
|
|
418
423
|
color: hsl(var(--destructive));
|
|
419
424
|
}
|
|
420
425
|
|
|
421
|
-
.ui-steps-inline-item[data-status="wait"] .ui-steps-inline-control
|
|
422
|
-
.ui-steps-inline-separator {
|
|
426
|
+
.ui-steps-inline-item[data-status="wait"] .ui-steps-inline-control {
|
|
423
427
|
color: hsl(var(--muted-foreground));
|
|
424
428
|
}
|
|
425
429
|
|
|
@@ -427,5 +431,6 @@
|
|
|
427
431
|
width: var(--steps-inline-separator-size);
|
|
428
432
|
height: var(--steps-inline-separator-size);
|
|
429
433
|
flex: none;
|
|
434
|
+
color: var(--steps-inline-separator-color, hsl(var(--muted-foreground)));
|
|
430
435
|
}
|
|
431
436
|
}
|
|
@@ -192,9 +192,14 @@
|
|
|
192
192
|
text-align: center;
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
+
/* Device-authorization also owns its CODE FIELD (gh#12): the preset's subject is the code, so
|
|
196
|
+
* the slot box comes from the preset instead of the generic square control tier. Scoped on the
|
|
197
|
+
* shell so every OTP inside the device flow inherits it; a per-instance override still wins. */
|
|
195
198
|
.ui-auth-shell[data-preset="device-authorization"] {
|
|
196
199
|
--auth-shell-card-max-width: var(--auth-shell-device-card-max-width);
|
|
197
200
|
--auth-shell-main-padding: var(--auth-shell-device-main-padding);
|
|
201
|
+
--otp-slot-inline-size: var(--auth-shell-device-otp-slot-inline-size);
|
|
202
|
+
--otp-slot-block-size: var(--auth-shell-device-otp-slot-block-size);
|
|
198
203
|
}
|
|
199
204
|
|
|
200
205
|
.ui-auth-shell[data-preset="context-selection"] {
|
|
@@ -519,7 +524,11 @@
|
|
|
519
524
|
}
|
|
520
525
|
|
|
521
526
|
/* AppShell-owned mobile drawer trigger — hidden on desktop, shown once the docked sidebar
|
|
522
|
-
* collapses out
|
|
527
|
+
* collapses out at 900px, THE ONE AppShell breakpoint (gh#165, gh#259). NOT Tailwind's `lg`
|
|
528
|
+
* (1024px): this comment said "below lg" for six minors while the shipped rule below and the
|
|
529
|
+
* hamburger's own `max-[900px]:inline-flex` both fired at 900 — see the block comment on the
|
|
530
|
+
* `@media (width <= 56.25rem)` rule for why 900 is the canonical value. The drawer nav itself is
|
|
531
|
+
* a focus-trapped Sheet. */
|
|
523
532
|
.app-mobile-nav-trigger {
|
|
524
533
|
display: none;
|
|
525
534
|
flex: 0 0 auto;
|
|
@@ -307,7 +307,18 @@
|
|
|
307
307
|
--table-action-collection-column-width: var(--table-action-collection-actions-width);
|
|
308
308
|
text-align: end;
|
|
309
309
|
}
|
|
310
|
+
}
|
|
310
311
|
|
|
312
|
+
/* The compact tier lives in `godxjp-ui-responsive` — the LAST layer, declared after Tailwind in
|
|
313
|
+
* styles/base.css — not in `@layer components`. `<table>` carries `text-sm`, a Tailwind utility,
|
|
314
|
+
* and `utilities` outranks `components` by LAYER ORDER, so a `font-size:` re-point written here as
|
|
315
|
+
* a component rule can never apply however specific it is: that is exactly why
|
|
316
|
+
* `--table-action-collection-font-size-compact` was dead at every width (gh#412), leaving the
|
|
317
|
+
* narrow frame at 14px where a 5–6 character Japanese label overflows its column measure. The
|
|
318
|
+
* column measures move with it so the whole compact tier is decided in ONE place, and so a
|
|
319
|
+
* consumer utility on the table can't half-collapse it either. See styles/base.css · THE LAYER
|
|
320
|
+
* CONTRACT. */
|
|
321
|
+
@layer godxjp-ui-responsive {
|
|
311
322
|
/* Below the step the SOURCE measures are re-pointed to their compact tier (and the type / row
|
|
312
323
|
* density with them); nothing else moves. One block per canonical step — a media/container
|
|
313
324
|
* query cannot read a `var()`, so the tokenized scale is written out (sm 40rem · md 48rem ·
|
|
@@ -324,6 +335,10 @@
|
|
|
324
335
|
--table-cell-space-x: var(--table-action-collection-cell-space-x-compact);
|
|
325
336
|
--table-cell-padding-y: var(--table-action-collection-cell-padding-y-compact);
|
|
326
337
|
font-size: var(--table-action-collection-font-size-compact);
|
|
338
|
+
/* The legibility floor. Default `0` keeps the table fitted to its container exactly as
|
|
339
|
+
* before; a consumer whose column count exceeds the priority budget raises it and the
|
|
340
|
+
* surrounding scroll region takes the overflow instead of the cells. */
|
|
341
|
+
min-inline-size: var(--table-action-collection-min-inline-size-compact);
|
|
327
342
|
}
|
|
328
343
|
|
|
329
344
|
/* A nowrap child (a Badge pill, a chip) would otherwise bleed into the next column at the
|
|
@@ -345,6 +360,10 @@
|
|
|
345
360
|
--table-cell-space-x: var(--table-action-collection-cell-space-x-compact);
|
|
346
361
|
--table-cell-padding-y: var(--table-action-collection-cell-padding-y-compact);
|
|
347
362
|
font-size: var(--table-action-collection-font-size-compact);
|
|
363
|
+
/* The legibility floor. Default `0` keeps the table fitted to its container exactly as
|
|
364
|
+
* before; a consumer whose column count exceeds the priority budget raises it and the
|
|
365
|
+
* surrounding scroll region takes the overflow instead of the cells. */
|
|
366
|
+
min-inline-size: var(--table-action-collection-min-inline-size-compact);
|
|
348
367
|
}
|
|
349
368
|
|
|
350
369
|
/* A nowrap child (a Badge pill, a chip) would otherwise bleed into the next column at the
|
|
@@ -366,6 +385,10 @@
|
|
|
366
385
|
--table-cell-space-x: var(--table-action-collection-cell-space-x-compact);
|
|
367
386
|
--table-cell-padding-y: var(--table-action-collection-cell-padding-y-compact);
|
|
368
387
|
font-size: var(--table-action-collection-font-size-compact);
|
|
388
|
+
/* The legibility floor. Default `0` keeps the table fitted to its container exactly as
|
|
389
|
+
* before; a consumer whose column count exceeds the priority budget raises it and the
|
|
390
|
+
* surrounding scroll region takes the overflow instead of the cells. */
|
|
391
|
+
min-inline-size: var(--table-action-collection-min-inline-size-compact);
|
|
369
392
|
}
|
|
370
393
|
|
|
371
394
|
/* A nowrap child (a Badge pill, a chip) would otherwise bleed into the next column at the
|
|
@@ -387,6 +410,10 @@
|
|
|
387
410
|
--table-cell-space-x: var(--table-action-collection-cell-space-x-compact);
|
|
388
411
|
--table-cell-padding-y: var(--table-action-collection-cell-padding-y-compact);
|
|
389
412
|
font-size: var(--table-action-collection-font-size-compact);
|
|
413
|
+
/* The legibility floor. Default `0` keeps the table fitted to its container exactly as
|
|
414
|
+
* before; a consumer whose column count exceeds the priority budget raises it and the
|
|
415
|
+
* surrounding scroll region takes the overflow instead of the cells. */
|
|
416
|
+
min-inline-size: var(--table-action-collection-min-inline-size-compact);
|
|
390
417
|
}
|
|
391
418
|
|
|
392
419
|
/* A nowrap child (a Badge pill, a chip) would otherwise bleed into the next column at the
|
|
@@ -74,6 +74,18 @@
|
|
|
74
74
|
* The slot padding compensation in card-layout.css subtracts the same token,
|
|
75
75
|
* so content stays aligned on the shell whatever the rail width. */
|
|
76
76
|
--card-accent-rail-width: 6px;
|
|
77
|
+
/* Accent placement `perimeter` (gh#12) — the FULL attention border. Two knobs so a service can
|
|
78
|
+
* tune the edge weight and the outer ring independently; the defaults reproduce the optical
|
|
79
|
+
* weight of `variant="featured"` (1px border + 1px ring) in the card's own semantic accent tone
|
|
80
|
+
* instead of --primary. Structural 1px literals, like the base card hairline. */
|
|
81
|
+
--card-accent-perimeter-width: 1px;
|
|
82
|
+
--card-accent-perimeter-ring-width: 1px;
|
|
83
|
+
/* `variant="featured"` edge — role-mirror knob (docs/TOKENS.md). `initial` so the --primary
|
|
84
|
+
* default resolves at the CALL SITE and a scoped [data-tenant]/.dark override of --primary
|
|
85
|
+
* reaches it; a service points it anywhere (e.g. var(--attention)) to retint every featured card
|
|
86
|
+
* at once. Default = hsl(var(--primary)). */
|
|
87
|
+
--card-featured-border-color: initial;
|
|
88
|
+
--card-featured-ring-width: 1px;
|
|
77
89
|
--stat-card-label-font-size: var(--font-size-xs);
|
|
78
90
|
--stat-card-label-font-weight: var(--font-weight-medium);
|
|
79
91
|
--stat-card-label-letter-spacing: 0.04em;
|
|
@@ -67,6 +67,22 @@
|
|
|
67
67
|
* re-scopes --control-height to 36px, would silently shrink. Verified in Chromium: 36px before
|
|
68
68
|
* and after. A service opts in with a NAMED tier (`var(--control-height-lg)`), never a calc. */
|
|
69
69
|
--otp-slot-size: initial; /* default = var(--control-height) at the call site */
|
|
70
|
+
/* Per-AXIS overrides of the slot box (gh#12). --otp-slot-size stays the square shorthand; these
|
|
71
|
+
* two win over it when set, so a code field can be WIDER-than-tall or TALLER-than-wide without
|
|
72
|
+
* abandoning the token. A canonical device-grant code field is 27.5x52 per slot (a 4-slot
|
|
73
|
+
* `appearance="grouped"` box of 112x54 with its 1px group border) — not expressible with one
|
|
74
|
+
* square knob, which is why grouped OTP measured 146x38 against that artboard. `initial` for the
|
|
75
|
+
* same tier-mirror reason as --otp-slot-size: the whole fallback chain
|
|
76
|
+
* (axis → square → --control-height) must resolve at the CALL SITE so a shell that re-scopes
|
|
77
|
+
* --control-height still reaches an OTP row that sets neither axis. */
|
|
78
|
+
--otp-slot-inline-size: initial; /* default = var(--otp-slot-size, var(--control-height)) */
|
|
79
|
+
--otp-slot-block-size: initial; /* default = var(--otp-slot-size, var(--control-height)) */
|
|
80
|
+
/* Main-axis alignment of the whole OTP row (rule #44/#45 — chrome is a token, default quiet).
|
|
81
|
+
* `flex-start` is the historical behaviour, so an existing field is byte-identical; a centred
|
|
82
|
+
* code field is `align="center"` on InputOTP (which wins over this knob) or this token set once
|
|
83
|
+
* in a service theme. It exists because EVERY consumer was wrapping .ui-otp-container in a
|
|
84
|
+
* flex-centring div to get a centred challenge. */
|
|
85
|
+
--otp-container-align: flex-start;
|
|
70
86
|
|
|
71
87
|
/* Checked/on/active fills — `initial` so the --primary default re-resolves at the call site
|
|
72
88
|
* under a scoped theme (a :root binding to var(--primary) freezes at the :root value and a scoped
|
|
@@ -28,6 +28,18 @@
|
|
|
28
28
|
--avatar-square-background: initial;
|
|
29
29
|
--avatar-square-foreground: initial;
|
|
30
30
|
|
|
31
|
+
/* Capability medallion — `<Avatar appearance="tinted">` (gh#12): the tinted plate a capability /
|
|
32
|
+
feature glyph sits on, as opposed to the SOLID entity mark above. Pair it with shape="square"
|
|
33
|
+
for the canonical rounded square. --avatar-tinted-background / --avatar-tinted-foreground are
|
|
34
|
+
role-mirror knobs (`initial`, docs/TOKENS.md) so the --primary defaults resolve at the CALL
|
|
35
|
+
SITE and a scoped [data-tenant]/.dark override of --primary reaches them — the exact reason
|
|
36
|
+
this is a token and not the `hsl(var(--primary) / 0.1)` literal consumers were writing in page
|
|
37
|
+
CSS. Defaults = hsl(var(--primary) / 0.1) wash · hsl(var(--primary)) glyph ·
|
|
38
|
+
--control-icon-size glyph box. */
|
|
39
|
+
--avatar-tinted-background: initial;
|
|
40
|
+
--avatar-tinted-foreground: initial;
|
|
41
|
+
--avatar-tinted-glyph-size: var(--control-icon-size);
|
|
42
|
+
|
|
31
43
|
/* Progress track + fill — `initial` so the role defaults re-resolve under a scoped theme.
|
|
32
44
|
Track reads --secondary, fill reads --success; a service re-tones once.
|
|
33
45
|
Defaults = hsl(var(--secondary)) track · hsl(var(--success)) fill. */
|
|
@@ -63,8 +63,14 @@
|
|
|
63
63
|
--email-heading-font-weight: 500; /* mirrors --font-weight-medium — an email title is calm, not bold */
|
|
64
64
|
|
|
65
65
|
/* ── Primary CTA — the one action button ────────────────────────────────────────────────── */
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
/* TOUCH TARGET, deliberately DECOUPLED from --control-height-lg (36px). Do not "restore" the
|
|
67
|
+
* mirror: email is a mobile-first, touch-only medium. There is no hover state, no precise
|
|
68
|
+
* pointer, and mail clients do not reliably offer zoom or a focus affordance — so the web's AA
|
|
69
|
+
* floor (SC 2.5.8, 24x24, which 36px clears) is the wrong bar here. 44px is the AAA target size
|
|
70
|
+
* (SC 2.5.5, 44x44) and the Apple HIG 44pt / Material 48dp convention. A service that must keep
|
|
71
|
+
* the old box sets --email-cta-height + --email-cta-line-height in its own theme. */
|
|
72
|
+
--email-cta-height: 44px;
|
|
73
|
+
--email-cta-line-height: 44px; /* equal to the height: bulletproof vertical centring in Outlook */
|
|
68
74
|
--email-cta-padding-x: 16px; /* mirrors --space-4 */
|
|
69
75
|
--email-cta-radius: 6px; /* mirrors the --radius base (0.375rem) */
|
|
70
76
|
--email-cta-font-size: 14px; /* mirrors --font-size-base */
|
|
@@ -14,6 +14,15 @@
|
|
|
14
14
|
--steps-inline-item-gap: var(--space-1);
|
|
15
15
|
--steps-inline-font-size: var(--font-size-xs);
|
|
16
16
|
--steps-inline-separator-size: var(--control-icon-size-sm);
|
|
17
|
+
/* Inline-step emphasis (gh#12). The step number's weight and tint are separate knobs so a
|
|
18
|
+
* service can express progression the canonical way — an accent TINT at normal weight — instead
|
|
19
|
+
* of the original bold. --steps-inline-index-color / --steps-inline-separator-color are
|
|
20
|
+
* role-mirror knobs (docs/TOKENS.md): `initial` so their defaults (the inherited step colour and
|
|
21
|
+
* the --muted-foreground role) resolve at the CALL SITE and a scoped [data-tenant]/.dark
|
|
22
|
+
* override still reaches them. Defaults reproduce today's row byte for byte. */
|
|
23
|
+
--steps-inline-index-font-weight: var(--font-weight-bold);
|
|
24
|
+
--steps-inline-index-color: initial; /* default = currentColor at the call site */
|
|
25
|
+
--steps-inline-separator-color: initial; /* default = hsl(var(--muted-foreground)) */
|
|
17
26
|
|
|
18
27
|
/* AppSettingPicker `compact` (gh#217) — the small, content-hugging labelled trigger used in an
|
|
19
28
|
* auth/legal footer, where the square icon-only default reads as a stray button and the full
|
|
@@ -81,10 +81,21 @@
|
|
|
81
81
|
--topbar-height: auto;
|
|
82
82
|
--topbar-inset: 0px;
|
|
83
83
|
--topbar-gap: var(--space-2);
|
|
84
|
-
/*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
84
|
+
/* ⚠ THIS KNOB DELETES CONTENT AT 1100px AND BELOW — read before you ship a center slot.
|
|
85
|
+
* At compact desktop/tablet widths the docked sidebar leaves too little inline room for three
|
|
86
|
+
* intrinsically-sized clusters, so the package hides the optional center slot before it can
|
|
87
|
+
* cover the breadcrumb/title or end utilities (gh#244). The default is `none`, which means a
|
|
88
|
+
* global search trigger placed in `center` is INVISIBLE from 1100px down — including on every
|
|
89
|
+
* phone — unless the consumer opts back in. That default arrived in 18.6.0 and removed the slot
|
|
90
|
+
* for consumers who never changed a line of their own code (gh#12); it stays because the
|
|
91
|
+
* overlap it prevents is a real defect and flipping a shipped default twice is worse than
|
|
92
|
+
* documenting it once, but it is a DECISION, not an accident:
|
|
93
|
+
*
|
|
94
|
+
* :root { --topbar-center-compact-display: flex; } ← restore the slot at every width
|
|
95
|
+
*
|
|
96
|
+
* Opt back in only once the center content has a compact presentation of its own (an icon-only
|
|
97
|
+
* search trigger, a collapsing field). A page-local media query is the anti-pattern this knob
|
|
98
|
+
* replaces. */
|
|
88
99
|
--topbar-center-compact-display: none;
|
|
89
100
|
--org-switcher-trigger-height: 2.75rem;
|
|
90
101
|
--org-switcher-trigger-padding-x: var(--space-2);
|
|
@@ -181,6 +192,18 @@
|
|
|
181
192
|
--auth-shell-device-card-max-width: 23.75rem;
|
|
182
193
|
--auth-shell-device-main-padding: 1rem;
|
|
183
194
|
--auth-shell-device-main-padding-mobile: 0.9375rem 0.3125rem;
|
|
195
|
+
/* CODE FIELD measure — the preset owns it (gh#12). A device grant is the one canonical flow whose
|
|
196
|
+
* PRIMARY control is the code field, so leaving it on the generic square --otp-slot-size made the
|
|
197
|
+
* preset own the page but not its own subject: two 4-slot `appearance="grouped"` boxes rendered
|
|
198
|
+
* 146x38 (4 x the canonical 36px control tier + the 1px group border) against a 112x54 artboard.
|
|
199
|
+
* These are the per-slot boxes that ADD UP to that artboard group:
|
|
200
|
+
* inline 27.5px x 4 + 2 x 1px group border = 112px
|
|
201
|
+
* block 52px + 2 x 1px group border = 54px
|
|
202
|
+
* Literal artboard lengths, like every other preset measure here — they must not drift with
|
|
203
|
+
* --scaling. A service that groups its code differently overrides --otp-slot-inline-size /
|
|
204
|
+
* --otp-slot-block-size on its own field; the generic square knob is untouched everywhere else. */
|
|
205
|
+
--auth-shell-device-otp-slot-inline-size: 1.71875rem;
|
|
206
|
+
--auth-shell-device-otp-slot-block-size: 3.25rem;
|
|
184
207
|
|
|
185
208
|
/* Context-selection preset — 25rem card measure on desktop/tablet, edge-to-edge on mobile
|
|
186
209
|
* (0 inline gutter) with a 16px rhythm between the intro, the card and the remember row. */
|
|
@@ -68,4 +68,25 @@
|
|
|
68
68
|
--table-action-collection-cell-space-x-compact: var(--space-2);
|
|
69
69
|
--table-action-collection-cell-padding-y-compact: var(--space-2);
|
|
70
70
|
--table-action-collection-row-height-compact: var(--table-row-height-compact);
|
|
71
|
+
/* Compact-tier LEGIBILITY FLOOR — the measure below which the preset stops fitting the table
|
|
72
|
+
* to its container and lets the scroll region it already owns take over.
|
|
73
|
+
*
|
|
74
|
+
* The percentage budget above is sized for ONE column per priority tier plus one free-text
|
|
75
|
+
* column. A queue that repeats a tier — two `secondary` columns, three `meta` columns, or an
|
|
76
|
+
* unmarked column beside them — asks for more than 100%, and under `table-layout: fixed` the
|
|
77
|
+
* surplus is taken out of the columns rather than out of the table: measured on a seven-column
|
|
78
|
+
* Japanese admin queue at 390, every column landed at 44–59px and CJK labels wrapped at ONE to
|
|
79
|
+
* TWO characters per line (dxs-platform/platform#680). That is a WCAG 2.2 SC 1.4.10 Reflow (AA)
|
|
80
|
+
* failure, and it is the failure mode this floor removes.
|
|
81
|
+
*
|
|
82
|
+
* Scrolling is the correct escape, not a concession: SC 1.4.10 exempts "parts of the content
|
|
83
|
+
* which require two-dimensional layout for usage or meaning", and its own note names data
|
|
84
|
+
* tables as the example. A table that scrolls horizontally inside its card conforms; a table
|
|
85
|
+
* whose cells are one character wide does not.
|
|
86
|
+
*
|
|
87
|
+
* Default `0` so nothing changes for any existing consumer: a queue that fits its priority
|
|
88
|
+
* budget keeps fitting. A consumer whose queue carries more columns than the budget sets this
|
|
89
|
+
* to the measure at which its narrowest column is still readable (roughly 5rem per column for
|
|
90
|
+
* Japanese at the compact type tier) and the table scrolls instead of crushing. */
|
|
91
|
+
--table-action-collection-min-inline-size-compact: 0;
|
|
71
92
|
}
|