@godxjp/ui 28.7.0 → 28.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.
Files changed (119) hide show
  1. package/dist/components/data-display/index.d.ts +2 -0
  2. package/dist/components/data-display/index.js +2 -0
  3. package/dist/components/data-display/marquee.d.ts +16 -0
  4. package/dist/components/data-display/marquee.js +155 -0
  5. package/dist/components/general/reveal.d.ts +23 -2
  6. package/dist/components/general/reveal.js +37 -7
  7. package/dist/components/general/typography.d.ts +4 -1
  8. package/dist/components/general/typography.js +14 -1
  9. package/dist/components/layout/affix.d.ts +86 -0
  10. package/dist/components/layout/affix.js +187 -0
  11. package/dist/components/layout/index.d.ts +4 -0
  12. package/dist/components/layout/index.js +4 -0
  13. package/dist/components/layout/legal-document-shell.js +4 -3
  14. package/dist/components/layout/masonry.d.ts +74 -0
  15. package/dist/components/layout/masonry.js +214 -0
  16. package/dist/components/layout/page-container.js +5 -20
  17. package/dist/components/navigation/anchor.d.ts +64 -0
  18. package/dist/components/navigation/anchor.js +284 -0
  19. package/dist/components/navigation/index.d.ts +4 -0
  20. package/dist/components/navigation/index.js +4 -0
  21. package/dist/components/navigation/mega-menu.d.ts +21 -0
  22. package/dist/components/navigation/mega-menu.js +526 -0
  23. package/dist/contracts/measurement.json +1 -1
  24. package/dist/i18n/messages/en.json +517 -0
  25. package/dist/i18n/messages/ja.json +513 -0
  26. package/dist/i18n/messages/vi.json +513 -0
  27. package/dist/lib/hooks.d.ts +68 -0
  28. package/dist/lib/hooks.js +52 -0
  29. package/dist/lib/platform.d.ts +14 -0
  30. package/dist/lib/platform.js +10 -1
  31. package/dist/lib/utils.d.ts +1 -1
  32. package/dist/lib/utils.js +3 -2
  33. package/dist/lib/variants.js +4 -1
  34. package/dist/props/components/data-display.prop.d.ts +95 -1
  35. package/dist/props/components/general.prop.d.ts +47 -3
  36. package/dist/props/components/layout.prop.d.ts +194 -0
  37. package/dist/props/components/navigation.prop.d.ts +263 -0
  38. package/dist/props/registry.d.ts +359 -4
  39. package/dist/props/registry.js +472 -3
  40. package/dist/props/vocabulary/index.d.ts +1 -1
  41. package/dist/props/vocabulary/interaction.prop.d.ts +39 -2
  42. package/dist/props/vocabulary/layout.prop.d.ts +1 -1
  43. package/dist/styles/base.css +47 -14
  44. package/dist/styles/card-layout.css +2 -2
  45. package/dist/styles/chart-layout.css +6 -6
  46. package/dist/styles/control.css +15 -10
  47. package/dist/styles/data-display-layout.css +22 -6
  48. package/dist/styles/density.css +6 -0
  49. package/dist/styles/dialog-layout.css +4 -1
  50. package/dist/styles/focus-ring.css +4 -1
  51. package/dist/styles/layout.css +92 -3
  52. package/dist/styles/motion.css +121 -1
  53. package/dist/styles/navigation-layout.css +397 -1
  54. package/dist/styles/shell-layout.css +28 -21
  55. package/dist/styles/text-layout.css +134 -15
  56. package/dist/tokens/base.css +5 -0
  57. package/dist/tokens/components/activity.css +13 -4
  58. package/dist/tokens/components/affix.css +7 -0
  59. package/dist/tokens/components/anchor.css +17 -0
  60. package/dist/tokens/components/attachments.css +1 -1
  61. package/dist/tokens/components/badge.css +1 -1
  62. package/dist/tokens/components/card.css +28 -7
  63. package/dist/tokens/components/chart.css +4 -1
  64. package/dist/tokens/components/chat-composer.css +4 -1
  65. package/dist/tokens/components/control.css +72 -33
  66. package/dist/tokens/components/conversations.css +4 -1
  67. package/dist/tokens/components/data-display.css +42 -15
  68. package/dist/tokens/components/data-entry.css +8 -2
  69. package/dist/tokens/components/descriptions.css +1 -1
  70. package/dist/tokens/components/feedback.css +8 -5
  71. package/dist/tokens/components/float-button.css +8 -2
  72. package/dist/tokens/components/form.css +1 -1
  73. package/dist/tokens/components/legal-document.css +12 -3
  74. package/dist/tokens/components/logo.css +15 -6
  75. package/dist/tokens/components/marquee.css +7 -0
  76. package/dist/tokens/components/masonry.css +6 -0
  77. package/dist/tokens/components/mega-menu.css +71 -0
  78. package/dist/tokens/components/navigation.css +37 -13
  79. package/dist/tokens/components/segmented.css +4 -1
  80. package/dist/tokens/components/separator.css +4 -1
  81. package/dist/tokens/components/shell.css +99 -31
  82. package/dist/tokens/components/table.css +11 -5
  83. package/dist/tokens/components/thought-chain.css +4 -1
  84. package/dist/tokens/components/toggle.css +4 -1
  85. package/dist/tokens/components/tree.css +1 -1
  86. package/dist/tokens/components/upload.css +21 -9
  87. package/dist/tokens/foundation.css +35 -30
  88. package/dist/tokens/semantic/layout.css +26 -5
  89. package/docs/COMPOSITION-VS-COMPONENT.md +19 -1
  90. package/docs/DESIGN-AUTHORITY.md +99 -18
  91. package/docs/FRAME-COVERAGE-REPORT.md +7 -2
  92. package/docs/TOKENS.md +16 -1
  93. package/docs/data-display/marquee.tsx +254 -0
  94. package/docs/foundation/_theme-editor-scope.ts +222 -0
  95. package/docs/foundation/density.tsx +12 -2
  96. package/docs/foundation/spacing.tsx +5 -0
  97. package/docs/foundation/theme-editor.tsx +645 -0
  98. package/docs/general/activity.tsx +65 -0
  99. package/docs/general/reveal.tsx +290 -22
  100. package/docs/general/typography.tsx +91 -1
  101. package/docs/layout/affix.tsx +209 -0
  102. package/docs/layout/masonry.tsx +291 -0
  103. package/docs/navigation/anchor.tsx +285 -0
  104. package/docs/navigation/mega-menu-panel.tsx +86 -0
  105. package/docs/navigation/mega-menu.tsx +254 -0
  106. package/docs/roadmap/website-components.md +779 -0
  107. package/docs/showcase/acme-website.tsx +75 -39
  108. package/docs/showcase/case4-login.tsx +10 -2
  109. package/docs/showcase/case5-shift-calendar.tsx +1 -1
  110. package/docs/showcase/case6-agency-handy.tsx +6 -6
  111. package/docs/showcase/futurelastic-web.tsx +89 -49
  112. package/docs/showcase/marketing-page.tsx +885 -0
  113. package/docs/showcase/table-expandable-rows.tsx +4 -1
  114. package/docs/showcase/table-footer-totals.tsx +12 -2
  115. package/docs/showcase/theme-customization.tsx +1259 -0
  116. package/package.json +6 -3
  117. package/scripts/brand-accent.generated.mjs +27 -0
  118. package/scripts/ui-audit.mjs +66 -0
  119. 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.7.0",
4
- "godxUiMcp": "28.7.0",
3
+ "version": "28.9.0",
4
+ "godxUiMcp": "28.9.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,9 @@
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",
425
+ "check:frame-token-scope": "node scripts/check-frame-token-scope.mjs"
423
426
  },
424
427
  "peerDependencies": {
425
428
  "@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
+ ];
@@ -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
- /** @returns {boolean} true when an accent surface exceeds the 渋み chroma limit. */
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