@omkarux/vela 0.3.1 → 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/CHANGELOG.md CHANGED
@@ -3,6 +3,41 @@
3
3
  All notable changes to this project are documented here.
4
4
  This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5
5
 
6
+ ## [0.5.0] — 2026-09-10
7
+
8
+ ### Added
9
+ - **A single token source.** `tokens/vela.tokens.json` (W3C Design Tokens 2025.10) now holds every
10
+ value once. `src/styles/tokens.*.css` are generated from it and were proven byte-identical to the
11
+ hand-written files before the switch. `npm run tokens:check` runs in the gate and fails if the
12
+ CSS is edited directly.
13
+ - **Figma sync.** `npm run tokens:figma` generates a Plugin API script that creates-or-updates every
14
+ variable in the library by name — values per mode, aliases, scopes, descriptions and the
15
+ `var(--vela-*)` code syntax. Idempotent: 0 created / 223 updated on its first run against the live
16
+ file. This closes the gap where the Figma library was a one-time export that would drift from
17
+ the code. The REST route was not an option: writing variables needs an Enterprise plan.
18
+ - The JSON ships in the package as `@omkarux/vela/tokens.json` for other stacks and tools
19
+ (Style Dictionary, Tokens Studio, Terrazzo all read the format).
20
+ - 5 tests guard the pipeline: every CSS declaration has a source token and vice versa, no
21
+ dangling alias, primitives hold raw values and semantics hold aliases only, every token carries
22
+ its Figma name, and regenerating the CSS changes nothing.
23
+
24
+ ## [0.4.0] — 2026-09-10
25
+
26
+ ### Added
27
+ - **Guidance for using the system on a stack that isn't React.** `guidelines/setup.md` and the
28
+ README now state plainly which layers travel and which don't: the tokens and the specs port
29
+ unchanged, the components do not. `npm i @omkarux/vela` followed by importing only
30
+ `tokens.css` gives an Angular, Vue, Svelte or server-rendered app the full palette, both
31
+ themes and the whole scale with no React in its dependency tree.
32
+ - `llms.txt` now tells a coding agent the specs are implementation-independent, so an agent
33
+ generating Angular follows every rule here and translates only the syntax.
34
+
35
+ ### Changed
36
+ - Stopped describing this as a "React component kit" in the README, the npm description and
37
+ `llms.txt`. It was accurate about the package and misleading about the method — React is the
38
+ boundary of *this implementation*, not of the approach, and conflating the two undersells the
39
+ contract, which is the part that actually ports.
40
+
6
41
  ## [0.3.1] — 2026-09-10
7
42
 
8
43
  ### Fixed
package/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # Vela
2
2
 
3
- A token-first React component kit. Six components, two themes, zero runtime dependencies
4
- and every design rule in the documentation is enforced by the TypeScript compiler.
3
+ A token-first design system. The tokens and the component specs are framework-agnostic;
4
+ this repository is a **React implementation** of them. Six components, two themes, zero runtime
5
+ dependencies — and every design rule in the documentation is enforced by the TypeScript compiler.
5
6
 
6
7
  ```bash
7
8
  npm install @omkarux/vela
@@ -100,6 +101,43 @@ npm run contrast # WCAG report for both themes
100
101
  npm run verify # typecheck + tests + build + pack-and-consume gate
101
102
  ```
102
103
 
104
+ ## Editing tokens — one source, three targets
105
+
106
+ `tokens/vela.tokens.json` is the only place a value is written. It uses the W3C Design Tokens
107
+ format (2025.10), ships inside the package as `@omkarux/vela/tokens.json`, and everything else is
108
+ generated from it:
109
+
110
+ ```bash
111
+ npm run tokens # → src/styles/tokens.light.css + tokens.dark.body.css
112
+ npm run tokens:figma # → a Plugin API script that creates-or-updates every Figma variable
113
+ npm run tokens:check # fails if the CSS was hand-edited instead of the JSON (runs in CI)
114
+ ```
115
+
116
+ Figma's REST API only lets Enterprise plans write variables, so the Figma half is a generated
117
+ script run inside the file (through the Figma MCP or the Scripter plugin). It looks each variable
118
+ up by collection and name, so re-running never duplicates: the first run against the live library
119
+ reported 0 created / 223 updated, then created the 11 numeric tokens Figma had never had.
120
+
121
+ ## Using it on another stack
122
+
123
+ Worth being precise about what travels and what doesn't:
124
+
125
+ | Layer | Ports? |
126
+ |---|---|
127
+ | **Tokens** (`dist/tokens.css`) | **Entirely.** Plain CSS custom properties — Angular, Vue, Svelte, Rails, plain HTML. From the same source you could emit Swift, Kotlin or XML for native. |
128
+ | **The contract** (`guidelines/`) | **Entirely.** Intent, closed prop sets, token bindings, accessibility, anti-patterns. Only the code samples are React-shaped. |
129
+ | **Components** (`dist/index.js`) | **No.** One implementation per framework, by definition. |
130
+ | **The contrast audit** (`npm run contrast`) | **Entirely.** It parses CSS and knows nothing about React. |
131
+
132
+ So on Angular you would take `tokens.css` and `guidelines/` unchanged, and write the components
133
+ against your own idioms — standalone components, signals, `ControlValueAccessor` for form controls.
134
+ The closed prop sets become union input types with `strictTemplates` enabled, which buys most of
135
+ what the discriminated unions buy here.
136
+
137
+ This is not a limitation to work around. If an organisation runs more than one framework, no
138
+ framework's components can be the source of truth for the others — **the only thing that can be
139
+ shared is the contract**, which is the argument this repository exists to make.
140
+
103
141
  ## Scope
104
142
 
105
143
  Six components, chosen to exercise every foundation and both status taxonomies. Not yet
@@ -1,8 +1,13 @@
1
1
  # Vela Design System
2
2
 
3
- > A token-first React component kit. Two-layer colour architecture (primitives -> semantics),
4
- > three deliberately separate status taxonomies, full light and dark theming, zero runtime
5
- > dependencies. Every design constraint below is enforced by the TypeScript types, not by review.
3
+ > A token-first design system. Two-layer colour architecture (primitives -> semantics), three
4
+ > deliberately separate status taxonomies, full light and dark theming, zero runtime dependencies.
5
+ > Every design constraint below is enforced by the TypeScript types, not by review.
6
+ >
7
+ > THESE SPECS ARE FRAMEWORK-AGNOSTIC. The published package is a React implementation of them, and
8
+ > the code samples below are React — but the tokens, the closed prop sets, the taxonomies and the
9
+ > accessibility rules hold for any implementation. If you are generating for Angular, Vue or
10
+ > Svelte, follow every rule here and translate only the syntax.
6
11
 
7
12
  Read `overview.md` first: it maps a user need to a component and lists the choices agents
8
13
  most often get wrong. Then read the per-component file before generating any markup.
@@ -59,6 +59,20 @@ fallbacks in the font stack, so the kit degrades rather than breaks if you do no
59
59
 
60
60
  ## Framework
61
61
 
62
- React 18.3+ / 19. Components are plain functional components consuming `--vela-*` tokens.
63
- There is no CSS-in-JS runtime, no Tailwind requirement, and no icon-library dependency
64
- the token layer is plain CSS custom properties and works in any framework.
62
+ **This package:** React 18.3+ / 19. Plain functional components consuming `--vela-*` tokens.
63
+ No CSS-in-JS runtime, no Tailwind requirement, no icon-library dependency.
64
+
65
+ **Any other framework:** the token layer and the specs port unchanged; the components do not.
66
+
67
+ ```bash
68
+ npm install @omkarux/vela # then import only the tokens
69
+ ```
70
+ ```ts
71
+ import '@omkarux/vela/tokens.css'
72
+ ```
73
+
74
+ That gives an Angular, Vue, Svelte or server-rendered app the full two-layer palette, both themes,
75
+ the spacing and sizing scales and the type ramp — with no React in the dependency tree. Build the
76
+ components against your own framework's idioms and hold them to `guidelines/`, which is written to
77
+ be implementation-independent. On Angular, the closed prop sets become union input types with
78
+ `strictTemplates` enabled; that buys most of what the discriminated unions buy in React.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@omkarux/vela",
3
- "version": "0.3.1",
4
- "description": "A token-first React component kit: two-layer color architecture, separate severity and risk taxonomies, full light and dark theming, zero runtime dependencies.",
3
+ "version": "0.5.0",
4
+ "description": "A token-first design system: framework-agnostic tokens and component specs with a React implementation. Two-layer color architecture, separate severity and risk taxonomies, light and dark theming, WCAG AA asserted in CI, zero runtime dependencies.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": "Omkar Khadamkar",
@@ -26,18 +26,23 @@
26
26
  "./styles.css": "./dist/vela.css",
27
27
  "./tokens.css": "./dist/tokens.css",
28
28
  "./guidelines/*": "./guidelines/*",
29
- "./package.json": "./package.json"
29
+ "./package.json": "./package.json",
30
+ "./tokens.json": "./tokens/vela.tokens.json"
30
31
  },
31
32
  "files": [
32
33
  "dist",
33
34
  "guidelines",
34
35
  "README.md",
35
36
  "CHANGELOG.md",
36
- "LICENSE"
37
+ "LICENSE",
38
+ "tokens"
37
39
  ],
38
40
  "scripts": {
39
41
  "clean": "rm -rf dist",
40
- "build": "npm run clean && npm run build:js && npm run build:css",
42
+ "tokens": "node scripts/build-tokens.mjs",
43
+ "tokens:figma": "node scripts/sync-figma.mjs",
44
+ "tokens:check": "node scripts/build-tokens.mjs && git diff --exit-code -- src/styles/tokens.light.css src/styles/tokens.dark.body.css",
45
+ "build": "npm run clean && npm run tokens && npm run build:js && npm run build:css",
41
46
  "build:js": "vite build",
42
47
  "build:css": "node scripts/build-css.mjs",
43
48
  "predev": "npm run build:css",
@@ -48,7 +53,7 @@
48
53
  "test:watch": "vitest",
49
54
  "typecheck": "tsc --noEmit",
50
55
  "contrast": "node scripts/contrast-report.mjs",
51
- "verify": "npm run typecheck && npm run test && npm run build && node scripts/verify-pack.mjs",
56
+ "verify": "npm run tokens:check && npm run typecheck && npm run test && npm run build && node scripts/verify-pack.mjs",
52
57
  "prepublishOnly": "npm run verify"
53
58
  },
54
59
  "peerDependencies": {
@@ -0,0 +1,152 @@
1
+ /* ============================================================================
2
+ DARK THEME
3
+ Note what is NOT here: primitives. Not one raw hex is redefined below.
4
+ Dark mode is achieved entirely by re-pointing semantic tokens onto a
5
+ different part of the same scale. That is the return on the two-layer
6
+ architecture — and the reason components never bind primitives directly.
7
+ Contrast ratios are asserted in src/styles/contrast.test.ts.
8
+ ============================================================================ */
9
+ color-scheme: dark;
10
+
11
+ /* ---- Surface ---- */
12
+ @bg-global
13
+ @bg-container
14
+ @bg-raised
15
+ @bg-hover
16
+ @bg-tooltip
17
+ @bg-inactive
18
+ @bg-selected
19
+ @primary-tint
20
+ @primary-active-bg
21
+
22
+ /* ---- Border ---- */
23
+ @border-default
24
+ @border-subtle
25
+ @border-active
26
+ @border-inactive
27
+ @border-action
28
+
29
+ /* ---- Text ---- */
30
+ @text-default
31
+ @text-heading
32
+ @text-de-emphasized
33
+ @text-inactive
34
+ @text-link
35
+ @text-on-brand
36
+ @text-on-tint
37
+ @text-error
38
+
39
+ /* ---- Icon ---- */
40
+ @icon-default
41
+ @icon-strong
42
+ @icon-selected
43
+ @icon-disabled
44
+ @icon-on-brand
45
+
46
+ /* ---- Button ---- */
47
+ @btn-primary-bg
48
+ @btn-primary-hover-bg
49
+ @btn-primary-border
50
+ @btn-primary-text
51
+ @btn-standard-bg
52
+ @btn-standard-hover-bg
53
+ @btn-standard-text
54
+ @btn-hollow-primary-text
55
+ @btn-hollow-primary-border
56
+ @btn-disabled-bg
57
+ @btn-disabled-text
58
+
59
+
60
+ @btn-destructive-bg
61
+ @btn-destructive-hover-bg
62
+ @btn-destructive-text
63
+ @btn-destructive-hollow-text
64
+ @btn-standard-border
65
+ @btn-hollow-hover-bg
66
+
67
+ /* ---- Form control ---- */
68
+ @control-bg
69
+ @control-border
70
+ @control-border-hover
71
+ @control-border-active
72
+ @control-border-error
73
+ @control-bg-disabled
74
+ @control-placeholder
75
+ @toggle-off-bg
76
+ @toggle-on-bg
77
+ @toggle-knob
78
+
79
+ /* ---- Severity (6) ---- */
80
+ @bg-severity-success
81
+ @border-severity-success
82
+ @signal-severity-success
83
+ @icon-severity-success
84
+ @text-severity-success
85
+ @border-severity-success-indicator
86
+
87
+ @bg-severity-info
88
+ @border-severity-info
89
+ @signal-severity-info
90
+ @icon-severity-info
91
+ @text-severity-info
92
+ @border-severity-info-indicator
93
+
94
+ @bg-severity-warning
95
+ @border-severity-warning
96
+ @signal-severity-warning
97
+ @icon-severity-warning
98
+ @text-severity-warning
99
+ @border-severity-warning-indicator
100
+
101
+ @bg-severity-minor
102
+ @border-severity-minor
103
+ @signal-severity-minor
104
+ @icon-severity-minor
105
+ @text-severity-minor
106
+ @border-severity-minor-indicator
107
+
108
+ @bg-severity-major
109
+ @border-severity-major
110
+ @signal-severity-major
111
+ @icon-severity-major
112
+ @text-severity-major
113
+ @border-severity-major-indicator
114
+
115
+ @bg-severity-critical
116
+ @border-severity-critical
117
+ @signal-severity-critical
118
+ @icon-severity-critical
119
+ @text-severity-critical
120
+ @border-severity-critical-indicator
121
+
122
+ /* ---- Risk (3) ---- */
123
+ @bg-risk-low
124
+ @border-risk-low
125
+ @signal-risk-low
126
+ @icon-risk-low
127
+ @text-risk-low
128
+ @border-risk-low-indicator
129
+
130
+ @bg-risk-medium
131
+ @border-risk-medium
132
+ @signal-risk-medium
133
+ @icon-risk-medium
134
+ @text-risk-medium
135
+ @border-risk-medium-indicator
136
+
137
+ @bg-risk-high
138
+ @border-risk-high
139
+ @signal-risk-high
140
+ @icon-risk-high
141
+ @text-risk-high
142
+ @border-risk-high-indicator
143
+
144
+ /* ---- Status (5 dots) ---- */
145
+ @signal-status-unknown
146
+ @signal-status-healthy
147
+ @signal-status-warning
148
+ @signal-status-medium
149
+ @signal-status-unhealthy
150
+
151
+ /* ---- Focus ---- */
152
+ @focus-ring-color
@@ -0,0 +1,293 @@
1
+ /* ============================================================================
2
+ VELA — Design tokens
3
+ Two layers: primitives (raw scale values) and semantics (role-named).
4
+ Always bind to semantics. Primitives exist so semantics can be re-pointed
5
+ for theming — binding a primitive directly defeats the entire architecture.
6
+ Numeric tokens carry their unit. Use them directly; never calc(... * 1px).
7
+ ============================================================================ */
8
+
9
+ :root {
10
+ color-scheme: light;
11
+
12
+ /* ---- Primitives: neutral ---- */
13
+ @black
14
+ @white
15
+ @grey-50
16
+ @grey-100
17
+ @grey-200
18
+ @grey-300
19
+ @grey-400
20
+ @grey-500
21
+ @grey-600 /* AA-safe de-emphasized text on white */
22
+ @grey-700
23
+ @grey-800
24
+ @grey-850
25
+ @grey-900
26
+ @grey-950
27
+
28
+ /* ---- Primitives: brand / blue ---- */
29
+ @primary-300
30
+ @primary-400
31
+ @primary-500
32
+ @primary-600
33
+ @blue-100
34
+ @blue-150
35
+ @blue-300
36
+ @blue-500
37
+ @blue-700
38
+ @blue-950
39
+
40
+ /* ---- Primitives: red / critical ---- */
41
+ @red-100
42
+ @red-300
43
+ @red-500
44
+ @red-700
45
+ @red-950
46
+ @critical-100
47
+ @critical-300
48
+ @critical-500
49
+ @critical-700
50
+ @critical-950
51
+
52
+ /* ---- Primitives: yellow / orange / green ---- */
53
+ @yellow-100
54
+ @yellow-300
55
+ @yellow-500
56
+ @yellow-600
57
+ @yellow-700
58
+ @yellow-800
59
+ @yellow-950
60
+ @orange-100
61
+ @orange-300
62
+ @orange-500
63
+ @orange-600 /* AA-safe status dot on white */
64
+ @orange-700
65
+ @orange-950
66
+ @green-100
67
+ @green-300
68
+ @green-500
69
+ @green-700
70
+ @green-800 /* AA-safe success text on green-100 */
71
+ @green-950
72
+
73
+ /* ---- Semantic: surface ---- */
74
+ @bg-global
75
+ @bg-container
76
+ @bg-raised
77
+ @bg-hover
78
+ @bg-tooltip
79
+ @bg-inactive
80
+ @bg-selected
81
+ @primary-tint
82
+ @primary-active-bg
83
+
84
+ /* ---- Semantic: border ---- */
85
+ @border-default
86
+ @border-subtle
87
+ @border-active
88
+ @border-inactive
89
+ @border-action
90
+
91
+ /* ---- Semantic: text ---- */
92
+ @text-default
93
+ @text-heading
94
+ @text-de-emphasized
95
+ @text-inactive
96
+ @text-link
97
+ @text-on-brand
98
+ @text-on-tint
99
+ @text-error
100
+
101
+ /* ---- Semantic: icon ---- */
102
+ @icon-default
103
+ @icon-strong
104
+ @icon-selected
105
+ @icon-disabled
106
+ @icon-on-brand
107
+
108
+ /* ---- Semantic: button ---- */
109
+ @btn-primary-bg
110
+ @btn-primary-hover-bg
111
+ @btn-primary-border
112
+ @btn-primary-text
113
+ @btn-standard-bg
114
+ @btn-standard-hover-bg
115
+ @btn-standard-text
116
+ @btn-hollow-primary-text
117
+ @btn-hollow-primary-border
118
+ @btn-disabled-bg
119
+ @btn-disabled-text
120
+
121
+
122
+ /* ---- Semantic: destructive button ----
123
+ NOTE: red-500 on white is 4.08:1 and fails WCAG AA for normal text.
124
+ Destructive filled therefore binds red-700 (6.57:1). This is a
125
+ deliberate divergence from a naive "use the signal color" mapping. */
126
+ @btn-destructive-bg
127
+ @btn-destructive-hover-bg
128
+ @btn-destructive-text
129
+ @btn-destructive-hollow-text
130
+ @btn-standard-border
131
+ @btn-hollow-hover-bg
132
+
133
+ /* ---- Semantic: form control ---- */
134
+ @control-bg
135
+ @control-border
136
+ @control-border-hover
137
+ @control-border-active
138
+ @control-border-error
139
+ @control-bg-disabled
140
+ @control-placeholder
141
+ @toggle-off-bg
142
+ @toggle-on-bg
143
+ @toggle-knob
144
+
145
+ /* ============================================================================
146
+ SEVERITY — 6 levels. Describes events and alerts.
147
+ Never interchange with Risk or Status. There is no "high severity".
148
+ Every level defines the full row: bg, border, signal, icon, text, indicator.
149
+ ============================================================================ */
150
+ @bg-severity-success
151
+ @border-severity-success
152
+ @signal-severity-success
153
+ @icon-severity-success
154
+ @text-severity-success
155
+ @border-severity-success-indicator
156
+
157
+ @bg-severity-info
158
+ @border-severity-info
159
+ @signal-severity-info
160
+ @icon-severity-info
161
+ @text-severity-info
162
+ @border-severity-info-indicator
163
+
164
+ @bg-severity-warning
165
+ @border-severity-warning
166
+ @signal-severity-warning
167
+ @icon-severity-warning
168
+ @text-severity-warning
169
+ @border-severity-warning-indicator
170
+
171
+ @bg-severity-minor
172
+ @border-severity-minor
173
+ @signal-severity-minor
174
+ @icon-severity-minor
175
+ @text-severity-minor
176
+ @border-severity-minor-indicator
177
+
178
+ @bg-severity-major
179
+ @border-severity-major
180
+ @signal-severity-major
181
+ @icon-severity-major
182
+ @text-severity-major
183
+ @border-severity-major-indicator
184
+
185
+ @bg-severity-critical
186
+ @border-severity-critical
187
+ @signal-severity-critical
188
+ @icon-severity-critical
189
+ @text-severity-critical
190
+ @border-severity-critical-indicator
191
+
192
+ /* ============================================================================
193
+ RISK — 3 levels. Describes scoring. There is no "critical risk".
194
+ ============================================================================ */
195
+ @bg-risk-low
196
+ @border-risk-low
197
+ @signal-risk-low
198
+ @icon-risk-low
199
+ @text-risk-low
200
+ @border-risk-low-indicator
201
+
202
+ @bg-risk-medium
203
+ @border-risk-medium
204
+ @signal-risk-medium
205
+ @icon-risk-medium
206
+ @text-risk-medium
207
+ @border-risk-medium-indicator
208
+
209
+ @bg-risk-high
210
+ @border-risk-high
211
+ @signal-risk-high
212
+ @icon-risk-high
213
+ @text-risk-high
214
+ @border-risk-high-indicator
215
+
216
+ /* ============================================================================
217
+ STATUS — 5 health states. Status Indicator dots only. Neither of the above.
218
+ ============================================================================ */
219
+ @signal-status-unknown
220
+ @signal-status-healthy
221
+ @signal-status-warning
222
+ @signal-status-medium
223
+ @signal-status-unhealthy
224
+
225
+ /* ---- Spacing: 5px base. 25 exists; 50 does not. ---- */
226
+ @space-5
227
+ @space-10
228
+ @space-15
229
+ @space-20
230
+ @space-25
231
+ @space-30
232
+ @space-40
233
+ @space-60
234
+ @space-80
235
+
236
+ /* ---- Icon sizes ---- */
237
+ @icon-12
238
+ @icon-16
239
+ @icon-20
240
+ @icon-24
241
+
242
+ /* ---- Control heights ---- */
243
+ @control-height-24
244
+ @control-height-25
245
+ @control-height-26
246
+ @control-height-35
247
+ @control-height-40
248
+ @control-height-48
249
+ @control-height-60
250
+
251
+ /* ---- Radius ---- */
252
+ @radius-3
253
+ @radius-4
254
+ @radius-8
255
+ @radius-12
256
+ @radius-full
257
+
258
+ /* ---- Typography: family & weight ---- */
259
+ @font-family-primary
260
+ @font-family-display
261
+ @font-weight-light
262
+ @font-weight-regular
263
+ @font-weight-semibold
264
+
265
+ /* ---- Typography: scale (size / weight / line-height triples) ---- */
266
+ @text-h1-size --vela-text-h1-weight: 300; --vela-text-h1-line-height: 42px;
267
+ @text-h2-size --vela-text-h2-weight: 300; --vela-text-h2-line-height: 33px;
268
+ @text-h3-size --vela-text-h3-weight: 300; --vela-text-h3-line-height: 27px;
269
+ @text-h4-size --vela-text-h4-weight: 400; --vela-text-h4-line-height: 24px;
270
+ @text-h5-size --vela-text-h5-weight: 600; --vela-text-h5-line-height: 21px;
271
+ @text-h6-size --vela-text-h6-weight: 600; --vela-text-h6-line-height: 18px;
272
+ @text-body-size --vela-text-body-weight: 400; --vela-text-body-line-height: 21px;
273
+ @text-meta-size --vela-text-meta-weight: 400; --vela-text-meta-line-height: 18px;
274
+ @text-meta-tracking
275
+
276
+ @text-button-tiny-size --vela-text-button-tiny-line-height: 18px;
277
+ @text-button-regular-size --vela-text-button-regular-line-height: 21px;
278
+ @text-button-large-size --vela-text-button-large-line-height: 22px;
279
+ @text-button-huge-size --vela-text-button-huge-line-height: 24px;
280
+
281
+ @text-tab-size --vela-text-tab-line-height: 19px;
282
+ @text-tab-large-size --vela-text-tab-large-line-height: 25px;
283
+
284
+ /* ---- Focus ---- */
285
+ @focus-ring-width
286
+ @focus-ring-offset
287
+ @focus-ring-color
288
+
289
+ /* ---- Motion (respects reduced-motion in components.css) ---- */
290
+ @duration-fast
291
+ @duration-base
292
+ @ease-standard
293
+ }