@godxjp/ui 17.0.0 → 18.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.
Files changed (78) hide show
  1. package/dist/components/data-display/card.d.ts +37 -1
  2. package/dist/components/data-display/card.js +7 -1
  3. package/dist/components/data-display/data-table.js +18 -3
  4. package/dist/components/data-display/empty-state.d.ts +1 -1
  5. package/dist/components/data-display/empty-state.js +5 -1
  6. package/dist/components/data-display/scroll-area.js +1 -1
  7. package/dist/components/data-display/table.js +14 -9
  8. package/dist/components/data-entry/calendar.js +4 -0
  9. package/dist/components/data-entry/cascader.d.ts +1 -1
  10. package/dist/components/data-entry/cascader.js +10 -1
  11. package/dist/components/data-entry/checkbox-group.d.ts +1 -1
  12. package/dist/components/data-entry/checkbox-group.js +13 -4
  13. package/dist/components/data-entry/color-picker.d.ts +1 -1
  14. package/dist/components/data-entry/color-picker.js +5 -2
  15. package/dist/components/data-entry/command.d.ts +2 -2
  16. package/dist/components/data-entry/date-picker.d.ts +1 -1
  17. package/dist/components/data-entry/date-picker.js +11 -1
  18. package/dist/components/data-entry/date-range-picker.d.ts +1 -1
  19. package/dist/components/data-entry/date-range-picker.js +11 -3
  20. package/dist/components/data-entry/form-field.js +7 -3
  21. package/dist/components/data-entry/month-picker.d.ts +1 -1
  22. package/dist/components/data-entry/month-picker.js +13 -1
  23. package/dist/components/data-entry/month-range-picker.d.ts +1 -1
  24. package/dist/components/data-entry/month-range-picker.js +11 -3
  25. package/dist/components/data-entry/number-input.d.ts +19 -2
  26. package/dist/components/data-entry/number-input.js +5 -7
  27. package/dist/components/data-entry/radio.d.ts +1 -1
  28. package/dist/components/data-entry/radio.js +11 -3
  29. package/dist/components/data-entry/search-input.d.ts +3 -2
  30. package/dist/components/data-entry/search-input.js +5 -2
  31. package/dist/components/data-entry/search-select.d.ts +1 -1
  32. package/dist/components/data-entry/search-select.js +51 -10
  33. package/dist/components/data-entry/select.d.ts +7 -0
  34. package/dist/components/data-entry/select.js +21 -3
  35. package/dist/components/data-entry/time-picker.d.ts +1 -1
  36. package/dist/components/data-entry/time-picker.js +11 -1
  37. package/dist/components/data-entry/transfer.d.ts +1 -1
  38. package/dist/components/data-entry/transfer.js +73 -59
  39. package/dist/components/data-entry/tree-select.d.ts +1 -1
  40. package/dist/components/data-entry/tree-select.js +10 -1
  41. package/dist/components/data-entry/upload.d.ts +1 -1
  42. package/dist/components/data-entry/upload.js +7 -2
  43. package/dist/components/layout/app-shell.d.ts +1 -1
  44. package/dist/components/layout/app-shell.js +42 -2
  45. package/dist/components/layout/breadcrumb.d.ts +7 -1
  46. package/dist/components/layout/breadcrumb.js +6 -2
  47. package/dist/components/layout/page-container.d.ts +1 -1
  48. package/dist/components/layout/page-container.js +32 -22
  49. package/dist/components/layout/responsive-grid.js +1 -1
  50. package/dist/components/layout/sidebar.d.ts +1 -1
  51. package/dist/components/layout/sidebar.js +44 -11
  52. package/dist/components/layout/split-pane.js +2 -2
  53. package/dist/components/navigation/app-setting-picker.js +8 -1
  54. package/dist/components/navigation/pagination.d.ts +1 -1
  55. package/dist/components/navigation/pagination.js +84 -95
  56. package/dist/components/navigation/tabs.js +21 -4
  57. package/dist/i18n/messages/en.json +3 -1
  58. package/dist/i18n/messages/ja.json +3 -1
  59. package/dist/i18n/messages/vi.json +3 -1
  60. package/dist/lib/field-a11y.d.ts +66 -0
  61. package/dist/lib/field-a11y.js +44 -0
  62. package/dist/props/components/data-display.prop.d.ts +16 -1
  63. package/dist/props/components/data-entry.prop.d.ts +69 -19
  64. package/dist/props/components/layout.prop.d.ts +42 -0
  65. package/dist/props/components/navigation.prop.d.ts +16 -0
  66. package/dist/props/registry.d.ts +8 -3
  67. package/dist/props/registry.js +21 -3
  68. package/dist/props/vocabulary/data.prop.d.ts +9 -0
  69. package/dist/props/vocabulary/interaction.prop.d.ts +1 -1
  70. package/dist/styles/base.css +1 -1
  71. package/dist/styles/layout.css +46 -9
  72. package/dist/styles/shell-layout.css +19 -0
  73. package/dist/styles/text-layout.css +1 -1
  74. package/dist/tokens/components/card.css +1 -1
  75. package/dist/tokens/foundation.css +3 -3
  76. package/package.json +8 -4
  77. package/scripts/ui-audit.mjs +2 -2
  78. package/scripts/visual-audit-rules.mjs +2 -2
@@ -13,6 +13,15 @@ export type OnRowClickProp<T> = (row: T) => void;
13
13
  export type ColumnDefProp<T> = {
14
14
  key: string;
15
15
  header: React.ReactNode;
16
+ /**
17
+ * Accessible header text for a column whose `header` is visually empty — the
18
+ * standard case for a row-actions or selection column. Rendered as an
19
+ * `sr-only` label inside the `<th>` so the column keeps a screen-reader name
20
+ * (e.g. "Actions" / "Select") while showing nothing on screen. Required
21
+ * (dev-warned) when `header` is empty; ignored when `header` has visible text.
22
+ * Route the string through `t()` for i18n.
23
+ */
24
+ ariaLabel?: string;
16
25
  render?: (row: T) => React.ReactNode;
17
26
  sortable?: boolean;
18
27
  width?: string;
@@ -11,7 +11,7 @@ export type ShapeProp = "default" | "pill" | "sharp";
11
11
  export type TextSizeProp = "2xs" | "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
12
12
  /** Text colour intent — maps to semantic foreground tokens (no raw palette). */
13
13
  export type TextToneProp = "default" | "muted" | "primary" | "success" | "warning" | "destructive" | "info";
14
- /** Font weight — the dxs-kintai canon is THREE weights only: `regular` (400 body), `medium` (500
14
+ /** Font weight — the reference-design canon is THREE weights only: `regular` (400 body), `medium` (500
15
15
  * heading/label), `bold` (700 emphasis). 600/`semibold` is forbidden. */
16
16
  export type FontWeightProp = "regular" | "medium" | "bold";
17
17
  /** Heading level — drives both the `--heading-h*` size token and the semantic `<h1..h4>` element. */
@@ -218,7 +218,7 @@
218
218
 
219
219
  /* Semantic heading scale — SEO + accessibility: real <h1>–<h6> elements carry
220
220
  * the document outline (one <h1> per page, levels never skipped). Visual size
221
- * follows the dxs-kintai scale; component titles override via their own class.
221
+ * follows the reference-design scale; component titles override via their own class.
222
222
  * Headings read --font-family-display (defaults to --font-family-sans → unchanged) for dual-font
223
223
  * brands (display face on headings, body face on prose). */
224
224
  :where(h1, h2, h3, h4, h5, h6) {
@@ -175,6 +175,16 @@
175
175
  gap: var(--space-inline-lg);
176
176
  }
177
177
 
178
+ /* ResponsiveGrid OWNS its query container. The component wraps the grid in
179
+ * `.ui-responsive-grid-scope` (container-type: inline-size, named `responsive-grid`) so the
180
+ * column breakpoints resolve against the WIDTH AVAILABLE TO THE GRID — not the viewport and not
181
+ * an undeclared ancestor `container-type`. This makes it correct inside a narrow card, a
182
+ * SplitPane aside, or a bare `<div>`; it can never silently stay one column for want of an
183
+ * external container context (gh#165). Named so it queries its own scope, never a closer one. */
184
+ .ui-responsive-grid-scope {
185
+ container: responsive-grid / inline-size;
186
+ }
187
+
178
188
  .ui-responsive-grid {
179
189
  display: grid;
180
190
  gap: var(--space-stack-md);
@@ -185,24 +195,33 @@
185
195
  min-width: 0;
186
196
  }
187
197
 
188
- @container (min-width: 640px) {
198
+ @container responsive-grid (min-width: 40rem) {
189
199
  .ui-responsive-grid {
190
200
  grid-template-columns: repeat(var(--responsive-grid-sm, 2), minmax(0, 1fr));
191
201
  }
192
202
  }
193
203
 
194
- @container (min-width: 768px) {
204
+ @container responsive-grid (min-width: 48rem) {
195
205
  .ui-responsive-grid {
196
206
  grid-template-columns: repeat(var(--responsive-grid-md, 3), minmax(0, 1fr));
197
207
  }
198
208
  }
199
209
 
200
- @container (min-width: 1024px) {
210
+ @container responsive-grid (min-width: 64rem) {
201
211
  .ui-responsive-grid {
202
212
  grid-template-columns: repeat(var(--responsive-grid-lg, 4), minmax(0, 1fr));
203
213
  }
204
214
  }
205
215
 
216
+ /* SplitPane OWNS its query container too — `.ui-split-pane-scope`
217
+ * (container-type: inline-size, named `split-pane`). The two-column layout is chosen from the
218
+ * pane's OWN available width, so a narrow embedded pane on a large viewport correctly stays a
219
+ * single column, and a wide pane on a small viewport can split (gh#165 — replaces the old
220
+ * `@media (min-width: 1080px)` viewport query that ignored the container). */
221
+ .ui-split-pane-scope {
222
+ container: split-pane / inline-size;
223
+ }
224
+
206
225
  .ui-split-pane {
207
226
  display: grid;
208
227
  gap: var(--space-stack-md);
@@ -259,7 +278,7 @@
259
278
  opacity: 0.5;
260
279
  }
261
280
 
262
- @media (min-width: 1080px) {
281
+ @container split-pane (min-width: 48rem) {
263
282
  .ui-split-pane[data-aside-width="sm"] {
264
283
  grid-template-columns: minmax(0, 1fr) 20rem;
265
284
  }
@@ -543,22 +562,30 @@
543
562
  }
544
563
 
545
564
  @layer components {
565
+ /* Pagination is ONE horizontal row and never wraps to a second desktop line (gh#153 — the old
566
+ * `flex-wrap: wrap` split the range / size-changer / page strip across rows). The number strip
567
+ * (`.ui-pagination-list`) absorbs any overflow by scrolling horizontally rather than wrapping; a
568
+ * genuinely narrow viewport uses the INTENTIONAL compact form via the `simple` prop (Prev · n/N ·
569
+ * Next, no page buttons) — not browser-driven wrapping. */
546
570
  .ui-pagination {
547
571
  display: flex;
548
- flex-wrap: wrap;
572
+ flex-wrap: nowrap;
573
+ min-width: 0;
549
574
  align-items: center;
550
575
  justify-content: flex-end;
551
576
  gap: var(--pagination-gap);
552
577
  }
553
578
 
554
- .ui-pagination[data-simple="true"] {
555
- flex-wrap: nowrap;
556
- }
557
-
579
+ /* Total / range label sits at the start and truncates rather than pushing the controls onto a
580
+ * second line — the nav row stays one line at every width (gh#153). */
558
581
  .ui-pagination-total {
582
+ min-width: 0;
559
583
  margin-inline-end: auto;
584
+ overflow: hidden;
560
585
  color: hsl(var(--muted-foreground));
561
586
  font-size: var(--pagination-total-font-size);
587
+ text-overflow: ellipsis;
588
+ white-space: nowrap;
562
589
  }
563
590
 
564
591
  .ui-pagination-count {
@@ -576,12 +603,22 @@
576
603
 
577
604
  .ui-pagination-list {
578
605
  display: flex;
606
+ min-width: 0;
579
607
  align-items: center;
580
608
  gap: var(--pagination-item-gap);
609
+ /* Overflow scrolls horizontally instead of wrapping onto a second row. Hidden scrollbar keeps
610
+ * the desktop bar clean; the strip stays swipeable on touch. */
611
+ overflow-x: auto;
612
+ scrollbar-width: none;
613
+ }
614
+
615
+ .ui-pagination-list::-webkit-scrollbar {
616
+ display: none;
581
617
  }
582
618
 
583
619
  .ui-pagination-item {
584
620
  display: flex;
621
+ flex: 0 0 auto;
585
622
  align-items: center;
586
623
  justify-content: center;
587
624
  }
@@ -143,6 +143,21 @@
143
143
  display: none;
144
144
  }
145
145
 
146
+ /* AppShell-owned mobile drawer trigger — hidden on desktop, shown once the docked sidebar
147
+ * collapses out below `lg` (gh#165). The drawer nav itself is a focus-trapped Sheet. */
148
+ .app-mobile-nav-trigger {
149
+ display: none;
150
+ flex: 0 0 auto;
151
+ margin-inline-end: var(--space-1);
152
+ }
153
+
154
+ /* The drawer nav reuses the docked Sidebar node; its `.sb-root { display: contents }` flattens
155
+ * into the scrollable SheetBody so sections/footer flow naturally in the panel. */
156
+ .app-mobile-nav-body {
157
+ display: flex;
158
+ flex-direction: column;
159
+ }
160
+
146
161
  .app-topbar-spacer {
147
162
  flex: 1;
148
163
  }
@@ -171,6 +186,10 @@
171
186
  .app-topbar-left:has(.app-mobile-sidebar-trigger) {
172
187
  display: flex;
173
188
  }
189
+
190
+ .app-mobile-nav-trigger {
191
+ display: inline-flex;
192
+ }
174
193
  }
175
194
 
176
195
  .sb-root {
@@ -33,7 +33,7 @@
33
33
  font-size: var(--font-size-2xl);
34
34
  }
35
35
 
36
- /* Weight — the dxs-kintai canon (400 body · 500 heading/label · 700 emphasis). No 600.
36
+ /* Weight — the reference-design canon (400 body · 500 heading/label · 700 emphasis). No 600.
37
37
  * Shared by text + heading; the two-attribute heading selectors outrank the base heading
38
38
  * rule below, so `<Heading weight="bold">` renders a semantic, emphasised title. */
39
39
  [data-slot="text"][data-weight="regular"],
@@ -43,7 +43,7 @@
43
43
  --card-header-border-bottom: initial;
44
44
  --card-radius: var(--radius);
45
45
  /* Resting elevation — quiet by default (rule #44): cards are flat (1px border, no shadow) in the
46
- * dxs-kintai baseline. A service that wants lifted cards sets this to an elevation token once,
46
+ * reference-design baseline. A service that wants lifted cards sets this to an elevation token once,
47
47
  * e.g. --card-shadow: var(--shadow-sm), and every Card picks up the shadow with no markup change. */
48
48
  --card-shadow: 0 0 0 0 transparent;
49
49
  /* Brand glow layer — invisible no-op at rest (rule #44). Paired AFTER --card-shadow in the
@@ -1,7 +1,7 @@
1
1
  /* Foundation tokens: color, typography, raw spacing, ratio, radius, shadow. */
2
2
 
3
3
  :root {
4
- /* Color system (HSL components) — dxs-kintai: warm hue-60 neutral spine,
4
+ /* Color system (HSL components) — the reference design: warm hue-60 neutral spine,
5
5
  * SmartHR blue primary, 和色/wa-iro semantics. */
6
6
  --background: 60 33% 99%; /* #fdfdfb warm off-white */
7
7
  --foreground: 48 8% 13%; /* #23221e SmartHR TEXT_BLACK */
@@ -220,7 +220,7 @@
220
220
 
221
221
  --font-weight-normal: 400;
222
222
  --font-weight-medium: 500;
223
- /* dxs-kintai uses three weights only: 400 body · 500 heading/label · 700 emphasis. 600 is
223
+ /* the reference design uses three weights only: 400 body · 500 heading/label · 700 emphasis. 600 is
224
224
  * forbidden (ambiguous between 500 and 700) — the legacy "semibold" token collapses to medium
225
225
  * so any remaining reference renders at 500. `bold` is the 700 emphasis weight. */
226
226
  --font-weight-semibold: 500;
@@ -325,7 +325,7 @@
325
325
 
326
326
  /* Motion — the primitive tier for animation, so enter/transition timing reads a token
327
327
  * instead of a literal 0.5s / cubic-bezier(...) / translateY(10px) (cardinal rule #2).
328
- * dxs-kintai keeps motion LOW: short durations, a calm decelerate curve, a small reveal shift.
328
+ * the reference design keeps motion LOW: short durations, a calm decelerate curve, a small reveal shift.
329
329
  * Consumers honour prefers-reduced-motion at the call site. */
330
330
  --duration-fast: 150ms;
331
331
  --duration-base: 250ms;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@godxjp/ui",
3
- "version": "17.0.0",
4
- "godxUiMcp": "16.10.0",
3
+ "version": "18.0.0",
4
+ "godxUiMcp": "17.0.1",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@10.29.1",
7
7
  "sideEffects": false,
@@ -276,9 +276,10 @@
276
276
  "test:watch": "vitest",
277
277
  "test:coverage": "vitest run --coverage",
278
278
  "check:example-imports": "node scripts/check-example-imports.mjs",
279
- "verify": "pnpm typecheck && pnpm lint && pnpm format && pnpm build && pnpm preview:build && pnpm check:example-imports && pnpm check:core-isolation && pnpm check:no-consumer-coupling && pnpm check:prop-vocabulary && pnpm check:token-tiers && pnpm check:control-sizing && pnpm check:rtl && pnpm check:typography && pnpm check:mcp-token-sync && pnpm check:mcp-lockstep && pnpm check:mcp-sync && pnpm check:mcp-orphans && pnpm check:audit-sync && pnpm test",
280
- "verify:release": "pnpm build && pnpm typecheck && pnpm typecheck:docs && pnpm lint && pnpm preview:build && pnpm check:example-imports && pnpm check:core-isolation && pnpm check:no-consumer-coupling && pnpm check:use-client && pnpm check:prop-vocabulary && pnpm check:token-tiers && pnpm check:control-sizing && pnpm check:rtl && pnpm check:typography && pnpm check:mcp-token-sync && pnpm check:mcp-lockstep && pnpm check:mcp-sync && pnpm check:mcp-orphans && pnpm check:audit-sync && pnpm check:mcp-prop-sync && pnpm check:contrast && pnpm check:visual-audit && pnpm test",
279
+ "verify": "pnpm typecheck && pnpm lint && pnpm format && pnpm build && pnpm preview:build && pnpm check:example-imports && pnpm check:core-isolation && pnpm check:no-consumer-coupling && pnpm check:prop-vocabulary && pnpm check:token-tiers && pnpm check:control-sizing && pnpm check:rtl && pnpm check:typography && pnpm check:mcp-token-sync && pnpm check:mcp-lockstep && pnpm check:mcp-sync && pnpm check:mcp-orphans && pnpm check:mcp-pattern-imports && pnpm check:audit-sync && pnpm check:frame-coverage && pnpm test",
280
+ "verify:release": "pnpm build && pnpm typecheck && pnpm typecheck:docs && pnpm lint && pnpm preview:build && pnpm check:example-imports && pnpm check:core-isolation && pnpm check:no-consumer-coupling && pnpm check:use-client && pnpm check:prop-vocabulary && pnpm check:token-tiers && pnpm check:control-sizing && pnpm check:rtl && pnpm check:typography && pnpm check:mcp-token-sync && pnpm check:mcp-lockstep && pnpm check:mcp-sync && pnpm check:mcp-orphans && pnpm check:mcp-pattern-imports && pnpm check:audit-sync && pnpm check:mcp-prop-sync && pnpm check:contrast && pnpm check:visual-audit && pnpm check:frame-coverage && pnpm check:frame-axe && pnpm test",
281
281
  "check:mcp-lockstep": "node scripts/check-release-lockstep.mjs",
282
+ "check:mcp-pattern-imports": "node scripts/check-mcp-pattern-imports.mjs",
282
283
  "check:mcp-sync": "node scripts/check-mcp-sync.mjs",
283
284
  "check:mcp-orphans": "node scripts/check-mcp-orphans.mjs",
284
285
  "check:mcp-prop-sync": "node scripts/check-mcp-prop-sync.mjs",
@@ -304,6 +305,9 @@
304
305
  "check:contrast": "node scripts/check-contrast.mjs",
305
306
  "visual-audit": "node scripts/visual-audit.mjs",
306
307
  "check:visual-audit": "node scripts/visual-audit-smoke.mjs",
308
+ "check:frame-axe": "node scripts/check-frame-axe.mjs",
309
+ "check:frame-geometry": "node scripts/frame-geometry.mjs",
310
+ "check:frame-coverage": "node scripts/frame-coverage.mjs",
307
311
  "postinstall": "node scripts/postinstall.mjs",
308
312
  "init-agent": "node scripts/init-agent-kit.mjs",
309
313
  "check:use-client": "node scripts/check-use-client.mjs"
@@ -281,9 +281,9 @@ const RULES = [
281
281
  {
282
282
  id: "no-em-dash-in-copy",
283
283
  severity: "warn",
284
- // Em-dash (U+2014) in JSX text — dxs-kintai uses the middot `·` for JP/EN pairs and calm copy.
284
+ // Em-dash (U+2014) in JSX text — the reference design uses the middot `·` for JP/EN pairs and calm copy.
285
285
  test: /[A-Za-z0-9぀-ヿ一-鿿]\s*—\s*[A-Za-z0-9぀-ヿ一-鿿]/,
286
- standard: "@godxjp/ui dxs-kintai typography (best-ux) · Unicode punctuation",
286
+ standard: "@godxjp/ui reference-design typography (best-ux) · Unicode punctuation",
287
287
  message:
288
288
  "No em-dash (—) in product copy. Use a middot `·` for JP/EN label pairs, or restructure into two calm sentences. Keep copy factual and quiet.",
289
289
  },
@@ -29,7 +29,7 @@ export const VISUAL_RULES = [
29
29
  id: "oversaturated-accent",
30
30
  severity: "warn",
31
31
  category: "color",
32
- standard: "@godxjp/ui dxs-kintai 渋み (OKLCH chroma ≤ 0.18)",
32
+ standard: "@godxjp/ui reference-design 渋み (OKLCH chroma ≤ 0.18)",
33
33
  fix: "Desaturate brand/primary surfaces — keep OKLCH chroma ≤ 0.18. Read --primary tokens; never paint a full-width bar in raw vivid blue.",
34
34
  },
35
35
  {
@@ -75,7 +75,7 @@ export function oklchChroma({ r, g, b }) {
75
75
  return Math.hypot(oa, ob);
76
76
  }
77
77
 
78
- /** dxs-kintai restraint bound. A signal/brand surface above this chroma "screams". */
78
+ /** reference-design restraint bound. A signal/brand surface above this chroma "screams". */
79
79
  export const CHROMA_LIMIT = 0.18;
80
80
 
81
81
  /** @returns {boolean} true when an accent surface exceeds the 渋み chroma limit. */