@vegastack/design-tokens 0.4.0 → 0.7.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/base.css CHANGED
@@ -1,27 +1,84 @@
1
- /* @vegastack/design-tokens/base.css — opt-in base reset (kept separate so consumers aren't forced into it). */
1
+ /* @vegastack/design-tokens/base.css — the opt-in base layer (kept separate so consumers aren't
2
+ forced into it).
3
+
4
+ REBUILT BY THE SHADCN RESET (Batch 1, 2026-09-18). This file is now upstream's own base layer
5
+ plus exactly the decisions `docs/plans/2026-09-18-shadcn-reset/decisions.md` marks **ours**:
6
+
7
+ FOC-1 one global :focus-visible outline; components restate nothing
8
+ FOC-7 the forced-colours fallback for text entry
9
+ MOT-5 the reduced-motion reset (and the one sanctioned !important)
10
+ INT-1 the hand cursor, on a wider selector list than shadcn's --pointer rule
11
+ INT-7 touch-action: manipulation and no tap highlight
12
+ OVL-13 body { isolation: isolate } for the portal theme scope
13
+
14
+ Deleted with the reset: INT-2's `cursor: default` on disabled controls (INT-2 = shadcn, and
15
+ upstream has no such rule).
16
+
17
+ `color-scheme` (COL-22) is not here — it is emitted per theme into `:root` and `.dark` by the
18
+ token build, because it has to travel with the palette it describes. */
19
+
2
20
  @layer base {
21
+ /* Upstream's own base layer, with one change. Upstream writes
22
+ `* { @apply border-border outline-ring/50 }`; the `outline-ring/50` half is a 50%-opacity
23
+ outline COLOUR on every element, which exists only to soften the focus glow this system
24
+ removes. FOC-1 below owns focus outright and paints the ring solid, so the `/50` is dropped —
25
+ keeping it would mean two rules describing the same affordance at two different opacities. */
3
26
  * {
4
- @apply border-border outline-ring/(--alpha-outline-soft);
27
+ @apply border-border;
5
28
  }
6
29
  body {
7
- /* `isolate` creates a root stacking context so Base UI portaled popups (Dialog, Popover,
8
- Tooltip, Select, menus, Sheet) reliably stack above page content — required by Base UI's
9
- Quick Start ("Set up portals"). Consumers importing base.css get this for free; if you don't
10
- import base.css, add `isolation: isolate` to your app root yourself. */
30
+ /* `isolate` (OVL-13) creates a root stacking context so Base UI portaled popups (Dialog,
31
+ Popover, Tooltip, Select, menus, Sheet) reliably stack above page content — required by Base
32
+ UI's Quick Start ("Set up portals"). Consumers importing base.css get this for free; if you
33
+ don't import base.css, add `isolation: isolate` to your app root yourself. */
11
34
  @apply bg-background text-foreground isolate;
35
+
36
+ /* TYP-17 (ours) — THE DECLARED DEFAULT BODY SIZE, 14px.
37
+ The component layer is 14px (`text-sm`, the dominant size by a wide margin), but nothing
38
+ used to declare a page default, so any text WITHOUT an explicit `text-*` class fell back to
39
+ the browser's 16px and a page mixed two sizes. 14px is the product-UI default Vercel's own
40
+ Geist copy scale is built around.
41
+
42
+ THIS IS DELIBERATELY ON `body`, NEVER ON `html`. `rem` resolves against the ROOT element, so
43
+ `html { font-size: 14px }` would silently rescale the entire system — `text-sm` (0.875rem)
44
+ would become 12.25px and every rem-based spacing token would shrink with it — and it would
45
+ override the reader's own browser font-size preference, which is a WCAG 1.4.4 failure. On
46
+ `body` the root stays 16px, every explicit `text-*` class is untouched, and only unclassed
47
+ text moves. */
48
+ font-size: var(--text-sm, 0.875rem);
49
+
50
+ /* TYP-16 (ours) — Geist is drawn for smoothed rendering. Without this the same weight renders
51
+ heavier and softer on macOS than the identical weight on Vercel's own site, which is a large
52
+ part of why the weight ladder read as "off" before the ramp existed. Upstream sets neither
53
+ property; Tailwind's preflight already handles `-webkit-text-size-adjust` and `tab-size`, so
54
+ those are not restated here. */
55
+ -webkit-font-smoothing: antialiased;
56
+ -moz-osx-font-smoothing: grayscale;
57
+ }
58
+ html {
59
+ @apply font-sans;
12
60
  }
13
- /* a11y: real focus ring (fixes the platform's outline:none defect — requirements §7.5).
14
- 2px solid neutral outline, offset 1 (hugs the control reads as a crisp border, not a halo).
15
- Single source of truth: components carry NO focus ring of their own; this re-skins from `ring`. */
61
+
62
+ /* FOC-1 the SINGLE focus affordance in the system. 2px solid, offset 1, in `ring`, which
63
+ FOC-2 binds to the near-black / near-white ink rather than shadcn's mid-grey. Components carry
64
+ no focus ring of their own: upstream's `focus-visible:ring-3 focus-visible:ring-ring/50` glow
65
+ is stripped from every migrated component (FOC-6), and `design-lint`'s `no-focus-ring-glow`
66
+ rule is what stops it coming back through a later upstream pull. */
16
67
  :focus-visible {
17
68
  @apply outline-2 outline-offset-1 outline-ring;
18
69
  }
19
- /* Pointer cursor on every interactive control. Tailwind v4 Preflight no longer sets it and the
20
- native <button> cursor is an arrow, so without this every button / icon-button / menu item /
21
- select option / tab / toggle would miss the hand cursor. This is the SINGLE global source of
22
- truth components must NOT re-declare `cursor-pointer` locally; only genuinely non-standard
23
- clickables (a clickable table row, a label tied to a control) or non-pointer cursors
24
- (`cursor-text`, the select scroll-arrows) set a cursor of their own. */
70
+
71
+ /* INT-1 the hand cursor. shadcn's `--pointer` flag writes
72
+ `button:not(:disabled), [role="button"]:not(:disabled) { cursor: pointer }`; ours is the same
73
+ rule over the full set of things a person clicks, so a menu row, a tab, a switch and a listbox
74
+ option all read as clickable. This is the SINGLE global source of truth components must NOT
75
+ re-declare `cursor-pointer` locally; only genuinely non-standard clickables (a clickable table
76
+ row, a label tied to a control) or non-pointer cursors (`cursor-text`, the select scroll
77
+ arrows) set a cursor of their own.
78
+
79
+ INT-7 rides on the same selector list: keep touch controls responsive without disabling page
80
+ pan or pinch-zoom, and suppress the grey Android tap flash. Text-entry fields and document
81
+ links stay untouched. */
25
82
  button,
26
83
  [role="button"],
27
84
  [role="tab"],
@@ -34,34 +91,24 @@
34
91
  [role="option"],
35
92
  summary {
36
93
  cursor: pointer;
37
- /* Keep touch controls responsive without disabling page pan/pinch zoom.
38
- Text-entry fields and document links stay untouched. */
39
94
  touch-action: manipulation;
40
95
  -webkit-tap-highlight-color: transparent;
41
96
  }
42
- /* Disabled controls opt out (those that don't already set `pointer-events-none` /
43
- `cursor-not-allowed`). Higher specificity than the element/role selectors above, so it wins;
44
- a component's own `disabled:cursor-not-allowed` lives in the later utilities layer and still wins. */
45
- :disabled,
46
- [aria-disabled="true"],
47
- [data-disabled] {
48
- cursor: default;
49
- }
50
97
  }
51
98
 
52
- /* Preserve the focus contract when authored colors are replaced by a forced-colors palette. */
99
+ /* FOC-7 — preserve the focus contract when authored colours are replaced by a forced-colours
100
+ palette. */
53
101
  @media (forced-colors: active) {
54
102
  :focus-visible {
55
103
  outline-color: Highlight;
56
104
  }
57
105
 
58
- /* Text-entry focus (B1-01, 2026-09-07). Every text-entry control signals focus with the
59
- border TINT (`focus:border-ring/(--alpha-tint-border)`) and strips the outline
60
- (`outline-none`), because a raw text field cannot tell mouse from keyboard. Forced colours
61
- replaces `border-color` outright, so the tint vanishes and `outline-style: none` stays in
62
- force a Windows High Contrast user had no caret location at all. This block is UNLAYERED
63
- on purpose: it beats the component's `outline-none` (a layered utility) regardless of
64
- specificity, so the fallback is written ONCE here, never per component.
106
+ /* Text-entry focus (FOC-3). Every text-entry control signals focus with a border TINT and strips
107
+ the outline, because a raw text field cannot tell mouse from keyboard. Forced colours replaces
108
+ `border-color` outright, so the tint vanishes and `outline-style: none` stays in force a
109
+ Windows High Contrast user had no caret location at all. This block is UNLAYERED on purpose:
110
+ it beats the component's `outline-none` (a layered utility) regardless of specificity, so the
111
+ fallback is written ONCE here, never per component.
65
112
  - `input`/`textarea` at `:focus` (not `:focus-visible`, matching the tint's trigger);
66
113
  - composite editors (TextEdit's contenteditable) at `:focus`;
67
114
  - wrapper-tinted surfaces (`data-slot="text-edit"`, and every `data-field-group`) at
@@ -75,14 +122,13 @@
75
122
  outline-offset: 1px;
76
123
  }
77
124
 
78
- /* …and only ONE of them paints (Fo1, 2026-09-07). A bordered field GROUP — Input's prefix/suffix
79
- wrapper, NumberField's stepper group, ChipInput, the Combobox input-group — clips with
80
- `overflow-hidden` so its addons follow the rounded corner. The inner input's own outline is
81
- offset outward INTO that clip, so it was drawn and then cut, which is how an addon field ended
82
- up with no visible focus at all under the forced palette. The group carries the outline (it is
83
- the element whose border the tint would have moved); the control inside it stands down, so the
84
- two never paint a double ring. `data-field-group` is set by every consumer of
85
- `fieldControlGroup` in `@vegastack/design` — the attribute IS the contract. */
125
+ /* …and only ONE of them paints. A bordered field GROUP — Input's prefix/suffix wrapper,
126
+ NumberField's stepper group, ChipInput, the Combobox input-group — clips with `overflow-hidden`
127
+ so its addons follow the rounded corner. The inner input's own outline is offset outward INTO
128
+ that clip, so it was drawn and then cut, which is how an addon field ended up with no visible
129
+ focus at all under the forced palette. The group carries the outline (it is the element whose
130
+ border the tint would have moved); the control inside it stands down, so the two never paint a
131
+ double ring. */
86
132
  [data-field-group] input:focus,
87
133
  [data-field-group] textarea:focus {
88
134
  outline: none;
@@ -90,15 +136,13 @@
90
136
  }
91
137
 
92
138
  /* ───────────────────────────────────────────────────────────────────────────────────────────────
93
- * THE ONE SANCTIONED `!important` EXCEPTION (WCAG 2.1 reduced-motion accessibility override).
94
- * Users who set `prefers-reduced-motion: reduce` MUST have animations/transitions neutralized even
95
- * over component-authored durations; `!important` is the standard, intentional mechanism for this
96
- * reset (it has to win against any animated rule, including ones defined later or with higher
97
- * specificity). This is the SOLE place `!important` is permitted in shipped VegaStack CSS — the
98
- * design-audit contract bans `!important` everywhere else, and `design-lint` (token-css mode)
99
- * FAILS on any `!important` that is NOT inside this `prefers-reduced-motion: reduce` block. Do not
100
- * add `!important` to win a specificity fight elsewhere; restructure the selector or token instead.
101
- * (requirements §7.5 / skills/design-audit.)
139
+ * MOT-5 — THE ONE SANCTIONED `!important` EXCEPTION (WCAG 2.1 reduced-motion override).
140
+ * Users who set `prefers-reduced-motion: reduce` MUST have animations and transitions neutralised
141
+ * even over component-authored durations; `!important` is the standard, intentional mechanism for
142
+ * this reset (it has to win against any animated rule, including ones defined later or with higher
143
+ * specificity). This is the SOLE place `!important` is permitted in shipped VegaStack CSS, and
144
+ * `design-lint --token-css` FAILS on any `!important` outside this block. Do not add `!important`
145
+ * to win a specificity fight elsewhere; restructure the selector or token instead.
102
146
  * ──────────────────────────────────────────────────────────────────────────────────────────────*/
103
147
  @media (prefers-reduced-motion: reduce) {
104
148
  *,
@@ -106,22 +150,19 @@
106
150
  ::after {
107
151
  animation-duration: 0.01ms !important;
108
152
  animation-iteration-count: 1 !important;
109
- /* A zeroed DURATION alone is not enough: a staggered entrance schedules its
110
- * per-item `animation-delay` in real time, so the items would still appear
111
- * one after another (each instantly) over the full stagger window. Zeroing
112
- * the delay too is what makes "reduced motion" a static end state and it
113
- * is why no component needs its own `motion-reduce:` restatement (audit
114
- * batch Di1 / B2-06). */
153
+ /* A zeroed DURATION alone is not enough: a staggered entrance schedules its per-item
154
+ * `animation-delay` in real time, so the items would still appear one after another (each
155
+ * instantly) over the full stagger window. Zeroing the delay too is what makes "reduced motion"
156
+ * a static end state and it is why no component needs its own `motion-reduce:` restatement. */
115
157
  animation-delay: 0s !important;
116
158
  transition-duration: 0.01ms !important;
117
159
  transition-delay: 0s !important;
118
160
  scroll-behavior: auto !important;
119
161
  }
120
162
 
121
- /* View Transitions (Phase S): the universal selector above never reaches the
122
- * ::view-transition-* pseudo-element tree (it lives on the root's snapshot layer,
123
- * outside normal element matching), so route-change cross-fades/morphs need their
124
- * own reduced-motion kill switch. */
163
+ /* View Transitions: the universal selector above never reaches the ::view-transition-* pseudo
164
+ * element tree (it lives on the root's snapshot layer, outside normal element matching), so
165
+ * route-change cross-fades and morphs need their own reduced-motion kill switch. */
125
166
  ::view-transition-group(*),
126
167
  ::view-transition-old(*),
127
168
  ::view-transition-new(*) {