@godxjp/ui 33.22.2 → 33.25.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 (130) hide show
  1. package/DESIGN.md +1 -1
  2. package/README.md +16 -5
  3. package/agent/DESIGN.md +1 -1
  4. package/agent/START-HERE.md +8 -8
  5. package/agent/components/AgentBadge.json +43 -0
  6. package/agent/components/AppShell.json +6 -0
  7. package/agent/components/Avatar.json +7 -0
  8. package/agent/components/CatalogDetail.json +7 -3
  9. package/agent/components/DatePicker.json +1 -1
  10. package/agent/components/DropdownMenu.json +1 -0
  11. package/agent/components/EditorialCard.json +2 -1
  12. package/agent/components/Image.json +6 -1
  13. package/agent/components/PageContainer.json +5 -0
  14. package/agent/components/PageErrorBoundary.json +48 -0
  15. package/agent/components/Pagination.json +5 -0
  16. package/agent/components/Popover.json +1 -0
  17. package/agent/components/Select.json +2 -1
  18. package/agent/components/Toaster.json +2 -2
  19. package/agent/components/Toolbar.json +1 -1
  20. package/agent/components/Topbar.json +1 -1
  21. package/agent/components-index.json +10 -0
  22. package/agent/components.json +138 -11
  23. package/agent/index.json +8 -8
  24. package/agent/llms.txt +7 -7
  25. package/agent/patterns/notice-bar.json +16 -0
  26. package/agent/patterns-index.json +15 -0
  27. package/agent/patterns.json +16 -0
  28. package/agent/rules.json +2 -2
  29. package/agent/tokens.json +121 -7
  30. package/dist/components/charts/chart-cartesian.js +5 -1
  31. package/dist/components/charts/chart-summary.d.ts +1 -1
  32. package/dist/components/charts/chart-summary.js +17 -6
  33. package/dist/components/charts/compact-bar-trend.js +4 -3
  34. package/dist/components/charts/pie-chart.js +2 -1
  35. package/dist/components/data-display/avatar.d.ts +1 -1
  36. package/dist/components/data-display/avatar.js +7 -1
  37. package/dist/components/data-display/badge.d.ts +11 -0
  38. package/dist/components/data-display/badge.js +20 -0
  39. package/dist/components/data-display/image.d.ts +1 -0
  40. package/dist/components/data-display/image.js +10 -2
  41. package/dist/components/data-display/index.d.ts +2 -2
  42. package/dist/components/data-display/index.js +2 -1
  43. package/dist/components/data-display/popover.js +11 -0
  44. package/dist/components/data-entry/calendar.js +1 -1
  45. package/dist/components/data-entry/date-picker.js +22 -5
  46. package/dist/components/data-entry/record-picker.js +122 -98
  47. package/dist/components/data-entry/select.js +13 -3
  48. package/dist/components/general/button.js +2 -2
  49. package/dist/components/layout/affix.js +1 -1
  50. package/dist/components/layout/app-shell.d.ts +1 -1
  51. package/dist/components/layout/app-shell.js +3 -1
  52. package/dist/components/layout/editorial-catalog.d.ts +1 -1
  53. package/dist/components/layout/editorial-catalog.js +53 -15
  54. package/dist/components/layout/index.d.ts +2 -0
  55. package/dist/components/layout/index.js +2 -0
  56. package/dist/components/layout/page-container.d.ts +1 -1
  57. package/dist/components/layout/page-container.js +6 -3
  58. package/dist/components/layout/page-error-boundary.d.ts +32 -0
  59. package/dist/components/layout/page-error-boundary.js +57 -0
  60. package/dist/components/layout/sidebar.d.ts +1 -1
  61. package/dist/components/layout/sidebar.js +100 -49
  62. package/dist/components/layout/topbar.js +108 -21
  63. package/dist/components/navigation/dropdown-menu.d.ts +13 -3
  64. package/dist/components/navigation/dropdown-menu.js +36 -6
  65. package/dist/components/navigation/pagination.d.ts +1 -1
  66. package/dist/components/navigation/pagination.js +13 -1
  67. package/dist/components/navigation/toolbar.js +2 -1
  68. package/dist/contracts/measurement.json +1 -1
  69. package/dist/i18n/messages/en.json +7 -0
  70. package/dist/i18n/messages/ja.json +7 -0
  71. package/dist/i18n/messages/vi.json +7 -0
  72. package/dist/props/components/data-display.prop.d.ts +29 -0
  73. package/dist/props/components/layout.prop.d.ts +52 -2
  74. package/dist/props/components/navigation.prop.d.ts +15 -1
  75. package/dist/props/registry.d.ts +65 -5
  76. package/dist/props/registry.js +90 -4
  77. package/dist/styles/alert-layout.css +4 -0
  78. package/dist/styles/application.css +24 -0
  79. package/dist/styles/badge-layout.css +14 -0
  80. package/dist/styles/base.css +11 -0
  81. package/dist/styles/control.css +32 -19
  82. package/dist/styles/core.css +1 -0
  83. package/dist/styles/data-display-layout.css +51 -0
  84. package/dist/styles/dialog-layout.css +9 -0
  85. package/dist/styles/editorial-layout.css +2076 -0
  86. package/dist/styles/editorial.css +3 -0
  87. package/dist/styles/icon-layout.css +3 -0
  88. package/dist/styles/index.css +1 -0
  89. package/dist/styles/layers.json +60 -9
  90. package/dist/styles/layout.css +9 -1985
  91. package/dist/styles/precompiled/alert-layout.css +2 -0
  92. package/dist/styles/precompiled/badge-layout.css +2 -0
  93. package/dist/styles/precompiled/base.css +2 -0
  94. package/dist/styles/precompiled/card-layout.css +2 -0
  95. package/dist/styles/precompiled/chart-layout.css +2 -0
  96. package/dist/styles/precompiled/control.css +2 -0
  97. package/dist/styles/precompiled/data-display-layout.css +2 -0
  98. package/dist/styles/precompiled/data-entry-layout.css +2 -0
  99. package/dist/styles/precompiled/dialog-layout.css +2 -0
  100. package/dist/styles/precompiled/editorial-account-layout.css +2 -0
  101. package/dist/styles/precompiled/editorial-layout.css +2 -0
  102. package/dist/styles/precompiled/float-button-layout.css +2 -0
  103. package/dist/styles/precompiled/form-layout.css +2 -0
  104. package/dist/styles/precompiled/icon-layout.css +2 -0
  105. package/dist/styles/precompiled/layout.css +2 -0
  106. package/dist/styles/precompiled/logo-layout.css +2 -0
  107. package/dist/styles/precompiled/motion.css +2 -0
  108. package/dist/styles/precompiled/navigation-layout.css +2 -0
  109. package/dist/styles/precompiled/shell-layout.css +2 -0
  110. package/dist/styles/precompiled/table-layout.css +2 -0
  111. package/dist/styles/precompiled/text-layout.css +2 -0
  112. package/dist/styles/precompiled/toggle.css +2 -0
  113. package/dist/styles/precompiled/vendor-day-picker.css +2 -0
  114. package/dist/styles/precompiled/vendor-sonner.css +2 -0
  115. package/dist/styles/shell-layout.css +17 -1
  116. package/dist/styles/table-layout.css +1 -1
  117. package/dist/styles.css +1 -1
  118. package/dist/tokens/components/badge.css +3 -0
  119. package/dist/tokens/components/control.css +3 -0
  120. package/dist/tokens/components/data-display.css +9 -0
  121. package/dist/tokens/components/editorial.css +9 -6
  122. package/dist/tokens/components/shell.css +2 -0
  123. package/dist/tokens/foundation.css +8 -0
  124. package/docs/CONSUMER-RULES.md +36 -1
  125. package/package.json +4 -2
  126. package/scripts/consumer-rule.md +1 -1
  127. package/scripts/prune-css.mjs +56 -8
  128. package/scripts/ui-audit.mjs +36 -0
  129. package/skills/godxjp-ui-navigation/SKILL.md +14 -9
  130. package/skills/godxjp-ui-navigation/references/principles.md +2 -2
@@ -13,6 +13,9 @@
13
13
  --badge-min-height: initial;
14
14
  --badge-outline-foreground: initial;
15
15
 
16
+ --badge-agent-border: initial;
17
+ --badge-agent-foreground: initial;
18
+
16
19
  --badge-icon-size: var(--icon-size-xs);
17
20
 
18
21
  --badge-space-x-removable: var(--space-1);
@@ -321,6 +321,9 @@
321
321
 
322
322
  --select-content-max-height: 24rem;
323
323
 
324
+ --select-content-max-width: 32rem;
325
+ --select-content-viewport-inset: var(--space-6);
326
+
324
327
  --select-content-inline-size: auto;
325
328
 
326
329
  --select-content-background: initial;
@@ -144,6 +144,15 @@
144
144
  --avatar-presence-busy-color: initial;
145
145
  --avatar-presence-offline-color: initial;
146
146
 
147
+ --avatar-agent-font-size: initial;
148
+ --avatar-agent-font-weight: initial;
149
+ --avatar-agent-padding-inline: initial;
150
+ --avatar-agent-inset: initial;
151
+ --avatar-agent-ring-width: initial;
152
+ --avatar-agent-background: initial;
153
+ --avatar-agent-foreground: initial;
154
+ --avatar-agent-ring-color: initial;
155
+
147
156
  --progress-track-background: initial;
148
157
  --progress-track-background-alpha: initial;
149
158
  --progress-fill-background: initial;
@@ -208,7 +208,7 @@
208
208
  --editorial-search-border-color: initial;
209
209
 
210
210
  --editorial-search-background: initial;
211
- --editorial-search-shadow: 0 4px 16px rgb(var(--shadow-color, 0 0 0) / 0.06);
211
+ --editorial-search-shadow: initial;
212
212
  --editorial-search-icon-size: var(--icon-size-lg);
213
213
 
214
214
  --editorial-search-scope-font-weight: inherit;
@@ -232,7 +232,7 @@
232
232
  --editorial-search-popup-max-height: min(32rem, 70vh);
233
233
  --editorial-search-popup-padding-block: calc(var(--space-1) + 0.125rem);
234
234
  --editorial-search-popup-radius: var(--radius);
235
- --editorial-search-popup-shadow: 0 12px 32px rgb(var(--shadow-color, 0 0 0) / 0.12);
235
+ --editorial-search-popup-shadow: initial;
236
236
 
237
237
  --editorial-search-popup-border-color: initial;
238
238
 
@@ -317,7 +317,7 @@
317
317
  --editorial-showcase-caption-gap: calc(var(--space-3) + 0.0625rem);
318
318
  --editorial-showcase-caption-min-width: 16.25rem;
319
319
  --editorial-showcase-caption-radius: var(--radius);
320
- --editorial-showcase-caption-shadow: 0 4px 16px rgb(var(--shadow-color, 0 0 0) / 0.06);
320
+ --editorial-showcase-caption-shadow: initial;
321
321
 
322
322
  --editorial-showcase-caption-icon-size: calc(var(--icon-size-xl) + 0.25rem);
323
323
 
@@ -365,7 +365,7 @@
365
365
  --editorial-banner-media-align: start;
366
366
 
367
367
  --editorial-catalog-card-radius: var(--radius);
368
- --editorial-catalog-card-hover-shadow: 0 10px 28px rgb(var(--shadow-color, 0 0 0) / 0.1);
368
+ --editorial-catalog-card-hover-shadow: initial;
369
369
  --editorial-catalog-card-hover-translate: -3px;
370
370
  --editorial-catalog-card-media-aspect-size: 1 / 1.05;
371
371
  --editorial-catalog-card-media-padding-block-start: var(--space-12);
@@ -450,7 +450,7 @@
450
450
  --editorial-spotlight-media-inset-block-start: 2rem;
451
451
  --editorial-spotlight-media-offset-angle: 12deg;
452
452
  --editorial-spotlight-media-shadow: 0 6px 6px rgb(40 25 20 / 0.12);
453
- --editorial-spotlight-hover-shadow: 0 4px 16px rgb(var(--shadow-color, 0 0 0) / 0.06);
453
+ --editorial-spotlight-hover-shadow: initial;
454
454
 
455
455
  --editorial-journal-cover-height: 13.25rem;
456
456
 
@@ -490,7 +490,7 @@
490
490
  --editorial-journal-arrow-size: 2rem;
491
491
  --editorial-journal-arrow-icon-size: var(--icon-size-md);
492
492
  --editorial-journal-focus-offset: -4px;
493
- --editorial-journal-hover-shadow: 0 12px 28px rgb(var(--shadow-color, 0 0 0) / 0.08);
493
+ --editorial-journal-hover-shadow: initial;
494
494
 
495
495
  --editorial-masthead-height: 5.5rem;
496
496
  --editorial-masthead-gap: var(--space-8);
@@ -603,6 +603,9 @@
603
603
  --editorial-catalog-detail-art-height: auto;
604
604
  --editorial-catalog-detail-art-max-width: none;
605
605
  --editorial-catalog-detail-art-padding: var(--space-10);
606
+ --editorial-catalog-detail-loading-background: initial;
607
+ --editorial-catalog-detail-loading-duration: var(--duration-loop);
608
+ --editorial-catalog-detail-loading-alpha: 0.5;
606
609
  --editorial-catalog-detail-art-radius: calc(var(--radius) * 2);
607
610
  --editorial-catalog-detail-media-width: 15.625rem;
608
611
  --editorial-catalog-detail-media-height: 22.5rem;
@@ -100,6 +100,8 @@
100
100
  --app-shell-bar-inset-compact: var(--space-page-compact-x);
101
101
  --app-shell-bar-gap: var(--space-3);
102
102
 
103
+ --app-shell-rail-gap: var(--space-3);
104
+
103
105
  --app-shell-brand-max-inline-size: none;
104
106
  --app-shell-brand-compact-max-inline-size: var(--app-shell-bar-height);
105
107
 
@@ -41,6 +41,10 @@
41
41
  --attention: 25 99% 46%;
42
42
  --attention-foreground: 48 8% 13%;
43
43
 
44
+ --agent: 262 83% 58%;
45
+ --agent-foreground: 0 0% 100%;
46
+ --text-agent: 263 70% 50%;
47
+
44
48
  --brand: 240 6% 10%;
45
49
  --brand-foreground: 60 33% 99%;
46
50
 
@@ -383,6 +387,10 @@
383
387
  --attention: 25 95% 56%;
384
388
  --attention-foreground: 48 9% 9%;
385
389
 
390
+ --agent: 262 83% 58%;
391
+ --agent-foreground: 0 0% 100%;
392
+ --text-agent: 252 95% 85%;
393
+
386
394
  --brand: 0 0% 98%;
387
395
  --brand-foreground: 48 9% 9%;
388
396
 
@@ -4,10 +4,37 @@ Read this once; the audit enforces it. What the audit cannot see (how a screen i
4
4
  should look like) is in the package's `DESIGN.md`. Everything else in `docs/` is for contributors.
5
5
 
6
6
  1. Load styles with `import "@godxjp/ui/styles.css"` (precompiled, no Tailwind needed) or `@import "@godxjp/ui/styles"` (Tailwind v4). Neither ships font files: fonts are opt-in since v32 (`pnpm add @fontsource/noto-sans-jp @fontsource/m-plus-2`, then `@import "@godxjp/ui/styles/fonts.css"` after the styles import; the GoDX preset does this for you). Never cherry-pick `*-layout.css`.
7
+
8
+ **Supported opt-out for Tailwind v4 (#1475):** replace `@import "@godxjp/ui/styles"` with
9
+ `@import "@godxjp/ui/styles/application"` in apps that render no editorial, catalog or holding
10
+ components. It retains foundation, shell, controls, tables, forms, navigation, charts and vendor
11
+ styles. `styles` and `styles/core` retain every family for compatibility. If editorial components
12
+ are added later, return to `styles`, or import `styles/editorial` after `styles/application`.
13
+ `styles/editorial` is additive: it requires the application entry. All entries bring no bundled fonts.
14
+
15
+ For a smaller, content-aware build, run the package-owned pruner instead of guessing layers:
16
+
17
+ ```sh
18
+ npx @godxjp/ui prune-css "src/**/*.tsx" --exclude-family editorial --out src/godx-ui.css
19
+ ```
20
+
21
+ Import the generated file instead of `@godxjp/ui/styles`; keep your Tailwind `@source` declarations.
22
+ The optional `--exclude-family editorial` fails if a detected component needs that family. Named
23
+ imports are required for a useful slice; namespace/dynamic imports conservatively retain all layers.
24
+ Both the family entries and the pruner use the generated `styles/layers.json` dependency map.
25
+ Re-run after component imports change and after upgrading the package. Never edit generated imports.
26
+
27
+ **Without Tailwind (#1455):** use the same command with `--precompiled`. The generated file
28
+ resolves package-owned plain CSS slices through public exports; bundle its CSS imports with your
29
+ existing CSS bundler (Vite, webpack or esbuild). It requires no Tailwind compiler. A browser alone
30
+ cannot resolve npm package specifiers. Fonts stay opt-in (`--fonts`). The packed-contract gate
31
+ installs the tarball outside the repository and builds both modes; reproduce its measured byte
32
+ counts with `pnpm build && node scripts/check-packed-css-pruning.mjs`.
33
+
7
34
  2. **Mount `<AppProvider defaultLocale="…">` once, at the root.** Without it the kit's OWN strings (dialog close/cancel/delete, counters, picker labels) fall to the resting locale (`<html lang>` when it names a registered locale, else `en`) and pickers to UTC (gh#1005). A development build now warns once when a kit component renders outside it; `syncI18nLocale(locale, fallback)` is the escape for an app that truly cannot mount the provider. Every page is `<PageContainer title extra footer>` (a `subtitle` only when it adds a fact the title does not); its sections are spaced by the page. Group items inside a section with `<Flex direction="col" gap>` or `<ResponsiveGrid>`.
8
35
  3. No Tailwind layout on your own elements: no `flex`, `grid`, `gap-*`, `p-*`, `m-*`, `space-*`. Rows are `<Flex>` (default row), stacks are `<Flex direction="col">`, grids are `<ResponsiveGrid>`. A list's filters are ONE `<FilterBar>` that wraps at every width — never a sideways-scrolling strip of filters or filter chips; past 2–4 filters the rest go behind one "More filters" `Sheet` (cardinal rule #51, pattern `list-filters`, audit rules `filter-bar-no-overflow`, `filter-strip-no-scroll` and `hand-rolled-filter-bar`). Filters are packed at the start: never a hand-built `<Flex justify="between">` of FormFields above a table. Many conditions or an explicit 検索 button → `SearchForm` (pattern `search-form`); field × operator rows saved as views → pattern `condition-builder`.
9
36
  4. No hand-rolled surfaces: no `rounded-* border bg-*` divs. A box is `Card`, a pill is `Badge`, a person is `Avatar`, a row is `ListRow`, a label/value pair is `Descriptions`, an empty area is `EmptyState`, a read-only sample of a colour the USER chose is `Swatch`. A LIST of those rows is `<Flex as="ul" marker="none" direction="col" gap="none">` with `<ListRow as="li">` children — `marker="none"` keeps the `<ul>`, the `<li>` semantics and the gap token while dropping the bullet and the indent. Never a raw `<ul>`/`<ol>` (it carries no gap token), never `<div role="list">` + `<div role="listitem">`, and never a wrapper around each row: the divider is `:not(:last-child)` among siblings, so a row alone in its own wrapper is always the last one and EVERY divider disappears silently. A bulleted prose list is the same `<Flex as="ul">` without `marker`.
10
- 5. Real controls only: `Button`, `Input`, `Select`, `Textarea`, `Checkbox`… never raw `<button>`/`<input>`; a labelled control lives in `<FormField label>`. A Select outside a form takes `width="auto"`. **A disabled control's reason is visible text, never a tooltip** — see below. **Fields live in a `<Form layout="horizontal" labelWidth controlWidth>`** — never a group of `FormField`s without one, never a hand-rolled `<Flex>` row of fields (a row that belongs together is `<Flex attached>` or `<Form columns>`); size each control for its content with `controlWidth` (GOV.UK text-input width: a port ~7rem, a short enum ~10rem); three or more fields is a page, not a Dialog. The audit rules are `formfield-needs-form`, `dialog-form-too-big` and `select-width-hint`. **A case the kit cannot express is an issue, not a hand-roll:** open it on godx-jp/godxjp-ui, use the nearest valid composition, and mark it `// TODO(godxjp-ui#<n>)` (gh#998). **One action row, one Button size:** every `Button` under one parent takes the same `size` — a missing size is `default`, and `icon-sm` pairs with `sm`, `icon-xs` with `xs`, `icon` with default. Never `size="sm"` beside a default Button. The audit rules are `mixed-button-size` (static) and `mixed-button-height` (visual-audit, within 0.5px) (gh#1084).
37
+ 5. Real controls only: `Button`, `Input`, `Select`, `Textarea`, `Checkbox`… never raw `<button>`/`<input>`; a labelled control lives in `<FormField label>`. A Select outside a form takes `width="auto"`. **A disabled control's reason is visible text, never a tooltip** — see below. **Fields live in a `<Form>`** — never a group of `FormField`s without one, never a hand-rolled `<Flex>` row of fields (a row that belongs together is `<Flex attached>` or `<Form columns>`). `layout="horizontal"` with `labelWidth`/`controlWidth` is the desktop default; `layout="vertical"` (labels above the inputs, as GOV.UK and the W3C WAI forms tutorial show) is equally valid when labels are long, the container is narrow, or the page asks one short question per row (gh#1496). Size each control for its content with `controlWidth` (GOV.UK text-input width: a port ~7rem, a short enum ~10rem). **GoDX house rule:** a form with three or more fields is its own page, not a Dialog (audit `dialog-form-too-big`) — a product decision for task focus and recovery, not a WCAG requirement. The audit rules are `formfield-needs-form`, `dialog-form-too-big` and `select-width-hint`. **A case the kit cannot express is an issue, not a hand-roll:** open it on godx-jp/godxjp-ui, use the nearest valid composition, and mark it `// TODO(godxjp-ui#<n>)` (gh#998). **One action row, one Button size:** every `Button` under one parent takes the same `size` — a missing size is `default`, and `icon-sm` pairs with `sm`, `icon-xs` with `xs`, `icon` with default. Never `size="sm"` beside a default Button. The audit rules are `mixed-button-size` (static) and `mixed-button-height` (visual-audit, within 0.5px) (gh#1084).
11
38
  6. Text is `<Text>` / `<Heading>` with `tone`, `size`, `weight`, `truncate`, `mono` — not `className="text-muted-foreground font-semibold"`. A STANDALONE glyph is `<Icon as={Lock} size="sm" tone="muted" />` from `@godxjp/ui/general`: a bare lucide icon carries its own `width="24" height="24"` and only four places re-size it for you (`Button`, a `DropdownMenuItem`, a `TopbarItem`, a `ListRow` leading slot), so everywhere else — in a `Text`, a table cell, an `<a>` — it draws at 24px beside 14px type. `size-4` and `w-[16px]` are closed to you by rules 3 and 8, and `size={16}` hard-codes a number the theme owns; `Icon` puts the glyph on the `--icon-size-*` scale (`2xs…4xl`) instead, and is `aria-hidden` unless you give it a `label` (then it is `role="img"` with that name). The audit rule is `lucide-icon-needs-size`.
12
39
  7. Colours are semantic tokens (`tone="destructive"`, `bg-primary`), never palette names, hex, or `bg-black` / `text-white`.
13
40
  8. Sizes come from props (`size`, `width`, `columns`), never `w-[240px]` / `max-h-[420px]`.
@@ -54,3 +81,11 @@ information belongs on the screen:
54
81
  If the control is disabled _pending something the user can fix_, prefer leaving it enabled and
55
82
  explaining on submit: an inert control with a paragraph beside it is often a validation message
56
83
  wearing a disguise.
84
+
85
+ ## Floating content and toast imports
86
+
87
+ Use `DropdownMenuContent` only for menu collection nodes (`DropdownMenuItem`, checkbox/radio items, groups, labels and separators). Arbitrary notification headers, lists and links use `Popover` + `ListRow`. Development diagnostics and `dropdown-menu-non-menu-children` identify unsupported DOM children. Opaque components returning menu items remain supported.
88
+
89
+ `PopoverContent` scrolls automatically when the available viewport height caps it; focus rings stay inside the scroll boundary, including `flush` content.
90
+
91
+ Always use `import { toast } from "@godxjp/ui"` (or `@godxjp/ui/feedback`) with the kit's `Toaster`. Do not import toast directly from `sonner`: an app-installed version can create a separate toast store and calls never reach the kit's Toaster. The `no-direct-sonner-import` audit rule flags runtime imports. Sonner remains a dependency; no peer dependency migration is required.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@godxjp/ui",
3
- "version": "33.22.2",
4
- "godxUiMcp": "33.22.2",
3
+ "version": "33.25.0",
4
+ "godxUiMcp": "33.25.0",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",
@@ -81,6 +81,8 @@
81
81
  "import": "./dist/lab/index.js"
82
82
  },
83
83
  "./styles": "./dist/styles/index.css",
84
+ "./styles/application": "./dist/styles/application.css",
85
+ "./styles/editorial": "./dist/styles/editorial.css",
84
86
  "./styles/core": "./dist/styles/core.css",
85
87
  "./styles/core-with-fallbacks": "./dist/styles/core-with-fallbacks.css",
86
88
  "./styles/core-with-jis-level1": "./dist/styles/core-with-jis-level1.css",
@@ -109,7 +109,7 @@ godx-mailer dính bốn lỗi form trong một ngày, cả bốn đều là kit
109
109
 
110
110
  | Sai | Đúng | rule |
111
111
  | ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ---------------------- |
112
- | ≥2 `FormField` không có `Form`, hoặc xếp hàng bằng `<Flex>` | `<Form layout="horizontal" labelWidth controlWidth>`; hàng = `<Flex attached>` / `Form columns` | `formfield-needs-form` |
112
+ | ≥2 `FormField` không có `Form`, hoặc xếp hàng bằng `<Flex>` | `<Form>` (`layout="horizontal" labelWidth controlWidth` là mặc định desktop; `layout="vertical"` hợp lệ khi nhãn dài/khung hẹp); hàng = `<Flex attached>` / `Form columns` | `formfield-needs-form` |
113
113
  | ≥3 `FormField` trong `DialogBody` | page riêng (route riêng) | `dialog-form-too-big` |
114
114
  | `Select` hai lựa chọn kéo full width | `controlWidth` trên field hoặc một lần trên `Form` | `select-width-hint` |
115
115
 
@@ -40,18 +40,25 @@ const args = process.argv.slice(2);
40
40
  if (args.includes("--help") || args.includes("-h") || args.length === 0) {
41
41
  console.log(`prune-css — emit a stylesheet with only the @godxjp/ui CSS layers your app uses
42
42
 
43
- usage: node node_modules/@godxjp/ui/scripts/prune-css.mjs <src dir/glob …> [--out <file>] [--fonts]
43
+ usage: node node_modules/@godxjp/ui/scripts/prune-css.mjs <src dir/glob …> [--out <file>] [--fonts] [--precompiled]
44
44
 
45
45
  <src dir/glob …> where your app's source lives (directories are walked; globs expand)
46
46
  --out <file> where to write the css (default: godx-ui.css in the current directory)
47
+ --exclude-family <name> fail if detected components require the omitted family (e.g. editorial)
47
48
  --fonts include the bundled M PLUS 2 / Noto Sans JP @font-face declarations
48
49
  (default mirrors @godxjp/ui/styles/core: none — bring your own fonts)
50
+ --precompiled import the precompiled slices (plain CSS, dist/styles/precompiled/) instead
51
+ of the Tailwind sources: for apps that use @godxjp/ui/styles.css and have
52
+ no Tailwind compiler (gh#1455)
49
53
 
50
- Then import the emitted file INSTEAD of "@godxjp/ui/styles" and re-run this command whenever
54
+ Then import the emitted file INSTEAD of "@godxjp/ui/styles" (or "@godxjp/ui/styles.css" with
55
+ --precompiled) and re-run this command whenever
51
56
  the set of components you use changes, and after every @godxjp/ui upgrade.`);
52
57
  process.exit(0);
53
58
  }
54
59
 
60
+ const excludeFlag = args.indexOf("--exclude-family");
61
+ const excludedFamily = excludeFlag === -1 ? undefined : args[excludeFlag + 1];
55
62
  const outFlag = args.indexOf("--out");
56
63
  const OUT = outFlag !== -1 ? args[outFlag + 1] : "godx-ui.css";
57
64
  if (outFlag !== -1 && (!OUT || OUT.startsWith("--"))) {
@@ -59,8 +66,12 @@ if (outFlag !== -1 && (!OUT || OUT.startsWith("--"))) {
59
66
  process.exit(2);
60
67
  }
61
68
  const FONTS = args.includes("--fonts");
69
+ const PRECOMPILED = args.includes("--precompiled");
62
70
  const positionals = args.filter(
63
- (a, i) => !a.startsWith("--") && (outFlag === -1 || i !== outFlag + 1),
71
+ (a, i) =>
72
+ !a.startsWith("--") &&
73
+ (outFlag === -1 || i !== outFlag + 1) &&
74
+ (excludeFlag === -1 || i !== excludeFlag + 1),
64
75
  );
65
76
 
66
77
  // ── the package's own graph, version-locked ──────────────────────────────────
@@ -85,6 +96,13 @@ if (manifest.version !== pkg.version) {
85
96
  process.exit(2);
86
97
  }
87
98
 
99
+ if (excludeFlag !== -1 && !Object.hasOwn(manifest.families ?? {}, excludedFamily ?? "")) {
100
+ console.error(
101
+ `prune-css: unknown --exclude-family ${excludedFamily ?? "(missing)"}; choose ${Object.keys(manifest.families ?? {}).join(", ")}`,
102
+ );
103
+ process.exit(2);
104
+ }
105
+
88
106
  // ── scan consumer sources ────────────────────────────────────────────────────
89
107
 
90
108
  const SCANNABLE = /\.(tsx|jsx|ts|mjs|js|cjs|mts)$/;
@@ -184,9 +202,35 @@ for (const name of used) {
184
202
  for (const v of entry.vendor ?? []) vendors.add(v);
185
203
  }
186
204
 
205
+ if (excludedFamily && manifest.families[excludedFamily].some((file) => layers.has(file))) {
206
+ console.error(
207
+ `prune-css: cannot exclude ${excludedFamily}: detected components require it. Remove their imports or keep the family.`,
208
+ );
209
+ process.exit(2);
210
+ }
211
+
187
212
  const keptLayers = manifest.order.filter((f) => layers.has(f));
188
213
  const keptVendors = manifest.vendor.filter((f) => vendors.has(f));
189
- const asImport = (f) => `@import "@godxjp/ui/styles/${f.replace(/\.css$/, "")}";`;
214
+ // --precompiled (gh#1455): the same slice, from the plain-CSS cut of styles.css that
215
+ // build-precompiled-css.mjs writes per manifest file. The fonts sheet has no Tailwind in it, so it
216
+ // is imported from its usual place either way.
217
+ const asImport = (f) =>
218
+ PRECOMPILED && f !== manifest.fonts
219
+ ? `@import "@godxjp/ui/styles/precompiled/${f.replace(/\.css$/, "")}";`
220
+ : `@import "@godxjp/ui/styles/${f.replace(/\.css$/, "")}";`;
221
+ if (PRECOMPILED) {
222
+ const missing = [...keptVendors, manifest.base, ...keptLayers].filter(
223
+ (f) => !existsSync(join(PKG_ROOT, "dist/styles/precompiled", f)),
224
+ );
225
+ if (missing.length) {
226
+ console.error(
227
+ `prune-css: --precompiled, but the installed @godxjp/ui ships no precompiled slice for ` +
228
+ `${missing.join(", ")} (dist/styles/precompiled/). Upgrade the package; inside the repo ` +
229
+ "itself, run `pnpm build` first.",
230
+ );
231
+ process.exit(2);
232
+ }
233
+ }
190
234
 
191
235
  const lines = [
192
236
  "/*",
@@ -194,17 +238,21 @@ const lines = [
194
238
  ` *`,
195
239
  ` * @godxjp/ui version: ${pkg.version}`,
196
240
  ` * components detected: ${[...used].sort().join(", ")}`,
197
- ` * layers: ${keptLayers.length} of ${manifest.order.length} (+base${FONTS ? "+fonts" : ", no bundled fonts — like styles/core"})`,
241
+ ` * layers: ${keptLayers.length} of ${manifest.order.length} (+base${FONTS ? "+fonts" : ", no bundled fonts — like styles/core"})${PRECOMPILED ? ", precompiled (no Tailwind needed)" : ""}`,
198
242
  ` *`,
199
243
  ` * Re-run when the components your app uses change, and after every @godxjp/ui upgrade:`,
200
- ` * node node_modules/@godxjp/ui/scripts/prune-css.mjs <src …> --out <this file>${FONTS ? " --fonts" : ""}`,
244
+ ` * node node_modules/@godxjp/ui/scripts/prune-css.mjs <src …> --out <this file>${FONTS ? " --fonts" : ""}${PRECOMPILED ? " --precompiled" : ""}`,
201
245
  ` *`,
202
246
  ` * Hand-editing this file is the cherry-picking the README forbids: layers share rules and`,
203
247
  ` * a missing one fails silently. Change your imports, then re-run the tool.`,
204
248
  " */",
205
249
  // Layer order must be declared before the first @import — same statement, same reasoning,
206
- // as styles/index.css (vendor between base and components; see the comment there).
207
- "@layer theme, base, vendor, components, utilities;",
250
+ // as styles/index.css (vendor between base and components; see the comment there). Precompiled
251
+ // CSS also carries Tailwind's `properties` layer (the @property fallbacks), which the compiler
252
+ // puts FIRST in styles.css; named here, it keeps that place instead of landing after utilities.
253
+ PRECOMPILED
254
+ ? "@layer properties, theme, base, vendor, components, utilities;"
255
+ : "@layer theme, base, vendor, components, utilities;",
208
256
  ...keptVendors.map(asImport),
209
257
  asImport(manifest.base),
210
258
  ...(FONTS ? [asImport(manifest.fonts)] : []),
@@ -366,6 +366,27 @@ const LIST_TAGS = new Set(["DataTable", "Table", "List", "ListRow"]);
366
366
  * CSS Logical Properties L1 (W3C) · HTML Living Standard (WHATWG).
367
367
  */
368
368
  const RULES = [
369
+ {
370
+ id: "no-direct-sonner-import",
371
+ replacement: "import { toast } from '@godxjp/ui'",
372
+ scope: "consumer",
373
+ severity: "error",
374
+ spansElement: true,
375
+ test: /\bimport\s+(?!type\b)(?:[^;"']*?\s+from\s*)?["']sonner["']/g,
376
+ message:
377
+ "import toast from @godxjp/ui — use `import { toast } from \"@godxjp/ui\"`; a consumer's sonner may own a different toast store from the kit's Toaster.",
378
+ },
379
+ {
380
+ id: "dropdown-menu-non-menu-children",
381
+ replacement: "Popover + ListRow",
382
+ scope: "consumer",
383
+ severity: "error",
384
+ spansElement: true,
385
+ matches: dropdownNonMenuMatches,
386
+ message:
387
+ "DropdownMenuContent accepts menu collection children only. Use DropdownMenuItem for actions; use Popover + ListRow for arbitrary headers, lists and links.",
388
+ },
389
+
369
390
  {
370
391
  id: "no-utility-spacing",
371
392
  replacement: "Flex gap / ResponsiveGrid / PageContainer",
@@ -2936,3 +2957,18 @@ if (filesScanned === 0 && !changedNoFiles) {
2936
2957
 
2937
2958
  // See the note above --rules: exitCode, so a large JSON report drains fully.
2938
2959
  if (filesScanned > 0 || changedNoFiles) process.exitCode = errors.length > 0 ? 1 : 0;
2960
+
2961
+ /** Known DOM children are invalid collection nodes; opaque wrappers are deliberately allowed. */
2962
+ function* dropdownNonMenuMatches(source) {
2963
+ const containers = new Set([
2964
+ "DropdownMenuContent",
2965
+ "DropdownMenuSubContent",
2966
+ "DropdownMenuGroup",
2967
+ "DropdownMenuRadioGroup",
2968
+ ]);
2969
+ for (const { tag, index, openTag, stack } of jsxElements(source)) {
2970
+ if (!/^[a-z]/.test(tag)) continue;
2971
+ const parent = [...stack].reverse().find((frame) => !frame.attr && frame.tag !== "");
2972
+ if (parent && containers.has(parent.tag)) yield { 0: openTag, index };
2973
+ }
2974
+ }
@@ -42,12 +42,16 @@ comparisons, domain vocabulary) in its own repository; they do not belong in thi
42
42
  ## 2. Hard rules
43
43
 
44
44
  **Count and form**
45
- - Phone: **bottom bar with 3–5 items** (the middle one may be a + quick-log button); everything else
46
- goes under "More". Never put setup on the bottom bar. Desktop: **an always-visible sidebar**, not
45
+ - Phone: **bottom bar with 3–5 destinations**; every slot navigates, never an action slot (see
46
+ principles §1.6 and rule 11). The #1 create action sits above the bar (a FAB) or in the top bar.
47
+ Everything else goes under "More". Never put setup on the bottom bar. Desktop: **an always-visible sidebar**, not
47
48
  only a ☰ button. Choose by window width, not by device. (@godxjp/ui's `MobileShell` has a bottom
48
49
  bar — use it; don't rely on a drawer alone.)
49
- - Sidebar at most **2 levels**, groups with short headings; each role sees **≤ ~7 primary items**.
50
- Anything deeper → list → detail inside the page.
50
+ - Keep the sidebar shallow (aim for **2 levels**, groups with short headings) and each role's primary
51
+ list short (**~7 items**). These numbers are **review signals, not pass/fail limits**: when a role
52
+ exceeds them, validate the tree with the role's real tasks (tree test or walkthrough) and split by
53
+ work area rather than hiding items in a catch-all group. Record data deeper than the menu →
54
+ list → detail inside the page.
51
55
 
52
56
  **Grouping and labels**
53
57
  - Group by **task / work area**. Never group by data table, org chart or developer vocabulary
@@ -59,8 +63,8 @@ comparisons, domain vocabulary) in its own repository; they do not belong in thi
59
63
  - **Each destination appears once** in the menu. Two items leading to the same kind of record = a
60
64
  defect.
61
65
  - Action buttons (Log/Create) are **not menu items**: one prominent primary button (top of the menu,
62
- or the + in the middle of the bottom bar) for the #1 task; child objects are created right inside
63
- their parent's page.
66
+ or a create button above the bottom bar — never inside a bar slot) for the #1 task; child objects
67
+ are created right inside their parent's page.
64
68
 
65
69
  **Per role**
66
70
  - Each role has its **own fixed landing page**: frontline staff → "Today" (due, overdue, my tasks);
@@ -99,14 +103,15 @@ An example only, for apps that run recurring checks across many sites:
99
103
  stay **permanently linked** to their source record.
100
104
  - Staff land on "Today" plus a separate place for "Overdue/Missed"; managers land on a rate table plus
101
105
  exceptions.
102
- - Phone bottom bar: 4–5 task items (e.g. Today · Inspections · +Log · Nonconformities · More).
106
+ - Phone bottom bar: 4–5 destinations (e.g. Today · Inspections · Nonconformities · More), with
107
+ + Log as a button above the bar.
103
108
  - Vocabulary: avoid "Reports"/"レポート" as the label for *records* (in English "Reports" means
104
109
  analytics; in Japanese "レポート" often means an individual record).
105
110
 
106
111
  ## 4. Self-check gate (before asking for approval / before merging)
107
112
 
108
- - [ ] A task table per role and a text menu tree per role exist; each role ≤ ~7 primary items; phone
109
- 3–5 items.
113
+ - [ ] A task table per role and a text menu tree per role exist; a role past ~7 primary items or 2
114
+ levels has a tree test or walkthrough behind it; phone 3–5 destinations, no action slot.
110
115
  - [ ] Walkthrough of 8–10 tasks per role: no step answered "no"; no pair of competing labels.
111
116
  - [ ] No duplicate destinations; no grouping by data table; label = H1 = `<title>`, consistent across
112
117
  every locale.
@@ -215,7 +215,7 @@ The Atlassian Design System's public navigation page and the Salesforce Lightnin
215
215
  | Breadcrumb as history, or a breadcrumb plus a back link | Breadcrumbs show hierarchy [P] NN/g; "Never use the back link… together with the Breadcrumbs" [P] GOV.UK |
216
216
  | Losing list position or filters on Back | "the page often resets to the top of the list" [P] NN/g saving scroll position |
217
217
  | Main navigation in the page header, or repeated in the page body | "Avoid placing the main navigation in the page header"; avoid "replicating the app nav content in the app body" [P] Shopify |
218
- | More than 2 levels of sidebar or cascading menus | [P] HIG sidebars; Carbon; Fluent; NN/g menu design |
218
+ | More than 2 levels of sidebar or cascading menus, with no tree test or walkthrough behind them | [P] HIG sidebars; Carbon; Fluent; NN/g menu design |
219
219
  | Dead ends on object pages | Give paths onward from every object instance [Pr] OOUX (A List Apart) |
220
220
 
221
221
  ---
@@ -232,7 +232,7 @@ The Atlassian Design System's public navigation page and the Salesforce Lightnin
232
232
  | 6 | Labels are short nouns in the users' words, 1–2 words, never coined terms. | https://shopify.dev/docs/apps/design/navigation ; https://www.nngroup.com/articles/top-10-ia-mistakes/ |
233
233
  | 7 | Every navigation item has a visible text label. Icons only support the label. Never truncate. | https://m3.material.io/components/navigation-bar/guidelines |
234
234
  | 8 | Order by frequency of use measured from users (top-tasks vote or field study). Positions stay fixed. | https://www.nngroup.com/articles/progressive-disclosure/ ; https://www.w3.org/WAI/WCAG22/Understanding/consistent-navigation.html |
235
- | 9 | Sidebar: at most 2 levels, with short group headings. Deeper data goes into list → detail inside the page. | https://developer.apple.com/design/human-interface-guidelines/sidebars |
235
+ | 9 | Sidebar: aim for 2 levels, with short group headings — a review signal, not a pass/fail limit. A third level is for a work area that needs it, validated by a tree test or walkthrough; record data goes into list → detail inside the page. | https://developer.apple.com/design/human-interface-guidelines/sidebars |
236
236
  | 10 | Settings, admin and configuration live apart from daily work (utility area or admin section), behind a second level at most. | https://www.nngroup.com/articles/utility-navigation/ ; https://www.nngroup.com/articles/progressive-disclosure/ |
237
237
  | 11 | Navigation slots navigate; they never perform actions. Put Create in a FAB or top bar for the main object, and in context for children. | https://developer.apple.com/design/human-interface-guidelines/tab-bars |
238
238
  | 12 | Each role gets its own fixed start page, named after the work area, with task-oriented sections. | https://www.sap.com/design-system/fiori-design-web/v1-148/foundations/integration-and-services/sap-fiori-launchpad/sap-fiori-launchpad-spaces |