@godxjp/ui 19.6.0 → 20.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.
- package/dist/components/data-display/card.d.ts +2 -2
- package/dist/components/data-display/collapsible.d.ts +23 -4
- package/dist/components/data-display/collapsible.js +119 -4
- package/dist/components/data-display/index.d.ts +3 -1
- package/dist/components/data-display/index.js +2 -0
- package/dist/components/data-display/legend.d.ts +15 -0
- package/dist/components/data-display/legend.js +11 -0
- package/dist/components/data-display/list-row.js +1 -1
- package/dist/components/data-display/popover.d.ts +58 -5
- package/dist/components/data-display/popover.js +206 -27
- package/dist/components/data-display/progress.d.ts +50 -2
- package/dist/components/data-display/progress.js +57 -9
- package/dist/components/data-entry/calendar.d.ts +1 -1
- package/dist/components/data-entry/calendar.js +67 -32
- package/dist/components/data-entry/checkbox.d.ts +19 -6
- package/dist/components/data-entry/checkbox.js +55 -16
- package/dist/components/data-entry/choice-option.d.ts +1 -1
- package/dist/components/data-entry/date-range-picker.d.ts +1 -1
- package/dist/components/data-entry/field.js +0 -1
- package/dist/components/data-entry/form.js +1 -1
- package/dist/components/data-entry/label.d.ts +3 -2
- package/dist/components/data-entry/label.js +23 -10
- package/dist/components/data-entry/month-picker.d.ts +1 -1
- package/dist/components/data-entry/month-range-picker.d.ts +1 -1
- package/dist/components/data-entry/transfer.js +16 -6
- package/dist/components/data-entry/tree-select-strategy.d.ts +1 -1
- package/dist/components/data-entry/tree-select.js +29 -12
- package/dist/components/data-entry/tree-utils.d.ts +1 -1
- package/dist/components/data-entry/upload.js +7 -2
- package/dist/components/feedback/dialog.d.ts +105 -38
- package/dist/components/feedback/dialog.js +272 -194
- package/dist/components/feedback/overlay-close-focus.d.ts +31 -0
- package/dist/components/feedback/overlay-close-focus.js +31 -0
- package/dist/components/feedback/overlay-header-tone.d.ts +1 -1
- package/dist/components/feedback/sheet.d.ts +53 -11
- package/dist/components/feedback/sheet.js +150 -81
- package/dist/components/feedback/tooltip.d.ts +51 -7
- package/dist/components/feedback/tooltip.js +107 -25
- package/dist/components/general/button.js +2 -2
- package/dist/components/general/logo.d.ts +17 -0
- package/dist/components/general/logo.js +22 -16
- package/dist/components/layout/app-shell.d.ts +1 -1
- package/dist/components/layout/app-shell.js +52 -8
- package/dist/components/layout/aspect-ratio.js +0 -1
- package/dist/components/layout/auth-divider.js +0 -1
- package/dist/components/layout/flex.d.ts +1 -1
- package/dist/components/layout/flex.js +19 -2
- package/dist/components/layout/nav-surface.d.ts +26 -0
- package/dist/components/layout/nav-surface.js +17 -0
- package/dist/components/layout/org-switcher.d.ts +5 -1
- package/dist/components/layout/org-switcher.js +5 -3
- package/dist/components/layout/separator.js +0 -1
- package/dist/components/layout/sidebar.js +4 -1
- package/dist/components/layout/split-pane.d.ts +14 -1
- package/dist/components/layout/split-pane.js +26 -13
- package/dist/components/layout/topbar-item.js +1 -2
- package/dist/components/navigation/app-setting-picker.js +21 -2
- package/dist/components/navigation/app-setting-toggle.d.ts +16 -0
- package/dist/components/navigation/app-setting-toggle.js +96 -0
- package/dist/components/navigation/dropdown-menu.d.ts +199 -18
- package/dist/components/navigation/dropdown-menu.js +344 -117
- package/dist/components/navigation/index.d.ts +2 -0
- package/dist/components/navigation/index.js +2 -0
- package/dist/components/navigation/pagination-utils.d.ts +2 -1
- package/dist/components/navigation/tabs.d.ts +47 -6
- package/dist/components/navigation/tabs.js +152 -90
- package/dist/components/ui/accordion.d.ts +50 -5
- package/dist/components/ui/accordion.js +239 -33
- package/dist/components/ui/aspect-ratio.d.ts +23 -2
- package/dist/components/ui/aspect-ratio.js +15 -13
- package/dist/components/ui/avatar.d.ts +29 -4
- package/dist/components/ui/avatar.js +111 -25
- package/dist/components/ui/hover-card.d.ts +42 -4
- package/dist/components/ui/hover-card.js +183 -27
- package/dist/components/ui/label.js +0 -1
- package/dist/components/ui/segmented.d.ts +3 -3
- package/dist/components/ui/separator.d.ts +8 -2
- package/dist/components/ui/separator.js +17 -9
- package/dist/components/ui/toggle-group.d.ts +50 -5
- package/dist/components/ui/toggle-group.js +79 -20
- package/dist/components/ui/toggle.d.ts +31 -5
- package/dist/components/ui/toggle.js +42 -3
- package/dist/i18n/messages/en.json +8 -0
- package/dist/i18n/messages/ja.json +8 -0
- package/dist/i18n/messages/vi.json +8 -0
- package/dist/lib/control-styles.d.ts +1 -1
- package/dist/lib/control-styles.js +1 -1
- package/dist/lib/slot.d.ts +32 -0
- package/dist/lib/slot.js +22 -0
- package/dist/lib/variants.d.ts +22 -3
- package/dist/lib/variants.js +56 -1
- package/dist/props/components/app.prop.d.ts +25 -1
- package/dist/props/components/data-display.prop.d.ts +17 -0
- package/dist/props/components/data-entry.prop.d.ts +32 -4
- package/dist/props/components/index.d.ts +1 -1
- package/dist/props/components/layout.prop.d.ts +118 -5
- package/dist/props/components/navigation.prop.d.ts +1 -1
- package/dist/props/registry.d.ts +22 -2
- package/dist/props/registry.js +33 -2
- package/dist/props/vocabulary/content.prop.d.ts +1 -1
- package/dist/props/vocabulary/index.d.ts +2 -2
- package/dist/props/vocabulary/interaction.prop.d.ts +17 -1
- package/dist/props/vocabulary/layout.prop.d.ts +57 -1
- package/dist/styles/control.css +142 -1
- package/dist/styles/core.css +5 -2
- package/dist/styles/data-display-layout.css +72 -0
- package/dist/styles/focus-ring.css +4 -0
- package/dist/styles/index.css +5 -2
- package/dist/styles/layout.css +106 -0
- package/dist/styles/shell-layout.css +140 -8
- package/dist/styles/text-layout.css +5 -4
- package/dist/tokens/base.css +1 -1
- package/dist/tokens/components/data-display.css +11 -0
- package/dist/tokens/components/shell.css +3 -1
- package/dist/tokens/foundation.css +2 -0
- package/docs/COMPONENTS.md +9 -3
- package/docs/DESIGN-AUTHORITY.md +96 -71
- package/docs/FRAME-COVERAGE-REPORT.md +5 -3
- package/docs/README.md +14 -14
- package/docs/WHAT-BELONGS-HERE.md +179 -0
- package/docs/data-display/legend.tsx +145 -0
- package/docs/data-display/progress.tsx +32 -0
- package/docs/data-entry/segmented.tsx +1 -1
- package/docs/data-entry/select.tsx +2 -2
- package/docs/feedback/sheet.tsx +1 -1
- package/docs/layout/app-shell-arrangements.tsx +225 -0
- package/docs/layout/aspect-ratio.tsx +1 -1
- package/docs/navigation/app-setting-picker.tsx +26 -1
- package/docs/navigation/app-setting-toggle.tsx +111 -0
- package/package.json +11 -8
- package/scripts/_agent-setup.mjs +165 -3
- package/scripts/consumer-rule.md +98 -0
- package/scripts/guinea-pig-skill.md +322 -0
- package/scripts/init-guinea-pig.mjs +62 -0
- package/scripts/postinstall.mjs +13 -2
- package/scripts/ui-audit.mjs +115 -14
- /package/dist/tokens/{antd.generated.css → derived.css} +0 -0
package/dist/tokens/base.css
CHANGED
|
@@ -64,6 +64,17 @@
|
|
|
64
64
|
--progress-track-background: initial;
|
|
65
65
|
--progress-fill-background: initial;
|
|
66
66
|
|
|
67
|
+
--progress-breakdown-block-size: 1.375rem;
|
|
68
|
+
--progress-breakdown-radius: var(--radius);
|
|
69
|
+
|
|
70
|
+
--legend-gap: 0.875rem;
|
|
71
|
+
--legend-item-gap: 0.3125rem;
|
|
72
|
+
--legend-swatch-size: 0.625rem;
|
|
73
|
+
--legend-swatch-radius: 0.125rem;
|
|
74
|
+
--legend-font-size: var(--font-size-xs);
|
|
75
|
+
|
|
76
|
+
--legend-color: initial;
|
|
77
|
+
|
|
67
78
|
--timeline-dot-done-background: initial;
|
|
68
79
|
--timeline-dot-current-background: initial;
|
|
69
80
|
--timeline-line-completed-background: initial;
|
|
@@ -42,7 +42,9 @@
|
|
|
42
42
|
--app-shell-bar-gap: var(--space-3);
|
|
43
43
|
|
|
44
44
|
--app-shell-sidebar-width: 16rem;
|
|
45
|
-
--app-shell-
|
|
45
|
+
--app-shell-sidebar-collapsed-width: 4rem;
|
|
46
|
+
|
|
47
|
+
--app-shell-nav-rail-width: 3.5rem;
|
|
46
48
|
--app-shell-page-max-width: 80rem;
|
|
47
49
|
--app-shell-main-background: hsl(var(--muted) / 0.4);
|
|
48
50
|
--app-shell-mobile-nav-width: 22.5rem;
|
|
@@ -273,6 +273,8 @@
|
|
|
273
273
|
--space-inline-sm: var(--space-2);
|
|
274
274
|
--space-inline-md: var(--space-3);
|
|
275
275
|
--space-inline-lg: var(--space-4);
|
|
276
|
+
|
|
277
|
+
--space-inline-xl: var(--space-6);
|
|
276
278
|
--space-chrome-x: var(--space-6);
|
|
277
279
|
--space-chrome-y: var(--space-4);
|
|
278
280
|
--space-chrome-gap: var(--space-2);
|
package/docs/COMPONENTS.md
CHANGED
|
@@ -48,9 +48,13 @@ 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
|
-
| --------- | ---------- |
|
|
53
|
-
| `Flex` | `FlexProp` | `direction: "row" \| "col"`, `gap: GapProp`, `align`, `justify`, `wrap` |
|
|
51
|
+
| Component | Prop | Key props |
|
|
52
|
+
| --------- | ---------- | ---------------------------------------------------------------------------------------------------- |
|
|
53
|
+
| `Flex` | `FlexProp` | `direction: "row" \| "col"`, `gap: GapProp`, `gapRaw`, `pad`, `padRaw`, `fill`, `width`, `align`, `justify`, `wrap` |
|
|
54
|
+
|
|
55
|
+
`fill` / `width` are the `fixed | elastic | fixed` row: `width` pins a column (number = px, and
|
|
56
|
+
leaves `data-width-raw` on the DOM so the measurement stays countable), `fill` takes what is left
|
|
57
|
+
and sets `min-inline-size: 0` so a truncating child ellipses instead of widening the row.
|
|
54
58
|
|
|
55
59
|
---
|
|
56
60
|
|
|
@@ -92,6 +96,8 @@ Default `direction="row"`; use `direction="col"` for vertical rhythm.
|
|
|
92
96
|
| `Descriptions` | `DescriptionsProp` | `items[]` |
|
|
93
97
|
| `Badge` | `BadgeProp` | `status`, `tone: ToneProp` |
|
|
94
98
|
| `Badge` | `BadgeProp` | shadcn badge variants |
|
|
99
|
+
| `Progress` | `ProgressProp` | `value` (meter) \| `segments` (breakdown) |
|
|
100
|
+
| `Legend` | `LegendProp` | `items[]` — the key for a tone |
|
|
95
101
|
| `Popover` | Radix primitives | floating content panel |
|
|
96
102
|
| `ScrollArea` | Radix primitives | scrollable regions |
|
|
97
103
|
| `Collapsible` | Radix primitives | expand/collapse sections |
|
package/docs/DESIGN-AUTHORITY.md
CHANGED
|
@@ -17,9 +17,9 @@ It changes no code by itself. It is the tie-breaker a reviewer points at.
|
|
|
17
17
|
| Interaction semantics, keyboard, ARIA | **WAI-ARIA APG** | already followed — 33 references in `src/` |
|
|
18
18
|
| Behaviour primitives | **Radix** | already the implementation — 193 references |
|
|
19
19
|
| Component composition shape | **shadcn** | already the structural convention — 23 references |
|
|
20
|
-
| Component taxonomy / grouping | **Ant Design** groups | already the catalog shape: `data-entry`, `data-display`, `layout`, `feedback`, `navigation`, `general`
|
|
20
|
+
| Component taxonomy / grouping | **Ant Design** groups | already the catalog shape: `data-entry`, `data-display`, `layout`, `feedback`, `navigation`, `general` — a naming precedent, nothing is installed |
|
|
21
21
|
| Colour foundation | **SmartHR** | already the palette source — `--primary` = SmartHR MAIN `#0071bd`, `--foreground` = TEXT_BLACK, `--border` = BORDER |
|
|
22
|
-
| **Derived colour —
|
|
22
|
+
| **Derived colour — the interaction states hanging off each seed** | **Measured contrast (WCAG 2.2 / JIS X 8341-3)** | Authored in `src/tokens/derived.css`; no algorithm derives them. Four contrast suites read that file and hold every value to a threshold — see below |
|
|
23
23
|
| **Japanese UI convention — density, JP typography, form patterns** | **SmartHR** | **NEW — this decision.** Extends SmartHR from "where the colours came from" to the authority for how a JP business screen behaves |
|
|
24
24
|
| **Japanese accessibility / public-sector convention** | **デジタル庁 Design System** (Digital Agency) | **NEW — this decision.** The reference when a JP customer asks which standard a screen meets (JIS X 8341-3) |
|
|
25
25
|
| **Spacing, density, type scale, information architecture** | **IBM Carbon** | **NEW — this decision** |
|
|
@@ -187,70 +187,94 @@ Recorded rather than silently fixed, because each is a real decision:
|
|
|
187
187
|
strokes blur far more visibly than Latin letterforms. Another reason to prefer Carbon's integer
|
|
188
188
|
steps if the scale is ever revisited.
|
|
189
189
|
|
|
190
|
-
## Derived colour is
|
|
190
|
+
## Derived colour is AUTHORED, and MEASUREMENT is what makes it authoritative
|
|
191
191
|
|
|
192
|
-
**
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
192
|
+
**Twenty values, in `src/tokens/derived.css`:** `--primary-hover`, `--primary-active`,
|
|
193
|
+
`--primary-border`, `--destructive-hover`, `--destructive-active`, `--control-outline` and its
|
|
194
|
+
alpha, `--control-outline-error` and its alpha, and `--ring` — each in both themes. They are the
|
|
195
|
+
interaction states that hang off the five authored seeds per theme (SmartHR MAIN plus four 和色,
|
|
196
|
+
and the lifted dark ramp), which stay in `src/tokens/foundation.css`.
|
|
196
197
|
|
|
197
|
-
**
|
|
198
|
-
|
|
199
|
-
|
|
198
|
+
**These twenty were once generated.** A colour algorithm ran at build time, took the seeds and
|
|
199
|
+
emitted the derived map; the authority behind each value was "the algorithm said so". That
|
|
200
|
+
generator has been removed and its dependency with it. The values did not change — but the reason
|
|
201
|
+
to trust them had to.
|
|
200
202
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
`src/tokens/antd.generated.css`. `pnpm check:antd-tokens` fails CI if that file drifts from the
|
|
204
|
-
algorithm. **antd is a devDependency and a build-time tool only** — `pnpm check:no-antd-runtime`
|
|
205
|
-
asserts it never reaches `dependencies`, `src/`, or `dist/`.
|
|
203
|
+
**The authority is now the measurement, not the derivation.** Four suites read `derived.css`
|
|
204
|
+
directly and hold every value in it to a threshold this repo has already committed to:
|
|
206
205
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
206
|
+
| suite | what it holds |
|
|
207
|
+
| ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
208
|
+
| `src/tokens/__tests__/focus-ring-contrast.test.ts` | the focus mark in both switch positions — ≥3:1 (WCAG 2.2 SC 1.4.11) on every surface a control sits on, and the halo proven to be decoration rather than the indicator |
|
|
209
|
+
| `src/tokens/__tests__/interactive-fill-contrast.test.ts` | an interactive fill must clear **4.5:1** against the label sitting on it |
|
|
210
|
+
| `src/tokens/__tests__/destructive-contrast.test.ts` | `--destructive-hover` / `--destructive-active` against the same bar |
|
|
211
|
+
| `src/lib/__tests__/theme-tokens-css.test.ts` | the tier is actually loaded, and complete in both themes |
|
|
210
212
|
|
|
211
|
-
|
|
213
|
+
The first three also pin each value as a literal, so an edit to `derived.css` alone turns CI red
|
|
214
|
+
rather than quietly retinting the library. **That is a stronger claim than the generator made, not
|
|
215
|
+
a weaker one.** An algorithm guarantees a value is _consistent_; this repo had to override it four
|
|
216
|
+
times (below) precisely because consistent is not the same as _accessible_. A threshold guarantees
|
|
217
|
+
the property actually being sold.
|
|
212
218
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
boundary and it measures **1.43:1** on the page, where `--input` is held to 3:1 by SC 1.4.11 and
|
|
216
|
-
by `input-boundary-contrast.test.ts`.
|
|
217
|
-
- **The text ramp.** antd's is alpha-based (`rgba(0,0,0,0.88)`), which cannot enter this library's
|
|
218
|
-
opaque `H S% L%` triple without choosing a surface to composite against — lossy by construction.
|
|
219
|
-
- **The dark `--primary` itself.** `darkAlgorithm` MOVES the seed (antd's own `#1677ff` becomes
|
|
220
|
-
`#1668dc`). Feeding this library's light seed in gives `#0363a4` at **2.81:1** on the dark spine,
|
|
221
|
-
which `primary-text-contrast.test.ts` rejects outright; antd's transform of the committed dark
|
|
222
|
-
seed gives `#3794d3` at 5.36:1 where the seed itself measures 7.07:1. The dark theme therefore
|
|
223
|
-
keeps its seed and everything derived from it is antd's. This is the one structural divergence,
|
|
224
|
-
recorded in `DIVERGENCES` in the generator.
|
|
225
|
-
|
|
226
|
-
**Geometry antd owns was already correct.** `lineWidth` 1, `controlOutlineWidth` 2,
|
|
227
|
-
`lineWidthFocus` 3, `borderRadius` 6, `controlHeight` 32 and `fontSize` 14 all match the named
|
|
228
|
-
scales this library already ships (`--stroke-*`, `--radius`, `--band-height-md`,
|
|
229
|
-
`--font-size-base`). The generator asserts that agreement rather than emitting a second copy, so
|
|
230
|
-
the two cannot drift apart silently.
|
|
231
|
-
|
|
232
|
-
## Focus appearance — Ant Design owns the shape, and the indicator SHIPS OFF
|
|
233
|
-
|
|
234
|
-
### The two forms, read from antd's source
|
|
219
|
+
**To change a derived value:** change it, run `pnpm test`, and if a threshold breaks the value is
|
|
220
|
+
wrong. Do not relax the threshold.
|
|
235
221
|
|
|
236
|
-
**
|
|
237
|
-
|
|
222
|
+
**The four overrides that existed even while a generator did** — the clearest evidence the
|
|
223
|
+
algorithm was never the real authority. Stepping an interactive fill towards the label sitting on
|
|
224
|
+
it lands under 4.5:1, so those four states take the same ramp at the same step size in the
|
|
225
|
+
opposite direction:
|
|
238
226
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
227
|
+
| token | conventional step | shipped |
|
|
228
|
+
| ------------------------ | ----------------- | ----------------- |
|
|
229
|
+
| light `--primary-hover` | #208bc9 · 3.69:1 | #005596 · 7.53:1 |
|
|
230
|
+
| light `--primary-active` | #005596 · 7.53:1 | #003c70 · 10.97:1 |
|
|
231
|
+
| dark `--primary-hover` | #61b6e8 · 7.92:1 | #61b6e8 · 7.92:1 |
|
|
232
|
+
| dark `--primary-active` | #2f76a6 · 3.60:1 | #8bd0f3 · 10.50:1 |
|
|
245
233
|
|
|
246
|
-
**
|
|
247
|
-
side; every value is identical to the digit in `antd@5.29.3`.
|
|
234
|
+
**What was deliberately never derived, and why — each a measurement, not a preference:**
|
|
248
235
|
|
|
249
|
-
**
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
236
|
+
- **The neutral spine.** Colour foundation stays SmartHR's (see the table above); the neutrals are
|
|
237
|
+
not derived from the brand seed. The one derived role on offer for a control boundary measures
|
|
238
|
+
**1.43:1** on the page, where `--input` is held to 3:1 by SC 1.4.11 and by
|
|
239
|
+
`input-boundary-contrast.test.ts`.
|
|
240
|
+
- **The text ramp.** The derived one is alpha-based (`rgba(0,0,0,0.88)`), which cannot enter this
|
|
241
|
+
library's opaque `H S% L%` triple without choosing a surface to composite against — lossy by
|
|
242
|
+
construction.
|
|
243
|
+
- **The dark `--primary` itself.** A mechanical dark derivation MOVES the seed. Deriving from this
|
|
244
|
+
library's light seed gives `#0363a4` at **2.81:1** on the dark spine, which
|
|
245
|
+
`primary-text-contrast.test.ts` rejects outright; deriving from the committed dark seed gives
|
|
246
|
+
`#3794d3` at 5.36:1, where the seed itself measures 7.07:1. The dark theme therefore keeps its
|
|
247
|
+
own seed — recorded, with the measurement, in `focus-ring-contrast.test.ts`.
|
|
248
|
+
|
|
249
|
+
**The focus geometry was already on the named scale.** Border 1, halo width 2, heavy outline 3,
|
|
250
|
+
radius 6, control height 32 and font size 14 all match scales this library already ships
|
|
251
|
+
(`--stroke-*`, `--radius`, `--band-height-md`, `--font-size-base`), so the focus tokens bind to
|
|
252
|
+
those steps instead of restating a second copy of the number.
|
|
253
|
+
|
|
254
|
+
**antd itself is gone from this repository**, and `pnpm check:no-antd-runtime` is what keeps it
|
|
255
|
+
gone: it fails if antd or any `@ant-design/*` package appears in any manifest field, is imported
|
|
256
|
+
anywhere in `src/`, or leaves a trace in `dist/`. Deleting that gate re-opens the door it closes.
|
|
257
|
+
|
|
258
|
+
## Focus appearance — a two-form convention, and the indicator SHIPS OFF
|
|
259
|
+
|
|
260
|
+
### The two forms
|
|
261
|
+
|
|
262
|
+
**SmartHR** (`smarthr-ui@99.6.0`) draws focus as an opaque ring held off the control by a white
|
|
263
|
+
spacer. This library takes a different, two-form convention instead — widely used in enterprise
|
|
264
|
+
component libraries, and surveyed across several before it was adopted here:
|
|
265
|
+
|
|
266
|
+
- **Field** — the boundary RECOLOURS to the primary at its unchanged hairline width, plus
|
|
267
|
+
`box-shadow: 0 0 0 var(--control-outline-width) var(--control-outline)`. A Select emits the same
|
|
268
|
+
declarations as an Input, which is why the two focus identically.
|
|
269
|
+
- **Non-field** — an outline outside the box model:
|
|
270
|
+
`outline: var(--focus-outline-weight) solid var(--primary-border); outline-offset: 1px`.
|
|
271
|
+
|
|
272
|
+
**Why this shape wins.** The library previously shipped an opaque brand ring drawn immediately
|
|
273
|
+
outside an untouched grey border — two outlines of different colours claiming the same edge — and a
|
|
274
|
+
Select that could not be told to agree with an Input. Recolouring the existing boundary rather than
|
|
275
|
+
adding a second one resolves both. SmartHR still owns the hue: the focus colour is `--ring`, which
|
|
276
|
+
the derived tier declares as `var(--primary)`, because a focused field takes the primary rather than
|
|
277
|
+
a focus colour of its own.
|
|
254
278
|
|
|
255
279
|
### The indicator ships OFF. That forfeits WCAG 2.4.7 and a JIS X 8341-3 AA claim.
|
|
256
280
|
|
|
@@ -272,15 +296,15 @@ owner chose to ship it off and let whoever needs it turn it on. What that costs,
|
|
|
272
296
|
<html data-focus-outline="on">
|
|
273
297
|
```
|
|
274
298
|
|
|
275
|
-
**The switch is
|
|
276
|
-
|
|
299
|
+
**The switch is a single multiplier, not a scatter of overrides.** `--focus-outline` is one flag,
|
|
300
|
+
and every painted focus length multiplies by it:
|
|
277
301
|
|
|
278
|
-
```
|
|
279
|
-
|
|
302
|
+
```css
|
|
303
|
+
--focus-ring-width: calc(var(--focus-ring-weight) * var(--focus-outline));
|
|
280
304
|
```
|
|
281
305
|
|
|
282
|
-
|
|
283
|
-
|
|
306
|
+
Setting it to `0` zeroes every focus length at once, so no component rebind can bring the mark back
|
|
307
|
+
while it is off — which is the property a scatter of per-component overrides could never give. The two paints
|
|
284
308
|
that are not lengths — the halo and a field's recoloured boundary — are scoped to the same attribute
|
|
285
309
|
in `styles/focus-ring.css`. **Every `:focus-visible` selector stays exactly where it is**; only the
|
|
286
310
|
painted result disappears.
|
|
@@ -288,8 +312,8 @@ painted result disappears.
|
|
|
288
312
|
### The ON state is the LIGHT one
|
|
289
313
|
|
|
290
314
|
The complaint was weight, not existence, so the on-position is not the old mark restored. It paints
|
|
291
|
-
|
|
292
|
-
`
|
|
315
|
+
the **field** indicator on every control — one hairline (1px) in the focus hue, plus the
|
|
316
|
+
`--control-outline` halo — rather than the heavy 3px outline form. Measured, in Chromium, on
|
|
293
317
|
`ql.test` after the transition settles:
|
|
294
318
|
|
|
295
319
|
| control | switch off | switch on |
|
|
@@ -298,8 +322,8 @@ antd's **field** indicator on every control — one `lineWidth` (1px) in the foc
|
|
|
298
322
|
| Button (primary) | outline `0px`, resting shadow intact | `outline: 1px solid rgb(0,113,189)` @ `0px` + same halo |
|
|
299
323
|
| Sidebar nav row / list row | outline `0px` | `outline: 1px solid rgb(0,113,189)` @ **`-1px`** (inset into the row) |
|
|
300
324
|
|
|
301
|
-
The field pair is
|
|
302
|
-
seed. The nav row insets its mark into its own shape rather than wrapping an already-shaded surface,
|
|
325
|
+
The field pair is the recoloured boundary plus the halo, exactly as the convention specifies for
|
|
326
|
+
this seed. The nav row insets its mark into its own shape rather than wrapping an already-shaded surface,
|
|
303
327
|
which is the specific stacking that read as heavy.
|
|
304
328
|
|
|
305
329
|
**No control's box moves when it is focused**, measured with `getBoundingClientRect()` before and
|
|
@@ -308,11 +332,12 @@ Input, Save and Delete all stay at 32.00px.
|
|
|
308
332
|
|
|
309
333
|
**Two criteria, and only one is met by thickness alone.** SC 1.4.11 (AA, non-text contrast) is about
|
|
310
334
|
COLOUR — the 1px mark measures 5.05:1 light and 7.07:1 dark on every surface a control sits on, so
|
|
311
|
-
the light weight costs nothing there.
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
`--focus-outline-weight: var(--stroke-lg)` restores
|
|
335
|
+
the light weight costs nothing there. `--primary-border` (#6dc0e3) measures **2.00:1 light /
|
|
336
|
+
1.66:1 dark** and could not have satisfied it at any thickness, which is why the on-state takes the
|
|
337
|
+
focus hue instead — the single place it departs from the outline form. SC 2.4.13 Focus Appearance
|
|
338
|
+
(AAA) additionally wants a 2px perimeter; the on-state does not target it, and
|
|
339
|
+
`--focus-outline-weight: var(--stroke-lg)` restores the 3px weight if a customer needs the area
|
|
340
|
+
clause.
|
|
316
341
|
|
|
317
342
|
Both positions of the switch are gated in `src/tokens/__tests__/focus-ring-contrast.test.ts`: with
|
|
318
343
|
it off nothing paints and no rebind can route around it, with it on the geometry and the ≥3:1
|
|
@@ -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: **
|
|
7
|
-
- With ≥1 frame: **
|
|
6
|
+
- Public components: **131**
|
|
7
|
+
- With ≥1 frame: **131** · zero-frame: **0**
|
|
8
8
|
- Fully covered (every contract axis declared): **0**
|
|
9
9
|
|
|
10
10
|
## Per-component contract axes
|
|
@@ -41,6 +41,7 @@ Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
|
|
|
41
41
|
| StatCard | ✓ | N/A | N/A | · | · | · | · | · | · | · |
|
|
42
42
|
| ServiceLauncherCard | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
43
43
|
| Badge | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
|
|
44
|
+
| Legend | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
44
45
|
| ListRow | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
|
|
45
46
|
| CredentialReveal | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
|
|
46
47
|
| QrCode | ✓ | ✓ | N/A | · | · | · | · | · | · | · |
|
|
@@ -106,7 +107,7 @@ Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
|
|
|
106
107
|
| Avatar | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
|
|
107
108
|
| Separator | ✓ | · | N/A | N/A | · | · | · | · | · | · |
|
|
108
109
|
| Skeleton | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
109
|
-
| Toggle | ✓ | ✓ |
|
|
110
|
+
| Toggle | ✓ | ✓ | · | N/A | · | · | · | · | · | · |
|
|
110
111
|
| ToggleGroup | ✓ | ✓ | · | · | · | · | · | · | · | · |
|
|
111
112
|
| AspectRatio | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
112
113
|
| Accordion | ✓ | N/A | · | · | · | · | · | · | · | · |
|
|
@@ -123,6 +124,7 @@ Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
|
|
|
123
124
|
| ResizablePanel | ✓ | N/A | · | N/A | · | · | · | · | · | · |
|
|
124
125
|
| Carousel | ✓ | N/A | N/A | · | · | · | · | · | · | · |
|
|
125
126
|
| AppSettingPicker | ✓ | N/A | · | · | · | · | · | · | · | · |
|
|
127
|
+
| AppSettingToggle | ✓ | N/A | · | · | · | · | · | · | · | · |
|
|
126
128
|
| Field | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
127
129
|
| LineChart | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
|
|
128
130
|
| BarChart | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
|
package/docs/README.md
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
# @godxjp/ui Documentation
|
|
2
2
|
|
|
3
|
-
| Doc | Purpose
|
|
4
|
-
| -------------------------------------------- |
|
|
5
|
-
| [CONSUMER-RULES.md](./CONSUMER-RULES.md) | **Start here if you build an app with @godxjp/ui** — the ten rules the audit enforces (styles entry, layout owners, real primitives, tokens)
|
|
6
|
-
| [DEVELOPMENT.md](./DEVELOPMENT.md) | **Contributor guideline** — role/boundary, architecture, how to add/extend, verify
|
|
7
|
-
| [COMPONENTS.md](./COMPONENTS.md) | Component catalog by
|
|
8
|
-
| [PROPS-VOCABULARY.md](./PROPS-VOCABULARY.md) | Atomic prop types (`*Prop` suffix)
|
|
9
|
-
| [PROPS-REGISTRY.md](./PROPS-REGISTRY.md) | Machine-readable registry + forbidden aliases
|
|
10
|
-
| [DATETIME.md](./DATETIME.md) | **Mandatory** `formatDate` — all date/time display
|
|
11
|
-
| [DESIGN-AUTHORITY.md](./DESIGN-AUTHORITY.md) | **Which external standard owns which decision** — APG, Radix, shadcn, Ant Design groups, SmartHR colour, Carbon geometry. Read before inventing a spacing/density/layout answer |
|
|
12
|
-
| [SPACING.md](./SPACING.md) | Golden ratio (φ) macro spacing — PageContainer sections, Flex gap, Card rhythm
|
|
13
|
-
| [FORMS.md](./FORMS.md) | **Mandatory** react-hook-form + Zod 4
|
|
14
|
-
| [TESTING.md](./TESTING.md) | **Mandatory** Vitest per component
|
|
15
|
-
| [FRAME-A11Y-CI.md](./FRAME-A11Y-CI.md) | Per-frame axe a11y + geometry + coverage CI gates — how to run/regenerate locally
|
|
16
|
-
| [../README.md](../README.md) | Setup, workspace wiring, theme
|
|
3
|
+
| Doc | Purpose |
|
|
4
|
+
| -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
5
|
+
| [CONSUMER-RULES.md](./CONSUMER-RULES.md) | **Start here if you build an app with @godxjp/ui** — the ten rules the audit enforces (styles entry, layout owners, real primitives, tokens) |
|
|
6
|
+
| [DEVELOPMENT.md](./DEVELOPMENT.md) | **Contributor guideline** — role/boundary, architecture, how to add/extend, verify |
|
|
7
|
+
| [COMPONENTS.md](./COMPONENTS.md) | Component catalog by taxonomy group |
|
|
8
|
+
| [PROPS-VOCABULARY.md](./PROPS-VOCABULARY.md) | Atomic prop types (`*Prop` suffix) |
|
|
9
|
+
| [PROPS-REGISTRY.md](./PROPS-REGISTRY.md) | Machine-readable registry + forbidden aliases |
|
|
10
|
+
| [DATETIME.md](./DATETIME.md) | **Mandatory** `formatDate` — all date/time display |
|
|
11
|
+
| [DESIGN-AUTHORITY.md](./DESIGN-AUTHORITY.md) | **Which external standard owns which decision** — APG, Radix, shadcn, Ant Design groups, SmartHR colour, Carbon geometry, measured contrast for derived colour. Read before inventing a spacing/density/layout answer |
|
|
12
|
+
| [SPACING.md](./SPACING.md) | Golden ratio (φ) macro spacing — PageContainer sections, Flex gap, Card rhythm |
|
|
13
|
+
| [FORMS.md](./FORMS.md) | **Mandatory** react-hook-form + Zod 4 |
|
|
14
|
+
| [TESTING.md](./TESTING.md) | **Mandatory** Vitest per component |
|
|
15
|
+
| [FRAME-A11Y-CI.md](./FRAME-A11Y-CI.md) | Per-frame axe a11y + geometry + coverage CI gates — how to run/regenerate locally |
|
|
16
|
+
| [../README.md](../README.md) | Setup, workspace wiring, theme |
|
|
17
17
|
|
|
18
18
|
## Preview
|
|
19
19
|
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# Cái gì đáng đưa vào @godxjp/ui
|
|
2
|
+
|
|
3
|
+
Một design system hỏng theo hai hướng ngược nhau, và cả hai đều hỏng thật:
|
|
4
|
+
|
|
5
|
+
- **Quá hẹp** — thiếu prop, nên consumer buộc phải lách bằng class hoặc mã màu
|
|
6
|
+
gõ tay. Đây là hướng đang hỏng: một lượt kiểm trên godx-chat (08/09/2026) ra
|
|
7
|
+
**51 lỗi, 42 trong đó đến từ ĐÚNG MỘT prop còn thiếu** (đệm trên primitive bố
|
|
8
|
+
cục).
|
|
9
|
+
- **Quá rộng** — nhận mọi yêu cầu, thành một đống prop không ai nhớ, và mỗi
|
|
10
|
+
prop là một mảnh DOM nội bộ bị đóng băng thành API công khai.
|
|
11
|
+
|
|
12
|
+
Tài liệu này là bộ lọc giữa hai hướng đó. Nó cố tình viết thành câu hỏi **trả
|
|
13
|
+
lời được bằng có/không**, không phải nguyên tắc để cảm nhận.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Ba câu hỏi — phải ĐỦ CẢ BA
|
|
18
|
+
|
|
19
|
+
### 1. Có phải consumer KHÔNG CÓ nước đi hợp lệ nào không?
|
|
20
|
+
|
|
21
|
+
Không phải "bất tiện". Là **bất khả**: mọi prop và token đang có đều không nói
|
|
22
|
+
được điều cần nói, và mọi đường còn lại đều bị `ui-audit` chặn.
|
|
23
|
+
|
|
24
|
+
- ✅ Thiết kế cần 12px giữa hai phần tử trên một HÀNG. Thang bậc tên đọc trục
|
|
25
|
+
dọc nên `md` = 16px; làm tròn thì lệch bố cục; viết `gap: 12px` thì
|
|
26
|
+
`no-arbitrary-spacing` chặn. **Không có nước đi.** → gh#401, đã vá.
|
|
27
|
+
- ❌ "Viết `<Flex gap={3}>` dài hơn `gap-3`." Có nước đi, chỉ là dài hơn.
|
|
28
|
+
|
|
29
|
+
Cách kiểm: viết ra đoạn mã bạn _muốn_ viết, chạy `ui-audit` lên nó. Nếu nó
|
|
30
|
+
xanh, bạn đã có nước đi.
|
|
31
|
+
|
|
32
|
+
### 2. Nó thuộc về HÌNH DẠNG của component, hay thuộc về NỘI DUNG của một màn?
|
|
33
|
+
|
|
34
|
+
Design system sở hữu hình dạng. Màn hình sở hữu nội dung.
|
|
35
|
+
|
|
36
|
+
- ✅ `Flex` không có đệm. Mọi hàng/cột trong mọi ứng dụng đều có thể cần đệm.
|
|
37
|
+
- ✅ `TableHead` không có trục căn lề. Mọi bảng có cột số đều cần.
|
|
38
|
+
- ❌ "Bảng quản trị thành viên cần cột vai trò rộng 8rem." Đó là màn hình ấy.
|
|
39
|
+
- ❌ "Dashboard cần bốn thẻ chỉ số xếp ngang." Đó là bố cục của một trang.
|
|
40
|
+
|
|
41
|
+
Câu hỏi phụ khi phân vân: **consumer thứ hai có gặp không?** Nếu câu trả lời là
|
|
42
|
+
"chắc là không" thì gần như luôn là nội dung, không phải hình dạng.
|
|
43
|
+
|
|
44
|
+
### 3. Diễn đạt được thành một TRỤC CÓ TÊN không?
|
|
45
|
+
|
|
46
|
+
Một prop phải gọi được **ý định**, không chỉ mở một lỗ.
|
|
47
|
+
|
|
48
|
+
- ✅ `pad={{ blockStart: 3 }}` — trục logic, bậc thang, đọc ra nghĩa.
|
|
49
|
+
- ❌ `styles={{ body: {...}, header: {...} }}` — không phải một trục; nó là một
|
|
50
|
+
lỗ tự do, và nó đóng băng tên khe DOM nội bộ thành API công khai. Xem mục
|
|
51
|
+
"Không đáng" bên dưới.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Bốn dấu hiệu MẠNH — một cái là đủ để ưu tiên làm ngay
|
|
56
|
+
|
|
57
|
+
Ba câu trên quyết định _có thuộc về đây không_. Bốn dấu hiệu này quyết định
|
|
58
|
+
_làm trước hay sau_.
|
|
59
|
+
|
|
60
|
+
### a. Nó chặn một yêu cầu TIẾP CẬN
|
|
61
|
+
|
|
62
|
+
Không thương lượng, làm trước.
|
|
63
|
+
Ví dụ đã gặp: vòng tiêu điểm của Input là 1px (hạ xuống có chủ ý), không
|
|
64
|
+
đạt WCAG 2.4.11 vốn đòi vùng chỉ báo tương đương đường 2px. Và `FormRoot` không
|
|
65
|
+
có móc `onInvalid`, nên không đưa được tiêu điểm về trường lỗi đầu tiên — WCAG
|
|
66
|
+
3.3.1 / 2.4.3.
|
|
67
|
+
|
|
68
|
+
### b. Nó hỏng IM LẶNG
|
|
69
|
+
|
|
70
|
+
Không lỗi biên dịch, không test đỏ, không cảnh báo — chỉ sai.
|
|
71
|
+
Ví dụ đã gặp: `AppShell` nhận cả `logo` lẫn `topbar` nhưng `resolvedTopbar` trả
|
|
72
|
+
thẳng `topbar`, nên `logo` **không bao giờ được vẽ**. godx-chat truyền cả hai
|
|
73
|
+
suốt nhiều tháng và không ai phát hiện.
|
|
74
|
+
|
|
75
|
+
Lớp lỗi này đắt gấp nhiều lần lớp lỗi ồn ào, vì nó không có ai báo.
|
|
76
|
+
|
|
77
|
+
### c. Đếm được
|
|
78
|
+
|
|
79
|
+
Một khoảng trống đẻ ra N lỗi audit là một khoảng trống có số đo, không phải một
|
|
80
|
+
ý kiến. 42 lỗi từ một prop thiếu là một ưu tiên; một lỗi từ một prop thiếu thì
|
|
81
|
+
chưa chắc.
|
|
82
|
+
|
|
83
|
+
### d. Nó BẤT ĐỐI XỨNG với thứ đã có
|
|
84
|
+
|
|
85
|
+
Nếu trục dọc có `xl` mà trục ngang không có, khoảng trống ấy gần như luôn là
|
|
86
|
+
**quên**, không phải quyết định. Sửa cho cân là rẻ và không tranh cãi.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Cái KHÔNG đáng vào — và vì sao
|
|
91
|
+
|
|
92
|
+
**Bố cục của một màn hình cụ thể.** Nó thuộc về màn hình đó. Nếu ba màn cùng
|
|
93
|
+
cần, lúc ấy nó đã thành hình dạng và quay lại câu hỏi 2.
|
|
94
|
+
|
|
95
|
+
**Một giá trị dùng đúng một lần.** Đừng đẻ prop cho nó — dùng cửa thoát
|
|
96
|
+
(`gapRaw`, `padRaw`). Cửa thoát để lại `data-*-raw` trên DOM nên đếm được; khi
|
|
97
|
+
số đếm ấy lớn lên, ĐÓ mới là lúc nó thành một trục đáng có tên.
|
|
98
|
+
|
|
99
|
+
**Lỗ kiểu dáng tự do** (`styles={{ slot }}`, `classNames={{ slot }}`). Nó đóng
|
|
100
|
+
băng cấu trúc DOM nội bộ thành API công khai, và audit **không đếm được** nó
|
|
101
|
+
(một object style dựng ở đâu cũng được rồi spread vào). Nó cũng ngược hẳn với
|
|
102
|
+
`style: 0/289` — lập trường nhất quán hiện nay là cho prop ý định, không cho lỗ
|
|
103
|
+
kiểu dáng.
|
|
104
|
+
|
|
105
|
+
**Gu thẩm mỹ.** "Dùng nhiều khoảng trắng", "hai màu nhấn là đủ", "tránh lưới
|
|
106
|
+
bento". Không kiểm chứng được, không cổng nào canh, và khi nó tới tay agent
|
|
107
|
+
TRƯỚC luật cứng thì kết quả là mã đẹp mà sai hợp đồng.
|
|
108
|
+
|
|
109
|
+
**Thứ chỉ tiết kiệm vài dòng ở call site.** Nếu đường hiện tại đã hợp lệ và
|
|
110
|
+
đọc được, việc rút ngắn nó không phải việc của design system.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Thêm rồi thì NỢ gì
|
|
115
|
+
|
|
116
|
+
Một prop mới chưa xong khi nó biên dịch được. Ba việc bắt buộc, và cả ba đều đã
|
|
117
|
+
có cổng CI:
|
|
118
|
+
|
|
119
|
+
1. **Vào catalog.** Chạy `node scripts/gen-component-api-manifest.mjs`.
|
|
120
|
+
`check:component-api-manifest` canh việc này. **Không vào catalog thì agent
|
|
121
|
+
không biết nó tồn tại** — và đó không phải giả thuyết: `pad`/`padRaw` được
|
|
122
|
+
thêm vào gói nhưng catalog phát hành chưa có, nên một agent tra MCP đúng quy
|
|
123
|
+
trình vẫn kết luận "Flex chỉ có gap" và bỏ cuộc trước 21 lỗi mà nó có thể
|
|
124
|
+
sửa.
|
|
125
|
+
|
|
126
|
+
2. **Có bằng chứng phủ từng nhánh giá trị.** `component-case-evidence.json` +
|
|
127
|
+
`check:frame-coverage-ledger`. Bằng chứng phải trỏ vào test THẬT SỰ chạy qua
|
|
128
|
+
nhánh ấy, không phải một tệp cho có.
|
|
129
|
+
|
|
130
|
+
3. **Có ví dụ BIÊN DỊCH ĐƯỢC.** Hậu quả đo được khi không kiểm: pattern
|
|
131
|
+
`confirm-destructive` truyền cho `Dialog` một prop `mode` không tồn tại,
|
|
132
|
+
hướng dẫn DataTable dùng `variant="success"` (chính thứ luật
|
|
133
|
+
`status-tone-not-variant` cấm), và ví dụ `Input` gọi một prop
|
|
134
|
+
`onValueChange` không có thật. Agent chép chúng và viết ra mã hỏng.
|
|
135
|
+
|
|
136
|
+
Ba chỗ ấy đã sửa, và `check:doc-prop-existence` nay bắt được lớp lỗi đó.
|
|
137
|
+
Lưu ý khi viết tài liệu: cổng ấy đọc mọi đoạn JSX trong `docs/**` như mã
|
|
138
|
+
THẬT, nên **trích dẫn một API sai để làm ví dụ cũng bị bắt**. Mô tả bằng lời
|
|
139
|
+
như đoạn trên, đừng dán một thẻ JSX hoàn chỉnh vào.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Một lợi ích ẩn của việc rời Radix, đo được
|
|
144
|
+
|
|
145
|
+
`check:doc-prop-existence` canh chiều "mọi prop dùng trong ví dụ đều phải tồn
|
|
146
|
+
tại" — chính lớp lỗi khiến agent chép ví dụ rồi viết ra mã hỏng (prop `mode`
|
|
147
|
+
trên `Dialog`, prop `onValueChange` trên `Input`). Nhưng nó **bỏ qua mọi
|
|
148
|
+
component bọc primitive bên thứ ba**, vì với chúng manifest chỉ là cận dưới:
|
|
149
|
+
prop thật nằm trong types của Radix, generator không mở ra được.
|
|
150
|
+
|
|
151
|
+
Số đo (08/09/2026):
|
|
152
|
+
|
|
153
|
+
| | component KHÔNG kiểm được |
|
|
154
|
+
| --------------------------------------------- | ------------------------- |
|
|
155
|
+
| `main` | **139 / 289** |
|
|
156
|
+
| `feat/v20-react-aria` (6 primitive đã chuyển) | **126 / 289** |
|
|
157
|
+
|
|
158
|
+
Mỗi primitive rời Radix là `declaredIn` chuyển từ `node_modules` vào `src/`, và
|
|
159
|
+
component đó **bước vào vùng kiểm được**. Sáu primitive đầu đã gỡ rào cho 13
|
|
160
|
+
component.
|
|
161
|
+
|
|
162
|
+
Nên đợt đổi nền không chỉ là thay thư viện. Nó là cách duy nhất làm cho 126
|
|
163
|
+
component còn lại có thể được canh — và trong số đó có `Dialog`, `Input`,
|
|
164
|
+
`Badge`, tức đúng những component mà ví dụ sai đã lọt qua.
|
|
165
|
+
|
|
166
|
+
## Khi câu trả lời là "không thuộc về đây"
|
|
167
|
+
|
|
168
|
+
Nói ra ở chỗ gặp nó, bằng mã:
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
/*
|
|
172
|
+
* KHÔNG thuộc về design system: bề rộng này là số đo của riêng màn hình
|
|
173
|
+
* quản trị thành viên, không phải một trục của Table. Nếu màn thứ hai cần
|
|
174
|
+
* cùng thứ, lúc đó mở issue.
|
|
175
|
+
*/
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Một dòng như thế đáng giá hơn một issue mở rồi bỏ đấy — nó nói cho người sau
|
|
179
|
+
biết ai đã cân nhắc, và cân nhắc ra sao.
|