@vegastack/design-tokens 0.4.0 → 0.5.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,62 @@
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;
12
35
  }
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`. */
36
+ html {
37
+ @apply font-sans;
38
+ }
39
+
40
+ /* FOC-1 — the SINGLE focus affordance in the system. 2px solid, offset 1, in `ring`, which
41
+ FOC-2 binds to the near-black / near-white ink rather than shadcn's mid-grey. Components carry
42
+ no focus ring of their own: upstream's `focus-visible:ring-3 focus-visible:ring-ring/50` glow
43
+ is stripped from every migrated component (FOC-6), and `design-lint`'s `no-focus-ring-glow`
44
+ rule is what stops it coming back through a later upstream pull. */
16
45
  :focus-visible {
17
46
  @apply outline-2 outline-offset-1 outline-ring;
18
47
  }
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. */
48
+
49
+ /* INT-1 the hand cursor. shadcn's `--pointer` flag writes
50
+ `button:not(:disabled), [role="button"]:not(:disabled) { cursor: pointer }`; ours is the same
51
+ rule over the full set of things a person clicks, so a menu row, a tab, a switch and a listbox
52
+ option all read as clickable. This is the SINGLE global source of truth components must NOT
53
+ re-declare `cursor-pointer` locally; only genuinely non-standard clickables (a clickable table
54
+ row, a label tied to a control) or non-pointer cursors (`cursor-text`, the select scroll
55
+ arrows) set a cursor of their own.
56
+
57
+ INT-7 rides on the same selector list: keep touch controls responsive without disabling page
58
+ pan or pinch-zoom, and suppress the grey Android tap flash. Text-entry fields and document
59
+ links stay untouched. */
25
60
  button,
26
61
  [role="button"],
27
62
  [role="tab"],
@@ -34,34 +69,24 @@
34
69
  [role="option"],
35
70
  summary {
36
71
  cursor: pointer;
37
- /* Keep touch controls responsive without disabling page pan/pinch zoom.
38
- Text-entry fields and document links stay untouched. */
39
72
  touch-action: manipulation;
40
73
  -webkit-tap-highlight-color: transparent;
41
74
  }
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
75
  }
51
76
 
52
- /* Preserve the focus contract when authored colors are replaced by a forced-colors palette. */
77
+ /* FOC-7 — preserve the focus contract when authored colours are replaced by a forced-colours
78
+ palette. */
53
79
  @media (forced-colors: active) {
54
80
  :focus-visible {
55
81
  outline-color: Highlight;
56
82
  }
57
83
 
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.
84
+ /* Text-entry focus (FOC-3). Every text-entry control signals focus with a border TINT and strips
85
+ the outline, because a raw text field cannot tell mouse from keyboard. Forced colours replaces
86
+ `border-color` outright, so the tint vanishes and `outline-style: none` stays in force a
87
+ Windows High Contrast user had no caret location at all. This block is UNLAYERED on purpose:
88
+ it beats the component's `outline-none` (a layered utility) regardless of specificity, so the
89
+ fallback is written ONCE here, never per component.
65
90
  - `input`/`textarea` at `:focus` (not `:focus-visible`, matching the tint's trigger);
66
91
  - composite editors (TextEdit's contenteditable) at `:focus`;
67
92
  - wrapper-tinted surfaces (`data-slot="text-edit"`, and every `data-field-group`) at
@@ -75,14 +100,13 @@
75
100
  outline-offset: 1px;
76
101
  }
77
102
 
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. */
103
+ /* …and only ONE of them paints. A bordered field GROUP — Input's prefix/suffix wrapper,
104
+ NumberField's stepper group, ChipInput, the Combobox input-group — clips with `overflow-hidden`
105
+ so its addons follow the rounded corner. The inner input's own outline is offset outward INTO
106
+ that clip, so it was drawn and then cut, which is how an addon field ended up with no visible
107
+ focus at all under the forced palette. The group carries the outline (it is the element whose
108
+ border the tint would have moved); the control inside it stands down, so the two never paint a
109
+ double ring. */
86
110
  [data-field-group] input:focus,
87
111
  [data-field-group] textarea:focus {
88
112
  outline: none;
@@ -90,15 +114,13 @@
90
114
  }
91
115
 
92
116
  /* ───────────────────────────────────────────────────────────────────────────────────────────────
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.)
117
+ * MOT-5 — THE ONE SANCTIONED `!important` EXCEPTION (WCAG 2.1 reduced-motion override).
118
+ * Users who set `prefers-reduced-motion: reduce` MUST have animations and transitions neutralised
119
+ * even over component-authored durations; `!important` is the standard, intentional mechanism for
120
+ * this reset (it has to win against any animated rule, including ones defined later or with higher
121
+ * specificity). This is the SOLE place `!important` is permitted in shipped VegaStack CSS, and
122
+ * `design-lint --token-css` FAILS on any `!important` outside this block. Do not add `!important`
123
+ * to win a specificity fight elsewhere; restructure the selector or token instead.
102
124
  * ──────────────────────────────────────────────────────────────────────────────────────────────*/
103
125
  @media (prefers-reduced-motion: reduce) {
104
126
  *,
@@ -106,22 +128,19 @@
106
128
  ::after {
107
129
  animation-duration: 0.01ms !important;
108
130
  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). */
131
+ /* A zeroed DURATION alone is not enough: a staggered entrance schedules its per-item
132
+ * `animation-delay` in real time, so the items would still appear one after another (each
133
+ * instantly) over the full stagger window. Zeroing the delay too is what makes "reduced motion"
134
+ * a static end state and it is why no component needs its own `motion-reduce:` restatement. */
115
135
  animation-delay: 0s !important;
116
136
  transition-duration: 0.01ms !important;
117
137
  transition-delay: 0s !important;
118
138
  scroll-behavior: auto !important;
119
139
  }
120
140
 
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. */
141
+ /* View Transitions: the universal selector above never reaches the ::view-transition-* pseudo
142
+ * element tree (it lives on the root's snapshot layer, outside normal element matching), so
143
+ * route-change cross-fades and morphs need their own reduced-motion kill switch. */
125
144
  ::view-transition-group(*),
126
145
  ::view-transition-old(*),
127
146
  ::view-transition-new(*) {