@hideyukimori/nene2-ui 0.8.0 → 0.14.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/README.md +96 -4
- package/dist/data/DataTable.js +1 -1
- package/dist/data/DataTable.js.map +1 -1
- package/dist/data/DetailList.js +1 -1
- package/dist/data/DetailList.js.map +1 -1
- package/dist/data/Pagination.js +1 -1
- package/dist/data/Pagination.js.map +1 -1
- package/dist/feedback/Badge.js +3 -3
- package/dist/feedback/Badge.js.map +1 -1
- package/dist/feedback/InlineAlert.d.ts +13 -4
- package/dist/feedback/InlineAlert.js +5 -5
- package/dist/feedback/InlineAlert.js.map +1 -1
- package/dist/feedback/ToastProvider.js +2 -2
- package/dist/feedback/ToastProvider.js.map +1 -1
- package/dist/forms/FormField.d.ts +11 -1
- package/dist/forms/FormField.js +7 -3
- package/dist/forms/FormField.js.map +1 -1
- package/dist/forms/field-context.d.ts +13 -0
- package/dist/forms/field-context.js +6 -1
- package/dist/forms/field-context.js.map +1 -1
- package/dist/layout/Card.js +1 -1
- package/dist/layout/Card.js.map +1 -1
- package/dist/layout/PageHeader.js +1 -1
- package/dist/layout/PageHeader.js.map +1 -1
- package/dist/lib/states.d.ts +33 -3
- package/dist/lib/states.js +37 -2
- package/dist/lib/states.js.map +1 -1
- package/dist/overlay/Modal.js +1 -1
- package/dist/overlay/Modal.js.map +1 -1
- package/dist/primitives/Button.js +32 -8
- package/dist/primitives/Button.js.map +1 -1
- package/dist/primitives/Checkbox.d.ts +21 -6
- package/dist/primitives/Checkbox.js +20 -10
- package/dist/primitives/Checkbox.js.map +1 -1
- package/dist/primitives/Input.js +2 -2
- package/dist/primitives/Input.js.map +1 -1
- package/dist/primitives/Radio.d.ts +20 -6
- package/dist/primitives/Radio.js +19 -10
- package/dist/primitives/Radio.js.map +1 -1
- package/dist/primitives/Select.js +2 -2
- package/dist/primitives/Select.js.map +1 -1
- package/dist/primitives/Spinner.js +1 -1
- package/dist/primitives/Spinner.js.map +1 -1
- package/dist/primitives/Switch.js +4 -2
- package/dist/primitives/Switch.js.map +1 -1
- package/dist/primitives/Text.js +1 -1
- package/dist/primitives/Text.js.map +1 -1
- package/dist/primitives/Textarea.js +2 -2
- package/dist/primitives/Textarea.js.map +1 -1
- package/dist/states/EmptyState.js +1 -1
- package/dist/states/EmptyState.js.map +1 -1
- package/dist/states/ErrorState.js +1 -1
- package/dist/states/ErrorState.js.map +1 -1
- package/package.json +1 -1
- package/themes/default.css +213 -2
package/themes/default.css
CHANGED
|
@@ -19,6 +19,30 @@
|
|
|
19
19
|
--color-on-accent: oklch(99% 0 0);
|
|
20
20
|
--color-danger: oklch(55% 0.2 25);
|
|
21
21
|
|
|
22
|
+
/* 🔴 Four contract colours the theme did not define. They are not new: `warn`,
|
|
23
|
+
* `warn-soft`, `on-warn` and `danger-soft` are in the frozen Core Token Contract v1
|
|
24
|
+
* (28 colours), and this theme defined 8 of them. Twenty are still missing — see the
|
|
25
|
+
* README — but these four are what a validity state needs.
|
|
26
|
+
*
|
|
27
|
+
* Same root cause as the radius scale: the theme was promoted verbatim from nene-payout,
|
|
28
|
+
* and payout has no warn. Six other ships do (nene-vault, nene-invoice, nene-field,
|
|
29
|
+
* nene-records, nene-deal, nene-profile), so `--color-x-slot-alert-warn-*` pointed at
|
|
30
|
+
* `--color-danger` — a warning that renders as an error. Promoting from the product with
|
|
31
|
+
* the fewest violations exported its missing distinctions along with its consistency.
|
|
32
|
+
*
|
|
33
|
+
* Values are the fleet median of the eight `--color-warn` definitions in use
|
|
34
|
+
* (2026-08-23; nene-field's hex converted to OKLCH to be counted).
|
|
35
|
+
*
|
|
36
|
+
* 🔴 `on-warn` is measured, not chosen. Against this `warn` fill: white is 3.48:1 and
|
|
37
|
+
* `text-primary` is 4.47:1 — which misses the 4.5:1 body-text threshold by 0.03, and is
|
|
38
|
+
* exactly the kind of near-miss that survives review by eye. The dark amber below is
|
|
39
|
+
* 4.97:1. The `warn` fill itself is 3.58:1 against `surface-raised`, clearing the 3:1
|
|
40
|
+
* that WCAG 1.4.11 asks of a non-text boundary. */
|
|
41
|
+
--color-warn: oklch(63% 0.135 75);
|
|
42
|
+
--color-warn-soft: oklch(95% 0.035 80);
|
|
43
|
+
--color-on-warn: oklch(21% 0.04 75);
|
|
44
|
+
--color-danger-soft: oklch(95% 0.03 30);
|
|
45
|
+
|
|
22
46
|
--shadow-sm: 0 1px 2px oklch(0% 0 0 / 0.06);
|
|
23
47
|
|
|
24
48
|
/* Typography. Until 0.3.0 the kit had none of these and the components carried
|
|
@@ -54,6 +78,21 @@
|
|
|
54
78
|
--text-x-xl: 1.5rem;
|
|
55
79
|
--text-x-ios-floor: 16px;
|
|
56
80
|
|
|
81
|
+
/* The smallest a control may be on a touch screen. Not a design step, for the same reason
|
|
82
|
+
* `--text-x-ios-floor` is not: it is what the platform requires, and a product lowering it
|
|
83
|
+
* is not expressing a preference, it is making targets people miss.
|
|
84
|
+
*
|
|
85
|
+
* 🔴 44px, which is what WCAG 2.5.5 (AAA) and Apple's HIG both ask for — the smallest
|
|
86
|
+
* value that satisfies the standards rather than the fleet's average. Two ships already
|
|
87
|
+
* set 48px (nene-profile, nene-deal), Material's number; a product that wants it raises
|
|
88
|
+
* the slot. Picking the higher one here would be the kit imposing a size, and picking a
|
|
89
|
+
* median would make a device constraint look like a preference.
|
|
90
|
+
*
|
|
91
|
+
* ⚠️ The kit's own `sm` button computes to about 31px against a 14px body — above the
|
|
92
|
+
* 24px WCAG 2.5.8 (AA) asks for, and well under this. The `md` button is already ~47px,
|
|
93
|
+
* so this changes nothing there. */
|
|
94
|
+
--spacing-x-touch-floor: 44px;
|
|
95
|
+
|
|
57
96
|
/* ─────────────────────────────────────────────────────────────────────────
|
|
58
97
|
* ② SLOTS — a product may redefine these.
|
|
59
98
|
*
|
|
@@ -70,6 +109,12 @@
|
|
|
70
109
|
--spacing-x-slot-button-pad-y: var(--spacing-x-xs);
|
|
71
110
|
--spacing-x-slot-button-sm-pad-x: var(--spacing-x-xs);
|
|
72
111
|
--spacing-x-slot-button-sm-pad-y: var(--spacing-x-3xs);
|
|
112
|
+
/* 🔴 The gap between an icon and its label. It exists because the button became a flex
|
|
113
|
+
* container in 0.13.0, and a flex container drops the whitespace that used to separate
|
|
114
|
+
* them: `<Button><Icon /> Save</Button>` renders the text as an anonymous flex item, and
|
|
115
|
+
* leading whitespace in one is stripped. `3xs` is 0.25rem, which is about the width of
|
|
116
|
+
* the space that disappears — so the change of display does not also change the spacing. */
|
|
117
|
+
--spacing-x-slot-button-gap: var(--spacing-x-3xs);
|
|
73
118
|
--spacing-x-slot-control-pad-x: var(--spacing-x-2xs);
|
|
74
119
|
--spacing-x-slot-control-pad-y: var(--spacing-x-xs);
|
|
75
120
|
--spacing-x-slot-field-gap: var(--spacing-x-2xs);
|
|
@@ -126,9 +171,11 @@
|
|
|
126
171
|
--color-x-slot-alert-info-bg: var(--color-surface);
|
|
127
172
|
--color-x-slot-alert-info-fg: var(--color-text-primary);
|
|
128
173
|
--color-x-slot-alert-info-border: var(--color-border);
|
|
174
|
+
/* 🔴 Changed in 0.12.0: these pointed at `--color-danger`, so a warning rendered as an
|
|
175
|
+
* error. Not a shade — a different meaning wearing the wrong one. */
|
|
129
176
|
--color-x-slot-alert-warn-bg: var(--color-surface);
|
|
130
|
-
--color-x-slot-alert-warn-fg: var(--color-
|
|
131
|
-
--color-x-slot-alert-warn-border: var(--color-
|
|
177
|
+
--color-x-slot-alert-warn-fg: var(--color-on-warn);
|
|
178
|
+
--color-x-slot-alert-warn-border: var(--color-warn);
|
|
132
179
|
--color-x-slot-alert-danger-bg: var(--color-surface);
|
|
133
180
|
--color-x-slot-alert-danger-fg: var(--color-danger);
|
|
134
181
|
--color-x-slot-alert-danger-border: var(--color-danger);
|
|
@@ -137,6 +184,10 @@
|
|
|
137
184
|
--text-x-slot-field-hint-size: var(--text-x-2xs);
|
|
138
185
|
--color-x-slot-field-error: var(--color-danger);
|
|
139
186
|
--text-x-slot-field-error-size: var(--text-x-2xs);
|
|
187
|
+
/* 🔴 `on-warn`, not `warn`. This text sits on the page, not on a warn fill: `--color-warn`
|
|
188
|
+
* measures 3.58:1 against `surface-raised`, which clears the 3:1 a border needs and misses
|
|
189
|
+
* the 4.5:1 body text needs. The dark amber clears both. */
|
|
190
|
+
--color-x-slot-field-warning-fg: var(--color-on-warn);
|
|
140
191
|
--text-x-slot-field-label-size: var(--text-x-xs);
|
|
141
192
|
/* 🔴 `var(--font-weight-medium)`, not `500`. Identical output — Tailwind defines that
|
|
142
193
|
* step as 500 — but a literal here is a weight the kit invented, and the namespace has a
|
|
@@ -156,6 +207,166 @@
|
|
|
156
207
|
* only because the kit cannot know a product's rem base — not because it is yours to tune. */
|
|
157
208
|
--text-x-slot-control-size: var(--text-x-md);
|
|
158
209
|
--text-x-slot-control-touch-size: var(--text-x-ios-floor);
|
|
210
|
+
/* The other half of the same constraint: the first says a focused control must not be
|
|
211
|
+
* small enough to make iOS zoom, this says it must not be small enough to miss. Applied
|
|
212
|
+
* only under `@media (pointer: coarse)` — pointer, not width, for the reason recorded
|
|
213
|
+
* above (an iPad in landscape is wide and still has fingers). */
|
|
214
|
+
--spacing-x-slot-control-touch-min: var(--spacing-x-touch-floor);
|
|
215
|
+
|
|
216
|
+
/* ── Colour slots ─────────────────────────────────────────────────────────────
|
|
217
|
+
* Every colour a component paints comes from here.
|
|
218
|
+
*
|
|
219
|
+
* 🔴 Until 0.9.0 only `FormField` and `InlineAlert` had any, and the other 26 components
|
|
220
|
+
* reached straight into the palette (`text-text-primary`, `bg-surface-raised`) — 59 places
|
|
221
|
+
* in 20 files. The README said "every design value a component uses comes from a slot"
|
|
222
|
+
* the whole time. The check that was supposed to enforce it listed the utility prefixes
|
|
223
|
+
* it knew about — `p px py … rounded` — so colour and weight were never inspected at all.
|
|
224
|
+
*
|
|
225
|
+
* Found by nene-vault comparing its production screens against the migrated build in a
|
|
226
|
+
* real browser (Playwright, computed styles, element by element, 2026-08-23): 73 elements
|
|
227
|
+
* matched, 21 differed, and every difference was inside a kit component. Its own markup —
|
|
228
|
+
* side nav, tables, headings — matched on every property. A product cannot make a control
|
|
229
|
+
* darker than its body text while the kit writes the colour itself.
|
|
230
|
+
*
|
|
231
|
+
* Defaults are palette references, so nothing renders differently. What changes is that
|
|
232
|
+
* there is now an opening. */
|
|
233
|
+
--color-x-slot-focus-ring: var(--color-text-primary);
|
|
234
|
+
|
|
235
|
+
--color-x-slot-button-primary-bg: var(--color-accent);
|
|
236
|
+
--color-x-slot-button-primary-fg: var(--color-on-accent);
|
|
237
|
+
--color-x-slot-button-secondary-bg: var(--color-surface-raised);
|
|
238
|
+
--color-x-slot-button-secondary-fg: var(--color-text-primary);
|
|
239
|
+
--color-x-slot-button-secondary-border: var(--color-border);
|
|
240
|
+
--color-x-slot-button-danger-bg: var(--color-danger);
|
|
241
|
+
--color-x-slot-button-danger-fg: var(--color-on-accent);
|
|
242
|
+
--color-x-slot-button-ghost-fg: var(--color-text-primary);
|
|
243
|
+
|
|
244
|
+
/* Shared by Input, Select and Textarea — they are one control with three shapes, and a
|
|
245
|
+
* product that darkens one and not the others has a bug, not a design. */
|
|
246
|
+
--color-x-slot-control-fg: var(--color-text-primary);
|
|
247
|
+
--color-x-slot-control-bg: var(--color-surface-raised);
|
|
248
|
+
--color-x-slot-control-border: var(--color-border);
|
|
249
|
+
--color-x-slot-control-placeholder: var(--color-text-muted);
|
|
250
|
+
|
|
251
|
+
/* 🔴 The kit painted nothing for validity until 0.12.0 — an invalid control looked exactly
|
|
252
|
+
* like a valid one, while `aria-invalid` was set and announced. Seven of nine ships paint
|
|
253
|
+
* it themselves (Tailwind `aria-invalid:` variants, CSS selectors, or a class name), so a
|
|
254
|
+
* migration onto the kit silently removed a signal the product already had. nene-vault
|
|
255
|
+
* lost an amber border on retention periods under ten years, and the test that guarded it
|
|
256
|
+
* asserted `aria-invalid="true"` — the attribute, not the paint — so it stayed green.
|
|
257
|
+
*
|
|
258
|
+
* 🔑 A test can check a stand-in for the thing it cares about. While both live in the
|
|
259
|
+
* same component that is the same test; the moment the real thing moves upstream, the
|
|
260
|
+
* stand-in stays behind and keeps passing.
|
|
261
|
+
*
|
|
262
|
+
* Border, not a ring: `ring-*` is a box-shadow and a parent with `overflow: hidden` clips
|
|
263
|
+
* it — the same reason the focus indicator here is an `outline`. The background carries
|
|
264
|
+
* the rest, and neither competes with the focus outline. */
|
|
265
|
+
--color-x-slot-control-invalid-border: var(--color-danger);
|
|
266
|
+
--color-x-slot-control-invalid-bg: var(--color-danger-soft);
|
|
267
|
+
--color-x-slot-control-warn-border: var(--color-warn);
|
|
268
|
+
--color-x-slot-control-warn-bg: var(--color-warn-soft);
|
|
269
|
+
|
|
270
|
+
--color-x-slot-choice-fg: var(--color-text-primary);
|
|
271
|
+
--color-x-slot-choice-border: var(--color-border);
|
|
272
|
+
|
|
273
|
+
--color-x-slot-switch-on-bg: var(--color-accent);
|
|
274
|
+
--color-x-slot-switch-on-fg: var(--color-on-accent);
|
|
275
|
+
--color-x-slot-switch-off-bg: var(--color-surface);
|
|
276
|
+
--color-x-slot-switch-off-fg: var(--color-text-muted);
|
|
277
|
+
--color-x-slot-switch-border: var(--color-border);
|
|
278
|
+
|
|
279
|
+
--color-x-slot-badge-neutral-bg: var(--color-surface);
|
|
280
|
+
--color-x-slot-badge-neutral-fg: var(--color-text-muted);
|
|
281
|
+
--color-x-slot-badge-neutral-border: var(--color-border);
|
|
282
|
+
--color-x-slot-badge-accent-bg: var(--color-accent);
|
|
283
|
+
--color-x-slot-badge-accent-fg: var(--color-on-accent);
|
|
284
|
+
--color-x-slot-badge-accent-border: var(--color-accent);
|
|
285
|
+
--color-x-slot-badge-danger-bg: var(--color-danger);
|
|
286
|
+
--color-x-slot-badge-danger-fg: var(--color-on-accent);
|
|
287
|
+
--color-x-slot-badge-danger-border: var(--color-danger);
|
|
288
|
+
|
|
289
|
+
--color-x-slot-toast-bg: var(--color-surface-raised);
|
|
290
|
+
--color-x-slot-toast-fg: var(--color-text-primary);
|
|
291
|
+
--color-x-slot-toast-border: var(--color-border);
|
|
292
|
+
--color-x-slot-toast-danger-fg: var(--color-danger);
|
|
293
|
+
--color-x-slot-toast-danger-border: var(--color-danger);
|
|
294
|
+
|
|
295
|
+
--color-x-slot-card-bg: var(--color-surface-raised);
|
|
296
|
+
--color-x-slot-card-border: var(--color-border);
|
|
297
|
+
|
|
298
|
+
--color-x-slot-modal-bg: var(--color-surface-raised);
|
|
299
|
+
--color-x-slot-modal-fg: var(--color-text-primary);
|
|
300
|
+
--color-x-slot-modal-border: var(--color-border);
|
|
301
|
+
/* The dim behind a modal. A colour, so it belongs here — the opacity that goes with it is
|
|
302
|
+
* applied by the component, since it is not a palette entry. */
|
|
303
|
+
--color-x-slot-modal-scrim: var(--color-text-primary);
|
|
304
|
+
|
|
305
|
+
--color-x-slot-table-fg: var(--color-text-primary);
|
|
306
|
+
--color-x-slot-table-header-fg: var(--color-text-muted);
|
|
307
|
+
--color-x-slot-table-border: var(--color-border);
|
|
308
|
+
|
|
309
|
+
--color-x-slot-detail-term-fg: var(--color-text-muted);
|
|
310
|
+
--color-x-slot-detail-description-fg: var(--color-text-primary);
|
|
311
|
+
--color-x-slot-detail-border: var(--color-border);
|
|
312
|
+
|
|
313
|
+
--color-x-slot-pagination-fg: var(--color-text-muted);
|
|
314
|
+
--color-x-slot-page-header-fg: var(--color-text-primary);
|
|
315
|
+
--color-x-slot-text-fg: var(--color-text-primary);
|
|
316
|
+
--color-x-slot-text-muted-fg: var(--color-text-muted);
|
|
317
|
+
--color-x-slot-spinner-fg: var(--color-text-muted);
|
|
318
|
+
--color-x-slot-empty-state-fg: var(--color-text-muted);
|
|
319
|
+
--color-x-slot-error-state-fg: var(--color-danger);
|
|
320
|
+
/* 🔴 The required marker, not the error message — that one is `--color-x-slot-field-error`.
|
|
321
|
+
* Named `-error-fg` when the colour slots were swept in (0.9.0, same day), which reads as
|
|
322
|
+
* "the error message's colour" and is not. Renamed before anything consumed it. */
|
|
323
|
+
--color-x-slot-field-required-marker: var(--color-danger);
|
|
324
|
+
|
|
325
|
+
/* An upper bound for an `<svg>` written directly inside a `Button`.
|
|
326
|
+
*
|
|
327
|
+
* 🔴 `max-*`, deliberately not `size-*`. An `<svg>` with no width/height attributes and no
|
|
328
|
+
* CSS size is a replaced element with no intrinsic size, so it lays out at 300x150 — which
|
|
329
|
+
* is how a nene-vault upload button became a 230px-tall rectangle the moment its wrapper
|
|
330
|
+
* changed from a `<div>` to a `<Stack>` (2026-08-23).
|
|
331
|
+
*
|
|
332
|
+
* Sizing it outright would fix that and break something else: a class produced by an
|
|
333
|
+
* arbitrary variant (`[&_svg]:size-6`) outranks the plain `h-6 w-6` that `Icon` puts on
|
|
334
|
+
* itself, so `<Icon size="sm">` inside a button would silently render at the button's
|
|
335
|
+
* size. A maximum has no such conflict — it constrains nothing that is already smaller,
|
|
336
|
+
* and there is no specificity contest because the two properties are different.
|
|
337
|
+
*
|
|
338
|
+
* The bound is `--spacing-x-lg`, which is 1.5rem — `Icon size="lg"` exactly — so no icon
|
|
339
|
+
* the kit ships is clamped by it. */
|
|
340
|
+
--spacing-x-slot-button-icon: var(--spacing-x-lg);
|
|
341
|
+
|
|
342
|
+
/* ── Weight slots ─────────────────────────────────────────────────────────────
|
|
343
|
+
* `font-medium` appeared inline in four components. Same rule, same reason as colour. */
|
|
344
|
+
--font-weight-x-slot-button: var(--font-weight-medium);
|
|
345
|
+
--font-weight-x-slot-table-header: var(--font-weight-medium);
|
|
346
|
+
--font-weight-x-slot-detail-term: var(--font-weight-medium);
|
|
347
|
+
--font-weight-x-slot-page-header: var(--font-weight-medium);
|
|
348
|
+
|
|
349
|
+
/* ── Size slots that decline to choose ────────────────────────────────────────
|
|
350
|
+
* 🔴 `inherit`, not a step. Button and the choice controls set no font-size today, so
|
|
351
|
+
* they take the page's — and picking a step here would change how every product already
|
|
352
|
+
* renders, which is the one thing this release must not do. `inherit` is not a design
|
|
353
|
+
* value the kit invented; it is the kit saying it has no opinion, in a place a product
|
|
354
|
+
* can now answer. nene-vault's production buttons are 13px against a 14px body, which is
|
|
355
|
+
* exactly the distinction that had nowhere to live.
|
|
356
|
+
*
|
|
357
|
+
* ⚠️ 13px is not on the type scale (0.6875 / 0.75 / 0.875 / 1 / 1.125 / 1.5rem). The
|
|
358
|
+
* scale was measured from what the fleet writes in Tailwind classes, which cannot express
|
|
359
|
+
* a value it has no step for — so that measurement could not have seen it. Recorded here
|
|
360
|
+
* rather than acted on: changing the type scale is its own decision, with its own
|
|
361
|
+
* measurement, and it is not this release's. */
|
|
362
|
+
--text-x-slot-button-size: inherit;
|
|
363
|
+
/* 🔴 `sm` gets its own, because padding already has one and the pair was asymmetric:
|
|
364
|
+
* `--spacing-x-slot-button-{,sm-}pad-{x,y}` is four slots, the font was one. nene-vault
|
|
365
|
+
* shipped md=13px and sm=12px, and on the kit both came out at the same size — its
|
|
366
|
+
* pagination "Previous" measured 12px in production against 13px on the migrated build.
|
|
367
|
+
* Defaults to `inherit` like its sibling, so nothing renders differently today. */
|
|
368
|
+
--text-x-slot-button-sm-size: inherit;
|
|
369
|
+
--text-x-slot-choice-size: inherit;
|
|
159
370
|
|
|
160
371
|
/* Sentinel for the consumer's build (#316). Zero-width, so applying it does nothing.
|
|
161
372
|
* Its only job is to exist: `p-x-source-probe` appears nowhere except this kit's `dist`,
|