@epilot/volt-ui-experimental-griffel 0.2.3 → 0.3.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/ADOPTING.md +416 -0
- package/README.md +32 -31
- package/dist/_virtual/jsx-dev-runtime.js +5 -0
- package/dist/_virtual/jsx-dev-runtime.js.map +1 -0
- package/dist/_virtual/react-jsx-dev-runtime.development.js +5 -0
- package/dist/_virtual/react-jsx-dev-runtime.development.js.map +1 -0
- package/dist/_virtual/react-jsx-dev-runtime.production.js +5 -0
- package/dist/_virtual/react-jsx-dev-runtime.production.js.map +1 -0
- package/dist/components/accordion/accordion.js +126 -66
- package/dist/components/accordion/accordion.js.map +1 -1
- package/dist/components/alert-dialog/alert-dialog.js +239 -137
- package/dist/components/alert-dialog/alert-dialog.js.map +1 -1
- package/dist/components/avatar/avatar-group.js +102 -47
- package/dist/components/avatar/avatar-group.js.map +1 -1
- package/dist/components/avatar/avatar.js +97 -73
- package/dist/components/avatar/avatar.js.map +1 -1
- package/dist/components/badge/badge.js +53 -37
- package/dist/components/badge/badge.js.map +1 -1
- package/dist/components/breadcrumb/breadcrumb.js +97 -49
- package/dist/components/breadcrumb/breadcrumb.js.map +1 -1
- package/dist/components/button/button.js +88 -77
- package/dist/components/button/button.js.map +1 -1
- package/dist/components/button-group/button-group.js +55 -31
- package/dist/components/button-group/button-group.js.map +1 -1
- package/dist/components/callout/callout.js +288 -164
- package/dist/components/callout/callout.js.map +1 -1
- package/dist/components/card/card.js +137 -81
- package/dist/components/card/card.js.map +1 -1
- package/dist/components/checkbox/checkbox.js +100 -49
- package/dist/components/checkbox/checkbox.js.map +1 -1
- package/dist/components/collapsible-sidebar/collapsible-sidebar.js +155 -107
- package/dist/components/collapsible-sidebar/collapsible-sidebar.js.map +1 -1
- package/dist/components/command/command.js +297 -158
- package/dist/components/command/command.js.map +1 -1
- package/dist/components/data-table/data-table-body.js +114 -54
- package/dist/components/data-table/data-table-body.js.map +1 -1
- package/dist/components/data-table/data-table-column-header.js +117 -45
- package/dist/components/data-table/data-table-column-header.js.map +1 -1
- package/dist/components/data-table/data-table-column-visibility.js +98 -39
- package/dist/components/data-table/data-table-column-visibility.js.map +1 -1
- package/dist/components/data-table/data-table-content.js +90 -58
- package/dist/components/data-table/data-table-content.js.map +1 -1
- package/dist/components/data-table/data-table-error.js +83 -39
- package/dist/components/data-table/data-table-error.js.map +1 -1
- package/dist/components/data-table/data-table-header.js +88 -54
- package/dist/components/data-table/data-table-header.js.map +1 -1
- package/dist/components/data-table/data-table-loading.js +27 -15
- package/dist/components/data-table/data-table-loading.js.map +1 -1
- package/dist/components/data-table/data-table-pagination.js +27 -19
- package/dist/components/data-table/data-table-pagination.js.map +1 -1
- package/dist/components/data-table/data-table-row.js +87 -64
- package/dist/components/data-table/data-table-row.js.map +1 -1
- package/dist/components/data-table/data-table-toolbar.js +24 -16
- package/dist/components/data-table/data-table-toolbar.js.map +1 -1
- package/dist/components/data-table/data-table.js +182 -145
- package/dist/components/data-table/data-table.js.map +1 -1
- package/dist/components/date-range-picker/date-range-picker.js +342 -214
- package/dist/components/date-range-picker/date-range-picker.js.map +1 -1
- package/dist/components/date-range-picker/range-calendar.js +132 -92
- package/dist/components/date-range-picker/range-calendar.js.map +1 -1
- package/dist/components/date-time-picker/calendar.js +501 -317
- package/dist/components/date-time-picker/calendar.js.map +1 -1
- package/dist/components/date-time-picker/date-time-picker-context.js +64 -60
- package/dist/components/date-time-picker/date-time-picker-context.js.map +1 -1
- package/dist/components/date-time-picker/date-time-picker-input.js +249 -149
- package/dist/components/date-time-picker/date-time-picker-input.js.map +1 -1
- package/dist/components/date-time-picker/date-time-picker-panel.js +273 -165
- package/dist/components/date-time-picker/date-time-picker-panel.js.map +1 -1
- package/dist/components/date-time-picker/date-time-picker-popover.js +22 -14
- package/dist/components/date-time-picker/date-time-picker-popover.js.map +1 -1
- package/dist/components/date-time-picker/date-time-picker.js +134 -90
- package/dist/components/date-time-picker/date-time-picker.js.map +1 -1
- package/dist/components/date-time-picker/time-picker.js +198 -132
- package/dist/components/date-time-picker/time-picker.js.map +1 -1
- package/dist/components/density-provider/density-provider.js +48 -0
- package/dist/components/density-provider/density-provider.js.map +1 -0
- package/dist/components/dialog/dialog.js +280 -150
- package/dist/components/dialog/dialog.js.map +1 -1
- package/dist/components/drawer/drawer.js +273 -163
- package/dist/components/drawer/drawer.js.map +1 -1
- package/dist/components/dropdown-menu/dropdown-menu.js +378 -195
- package/dist/components/dropdown-menu/dropdown-menu.js.map +1 -1
- package/dist/components/field/field-combobox.js +447 -240
- package/dist/components/field/field-combobox.js.map +1 -1
- package/dist/components/field/field-select.js +23 -19
- package/dist/components/field/field-select.js.map +1 -1
- package/dist/components/field/field.js +318 -209
- package/dist/components/field/field.js.map +1 -1
- package/dist/components/label/label.js +27 -19
- package/dist/components/label/label.js.map +1 -1
- package/dist/components/list/list.js +623 -369
- package/dist/components/list/list.js.map +1 -1
- package/dist/components/option-card/option-card.js +323 -159
- package/dist/components/option-card/option-card.js.map +1 -1
- package/dist/components/pill/pill.js +82 -64
- package/dist/components/pill/pill.js.map +1 -1
- package/dist/components/popover/popover.js +101 -56
- package/dist/components/popover/popover.js.map +1 -1
- package/dist/components/radio/radio.js +94 -51
- package/dist/components/radio/radio.js.map +1 -1
- package/dist/components/scroll-overflow/scroll-overflow.js +350 -198
- package/dist/components/scroll-overflow/scroll-overflow.js.map +1 -1
- package/dist/components/segmented-control/segmented-control.js +141 -97
- package/dist/components/segmented-control/segmented-control.js.map +1 -1
- package/dist/components/select/select.js +452 -227
- package/dist/components/select/select.js.map +1 -1
- package/dist/components/separator/separator.js +24 -16
- package/dist/components/separator/separator.js.map +1 -1
- package/dist/components/skeleton/skeleton.js +83 -43
- package/dist/components/skeleton/skeleton.js.map +1 -1
- package/dist/components/spinner/spinner.js +44 -20
- package/dist/components/spinner/spinner.js.map +1 -1
- package/dist/components/switch/switch.js +163 -86
- package/dist/components/switch/switch.js.map +1 -1
- package/dist/components/table/table-pagination.js +465 -205
- package/dist/components/table/table-pagination.js.map +1 -1
- package/dist/components/table/table.js +212 -144
- package/dist/components/table/table.js.map +1 -1
- package/dist/components/tabs/tabs.js +94 -58
- package/dist/components/tabs/tabs.js.map +1 -1
- package/dist/components/text/text.js +40 -32
- package/dist/components/text/text.js.map +1 -1
- package/dist/components/toast/toast.js +187 -111
- package/dist/components/toast/toast.js.map +1 -1
- package/dist/components/toggle/toggle-group.js +40 -24
- package/dist/components/toggle/toggle-group.js.map +1 -1
- package/dist/components/toggle/toggle.js +48 -40
- package/dist/components/toggle/toggle.js.map +1 -1
- package/dist/components/tooltip/tooltip.js +101 -55
- package/dist/components/tooltip/tooltip.js.map +1 -1
- package/dist/index.d.ts +600 -427
- package/dist/index.js +215 -212
- package/dist/index.js.map +1 -1
- package/dist/lib/clear-button.js +27 -15
- package/dist/lib/clear-button.js.map +1 -1
- package/dist/lib/icons.js +18 -6
- package/dist/lib/icons.js.map +1 -1
- package/dist/packages/volt-runtime/dist/sheet.js +27 -10
- package/dist/packages/volt-runtime/dist/sheet.js.map +1 -1
- package/dist/packages/volt-runtime/dist/styles/recipe.js +76 -0
- package/dist/packages/volt-runtime/dist/styles/recipe.js.map +1 -0
- package/dist/styles/document-context.js +12 -0
- package/dist/styles/document-context.js.map +1 -0
- package/dist/styles/make-styles.js +18 -10
- package/dist/styles/make-styles.js.map +1 -1
- package/dist/tokens/core.generated.js +1238 -1166
- package/dist/tokens/core.generated.js.map +1 -1
- package/dist/tokens/index.d.ts +442 -415
- package/dist/tokens/index.js +1105 -1078
- package/dist/tokens/index.js.map +1 -1
- package/dist/tokens/install.js +9 -5
- package/dist/tokens/install.js.map +1 -1
- package/dist/tokens/use-palette-tokens.js +20 -0
- package/dist/tokens/use-palette-tokens.js.map +1 -0
- package/dist/tokens.json +83 -2
- package/dist/vendor/react/cjs/react-jsx-dev-runtime.development.js +236 -0
- package/dist/vendor/react/cjs/react-jsx-dev-runtime.development.js.map +1 -0
- package/dist/vendor/react/cjs/react-jsx-dev-runtime.production.js +21 -0
- package/dist/vendor/react/cjs/react-jsx-dev-runtime.production.js.map +1 -0
- package/dist/vendor/react/jsx-dev-runtime.js +9 -0
- package/dist/vendor/react/jsx-dev-runtime.js.map +1 -0
- package/package.json +13 -5
- package/scripts/migration-diagnostics-core.mjs +859 -0
- package/scripts/migration-diagnostics.mjs +353 -0
- package/scripts/migration-sweep.mjs +179 -0
- package/AGENTS.md +0 -153
- package/AUTHORING.md +0 -292
- package/dist/packages/volt-runtime/dist/styles/make-styles.js +0 -22
- package/dist/packages/volt-runtime/dist/styles/make-styles.js.map +0 -1
package/AUTHORING.md
DELETED
|
@@ -1,292 +0,0 @@
|
|
|
1
|
-
# Authoring volt-ui-v2 — what went wrong, and why the rules exist
|
|
2
|
-
|
|
3
|
-
`AGENTS.md` is the brief: what to do, and it is normative. This is the reasoning: what broke, how it was found,
|
|
4
|
-
and what it cost. Every item here is a defect that actually shipped into the branch, and most of them
|
|
5
|
-
passed source review, a green unit suite and a source-level audit before a rendering comparison caught
|
|
6
|
-
them.
|
|
7
|
-
|
|
8
|
-
Read this before changing a component or adding one. The single most useful thing in it is the pattern
|
|
9
|
-
at the end: **the same class of mistake recurred four times, and it was never caught by reading.**
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## 1. The engine — four Griffel constraints, all of which fail silently
|
|
14
|
-
|
|
15
|
-
### 1.1 `&` must be the first token of a selector
|
|
16
|
-
|
|
17
|
-
Writing `"[data-pressed] &"` reads as "style me when an ancestor is pressed". Griffel does not
|
|
18
|
-
substitute `&` in that position; it prepends its own class to the whole selector and emits:
|
|
19
|
-
|
|
20
|
-
```css
|
|
21
|
-
.f1y9ypwo[data-pressed] .f1y9ypwo {
|
|
22
|
-
opacity: 1;
|
|
23
|
-
}
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
That needs one element to carry the class AND be pressed AND contain a second element with the same
|
|
27
|
-
class. It never matches. No warning, no error, no failing test.
|
|
28
|
-
|
|
29
|
-
**Cost: fifteen dead declarations across six components**, every one a feature that silently did not
|
|
30
|
-
work — all four of Toast's type colours (every toast looked identical), OptionCard's six
|
|
31
|
-
selected-state glyphs, Label's disabled dimming, Select's chevron rotation.
|
|
32
|
-
|
|
33
|
-
Only OptionCard's was caught by the visual gate, because it was the only one with a specimen that
|
|
34
|
-
exercised the state. `src/ancestor-selector.test.ts` now forbids the form, and reads source for
|
|
35
|
-
exactly that reason.
|
|
36
|
-
|
|
37
|
-
**Instead:** declare the rule on the slot of the element that carries the state, and reach the
|
|
38
|
-
descendant by `SLOT_ATTR`:
|
|
39
|
-
|
|
40
|
-
```ts
|
|
41
|
-
[`&[data-pressed] [${SLOT_ATTR}="option-card-radio-dot"]`]: { opacity: 1 }
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
If the ancestor belongs to another component, the rule belongs in that component — Label's disabled
|
|
45
|
-
rules live in `field.tsx`. **And there is a real capability loss here:** v1 expresses these with
|
|
46
|
-
Tailwind's `group-data-[…]:` and `peer-disabled:`, which work only because Tailwind emits _global_
|
|
47
|
-
utility classes that can be rooted anywhere. Scoped atomic CSS cannot. A Label used outside a Field,
|
|
48
|
-
next to a disabled input, will not dim.
|
|
49
|
-
|
|
50
|
-
### 1.2 Declaration order inside a slot encodes precedence
|
|
51
|
-
|
|
52
|
-
Griffel emits atoms in source order, so a later state selector beats an earlier one. **v1's precedence
|
|
53
|
-
does not come from order** — it comes from Tailwind's utility layer, where `aria-invalid:` beats
|
|
54
|
-
`disabled:` no matter where either sits in the class string.
|
|
55
|
-
|
|
56
|
-
Transcribing v1's class order therefore inverts the result. Checkbox and Radio both put
|
|
57
|
-
`&[aria-invalid='true']` before `&[data-disabled]`, so a disabled invalid control lost its error
|
|
58
|
-
border to the gray one: 17% and 44% pixel differences on a 16×16 box.
|
|
59
|
-
|
|
60
|
-
**Put the state that must win last.**
|
|
61
|
-
|
|
62
|
-
### 1.3 Dedup only happens when the expanded property keys match
|
|
63
|
-
|
|
64
|
-
OptionCard's card is a `ToggleGroupItem`. Toggle declares `shorthands.paddingInline`; the card
|
|
65
|
-
declared `paddingLeft`/`paddingRight`. Different keys, so both survived and the cascade kept Toggle's
|
|
66
|
-
value — the padding stayed 4px-per-side wrong _after the value itself had been corrected_, which is a
|
|
67
|
-
confusing thing to debug.
|
|
68
|
-
|
|
69
|
-
Match the neighbour's property shape, not just its value.
|
|
70
|
-
|
|
71
|
-
### 1.4 A typo'd slot name used to compile
|
|
72
|
-
|
|
73
|
-
`makeVoltStyles` was generic over the key union:
|
|
74
|
-
|
|
75
|
-
```
|
|
76
|
-
makeVoltStyles<Slots extends string>(styles: Record<Slots, GriffelStyle>): () => Record<Slots, string>
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
Inferring a type parameter from `Record<Slots, …>` against an object literal **widens `Slots` to
|
|
80
|
-
`string`**, so the result was `Record<string, string>` and every slot name typechecked — including
|
|
81
|
-
ones that did not exist.
|
|
82
|
-
|
|
83
|
-
OptionCard read `styles.card` and `styles[layout]` with neither declared. The component rendered with
|
|
84
|
-
**no card styling at all** — v1's card properties had been copied onto the `group` slot and applied to
|
|
85
|
-
the wrong element. `tsc` clean, 322 tests green.
|
|
86
|
-
|
|
87
|
-
Now generic over the object, so `keyof S` is the real union. It found exactly three errors, all in
|
|
88
|
-
OptionCard: the hole was library-wide, the exploitation was not.
|
|
89
|
-
|
|
90
|
-
---
|
|
91
|
-
|
|
92
|
-
## 2. Shipping no reset — v2 inherits nothing v1 got for free
|
|
93
|
-
|
|
94
|
-
v1's components were never written against a reset either. They inherited whatever the _element Radix
|
|
95
|
-
happened to render_ got from the browser's UA stylesheet. Base UI renders different elements, so v2
|
|
96
|
-
inherits nothing.
|
|
97
|
-
|
|
98
|
-
**Checkbox rendered 2px wide.** Base UI's root is a `<span>`: `display: inline`, where width and
|
|
99
|
-
height **do not apply at all**. v1's Radix root is a `<button>`, which the UA sheet gives
|
|
100
|
-
`inline-block` and `border-box`. v1's own class list declares neither.
|
|
101
|
-
|
|
102
|
-
Two further subtleties from the same fix:
|
|
103
|
-
|
|
104
|
-
- `inline-block` specifically, not `inline-flex`. The box is 16×16 either way, but an inline-flex box
|
|
105
|
-
takes its baseline from its first flex item and sat the line box 2px taller.
|
|
106
|
-
- Declare `boxSizing` where the element type changed, but **not library-wide**. Where v1 also renders
|
|
107
|
-
a plain div, both arms are already `content-box` and agree — adding `border-box` there would
|
|
108
|
-
_create_ a difference. 26 slots across 17 components set a size and a border; only the ones the
|
|
109
|
-
visual gate flagged were actually wrong.
|
|
110
|
-
|
|
111
|
-
**The reset does exist in production, but do not rely on it.** `epilot360-root-config` imports
|
|
112
|
-
`tailwindcss/preflight.css` globally at the single-spa root. So `box-sizing: border-box` is there for
|
|
113
|
-
an MFE inside the portal — and absent for one bundled standalone. Explicit declarations are no-ops in
|
|
114
|
-
the first case and load-bearing in the second.
|
|
115
|
-
|
|
116
|
-
---
|
|
117
|
-
|
|
118
|
-
## 3. The primitive layer is Radix — and the Base UI detour is why
|
|
119
|
-
|
|
120
|
-
**Historical. v2 uses Radix, at v1's exact pins.** Nothing in this section is current guidance; it is
|
|
121
|
-
here because the detour cost real time and the reasoning generalises. `src/lib/auto-portal.tsx`, the
|
|
122
|
-
`InsideMenuGroupContext` wrapper and `collectItemLabels` are all deleted — if you find a reference to
|
|
123
|
-
them, it is stale.
|
|
124
|
-
|
|
125
|
-
v2 was first built on Base UI and rolled back. The measured case against it:
|
|
126
|
-
|
|
127
|
-
- **2.1x the size** for the same primitive — tooltip 33.9k vs 16.8k gzip.
|
|
128
|
-
- Its shipped types **forced every consumer onto `@types/react` 19**: 312 errors under 18, none of them
|
|
129
|
-
in our own logic. It was the sole source of that requirement.
|
|
130
|
-
- It needed wrappers Radix does not — `<Dialog.Portal>` is mandatory, `DropdownMenuLabel` must sit
|
|
131
|
-
inside a `Menu.Group` — so **every overlay in the package rendered nothing**: nine specimens, blank
|
|
132
|
-
pages, while 321 tests and four isolation gates stayed green. The package's own tests used the
|
|
133
|
-
explicit composition Base UI documents, which is exactly why they never hit it.
|
|
134
|
-
|
|
135
|
-
Two lessons that outlive the decision:
|
|
136
|
-
|
|
137
|
-
- **Silent acceptance of an unsupported option.** `toast("…", { duration: Infinity })` was accepted,
|
|
138
|
-
did nothing, and reported nothing; all four toast specimens rendered a bare viewport.
|
|
139
|
-
- **Strictness is not uniformly one way.** v1 **throws** on `OptionCardGroup` without a `type` prop
|
|
140
|
-
where v2 tolerates it silently — so "v2 is more permissive" is not a safe generalisation, and neither
|
|
141
|
-
is its opposite.
|
|
142
|
-
|
|
143
|
-
Radix state attributes are what the code uses now: `data-state="checked" | "on" | "open" | "closed"`,
|
|
144
|
-
and enter/exit transitions key on `data-state` rather than on separate start/end hooks.
|
|
145
|
-
|
|
146
|
-
---
|
|
147
|
-
|
|
148
|
-
## 4. Rule 0 means v1's OUTPUT, not v1's source
|
|
149
|
-
|
|
150
|
-
This is the subtlest thing in the package, and it cuts both ways.
|
|
151
|
-
|
|
152
|
-
**Four v1 production bugs were found here, all one family — the prefixer rewrites definitions and
|
|
153
|
-
misses references:**
|
|
154
|
-
|
|
155
|
-
1. `--color-level-2`/`-3` point at `--volt-level-2`/`-3`, which nothing defines.
|
|
156
|
-
2. `.volt-text-xs` resolves `line-height: var(--volt-leading, var(--text-xs--line-height))` — the
|
|
157
|
-
definition ships prefixed, the reference does not, and the unprefixed name is defined nowhere. So
|
|
158
|
-
**every text utility in v1 computes an invalid line-height and inherits instead.**
|
|
159
|
-
3. Label's `leading-none` never reaches the DOM at all: v1's `cn()` runs tailwind-merge, which treats
|
|
160
|
-
the later `text-sm` as owning line-height and drops it. Combined with (2), production v1 renders
|
|
161
|
-
labels at preflight's 1.5 rather than at 1.
|
|
162
|
-
4. ScrollOverflow's viewport declares `[&>div]:!flex` and `[&>div]:!flex-row`, and neither survives
|
|
163
|
-
the build — so v1's _horizontal_ scroller stacks its children vertically.
|
|
164
|
-
|
|
165
|
-
**The rule: reproduce the harmless ones, refuse the harmful ones.**
|
|
166
|
-
|
|
167
|
-
- ScrollOverflow does **not** reproduce bug (4). Matching it would ship a horizontal scroll container
|
|
168
|
-
that cannot scroll horizontally. That field requires a written reason and prints on every run.
|
|
169
|
-
- Nor does anything reproduce bugs (2) and (3), and that is the one **standing** exception, decided
|
|
170
|
-
in DECISIONS.md D9. Every slot that sets `fontSize: tokens.text*` also sets the matching
|
|
171
|
-
`lineHeight: tokens.leading*`, Label included — it follows the `text-sm` its class list actually
|
|
172
|
-
ships, not the `leading-none` tailwind-merge ate. Bug (2) is not cosmetic: it kills the leading at
|
|
173
|
-
every one of the seven steps, so reproducing it means shipping a type scale with no vertical
|
|
174
|
-
rhythm. It is also too wide for an `acceptedDifference` — every specimen containing text would need
|
|
175
|
-
one — so the VR harness corrects it on the v1 arm instead, once and by name
|
|
176
|
-
(`V1_DECLARED_LEADING_SHIM` in `vr/run.mjs`, pinned and dated by
|
|
177
|
-
`vr/v1-leading-references.test.ts`).
|
|
178
|
-
|
|
179
|
-
Read the exception narrowly. It is about leading and nothing else; everywhere else, output still
|
|
180
|
-
wins.
|
|
181
|
-
|
|
182
|
-
The same principle settles smaller cases without any bug involved: v1 doesn't style disabled tabs at
|
|
183
|
-
all, so v2's invented `opacity: 0.5` was removed. v1's toast declares `shadow-md` and renders a fully
|
|
184
|
-
transparent one, so v2 has no shadow. v1's OptionCard has a bare Tailwind `border` with no colour
|
|
185
|
-
utility, so its border is `currentColor` — a near-black `rgba(2, 0, 8, 0.88)` that looks like an
|
|
186
|
-
accident and is nevertheless what ships.
|
|
187
|
-
|
|
188
|
-
**When in doubt, measure the rendered result on both arms. Do not read the class list and infer.**
|
|
189
|
-
|
|
190
|
-
---
|
|
191
|
-
|
|
192
|
-
## 5. Retiring a dependency inherits its defaults
|
|
193
|
-
|
|
194
|
-
Toast's width (356px), corner offset (24px), zero-width border and absent shadow were all **sonner**
|
|
195
|
-
defaults, not choices in v1's source. The port replaced sonner's behaviour and kept Tailwind-flavoured
|
|
196
|
-
values — 24rem, 1rem, a real 1px border, a real shadow — so every toast was 28px too wide, 8px too
|
|
197
|
-
close to the corner, and carried an edge and a shadow v1 does not render.
|
|
198
|
-
|
|
199
|
-
Nothing but a pixel comparison would have said so. The same exposure applies to `cmdk` in Command,
|
|
200
|
-
which has no v1 export to compare against at all.
|
|
201
|
-
|
|
202
|
-
---
|
|
203
|
-
|
|
204
|
-
## 6. The token layer
|
|
205
|
-
|
|
206
|
-
- **There was no 0.75rem spacing step, and that is why `spacingElement3` now exists.** The scale ran
|
|
207
|
-
0.25 / 0.5 / 0.5 / 1 / 1.5 / 2 rem while v1 uses Tailwind's `*-3` freely, so authors reached for the
|
|
208
|
-
nearest-_sounding_ token (`spacingGroup1`, which is **0.5rem**) and silently lost 4px per side.
|
|
209
|
-
**That exact mistake was found four times** — Toast, Command, Field, SegmentedControl — which made
|
|
210
|
-
it a scale problem, not four authoring slips. The step was added; use `tokens.spacingElement3`.
|
|
211
|
-
This paragraph used to end "use the literal `0.75rem`", which was correct only before the token
|
|
212
|
-
landed. Sizes are still literals: there is no dimension token family, so `width: "0.75rem"` on an
|
|
213
|
-
svg is not `spacingElement3` — spacing and size are different axes that share a number.
|
|
214
|
-
- **Line-height tokens are v1's own declared values, and they are applied.** This paragraph used to
|
|
215
|
-
read "deliberately unused", on the reasoning that applying them diverges from what v1 _renders_
|
|
216
|
-
(§4 bug 2). That was reversed in DECISIONS.md D9: the divergence is from v1's bug, not from v1, and
|
|
217
|
-
the harness now measures against v1-as-declared. Pair the leading with the size in the same slot,
|
|
218
|
-
`fontSize` first. Leave alone the slots that already declare a line-height — those are v1
|
|
219
|
-
`leading-*` utilities that survived tailwind-merge, so they are v1's output as well as its intent.
|
|
220
|
-
- **The namespace segment is derived from the token values.** `--volt2-<hash>-accent-9` changes
|
|
221
|
-
whenever any token changes; that is what lets two builds coexist on `:root`. **Never hardcode it.**
|
|
222
|
-
One test did and broke the moment line-height tokens were added.
|
|
223
|
-
- The accessor is 942 **named exports**, not an object literal, because a bundler cannot drop unused
|
|
224
|
-
properties of a literal. Worth 6.5 kB gzip to every partial consumer. Authoring is unchanged:
|
|
225
|
-
`import * as tokens` and keep saying `tokens.accentSolid`.
|
|
226
|
-
|
|
227
|
-
---
|
|
228
|
-
|
|
229
|
-
## 7. Verifying — where the checks themselves were wrong
|
|
230
|
-
|
|
231
|
-
A gate that cannot fail is worse than no gate, because it launders an unverified claim. Every one of
|
|
232
|
-
these was a real false pass:
|
|
233
|
-
|
|
234
|
-
- **Run gates AFTER wiring exports.** `gates/isolation-fixture.tsx` imports the package namespace, so
|
|
235
|
-
it only inspects what `src/index.ts` exports. Running it on an unwired component is a meaningless
|
|
236
|
-
pass — that is how Select's `data-slot` violation shipped.
|
|
237
|
-
- **The visual gate reads `dist`.** The first negative control perturbed a padding by 2px and still
|
|
238
|
-
reported a clean pass, because the edit was in `src`. Staleness is now a hard error.
|
|
239
|
-
- **A blank render is not a match.** Nine overlays rendered nothing while the gate reported
|
|
240
|
-
"116/124 match", because a blank capture against a mostly-white v1 is a _small_ diff. Uniform
|
|
241
|
-
captures and thrown pages are now hard failures.
|
|
242
|
-
- **A parse error makes everything pass.** Typechecking a real consumer against v2 reported 0 errors,
|
|
243
|
-
and a positive control importing a nonexistent name also reported 0 — because the consumer's
|
|
244
|
-
TypeScript 4.9 could not parse Base UI's `.d.ts`, so the module resolved to an error type. Under TS
|
|
245
|
-
5.6 the same code reports 148.
|
|
246
|
-
- **A state nobody renders is a state nobody has checked.** That is the whole reason §1.1 needed a
|
|
247
|
-
source scan on top of the visual gate.
|
|
248
|
-
- **Identical results after a change that should have moved something is a signal.** A patch adding
|
|
249
|
-
preflight to the harness silently missed its target line; the numbers came back byte-identical and
|
|
250
|
-
only a direct `box-sizing` measurement caught it.
|
|
251
|
-
- **A test that passes in the file and fails alone has not passed.** Four overlay dismissal tests were
|
|
252
|
-
green under React 19 and red under React 18, and the component behaved _identically_ in both. Run
|
|
253
|
-
the suspect test with `-t` before you believe either colour: all four also failed under React 19 in
|
|
254
|
-
isolation. Three defects were stacked, each masking the next — see §7.1.
|
|
255
|
-
|
|
256
|
-
### 7.1 The three that hid behind each other
|
|
257
|
-
|
|
258
|
-
Worth reading as a set, because no single one of them was visible while the others stood:
|
|
259
|
-
|
|
260
|
-
1. **Testing Library's auto-cleanup never ran.** It self-registers only when the framework's
|
|
261
|
-
`afterEach` is a global, and neither vitest config sets `globals: true`. Test files had compensated
|
|
262
|
-
with `document.body.innerHTML = ""`, which detaches nodes without unmounting roots. Radix delivers
|
|
263
|
-
Escape only to the topmost `DismissableLayer`, so layers left behind by earlier tests swallowed the
|
|
264
|
-
key. The tell was `data-scroll-locked="3"` on a page containing one dialog.
|
|
265
|
-
Now registered explicitly in `src/setup.testing-library.ts`, in both configs.
|
|
266
|
-
2. **The removal assertion could not pass on its own merits.** Radix's `Presence` holds a closed node
|
|
267
|
-
until `animationend`, and jsdom runs no animations. `fireEvent.animationEnd` does not fix it:
|
|
268
|
-
jsdom implements no `AnimationEvent`, so Testing Library falls back to a plain `Event` and drops
|
|
269
|
-
`animationName` — precisely the field `Presence` gates on. Construct the event by hand
|
|
270
|
-
(`src/overlay-test-helpers.ts`). Asserting `data-state="closed"` instead would be vacuous: that
|
|
271
|
-
attribute is set before `Presence` decides anything.
|
|
272
|
-
3. **Tooltip's Escape raced its own listener.** Radix attaches it in an effect, and the `waitFor`
|
|
273
|
-
resolved on DOM presence — under React 18, before that effect ran, so the keydown landed with
|
|
274
|
-
nothing listening. `await act(async () => {})` first. React 19 flushed early enough to hide it.
|
|
275
|
-
|
|
276
|
-
The general shape: **a version-dependent test result is a claim about the test, until you have shown
|
|
277
|
-
the component behaves differently.** Here it never did.
|
|
278
|
-
|
|
279
|
-
---
|
|
280
|
-
|
|
281
|
-
## 8. The pattern worth internalising
|
|
282
|
-
|
|
283
|
-
Four separate times, the same 4px spacing error. Fifteen dead selectors of one shape. Every overlay
|
|
284
|
-
non-functional. A component with no styling at all. None of it was found by reading code — not by
|
|
285
|
-
source review, not by unit tests, not by three source-level audit passes that each found _other_ real
|
|
286
|
-
defects.
|
|
287
|
-
|
|
288
|
-
All of it was found by rendering both versions and comparing pixels.
|
|
289
|
-
|
|
290
|
-
So: **when you change a component, run `bun run gate:vr`.** When you add one, add specimens for its
|
|
291
|
-
states, not just its default. And when a check comes back clean on something that has been wrong
|
|
292
|
-
before, find out whether the check can fail at all.
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { getVoltRenderer as r } from "../renderer/index.js";
|
|
2
|
-
import { makeStyles as n } from "../vendor/@griffel/core/src/makeStyles.js";
|
|
3
|
-
import { makeResetStyles as o } from "../vendor/@griffel/core/src/makeResetStyles.js";
|
|
4
|
-
function a(t) {
|
|
5
|
-
const e = n(
|
|
6
|
-
t
|
|
7
|
-
);
|
|
8
|
-
return function() {
|
|
9
|
-
return e({ dir: "ltr", renderer: r() });
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
|
-
function l(t) {
|
|
13
|
-
const e = o(t);
|
|
14
|
-
return function() {
|
|
15
|
-
return e({ dir: "ltr", renderer: r() });
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
export {
|
|
19
|
-
l as makeVoltResetStyles,
|
|
20
|
-
a as makeVoltStyles
|
|
21
|
-
};
|
|
22
|
-
//# sourceMappingURL=make-styles.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"make-styles.js","sources":["../../../../../../volt-runtime/dist/styles/make-styles.js"],"sourcesContent":["import { getVoltRenderer as r } from \"../renderer/index.js\";\nimport { makeStyles as n } from \"../vendor/@griffel/core/src/makeStyles.js\";\nimport { makeResetStyles as l } from \"../vendor/@griffel/core/src/makeResetStyles.js\";\nfunction i(e) {\n const t = n(\n e\n );\n return function() {\n return t({ dir: \"ltr\", renderer: r() });\n };\n}\nfunction c(e) {\n const t = l(e);\n return function() {\n return t({ dir: \"ltr\", renderer: r() });\n };\n}\nexport {\n c as makeVoltResetStyles,\n i as makeVoltStyles\n};\n//# sourceMappingURL=make-styles.js.map\n"],"names":["i","e","t","n","r","c","l"],"mappings":";;;AAGA,SAASA,EAAEC,GAAG;AACZ,QAAMC,IAAIC;AAAAA,IACRF;AAAA,EACJ;AACE,SAAO,WAAW;AAChB,WAAOC,EAAE,EAAE,KAAK,OAAO,UAAUE,EAAC,GAAI;AAAA,EACxC;AACF;AACA,SAASC,EAAEJ,GAAG;AACZ,QAAMC,IAAII,EAAEL,CAAC;AACb,SAAO,WAAW;AAChB,WAAOC,EAAE,EAAE,KAAK,OAAO,UAAUE,EAAC,GAAI;AAAA,EACxC;AACF;"}
|