@vinyasa/tokens 2.0.1 → 2.0.2
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 +176 -43
- package/dist/cjs/index.cjs +250 -298
- package/dist/esm/VinyasaProvider.d.ts +5 -2
- package/dist/esm/border.d.ts +4 -0
- package/dist/esm/brands.d.ts +20 -0
- package/dist/esm/contract.css.d.ts +5 -0
- package/dist/esm/density.d.ts +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +240 -251
- package/dist/esm/radius.d.ts +7 -0
- package/dist/esm/shadow.d.ts +3 -0
- package/dist/esm/spacing.d.ts +10 -0
- package/dist/esm/themes/base.d.ts +13 -3
- package/dist/esm/themes.d.ts +1 -1
- package/package.json +1 -1
- package/dist/esm/primitives.d.ts +0 -107
- package/dist/esm/themes/orange.d.ts +0 -3
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# @vinyasa/tokens
|
|
2
2
|
|
|
3
|
-
The shared design-token and theming foundation for `@vinyasa/*` components: color, spacing, radius, typography, motion, focus rings, opacity, shadows, elevation, blur, and z-index tokens, plus static breakpoint/grid values and a `VinyasaProvider` that resolves and injects the themeable tokens at runtime.
|
|
3
|
+
The shared design-token and theming foundation for `@vinyasa/*` components: color, spacing, radius, border, typography, motion, focus rings, opacity, shadows, elevation, blur, and z-index tokens, plus static breakpoint/grid values and a `VinyasaProvider` that resolves and injects the themeable tokens at runtime.
|
|
4
|
+
|
|
5
|
+
For a live, click-to-copy reference of every single token value, run Storybook and open **Foundations** (`pnpm storybook`, then Colors / Elevation / Typography / Spacing / Brand / Layout). This README covers setup, integration, and the full prop/API surface instead of re-listing every value.
|
|
4
6
|
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
@@ -28,65 +30,143 @@ function App() {
|
|
|
28
30
|
|
|
29
31
|
## How theming works
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
There's no "primitives" tier — every file owns its own literal values directly, organized by what actually varies together:
|
|
34
|
+
|
|
35
|
+
1. **Structural scales** (`spacing.ts`, `radius.ts`, `border.ts`, `shadow.ts`) — plain numeric/length scales that never change per theme or brand: the same `space1`–`space8`, `radiusXs`–`radiusFull`, `borderWidthThin`/`Thick`, and shadow shape everywhere. `themes/base.ts` pulls these in, adds typography/motion/opacity/blur/z-index (owned directly, since nothing else needs them), and defines the full `VinyasaTheme` interface every theme must satisfy.
|
|
36
|
+
2. **Scheme** (`themes/light.ts`, `themes/dark.ts`) — the neutral gray ramp, surface, text, borders, shadows, and the info/success/warning/error semantic colors. A true neutral, not tinted toward any brand — switching brand never changes how "the page" looks, only the accent.
|
|
37
|
+
3. **Brand** (`brands.ts`) — just the accent: `primary`/`onPrimary`/`focus`/`tertiary`/`onTertiary`, tuned separately for light and dark grounds and for normal/high contrast. `duskBrand` (default), `sunriseBrand`, and `yellowBrand` ship today; adding a new one is a new entry in `brands.ts` plus one line in `VinyasaProvider`'s internal lookup — no other file changes.
|
|
38
|
+
4. **Semantic contract** (`themeContract`, exported as `vars`) — the flat set of CSS custom property names every `@vinyasa/*` component's own `.css.ts` file reads. Components never reach past this into scheme or brand files directly.
|
|
39
|
+
5. **Runtime theme** — `VinyasaProvider` merges scheme + brand + density + your own `theme` override into one `VinyasaTheme` object and injects it as CSS custom properties at render time (via `@vanilla-extract/dynamic`), not baked into a static stylesheet. Overriding a value is a runtime prop, not a rebuild.
|
|
40
|
+
|
|
41
|
+
`breakpoints` and `grid` (from `layout.ts`) are the one exception to all of this — they're exported as plain static values, **not** part of `themeContract`. CSS custom properties can't appear inside `@media` query conditions (`@media (min-width: var(--x))` is invalid in every browser), so responsive breakpoints can never be runtime-themed the way colors or spacing can. Consume them as literals directly in your own component's build-time `@media` blocks.
|
|
42
|
+
|
|
43
|
+
## `VinyasaProvider` props
|
|
44
|
+
|
|
45
|
+
| Prop | Type | Default | Description |
|
|
46
|
+
| ------------- | ------------------------------------------ | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
47
|
+
| `children` | `ReactNode` | — | Required. |
|
|
48
|
+
| `colorScheme` | `'light' \| 'dark'` | `'light'` | Which scheme's neutrals/surface/text/borders/shadows/status colors to use. |
|
|
49
|
+
| `brand` | `'dusk' \| 'sunrise' \| 'yellow'` | `'dusk'` | Which accent (`primary`/`onPrimary`/`focus`/`tertiary`/`onTertiary`) to layer on top. |
|
|
50
|
+
| `contrast` | `'normal' \| 'high'` | `'normal'` | Swaps in WCAG-AAA-verified (7:1) shades for the tokens that need it at high contrast. |
|
|
51
|
+
| `density` | `'compact' \| 'comfortable' \| 'spacious'` | `'comfortable'` | Rescales `space1`–`space8` only — nothing else changes. |
|
|
52
|
+
| `theme` | `ThemeOverride` | `undefined` | `{ light?: { normal?, high? }, dark?: { normal?, high? } }` of `Partial<VinyasaTheme>` — scoped to the active `colorScheme`/`contrast`, see below. |
|
|
32
53
|
|
|
33
|
-
|
|
34
|
-
2. **Semantic contract** (`themeContract`, exported from this package) — a flat, brand-role naming tier covering colors (`primary`/`onPrimary`, `secondary`/`onSecondary`, `tertiary`/`onTertiary`, `error`/`onError`, `focus`), spacing (`space1`–`space6`), radii, border width, typography (font family/size/weight/letter-spacing/line-height), motion (duration/easing), focus rings, opacity, shadows, elevation, blur, and z-index. This is what a component's own `.css.ts` file imports and references — never the primitives directly.
|
|
54
|
+
Resolution order (each step merges onto the previous, right-most wins): `resolveBaseTheme(colorScheme, contrast)` → brand accent for `(brand, colorScheme, contrast)` → density spacing overlay → the `theme[colorScheme][contrast]` branch of your `theme` prop, if you supplied one.
|
|
35
55
|
|
|
36
|
-
|
|
56
|
+
### `colorScheme`
|
|
37
57
|
|
|
38
|
-
|
|
58
|
+
```tsx
|
|
59
|
+
<VinyasaProvider colorScheme="dark">
|
|
60
|
+
<YourApp />
|
|
61
|
+
</VinyasaProvider>
|
|
62
|
+
```
|
|
39
63
|
|
|
40
|
-
###
|
|
64
|
+
### `brand`
|
|
41
65
|
|
|
42
|
-
|
|
66
|
+
Independent of `colorScheme` — any brand works with either scheme:
|
|
43
67
|
|
|
44
68
|
```tsx
|
|
45
|
-
<VinyasaProvider
|
|
69
|
+
<VinyasaProvider brand="sunrise">
|
|
70
|
+
<YourApp />
|
|
71
|
+
</VinyasaProvider>
|
|
72
|
+
|
|
73
|
+
<VinyasaProvider colorScheme="dark" brand="sunrise">
|
|
46
74
|
<YourApp />
|
|
47
75
|
</VinyasaProvider>
|
|
48
76
|
```
|
|
49
77
|
|
|
50
|
-
|
|
78
|
+
### `contrast`
|
|
51
79
|
|
|
52
80
|
```tsx
|
|
53
|
-
|
|
81
|
+
<VinyasaProvider contrast="high">
|
|
82
|
+
<YourApp />
|
|
83
|
+
</VinyasaProvider>
|
|
84
|
+
```
|
|
54
85
|
|
|
55
|
-
|
|
86
|
+
### `density`
|
|
56
87
|
|
|
57
|
-
|
|
88
|
+
```tsx
|
|
89
|
+
<VinyasaProvider density="compact">
|
|
58
90
|
<YourApp />
|
|
59
|
-
</VinyasaProvider
|
|
91
|
+
</VinyasaProvider>
|
|
60
92
|
```
|
|
61
93
|
|
|
62
|
-
|
|
94
|
+
### Combining props
|
|
63
95
|
|
|
64
|
-
|
|
96
|
+
All four combine freely — e.g. a dark, high-contrast, compact, sunrise-branded app:
|
|
65
97
|
|
|
66
98
|
```tsx
|
|
67
|
-
|
|
99
|
+
<VinyasaProvider colorScheme="dark" brand="sunrise" contrast="high" density="compact">
|
|
100
|
+
<YourApp />
|
|
101
|
+
</VinyasaProvider>
|
|
102
|
+
```
|
|
68
103
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
104
|
+
### Nesting providers
|
|
105
|
+
|
|
106
|
+
An inner `VinyasaProvider` only affects the subtree it wraps — useful for a themed preview panel, an embedded widget, or a settings page previewing a different mode without affecting the rest of the app:
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
<VinyasaProvider>
|
|
110
|
+
<YourApp />
|
|
111
|
+
<VinyasaProvider colorScheme="dark">
|
|
112
|
+
<EmbeddedPreview />
|
|
113
|
+
</VinyasaProvider>
|
|
114
|
+
</VinyasaProvider>
|
|
73
115
|
```
|
|
74
116
|
|
|
75
|
-
###
|
|
117
|
+
### Overriding individual values
|
|
118
|
+
|
|
119
|
+
`theme` is scoped by `colorScheme` and `contrast` — an override written under `light` is only ever applied when `colorScheme="light"` is also active, and never leaks into `dark` (or vice versa). Every level is optional; only fill in the branches you actually need:
|
|
76
120
|
|
|
77
121
|
```tsx
|
|
78
|
-
<VinyasaProvider
|
|
122
|
+
<VinyasaProvider
|
|
123
|
+
theme={{
|
|
124
|
+
light: { normal: { primary: '#7c3aed' } },
|
|
125
|
+
dark: { normal: { primary: '#a78bfa' } },
|
|
126
|
+
}}
|
|
127
|
+
>
|
|
128
|
+
<YourApp />
|
|
129
|
+
</VinyasaProvider>
|
|
79
130
|
```
|
|
80
131
|
|
|
81
|
-
|
|
82
|
-
- `contrast`: `'normal'` (default) | `'high'` — every `colorScheme` × `contrast` combination is populated and WCAG-AAA-verified (7:1), not a placeholder; see `primitives.ts`'s and each `src/themes/*.ts` file's own comments for the exact ratios behind each override.
|
|
83
|
-
- `density`: `'comfortable'` (default) | `'compact'` | `'spacious'`.
|
|
132
|
+
Only overriding one scheme is fine — the other keeps its normal resolved value:
|
|
84
133
|
|
|
85
|
-
|
|
134
|
+
```tsx
|
|
135
|
+
<VinyasaProvider theme={{ light: { normal: { primary: '#7c3aed' } } }}>
|
|
136
|
+
<YourApp />
|
|
137
|
+
</VinyasaProvider>
|
|
138
|
+
```
|
|
86
139
|
|
|
87
|
-
|
|
140
|
+
**To scope an override to one `brand`** (rather than one `colorScheme`), pick the override object yourself, the same way you already pick which `brand` to pass — there's no separate brand axis in `theme` itself, since you already hold that value directly:
|
|
88
141
|
|
|
89
|
-
|
|
142
|
+
```tsx
|
|
143
|
+
const sunriseOverride = { light: { normal: { primary: '#ff5a2b' } } };
|
|
144
|
+
|
|
145
|
+
<VinyasaProvider brand={brand} theme={brand === 'sunrise' ? sunriseOverride : undefined}>
|
|
146
|
+
<YourApp />
|
|
147
|
+
</VinyasaProvider>;
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`createTheme` is a separate helper — it builds a complete, standalone `VinyasaTheme` object (based on `lightTheme`), not a `theme` prop value. Use it when you need a full theme object on its own (e.g. to pass to `ThemeContext` directly), not for scoped overrides:
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
import { createTheme } from '@vinyasa/tokens';
|
|
154
|
+
|
|
155
|
+
const customTheme = createTheme({ primary: '#7c3aed', onPrimary: '#ffffff' });
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Reading the theme programmatically
|
|
159
|
+
|
|
160
|
+
```tsx
|
|
161
|
+
import { useVinyasaTheme } from '@vinyasa/tokens';
|
|
162
|
+
|
|
163
|
+
function Component() {
|
|
164
|
+
const theme = useVinyasaTheme();
|
|
165
|
+
return <span style={{ color: theme.primary }}>...</span>;
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Reads the resolved `VinyasaTheme` object from React context — the same values injected as CSS custom properties, available as plain strings for cases (canvas, SVG, inline `style` math) that can't take a `var(...)`.
|
|
90
170
|
|
|
91
171
|
## Consuming the contract in your own component
|
|
92
172
|
|
|
@@ -107,20 +187,72 @@ export const root = style({
|
|
|
107
187
|
|
|
108
188
|
`pnpm create:package <name>` scaffolds new component packages wired to this contract by default.
|
|
109
189
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
190
|
+
### Wiring up the component's own ref
|
|
191
|
+
|
|
192
|
+
Any component that reads `themeContract` values on its own rendered node should use `useThemedRef` instead of a plain `useRef`/`forwardRef` ref — it merges the caller's own `ref` with an internal one used to dev-warn if the node ever renders outside a `VinyasaProvider` (e.g. via a portal that escapes its DOM subtree):
|
|
193
|
+
|
|
194
|
+
```tsx
|
|
195
|
+
import { useThemedRef } from '@vinyasa/tokens';
|
|
196
|
+
import { forwardRef, type ComponentPropsWithoutRef } from 'react';
|
|
197
|
+
|
|
198
|
+
const YourComponent = forwardRef<HTMLDivElement, ComponentPropsWithoutRef<'div'>>((props, ref) => {
|
|
199
|
+
const themedRef = useThemedRef(ref);
|
|
200
|
+
return <div ref={themedRef} {...props} />;
|
|
201
|
+
});
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Focus rings
|
|
205
|
+
|
|
206
|
+
`focusRingStyle(vars)` returns the shared focus-visible style (a soft box-shadow halo, not a hard `outline` — outlines don't reliably follow `border-radius` across browsers) to spread into a component's own `:focus-visible` selector:
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
import { recipe } from '@vanilla-extract/recipes';
|
|
210
|
+
import { focusRingStyle, themeContract as vars } from '@vinyasa/tokens';
|
|
211
|
+
|
|
212
|
+
export const button = recipe({
|
|
213
|
+
base: {
|
|
214
|
+
selectors: {
|
|
215
|
+
'&:focus-visible': focusRingStyle(vars),
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Breakpoints & grid
|
|
222
|
+
|
|
223
|
+
Static, not themeable — import and use directly in your own build-time styles:
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
import { style } from '@vanilla-extract/css';
|
|
227
|
+
import { breakpoints } from '@vinyasa/tokens';
|
|
228
|
+
|
|
229
|
+
export const responsive = style({
|
|
230
|
+
'@media': {
|
|
231
|
+
[`(min-width: ${breakpoints.md})`]: {
|
|
232
|
+
flexDirection: 'row',
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## Full API reference
|
|
239
|
+
|
|
240
|
+
| Export | Description |
|
|
241
|
+
| ---------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
|
|
242
|
+
| `VinyasaProvider` | React component. Props: `theme?`, `colorScheme?`, `brand?`, `contrast?`, `density?`, `children`. |
|
|
243
|
+
| `useVinyasaTheme()` | Hook returning the currently resolved `VinyasaTheme` object. |
|
|
244
|
+
| `useThemedRef(ref?)` | Hook merging a caller's ref with a dev-mode "rendered outside a provider" check. |
|
|
245
|
+
| `useAssertVinyasaProvider(ref)` | Lower-level dev-mode check `useThemedRef` builds on — rarely used directly. |
|
|
246
|
+
| `themeContract` | The token contract — import as `vars` in a component's `.css.ts` file. |
|
|
247
|
+
| `focusRingStyle(vars)` | Returns the shared `:focus-visible` style object. |
|
|
248
|
+
| `lightTheme`, `darkTheme` | The normal-contrast, default-brand values object for each scheme. |
|
|
249
|
+
| `lightHighContrastTheme`, `darkHighContrastTheme` | The high-contrast, default-brand values object for each scheme. |
|
|
250
|
+
| `createTheme(overrides)` | Merges a partial theme onto `lightTheme`, returning a full `VinyasaTheme`. |
|
|
251
|
+
| `resolveBaseTheme(colorScheme, contrast)` | Looks up the scheme theme for a `colorScheme`/`contrast` pair, with a dev-mode fallback warning. |
|
|
252
|
+
| `resolveDensitySpacing(density)` | Looks up the `space1`–`space8` overlay for a density, with a dev-mode fallback warning. |
|
|
253
|
+
| `duskBrand`, `sunriseBrand`, `yellowBrand` | The brand accent objects, each `{ light: { normal, high }, dark: { normal, high } }`. |
|
|
254
|
+
| `breakpoints`, `grid` | Static layout values (not themeable — see above). From `layout.ts`. |
|
|
255
|
+
| `VinyasaTheme`, `ColorScheme`, `Contrast`, `Density`, `BrandName`, `Brand`, `BrandAccent`, `ThemeOverride`, `VinyasaProviderProps` | Types. |
|
|
124
256
|
|
|
125
257
|
## Development
|
|
126
258
|
|
|
@@ -130,4 +262,5 @@ From the repository root:
|
|
|
130
262
|
pnpm --filter @vinyasa/tokens build
|
|
131
263
|
pnpm --filter @vinyasa/tokens test
|
|
132
264
|
pnpm --filter @vinyasa/tokens lint
|
|
265
|
+
pnpm --filter @vinyasa/tokens typecheck
|
|
133
266
|
```
|