@godxjp/ui 18.10.0 → 18.11.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 +31 -2
- package/dist/components/data-entry/input-otp.d.ts +1 -1
- package/dist/components/data-entry/select.d.ts +7 -1
- package/dist/components/data-entry/select.js +46 -26
- package/dist/components/feedback/banner.js +5 -1
- package/dist/components/layout/page-container.d.ts +1 -1
- package/dist/components/layout/page-container.js +61 -41
- package/dist/components/layout/resizable.d.ts +1 -1
- package/dist/components/layout/resizable.js +26 -8
- package/dist/components/navigation/pagination.js +20 -1
- package/dist/components/navigation/steps.d.ts +2 -2
- package/dist/components/navigation/steps.js +4 -2
- package/dist/components/navigation/tabs.js +6 -1
- 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/i18n/messages/en.json +3 -0
- package/dist/i18n/messages/ja.json +3 -0
- package/dist/i18n/messages/vi.json +3 -0
- package/dist/lib/field-a11y.d.ts +7 -0
- package/dist/lib/field-a11y.js +6 -0
- package/dist/lib/hooks.d.ts +15 -0
- package/dist/lib/hooks.js +37 -0
- 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/layout.prop.d.ts +8 -0
- package/dist/props/components/navigation.prop.d.ts +9 -0
- package/dist/props/registry.d.ts +20 -1
- package/dist/props/registry.js +21 -1
- package/dist/styles/base.css +28 -0
- package/dist/styles/card-layout.css +49 -13
- package/dist/styles/control.css +17 -2
- package/dist/styles/data-display-layout.css +16 -0
- package/dist/styles/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 +42 -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/feedback.css +20 -0
- 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/dist/tokens/semantic/layout.css +8 -0
- package/package.json +2 -2
|
@@ -106,7 +106,12 @@ const TabsList = React.forwardRef(({ className, variant = "default", ...props },
|
|
|
106
106
|
// active trigger when a resize would otherwise strand it off-strip (gh#204). Vertical
|
|
107
107
|
// orientation is untouched — it already stacks in a column and is sized by its own
|
|
108
108
|
// `h-*`/`w-*` overrides.
|
|
109
|
-
|
|
109
|
+
// justify-center-SAFE: the strip is a centred flex box that also scrolls. Plain `center`
|
|
110
|
+
// splits the overflow across BOTH edges, and scrollLeft only ever covers the trailing one
|
|
111
|
+
// — so the leading tab sat permanently outside the scrollport (an unreachable control at
|
|
112
|
+
// 320px, WCAG 2.2 SC 2.1.1). `safe` falls back to start alignment exactly when it
|
|
113
|
+
// overflows, and still centres whenever the tabs fit.
|
|
114
|
+
"group/tabs-list text-muted-foreground data-[variant=default]:bg-muted inline-flex w-fit max-w-full min-w-0 items-center justify-center-safe rounded-lg p-1 group-data-[orientation=vertical]/tabs:flex-col data-[orientation=horizontal]:[scrollbar-width:none] data-[orientation=horizontal]:overflow-x-auto data-[orientation=horizontal]:overflow-y-hidden data-[variant=line]:gap-1 data-[variant=line]:rounded-none data-[variant=line]:bg-transparent [&[data-orientation=horizontal]::-webkit-scrollbar]:hidden",
|
|
110
115
|
className
|
|
111
116
|
),
|
|
112
117
|
...props
|
|
@@ -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",
|
package/dist/lib/field-a11y.d.ts
CHANGED
|
@@ -39,6 +39,13 @@ export declare function mergeAriaIds(...values: Array<string | undefined>): stri
|
|
|
39
39
|
* ```
|
|
40
40
|
*/
|
|
41
41
|
export declare function pickFieldA11y(props: FieldA11yProps): FieldA11yProps;
|
|
42
|
+
/**
|
|
43
|
+
* The inverse of {@link pickFieldA11y}: everything EXCEPT the field-a11y attributes. Use it when a
|
|
44
|
+
* component must route the contract somewhere other than the element the remaining props land on —
|
|
45
|
+
* e.g. `Select`'s compound API, where the props bag belongs to `SelectPrimitive.Root` (a
|
|
46
|
+
* context-only component that renders no DOM) while the accessible name has to reach the trigger.
|
|
47
|
+
*/
|
|
48
|
+
export declare function omitFieldA11y<T extends FieldA11yProps>(props: T): Omit<T, keyof FieldA11yProps>;
|
|
42
49
|
/**
|
|
43
50
|
* As {@link pickFieldA11y}, but resolves the accessible **name** for a control that also has an
|
|
44
51
|
* intrinsic `aria-label` (e.g. SearchInput's built-in "Search"): when the FormField supplies an
|
package/dist/lib/field-a11y.js
CHANGED
|
@@ -19,6 +19,11 @@ function pickFieldA11y(props) {
|
|
|
19
19
|
}
|
|
20
20
|
return out;
|
|
21
21
|
}
|
|
22
|
+
function omitFieldA11y(props) {
|
|
23
|
+
const out = { ...props };
|
|
24
|
+
for (const key of FIELD_A11Y_KEYS) delete out[key];
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
22
27
|
function resolveFieldA11y(props, intrinsicAriaLabel) {
|
|
23
28
|
const picked = pickFieldA11y(props);
|
|
24
29
|
if (picked["aria-labelledby"]) {
|
|
@@ -38,6 +43,7 @@ function pickGroupFieldA11y(props) {
|
|
|
38
43
|
}
|
|
39
44
|
export {
|
|
40
45
|
mergeAriaIds,
|
|
46
|
+
omitFieldA11y,
|
|
41
47
|
pickFieldA11y,
|
|
42
48
|
pickGroupFieldA11y,
|
|
43
49
|
resolveFieldA11y
|
package/dist/lib/hooks.d.ts
CHANGED
|
@@ -28,3 +28,18 @@ export declare function useTimeoutFlag(signal: unknown, ms?: number): boolean;
|
|
|
28
28
|
export declare function useControlledLatch(valueIsDefined: boolean): boolean;
|
|
29
29
|
export declare function useMediaQuery(query: string): boolean;
|
|
30
30
|
export declare function useIsMobile(): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* WCAG 2.1.1 — a region that scrolls must be operable by keyboard. Tabbing to a focusable child
|
|
33
|
+
* scrolls the container, so a region whose content IS focusable needs nothing; one whose content is
|
|
34
|
+
* inert (plain text) strands its overflow for anyone not using a pointer and must therefore take
|
|
35
|
+
* focus itself via `tabindex="0"`. Which case applies depends on the RENDERED size and content —
|
|
36
|
+
* the same pagination strip is fine at 1440px and unreachable at 375px, and a panel full of text
|
|
37
|
+
* only overflows once it is resized — so it is measured at runtime and kept in sync as the element
|
|
38
|
+
* resizes or its content changes.
|
|
39
|
+
*
|
|
40
|
+
* The attribute is written imperatively rather than rendered: `react-resizable-panels` applies our
|
|
41
|
+
* className to a nested div it owns, which no prop can reach.
|
|
42
|
+
*
|
|
43
|
+
* @param element the scroll container itself (state, not a ref, so the effect re-runs when it mounts)
|
|
44
|
+
*/
|
|
45
|
+
export declare function useScrollableRegionTabIndex(element: HTMLElement | null): void;
|
package/dist/lib/hooks.js
CHANGED
|
@@ -68,10 +68,47 @@ function useMediaQuery(query) {
|
|
|
68
68
|
function useIsMobile() {
|
|
69
69
|
return useMediaQuery("(max-width: 767px)");
|
|
70
70
|
}
|
|
71
|
+
const SCROLLABLE_REGION_FOCUSABLE_SELECTOR = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
72
|
+
function useScrollableRegionTabIndex(element) {
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
if (!element) return;
|
|
75
|
+
const sync = () => {
|
|
76
|
+
const scrolls = element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth;
|
|
77
|
+
const hasFocusableContent = element.querySelector(SCROLLABLE_REGION_FOCUSABLE_SELECTOR) !== null;
|
|
78
|
+
if (scrolls && !hasFocusableContent) {
|
|
79
|
+
if (element.getAttribute("tabindex") !== "0") element.setAttribute("tabindex", "0");
|
|
80
|
+
} else if (element.getAttribute("tabindex") === "0") {
|
|
81
|
+
element.removeAttribute("tabindex");
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
sync();
|
|
85
|
+
const observers = [];
|
|
86
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
87
|
+
const resizeObserver = new ResizeObserver(sync);
|
|
88
|
+
resizeObserver.observe(element);
|
|
89
|
+
for (const child of Array.from(element.children)) resizeObserver.observe(child);
|
|
90
|
+
observers.push(resizeObserver);
|
|
91
|
+
}
|
|
92
|
+
if (typeof MutationObserver !== "undefined") {
|
|
93
|
+
const mutationObserver = new MutationObserver(sync);
|
|
94
|
+
mutationObserver.observe(element, {
|
|
95
|
+
childList: true,
|
|
96
|
+
subtree: true,
|
|
97
|
+
attributes: true,
|
|
98
|
+
attributeFilter: ["disabled", "href", "tabindex"]
|
|
99
|
+
});
|
|
100
|
+
observers.push(mutationObserver);
|
|
101
|
+
}
|
|
102
|
+
return () => {
|
|
103
|
+
for (const observer of observers) observer.disconnect();
|
|
104
|
+
};
|
|
105
|
+
}, [element]);
|
|
106
|
+
}
|
|
71
107
|
export {
|
|
72
108
|
useControlledLatch,
|
|
73
109
|
useDebouncedValue,
|
|
74
110
|
useIsMobile,
|
|
75
111
|
useMediaQuery,
|
|
112
|
+
useScrollableRegionTabIndex,
|
|
76
113
|
useTimeoutFlag
|
|
77
114
|
};
|
|
@@ -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). */
|
|
@@ -38,6 +38,14 @@ export type PageContainerProp = {
|
|
|
38
38
|
* overflow (`headerLayout` / `measure`) all live on this one renderer.
|
|
39
39
|
*/
|
|
40
40
|
status?: StatusProp;
|
|
41
|
+
/**
|
|
42
|
+
* Pending state for the title band while the page's own record resolves. Renders the
|
|
43
|
+
* title/subtitle as `ui-skeleton-block` placeholders and marks the header `aria-busy`, keeping
|
|
44
|
+
* the `<h1>` in the document with an sr-only accessible name (an empty heading is an axe
|
|
45
|
+
* violation) so the page's heading outline never disappears mid-load. Breadcrumbs and `extra`
|
|
46
|
+
* are NOT skeletonised — they come from the route, not the record.
|
|
47
|
+
*/
|
|
48
|
+
headerLoading?: boolean;
|
|
41
49
|
extra?: ExtraProp;
|
|
42
50
|
footer?: FooterProp;
|
|
43
51
|
breadcrumb?: BreadcrumbProp;
|
|
@@ -171,9 +171,18 @@ export type StepsProp = {
|
|
|
171
171
|
type?: "default" | "dot" | "inline";
|
|
172
172
|
size?: "md" | "sm";
|
|
173
173
|
titlePlacement?: "horizontal" | "vertical";
|
|
174
|
+
/**
|
|
175
|
+
* The glyph between inline steps (`type="inline"` only). `chevron` (default, `›`) is the
|
|
176
|
+
* breadcrumb-flavoured original. `arrow` (`→`) is the canonical hosted-identity progression
|
|
177
|
+
* marker: a chevron reads as "drill into", an arrow reads as "then" — which is what a step row
|
|
178
|
+
* means. Ignored by every other `type`.
|
|
179
|
+
*/
|
|
180
|
+
separator?: StepsSeparatorProp;
|
|
174
181
|
onValueChange?: (value: number) => void;
|
|
175
182
|
className?: ClassNameProp;
|
|
176
183
|
};
|
|
184
|
+
/** @see Steps — inline separator glyph. */
|
|
185
|
+
export type StepsSeparatorProp = "chevron" | "arrow";
|
|
177
186
|
/** Tab pane — Ant Design `items` entry. */
|
|
178
187
|
export type TabItemProp = {
|
|
179
188
|
value: string;
|
package/dist/props/registry.d.ts
CHANGED
|
@@ -470,6 +470,10 @@ export declare const COMPONENT_PROP_REGISTRY: {
|
|
|
470
470
|
readonly group: "layout";
|
|
471
471
|
readonly file: "components/layout.prop.ts";
|
|
472
472
|
readonly vocabulary: readonly ["TitleProp", "SubtitleProp", "StatusProp", "ExtraProp", "FooterProp", "BreadcrumbProp", "DensityProp", "PageContainerVariantProp", {
|
|
473
|
+
readonly field: "headerLoading";
|
|
474
|
+
readonly local: true;
|
|
475
|
+
readonly reason: "Pending state of the TITLE BAND only (skeleton title/subtitle + aria-busy on the header). Named for the BAND it skeletonises, so it can never be read as a page-wide loading flag (that is DataState's job); breadcrumbs and `extra` come from the route and stay live while the record resolves.";
|
|
476
|
+
}, {
|
|
473
477
|
readonly field: "headerLayout";
|
|
474
478
|
readonly local: true;
|
|
475
479
|
readonly reason: "Header ARRANGEMENT of the title band vs the extra slot below the 640px step (stack | responsive-inline) — orthogonal to PageContainerVariantProp, which selects the page shell layout.";
|
|
@@ -1043,6 +1047,11 @@ export declare const COMPONENT_PROP_REGISTRY: {
|
|
|
1043
1047
|
readonly file: "components/data-display.prop.ts";
|
|
1044
1048
|
readonly vocabulary: readonly ["AvatarShapeProp", "ChildrenProp", "ClassNameProp"];
|
|
1045
1049
|
};
|
|
1050
|
+
readonly AvatarAppearanceProp: {
|
|
1051
|
+
readonly group: "data-display";
|
|
1052
|
+
readonly file: "components/data-display.prop.ts";
|
|
1053
|
+
readonly vocabulary: readonly [];
|
|
1054
|
+
};
|
|
1046
1055
|
readonly BadgeProp: {
|
|
1047
1056
|
readonly group: "data-display";
|
|
1048
1057
|
readonly file: "components/data-display.prop.ts";
|
|
@@ -1440,6 +1449,11 @@ export declare const COMPONENT_PROP_REGISTRY: {
|
|
|
1440
1449
|
readonly file: "components/data-entry.prop.ts";
|
|
1441
1450
|
readonly vocabulary: readonly [];
|
|
1442
1451
|
};
|
|
1452
|
+
readonly InputOTPAlignProp: {
|
|
1453
|
+
readonly group: "data-entry";
|
|
1454
|
+
readonly file: "components/data-entry.prop.ts";
|
|
1455
|
+
readonly vocabulary: readonly [];
|
|
1456
|
+
};
|
|
1443
1457
|
readonly StepItemProp: {
|
|
1444
1458
|
readonly group: "navigation";
|
|
1445
1459
|
readonly file: "components/navigation.prop.ts";
|
|
@@ -1450,6 +1464,11 @@ export declare const COMPONENT_PROP_REGISTRY: {
|
|
|
1450
1464
|
readonly file: "components/navigation.prop.ts";
|
|
1451
1465
|
readonly vocabulary: readonly [];
|
|
1452
1466
|
};
|
|
1467
|
+
readonly StepsSeparatorProp: {
|
|
1468
|
+
readonly group: "navigation";
|
|
1469
|
+
readonly file: "components/navigation.prop.ts";
|
|
1470
|
+
readonly vocabulary: readonly [];
|
|
1471
|
+
};
|
|
1453
1472
|
readonly TabsProp: {
|
|
1454
1473
|
readonly group: "navigation";
|
|
1455
1474
|
readonly file: "components/navigation.prop.ts";
|
|
@@ -1498,7 +1517,7 @@ export declare const COMPONENT_PROP_REGISTRY: {
|
|
|
1498
1517
|
readonly CommandPaletteProp: {
|
|
1499
1518
|
readonly group: "data-entry";
|
|
1500
1519
|
readonly file: "components/data-entry/command-palette.tsx";
|
|
1501
|
-
readonly vocabulary: readonly ["OpenProp", "DefaultOpenProp", "OnOpenChangeProp"];
|
|
1520
|
+
readonly vocabulary: readonly ["OpenProp", "DefaultOpenProp", "OnOpenChangeProp", "OnSearchChangeProp"];
|
|
1502
1521
|
};
|
|
1503
1522
|
readonly TwoFactorSetupProp: {
|
|
1504
1523
|
readonly group: "feedback";
|
package/dist/props/registry.js
CHANGED
|
@@ -482,6 +482,11 @@ const COMPONENT_PROP_REGISTRY = {
|
|
|
482
482
|
"BreadcrumbProp",
|
|
483
483
|
"DensityProp",
|
|
484
484
|
"PageContainerVariantProp",
|
|
485
|
+
{
|
|
486
|
+
field: "headerLoading",
|
|
487
|
+
local: true,
|
|
488
|
+
reason: "Pending state of the TITLE BAND only (skeleton title/subtitle + aria-busy on the header). Named for the BAND it skeletonises, so it can never be read as a page-wide loading flag (that is DataState's job); breadcrumbs and `extra` come from the route and stay live while the record resolves."
|
|
489
|
+
},
|
|
485
490
|
{
|
|
486
491
|
field: "headerLayout",
|
|
487
492
|
local: true,
|
|
@@ -1195,6 +1200,11 @@ const COMPONENT_PROP_REGISTRY = {
|
|
|
1195
1200
|
file: "components/data-display.prop.ts",
|
|
1196
1201
|
vocabulary: ["AvatarShapeProp", "ChildrenProp", "ClassNameProp"]
|
|
1197
1202
|
},
|
|
1203
|
+
AvatarAppearanceProp: {
|
|
1204
|
+
group: "data-display",
|
|
1205
|
+
file: "components/data-display.prop.ts",
|
|
1206
|
+
vocabulary: []
|
|
1207
|
+
},
|
|
1198
1208
|
BadgeProp: {
|
|
1199
1209
|
group: "data-display",
|
|
1200
1210
|
file: "components/data-display.prop.ts",
|
|
@@ -1576,12 +1586,22 @@ const COMPONENT_PROP_REGISTRY = {
|
|
|
1576
1586
|
file: "components/data-entry.prop.ts",
|
|
1577
1587
|
vocabulary: []
|
|
1578
1588
|
},
|
|
1589
|
+
InputOTPAlignProp: {
|
|
1590
|
+
group: "data-entry",
|
|
1591
|
+
file: "components/data-entry.prop.ts",
|
|
1592
|
+
vocabulary: []
|
|
1593
|
+
},
|
|
1579
1594
|
StepItemProp: {
|
|
1580
1595
|
group: "navigation",
|
|
1581
1596
|
file: "components/navigation.prop.ts",
|
|
1582
1597
|
vocabulary: ["TitleProp", "SubtitleProp", "DescriptionProp", "IconProp", "DisabledProp"]
|
|
1583
1598
|
},
|
|
1584
1599
|
StepStatusProp: { group: "navigation", file: "components/navigation.prop.ts", vocabulary: [] },
|
|
1600
|
+
StepsSeparatorProp: {
|
|
1601
|
+
group: "navigation",
|
|
1602
|
+
file: "components/navigation.prop.ts",
|
|
1603
|
+
vocabulary: []
|
|
1604
|
+
},
|
|
1585
1605
|
TabsProp: {
|
|
1586
1606
|
group: "navigation",
|
|
1587
1607
|
file: "components/navigation.prop.ts",
|
|
@@ -1632,7 +1652,7 @@ const COMPONENT_PROP_REGISTRY = {
|
|
|
1632
1652
|
CommandPaletteProp: {
|
|
1633
1653
|
group: "data-entry",
|
|
1634
1654
|
file: "components/data-entry/command-palette.tsx",
|
|
1635
|
-
vocabulary: ["OpenProp", "DefaultOpenProp", "OnOpenChangeProp"]
|
|
1655
|
+
vocabulary: ["OpenProp", "DefaultOpenProp", "OnOpenChangeProp", "OnSearchChangeProp"]
|
|
1636
1656
|
},
|
|
1637
1657
|
TwoFactorSetupProp: {
|
|
1638
1658
|
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);
|
|
@@ -817,6 +827,11 @@
|
|
|
817
827
|
display: inline-flex;
|
|
818
828
|
align-items: center;
|
|
819
829
|
gap: var(--space-1);
|
|
830
|
+
/* A 10-star scale needs ~276px of hit area; a 320px viewport offers ~212 inside a card. Without
|
|
831
|
+
* wrapping, the last stars are painted outside the surface with nothing to scroll them into
|
|
832
|
+
* view, so they can be neither seen nor clicked (WCAG 2.1.1 / 2.4.7). Wrapping keeps every star
|
|
833
|
+
* reachable and is a no-op wherever the row already fits. */
|
|
834
|
+
flex-wrap: wrap;
|
|
820
835
|
}
|
|
821
836
|
.ui-rating-star {
|
|
822
837
|
/* ≥24px hit area (WCAG 2.5.8) around the 20px glyph */
|
|
@@ -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%;
|