@godxjp/ui 24.1.0 → 25.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/data-display/badge.js +3 -0
- package/dist/components/data-entry/select.js +7 -0
- package/dist/components/general/logo.d.ts +16 -0
- package/dist/components/general/logo.js +10 -2
- package/dist/components/layout/auth-identity.d.ts +1 -1
- package/dist/components/layout/auth-identity.js +5 -2
- package/dist/components/navigation/tabs.js +24 -6
- package/dist/contracts/measurement.json +1 -1
- package/dist/email/tokens.generated.d.ts +1 -1
- package/dist/email/tokens.generated.js +1 -1
- package/dist/props/components/layout.prop.d.ts +32 -1
- package/dist/props/registry.d.ts +4 -0
- package/dist/props/registry.js +5 -0
- package/dist/props/vocabulary/interaction.prop.d.ts +10 -1
- package/dist/styles/control.css +5 -1
- package/dist/styles/data-display-layout.css +16 -5
- package/dist/styles/data-entry-layout.css +5 -0
- package/dist/styles/layout.css +5 -0
- package/dist/styles/logo-layout.css +30 -3
- package/dist/styles/motion.css +4 -0
- package/dist/styles/shell-layout.css +11 -4
- package/dist/styles/text-layout.css +5 -0
- package/dist/tokens/components/logo.css +8 -0
- package/dist/tokens/components/shell.css +5 -3
- package/dist/tokens/derived.css +8 -8
- package/dist/tokens/foundation.css +2 -2
- package/docs/DESIGN-AUTHORITY.md +36 -22
- package/docs/FRAME-A11Y-CI.md +46 -17
- package/docs/foundation/colors.tsx +4 -1
- package/docs/general/logo.tsx +63 -0
- package/docs/general/typography.tsx +31 -0
- package/docs/layout/auth-identity.tsx +34 -0
- package/docs/showcase/acme-website.tsx +3 -1
- package/docs/showcase/case4-login.tsx +2 -2
- package/docs/showcase/case5-shift-calendar.tsx +5 -3
- package/docs/showcase/case6-agency-handy.tsx +13 -9
- package/docs/showcase/futurelastic-web.tsx +11 -3
- package/docs/showcase/table-bulk-actions.tsx +13 -2
- package/docs/showcase/table-density.tsx +3 -1
- package/docs/showcase/table-expandable-rows.tsx +25 -15
- package/docs/showcase/table-grouped-subtotals.tsx +5 -1
- package/package.json +2 -2
|
@@ -112,10 +112,13 @@ function Badge({
|
|
|
112
112
|
const resolvedChildren = children ?? (status ? status in STATUS_MAP ? t(`status.${status}`) : status : void 0);
|
|
113
113
|
const tinted = color != null && color !== "";
|
|
114
114
|
const removeLabel = typeof children === "string" ? children : typeof resolvedChildren === "string" ? resolvedChildren : void 0;
|
|
115
|
+
const named = props["aria-label"] != null || props["aria-labelledby"] != null;
|
|
116
|
+
const nameRole = named && props.role === void 0 && !onRemove ? "img" : void 0;
|
|
115
117
|
return /* @__PURE__ */ jsxs(
|
|
116
118
|
Element,
|
|
117
119
|
{
|
|
118
120
|
"data-slot": "badge",
|
|
121
|
+
role: nameRole,
|
|
119
122
|
"data-tone": tinted ? void 0 : resolvedTone,
|
|
120
123
|
"data-tinted": tinted ? "" : void 0,
|
|
121
124
|
"data-shape": shape ?? "default",
|
|
@@ -579,6 +579,10 @@ function DataSelect(props) {
|
|
|
579
579
|
const showSearchConfig = typeof props.showSearch === "object" && props.showSearch !== null ? props.showSearch : void 0;
|
|
580
580
|
const showSearch = showSearchConfig ? true : props.showSearch;
|
|
581
581
|
const searchable = showSearch ?? (Boolean(props.loadOptions) || props.mode !== void 0);
|
|
582
|
+
const inheritedName = useFieldNameFallback({
|
|
583
|
+
"aria-label": props["aria-label"],
|
|
584
|
+
"aria-labelledby": props["aria-labelledby"]
|
|
585
|
+
});
|
|
582
586
|
if (props.mode !== void 0 || searchable) {
|
|
583
587
|
const antdFilter = showSearchConfig?.filterOption;
|
|
584
588
|
const filterOption = antdFilter === void 0 ? props.filterOption : antdFilter === false ? () => true : antdFilter === true ? void 0 : (option, query) => antdFilter(query, option);
|
|
@@ -643,6 +647,8 @@ function DataSelect(props) {
|
|
|
643
647
|
const ariaProps = Object.fromEntries(
|
|
644
648
|
Object.entries(rest).filter(([key]) => key.startsWith("aria-"))
|
|
645
649
|
);
|
|
650
|
+
const placeholderName = typeof placeholder === "string" ? placeholder : void 0;
|
|
651
|
+
const nameFromPlaceholder = ariaProps["aria-label"] === void 0 && ariaProps["aria-labelledby"] === void 0 && inheritedName["aria-label"] === void 0 && inheritedName["aria-labelledby"] === void 0 ? placeholderName : void 0;
|
|
646
652
|
const optionTestId = (optionValue) => dataTestId ? `${dataTestId}-option-${optionValue}` : void 0;
|
|
647
653
|
let flatIndex = 0;
|
|
648
654
|
const renderItem = (option) => {
|
|
@@ -700,6 +706,7 @@ function DataSelect(props) {
|
|
|
700
706
|
status,
|
|
701
707
|
width,
|
|
702
708
|
"aria-busy": loading || void 0,
|
|
709
|
+
"aria-label": nameFromPlaceholder,
|
|
703
710
|
className: cn(
|
|
704
711
|
prefix === void 0 || prefix === null ? void 0 : "ui-select-trigger-prefixed",
|
|
705
712
|
(showClear || loading) && "ui-control-trigger-affixed",
|
|
@@ -39,6 +39,22 @@ export interface LogoProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, "
|
|
|
39
39
|
* (a string, or a node when the name needs its own markup).
|
|
40
40
|
*/
|
|
41
41
|
wordmark?: React.ReactNode;
|
|
42
|
+
/**
|
|
43
|
+
* The PRODUCT name that follows the brand, set off by the lockup's own rule — "GoDX | ID",
|
|
44
|
+
* "GoDX | Console", "GoDX | Admin" (gh#649). Pass the localized product name; it renders after
|
|
45
|
+
* `wordmark` when both are set.
|
|
46
|
+
*
|
|
47
|
+
* It is TEXT, in the lockup's own type scale, not artwork. The kit's flattened "GoDX | ID" file
|
|
48
|
+
* is a SECOND master (`0 0 1234 242` against this package's `30 30 871.29 182`) with its own
|
|
49
|
+
* hardcoded ink, its own global `id="title"`/`id="desc"` and a `#C5C8D6` rule; drawing the
|
|
50
|
+
* suffix would mean one more of those, one more dark variant and one more release per product
|
|
51
|
+
* name, and no localization at all. The brand's own construction — the G, the arrow, the "GoDX"
|
|
52
|
+
* logotype — stays untouched inside `mark="godx-lockup"`; a product name is not part of it.
|
|
53
|
+
*
|
|
54
|
+
* The rule's colour/width/height and the gap around it are `--logo-divider-*` /
|
|
55
|
+
* `--logo-product-suffix-*` tokens, so a service retunes them once rather than per page.
|
|
56
|
+
*/
|
|
57
|
+
productSuffix?: React.ReactNode;
|
|
42
58
|
/**
|
|
43
59
|
* Accessible name for the mark. When set, the logo is exposed to assistive tech as an image with
|
|
44
60
|
* this name; when omitted the mark is decorative (`aria-hidden`) — the correct default when a
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from "../../brand/godx-artwork.generated.js";
|
|
11
11
|
import { Slot } from "../../lib/slot.js";
|
|
12
12
|
import { cn } from "../../lib/utils.js";
|
|
13
|
+
import { VisuallyHidden } from "./visually-hidden.js";
|
|
13
14
|
const X_HEIGHT_TOP = /^[acegmnopqrsuvwxyz]+$/;
|
|
14
15
|
const LATIN_DESCENDER = /[gjpqy]/;
|
|
15
16
|
function logoGlyphInk(glyph) {
|
|
@@ -86,6 +87,7 @@ function MarkArtwork({ mark, glyph }) {
|
|
|
86
87
|
scheme
|
|
87
88
|
)) });
|
|
88
89
|
}
|
|
90
|
+
const GODX_LOGOTYPE_TEXT = "GoDX";
|
|
89
91
|
const Logo = React.forwardRef(
|
|
90
92
|
({
|
|
91
93
|
glyph = "g",
|
|
@@ -93,6 +95,7 @@ const Logo = React.forwardRef(
|
|
|
93
95
|
size = "md",
|
|
94
96
|
tone = "primary",
|
|
95
97
|
wordmark,
|
|
98
|
+
productSuffix,
|
|
96
99
|
label,
|
|
97
100
|
asChild,
|
|
98
101
|
children,
|
|
@@ -100,7 +103,10 @@ const Logo = React.forwardRef(
|
|
|
100
103
|
...props
|
|
101
104
|
}, ref) => {
|
|
102
105
|
const hasWordmark = wordmark !== void 0 && wordmark !== null && wordmark !== false;
|
|
106
|
+
const hasProductSuffix = productSuffix !== void 0 && productSuffix !== null && productSuffix !== false;
|
|
103
107
|
const identity = { "data-mark": mark, "data-size": size, "data-tone": tone };
|
|
108
|
+
const drawnWordmark = mark === "godx-lockup" && !hasWordmark;
|
|
109
|
+
const suffixNamePrefix = drawnWordmark ? GODX_LOGOTYPE_TEXT : "";
|
|
104
110
|
const renderRoot = (rootProps, content) => {
|
|
105
111
|
if (!asChild) {
|
|
106
112
|
return /* @__PURE__ */ jsx("span", { ref, ...rootProps, children: content });
|
|
@@ -108,7 +114,7 @@ const Logo = React.forwardRef(
|
|
|
108
114
|
const borrowed = React.Children.only(children);
|
|
109
115
|
return /* @__PURE__ */ jsx(Slot, { ...rootProps, ref, children: React.cloneElement(borrowed, void 0, content) });
|
|
110
116
|
};
|
|
111
|
-
if (hasWordmark) {
|
|
117
|
+
if (hasWordmark || hasProductSuffix) {
|
|
112
118
|
return renderRoot(
|
|
113
119
|
{
|
|
114
120
|
"data-slot": "logo-lockup",
|
|
@@ -120,7 +126,9 @@ const Logo = React.forwardRef(
|
|
|
120
126
|
},
|
|
121
127
|
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
122
128
|
/* @__PURE__ */ jsx("span", { "data-slot": "logo", ...identity, className: "ui-logo", "aria-hidden": "true", children: /* @__PURE__ */ jsx(MarkArtwork, { mark, glyph }) }),
|
|
123
|
-
/* @__PURE__ */ jsx("span", { "data-slot": "logo-wordmark", "data-logotype": true, className: "ui-logo-wordmark", children: wordmark })
|
|
129
|
+
hasWordmark ? /* @__PURE__ */ jsx("span", { "data-slot": "logo-wordmark", "data-logotype": true, className: "ui-logo-wordmark", children: wordmark }) : null,
|
|
130
|
+
hasProductSuffix ? /* @__PURE__ */ jsx(VisuallyHidden, { style: { display: "block" }, children: suffixNamePrefix }) : null,
|
|
131
|
+
hasProductSuffix ? /* @__PURE__ */ jsx("span", { "data-slot": "logo-product-suffix", className: "ui-logo-product-suffix", children: productSuffix }) : null
|
|
124
132
|
] })
|
|
125
133
|
);
|
|
126
134
|
}
|
|
@@ -2,4 +2,4 @@ import type { AuthIdentityProp } from "../../props/components/layout.prop.js";
|
|
|
2
2
|
export type { AuthIdentityProp } from "../../props/components/layout.prop.js";
|
|
3
3
|
export type { AuthIdentityProp as AuthIdentityProps } from "../../props/components/layout.prop.js";
|
|
4
4
|
/** Canonical hosted-identity mark, heading and optional real requesting-client context. */
|
|
5
|
-
export declare function AuthIdentity({ title, requester, className }: AuthIdentityProp): import("react").JSX.Element;
|
|
5
|
+
export declare function AuthIdentity({ title, brand, requester, className }: AuthIdentityProp): import("react").JSX.Element;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Clock } from "lucide-react";
|
|
3
|
+
import { isValidElement } from "react";
|
|
4
|
+
import { Slot } from "../../lib/slot.js";
|
|
3
5
|
import { cn } from "../../lib/utils.js";
|
|
4
6
|
import { Heading, Logo, Text } from "../general/index.js";
|
|
5
|
-
function AuthIdentity({ title, requester, className }) {
|
|
7
|
+
function AuthIdentity({ title, brand, requester, className }) {
|
|
8
|
+
const artwork = brand === void 0 || brand === null ? /* @__PURE__ */ jsx(Logo, { mark: "godx", tone: "success" }) : isValidElement(brand) ? /* @__PURE__ */ jsx(Slot, { "aria-hidden": "true", children: brand }) : /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: brand });
|
|
6
9
|
return /* @__PURE__ */ jsxs("div", { "data-slot": "auth-identity", className: cn("ui-auth-identity", className), children: [
|
|
7
|
-
|
|
10
|
+
artwork,
|
|
8
11
|
/* @__PURE__ */ jsx(Heading, { level: 1, children: title }),
|
|
9
12
|
requester !== void 0 && requester !== null ? /* @__PURE__ */ jsxs("div", { "data-slot": "auth-requester", className: "ui-auth-requester", children: [
|
|
10
13
|
/* @__PURE__ */ jsx("span", { "data-slot": "auth-requester-icon", className: "ui-auth-requester-icon", children: /* @__PURE__ */ jsx(Clock, { "aria-hidden": "true" }) }),
|
|
@@ -22,6 +22,8 @@ import {
|
|
|
22
22
|
useTabsOverflowValues,
|
|
23
23
|
useTabsScrollReporter
|
|
24
24
|
} from "./tabs-scroll.js";
|
|
25
|
+
const TabsPanelRegistryContext = React.createContext(null);
|
|
26
|
+
const NO_PANELS = /* @__PURE__ */ new Set();
|
|
25
27
|
function triggerSizeClassName(size) {
|
|
26
28
|
return cn(
|
|
27
29
|
size === "sm" && "min-h-[var(--tabs-trigger-height-sm)] px-[var(--tabs-trigger-padding-x-sm)] text-[length:var(--tabs-trigger-font-size-sm)]",
|
|
@@ -32,7 +34,8 @@ function triggerSizeClassName(size) {
|
|
|
32
34
|
const TabsFrameContext = React.createContext({
|
|
33
35
|
orientation: "horizontal",
|
|
34
36
|
selectionSuppressed: false,
|
|
35
|
-
size: "md"
|
|
37
|
+
size: "md",
|
|
38
|
+
panels: null
|
|
36
39
|
});
|
|
37
40
|
function resolveFallbackTabValue(items, requested) {
|
|
38
41
|
if (!items || items.length === 0) return requested;
|
|
@@ -127,9 +130,19 @@ function Tabs({
|
|
|
127
130
|
narrow
|
|
128
131
|
);
|
|
129
132
|
const selectionSuppressed = value === void 0 && items != null && items.length > 0 && resolvedDefault === void 0;
|
|
133
|
+
const [panels, setPanels] = React.useState(NO_PANELS);
|
|
134
|
+
const registerPanel = React.useCallback((panelValue) => {
|
|
135
|
+
setPanels((prev) => prev.has(panelValue) ? prev : new Set(prev).add(panelValue));
|
|
136
|
+
return () => setPanels((prev) => {
|
|
137
|
+
if (!prev.has(panelValue)) return prev;
|
|
138
|
+
const next = new Set(prev);
|
|
139
|
+
next.delete(panelValue);
|
|
140
|
+
return next;
|
|
141
|
+
});
|
|
142
|
+
}, []);
|
|
130
143
|
const frame = React.useMemo(
|
|
131
|
-
() => ({ orientation: resolvedOrientation, selectionSuppressed, size }),
|
|
132
|
-
[resolvedOrientation, selectionSuppressed, size]
|
|
144
|
+
() => ({ orientation: resolvedOrientation, selectionSuppressed, size, panels }),
|
|
145
|
+
[resolvedOrientation, selectionSuppressed, size, panels]
|
|
133
146
|
);
|
|
134
147
|
const editable = variant === "editable-card";
|
|
135
148
|
const collapsible = items != null && overflow === "menu";
|
|
@@ -312,7 +325,7 @@ function Tabs({
|
|
|
312
325
|
})
|
|
313
326
|
}
|
|
314
327
|
) : null;
|
|
315
|
-
return /* @__PURE__ */ jsx(TabsFrameContext.Provider, { value: frame, children: /* @__PURE__ */ jsx(
|
|
328
|
+
return /* @__PURE__ */ jsx(TabsFrameContext.Provider, { value: frame, children: /* @__PURE__ */ jsx(TabsPanelRegistryContext.Provider, { value: registerPanel, children: /* @__PURE__ */ jsx(
|
|
316
329
|
AriaTabs,
|
|
317
330
|
{
|
|
318
331
|
"data-slot": "tabs",
|
|
@@ -408,7 +421,7 @@ function Tabs({
|
|
|
408
421
|
})
|
|
409
422
|
] }) : children
|
|
410
423
|
}
|
|
411
|
-
) });
|
|
424
|
+
) }) });
|
|
412
425
|
}
|
|
413
426
|
const TabsList = React.forwardRef(
|
|
414
427
|
({ className, variant = "default", loop: _loop, children, ...props }, ref) => {
|
|
@@ -453,7 +466,8 @@ const TabsList = React.forwardRef(
|
|
|
453
466
|
TabsList.displayName = "TabsList";
|
|
454
467
|
const TabsTrigger = React.forwardRef(
|
|
455
468
|
({ className, value, disabled, children, onKeyDown, onClick, ...props }, ref) => {
|
|
456
|
-
const { orientation, selectionSuppressed, size } = React.useContext(TabsFrameContext);
|
|
469
|
+
const { orientation, selectionSuppressed, size, panels } = React.useContext(TabsFrameContext);
|
|
470
|
+
const controlsAPanel = panels === null || panels.has(value);
|
|
457
471
|
return /* @__PURE__ */ jsx(
|
|
458
472
|
AriaTab,
|
|
459
473
|
{
|
|
@@ -490,6 +504,8 @@ const TabsTrigger = React.forwardRef(
|
|
|
490
504
|
// here for BOTH states, exactly as Radix did.
|
|
491
505
|
"data-state": renderProps.isSelected && !selectionSuppressed ? "active" : "inactive",
|
|
492
506
|
"aria-selected": renderProps.isSelected && !selectionSuppressed,
|
|
507
|
+
// RAC writes this unconditionally; see `TabsFrame.panels` (gh#643).
|
|
508
|
+
"aria-controls": controlsAPanel ? domProps["aria-controls"] : void 0,
|
|
493
509
|
"data-orientation": orientation,
|
|
494
510
|
disabled,
|
|
495
511
|
// CHAINED, not spread — the same reason `onKeyDown` below is. `withDomProps` lays the
|
|
@@ -523,6 +539,8 @@ TabsTrigger.displayName = "TabsTrigger";
|
|
|
523
539
|
const TabsContent = React.forwardRef(
|
|
524
540
|
({ className, value, forceMount, children, ...props }, ref) => {
|
|
525
541
|
const { orientation, selectionSuppressed } = React.useContext(TabsFrameContext);
|
|
542
|
+
const registerPanel = React.useContext(TabsPanelRegistryContext);
|
|
543
|
+
React.useEffect(() => registerPanel?.(value), [registerPanel, value]);
|
|
526
544
|
if (selectionSuppressed && !forceMount) return null;
|
|
527
545
|
return /* @__PURE__ */ jsx(
|
|
528
546
|
AriaTabPanel,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$comment": "AUTO-GENERATED by scripts/gen-measurement-contract.mjs — do not edit. Read this instead of guessing: docs/MEASUREMENT-CONTRACT.md.",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "25.0.0",
|
|
4
4
|
"targetSize": {
|
|
5
5
|
"standard": "WCAG 2.2 SC 2.5.8 Target Size (Minimum), level AA — 24×24 CSS px",
|
|
6
6
|
"min": 24,
|
|
@@ -61,7 +61,7 @@ export declare const EMAIL_COLOR_SOURCE: {
|
|
|
61
61
|
};
|
|
62
62
|
readonly urgencyForeground: {
|
|
63
63
|
readonly cssVar: "--attention-foreground";
|
|
64
|
-
readonly hsl: "
|
|
64
|
+
readonly hsl: "48 8% 13%";
|
|
65
65
|
};
|
|
66
66
|
};
|
|
67
67
|
/** Dark-scheme colour sources, for `@media (prefers-color-scheme: dark)` overrides. */
|
|
@@ -630,8 +630,39 @@ export type AuthFooterProp = {
|
|
|
630
630
|
* (`--auth-identity-gap` / `--auth-requester-*`) — a consumer never re-centres or re-spaces it.
|
|
631
631
|
*/
|
|
632
632
|
export type AuthIdentityProp = {
|
|
633
|
-
/**
|
|
633
|
+
/**
|
|
634
|
+
* Primary auth heading, rendered as the page `h1`. With `brand` set it is still the `h1` and
|
|
635
|
+
* still the block's accessible NAME; it simply stops being painted — see `brand`.
|
|
636
|
+
*/
|
|
634
637
|
title: ReactNode;
|
|
638
|
+
/**
|
|
639
|
+
* Brand artwork rendered in the mark's place, for a service whose design supplies a real
|
|
640
|
+
* lockup (mark + wordmark, sometimes a product suffix) rather than the bare GoDX mark. Leave
|
|
641
|
+
* it out and the canonical `<Logo mark="godx">` renders, which is right for every plain
|
|
642
|
+
* hosted-GoDX surface.
|
|
643
|
+
*
|
|
644
|
+
* The node takes the MARK's slot only: `data-slot="auth-identity"`, the `.ui-auth-identity`
|
|
645
|
+
* spacing and the `h1` contract are untouched, so a consumer swapping artwork does not fork
|
|
646
|
+
* the identity block to do it. Pass an inline `<svg>`, never a bitmap.
|
|
647
|
+
*
|
|
648
|
+
* IT IS RENDERED DECORATIVE, and that is measured rather than assumed (gh#652). The mark it
|
|
649
|
+
* replaces was always `aria-hidden` — the `h1` names this block — but a real LOCKUP is not a
|
|
650
|
+
* mark: `Logo mark="godx-lockup" productSuffix="ID"` puts "GoDX ID" into the accessibility tree
|
|
651
|
+
* as real text (gh#649's sr-only logotype plus the suffix), so left exposed beside an `h1` named
|
|
652
|
+
* "GoDX ID" the block announces the product TWICE — measured "GoDXIDGoDX ID" against the
|
|
653
|
+
* "GoDX ID" it has announced since it shipped. `aria-hidden` is merged ONTO the supplied element
|
|
654
|
+
* through `Slot` rather than onto a wrapper: a wrapper would be the flex item, and an
|
|
655
|
+
* `inline-flex` lockup inside a block box is back on a LINE BOX, whose strut descender lifts the
|
|
656
|
+
* mark a few px — the measured defect `Logo`'s own `asChild` prop exists to remove. A `brand`
|
|
657
|
+
* that is not a single element cannot take a merged prop and is wrapped instead; artwork always
|
|
658
|
+
* is one, which is why the doc above says to pass an inline `<svg>`.
|
|
659
|
+
*
|
|
660
|
+
* Keep `brand` NON-INTERACTIVE: `aria-hidden` over a focusable child is a WCAG failure.
|
|
661
|
+
*
|
|
662
|
+
* The painted `h1` stays. A lockup that already draws the product name therefore shows it twice
|
|
663
|
+
* on screen; that half of gh#652 is a separate decision and is not taken here.
|
|
664
|
+
*/
|
|
665
|
+
brand?: ReactNode;
|
|
635
666
|
/**
|
|
636
667
|
* Optional real requesting-client context ("Attendance is requesting sign in"). Pass it ONLY
|
|
637
668
|
* when the client identity is authoritative — never a placeholder.
|
package/dist/props/registry.d.ts
CHANGED
|
@@ -938,6 +938,10 @@ export declare const COMPONENT_PROP_REGISTRY: {
|
|
|
938
938
|
readonly group: "layout";
|
|
939
939
|
readonly file: "components/layout.prop.ts";
|
|
940
940
|
readonly vocabulary: readonly ["TitleProp", "ClassNameProp", {
|
|
941
|
+
readonly field: "brand";
|
|
942
|
+
readonly local: true;
|
|
943
|
+
readonly reason: "The product's own identity artwork (a Logo lockup), replacing the package mark AND the painted h1; a single element, because the block marks it decorative by merging aria-hidden onto it.";
|
|
944
|
+
}, {
|
|
941
945
|
readonly field: "requester";
|
|
942
946
|
readonly local: true;
|
|
943
947
|
readonly reason: "Authoritative requesting-client context for a delegated auth flow (device grant / consent).";
|
package/dist/props/registry.js
CHANGED
|
@@ -978,6 +978,11 @@ const COMPONENT_PROP_REGISTRY = {
|
|
|
978
978
|
vocabulary: [
|
|
979
979
|
"TitleProp",
|
|
980
980
|
"ClassNameProp",
|
|
981
|
+
{
|
|
982
|
+
field: "brand",
|
|
983
|
+
local: true,
|
|
984
|
+
reason: "The product's own identity artwork (a Logo lockup), replacing the package mark AND the painted h1; a single element, because the block marks it decorative by merging aria-hidden onto it."
|
|
985
|
+
},
|
|
981
986
|
{
|
|
982
987
|
field: "requester",
|
|
983
988
|
local: true,
|
|
@@ -32,7 +32,16 @@ export type AvatarShapeProp = "circle" | "square";
|
|
|
32
32
|
/** Text size — steps of the golden-ratio type scale (NEVER an arbitrary px). `sm` = base. */
|
|
33
33
|
export type TextSizeProp = "2xs" | "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
|
|
34
34
|
/** Text colour intent — maps to semantic foreground tokens (no raw palette). */
|
|
35
|
-
|
|
35
|
+
/**
|
|
36
|
+
* `inherit` is the one that is NOT a colour: it takes whatever the surface already set (gh#643).
|
|
37
|
+
*
|
|
38
|
+
* Every other member paints an ABSOLUTE token, which is right on a page surface and wrong the
|
|
39
|
+
* moment the text sits on a coloured one — a brand fill, a tinted calendar block, a status band.
|
|
40
|
+
* There was no way to say "use this surface's own foreground", so `Text` on `bg-primary` painted
|
|
41
|
+
* `--foreground` over `--primary` and measured 2.45:1. Found by check:frame-axe on this package's
|
|
42
|
+
* OWN colour-token page, of all places, and on two showcases besides.
|
|
43
|
+
*/
|
|
44
|
+
export type TextToneProp = "default" | "muted" | "primary" | "success" | "warning" | "destructive" | "info" | "inherit";
|
|
36
45
|
/** Font weight — the reference-design canon is THREE weights only: `regular` (400 body), `medium` (500
|
|
37
46
|
* heading/label), `bold` (700 emphasis). 600/`semibold` is forbidden. */
|
|
38
47
|
export type FontWeightProp = "regular" | "medium" | "semibold" | "bold";
|
package/dist/styles/control.css
CHANGED
|
@@ -847,10 +847,14 @@
|
|
|
847
847
|
|
|
848
848
|
.ui-search-input-clear {
|
|
849
849
|
position: absolute;
|
|
850
|
+
display: grid;
|
|
851
|
+
min-inline-size: var(--touch-target-min);
|
|
852
|
+
min-block-size: var(--touch-target-min);
|
|
850
853
|
inset-inline-end: var(--search-input-edge-inset);
|
|
851
854
|
top: 50%;
|
|
852
|
-
|
|
855
|
+
place-items: center;
|
|
853
856
|
color: hsl(var(--muted-foreground));
|
|
857
|
+
transform: translateY(-50%);
|
|
854
858
|
}
|
|
855
859
|
|
|
856
860
|
.ui-search-input-clear:hover {
|
|
@@ -636,14 +636,25 @@
|
|
|
636
636
|
flex-wrap: wrap;
|
|
637
637
|
align-items: center;
|
|
638
638
|
justify-content: center;
|
|
639
|
-
|
|
639
|
+
|
|
640
|
+
gap: 0;
|
|
640
641
|
padding-top: var(--space-4);
|
|
641
642
|
}
|
|
642
643
|
|
|
643
644
|
.ui-carousel-dot {
|
|
645
|
+
display: grid;
|
|
646
|
+
min-inline-size: var(--touch-target-min);
|
|
647
|
+
min-block-size: var(--touch-target-min);
|
|
648
|
+
flex-shrink: 0;
|
|
649
|
+
place-items: center;
|
|
650
|
+
background: transparent;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
.ui-carousel-dot::after {
|
|
654
|
+
content: "";
|
|
655
|
+
display: block;
|
|
644
656
|
width: var(--space-2);
|
|
645
657
|
height: var(--space-2);
|
|
646
|
-
flex-shrink: 0;
|
|
647
658
|
border-radius: var(--radius-pill);
|
|
648
659
|
background: hsl(var(--border));
|
|
649
660
|
transition:
|
|
@@ -651,11 +662,11 @@
|
|
|
651
662
|
background-color 150ms ease;
|
|
652
663
|
}
|
|
653
664
|
|
|
654
|
-
.ui-carousel-dot:hover {
|
|
665
|
+
.ui-carousel-dot:hover::after {
|
|
655
666
|
background: hsl(var(--muted-foreground));
|
|
656
667
|
}
|
|
657
668
|
|
|
658
|
-
.ui-carousel-dot[data-active] {
|
|
669
|
+
.ui-carousel-dot[data-active]::after {
|
|
659
670
|
width: var(--space-5);
|
|
660
671
|
background: hsl(var(--primary));
|
|
661
672
|
}
|
|
@@ -664,7 +675,7 @@
|
|
|
664
675
|
flex-direction: column;
|
|
665
676
|
}
|
|
666
677
|
|
|
667
|
-
.ui-carousel[data-orientation="vertical"] .ui-carousel-dot[data-active] {
|
|
678
|
+
.ui-carousel[data-orientation="vertical"] .ui-carousel-dot[data-active]::after {
|
|
668
679
|
width: var(--space-2);
|
|
669
680
|
height: var(--space-5);
|
|
670
681
|
}
|
package/dist/styles/layout.css
CHANGED
|
@@ -79,6 +79,11 @@
|
|
|
79
79
|
--separator-label-color: var(--separator-tone-info-label-color, var(--info));
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
.ui-separator[data-tone="inherit"] {
|
|
83
|
+
--separator-rule-color: currentcolor;
|
|
84
|
+
--separator-label-color: currentcolor;
|
|
85
|
+
}
|
|
86
|
+
|
|
82
87
|
.ui-aspect-ratio {
|
|
83
88
|
overflow: clip;
|
|
84
89
|
overflow-clip-margin: var(--focus-ring-clip-margin);
|
|
@@ -163,15 +163,18 @@
|
|
|
163
163
|
white-space: nowrap;
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
-
.ui-logo-lockup[data-size="xs"] .ui-logo-wordmark
|
|
166
|
+
.ui-logo-lockup[data-size="xs"] .ui-logo-wordmark,
|
|
167
|
+
.ui-logo-lockup[data-size="xs"] .ui-logo-product-suffix {
|
|
167
168
|
font-size: var(--logo-wordmark-font-size-xs);
|
|
168
169
|
}
|
|
169
170
|
|
|
170
|
-
.ui-logo-lockup[data-size="sm"] .ui-logo-wordmark
|
|
171
|
+
.ui-logo-lockup[data-size="sm"] .ui-logo-wordmark,
|
|
172
|
+
.ui-logo-lockup[data-size="sm"] .ui-logo-product-suffix {
|
|
171
173
|
font-size: var(--logo-wordmark-font-size-sm);
|
|
172
174
|
}
|
|
173
175
|
|
|
174
|
-
.ui-logo-lockup[data-size="lg"] .ui-logo-wordmark
|
|
176
|
+
.ui-logo-lockup[data-size="lg"] .ui-logo-wordmark,
|
|
177
|
+
.ui-logo-lockup[data-size="lg"] .ui-logo-product-suffix {
|
|
175
178
|
font-size: var(--logo-wordmark-font-size-lg);
|
|
176
179
|
}
|
|
177
180
|
|
|
@@ -179,6 +182,30 @@
|
|
|
179
182
|
.ui-logo-lockup[data-tone="success"] .ui-logo-wordmark {
|
|
180
183
|
color: hsl(var(--logo-wordmark-color, var(--logo-godx-color, var(--brand))));
|
|
181
184
|
}
|
|
185
|
+
|
|
186
|
+
.ui-logo-lockup .ui-logo-product-suffix {
|
|
187
|
+
display: inline-flex;
|
|
188
|
+
align-items: center;
|
|
189
|
+
gap: var(--logo-product-suffix-gap);
|
|
190
|
+
font-family: var(--logo-wordmark-font-family, var(--font-family-display));
|
|
191
|
+
font-size: var(--logo-wordmark-font-size-md);
|
|
192
|
+
font-weight: var(--logo-product-suffix-font-weight);
|
|
193
|
+
letter-spacing: var(--logo-wordmark-letter-spacing);
|
|
194
|
+
line-height: 1;
|
|
195
|
+
color: hsl(var(--logo-product-suffix-color, var(--foreground)));
|
|
196
|
+
white-space: nowrap;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.ui-logo-lockup .ui-logo-product-suffix::before {
|
|
200
|
+
content: "";
|
|
201
|
+
flex: 0 0 auto;
|
|
202
|
+
inline-size: var(--logo-divider-width);
|
|
203
|
+
block-size: var(--logo-divider-height);
|
|
204
|
+
border-radius: var(--logo-divider-width);
|
|
205
|
+
background: hsl(
|
|
206
|
+
var(--logo-divider-color, var(--logo-godx-ink-color)) / var(--logo-divider-alpha)
|
|
207
|
+
);
|
|
208
|
+
}
|
|
182
209
|
}
|
|
183
210
|
|
|
184
211
|
[data-slot="logo-artwork"][data-scheme="dark"] {
|
package/dist/styles/motion.css
CHANGED
|
@@ -1640,8 +1640,15 @@
|
|
|
1640
1640
|
}
|
|
1641
1641
|
|
|
1642
1642
|
.sb-nav-item[data-active="true"] {
|
|
1643
|
-
background: var(
|
|
1644
|
-
|
|
1643
|
+
background: var(
|
|
1644
|
+
--sidebar-item-active-background,
|
|
1645
|
+
color-mix(
|
|
1646
|
+
in srgb,
|
|
1647
|
+
hsl(var(--primary)) var(--sidebar-item-active-background-alpha),
|
|
1648
|
+
transparent
|
|
1649
|
+
)
|
|
1650
|
+
);
|
|
1651
|
+
color: var(--sidebar-item-active-foreground, hsl(var(--primary)));
|
|
1645
1652
|
font-weight: var(--font-weight-medium);
|
|
1646
1653
|
}
|
|
1647
1654
|
|
|
@@ -1758,8 +1765,8 @@
|
|
|
1758
1765
|
|
|
1759
1766
|
.sb-nav-item--sub[data-active="true"] {
|
|
1760
1767
|
background: transparent;
|
|
1761
|
-
color: var(--sidebar-item-active-
|
|
1762
|
-
font-weight:
|
|
1768
|
+
color: var(--sidebar-item-active-foreground, hsl(var(--primary)));
|
|
1769
|
+
font-weight: var(--font-weight-medium);
|
|
1763
1770
|
}
|
|
1764
1771
|
|
|
1765
1772
|
.sb-nav-item--sub[data-active="true"]::before {
|
|
@@ -58,6 +58,11 @@
|
|
|
58
58
|
[data-slot="heading"][data-tone="muted"] {
|
|
59
59
|
color: hsl(var(--muted-foreground));
|
|
60
60
|
}
|
|
61
|
+
|
|
62
|
+
[data-slot="text"][data-tone="inherit"],
|
|
63
|
+
[data-slot="heading"][data-tone="inherit"] {
|
|
64
|
+
color: inherit;
|
|
65
|
+
}
|
|
61
66
|
[data-slot="text"][data-tone="primary"],
|
|
62
67
|
[data-slot="heading"][data-tone="primary"] {
|
|
63
68
|
color: hsl(var(--primary));
|
|
@@ -46,6 +46,14 @@
|
|
|
46
46
|
--logo-wordmark-letter-spacing: -0.01em;
|
|
47
47
|
--logo-wordmark-font-family: initial;
|
|
48
48
|
--logo-wordmark-color: initial;
|
|
49
|
+
|
|
50
|
+
--logo-divider-width: var(--stroke-hairline);
|
|
51
|
+
--logo-divider-height: 1em;
|
|
52
|
+
--logo-divider-alpha: 0.25;
|
|
53
|
+
--logo-divider-color: initial;
|
|
54
|
+
--logo-product-suffix-gap: var(--space-2);
|
|
55
|
+
--logo-product-suffix-font-weight: 500;
|
|
56
|
+
--logo-product-suffix-color: initial;
|
|
49
57
|
}
|
|
50
58
|
|
|
51
59
|
.dark,
|
|
@@ -211,12 +211,14 @@
|
|
|
211
211
|
--app-launcher-launchpad-title-letter-spacing: 0.08em;
|
|
212
212
|
--app-launcher-launchpad-close-space-padding: var(--space-2);
|
|
213
213
|
|
|
214
|
-
--sidebar-item-active-color: initial;
|
|
215
|
-
--sidebar-item-active-tint: initial;
|
|
216
|
-
|
|
217
214
|
--sidebar-item-active-background: initial;
|
|
215
|
+
|
|
218
216
|
--sidebar-item-active-foreground: initial;
|
|
219
217
|
|
|
218
|
+
--sidebar-item-active-tint: initial;
|
|
219
|
+
|
|
220
|
+
--sidebar-item-active-background-alpha: 12%;
|
|
221
|
+
|
|
220
222
|
--auth-shell-control-height: var(--control-height-comfortable);
|
|
221
223
|
--auth-shell-heading-size: var(--font-size-2xl);
|
|
222
224
|
--auth-shell-card-max-width: 24rem;
|
package/dist/tokens/derived.css
CHANGED
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
:root {
|
|
4
4
|
|
|
5
|
-
--primary-hover:
|
|
5
|
+
--primary-hover: 268.7 100% 41.6%;
|
|
6
6
|
|
|
7
|
-
--primary-active:
|
|
7
|
+
--primary-active: 268.7 100% 34.5%;
|
|
8
8
|
|
|
9
|
-
--primary-border:
|
|
9
|
+
--primary-border: 268.7 68% 66%;
|
|
10
10
|
--destructive-hover: 359 50.6% 53.1%;
|
|
11
11
|
--destructive-active: 355 70.6% 33.3%;
|
|
12
12
|
|
|
13
|
-
--control-outline:
|
|
13
|
+
--control-outline: 268.7 100% 44.7%;
|
|
14
14
|
--control-outline-alpha: 0.11;
|
|
15
15
|
|
|
16
16
|
--control-outline-error: 4.3 87.6% 34.7%;
|
|
@@ -22,15 +22,15 @@
|
|
|
22
22
|
.dark,
|
|
23
23
|
:root[data-theme="dark"] {
|
|
24
24
|
|
|
25
|
-
--primary-hover:
|
|
25
|
+
--primary-hover: 268.7 100% 92.7%;
|
|
26
26
|
|
|
27
|
-
--primary-active:
|
|
27
|
+
--primary-active: 268.7 100% 81.1%;
|
|
28
28
|
|
|
29
|
-
--primary-border:
|
|
29
|
+
--primary-border: 268.7 46.7% 23.5%;
|
|
30
30
|
--destructive-hover: 358.8 42.3% 53.1%;
|
|
31
31
|
--destructive-active: 357.3 50.6% 34.1%;
|
|
32
32
|
|
|
33
|
-
--control-outline:
|
|
33
|
+
--control-outline: 268.7 99% 61.8%;
|
|
34
34
|
--control-outline-alpha: 0.29;
|
|
35
35
|
|
|
36
36
|
--control-outline-error: 351.5 98.3% 53.5%;
|
|
@@ -26,13 +26,13 @@
|
|
|
26
26
|
--input: 30 7% 53%;
|
|
27
27
|
|
|
28
28
|
--success: 146 40% 58%;
|
|
29
|
-
--success-foreground:
|
|
29
|
+
--success-foreground: 48 8% 13%;
|
|
30
30
|
--warning: 44 100% 49%;
|
|
31
31
|
--warning-foreground: 48 8% 13%;
|
|
32
32
|
--info: 221 40% 50%;
|
|
33
33
|
--info-foreground: 60 33% 99%;
|
|
34
34
|
--attention: 25 99% 46%;
|
|
35
|
-
--attention-foreground:
|
|
35
|
+
--attention-foreground: 48 8% 13%;
|
|
36
36
|
|
|
37
37
|
--brand: 268.7 100% 50%;
|
|
38
38
|
--brand-foreground: 60 33% 99%;
|