@godxjp/ui 20.2.1 → 22.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 (141) hide show
  1. package/dist/app/app-provider.js +2 -1
  2. package/dist/components/data-display/badge.d.ts +28 -1
  3. package/dist/components/data-display/badge.js +2 -0
  4. package/dist/components/data-display/chat-bubble.d.ts +30 -0
  5. package/dist/components/data-display/chat-bubble.js +229 -0
  6. package/dist/components/data-display/data-table.d.ts +18 -2
  7. package/dist/components/data-display/data-table.js +4 -0
  8. package/dist/components/data-display/descriptions.js +4 -1
  9. package/dist/components/data-display/index.d.ts +5 -3
  10. package/dist/components/data-display/index.js +5 -2
  11. package/dist/components/data-display/popover.d.ts +19 -1
  12. package/dist/components/data-display/popover.js +12 -3
  13. package/dist/components/data-display/progress.d.ts +37 -0
  14. package/dist/components/data-display/progress.js +57 -5
  15. package/dist/components/data-display/scroll-area.js +26 -1
  16. package/dist/components/data-display/tree.d.ts +8 -0
  17. package/dist/components/data-display/tree.js +426 -0
  18. package/dist/components/data-entry/chat-composer.d.ts +50 -0
  19. package/dist/components/data-entry/chat-composer.js +163 -0
  20. package/dist/components/data-entry/chat-suggestion.d.ts +28 -0
  21. package/dist/components/data-entry/chat-suggestion.js +285 -0
  22. package/dist/components/data-entry/date-picker.d.ts +10 -3
  23. package/dist/components/data-entry/date-picker.js +503 -238
  24. package/dist/components/data-entry/form-field.d.ts +1 -1
  25. package/dist/components/data-entry/form-field.js +4 -1
  26. package/dist/components/data-entry/form.js +10 -1
  27. package/dist/components/data-entry/index.d.ts +4 -6
  28. package/dist/components/data-entry/index.js +4 -6
  29. package/dist/components/data-entry/label.d.ts +14 -1
  30. package/dist/components/data-entry/label.js +6 -1
  31. package/dist/components/data-entry/radio.d.ts +27 -6
  32. package/dist/components/data-entry/radio.js +88 -43
  33. package/dist/components/data-entry/search-select.js +8 -1
  34. package/dist/components/data-entry/select.js +2 -0
  35. package/dist/components/data-entry/switch.d.ts +6 -3
  36. package/dist/components/data-entry/switch.js +34 -11
  37. package/dist/components/data-entry/textarea.js +3 -1
  38. package/dist/components/data-entry/tree-utils.d.ts +10 -48
  39. package/dist/components/data-entry/tree-utils.js +1 -154
  40. package/dist/components/layout/mobile-shell.d.ts +1 -1
  41. package/dist/components/layout/mobile-shell.js +2 -0
  42. package/dist/components/navigation/menubar.d.ts +10 -3
  43. package/dist/components/navigation/steps.d.ts +1 -1
  44. package/dist/components/navigation/steps.js +3 -1
  45. package/dist/components/navigation/tabs-scroll.d.ts +27 -0
  46. package/dist/components/navigation/tabs-scroll.js +52 -1
  47. package/dist/components/navigation/tabs.d.ts +2 -2
  48. package/dist/components/navigation/tabs.js +88 -23
  49. package/dist/components/ui/index.d.ts +0 -1
  50. package/dist/components/ui/index.js +0 -1
  51. package/dist/components/ui/segmented.d.ts +15 -1
  52. package/dist/components/ui/segmented.js +13 -6
  53. package/dist/i18n/messages/en.json +48 -2
  54. package/dist/i18n/messages/ja.json +46 -2
  55. package/dist/i18n/messages/vi.json +47 -2
  56. package/dist/lib/datetime/picker-format.d.ts +18 -0
  57. package/dist/lib/datetime/picker-format.js +35 -1
  58. package/dist/lib/field-a11y.d.ts +12 -0
  59. package/dist/lib/field-a11y.js +14 -1
  60. package/dist/lib/tree.d.ts +53 -0
  61. package/dist/lib/tree.js +155 -0
  62. package/dist/props/components/data-display.prop.d.ts +194 -1
  63. package/dist/props/components/data-entry.prop.d.ts +307 -99
  64. package/dist/props/components/index.d.ts +1 -1
  65. package/dist/props/components/layout.prop.d.ts +12 -1
  66. package/dist/props/components/navigation.prop.d.ts +76 -1
  67. package/dist/props/registry.d.ts +156 -19
  68. package/dist/props/registry.js +218 -18
  69. package/dist/props/vocabulary/data.prop.d.ts +12 -0
  70. package/dist/props/vocabulary/index.d.ts +2 -2
  71. package/dist/props/vocabulary/layout.prop.d.ts +11 -0
  72. package/dist/styles/badge-layout.css +4 -0
  73. package/dist/styles/card-layout.css +6 -6
  74. package/dist/styles/control.css +22 -6
  75. package/dist/styles/data-display-layout.css +385 -68
  76. package/dist/styles/data-entry-layout.css +64 -0
  77. package/dist/styles/focus-ring.css +4 -2
  78. package/dist/styles/navigation-layout.css +25 -0
  79. package/dist/styles/shell-layout.css +7 -1
  80. package/dist/styles/table-layout.css +34 -0
  81. package/dist/tokens/base.css +3 -0
  82. package/dist/tokens/components/chat-bubble.css +36 -0
  83. package/dist/tokens/components/chat-composer.css +19 -0
  84. package/dist/tokens/components/data-display.css +9 -6
  85. package/dist/tokens/components/descriptions.css +4 -0
  86. package/dist/tokens/components/navigation.css +6 -0
  87. package/dist/tokens/components/segmented.css +0 -2
  88. package/dist/tokens/components/shell.css +3 -0
  89. package/dist/tokens/components/table.css +3 -0
  90. package/dist/tokens/components/tree.css +27 -0
  91. package/dist/tokens/foundation.css +7 -0
  92. package/docs/COMPONENTS.md +14 -14
  93. package/docs/CONSUMER-RULES.md +24 -1
  94. package/docs/FRAME-COVERAGE-LEDGER.md +1 -1
  95. package/docs/FRAME-COVERAGE-REPORT.md +9 -7
  96. package/docs/TOKENS.md +65 -0
  97. package/docs/data-display/badge.tsx +37 -0
  98. package/docs/data-display/chat-bubble.tsx +397 -0
  99. package/docs/data-display/data-table/index.tsx +18 -0
  100. package/docs/data-display/popover.tsx +41 -1
  101. package/docs/data-display/progress.tsx +28 -0
  102. package/docs/data-display/timeline.tsx +46 -0
  103. package/docs/data-display/tree.tsx +394 -0
  104. package/docs/data-entry/calendar.tsx +2 -2
  105. package/docs/data-entry/chat-composer.tsx +464 -0
  106. package/docs/data-entry/chat-suggestion.tsx +301 -0
  107. package/docs/data-entry/date-picker.tsx +143 -3
  108. package/docs/data-entry/form/examples/invoice-form.tsx +2 -2
  109. package/docs/data-entry/form-field/examples/a11y-contract.tsx +3 -5
  110. package/docs/data-entry/form.tsx +4 -4
  111. package/docs/data-entry/number-input.tsx +31 -0
  112. package/docs/data-entry/segmented.tsx +103 -1
  113. package/docs/layout/mobile-shell.tsx +5 -0
  114. package/docs/navigation/tabs.tsx +149 -0
  115. package/docs/roadmap/ai-chat-components.md +207 -0
  116. package/docs/roadmap/antd-parity.md +155 -0
  117. package/docs/roadmap/badge-tag-chip-count.md +172 -0
  118. package/docs/roadmap/list-masonry.md +159 -0
  119. package/docs/roadmap/parity-audit-data-display-feedback.md +567 -0
  120. package/docs/roadmap/parity-audit-data-entry.md +357 -0
  121. package/docs/roadmap/parity-audit-layout-navigation-general.md +464 -0
  122. package/docs/roadmap/parity-backlog.md +78 -0
  123. package/docs/roadmap/tree-components.md +151 -0
  124. package/docs/showcase/acme-portal.tsx +7 -0
  125. package/docs/showcase/table-tree-rows.tsx +4 -4
  126. package/package.json +13 -23
  127. package/scripts/ui-audit.mjs +17 -4
  128. package/dist/components/data-display/tree-list.d.ts +0 -13
  129. package/dist/components/data-display/tree-list.js +0 -26
  130. package/dist/components/data-entry/date-range-picker.d.ts +0 -11
  131. package/dist/components/data-entry/date-range-picker.js +0 -349
  132. package/dist/components/data-entry/month-picker.d.ts +0 -10
  133. package/dist/components/data-entry/month-picker.js +0 -241
  134. package/dist/components/data-entry/month-range-picker.d.ts +0 -10
  135. package/dist/components/data-entry/month-range-picker.js +0 -301
  136. package/dist/components/ui/date-range-picker.d.ts +0 -1
  137. package/dist/components/ui/date-range-picker.js +0 -2
  138. package/docs/data-display/tree-list.tsx +0 -107
  139. package/docs/data-entry/date-range-picker.tsx +0 -175
  140. package/docs/data-entry/month-picker.tsx +0 -101
  141. package/docs/data-entry/month-range-picker.tsx +0 -115
@@ -14,7 +14,6 @@
14
14
  .ui-checkbox,
15
15
  .ui-radio,
16
16
  .ui-slider-thumb,
17
- .ui-switch,
18
17
  .ui-steps-inline-control,
19
18
  .ui-pagination-link,
20
19
  .ui-legal-document-toc-link,
@@ -30,6 +29,8 @@
30
29
  ):focus-visible,
31
30
 
32
31
  .ui-checkbox[data-focus-visible],
32
+ .ui-switch[data-focus-visible],
33
+ .ui-focus-ring[data-focus-visible],
33
34
  .ui-control-trigger[data-state="open"],
34
35
  .ui-tag-input:focus-within,
35
36
  .ui-control-composite-field:focus-within,
@@ -59,7 +60,6 @@
59
60
  .ui-checkbox,
60
61
  .ui-radio,
61
62
  .ui-slider-thumb,
62
- .ui-switch,
63
63
  .ui-steps-inline-control,
64
64
  .ui-pagination-link,
65
65
  .ui-legal-document-toc-link,
@@ -75,6 +75,8 @@
75
75
  ):focus-visible,
76
76
 
77
77
  :root[data-focus-outline="on"] .ui-checkbox[data-focus-visible],
78
+ :root[data-focus-outline="on"] .ui-switch[data-focus-visible],
79
+ :root[data-focus-outline="on"] .ui-focus-ring[data-focus-visible],
78
80
  :root[data-focus-outline="on"] .ui-control-trigger[data-state="open"],
79
81
  :root[data-focus-outline="on"] .ui-tag-input:focus-within,
80
82
  :root[data-focus-outline="on"] .ui-control-composite-field:focus-within,
@@ -96,6 +96,10 @@
96
96
 
97
97
  .ui-tabs-bar > [data-slot="tabs-list"] {
98
98
  min-inline-size: 0;
99
+ flex: 0 1 auto;
100
+ }
101
+
102
+ [data-slot="tabs"]:not([data-orientation="vertical"]) .ui-tabs-bar > [data-slot="tabs-list"] {
99
103
  flex: 1 1 auto;
100
104
  }
101
105
 
@@ -146,6 +150,27 @@
146
150
  block-size: var(--tabs-tab-remove-icon-size);
147
151
  }
148
152
 
153
+ .ui-tabs-overflow {
154
+ display: inline-flex;
155
+ flex: 0 0 auto;
156
+ inline-size: var(--tabs-overflow-size);
157
+ block-size: var(--tabs-overflow-size);
158
+ align-items: center;
159
+ justify-content: center;
160
+ border-radius: var(--tabs-overflow-radius);
161
+ color: hsl(var(--muted-foreground));
162
+ }
163
+
164
+ .ui-tabs-overflow:hover {
165
+ background: hsl(var(--accent));
166
+ color: hsl(var(--accent-foreground));
167
+ }
168
+
169
+ .ui-tabs-overflow-icon {
170
+ inline-size: var(--tabs-overflow-icon-size);
171
+ block-size: var(--tabs-overflow-icon-size);
172
+ }
173
+
149
174
  .ui-tabs-add {
150
175
  display: inline-flex;
151
176
  flex: 0 0 auto;
@@ -12,7 +12,8 @@
12
12
  "sidebar footer";
13
13
  grid-template-columns: var(--app-shell-sidebar-width) minmax(0, 1fr);
14
14
  grid-template-rows: var(--app-shell-bar-height) minmax(0, 1fr) auto;
15
- height: 100vh;
15
+
16
+ height: calc(100vh - var(--app-shell-viewport-inset, 0px));
16
17
  background: hsl(var(--background));
17
18
  }
18
19
 
@@ -2331,6 +2332,11 @@
2331
2332
  --mobile-shell-block-size: 100%;
2332
2333
  }
2333
2334
 
2335
+ .ui-mobile-shell[data-width="phone"] {
2336
+ max-inline-size: var(--mobile-shell-max-inline-size);
2337
+ margin-inline: auto;
2338
+ }
2339
+
2334
2340
  .ui-mobile-shell-chrome {
2335
2341
  display: flex;
2336
2342
  min-inline-size: 0;
@@ -15,6 +15,40 @@
15
15
  border-block-end-width: 0;
16
16
  }
17
17
 
18
+ .ui-table-row[data-tone="primary"] {
19
+ --table-row-tone-color: var(--mark-primary);
20
+ }
21
+
22
+ .ui-table-row[data-tone="success"] {
23
+ --table-row-tone-color: var(--mark-success);
24
+ }
25
+
26
+ .ui-table-row[data-tone="warning"] {
27
+ --table-row-tone-color: var(--mark-warning);
28
+ }
29
+
30
+ .ui-table-row[data-tone="info"] {
31
+ --table-row-tone-color: var(--mark-info);
32
+ }
33
+
34
+ .ui-table-row[data-tone="attention"] {
35
+ --table-row-tone-color: var(--mark-attention);
36
+ }
37
+
38
+ .ui-table-row[data-tone="destructive"] {
39
+ --table-row-tone-color: var(--mark-destructive);
40
+ }
41
+
42
+ .ui-table-row[data-tone] {
43
+ background: hsl(var(--table-row-tone-color) / var(--table-row-tone-wash-alpha));
44
+ }
45
+
46
+ .ui-table-row[data-tone] > [data-slot="table-cell"]:first-child {
47
+ border-inline-start: var(--table-row-tone-rail-width) solid
48
+ hsl(var(--table-row-tone-color));
49
+ padding-inline-start: calc(var(--table-cell-space-x) - var(--table-row-tone-rail-width));
50
+ }
51
+
18
52
  [data-slot="table-head"] {
19
53
  height: var(--table-row-height);
20
54
  padding: var(--table-cell-padding-y) var(--table-cell-space-x);
@@ -22,9 +22,11 @@
22
22
  @import "./components/data-display.css";
23
23
  @import "./components/scroll-area.css";
24
24
  @import "./components/list-row.css";
25
+ @import "./components/chat-bubble.css";
25
26
  @import "./components/logo.css";
26
27
  @import "./components/email.css";
27
28
  @import "./components/data-entry.css";
29
+ @import "./components/chat-composer.css";
28
30
  @import "./components/upload.css";
29
31
  @import "./components/shell.css";
30
32
  @import "./components/separator.css";
@@ -33,3 +35,4 @@
33
35
  @import "./components/error-surface.css";
34
36
  @import "./components/activity.css";
35
37
  @import "./components/text.css";
38
+ @import "./components/tree.css";
@@ -0,0 +1,36 @@
1
+
2
+ :root {
3
+
4
+ --chat-bubble-background: initial;
5
+
6
+ --chat-bubble-foreground: initial;
7
+
8
+ --chat-bubble-border-color: initial;
9
+ --chat-bubble-radius: var(--radius-lg);
10
+
11
+ --chat-bubble-gap: var(--space-3);
12
+
13
+ --chat-bubble-max-inline-size: 34rem;
14
+
15
+ --chat-bubble-avatar-size: initial;
16
+
17
+ --chat-bubble-padding-block: var(--space-2);
18
+ --chat-bubble-padding-inline: var(--space-3);
19
+
20
+ --chat-bubble-list-gap: var(--space-4);
21
+
22
+ --chat-bubble-list-jump-inset: var(--space-3);
23
+
24
+ --chat-bubble-typing-caret-color: initial;
25
+ --chat-bubble-typing-caret-width: var(--stroke-md);
26
+
27
+ --chat-bubble-typing-caret-height: 1em;
28
+
29
+ --chat-bubble-skeleton-short-inline-size: 60%;
30
+
31
+ --chat-bubble-tone-background-alpha: 0.08;
32
+ --chat-bubble-tone-border-alpha: 0.35;
33
+
34
+ --chat-bubble-end-background: initial;
35
+ --chat-bubble-end-foreground: initial;
36
+ }
@@ -0,0 +1,19 @@
1
+
2
+ :root {
3
+
4
+ --chat-composer-min-height: initial;
5
+ --chat-composer-max-height: initial;
6
+
7
+ --chat-composer-gap: var(--space-2);
8
+
9
+ --chat-composer-padding-block: var(--space-2);
10
+ --chat-composer-padding-inline: var(--space-2);
11
+
12
+ --chat-composer-row-gap: var(--space-2);
13
+
14
+ --chat-composer-radius: initial;
15
+
16
+ --chat-composer-header-border: initial;
17
+
18
+ --chat-composer-footer-font-size: var(--font-size-xs);
19
+ }
@@ -31,9 +31,6 @@
31
31
  --prose-code-space-inline: var(--space-1);
32
32
  --prose-code-radius: var(--radius-sm);
33
33
  --prose-image-radius: var(--radius-md);
34
- --tree-item-indent-width: var(--space-5);
35
- --tree-item-title-font-size: var(--font-size-xs);
36
- --tree-item-description-font-size: var(--font-size-xs);
37
34
  --timeline-note-font-size: var(--font-size-xs);
38
35
 
39
36
  --avatar-background: initial;
@@ -68,6 +65,15 @@
68
65
  --progress-breakdown-block-size: 1.375rem;
69
66
  --progress-breakdown-radius: var(--radius);
70
67
 
68
+ --progress-meter-block-size: 0.5rem;
69
+ --progress-meter-block-size-sm: 0.25rem;
70
+ --progress-breakdown-block-size-sm: 0.5rem;
71
+
72
+ --progress-ring-size: 2.75rem;
73
+ --progress-ring-size-sm: 2rem;
74
+ --progress-ring-border-width: var(--stroke-lg);
75
+ --progress-ring-label-font-size: var(--font-size-xs);
76
+
71
77
  --legend-gap: 0.875rem;
72
78
  --legend-item-gap: 0.3125rem;
73
79
  --legend-swatch-size: 0.625rem;
@@ -80,9 +86,6 @@
80
86
  --timeline-dot-current-background: initial;
81
87
  --timeline-line-completed-background: initial;
82
88
 
83
- --tree-item-active-border: initial;
84
- --tree-item-active-background: initial;
85
-
86
89
  --qr-code-foreground: 0 0% 0%;
87
90
  --qr-code-background: 0 0% 100%;
88
91
  --qr-code-size-xs: 6rem;
@@ -14,6 +14,10 @@
14
14
 
15
15
  --descriptions-value-line-height: calc(1.25 / 0.875);
16
16
 
17
+ --descriptions-label-font-size: var(--descriptions-value-font-size);
18
+
19
+ --descriptions-label-line-height: var(--descriptions-value-line-height);
20
+
17
21
  --descriptions-row-border: none;
18
22
  --descriptions-row-min-height: auto;
19
23
 
@@ -49,6 +49,8 @@
49
49
 
50
50
  --tabs-list-line-space-inset: 0px;
51
51
 
52
+ --tabs-list-line-space-gap: 0.25rem;
53
+
52
54
  --tabs-list-focus-ring-space-inset: initial;
53
55
 
54
56
  --tabs-trigger-line-radius: 0px;
@@ -74,6 +76,10 @@
74
76
 
75
77
  --tabs-bar-gap: var(--space-2);
76
78
  --tabs-extra-gap: var(--space-2);
79
+
80
+ --tabs-overflow-size: var(--band-height-md);
81
+ --tabs-overflow-radius: var(--radius-md);
82
+ --tabs-overflow-icon-size: var(--control-icon-size);
77
83
  --tabs-add-size: var(--band-height-md);
78
84
  --tabs-add-radius: var(--radius-md);
79
85
  --tabs-add-icon-size: var(--control-icon-size);
@@ -9,8 +9,6 @@
9
9
  --segmented-track-radius: var(--radius);
10
10
  --segmented-item-radius: var(--radius-md);
11
11
 
12
- --segmented-item-height: calc(var(--control-height) - var(--segmented-track-padding) * 2);
13
-
14
12
  --segmented-item-padding-inline: calc(var(--control-padding-x) - 1px);
15
13
 
16
14
  --segmented-item-gap: calc(var(--space-3) / 2);
@@ -36,6 +36,7 @@
36
36
  --sidebar-gradient: none;
37
37
  --topbar-gradient: none;
38
38
 
39
+ --app-shell-viewport-inset: 0px;
39
40
  --app-shell-bar-height: var(--band-height-2xl);
40
41
 
41
42
  --app-shell-bar-inset: var(--space-page-x);
@@ -354,6 +355,8 @@
354
355
 
355
356
  :root {
356
357
  --mobile-shell-block-size: 100dvh;
358
+
359
+ --mobile-shell-max-inline-size: 26.875rem;
357
360
  --mobile-shell-safe-inset-block-start: env(safe-area-inset-top);
358
361
  --mobile-shell-safe-inset-block-end: env(safe-area-inset-bottom);
359
362
  --mobile-shell-safe-inset-inline: max(env(safe-area-inset-left), env(safe-area-inset-right));
@@ -91,4 +91,7 @@
91
91
  --table-row-border-width: var(--stroke-hairline);
92
92
 
93
93
  --table-flush-divider-width: var(--table-row-border-width);
94
+
95
+ --table-row-tone-rail-width: var(--stroke-2xl);
96
+ --table-row-tone-wash-alpha: 0.06;
94
97
  }
@@ -0,0 +1,27 @@
1
+
2
+
3
+ :root {
4
+
5
+ --tree-node-height: initial;
6
+
7
+ --tree-indent-width: var(--space-5);
8
+
9
+ --tree-node-padding-inline: var(--space-2);
10
+ --tree-node-gap: var(--space-2);
11
+ --tree-node-radius: var(--radius-sm);
12
+ --tree-node-font-size: var(--font-size-sm);
13
+
14
+ --tree-switcher-size: 1.25rem;
15
+
16
+ --tree-switcher-color: initial;
17
+
18
+ --tree-icon-size: var(--icon-size-md);
19
+
20
+ --tree-line-color: initial;
21
+
22
+ --tree-node-hover-background: initial;
23
+ --tree-node-selected-background: initial;
24
+ --tree-node-selected-foreground: initial;
25
+
26
+ --tree-loading-bar-inline-size: 40%;
27
+ }
@@ -47,6 +47,13 @@
47
47
  --text-info: 221 70% 38%;
48
48
  --text-error: 357 75% 38%;
49
49
 
50
+ --mark-primary: var(--text-primary);
51
+ --mark-success: var(--text-success);
52
+ --mark-warning: var(--text-warning);
53
+ --mark-info: var(--text-info);
54
+ --mark-destructive: var(--text-error);
55
+ --mark-attention: var(--attention);
56
+
50
57
  --secondary-hover: 45 15% 91%;
51
58
  --secondary-active: 45 15% 88%;
52
59
 
@@ -48,8 +48,8 @@ All UI is **mobile-first**: base layout targets ~320–428px viewport; `sm` (640
48
48
 
49
49
  Default `direction="row"`; use `direction="col"` for vertical rhythm.
50
50
 
51
- | Component | Prop | Key props |
52
- | --------- | ---------- | ---------------------------------------------------------------------------------------------------- |
51
+ | Component | Prop | Key props |
52
+ | --------- | ---------- | ------------------------------------------------------------------------------------------------------------------- |
53
53
  | `Flex` | `FlexProp` | `direction: "row" \| "col"`, `gap: GapProp`, `gapRaw`, `pad`, `padRaw`, `fill`, `width`, `align`, `justify`, `wrap` |
54
54
 
55
55
  `fill` / `width` are the `fixed | elastic | fixed` row: `width` pins a column (number = px, and
@@ -82,25 +82,25 @@ and sets `min-inline-size: 0` so a truncating child ellipses instead of widening
82
82
  | `Textarea` | `TextareaProp` | extends native textarea |
83
83
  | `FormField` | `FormFieldProp` | `id`, `label`, `required`, `helper`, `error` |
84
84
  | `SearchInput` | `SearchInputProp` | `onSearchChange: OnSearchChangeProp` |
85
- | `Checkbox` | `CheckboxProp` | Radix checkbox; `Checkbox.Group` với `options` |
85
+ | `Checkbox` | `CheckboxProp` | Checkbox (RAC); `Checkbox.Group` với `options` |
86
86
  | `Radio` | `RadioGroupProp` | `Radio.Group` single-select; `Radio.Root` compound |
87
87
 
88
88
  ---
89
89
 
90
90
  ## Data Display
91
91
 
92
- | Component | Prop type | Key props |
93
- | -------------- | ------------------ | ---------------------------------------- |
94
- | `EmptyState` | `EmptyStateProp` | `icon`, `title`, `description`, `action` |
95
- | `DataTable` | `DataTableProp<T>` | `columns`, `density: TableDensityProp` |
96
- | `Descriptions` | `DescriptionsProp` | `items[]` |
97
- | `Badge` | `BadgeProp` | `status`, `tone: ToneProp` |
98
- | `Badge` | `BadgeProp` | shadcn badge variants |
92
+ | Component | Prop type | Key props |
93
+ | -------------- | ------------------ | ----------------------------------------- |
94
+ | `EmptyState` | `EmptyStateProp` | `icon`, `title`, `description`, `action` |
95
+ | `DataTable` | `DataTableProp<T>` | `columns`, `density: TableDensityProp` |
96
+ | `Descriptions` | `DescriptionsProp` | `items[]` |
97
+ | `Badge` | `BadgeProp` | `status`, `tone: ToneProp` |
98
+ | `Badge` | `BadgeProp` | shadcn badge variants |
99
99
  | `Progress` | `ProgressProp` | `value` (meter) \| `segments` (breakdown) |
100
- | `Legend` | `LegendProp` | `items[]` — the key for a tone |
101
- | `Popover` | Radix primitives | floating content panel |
102
- | `ScrollArea` | Radix primitives | scrollable regions |
103
- | `Collapsible` | Radix primitives | expand/collapse sections |
100
+ | `Legend` | `LegendProp` | `items[]` — the key for a tone |
101
+ | `Popover` | Radix primitives | floating content panel |
102
+ | `ScrollArea` | Radix primitives | scrollable regions |
103
+ | `Collapsible` | Radix primitives | expand/collapse sections |
104
104
 
105
105
  ---
106
106
 
@@ -6,7 +6,7 @@ Read this once; the audit enforces it. Everything else in `docs/` is for contrib
6
6
  2. Every page is `<PageContainer title subtitle extra footer>`; its sections are spaced by the page. Group items inside a section with `<Flex direction="col" gap>` or `<ResponsiveGrid>`.
7
7
  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>`.
8
8
  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`.
9
- 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"`.
9
+ 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.
10
10
  6. Text is `<Text>` / `<Heading>` with `tone`, `size`, `weight`, `truncate`, `mono` — not `className="text-muted-foreground font-semibold"`.
11
11
  7. Colours are semantic tokens (`tone="destructive"`, `bg-primary`), never palette names, hex, or `bg-black` / `text-white`.
12
12
  8. Sizes come from props (`size`, `width`, `columns`), never `w-[240px]` / `max-h-[420px]`.
@@ -25,3 +25,26 @@ const legacyClasses = ["gap-3", "p-2"];
25
25
  ```
26
26
 
27
27
  A block with no reason (or under 12 characters of it) is ignored and the finding stands; an unclosed block runs to the end of the file. The class-shaped rules (`gap-*`, `bg-red-500`, `w-[37px]`, `pr-*`, `dark:*`) only read class expressions — a `className`/`class` attribute, a class-named binding (`baseClass`, `statusStyles`, `badgeVariants`) or a `cn()`/`clsx()`/`cva()` call — so product copy or an i18n value that happens to spell a utility is never a finding.
28
+
29
+ ---
30
+
31
+ ## Why a disabled control's reason cannot be a tooltip
32
+
33
+ A disabled `<button>` receives no pointer events and is not in the tab order, so an explanation
34
+ attached to it never reaches anyone. Measured in Chromium on this package's own tooltip page: the
35
+ same trigger produced **1** tooltip on hover while enabled and **0** while disabled, and calling
36
+ `focus()` on it left `document.activeElement` as `BODY` — so keyboard and touch get nothing either.
37
+ Hover-only would fail a touch user in any case.
38
+
39
+ This is not a gap waiting for a DS prop. The reason is required information, and required
40
+ information belongs on the screen:
41
+
42
+ - Render it as a `<Text size="xs" tone="muted">` line **before** the control, so a screen-reader or
43
+ keyboard user meets the explanation ahead of the thing they cannot use (WCAG 2.2 · 1.3.2,
44
+ meaningful sequence).
45
+ - `ServiceLauncherCard` already has this shape as a prop — `disabledReason`, rendered above the
46
+ action — and it is the pattern to copy anywhere else.
47
+
48
+ If the control is disabled *pending something the user can fix*, prefer leaving it enabled and
49
+ explaining on submit: an inert control with a paragraph beside it is often a validation message
50
+ wearing a disguise.
@@ -112,7 +112,7 @@ The executed geometry sweep (`pnpm check:frame-geometry`, `scripts/frame-geometr
112
112
 
113
113
  ## Known gaps
114
114
 
115
- The issue's "Initial known gaps to encode" are locked in code (`KNOWN_GAPS` in `scripts/frame-coverage-contract.mjs`) and mirrored into `policy.knownGaps`: Button, Card, Card subcomponents, EmptyState, Progress, Carousel, DataTable, Charts and the Timeline/TreeList/PasswordStrength locale stress. They cannot be deleted, and they cannot be waived through `policy.notApplicable` — they must be **covered** or stay **UNTESTED**.
115
+ The issue's "Initial known gaps to encode" are locked in code (`KNOWN_GAPS` in `scripts/frame-coverage-contract.mjs`) and mirrored into `policy.knownGaps`: Button, Card, Card subcomponents, EmptyState, Progress, Carousel, DataTable, Charts and the Timeline/PasswordStrength locale stress. They cannot be deleted, and they cannot be waived through `policy.notApplicable` — they must be **covered** or stay **UNTESTED**.
116
116
 
117
117
  ## Current state — read this before quoting coverage
118
118
 
@@ -3,8 +3,8 @@
3
3
  > Generated by `scripts/frame-coverage.mjs` (issue #163). Do not edit by hand — run `pnpm check:frame-coverage`.
4
4
  > Standard: [FRAME-COVERAGE-STANDARD.md](./FRAME-COVERAGE-STANDARD.md). A missing dimension is **UNTESTED**, never a pass.
5
5
 
6
- - Public components: **137**
7
- - With ≥1 frame: **133** · zero-frame: **4**
6
+ - Public components: **138**
7
+ - With ≥1 frame: **133** · zero-frame: **5**
8
8
  - Fully covered (every contract axis declared): **0**
9
9
 
10
10
  ## UNTESTED — no `/frame/**` route
@@ -13,6 +13,7 @@
13
13
  - [ ] `CardBar`
14
14
  - [ ] `VisuallyHidden`
15
15
  - [ ] `RangeTimeline`
16
+ - [ ] `ChatBubbleList`
16
17
 
17
18
  ## Per-component contract axes
18
19
 
@@ -59,7 +60,7 @@ Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
59
60
  | QrCode | ✓ | ✓ | N/A | · | · | · | · | · | · | · |
60
61
  | Descriptions | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
61
62
  | EmptyState | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
62
- | Progress | ✓ | | N/A | · | · | · | · | · | · | · |
63
+ | Progress | ✓ | · | N/A | · | · | · | · | · | · | · |
63
64
  | CodeBlock | ✓ | · | N/A | N/A | · | · | · | · | · | · |
64
65
  | Prose | ✓ | · | N/A | N/A | · | · | · | · | · | · |
65
66
  | Timeline | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
@@ -79,8 +80,6 @@ Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
79
80
  | Label | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
80
81
  | Checkbox | ✓ | N/A | · | · | · | · | · | · | · | · |
81
82
  | RadioGroup | ✓ | N/A | · | · | · | · | · | · | · | · |
82
- | MonthPicker | ✓ | · | · | · | · | · | · | · | · | · |
83
- | MonthRangePicker | ✓ | · | · | · | · | · | · | · | · | · |
84
83
  | DatePicker | ✓ | · | · | · | · | · | · | · | · | · |
85
84
  | Dialog | ✓ | N/A | N/A | · | · | · | · | · | · | · |
86
85
  | AlertDialog | ✓ | ✓ | · | · | · | · | · | · | · | · |
@@ -98,7 +97,6 @@ Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
98
97
  | AppProvider | ✓ | · | · | · | · | · | · | · | · | · |
99
98
  | formatDate | ✓ | · | · | · | · | · | · | · | · | · |
100
99
  | TimePicker | ✓ | · | · | · | · | · | · | · | · | · |
101
- | DateRangePicker | ✓ | · | · | · | · | · | · | · | · | · |
102
100
  | Cascader | ✓ | ✓ | · | · | · | · | · | · | · | · |
103
101
  | TreeSelect | ✓ | ✓ | · | · | · | · | · | · | · | · |
104
102
  | Transfer | ✓ | N/A | · | · | · | · | · | · | · | · |
@@ -113,7 +111,7 @@ Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
113
111
  | Popover | ✓ | N/A | N/A | · | · | · | · | · | · | · |
114
112
  | ScrollArea | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
115
113
  | Collapsible | ✓ | N/A | · | · | · | · | · | · | · | · |
116
- | TreeList | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
114
+ | Tree | ✓ | · | · | · | · | · | · | · | · | · |
117
115
  | Tooltip | ✓ | N/A | N/A | · | · | · | · | · | · | · |
118
116
  | PrefetchLink | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
119
117
  | Avatar | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
@@ -157,4 +155,8 @@ Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
157
155
  | PermissionMatrix | ✓ | N/A | · | N/A | · | · | · | · | · | · |
158
156
  | BranchScopePicker | ✓ | N/A | · | · | · | · | · | · | · | · |
159
157
  | ServiceRolePanel | ✓ | N/A | · | · | · | · | · | · | · | · |
158
+ | ChatBubble | ✓ | · | · | N/A | · | · | · | · | · | · |
159
+ | ChatBubbleList | **✗** | N/A | N/A | N/A | · | · | · | · | · | · |
160
+ | ChatComposer | ✓ | · | · | · | · | · | · | · | · | · |
161
+ | ChatSuggestion | ✓ | N/A | N/A | · | · | · | · | · | · | · |
160
162
 
package/docs/TOKENS.md CHANGED
@@ -44,6 +44,71 @@ src/styles/
44
44
 
45
45
  Default brand tokens use the GodX Agent Portal palette: navy primary, 朱 orange focus/accent, warm neutral surfaces. App or customer identity colors belong in the consuming app theme, not in package tokens.
46
46
 
47
+ #### The three tone tiers — FILL, TEXT, MARK
48
+
49
+ A status tone can be painted three ways, and each way is judged against a different thing. Reading
50
+ the wrong tier is this palette's most expensive recurring bug, because nothing about it looks wrong
51
+ in the source.
52
+
53
+ | Tier | Tokens | What it paints | Contrast bar |
54
+ | -------- | ------------------------------------------- | ------------------------------------------------------------------ | --------------------------------------------------------- |
55
+ | **FILL** | `--success`, `--warning`, `--info`, `--destructive` | A solid chip, band or bar with a label ON it | AA **4.5:1** against its own `*-foreground` label |
56
+ | **TEXT** | `--text-success`, `--text-warning`, `--text-info`, `--text-error` | Small coloured type — a StatCard delta, an outline badge label | AA **4.5:1** against the surface BEHIND it |
57
+ | **MARK** | `--mark-success`, `--mark-warning`, `--mark-info`, `--mark-destructive`, `--mark-primary`, `--mark-attention` | A thin shape carrying meaning with nothing written on it — a `Card accent` rail, a `DataTable rowTone` rail | SC 1.4.11 **3:1** against the surface it sits on |
58
+
59
+ The MARK tier exists because both rails were reading FILL, and two of them were effectively
60
+ invisible. Measured in Chromium on the light card: `Card accent="warning"` drew its 6px rail at
61
+ **1.74:1**, `accent="success"` at **2.18:1**. `--warning` is a bright 山吹 tuned to carry dark text
62
+ on top of it; on a near-white card it is barely a colour at all.
63
+
64
+ `--mark-*` resolve THROUGH the TEXT tier rather than restating its numbers, so a theme retunes one
65
+ place and both follow — and since 3:1 is looser than 4.5:1, anything legible as text is legible as
66
+ a mark by construction. `--mark-attention` is the exception: there is no `--text-attention`, and
67
+ the fill already clears the floor in both themes (3.32 / 6.75).
68
+
69
+ Two guards, because one was not enough:
70
+ `src/tokens/__tests__/tone-mark-contrast.test.ts` recomputes every tone × ground × theme off the
71
+ committed tokens and reads the alias out of the CSS (so repointing a mark back at FILL fails the
72
+ ratio, not just a name check); and `check:contrast` grew a **rail pass** — its graphic pass only
73
+ ever measured elements ≤24px on both axes, and a rail is 6px by the full height of a card, so no
74
+ gate had ever looked at one.
75
+
76
+ **Progress and Legend joined the tier, and the wa-iro question is settled.** `.ui-legend-swatch`,
77
+ `.ui-progress-bar` and `.ui-progress-segment` all read FILL as standalone graphics and all failed
78
+ the same floor. The floor applies: nothing is written on a progress fill, so *where the colour
79
+ stops* is the entire datum, and a slice of a partition carries its share of the whole with no words
80
+ on it — that is precisely a "graphical object required to understand the content". The hue loses.
81
+ The two moved together because a swatch is a SAMPLE of the bar beside it; a key that is not the
82
+ colour it is a key to is not a key.
83
+
84
+ | surface | tone | ground | FILL (before) | MARK (after) |
85
+ | --- | --- | --- | --- | --- |
86
+ | `.ui-legend-swatch` | warning | light card | **1.74** | **5.90** |
87
+ | `.ui-legend-swatch` | success | light card | **2.18** | **6.84** |
88
+ | `.ui-legend-swatch` | destructive | dark card | **2.95** | **5.52** |
89
+ | `.ui-progress-segment` | warning | light track | **1.60** | **5.41** |
90
+ | `.ui-progress-segment` | success | light track | **2.00** | **6.28** |
91
+ | `.ui-progress-segment` | destructive | dark track | **2.42** | **4.52** |
92
+
93
+ Worst case anywhere on the two routes after the move: **4.52:1**. `.ui-progress-bar` (meter and
94
+ over-capacity) reads the same tokens as the slice, so a `tone="warning"` meter and a `warning`
95
+ slice on one screen stay the same colour.
96
+
97
+ **A theme that repoints `--secondary` owes `--progress-track-background`.** The track defaults to
98
+ `hsl(var(--secondary))`, which is a pale neutral in the stock palette. `docs/showcase/acme-portal`
99
+ repurposes `--secondary` as a navy *button* colour, so its bars were drawn on a near-black track
100
+ and the mark fills measured 2.50 (success) / 2.90 (warning) on it. Naming the track explicitly is
101
+ the fix — 6.49 / 5.59 after — not dragging the tier back.
102
+
103
+ Three guards now, and each sees something the others cannot: the ratio tests in
104
+ `tone-mark-contrast.test.ts` (rails against card/background, progress marks against the track);
105
+ the *same file's* CSS-alias assertions, which fail if a rule is repointed at the fill tier and
106
+ which also pin the swatch and the slice to the **same** token per tone; and `check:contrast`'s
107
+ **thin fill** pass — added because adding `/isolate/data-display-progress` to that gate's route
108
+ list on its own changed nothing at all. The graphic pass wants ≤24px on both axes and the rail
109
+ pass wants a one-sided border; a progress fill is 8px (meter) or 22px (slice) tall by whatever
110
+ width its share makes it, so the sweep printed "AA clean" over a bar sitting at 1.60:1.
111
+
47
112
  #### `--border` vs `--input` — decorative chrome vs control boundary (gh#315)
48
113
 
49
114
  These two look like synonyms and are not. Keep them apart:
@@ -262,6 +262,43 @@ export default function Demo() {
262
262
  </CardContent>
263
263
  </Card>
264
264
 
265
+ <Card>
266
+ <CardHeader>
267
+ <CardTitle level={2}>tabular · 数字を桁で揃える</CardTitle>
268
+ <CardDescription>
269
+ 件数のチップが縦に並ぶときは tabular を。プロポーショナル数字だと 1 が 0
270
+ より狭いので、列で見たときに桁がずれてチップ幅も揺れます。既定はオフ — 語を載せる
271
+ チップまで幅の広い数字を払う必要はありません。
272
+ </CardDescription>
273
+ </CardHeader>
274
+ <CardContent>
275
+ <Flex direction="row" gap="lg">
276
+ <Flex direction="col" align="start" gap="xs">
277
+ <Text size="xs" tone="muted">
278
+ 既定(比例数字)
279
+ </Text>
280
+ <Badge tone="info">11</Badge>
281
+ <Badge tone="info">100</Badge>
282
+ <Badge tone="warning">1811</Badge>
283
+ </Flex>
284
+ <Flex direction="col" align="start" gap="xs">
285
+ <Text size="xs" tone="muted">
286
+ tabular
287
+ </Text>
288
+ <Badge tone="info" tabular>
289
+ 11
290
+ </Badge>
291
+ <Badge tone="info" tabular>
292
+ 100
293
+ </Badge>
294
+ <Badge tone="warning" tabular>
295
+ 1811
296
+ </Badge>
297
+ </Flex>
298
+ </Flex>
299
+ </CardContent>
300
+ </Card>
301
+
265
302
  <Card>
266
303
  <CardHeader>
267
304
  <CardTitle level={2}>Icon overrides</CardTitle>