@crewlethq/tokens 0.2.0 → 0.3.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/README.md CHANGED
@@ -1,8 +1,10 @@
1
1
  # @crewlethq/tokens
2
2
 
3
- Single source of truth for the Crewlet design tokens: color, spacing, typography (`font`), radius, shadow, blur, breakpoint, motion and z-index. Authored as JSON, compiled by Style Dictionary into:
3
+ Single source of truth for the Crewlet design tokens: color, spacing, size, typography (`font`), radius, shadow, blur, breakpoint, density, motion and z-index. Authored as JSON, compiled by Style Dictionary into:
4
4
 
5
5
  - `dist/css/tokens.css` (CSS custom properties on `:root`)
6
+ - `dist/css/themes.css` (the light and dark palettes)
7
+ - `dist/css/density.css`, `dist/css/base.css`, `dist/css/breakpoint.css`
6
8
  - `dist/index.js` + `dist/index.d.ts` (typed JS object for runtime use)
7
9
 
8
10
  ## Use it from a consumer app
@@ -11,32 +13,160 @@ Single source of truth for the Crewlet design tokens: color, spacing, typography
11
13
  // 1) Canonical variables. Always import this once at the app entrypoint.
12
14
  import '@crewlethq/tokens/css';
13
15
 
14
- // 2) Optional: self-hosted Inter and JetBrains Mono (see "Fonts" below).
16
+ // 2) The light and dark palettes, as a three-state contract on <html>.
17
+ // Import it after the line above; see "Theming" below.
18
+ import '@crewlethq/tokens/css/themes';
19
+
20
+ // 3) Optional: the density contract. Import it if the app has a density
21
+ // control; see "Density" below.
22
+ import '@crewlethq/tokens/css/density';
23
+
24
+ // 4) Optional: self-hosted Inter and JetBrains Mono (see "Fonts" below).
15
25
  // Skip this if the app already loads its own fonts at the shell.
16
26
  import '@crewlethq/tokens/css/fonts';
17
27
 
18
- // 3) Required when the app renders @crewlethq/ui components and does not load
19
- // the Material Symbols Outlined icon font itself. This is the only
20
- // stylesheet in the package that contacts a third-party host (see
21
- // "Material Symbols" below).
22
- import '@crewlethq/tokens/css/material-symbols';
23
-
24
- // 4) Optional: legacy aliases. Map short, unprefixed names (--accent,
25
- // --bg-primary, --text-primary, --accent-pink, etc.) onto the canonical
26
- // tokens. Use this while migrating an existing stylesheet that already
27
- // uses those names, so it keeps working without a sweeping
28
- // find-and-replace.
29
- import '@crewlethq/tokens/css/legacy';
28
+ // 5) Optional: the document baseline. The reset, the document's own type and
29
+ // colour, one focus ring, themed scrollbars and the reduced-motion
30
+ // collapse; see "The document baseline" below.
31
+ import '@crewlethq/tokens/css/base';
30
32
 
31
33
  // Or read tokens at runtime / build theme objects
32
34
  import { color, spacing } from '@crewlethq/tokens';
33
35
 
34
- const accent = color.brand.primary;
36
+ const accent = color.brand.accent;
35
37
  ```
36
38
 
37
39
  CSS variables follow Style Dictionary's `--{group}-{path}` convention,
38
40
  for example `var(--color-brand-primary)` or `var(--spacing-4)`.
39
41
 
42
+ ## Theming
43
+
44
+ `@crewlethq/tokens/css/themes` carries the light and dark palettes as three states on the root element:
45
+
46
+ ```css
47
+ :root { /* light */ }
48
+ @media (prefers-color-scheme: dark) {
49
+ :root:not([data-theme="light"]) { /* dark */ }
50
+ }
51
+ :root[data-theme="dark"] { /* dark */ }
52
+ ```
53
+
54
+ - A document that sets nothing is light, or dark if the reader's system asks for dark.
55
+ - `document.documentElement.dataset.theme = 'dark'` or `'light'` pins one, and wins in both directions, which a media query alone cannot do.
56
+ - Removing the attribute follows the system again.
57
+ - Both dark blocks are generated from one source, `tokens/themes/dark.json`, and the palette suite compares them.
58
+
59
+ Import it **after** `@crewlethq/tokens/css`. The two files share the `:root` selector and neither adds specificity, so the later import is the one that paints. `tokens.css` on its own is the base marketing palette, which is dark, and which carries a value for every themed slot so an application that imports only the token layer is never missing one.
60
+
61
+ `color-scheme` is set by the theme layer, so form controls, scrollbars and the canvas behind the page follow the palette.
62
+
63
+ ### Breaking change in 0.3.0
64
+
65
+ `themes.css` used to repaint the palette under `body.theme-light` and `body.theme-dark`. Those selectors are gone. An application that switched themes by toggling a class on `<body>` sets the attribute on `<html>` instead:
66
+
67
+ ```diff
68
+ - document.body.classList.add('theme-dark');
69
+ + document.documentElement.setAttribute('data-theme', 'dark');
70
+ ```
71
+
72
+ Three things follow from the move:
73
+
74
+ - The theme now repaints on the same element the tokens are declared on, so `@crewlethq/tokens/css/legacy` declares its aliases on `:root` alone. It used to repeat every alias on the theme classes, because an alias holding `var()` resolves on the element that declares it and a theme applied lower down could not reach it.
75
+ - A document that sets nothing is light rather than dark. The dark palette is still what a reader whose system asks for dark gets.
76
+ - `--color-surface-elevated` in the light palette moved from `#e3e3e6` to `#ebebee`. It was darker than the page, which made a dialog body the tightest ground in the palette.
77
+
78
+ Other changes a consumer may notice, none of which needs an edit:
79
+
80
+ - Every font size is emitted in `rem` rather than `px`, so a reader's own browser setting moves the page. The typed export still carries pixels.
81
+ - Every spacing and size token is emitted as `calc(Npx * var(--density, 1))`. With no density stylesheet imported the `var()` falls back to `1` and every value is exactly what it was.
82
+ - The status hues moved to clear the contrast floors and to stay separable under protan and deuteranopic vision. The success family is a teal rather than a green.
83
+ - `--shadow-focus` is a crisp two-ring shadow rather than a translucent glow, and every shadow step now has a light value as well as a dark one.
84
+
85
+ ### The palettes in JavaScript
86
+
87
+ `themes.light` and `themes.dark` carry the same two palettes as typed objects, for the code that cannot read a custom property: the tool chrome around a preview, a chart library that wants a series colour as a string, a canvas painting its own pixels.
88
+
89
+ ```ts
90
+ import { themes } from '@crewlethq/tokens';
91
+
92
+ themes.light.color.text.secondary; // '#52525b'
93
+ themes.dark.color.data['4']; // '#86efac'
94
+ ```
95
+
96
+ Prefer the custom property wherever CSS can reach: a value read here is taken at build time, so it does not follow a theme the reader changes. The package's own suite compares every exported value against the declaration in `themes.css`, in both directions, because two spellings of one palette is the arrangement that drifts.
97
+
98
+ ## Density
99
+
100
+ `@crewlethq/tokens/css/density` sets `--density` from an attribute on the root element:
101
+
102
+ ```css
103
+ :root { --density: 1 }
104
+ :root[data-density="compact"] { --density: 0.82 }
105
+ :root[data-density="comfortable"] { --density: 1.14 }
106
+ ```
107
+
108
+ Every spacing and size token is emitted as `calc(Npx * var(--density, 1))`, so a density setting is a real change to every gap, pad, row and control rather than to three font sizes, and every value is unchanged at density 1. The stylesheet is a separate import because a product with no density control should not ship the selectors that switch one; the tokens themselves work without it.
109
+
110
+ `--size-control-sm` and `--size-row-sm` are `max(24px, calc(28px * var(--density, 1)))`. The floor is deliberate: 28 x 0.82 is 22.96px, and a target under 24px is one a finger cannot reliably hit. A test resolves both at all three densities.
111
+
112
+ `--size-target-min` (24px) is that same floor, named, for a component that DERIVES a height from a control step rather than taking one. Subtracting from a step subtracts from its floor too, so `calc(var(--size-control-sm) - 4px)` is 20px at compact density; `max(var(--size-target-min), calc(var(--size-control-sm) - 4px))` is the same value at density 1 and still a reachable target at every other.
113
+
114
+ `--radius-chip` is `calc(var(--radius-md) - 1px)`, the corner of a chip lifted inside a well drawn at `--radius-md`. It is derived rather than written as a step of its own, so it follows the well.
115
+
116
+ ## The document baseline
117
+
118
+ `@crewlethq/tokens/css/base` is the opt-in document baseline: the box-sizing reset, the document's own type and colour, headings, controls that inherit the font, `code` and `kbd` in mono with tabular figures, one `:focus-visible` ring, `::selection`, themed scrollbars and the global reduced-motion collapse. Every value comes from a token, so it follows the palette and the density without a rule of its own.
119
+
120
+ The focus indicator is an **outline**, never a box-shadow alone. Forced-colors mode drops every box-shadow and keeps outlines, so a ring drawn as a shadow disappears for exactly the readers who most need one. `--shadow-focus` is still there for a component that wants a shadow as well, paired with a transparent outline so forced colors substitutes a system colour for it. `--size-focus-ring-inset-offset` is the offset for a focusable row inside a clipping scroller, where an outset ring is clipped by the scroller and painted over by the next row.
121
+
122
+ The body's height and overflow are deliberately not here. A page that can scroll as a whole moves an application's rail off the top of the window, so the rule that stops it belongs to the shell that owns the rail.
123
+
124
+ ## Breakpoints
125
+
126
+ A media query cannot read a custom property, so a stylesheet that switches layout at a breakpoint spells the number. `@crewlethq/tokens/css/breakpoint` is the generated partial that says which number, and `breakpoint` in the typed export carries the same values, so a check can compare a query's literal against them instead of trusting a comment.
127
+
128
+ `--breakpoint-shell` (900px) is where an application shell stops being a rail beside a pane: a 280px rail plus a 620px minimum content column.
129
+
130
+ ## What colour means
131
+
132
+ Four families, and the meanings never move:
133
+
134
+ | Use | Meaning | Tokens |
135
+ | --- | --- | --- |
136
+ | Status | `success` a good terminal state, `warning` a person is needed, `danger` failure, `info` neutral information | `--color-feedback-{success,warning,danger,info}` and their `-ink`, `-soft` and `-line` steps |
137
+ | Phase | onboarding, execute, review | `--color-phase-{onboarding,execute,review}` and their `-ink` and `-soft` steps |
138
+ | Accent | where the reader is: the active nav indicator, the focus ring, the selected row, the filter that is on | `--color-brand-accent`, `-hover`, `-active`, `-ink`, `-soft`, `-soft-strong` |
139
+ | Data | a chart series, and only inside a chart that carries a legend | `--color-data-1` to `-5`, `--color-data-other` |
140
+
141
+ Two rules travel with them:
142
+
143
+ - **A fill step is never text, and an `-ink` step is never a background.** A fill clears 3:1 as a mark; an ink clears 4.5:1 as text, including on its own soft tint.
144
+ - **Everything else is neutral.** A category with no state in it takes neutral colour, and its identity is carried by its name, its glyph and its position.
145
+
146
+ A `-soft` step is its own fill at alpha 0.12 and a `-line` step is the same fill at alpha 0.30. They are derived by the build from the fill, per palette, so a tint cannot come to belong to a hue the fill no longer is.
147
+
148
+ ## The palette suite
149
+
150
+ `test/palette.mjs` holds the rule table and the colour maths, and `test/palette.test.mjs` runs it over `dist/css` in import order, in every theme state: the base marketing root, light, dark by media query and dark by attribute. It measures every text step on every surface it can land on (the translucent overlays composited over each opaque ground included), every ink on its own soft tint, every fill as a mark, the focus ring, the control boundary, the hue separations under normal, protan and deuteranopic vision, and the structure of the theme file itself.
151
+
152
+ The module is **published**, as `@crewlethq/tokens/test/palette`, so a consumer runs the same rules over the version it installed:
153
+
154
+ ```ts
155
+ import { readFileSync } from 'node:fs';
156
+ import { runPalette, describeFailure } from '@crewlethq/tokens/test/palette';
157
+
158
+ const dir = 'node_modules/@crewlethq/tokens/dist/css';
159
+ const { failures } = runPalette({
160
+ tokens: readFileSync(`${dir}/tokens.css`, 'utf8'),
161
+ themes: readFileSync(`${dir}/themes.css`, 'utf8'),
162
+ });
163
+ expect(failures.map(describeFailure)).toEqual([]);
164
+ ```
165
+
166
+ One implementation, two gates. The constraint is that the floors are kept, not only that the measurement moves, and a tokens bump that lowered a ratio would otherwise reach a consumer through an auto-merged dependency update with nothing measuring a ratio again.
167
+
168
+ The tarball ships `test/color.mjs`, `test/palette.mjs` and `test/palette.test.mjs`, so `node --test "node_modules/@crewlethq/tokens/test/*.test.mjs"` runs the whole table over the installed version with no configuration at all. `test/build.test.mjs` stays out of it: it reads the token source the build compiles from, which a tarball does not carry.
169
+
40
170
  ## Fonts
41
171
 
42
172
  The design system uses two type families, both self-hosted inside this package and licensed under the SIL Open Font License 1.1:
@@ -55,20 +185,23 @@ Versions, subsets and the replacement procedure are in [`fonts/README.md`](./fon
55
185
 
56
186
  ## Material Symbols
57
187
 
58
- The `@crewlethq/ui` components render their glyphs (chevrons, close, copy, search and similar) as ligatures of the Material Symbols Outlined icon font, inside `<span class="material-symbols-outlined">`. That font is not part of this package. `@crewlethq/tokens/css/material-symbols` is a separate, opt-in stylesheet that loads it from Google Fonts: it requests `fonts.googleapis.com`, which serves the `@font-face` rule and the `material-symbols-outlined` class, and the browser then downloads the font from `fonts.gstatic.com`.
188
+ `@crewlethq/tokens/css/material-symbols` is an opt-in stylesheet that loads the Material Symbols Outlined icon font from Google Fonts: it requests `fonts.googleapis.com`, which serves the `@font-face` rule and the `material-symbols-outlined` class, and the browser then downloads the font from `fonts.gstatic.com`.
189
+
190
+ No `@crewlethq/ui` component needs it. Those draw their glyphs as SVG from `@crewlethq/icons`, which is vendored and makes no network request. This stylesheet stays for an application that writes its own `<span class="material-symbols-outlined">` ligatures.
59
191
 
60
192
  Material Symbols is published by Google under the Apache License 2.0 (<https://github.com/google/material-design-icons>). Because this package only references the hosted font and redistributes none of its files, it carries no copy of that license.
61
193
 
62
- Keep `@crewlethq/tokens/css/fonts` and `@crewlethq/tokens/css/material-symbols` as separate imports: the first never leaves the application's own origin, so an app that must not contact third-party hosts imports only that one and loads the icon font from its own origin under the same class name.
194
+ It is the one stylesheet in this package that contacts a third-party host, which is why it is a separate import: an app that must not do that imports the other stylesheets and loads its icons from its own origin.
63
195
 
64
196
  ## Add or change a token
65
197
 
66
- 1. Edit a JSON file in `tokens/`.
198
+ 1. Edit a JSON file in `tokens/`, or `tokens/themes/light.json` and `tokens/themes/dark.json` for a slot that changes with the palette. A themed slot needs an entry in **both** theme files and a value in `tokens/color.json`, which the palette suite checks.
67
199
  2. Run `npm run build` (or `npm run dev` for watch).
68
- 3. The generated `dist/` and `src/index.ts` are regenerated.
200
+ 3. Run `npm test`. A colour change that lowers a measured floor fails here.
201
+ 4. The generated `dist/` and `src/index.ts` are regenerated.
69
202
 
70
203
  Never edit files in `dist/` or `src/index.ts` directly. They are
71
- regenerated on every build.
204
+ regenerated on every build. `stylesheets/base.css` is the one stylesheet in this package written by hand rather than generated; the build copies it into `dist/css/`.
72
205
 
73
206
  ## License
74
207
 
@@ -0,0 +1,194 @@
1
+ /* The opt-in document baseline: the reset, the document's own type and
2
+ colour, one focus treatment, themed scrollbars and the reduced-motion
3
+ collapse. Copied into dist/css/base.css by scripts/build.mjs.
4
+
5
+ Import it after tokens.css and themes.css:
6
+ import '@crewlethq/tokens/css';
7
+ import '@crewlethq/tokens/css/themes';
8
+ import '@crewlethq/tokens/css/base';
9
+
10
+ It is opt-in because it styles element selectors, which an application
11
+ with its own reset does not want twice. Every COLOUR, type step, radius and
12
+ space comes from a token, so it follows the palette and the density without
13
+ a rule of its own, and the package's suite refuses a colour literal here.
14
+
15
+ Four lengths are written out, and each is a measurement of a thing rather
16
+ than a step on a scale: the focus ring's own width and offset, and the
17
+ scrollbar's width and the inset that rounds its thumb. A token for any of
18
+ them would be a scale with one value on it.
19
+
20
+ What is deliberately NOT here: the body's height and overflow. A page that
21
+ can scroll as a whole moves an application's rail off the top of the
22
+ window, so the rule that stops it belongs to the shell that owns the rail,
23
+ not to every document that imports a baseline. */
24
+
25
+ *,
26
+ *::before,
27
+ *::after {
28
+ box-sizing: border-box;
29
+ }
30
+
31
+ html {
32
+ -webkit-text-size-adjust: 100%;
33
+ text-size-adjust: 100%;
34
+ }
35
+
36
+ body {
37
+ margin: 0;
38
+ background: var(--color-surface-background);
39
+ color: var(--color-text-primary);
40
+ font-family: var(--font-family-sans);
41
+ font-size: var(--font-size-sm);
42
+ font-weight: var(--font-weight-regular);
43
+ line-height: var(--font-line-height-normal);
44
+ -webkit-font-smoothing: antialiased;
45
+ -moz-osx-font-smoothing: grayscale;
46
+ }
47
+
48
+ h1,
49
+ h2,
50
+ h3,
51
+ h4,
52
+ h5,
53
+ h6 {
54
+ margin: 0;
55
+ font-family: var(--font-family-display);
56
+ font-weight: var(--font-weight-semibold);
57
+ line-height: var(--font-line-height-tight);
58
+ letter-spacing: var(--font-letter-spacing-tight);
59
+ }
60
+
61
+ p,
62
+ figure,
63
+ pre {
64
+ margin: 0;
65
+ }
66
+
67
+ a {
68
+ color: var(--color-brand-accent-ink);
69
+ text-decoration: none;
70
+ }
71
+
72
+ a:hover {
73
+ text-decoration: underline;
74
+ }
75
+
76
+ /* A control inherits the document's type. Left alone, every browser gives
77
+ form controls a system face at a size of its own, which is how one button
78
+ in a row ends up a pixel taller than the rest. */
79
+ button,
80
+ input,
81
+ optgroup,
82
+ select,
83
+ textarea {
84
+ font: inherit;
85
+ color: inherit;
86
+ margin: 0;
87
+ }
88
+
89
+ button {
90
+ background: none;
91
+ border: none;
92
+ padding: 0;
93
+ cursor: pointer;
94
+ }
95
+
96
+ code,
97
+ kbd,
98
+ pre,
99
+ samp {
100
+ font-family: var(--font-family-mono);
101
+ font-variant-ligatures: none;
102
+ /* Every number in a product is a number that changes. Tabular figures are
103
+ what stop a live counter shifting the column beside it. */
104
+ font-variant-numeric: tabular-nums;
105
+ }
106
+
107
+ hr {
108
+ border: none;
109
+ border-top: 1px solid var(--color-border-default);
110
+ margin: 0;
111
+ }
112
+
113
+ table {
114
+ border-collapse: collapse;
115
+ width: 100%;
116
+ }
117
+
118
+ svg {
119
+ display: block;
120
+ flex: none;
121
+ }
122
+
123
+ img {
124
+ max-width: 100%;
125
+ }
126
+
127
+ /* One focus treatment for the whole document, and only ever the keyboard
128
+ ring: a mouse click that painted a ring around a row made every list look
129
+ permanently selected.
130
+
131
+ The indicator is an OUTLINE. Forced-colors mode drops every box-shadow and
132
+ keeps outlines, so a ring drawn as a shadow alone disappears for exactly
133
+ the readers who most need one. A component that also wants a shadow pairs
134
+ --shadow-focus with a transparent outline, so forced colors substitutes a
135
+ system colour for it. */
136
+ :focus {
137
+ outline: none;
138
+ }
139
+
140
+ :focus-visible {
141
+ outline: 2px solid var(--color-focus);
142
+ outline-offset: 2px;
143
+ border-radius: var(--radius-xs);
144
+ }
145
+
146
+ ::selection {
147
+ background: var(--color-brand-accent-soft);
148
+ }
149
+
150
+ /* Scrollbars belong to the palette. A bright system gutter down the side of a
151
+ dark page is the most visible way an application admits it is a web page. */
152
+ * {
153
+ scrollbar-color: var(--color-scrollbar) transparent;
154
+ scrollbar-width: thin;
155
+ }
156
+
157
+ ::-webkit-scrollbar {
158
+ width: 10px;
159
+ height: 10px;
160
+ }
161
+
162
+ ::-webkit-scrollbar-track {
163
+ background: transparent;
164
+ }
165
+
166
+ ::-webkit-scrollbar-thumb {
167
+ background: var(--color-scrollbar);
168
+ border: 3px solid transparent;
169
+ background-clip: content-box;
170
+ border-radius: var(--radius-pill);
171
+ }
172
+
173
+ ::-webkit-scrollbar-thumb:hover {
174
+ background: var(--color-scrollbar-hover);
175
+ background-clip: content-box;
176
+ }
177
+
178
+ ::-webkit-scrollbar-corner {
179
+ background: transparent;
180
+ }
181
+
182
+ /* The one place a reduced-motion preference is honoured for the whole
183
+ document. A component still states its own rule, because a component is
184
+ also used in a document that does not import this file. */
185
+ @media (prefers-reduced-motion: reduce) {
186
+ *,
187
+ *::before,
188
+ *::after {
189
+ animation-duration: 0.01ms !important;
190
+ animation-iteration-count: 1 !important;
191
+ transition-duration: 0.01ms !important;
192
+ scroll-behavior: auto !important;
193
+ }
194
+ }
@@ -0,0 +1,18 @@
1
+ /* Generated by style-dictionary. The layout breakpoints, on their own, for a
2
+ stylesheet that wants them without the rest of the token layer:
3
+ import '@crewlethq/tokens/css/breakpoint';
4
+
5
+ A media query cannot read a custom property, so a stylesheet that switches
6
+ layout at a breakpoint writes the number out. These declarations are what
7
+ says which number it must be. The same values are in the typed export as
8
+ `breakpoint`, so a check can compare a query's literal against them
9
+ instead of trusting a comment. */
10
+
11
+ :root {
12
+ --breakpoint-sm: 480px;
13
+ --breakpoint-md: 768px;
14
+ --breakpoint-shell: 900px;
15
+ --breakpoint-lg: 1024px;
16
+ --breakpoint-xl: 1200px;
17
+ --breakpoint-2xl: 1600px;
18
+ }
@@ -0,0 +1,23 @@
1
+ /* Generated by style-dictionary. The density contract, as an attribute on
2
+ the root element. Import it after tokens.css:
3
+ import '@crewlethq/tokens/css';
4
+ import '@crewlethq/tokens/css/density';
5
+
6
+ Set data-density="compact" or data-density="comfortable" on <html>;
7
+ anything else, the attribute removed included, is the normal density.
8
+ Every spacing and size token is emitted as calc(Npx * var(--density, 1)),
9
+ so the scale reaches every gap, pad, row and control, and every value is
10
+ unchanged at 1. The small control and row steps carry a 24px floor, so
11
+ compact cannot take a target under the size a finger can hit. */
12
+
13
+ :root {
14
+ --density: 1;
15
+ }
16
+
17
+ :root[data-density="compact"] {
18
+ --density: 0.82;
19
+ }
20
+
21
+ :root[data-density="comfortable"] {
22
+ --density: 1.14;
23
+ }
@@ -4,12 +4,10 @@
4
4
  New code should reference --color-*, --spacing-*, --font-*, --radius-*,
5
5
  --shadow-* directly.
6
6
 
7
- The aliases are re-declared on the theme classes so they follow a theme
8
- switch. Override an alias on the element that switches the theme (or a
9
- descendant of it); an override on :root is replaced inside a themed body. */
10
- :root,
11
- body.theme-dark,
12
- body.theme-light {
7
+ Declared on :root only. Both themes repaint the canonical tokens on :root,
8
+ so an alias resolved there follows a theme switch. Override an alias on
9
+ :root or on a descendant of it. */
10
+ :root {
13
11
  /* Brand */
14
12
  --accent: var(--color-brand-primary);
15
13
  --primary-blue: var(--color-brand-primary);
@@ -1,9 +1,11 @@
1
1
  /* Generated by style-dictionary. Loads the Material Symbols Outlined icon
2
- font that @crewlethq/ui components render through
3
- <span class="material-symbols-outlined">. Import it when the app renders
4
- those components and does not load the icon font itself:
2
+ font, for an application that renders its own
3
+ <span class="material-symbols-outlined"> ligatures:
5
4
  import '@crewlethq/tokens/css/material-symbols';
6
5
 
6
+ No @crewlethq/ui component needs it. Those draw their glyphs as SVG from
7
+ @crewlethq/icons, which is vendored and makes no network request.
8
+
7
9
  This stylesheet requests fonts.googleapis.com, and the browser then
8
10
  downloads the font from fonts.gstatic.com. Material Symbols is published
9
11
  by Google under the Apache License 2.0