@swiftsolve/rove-tokens 0.0.0-stage → 1.1.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/DESIGN.md +138 -0
- package/README.md +127 -2
- package/package.json +48 -4
- package/src/code-tokens.json +19 -0
- package/src/figma/text-styles.json +289 -0
- package/src/figma/variables.json +2077 -0
- package/text-styles.css +130 -0
- package/tokens/code.tokens.json +145 -0
- package/tokens/color.dark.tokens.json +251 -0
- package/tokens/color.light.tokens.json +251 -0
- package/tokens/layout.tokens.json +67 -0
- package/tokens/motion.tokens.json +20 -0
- package/tokens/opacity.tokens.json +39 -0
- package/tokens/palette.tokens.json +935 -0
- package/tokens/spacing.tokens.json +122 -0
- package/tokens/text-styles.tokens.json +205 -0
- package/tokens/typography.tokens.json +96 -0
- package/tokens.css +207 -0
package/DESIGN.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Rove design
|
|
2
|
+
|
|
3
|
+
How to build Rove's interface with these tokens, for anyone writing Rove UI, people and coding agents alike. The [token reference](#token-reference) at the end lists every value; [`docs/rationale.md`](docs/rationale.md) explains why they are what they are.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```css
|
|
8
|
+
@import '@swiftsolve/rove-tokens/tokens.css';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Dark is the default, on `:root`. Add the `theme-light` class to the root element for Light. Every token has both values, so a screen that only uses tokens themes itself.
|
|
12
|
+
|
|
13
|
+
## Rules
|
|
14
|
+
|
|
15
|
+
- **Use a token, never a raw value.** No hex colours, no pixel gaps, no font sizes typed by hand. If nothing fits, the design needs a new token in Figma, not a one-off in code.
|
|
16
|
+
- **Name by job, not by look.** Pick `--bg-surface` because it's content on the backdrop, not because it's the right grey. The Palette shades (`neutral/850`) aren't CSS variables, on purpose.
|
|
17
|
+
|
|
18
|
+
### Surfaces
|
|
19
|
+
|
|
20
|
+
- Three backgrounds: `--bg-base` for the backdrop, `--bg-surface` for content that sits on it (cards, dialogs), `--bg-inset` for areas sunk into a surface (bands, inputs, soft and icon buttons). `--bg-chrome` is the title bar and rail, a half step off the backdrop. `--bg-popover` is a step lighter in Dark, for tooltips and menus. `--bg-hover` is a hovered surface, `--bg-scrim` goes behind dialogs.
|
|
21
|
+
- Cards get depth from `--shadow-panel`, not hard outlines. Tooltips and menus use `--shadow-popover` on `--bg-popover`; dialogs and hovered cards use `--shadow-raised`.
|
|
22
|
+
- Every line is `--border-default`: hairlines, and the edges of menus, tooltips and dialogs.
|
|
23
|
+
- For hovers, chips and tracks on a surface, use the `--fill-*` overlays, by strength: `--fill-faint` (3%), `--fill-subtle` (5%), `--fill-default` (7%), `--fill-strong` (16%). They flip with the theme, so they stay visible on both. A hovered row or expandable header uses `--fill-hover`: 3% white in Dark, 2% ink in Light, so in Light it stays lighter than an open `--bg-inset` panel below it.
|
|
24
|
+
|
|
25
|
+
### Text and icons
|
|
26
|
+
|
|
27
|
+
- `--text-primary` for content, `--text-secondary` for supporting lines, `--text-tertiary` for the quietest text and for icons by default.
|
|
28
|
+
- On a solid fill (accent or negative), labels and icons use `--text-on-fill`.
|
|
29
|
+
- Words in the accent (a link, a highlighted value) use `--text-accent`, on a surface or on `--accent-muted` (a selected item, a tinted button).
|
|
30
|
+
|
|
31
|
+
### Accent and buttons
|
|
32
|
+
|
|
33
|
+
- `--accent-default` is for accent icons and lines. It's too light for words in Light mode, and under a white label.
|
|
34
|
+
- Solid buttons use `--accent-fill`, hovering to `--accent-fill-hover`. Hovers deepen, never lighten, so the white label keeps its contrast.
|
|
35
|
+
- A solid red control (a solid negative button, the window close hover) uses `--status-negative-fill` and `--status-negative-fill-hover`.
|
|
36
|
+
|
|
37
|
+
### Status
|
|
38
|
+
|
|
39
|
+
- `--status-*` (positive, good, warning, poor, negative) colour icons, bars and dots.
|
|
40
|
+
- Words in a status colour use `--text-*` of the same name, which also clear a tint of their hue (a badge). The `--status-*` shades are too light for text in Light mode.
|
|
41
|
+
- Download and upload in charts are `--series-down` and `--series-up`.
|
|
42
|
+
|
|
43
|
+
### Tints and opacity
|
|
44
|
+
|
|
45
|
+
Tints are a colour mixed with transparent at one of the shared strengths, so a tinted tile reads the same everywhere:
|
|
46
|
+
|
|
47
|
+
```css
|
|
48
|
+
background: color-mix(in srgb, var(--status-positive) var(--opacity-tint-md), transparent);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Disabled controls use `--opacity-disabled`.
|
|
52
|
+
|
|
53
|
+
### Spacing and shape
|
|
54
|
+
|
|
55
|
+
- Every gap, padding and margin is a `--spacing-*` step.
|
|
56
|
+
- Cards use `--card-padding` on every side. The rail and the content column share the side inset `--spacing-12`.
|
|
57
|
+
- Corners are `--radius-*`. `--radius-md` is for nav items and the page-header chip, a step rounder than `--radius-sm`; `--radius-full` makes pills.
|
|
58
|
+
|
|
59
|
+
### Type
|
|
60
|
+
|
|
61
|
+
- Set text with a text style's class from `text-styles.css` (`.type-heading-large`), or all four of its `--type-<style>-*` variables, never a font size alone. Each style's use is in [Text styles](#text-styles).
|
|
62
|
+
- Numbers that are data (speeds, totals, chart labels) use the Number styles (`number-*`), set in Questrial; everything else is Inter.
|
|
63
|
+
|
|
64
|
+
### Focus and motion
|
|
65
|
+
|
|
66
|
+
- Keyboard focus is `outline: var(--focus-outline)` with `outline-offset: var(--focus-offset)`. Rows flush with a card's edge draw it inside instead, with `--focus-inset`.
|
|
67
|
+
- Animate with `--ease-out` (or `--ease-in-out`) over `--duration-fast` or `--duration-normal`. A looping pulse uses `--ease-pulse`, which is symmetric, so it fades out and back in alike.
|
|
68
|
+
|
|
69
|
+
### Icons
|
|
70
|
+
|
|
71
|
+
- Icons share a 24px grid, a 1.75px stroke and round caps.
|
|
72
|
+
- Icons are `--text-tertiary` by default. The active nav item's is `--accent-default`, an icon button's `--text-secondary`, a download icon `--series-down`, a secured or passing one `--status-positive`.
|
|
73
|
+
- The brand mark is always `--accent-default`, 22px in the title bar.
|
|
74
|
+
|
|
75
|
+
## Changing the design
|
|
76
|
+
|
|
77
|
+
Figma is the source of truth. Change a token in the Rove Design System Figma file, export it with the plugin in `figma-plugin/` into `src/figma/`, then run `npm run build` (see the [README](README.md#update-the-tokens)). Don't edit `tokens.css`, `tokens/` or the values in `src/figma/` by hand. What Figma can't hold (shadows, easing curves, the focus outline) lives in `src/code-tokens.json`.
|
|
78
|
+
|
|
79
|
+
Everything below the next line is rewritten by `npm run build`; edit only above it.
|
|
80
|
+
|
|
81
|
+
<!-- Generated by npm run build from the Figma export and tokens.css. Edit above this line. -->
|
|
82
|
+
|
|
83
|
+
## Token reference
|
|
84
|
+
|
|
85
|
+
Names and uses, from Figma. The values are in `tokens.css`; Dark and Light differ only in colour and shadow.
|
|
86
|
+
|
|
87
|
+
### Colour
|
|
88
|
+
|
|
89
|
+
- **Backgrounds:** `--bg-base` (backdrop), `--bg-surface` (content on the backdrop), `--bg-hover` (hovered rows and buttons), `--bg-scrim` (behind dialogs), `--bg-inset` (sunken areas inside a surface), `--bg-popover` (tooltips and menus), `--bg-chrome` (title bar and rail)
|
|
90
|
+
- **Lines:** `--border-default` (hairlines)
|
|
91
|
+
- **Fills and overlays:** `--fill-default` (overlay), `--fill-subtle` (light overlay), `--fill-faint` (faintest overlay), `--fill-strong` (strong overlay), `--fill-selection` (text selection), `--fill-hover` (hovered rows and headers)
|
|
92
|
+
- **Text:** `--text-primary`, `--text-secondary`, `--text-tertiary` (default icon colour), `--text-accent` (accent words, also on accent/muted), `--text-positive`, `--text-good`, `--text-warning`, `--text-poor`, `--text-negative`, `--text-on-fill` (labels and icons on solid fills)
|
|
93
|
+
- **Accent:** `--accent-default`, `--accent-hover`, `--accent-press`, `--accent-muted` (tinted accent background), `--accent-fill` (fill under white labels), `--accent-fill-hover`
|
|
94
|
+
- **Status:** `--status-positive`, `--status-good` (between positive and warning), `--status-warning`, `--status-poor` (between warning and negative), `--status-negative`, `--status-negative-fill` (solid negative under white labels), `--status-negative-fill-hover` (negative fill hover)
|
|
95
|
+
- **Chart series:** `--series-down` (download), `--series-up` (upload)
|
|
96
|
+
- **Focus:** `--focus-ring` (keyboard focus ring)
|
|
97
|
+
|
|
98
|
+
### Scales
|
|
99
|
+
|
|
100
|
+
- **Spacing:** `--spacing-<n>`, n in px: 2, 4, 6, 8, 10, 12, 14, 16, 20
|
|
101
|
+
- **Radius:** `--radius-xs` 5px, `--radius-sm` 8px, `--radius-md` 10px, `--radius-lg` 13px, `--radius-full` 999px
|
|
102
|
+
- **Font sizes:** `--font-size-2xs` 10px, `--font-size-xs` 11px, `--font-size-sm` 12px, `--font-size-md` 13px, `--font-size-lg` 14px, `--font-size-xl` 15px (dialog titles), `--font-size-2xl` 17px, `--font-size-3xl` 28px
|
|
103
|
+
- **Font families:** `--font-family-sans` Inter, `--font-family-numeric` Questrial
|
|
104
|
+
- **Focus:** `--focus-ring-width` 1px (focus ring stroke), `--focus-offset` 2px (focus ring offset)
|
|
105
|
+
- **Opacity:** `--opacity-tint-xs` 8% (faintest tint), `--opacity-tint-sm` 10% (light tint), `--opacity-tint-md` 14% (tint), `--opacity-tint-lg` 22% (strong tint), `--opacity-tint-xl` 28% (strongest tint), `--opacity-high` 80% (nearly opaque), `--opacity-disabled` 40% (disabled controls)
|
|
106
|
+
- **Layout:** `--rail-item-size` 38px (rail items), `--titlebar-height` 56px (title bar)
|
|
107
|
+
- **Cards:** `--card-padding` `var(--spacing-14)` (card inset), `--card-header-padding-y` `var(--card-padding)`, `--card-header-padding-x` `var(--card-padding)`, `--card-header-min-height` 42px (compact card header), `--card-header-height` 44px (card header), `--card-body-padding` `var(--card-padding)`, `--row-padding-y` 8px (data row, above and below)
|
|
108
|
+
- **Font weights:** `--weight-regular` 400, `--weight-medium` 500, `--weight-semibold` 600
|
|
109
|
+
- **Motion:** `--duration-fast` 140ms (hovers, small changes), `--duration-normal` 220ms (panels, page changes)
|
|
110
|
+
|
|
111
|
+
### Text styles
|
|
112
|
+
|
|
113
|
+
Apply a style with its class from `text-styles.css`. Each class reads four variables, `--type-<style>-size`, `-weight`, `-line-height` and `-letter-spacing`; set all four if you use them directly.
|
|
114
|
+
|
|
115
|
+
| Style | Class | Size, weight | Use |
|
|
116
|
+
| --- | --- | --- | --- |
|
|
117
|
+
| Heading/Large | `.type-heading-large` | 17px 600 | Page titles, the wordmark. Code: .type-heading-large |
|
|
118
|
+
| Heading/Medium | `.type-heading-medium` | 15px 600 | Dialog titles. Code: .type-heading-medium |
|
|
119
|
+
| Heading/Small | `.type-heading-small` | 14px 600 | Card headings. Code: .type-heading-small |
|
|
120
|
+
| Body/Default | `.type-body-default` | 13px 400 | Body text. Code: .type-body-default |
|
|
121
|
+
| Overline | `.type-overline` | 11px 500 | Uppercase labels above values. Code: .type-overline |
|
|
122
|
+
| Caption | `.type-caption` | 11px 400 | Badges, captions, footer hints. Code: .type-caption |
|
|
123
|
+
| Label/Large | `.type-label-large` | 12px 600 | Solid button labels. Code: Button variant=solid |
|
|
124
|
+
| Number/Display | `.type-number-display` | 28px 400, Questrial | Live speeds. Add a 0.43px outside stroke. Code: .type-number-display .metric-value, at weight 600: Questrial has one weight, so the browser emboldens it. |
|
|
125
|
+
| Number/Large | `.type-number-large` | 17px 400, Questrial | Stat totals. Add a 0.26px outside stroke. Code: Readout size=compact |
|
|
126
|
+
| Number/Default | `.type-number-default` | 13px 400, Questrial | Data row values. Code: .type-number-default |
|
|
127
|
+
| Number/Small | `.type-number-small` | 10px 400, Questrial | Chart axes and scale. Code: .type-number-small |
|
|
128
|
+
| Label/Default | `.type-label-default` | 11px 600 | Bold small labels on tinted chips. Code: .type-label-default |
|
|
129
|
+
| Label/Small | `.type-label-small` | 11px 500 | Soft buttons, title bar status. Code: .type-label-small |
|
|
130
|
+
| Body/Small | `.type-body-small` | 12px 400 | Helper text, hints and units. Code: .type-body-small |
|
|
131
|
+
|
|
132
|
+
### Code-only tokens
|
|
133
|
+
|
|
134
|
+
What Figma variables can't hold, from `src/code-tokens.json`.
|
|
135
|
+
|
|
136
|
+
- **Shadows:** `--shadow-panel`, `--shadow-raised`, `--shadow-popover`
|
|
137
|
+
- **Focus:** `--focus-outline`, `--focus-inset`
|
|
138
|
+
- **Motion:** `--ease-out`, `--ease-in-out`, `--ease-pulse`
|
package/README.md
CHANGED
|
@@ -1,3 +1,128 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<img src="docs/assets/rove-mark.svg" width="72" height="72" alt="Rove Mark">
|
|
4
|
+
|
|
5
|
+
# Rove Tokens
|
|
6
|
+
|
|
7
|
+
Tokens for Rove, straight from the Rove Design System in Figma.
|
|
8
|
+
|
|
9
|
+
`@swiftsolve/rove-tokens`
|
|
10
|
+
|
|
11
|
+
[](https://github.com/swiftsolve/rove-tokens/actions/workflows/build.yml)
|
|
12
|
+
[](https://github.com/swiftsolve/rove-tokens/actions/workflows/presentation.yml)
|
|
13
|
+
[](https://github.com/swiftsolve/rove-tokens/tags)
|
|
14
|
+
[](https://www.figma.com/design/NoICIoyhWNOya9tOnNfEhH/Rove-Design-System)
|
|
15
|
+
[](https://www.designtokens.org/tr/2025.10/format/)
|
|
16
|
+
[](https://styledictionary.com/)
|
|
17
|
+
[](#-contrast)
|
|
18
|
+
[](#-use)
|
|
19
|
+
[](.nvmrc)
|
|
20
|
+
|
|
21
|
+
[🎨 Figma file](https://www.figma.com/design/NoICIoyhWNOya9tOnNfEhH/Rove-Design-System) ·
|
|
22
|
+
[🖼️ Slideshow](https://swiftsolve.github.io/rove-tokens/) ·
|
|
23
|
+
[📐 DESIGN.md](DESIGN.md) ·
|
|
24
|
+
[💡 Rationale](docs/rationale.md)
|
|
25
|
+
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
Figma is the source of truth; this package turns its variables and text styles into DTCG token files, and those into CSS with [Style Dictionary](https://styledictionary.com/).
|
|
31
|
+
|
|
32
|
+
> [!TIP]
|
|
33
|
+
> **Building Rove UI?** [`DESIGN.md`](DESIGN.md) says which token to use for what, with every value. It ships in the package, so coding agents in Rove can read it from `node_modules/@swiftsolve/rove-tokens/DESIGN.md`.
|
|
34
|
+
|
|
35
|
+
## 📦 Use
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install @swiftsolve/rove-tokens
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The package ships `tokens.css`, `text-styles.css` and `tokens/` built. You can also install straight from a version tag (`github:swiftsolve/rove-tokens#v1.0.0`); then npm builds them on install, which needs Node 22 (see `.nvmrc`).
|
|
42
|
+
|
|
43
|
+
```css
|
|
44
|
+
@import '@swiftsolve/rove-tokens/tokens.css';
|
|
45
|
+
@import '@swiftsolve/rove-tokens/text-styles.css'; /* optional: a class per text style */
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Dark values sit on `:root`, Light values on `:root.theme-light`. CSS names follow the Figma names: `bg/base` is `--bg-base`, `spacing/12` is `--spacing-12`.
|
|
49
|
+
|
|
50
|
+
Each Figma text style becomes four tokens: `--type-<style>-size`, `-weight`, `-line-height` (a unitless ratio) and `-letter-spacing` (in `em`). `Number/Display` gives `--type-number-display-size` and so on. A size bound to a variable comes through as that variable (`var(--font-size-3xl)`).
|
|
51
|
+
|
|
52
|
+
`text-styles.css` applies a style whole: `.type-heading-large` sets the font family and all four, plus the text case (Overline is uppercase). The classes set no colour and sit in `@layer components`, so Tailwind utilities on the same element still win.
|
|
53
|
+
|
|
54
|
+
### 🧩 DTCG
|
|
55
|
+
|
|
56
|
+
`tokens/` holds the same tokens in the [Design Tokens Format](https://www.designtokens.org/tr/2025.10/format/) (2025.10), for Style Dictionary, Tokens Studio and similar tools:
|
|
57
|
+
|
|
58
|
+
| File | What |
|
|
59
|
+
| --- | --- |
|
|
60
|
+
| `palette.tokens.json` | Every colour, once |
|
|
61
|
+
| `color.dark.tokens.json`, `color.light.tokens.json` | The Color tokens in each theme, pointing at the palette (`{neutral.850}`) |
|
|
62
|
+
| `spacing.tokens.json`, `typography.tokens.json`, `opacity.tokens.json`, `layout.tokens.json`, `motion.tokens.json` | The other Figma collections |
|
|
63
|
+
| `text-styles.tokens.json` | Each text style as a `typography` token |
|
|
64
|
+
| `code.tokens.json` | The code-only tokens (shadows, easing curves, the focus outline) |
|
|
65
|
+
|
|
66
|
+
A theme is every file plus one of the two color files. Paths follow the Figma names (`bg/base` is `bg.base`). Tokens that other tokens point at but that aren't CSS variables (the palette, the shadow colours) are marked `"$extensions": { "net.swiftsolve.rove-tokens": { "css": false } }`. CSS-only expressions (`--focus-inset`, a `calc()`) aren't in them.
|
|
67
|
+
|
|
68
|
+
## ♿ Contrast
|
|
69
|
+
|
|
70
|
+
The colours meet [WCAG 2.2](https://www.w3.org/TR/WCAG22/) level AA in both themes, on the surfaces they sit on: text clears 4.5:1 ([1.4.3 Contrast (Minimum)](https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html)), and icons, bars and dots 3:1 ([1.4.11 Non-text Contrast](https://www.w3.org/WAI/WCAG22/Understanding/non-text-contrast.html)). AAA (7:1) isn't a target. [Rationale](docs/rationale.md) has the close calls.
|
|
71
|
+
|
|
72
|
+
## 📖 Storybook
|
|
73
|
+
|
|
74
|
+
**Rove Tokens** is a Storybook of every token in both themes: colour, palette, type, spacing, radius, effects, motion and layout.
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npm run storybook
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
It opens on <http://localhost:6007>; the Theme menu in the toolbar switches Dark and Light. The pages read the DTCG files in `tokens/` and are styled with `tokens.css`, so a new build shows up without editing them. `npm run build:storybook` writes a static copy to `storybook-static/`.
|
|
81
|
+
|
|
82
|
+
## 🔄 Update the tokens
|
|
83
|
+
|
|
84
|
+
1. Change the variables or text styles in Figma.
|
|
85
|
+
2. Export them to `src/figma/variables.json` and `src/figma/text-styles.json`, either with the plugin in `figma-plugin/` (Figma desktop: Plugins → Development → Import plugin from manifest; then **Export**) or by asking Claude to export them through the Figma connection.
|
|
86
|
+
3. Run `npm run build` (Node 22) and check `tokens.css`. It writes the DTCG files, then generates `tokens.css` from them with Style Dictionary and `text-styles.css` from the text styles, then rewrites the token reference at the end of `DESIGN.md`. The build stops if a variable's code syntax doesn't match its name, or a DTCG reference doesn't resolve.
|
|
87
|
+
4. Commit the change.
|
|
88
|
+
|
|
89
|
+
## 🚀 Release
|
|
90
|
+
|
|
91
|
+
1. On the Figma cover (`COVER` page), set **Version** to the one you're about to release and **Updated** to today's date (`27 Oct 2026`). They're the one place a release can't be stamped from here: Figma's API can't edit canvas text on this plan, so do it by hand or ask Claude to through the Figma connection.
|
|
92
|
+
2. Bump, commit and tag in one step, then push both:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
npm version minor
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
git push --follow-tags
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Use `patch` for fixes and `major` for breaking changes.
|
|
103
|
+
3. The tag runs three workflows: **Build** checks the tokens, **Publish** puts the version on [npm](https://www.npmjs.com/package/@swiftsolve/rove-tokens), and **Presentation** exports the Figma file and republishes <https://swiftsolve.github.io/rove-tokens/>. The page's header shows the version from `package.json`. To republish the slideshow between releases (after Figma edits), run Presentation by hand from the Actions tab.
|
|
104
|
+
4. Update Rove to the new version (`npm install @swiftsolve/rove-tokens@latest`).
|
|
105
|
+
|
|
106
|
+
## 🗂️ What's where
|
|
107
|
+
|
|
108
|
+
| Path | What |
|
|
109
|
+
| --- | --- |
|
|
110
|
+
| `src/figma/variables.json` | Raw Figma export. Every variable with a Web code syntax becomes that CSS variable. |
|
|
111
|
+
| `src/figma/text-styles.json` | Raw export of the local text styles: font, size, weight, line height, letter spacing, text case (when set) and the variables bound to them. Each style becomes a `--type-*` group. |
|
|
112
|
+
| `figma-plugin/` | The Figma plugin that writes the two exports. |
|
|
113
|
+
| `src/code-tokens.json` | What Figma variables can't hold: font stacks for the font-family variables, shadows, easing curves and the CSS expressions built on variables (`--focus-outline`). `{figma/name}` pulls a Figma value in per theme. |
|
|
114
|
+
| `DESIGN.md` | How to build Rove UI with the tokens. The rules at the top are written by hand; the token reference below the marker line is generated by the build. |
|
|
115
|
+
| `scripts/tokens/build.mjs` | `npm run build`: runs the steps below and writes `tokens.css` and `text-styles.css` at the package root, where the import paths point. |
|
|
116
|
+
| `scripts/tokens/dtcg.mjs` | Figma export → the DTCG files in `tokens/`, checking that every reference resolves in both themes. |
|
|
117
|
+
| `scripts/tokens/css.mjs` | DTCG files → `tokens.css`, with Style Dictionary and a `rove/css` format: Dark on `:root`, what differs in Light on `:root.theme-light`, each text style as four `--type-*` variables (letter spacing included). |
|
|
118
|
+
| `scripts/tokens/text-styles.mjs` | DTCG text styles → `text-styles.css`: a `.type-<style>` class per style that reads its `--type-*` variables. |
|
|
119
|
+
| `scripts/tokens/design.mjs` | Figma export and `tokens.css` → the token reference in `DESIGN.md`: every token by name and Figma description, with values for the scales (spacing, radius, type, opacity) but not the colours, so nobody copies a hex. |
|
|
120
|
+
| `scripts/presentation/export.mjs` | `npm run present`: exports the Figma file's pages to `presentation/index.html`, a slideshow styled with the tokens. Needs `FIGMA_TOKEN` (a Figma personal access token, `file_content:read`) in `.env.local`. |
|
|
121
|
+
| `scripts/presentation/open.mjs` | `npm run present:open`: opens the exported presentation in your browser. |
|
|
122
|
+
| `.storybook/` | The Rove Tokens Storybook's config, and the Inter and Questrial files it serves. |
|
|
123
|
+
| `stories/` | Its pages, one file per sidebar group. `tokens.js` reads the DTCG files. |
|
|
124
|
+
| `scripts/presentation/publish.mjs` | `npm run present:publish`: pushes the exported presentation to the `gh-pages` branch, which GitHub Pages serves at <https://swiftsolve.github.io/rove-tokens/>. Each publish replaces the last. |
|
|
125
|
+
| `.github/workflows/presentation.yml` | Runs the export and publish on every `v*` tag, or by hand. Needs the `FIGMA_TOKEN` repository secret. |
|
|
126
|
+
| `docs/rationale.md` | Why the values are what they are (contrast targets, theme choices). |
|
|
127
|
+
|
|
128
|
+
Every colour lives once in the Palette collection (`neutral/850`, `blue/450`; higher numbers are darker; a see-through shade adds `-a` and its opacity, `neutral/850-a10`), and the Color tokens point at it. The Palette isn't in `tokens.css`: a token that points at a shade gets the shade's value there. Figma-only collections (Icon tone, Capability level) and variables without a code syntax aren't in `tokens.css` either.
|
package/package.json
CHANGED
|
@@ -1,6 +1,50 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swiftsolve/rove-tokens",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Rove's design tokens, exported from the Rove Design System in Figma, as CSS and DTCG.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/swiftsolve/rove-tokens.git"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
"./tokens.css": "./tokens.css",
|
|
13
|
+
"./text-styles.css": "./text-styles.css",
|
|
14
|
+
"./tokens/*": "./tokens/*",
|
|
15
|
+
"./package.json": "./package.json"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"tokens.css",
|
|
19
|
+
"text-styles.css",
|
|
20
|
+
"tokens",
|
|
21
|
+
"src",
|
|
22
|
+
"DESIGN.md"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "node scripts/tokens/build.mjs",
|
|
26
|
+
"present": "npm run build && node scripts/presentation/export.mjs",
|
|
27
|
+
"present:open": "node scripts/presentation/open.mjs",
|
|
28
|
+
"present:publish": "node scripts/presentation/publish.mjs",
|
|
29
|
+
"prepare": "npm run build",
|
|
30
|
+
"storybook": "storybook dev -p 6007",
|
|
31
|
+
"build:storybook": "storybook build"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"registry": "https://registry.npmjs.org",
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=22"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@storybook/addon-docs": "^10.6.5",
|
|
42
|
+
"@storybook/react-vite": "^10.6.5",
|
|
43
|
+
"@vitejs/plugin-react": "^5.2.0",
|
|
44
|
+
"react": "^18.3.1",
|
|
45
|
+
"react-dom": "^18.3.1",
|
|
46
|
+
"storybook": "^10.6.5",
|
|
47
|
+
"style-dictionary": "^5.7.1",
|
|
48
|
+
"vite": "^7.3.7"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "What Figma variables can't hold. `figma` sets how a Figma variable is written in CSS; `tokens` adds code-only tokens. `{figma/name}` pulls a Figma variable's raw value in for each theme.",
|
|
3
|
+
"figma": {
|
|
4
|
+
"font-family/sans": "'{font-family/sans}', system-ui, -apple-system, 'Segoe UI', sans-serif",
|
|
5
|
+
"font-family/numeric": "'{font-family/numeric}', ui-sans-serif, system-ui, sans-serif"
|
|
6
|
+
},
|
|
7
|
+
"tokens": {
|
|
8
|
+
"--shadow-panel": "0 1px 2px {shadow-color/panel-1}, 0 4px 12px {shadow-color/panel-2}",
|
|
9
|
+
"--shadow-raised": "0 12px 32px {shadow-color/raised}",
|
|
10
|
+
"--shadow-popover": "0 2px 6px {shadow-color/popover-1}, 0 6px 16px {shadow-color/popover-2}",
|
|
11
|
+
|
|
12
|
+
"--focus-outline": "var(--focus-ring-width) solid var(--focus-ring)",
|
|
13
|
+
"--focus-inset": "calc(-1 * var(--focus-ring-width))",
|
|
14
|
+
|
|
15
|
+
"--ease-out": "cubic-bezier(0.16, 1, 0.3, 1)",
|
|
16
|
+
"--ease-in-out": "cubic-bezier(0.4, 0, 0.2, 1)",
|
|
17
|
+
"--ease-pulse": "cubic-bezier(0.42, 0, 0.58, 1)"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
{
|
|
2
|
+
"file": {
|
|
3
|
+
"name": "Rove Design System",
|
|
4
|
+
"key": "NoICIoyhWNOya9tOnNfEhH"
|
|
5
|
+
},
|
|
6
|
+
"styles": [
|
|
7
|
+
{
|
|
8
|
+
"name": "Heading/Large",
|
|
9
|
+
"description": "Page titles, the wordmark. Code: .type-heading-large",
|
|
10
|
+
"fontFamily": "Inter",
|
|
11
|
+
"fontStyle": "Semi Bold",
|
|
12
|
+
"fontWeight": 600,
|
|
13
|
+
"fontSize": 17,
|
|
14
|
+
"lineHeight": {
|
|
15
|
+
"unit": "PERCENT",
|
|
16
|
+
"value": 125
|
|
17
|
+
},
|
|
18
|
+
"letterSpacing": {
|
|
19
|
+
"unit": "PERCENT",
|
|
20
|
+
"value": 0
|
|
21
|
+
},
|
|
22
|
+
"boundVariables": {
|
|
23
|
+
"fontFamily": "font-family/sans",
|
|
24
|
+
"fontSize": "font-size/2xl"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"name": "Heading/Medium",
|
|
29
|
+
"description": "Dialog titles. Code: .type-heading-medium",
|
|
30
|
+
"fontFamily": "Inter",
|
|
31
|
+
"fontStyle": "Semi Bold",
|
|
32
|
+
"fontWeight": 600,
|
|
33
|
+
"fontSize": 15,
|
|
34
|
+
"lineHeight": {
|
|
35
|
+
"unit": "PERCENT",
|
|
36
|
+
"value": 145
|
|
37
|
+
},
|
|
38
|
+
"letterSpacing": {
|
|
39
|
+
"unit": "PERCENT",
|
|
40
|
+
"value": 0
|
|
41
|
+
},
|
|
42
|
+
"boundVariables": {
|
|
43
|
+
"fontFamily": "font-family/sans",
|
|
44
|
+
"fontSize": "font-size/xl"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"name": "Heading/Small",
|
|
49
|
+
"description": "Card headings. Code: .type-heading-small",
|
|
50
|
+
"fontFamily": "Inter",
|
|
51
|
+
"fontStyle": "Semi Bold",
|
|
52
|
+
"fontWeight": 600,
|
|
53
|
+
"fontSize": 14,
|
|
54
|
+
"lineHeight": {
|
|
55
|
+
"unit": "PERCENT",
|
|
56
|
+
"value": 120
|
|
57
|
+
},
|
|
58
|
+
"letterSpacing": {
|
|
59
|
+
"unit": "PERCENT",
|
|
60
|
+
"value": 0
|
|
61
|
+
},
|
|
62
|
+
"boundVariables": {
|
|
63
|
+
"fontFamily": "font-family/sans",
|
|
64
|
+
"fontSize": "font-size/lg"
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"name": "Body/Default",
|
|
69
|
+
"description": "Body text. Code: .type-body-default",
|
|
70
|
+
"fontFamily": "Inter",
|
|
71
|
+
"fontStyle": "Regular",
|
|
72
|
+
"fontWeight": 400,
|
|
73
|
+
"fontSize": 13,
|
|
74
|
+
"lineHeight": {
|
|
75
|
+
"unit": "PERCENT",
|
|
76
|
+
"value": 145
|
|
77
|
+
},
|
|
78
|
+
"letterSpacing": {
|
|
79
|
+
"unit": "PERCENT",
|
|
80
|
+
"value": 0
|
|
81
|
+
},
|
|
82
|
+
"boundVariables": {
|
|
83
|
+
"fontFamily": "font-family/sans",
|
|
84
|
+
"fontSize": "font-size/md"
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"name": "Overline",
|
|
89
|
+
"description": "Uppercase labels above values. Code: .type-overline",
|
|
90
|
+
"fontFamily": "Inter",
|
|
91
|
+
"fontStyle": "Medium",
|
|
92
|
+
"fontWeight": 500,
|
|
93
|
+
"fontSize": 11,
|
|
94
|
+
"lineHeight": {
|
|
95
|
+
"unit": "PERCENT",
|
|
96
|
+
"value": 120
|
|
97
|
+
},
|
|
98
|
+
"letterSpacing": {
|
|
99
|
+
"unit": "PERCENT",
|
|
100
|
+
"value": 6
|
|
101
|
+
},
|
|
102
|
+
"textCase": "UPPER",
|
|
103
|
+
"boundVariables": {
|
|
104
|
+
"fontFamily": "font-family/sans",
|
|
105
|
+
"fontSize": "font-size/xs"
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"name": "Caption",
|
|
110
|
+
"description": "Badges, captions, footer hints. Code: .type-caption",
|
|
111
|
+
"fontFamily": "Inter",
|
|
112
|
+
"fontStyle": "Regular",
|
|
113
|
+
"fontWeight": 400,
|
|
114
|
+
"fontSize": 11,
|
|
115
|
+
"lineHeight": {
|
|
116
|
+
"unit": "PERCENT",
|
|
117
|
+
"value": 145
|
|
118
|
+
},
|
|
119
|
+
"letterSpacing": {
|
|
120
|
+
"unit": "PERCENT",
|
|
121
|
+
"value": 0
|
|
122
|
+
},
|
|
123
|
+
"boundVariables": {
|
|
124
|
+
"fontFamily": "font-family/sans",
|
|
125
|
+
"fontSize": "font-size/xs"
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"name": "Label/Large",
|
|
130
|
+
"description": "Solid button labels. Code: Button variant=solid",
|
|
131
|
+
"fontFamily": "Inter",
|
|
132
|
+
"fontStyle": "Semi Bold",
|
|
133
|
+
"fontWeight": 600,
|
|
134
|
+
"fontSize": 12,
|
|
135
|
+
"lineHeight": {
|
|
136
|
+
"unit": "PERCENT",
|
|
137
|
+
"value": 145
|
|
138
|
+
},
|
|
139
|
+
"letterSpacing": {
|
|
140
|
+
"unit": "PERCENT",
|
|
141
|
+
"value": 0
|
|
142
|
+
},
|
|
143
|
+
"boundVariables": {
|
|
144
|
+
"fontFamily": "font-family/sans",
|
|
145
|
+
"fontSize": "font-size/sm"
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"name": "Number/Display",
|
|
150
|
+
"description": "Live speeds. Add a 0.43px outside stroke. Code: .type-number-display .metric-value, at weight 600: Questrial has one weight, so the browser emboldens it.",
|
|
151
|
+
"fontFamily": "Questrial",
|
|
152
|
+
"fontStyle": "Regular",
|
|
153
|
+
"fontWeight": 400,
|
|
154
|
+
"fontSize": 28,
|
|
155
|
+
"lineHeight": {
|
|
156
|
+
"unit": "PERCENT",
|
|
157
|
+
"value": 100
|
|
158
|
+
},
|
|
159
|
+
"letterSpacing": {
|
|
160
|
+
"unit": "PERCENT",
|
|
161
|
+
"value": 0
|
|
162
|
+
},
|
|
163
|
+
"boundVariables": {
|
|
164
|
+
"fontFamily": "font-family/numeric",
|
|
165
|
+
"fontSize": "font-size/3xl"
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"name": "Number/Large",
|
|
170
|
+
"description": "Stat totals. Add a 0.26px outside stroke. Code: Readout size=compact",
|
|
171
|
+
"fontFamily": "Questrial",
|
|
172
|
+
"fontStyle": "Regular",
|
|
173
|
+
"fontWeight": 400,
|
|
174
|
+
"fontSize": 17,
|
|
175
|
+
"lineHeight": {
|
|
176
|
+
"unit": "PERCENT",
|
|
177
|
+
"value": 100
|
|
178
|
+
},
|
|
179
|
+
"letterSpacing": {
|
|
180
|
+
"unit": "PERCENT",
|
|
181
|
+
"value": 0
|
|
182
|
+
},
|
|
183
|
+
"boundVariables": {
|
|
184
|
+
"fontFamily": "font-family/numeric",
|
|
185
|
+
"fontSize": "font-size/2xl"
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"name": "Number/Default",
|
|
190
|
+
"description": "Data row values. Code: .type-number-default",
|
|
191
|
+
"fontFamily": "Questrial",
|
|
192
|
+
"fontStyle": "Regular",
|
|
193
|
+
"fontWeight": 400,
|
|
194
|
+
"fontSize": 13,
|
|
195
|
+
"lineHeight": {
|
|
196
|
+
"unit": "PERCENT",
|
|
197
|
+
"value": 135
|
|
198
|
+
},
|
|
199
|
+
"letterSpacing": {
|
|
200
|
+
"unit": "PERCENT",
|
|
201
|
+
"value": 0
|
|
202
|
+
},
|
|
203
|
+
"boundVariables": {
|
|
204
|
+
"fontFamily": "font-family/numeric",
|
|
205
|
+
"fontSize": "font-size/md"
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"name": "Number/Small",
|
|
210
|
+
"description": "Chart axes and scale. Code: .type-number-small",
|
|
211
|
+
"fontFamily": "Questrial",
|
|
212
|
+
"fontStyle": "Regular",
|
|
213
|
+
"fontWeight": 400,
|
|
214
|
+
"fontSize": 10,
|
|
215
|
+
"lineHeight": {
|
|
216
|
+
"unit": "PERCENT",
|
|
217
|
+
"value": 100
|
|
218
|
+
},
|
|
219
|
+
"letterSpacing": {
|
|
220
|
+
"unit": "PERCENT",
|
|
221
|
+
"value": 0
|
|
222
|
+
},
|
|
223
|
+
"boundVariables": {
|
|
224
|
+
"fontFamily": "font-family/numeric",
|
|
225
|
+
"fontSize": "font-size/2xs"
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"name": "Label/Default",
|
|
230
|
+
"description": "Bold small labels on tinted chips. Code: .type-label-default",
|
|
231
|
+
"fontFamily": "Inter",
|
|
232
|
+
"fontStyle": "Semi Bold",
|
|
233
|
+
"fontWeight": 600,
|
|
234
|
+
"fontSize": 11,
|
|
235
|
+
"lineHeight": {
|
|
236
|
+
"unit": "PERCENT",
|
|
237
|
+
"value": 145
|
|
238
|
+
},
|
|
239
|
+
"letterSpacing": {
|
|
240
|
+
"unit": "PERCENT",
|
|
241
|
+
"value": 0
|
|
242
|
+
},
|
|
243
|
+
"boundVariables": {
|
|
244
|
+
"fontFamily": "font-family/sans",
|
|
245
|
+
"fontSize": "font-size/xs"
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
"name": "Label/Small",
|
|
250
|
+
"description": "Soft buttons, title bar status. Code: .type-label-small",
|
|
251
|
+
"fontFamily": "Inter",
|
|
252
|
+
"fontStyle": "Medium",
|
|
253
|
+
"fontWeight": 500,
|
|
254
|
+
"fontSize": 11,
|
|
255
|
+
"lineHeight": {
|
|
256
|
+
"unit": "PERCENT",
|
|
257
|
+
"value": 145
|
|
258
|
+
},
|
|
259
|
+
"letterSpacing": {
|
|
260
|
+
"unit": "PERCENT",
|
|
261
|
+
"value": 0
|
|
262
|
+
},
|
|
263
|
+
"boundVariables": {
|
|
264
|
+
"fontFamily": "font-family/sans",
|
|
265
|
+
"fontSize": "font-size/xs"
|
|
266
|
+
}
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
"name": "Body/Small",
|
|
270
|
+
"description": "Helper text, hints and units. Code: .type-body-small",
|
|
271
|
+
"fontFamily": "Inter",
|
|
272
|
+
"fontStyle": "Regular",
|
|
273
|
+
"fontWeight": 400,
|
|
274
|
+
"fontSize": 12,
|
|
275
|
+
"lineHeight": {
|
|
276
|
+
"unit": "PERCENT",
|
|
277
|
+
"value": 145
|
|
278
|
+
},
|
|
279
|
+
"letterSpacing": {
|
|
280
|
+
"unit": "PERCENT",
|
|
281
|
+
"value": 0
|
|
282
|
+
},
|
|
283
|
+
"boundVariables": {
|
|
284
|
+
"fontFamily": "font-family/sans",
|
|
285
|
+
"fontSize": "font-size/sm"
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
]
|
|
289
|
+
}
|