@godxjp/ui 28.7.0 → 28.8.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/index.d.ts +2 -0
- package/dist/components/data-display/index.js +2 -0
- package/dist/components/data-display/marquee.d.ts +16 -0
- package/dist/components/data-display/marquee.js +155 -0
- package/dist/components/general/reveal.d.ts +23 -2
- package/dist/components/general/reveal.js +37 -7
- package/dist/components/general/typography.d.ts +4 -1
- package/dist/components/general/typography.js +14 -1
- package/dist/components/layout/affix.d.ts +86 -0
- package/dist/components/layout/affix.js +187 -0
- package/dist/components/layout/index.d.ts +4 -0
- package/dist/components/layout/index.js +4 -0
- package/dist/components/layout/legal-document-shell.js +4 -3
- package/dist/components/layout/masonry.d.ts +74 -0
- package/dist/components/layout/masonry.js +214 -0
- package/dist/components/layout/page-container.js +5 -20
- package/dist/components/navigation/anchor.d.ts +64 -0
- package/dist/components/navigation/anchor.js +284 -0
- package/dist/components/navigation/index.d.ts +4 -0
- package/dist/components/navigation/index.js +4 -0
- package/dist/components/navigation/mega-menu.d.ts +21 -0
- package/dist/components/navigation/mega-menu.js +526 -0
- package/dist/contracts/measurement.json +1 -1
- package/dist/i18n/messages/en.json +517 -0
- package/dist/i18n/messages/ja.json +513 -0
- package/dist/i18n/messages/vi.json +513 -0
- package/dist/lib/hooks.d.ts +68 -0
- package/dist/lib/hooks.js +52 -0
- package/dist/lib/platform.d.ts +14 -0
- package/dist/lib/platform.js +10 -1
- package/dist/lib/utils.d.ts +1 -1
- package/dist/lib/utils.js +3 -2
- package/dist/props/components/data-display.prop.d.ts +95 -1
- package/dist/props/components/general.prop.d.ts +47 -3
- package/dist/props/components/layout.prop.d.ts +194 -0
- package/dist/props/components/navigation.prop.d.ts +263 -0
- package/dist/props/registry.d.ts +359 -4
- package/dist/props/registry.js +472 -3
- package/dist/props/vocabulary/index.d.ts +1 -1
- package/dist/props/vocabulary/interaction.prop.d.ts +39 -2
- package/dist/styles/control.css +5 -6
- package/dist/styles/data-display-layout.css +2 -1
- package/dist/styles/density.css +4 -0
- package/dist/styles/layout.css +79 -0
- package/dist/styles/motion.css +121 -1
- package/dist/styles/navigation-layout.css +397 -1
- package/dist/styles/shell-layout.css +3 -0
- package/dist/styles/text-layout.css +52 -4
- package/dist/tokens/base.css +5 -0
- package/dist/tokens/components/affix.css +7 -0
- package/dist/tokens/components/anchor.css +17 -0
- package/dist/tokens/components/control.css +3 -3
- package/dist/tokens/components/form.css +1 -1
- package/dist/tokens/components/marquee.css +7 -0
- package/dist/tokens/components/masonry.css +6 -0
- package/dist/tokens/components/mega-menu.css +62 -0
- package/dist/tokens/components/shell.css +3 -0
- package/dist/tokens/foundation.css +11 -0
- package/dist/tokens/semantic/layout.css +7 -0
- package/docs/COMPOSITION-VS-COMPONENT.md +19 -1
- package/docs/DESIGN-AUTHORITY.md +99 -18
- package/docs/FRAME-COVERAGE-REPORT.md +7 -2
- package/docs/data-display/marquee.tsx +254 -0
- package/docs/foundation/_theme-editor-scope.ts +222 -0
- package/docs/foundation/density.tsx +12 -2
- package/docs/foundation/spacing.tsx +5 -0
- package/docs/foundation/theme-editor.tsx +645 -0
- package/docs/general/activity.tsx +65 -0
- package/docs/general/reveal.tsx +290 -22
- package/docs/general/typography.tsx +91 -1
- package/docs/layout/affix.tsx +209 -0
- package/docs/layout/masonry.tsx +291 -0
- package/docs/navigation/anchor.tsx +285 -0
- package/docs/navigation/mega-menu-panel.tsx +86 -0
- package/docs/navigation/mega-menu.tsx +254 -0
- package/docs/roadmap/website-components.md +779 -0
- package/docs/showcase/acme-website.tsx +75 -39
- package/docs/showcase/futurelastic-web.tsx +91 -49
- package/docs/showcase/marketing-page.tsx +885 -0
- package/docs/showcase/table-footer-totals.tsx +12 -2
- package/docs/showcase/theme-customization.tsx +1259 -0
- package/package.json +5 -3
- package/scripts/brand-accent.generated.mjs +27 -0
- package/scripts/ui-audit.mjs +66 -0
- package/scripts/visual-audit-rules.mjs +46 -2
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@godxjp/ui",
|
|
3
|
-
"version": "28.
|
|
4
|
-
"godxUiMcp": "28.
|
|
3
|
+
"version": "28.8.0",
|
|
4
|
+
"godxUiMcp": "28.8.0",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"README.md",
|
|
29
29
|
"scripts/visual-audit.mjs",
|
|
30
30
|
"scripts/visual-audit-rules.mjs",
|
|
31
|
+
"scripts/brand-accent.generated.mjs",
|
|
31
32
|
"scripts/audit-hook.mjs",
|
|
32
33
|
"scripts/init-agent-kit.mjs",
|
|
33
34
|
"scripts/init-guinea-pig.mjs",
|
|
@@ -419,7 +420,8 @@
|
|
|
419
420
|
"visual-audit": "node scripts/visual-audit.mjs",
|
|
420
421
|
"gen:brand": "node scripts/gen-brand.mjs",
|
|
421
422
|
"check:absorbed-names": "node scripts/check-absorbed-names.mjs",
|
|
422
|
-
"check:frame-overflow": "node scripts/check-frame-overflow.mjs"
|
|
423
|
+
"check:frame-overflow": "node scripts/check-frame-overflow.mjs",
|
|
424
|
+
"check:frame-token-wins": "node scripts/check-frame-token-wins.mjs"
|
|
423
425
|
},
|
|
424
426
|
"peerDependencies": {
|
|
425
427
|
"@hookform/resolvers": "^5.2.0",
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// AUTO-GENERATED by scripts/gen-email-tokens.mjs — do not edit.
|
|
2
|
+
// Source: src/tokens/foundation.css (:root — the LIGHT scheme).
|
|
3
|
+
// Run `node scripts/gen-email-tokens.mjs` (guard: `node scripts/gen-email-tokens.mjs --check`).
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The accent colour(s) THIS PACKAGE SHIPS as its own default — the one accent on a consumer's page
|
|
7
|
+
* that the consumer did not choose and cannot change without abandoning the GoDX brand (gh#823).
|
|
8
|
+
* `scripts/visual-audit-rules.mjs` exempts exactly these from the 渋み chroma bound.
|
|
9
|
+
*
|
|
10
|
+
* LIGHT SCHEME ONLY, deliberately: the dark `--primary`/`--brand` (#DCBCFF) measures OKLCH
|
|
11
|
+
* chroma 0.097, comfortably inside the 0.18 bound, so it never reaches an exemption. Listing it
|
|
12
|
+
* would widen the carve-out for nothing.
|
|
13
|
+
*/
|
|
14
|
+
export const SHIPPED_BRAND_ACCENTS = [
|
|
15
|
+
{
|
|
16
|
+
"cssVars": [
|
|
17
|
+
"--primary",
|
|
18
|
+
"--brand"
|
|
19
|
+
],
|
|
20
|
+
"hsl": "268.7 100% 50%",
|
|
21
|
+
"rgb": {
|
|
22
|
+
"r": 122,
|
|
23
|
+
"g": 0,
|
|
24
|
+
"b": 255
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
];
|
package/scripts/ui-audit.mjs
CHANGED
|
@@ -722,6 +722,72 @@ const RULES = [
|
|
|
722
722
|
message:
|
|
723
723
|
'Hand-rolled list — a raw <ul>/<ol>, an ARIA role="list"/"listitem", or a <li> wrapped around a library row. Build it from <Flex as="ul" marker="none"> (keeps the element, the <li> semantics and the gap token; no bullet, no indent) with <ListRow as="li"> as the rows. A row inside a wrapper of its own is an only child, so its :not(:last-child) divider never matches and every divider disappears silently (docs/CONSUMER-RULES.md §4, gh#714).',
|
|
724
724
|
},
|
|
725
|
+
{
|
|
726
|
+
id: "no-hand-rolled-scrollport",
|
|
727
|
+
replacement: "ScrollArea (label, orientation)",
|
|
728
|
+
classOnly: true,
|
|
729
|
+
scope: "consumer",
|
|
730
|
+
severity: "warn",
|
|
731
|
+
/*
|
|
732
|
+
* A scroll container built from an `overflow-*` utility instead of the primitive that exists
|
|
733
|
+
* for one.
|
|
734
|
+
*
|
|
735
|
+
* This is the only "hand-rolled X" rule here that is NOT about consistency. A scrollport with
|
|
736
|
+
* no tab stop is content a mouse can reach and a keyboard cannot — WCAG 2.2 SC 2.1.1 — and
|
|
737
|
+
* `ScrollArea` is what supplies the stop, the `role="group"` and a localized accessible name,
|
|
738
|
+
* withholding all three while there is nothing to scroll (gh#817/gh#821).
|
|
739
|
+
*
|
|
740
|
+
* WHY A LINT RULE AND NOT THE AXE RUN. `overflow-auto` on a hand-written `div` appeared ELEVEN
|
|
741
|
+
* times in this repo's docs. `check:frame-axe` flagged exactly ONE — `/showcase/table-footer-
|
|
742
|
+
* totals`, 125px of totals below the fold — because axe's `scrollable-region-focusable` PASSES
|
|
743
|
+
* a scroll container that CONTAINS a focusable element: tabbing to the link inside scrolls the
|
|
744
|
+
* box. The other ten hold links, buttons or inputs. The one that failed held a read-only totals
|
|
745
|
+
* table, so there was nothing to tab to.
|
|
746
|
+
*
|
|
747
|
+
* So the same markup is clean or broken depending on the DATA, and one link added to that
|
|
748
|
+
* totals table would have hidden it again. A runtime audit can only ever see the instance whose
|
|
749
|
+
* content happened to expose the defect; the MARKUP is what is wrong, and markup is what lint
|
|
750
|
+
* reads. That is also why this rule deliberately does NOT copy axe's focusable-descendant
|
|
751
|
+
* exemption. Read axe's own source and the reason is plain: EVERY gate it uses is a rendered
|
|
752
|
+
* measurement. The rule does not even apply unless `getScroll(node, 13)` finds real overflow
|
|
753
|
+
* and a child's `getBoundingClientRect()` falls outside the container's; the exemption is
|
|
754
|
+
* `focusable-content-evaluate`, which filters `vNode.tabbableElements` — resolved against
|
|
755
|
+
* `disabled`, `hidden` and computed style. A lint pass has none of that. Copying the exemption
|
|
756
|
+
* without the applicability test keeps all of its unsoundness and none of its precision: the
|
|
757
|
+
* `items.map()` a rule would see may render empty, conditionally, disabled, or with
|
|
758
|
+
* `tabIndex={-1}` under a roving-focus pattern.
|
|
759
|
+
*
|
|
760
|
+
* Prior art says the same. eslint-plugin-jsx-a11y has NO rule for scrollable regions and
|
|
761
|
+
* declined to add reasoning about them (jsx-eslint/eslint-plugin-jsx-a11y#717: "there isn't
|
|
762
|
+
* really anything to key off of" — closed as docs); its `no-noninteractive-tabindex` in fact
|
|
763
|
+
* FLAGS the bare `tabIndex={0}` remedy. Deque's own guidance for this rule says to put a
|
|
764
|
+
* `tabindex` of 0 on the region rather than rely on a focusable child, "because the browser may
|
|
765
|
+
* intercept the keyboard events". What this package has that jsx-a11y did not is the thing to
|
|
766
|
+
* key off: a primitive that makes the guarantee at runtime, where the guarantee lives.
|
|
767
|
+
*
|
|
768
|
+
* WARN, not error, and the measurement says why: those ten call sites are not accessibility
|
|
769
|
+
* failures TODAY, and a rule that opens with a wall of errors against markup that passes the
|
|
770
|
+
* browser gate is a rule someone deletes (godx-corebooks#114 — 1189 errors made a documented
|
|
771
|
+
* rule unenforceable). `warn` plus the `--changed` ratchet catches every scrollport an author
|
|
772
|
+
* touches from here on, and a deliberate one takes an `ui-audit-disable-line` that says why.
|
|
773
|
+
*
|
|
774
|
+
* BOUNDARIES. `overflow-hidden` / `overflow-y-hidden` / `overflow-clip` are NOT scrollports —
|
|
775
|
+
* a clipping box with `text-overflow: ellipsis` is a different and correct pattern — so the
|
|
776
|
+
* alternation names `auto|scroll` explicitly instead of matching `overflow-` and subtracting.
|
|
777
|
+
* `(?<![\w-])` keeps the match off the middle of a longer word, which this file has already
|
|
778
|
+
* paid for once elsewhere: `check-frame-overflow`'s bare /arrow/ matched inside `--n-arrow-`
|
|
779
|
+
* and reported a page container as an oversized icon. Here the same shape is one line away —
|
|
780
|
+
* the tabs example carries `id="antd-overflow-scroll"`. `classOnly` is the second guard: an
|
|
781
|
+
* `id`, a prop or product copy is blanked before this pattern ever sees it. A variant prefix
|
|
782
|
+
* (`md:overflow-y-auto`, `data-[orientation=horizontal]:overflow-x-auto`) still matches,
|
|
783
|
+
* because a conditional scrollport is still a scrollport.
|
|
784
|
+
*/
|
|
785
|
+
test: /(?<![\w-])overflow-(?:[xy]-)?(?:auto|scroll)(?![\w-])/,
|
|
786
|
+
standard:
|
|
787
|
+
"WCAG 2.2 SC 2.1.1 (Keyboard) · WAI-ARIA 1.2 (group) · Deque axe-core scrollable-region-focusable",
|
|
788
|
+
message:
|
|
789
|
+
'Hand-rolled scrollport (overflow-auto / -scroll on your own element) — use <ScrollArea label={t("…")} orientation> so the box that scrolls is a tab stop with a role and a localized name, and stops being one when there is nothing to scroll (gh#821). Without it, anything below the fold is reachable by mouse and by nothing else (WCAG 2.2 SC 2.1.1). axe only catches the instances whose content happens to have no focusable child, so this checks the markup rather than one render (gh#825). overflow-hidden is a clipping box, not a scrollport, and is not flagged.',
|
|
790
|
+
},
|
|
725
791
|
];
|
|
726
792
|
|
|
727
793
|
/**
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* feeds them here; the browser glue stays thin and these rules are fully unit-testable without a
|
|
5
5
|
* browser.
|
|
6
6
|
*/
|
|
7
|
+
import { SHIPPED_BRAND_ACCENTS } from "./brand-accent.generated.mjs";
|
|
7
8
|
|
|
8
9
|
/** Catalog (agent-facing mirror lives in mcp/src/data/visual-rules.ts; kept in sync by a guard). */
|
|
9
10
|
export const VISUAL_RULES = [
|
|
@@ -19,7 +20,7 @@ export const VISUAL_RULES = [
|
|
|
19
20
|
severity: "warn",
|
|
20
21
|
category: "color",
|
|
21
22
|
standard: "@godxjp/ui reference-design 渋み (OKLCH chroma ≤ 0.18)",
|
|
22
|
-
fix: "Desaturate brand/primary surfaces — keep OKLCH chroma ≤ 0.18. Read --primary tokens; never paint a full-width bar in raw vivid blue.",
|
|
23
|
+
fix: "Desaturate brand/primary surfaces — keep OKLCH chroma ≤ 0.18. Read --primary tokens; never paint a full-width bar in raw vivid blue. The accent @godxjp/ui itself ships is exempt (gh#823) — this finding is always a colour someone chose.",
|
|
23
24
|
},
|
|
24
25
|
{
|
|
25
26
|
id: "emoji-rendered",
|
|
@@ -95,8 +96,51 @@ export function oklchChroma({ r, g, b }) {
|
|
|
95
96
|
/** reference-design restraint bound. A signal/brand surface above this chroma "screams". */
|
|
96
97
|
export const CHROMA_LIMIT = 0.18;
|
|
97
98
|
|
|
98
|
-
/**
|
|
99
|
+
/**
|
|
100
|
+
* Per-channel sRGB tolerance for "this IS the accent we ship" (gh#823).
|
|
101
|
+
*
|
|
102
|
+
* The audit reads a computed `rgb()` off a live page, so the match has to survive whatever rounding
|
|
103
|
+
* the engine applied on the way there. Measured in Chromium (playwright 1.61), all four routes to
|
|
104
|
+
* the token — `#7A00FF`, `hsl(268.7 100% 50%)`, the legacy comma form, and `hsl(var(--primary))` —
|
|
105
|
+
* serialise to exactly `rgb(122, 0, 255)`: today's drift is 0. The tolerance is 1, kept as headroom
|
|
106
|
+
* for an engine that rounds the other way, not because Chromium needs it.
|
|
107
|
+
*
|
|
108
|
+
* WHAT 1 ADMITS, and why it cannot hide a louder purple. The window is the ±1 box around
|
|
109
|
+
* rgb(122, 0, 255): 12 colours, since g is already at 0 and b at 255 — so 11 that are not the
|
|
110
|
+
* brand. The nearest of those is rgb(122, 1, 255) / #7A01FF at OKLab ΔE 0.0003; the furthest is
|
|
111
|
+
* rgb(123, 1, 254) / #7B01FE at ΔE 0.0027, about 1/7 of a just-noticeable difference (~0.02).
|
|
112
|
+
* None is distinguishable from the brand on a screen. The nearest colour OUTSIDE the window,
|
|
113
|
+
* rgb(124, 0, 255) / #7C00FF, is still flagged. A tenant who picks a genuinely different loud
|
|
114
|
+
* purple — #9D00FF (chroma 0.296), #B026FF (0.286) — is 30+ channel steps away and still told.
|
|
115
|
+
*/
|
|
116
|
+
export const BRAND_ACCENT_TOLERANCE = 1;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* True when an accent is the brand accent THIS PACKAGE SHIPS (gh#823) — read from
|
|
120
|
+
* `brand-accent.generated.mjs`, which `scripts/gen-email-tokens.mjs` derives from foundation.css,
|
|
121
|
+
* so the exemption moves if the brand ever does.
|
|
122
|
+
*
|
|
123
|
+
* Deliberately NOT "whatever `--primary` currently resolves to": a tenant who overrides `--primary`
|
|
124
|
+
* with something louder chose that colour and must still be told. Only the value a consumer
|
|
125
|
+
* receives from us, and cannot change without abandoning the brand, is exempt.
|
|
126
|
+
*/
|
|
127
|
+
export function isShippedBrandAccent({ r, g, b }) {
|
|
128
|
+
return SHIPPED_BRAND_ACCENTS.some(
|
|
129
|
+
(accent) =>
|
|
130
|
+
Math.abs(r - accent.rgb.r) <= BRAND_ACCENT_TOLERANCE &&
|
|
131
|
+
Math.abs(g - accent.rgb.g) <= BRAND_ACCENT_TOLERANCE &&
|
|
132
|
+
Math.abs(b - accent.rgb.b) <= BRAND_ACCENT_TOLERANCE,
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* @returns {boolean} true when an accent surface exceeds the 渋み chroma limit.
|
|
138
|
+
* The shipped brand accent is exempt — it is over the bound (chroma 0.293) but it is not a choice
|
|
139
|
+
* any consumer made, so reporting it warns every page about a colour nobody can act on, and the
|
|
140
|
+
* first reader to hit it silences the rule along with every real finding it would have caught.
|
|
141
|
+
*/
|
|
99
142
|
export function isOversaturated(rgb, limit = CHROMA_LIMIT) {
|
|
143
|
+
if (isShippedBrandAccent(rgb)) return false;
|
|
100
144
|
return oklchChroma(rgb) > limit;
|
|
101
145
|
}
|
|
102
146
|
|