@sandilya/ui 0.2.0 → 0.4.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,6 +1,6 @@
1
1
  # @sandilya/ui
2
2
 
3
- Color tokens, brand themes, base styles and the Tailwind CSS 4 theme for [san-ui](https://ui.sandilya.dev).
3
+ Design tokens, themes, base styles and the Tailwind CSS 4 theme for [san-ui](https://ui.sandilya.dev): OKLCH color roles that stay WCAG AA for any theme, density, type, springs and materials.
4
4
 
5
5
  ```bash
6
6
  bun add @sandilya/ui
@@ -10,16 +10,16 @@ bun add @sandilya/ui
10
10
  /* src/index.css */
11
11
  @import 'tailwindcss';
12
12
  @import '@sandilya/ui/tokens.css';
13
- @import '@sandilya/ui/themes/eloovor.css'; /* or your own theme */
13
+ @import '@sandilya/ui/themes/forest.css'; /* optional: or your own theme */
14
14
  @import '@sandilya/ui/theme.css';
15
15
  @import '@sandilya/ui/base.css';
16
16
  ```
17
17
 
18
- | File | What it is |
19
- | -------------- | ------------------------------------------------------------------------------- |
20
- | `tokens.css` | The shared base: every color role in light and dark, radius |
21
- | `themes/*.css` | A brand: color ramps, the hues neutrals lean toward, fonts |
22
- | `theme.css` | Tailwind names for the tokens, radius, shadows, fonts, dark variant, animations |
23
- | `base.css` | Border, cursor, page and selection defaults the components assume |
18
+ | File | What it is |
19
+ | -------------- | ------------------------------------------------------------------------------------ |
20
+ | `tokens.css` | Every role in light and dark, derived from the theme inputs; density; radius; motion |
21
+ | `themes/*.css` | Five inputs: accent hue and chroma, neutral hue and chroma, radius scale |
22
+ | `theme.css` | Tailwind names for the roles, in place of Tailwind's defaults; keyframes; `material` |
23
+ | `base.css` | Page, border, selection and placeholder defaults the components assume |
24
24
 
25
25
  To make your own theme, copy `themes/template.css` into your app. The components come from the shadcn registry; setup, the theme contract and the rules are in the [repository README](https://github.com/anand-san/ui#readme).
package/base.css CHANGED
@@ -1,23 +1,58 @@
1
1
  /*
2
- * san-ui base element styles. Import it with the other @imports at
3
- * the top of the app's CSS; `@layer base` puts it after Tailwind's preflight.
4
- * Components assume borders default to the border token and the page sits
5
- * on the background token.
2
+ * san-ui base element styles. Import it after theme.css; `@layer base`
3
+ * puts it after Tailwind's preflight.
6
4
  */
7
5
  @layer base {
8
6
  *,
9
7
  ::before,
10
8
  ::after {
11
- border-color: hsl(var(--border));
9
+ border-color: var(--separator);
10
+ }
11
+
12
+ html {
13
+ -webkit-text-size-adjust: 100%;
14
+ text-size-adjust: 100%;
15
+ -webkit-tap-highlight-color: transparent;
16
+ }
17
+
18
+ /* The roles are in tokens.css; the paint is here so utilities win */
19
+ .surface-accent {
20
+ background-color: var(--surface);
21
+ color: var(--ink);
12
22
  }
13
23
 
14
24
  html,
15
25
  body {
16
- background-color: hsl(var(--background));
17
- color: hsl(var(--foreground));
26
+ background-color: var(--canvas);
27
+ color: var(--ink);
18
28
  font-family: var(--font-sans);
29
+ line-height: 1.45;
30
+ font-optical-sizing: auto;
19
31
  -webkit-font-smoothing: antialiased;
20
32
  -moz-osx-font-smoothing: grayscale;
33
+ caret-color: var(--accent);
34
+ accent-color: var(--accent);
35
+ }
36
+
37
+ /* On body only: on html it would change the rem every token is sized in */
38
+ body {
39
+ font-size: var(--type-body);
40
+ }
41
+
42
+ p,
43
+ li,
44
+ figcaption,
45
+ blockquote {
46
+ text-wrap: pretty;
47
+ }
48
+
49
+ /* Smooth the jump when the theme switches, like the system does */
50
+ @media (prefers-reduced-motion: no-preference) {
51
+ body {
52
+ transition:
53
+ background-color var(--duration-base) var(--ease-out),
54
+ color var(--duration-base) var(--ease-out);
55
+ }
21
56
  }
22
57
 
23
58
  button:not(:disabled),
@@ -26,7 +61,38 @@
26
61
  }
27
62
 
28
63
  ::selection {
29
- background-color: hsl(var(--highlight) / 0.55);
30
- color: hsl(var(--highlight-foreground));
64
+ background-color: color-mix(in oklch, var(--highlight) 70%, transparent);
65
+ color: var(--highlight-foreground);
66
+ }
67
+
68
+ ::placeholder {
69
+ color: var(--ink-secondary);
70
+ opacity: 1;
71
+ }
72
+
73
+ * {
74
+ scrollbar-color: var(--fill-strong) transparent;
75
+ scrollbar-width: thin;
76
+ }
77
+
78
+ a {
79
+ text-underline-offset: 0.2em;
80
+ text-decoration-thickness: from-font;
81
+ }
82
+
83
+ code,
84
+ kbd,
85
+ samp,
86
+ pre {
87
+ font-family: var(--font-mono);
88
+ font-size: 0.92em;
89
+ }
90
+
91
+ @media (prefers-reduced-motion: reduce) {
92
+ *,
93
+ ::before,
94
+ ::after {
95
+ scroll-behavior: auto !important;
96
+ }
31
97
  }
32
98
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sandilya/ui",
3
- "version": "0.2.0",
4
- "description": "Design tokens, themes and the Tailwind CSS 4 theme for san-ui",
3
+ "version": "0.4.0",
4
+ "description": "Design tokens, themes and the Tailwind CSS 4 theme for san-ui: OKLCH colors that stay WCAG AA for any theme, density, type, motion and materials",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "repository": {
@@ -14,7 +14,8 @@
14
14
  "design-tokens",
15
15
  "tailwindcss",
16
16
  "tailwind-theme",
17
- "design-system"
17
+ "design-system",
18
+ "oklch"
18
19
  ],
19
20
  "exports": {
20
21
  "./theme.css": "./theme.css",
@@ -33,11 +34,8 @@
33
34
  "sideEffects": [
34
35
  "*.css"
35
36
  ],
36
- "dependencies": {
37
- "tw-animate-css": "^1.4.0"
38
- },
39
37
  "peerDependencies": {
40
- "tailwindcss": "^4.0.0"
38
+ "tailwindcss": "^4.3.0"
41
39
  },
42
40
  "publishConfig": {
43
41
  "access": "public"
package/theme.css CHANGED
@@ -1,91 +1,278 @@
1
1
  /*
2
- * Tailwind names for the san-ui tokens (tokens.css plus a theme from
3
- * themes/). Import it after `tailwindcss`; an app's CSS adds only its own
4
- * domain colors and motion in its own @theme block.
2
+ * Tailwind names for the san-ui tokens. Import it after `tailwindcss` and
3
+ * tokens.css (and a theme).
4
+ *
5
+ * It replaces Tailwind's default colors, type sizes, radii and shadows with
6
+ * the san-ui roles, so off-system classes like `bg-slate-500` or `text-sm`
7
+ * don't build. An app adds its own domain colors in its own @theme block.
5
8
  */
6
- @import 'tw-animate-css';
7
9
 
8
10
  @custom-variant dark (&:is(.dark *));
9
11
 
10
- /* The faces come from the theme (--font-sans, --font-display) */
12
+ @theme {
13
+ --color-*: initial;
14
+ --text-*: initial;
15
+ --radius-*: initial;
16
+ --shadow-*: initial;
17
+ --inset-shadow-*: initial;
18
+ --drop-shadow-*: initial;
19
+ --font-*: initial;
20
+ --animate-*: initial;
21
+ --ease-*: initial;
22
+ }
23
+
11
24
  @theme inline reference {
12
25
  --font-sans: var(--font-sans);
13
26
  --font-display: var(--font-display);
27
+ --font-rounded: var(--font-rounded);
28
+ --font-mono: var(--font-mono);
29
+
30
+ /* Neutrals */
31
+ --color-canvas: var(--canvas);
32
+ --color-surface: var(--surface);
33
+ --color-surface-inset: var(--surface-inset);
34
+ --color-overlay: var(--overlay);
35
+ --color-ink: var(--ink);
36
+ --color-ink-secondary: var(--ink-secondary);
37
+ --color-ink-tertiary: var(--ink-tertiary);
38
+ --color-separator: var(--separator);
39
+ --color-control: var(--border-control);
40
+ --color-fill: var(--fill);
41
+ --color-fill-hover: var(--fill-hover);
42
+ --color-fill-strong: var(--fill-strong);
43
+ --color-scrim: var(--scrim);
44
+ --color-focus: var(--focus);
45
+
46
+ /* The same in every mode, like a switch's thumb */
47
+ --color-white: oklch(1 0 0);
48
+ --color-black: oklch(0 0 0);
49
+
50
+ /*
51
+ * Accent and status. `bg-accent` is the fill under white text;
52
+ * `text-accent` is the shade that reads as text on any surface.
53
+ */
54
+ --color-accent: var(--accent);
55
+ --color-accent-pressed: var(--accent-pressed);
56
+ --color-accent-foreground: var(--accent-foreground);
57
+ --color-accent-soft: var(--accent-soft);
58
+ --color-accent-soft-hover: var(--accent-soft-hover);
59
+ --text-color-accent: var(--accent-text);
60
+
61
+ --color-success: var(--success);
62
+ --color-success-foreground: var(--success-foreground);
63
+ --color-success-soft: var(--success-soft);
64
+ --text-color-success: var(--success-text);
65
+ --color-warning: var(--warning);
66
+ --color-warning-foreground: var(--warning-foreground);
67
+ --color-warning-soft: var(--warning-soft);
68
+ --text-color-warning: var(--warning-text);
69
+ --color-danger: var(--danger);
70
+ --color-danger-foreground: var(--danger-foreground);
71
+ --color-danger-soft: var(--danger-soft);
72
+ --text-color-danger: var(--danger-text);
73
+ --color-info: var(--info);
74
+ --color-info-foreground: var(--info-foreground);
75
+ --color-info-soft: var(--info-soft);
76
+ --text-color-info: var(--info-text);
77
+
78
+ --color-highlight: var(--highlight);
79
+ --color-highlight-foreground: var(--highlight-foreground);
80
+
81
+ /*
82
+ * Type roles: size, leading, tracking and weight as one set. Tracking
83
+ * tightens as size grows. Sizes follow the density.
84
+ */
85
+ --text-display: clamp(2.5rem, 1.6rem + 3.6vw, 4.5rem);
86
+ --text-display--line-height: 1.05;
87
+ --text-display--letter-spacing: -0.028em;
88
+ --text-display--font-weight: 700;
89
+ --text-title-1: var(--type-title-1);
90
+ --text-title-1--line-height: 1.15;
91
+ --text-title-1--letter-spacing: -0.022em;
92
+ --text-title-1--font-weight: 700;
93
+ --text-title-2: var(--type-title-2);
94
+ --text-title-2--line-height: 1.25;
95
+ --text-title-2--letter-spacing: -0.016em;
96
+ --text-title-2--font-weight: 600;
97
+ --text-title-3: var(--type-title-3);
98
+ --text-title-3--line-height: 1.3;
99
+ --text-title-3--letter-spacing: -0.012em;
100
+ --text-title-3--font-weight: 600;
101
+ --text-headline: var(--type-body);
102
+ --text-headline--line-height: 1.35;
103
+ --text-headline--letter-spacing: -0.006em;
104
+ --text-headline--font-weight: 600;
105
+ --text-body: var(--type-body);
106
+ --text-body--line-height: 1.45;
107
+ --text-body--letter-spacing: -0.003em;
108
+ --text-subhead: var(--type-subhead);
109
+ --text-subhead--line-height: 1.4;
110
+ --text-caption: var(--type-caption);
111
+ --text-caption--line-height: 1.35;
112
+ --text-caption--letter-spacing: 0.006em;
113
+
114
+ /* Radius by role; panels, items and sheets are concentric */
115
+ --radius-xs: calc(4px * var(--radius-scale));
116
+ --radius-control: var(--radius-control);
117
+ --radius-item: var(--radius-item);
118
+ --radius-card: var(--radius-card);
119
+ --radius-panel: var(--radius-panel);
120
+ --radius-sheet: var(--radius-sheet);
121
+
122
+ /* Sizes that follow the density: h-control-md, min-h-row, p-inset */
123
+ --spacing-control-sm: var(--control-sm);
124
+ --spacing-control-md: var(--control-md);
125
+ --spacing-control-lg: var(--control-lg);
126
+ --spacing-row: var(--row);
127
+ --spacing-inset: var(--inset);
128
+ --spacing-panel: var(--panel-inset);
129
+ --spacing-touch: 2.75rem;
130
+
131
+ /* Shadows are tinted with the neutral hue and kept for things that float */
132
+ --shadow-control:
133
+ 0 1px 2px color-mix(in oklch, var(--shadow-color) 14%, transparent),
134
+ 0 2px 6px color-mix(in oklch, var(--shadow-color) 8%, transparent);
135
+ --shadow-floating:
136
+ 0 12px 32px -8px color-mix(in oklch, var(--shadow-color) 22%, transparent),
137
+ 0 2px 6px color-mix(in oklch, var(--shadow-color) 8%, transparent);
138
+ --shadow-modal:
139
+ 0 24px 64px -12px color-mix(in oklch, var(--shadow-color) 32%, transparent),
140
+ 0 4px 12px color-mix(in oklch, var(--shadow-color) 10%, transparent);
141
+
142
+ --ease-spring: var(--ease-spring);
143
+ --ease-out: var(--ease-out);
144
+
145
+ --animate-pop-in: pop-in var(--duration-spring) var(--ease-spring);
146
+ --animate-pop-out: pop-out var(--duration-fast) var(--ease-out) forwards;
147
+ --animate-fade-in: fade-in var(--duration-base) var(--ease-out);
148
+ --animate-fade-out: fade-out var(--duration-fast) var(--ease-out) forwards;
149
+ --animate-disclose: disclose var(--duration-spring) var(--ease-spring);
150
+ --animate-conceal: conceal var(--duration-fast) var(--ease-out) forwards;
151
+ --animate-shimmer: shimmer 1.6s ease-in-out infinite;
152
+ --animate-spin: spin 0.8s linear infinite;
153
+ }
154
+
155
+ /*
156
+ * Floating panels grow from the control that opened them (Radix sets the
157
+ * origin), and shrink back toward it.
158
+ */
159
+ @keyframes pop-in {
160
+ from {
161
+ opacity: 0;
162
+ transform: scale(0.94);
163
+ }
164
+ }
165
+
166
+ @keyframes pop-out {
167
+ to {
168
+ opacity: 0;
169
+ transform: scale(0.97);
170
+ }
171
+ }
172
+
173
+ @keyframes fade-in {
174
+ from {
175
+ opacity: 0;
176
+ }
177
+ }
178
+
179
+ @keyframes fade-out {
180
+ to {
181
+ opacity: 0;
182
+ }
183
+ }
184
+
185
+ /* Accordion and collapsible content opens to the height Radix measures */
186
+ @keyframes disclose {
187
+ from {
188
+ height: 0;
189
+ opacity: 0;
190
+ }
191
+ to {
192
+ height: var(--radix-collapsible-content-height);
193
+ }
194
+ }
195
+
196
+ @keyframes conceal {
197
+ from {
198
+ height: var(--radix-collapsible-content-height);
199
+ }
200
+ to {
201
+ height: 0;
202
+ opacity: 0;
203
+ }
204
+ }
205
+
206
+ @keyframes shimmer {
207
+ 50% {
208
+ opacity: 0.55;
209
+ }
210
+ }
211
+
212
+ @keyframes spin {
213
+ to {
214
+ transform: rotate(360deg);
215
+ }
216
+ }
217
+
218
+ /* Reduced motion keeps the state change but drops the movement */
219
+ @media (prefers-reduced-motion: reduce) {
220
+ @keyframes pop-in {
221
+ from {
222
+ opacity: 0;
223
+ }
224
+ }
225
+
226
+ @keyframes pop-out {
227
+ to {
228
+ opacity: 0;
229
+ }
230
+ }
231
+
232
+ @keyframes disclose {
233
+ from {
234
+ opacity: 0;
235
+ }
236
+ }
237
+
238
+ @keyframes conceal {
239
+ to {
240
+ opacity: 0;
241
+ }
242
+ }
243
+
244
+ @keyframes shimmer {
245
+ 50% {
246
+ opacity: 0.8;
247
+ }
248
+ }
249
+ }
250
+
251
+ /*
252
+ * Materials: translucent layers for chrome that floats over content. Thicker
253
+ * materials hide more of what is behind them. With reduced transparency or
254
+ * more contrast they turn solid.
255
+ */
256
+ @utility material-thin {
257
+ background-color: color-mix(in oklch, var(--overlay) 88%, transparent);
258
+ backdrop-filter: blur(16px) saturate(180%);
259
+ }
260
+
261
+ @utility material {
262
+ background-color: color-mix(in oklch, var(--overlay) 93%, transparent);
263
+ backdrop-filter: blur(24px) saturate(180%);
264
+ }
265
+
266
+ @utility material-thick {
267
+ background-color: color-mix(in oklch, var(--overlay) 97%, transparent);
268
+ backdrop-filter: blur(32px) saturate(180%);
14
269
  }
15
270
 
16
- @theme inline {
17
- /* Radius by role: xs for chips, md for fields, lg for small cards,
18
- xl for section cards, 2xl for dialogs. Buttons are pills. */
19
- --radius-xs: calc(var(--radius) - 4px);
20
- --radius-md: calc(var(--radius) - 2px);
21
- --radius-lg: var(--radius);
22
- --radius-xl: calc(var(--radius) + 4px);
23
- --radius-2xl: calc(var(--radius) + 8px);
24
-
25
- /* Ink-tinted shadows, only for things that float: menus, dialogs, toasts */
26
- --shadow-xs: 0 1px 2px hsl(var(--shadow-color) / 0.06);
27
- --shadow-sm:
28
- 0 1px 3px hsl(var(--shadow-color) / 0.08),
29
- 0 1px 2px hsl(var(--shadow-color) / 0.04);
30
- --shadow-md:
31
- 0 4px 12px -2px hsl(var(--shadow-color) / 0.1),
32
- 0 2px 4px hsl(var(--shadow-color) / 0.04);
33
- --shadow-lg:
34
- 0 12px 32px -8px hsl(var(--shadow-color) / 0.18),
35
- 0 2px 6px hsl(var(--shadow-color) / 0.06);
36
- --shadow-xl:
37
- 0 24px 56px -12px hsl(var(--shadow-color) / 0.26),
38
- 0 4px 12px hsl(var(--shadow-color) / 0.08);
39
-
40
- --color-background: hsl(var(--background));
41
- --color-foreground: hsl(var(--foreground));
42
- --color-surface: hsl(var(--surface));
43
- --color-card: hsl(var(--card));
44
- --color-card-foreground: hsl(var(--card-foreground));
45
- --color-popover: hsl(var(--popover));
46
- --color-popover-foreground: hsl(var(--popover-foreground));
47
- --color-muted: hsl(var(--muted));
48
- --color-muted-foreground: hsl(var(--muted-foreground));
49
- --color-primary: hsl(var(--primary));
50
- --color-primary-foreground: hsl(var(--primary-foreground));
51
-
52
- --color-brand: hsl(var(--brand));
53
- --color-brand-foreground: hsl(var(--brand-foreground));
54
- --color-brand-50: hsl(var(--brand-50));
55
- --color-brand-100: hsl(var(--brand-100));
56
- --color-brand-200: hsl(var(--brand-200));
57
- --color-brand-300: hsl(var(--brand-300));
58
- --color-brand-400: hsl(var(--brand-400));
59
- --color-brand-500: hsl(var(--brand-500));
60
- --color-brand-600: hsl(var(--brand-600));
61
- --color-brand-700: hsl(var(--brand-700));
62
- --color-brand-800: hsl(var(--brand-800));
63
- --color-brand-900: hsl(var(--brand-900));
64
- --color-brand-950: hsl(var(--brand-950));
65
-
66
- --color-highlight: hsl(var(--highlight));
67
- --color-highlight-foreground: hsl(var(--highlight-foreground));
68
- --color-highlight-50: hsl(var(--highlight-50));
69
- --color-highlight-100: hsl(var(--highlight-100));
70
- --color-highlight-200: hsl(var(--highlight-200));
71
- --color-highlight-300: hsl(var(--highlight-300));
72
- --color-highlight-400: hsl(var(--highlight-400));
73
- --color-highlight-500: hsl(var(--highlight-500));
74
- --color-highlight-600: hsl(var(--highlight-600));
75
- --color-highlight-700: hsl(var(--highlight-700));
76
- --color-highlight-800: hsl(var(--highlight-800));
77
- --color-highlight-900: hsl(var(--highlight-900));
78
- --color-highlight-950: hsl(var(--highlight-950));
79
-
80
- --color-destructive: hsl(var(--destructive));
81
- --color-destructive-foreground: hsl(var(--destructive-foreground));
82
- --color-success: hsl(var(--success));
83
- --color-success-foreground: hsl(var(--success-foreground));
84
- --color-warning: hsl(var(--warning));
85
- --color-warning-foreground: hsl(var(--warning-foreground));
86
- --color-info: hsl(var(--info));
87
- --color-info-foreground: hsl(var(--info-foreground));
88
- --color-border: hsl(var(--border));
89
- --color-input: hsl(var(--input));
90
- --color-ring: hsl(var(--ring));
271
+ @media (prefers-reduced-transparency: reduce), (prefers-contrast: more) {
272
+ .material-thin,
273
+ .material,
274
+ .material-thick {
275
+ background-color: var(--overlay);
276
+ backdrop-filter: none;
277
+ }
91
278
  }
@@ -0,0 +1,8 @@
1
+ /* Ember: a red-orange accent over warm greys, with tighter corners */
2
+ :root {
3
+ --accent-hue: 38;
4
+ --accent-chroma: 0.19;
5
+ --neutral-hue: 60;
6
+ --neutral-chroma: 0.01;
7
+ --radius-scale: 0.7;
8
+ }
@@ -0,0 +1,8 @@
1
+ /* Forest: a green accent over slightly green greys, with softer corners */
2
+ :root {
3
+ --accent-hue: 155;
4
+ --accent-chroma: 0.15;
5
+ --neutral-hue: 150;
6
+ --neutral-chroma: 0.012;
7
+ --radius-scale: 1.25;
8
+ }
@@ -0,0 +1,8 @@
1
+ /* Graphite: a grey accent and pure greys, for tools that stay quiet */
2
+ :root {
3
+ --accent-hue: 260;
4
+ --accent-chroma: 0.02;
5
+ --neutral-hue: 260;
6
+ --neutral-chroma: 0;
7
+ --radius-scale: 0.85;
8
+ }
@@ -1,62 +1,28 @@
1
1
  /*
2
- * Starting point for a new app's theme. Copy it into the app, rename it,
3
- * and replace the values. It works as is, with a teal brand, a lemon
4
- * highlighter and system fonts.
2
+ * A starting point for an app's theme. Copy it into the app (for example as
3
+ * src/theme.css), import it after @sandilya/ui/tokens.css, and change the
4
+ * values. Everything else, light and dark, follows from these.
5
5
  *
6
- * Import after tokens.css. Everything else (surfaces, feedback colors,
7
- * lines, radius) comes from the base and follows these hues.
8
- *
9
- * Checks before shipping a theme:
10
- * - --brand-600 on white and white on --brand-600 are at least 4.5:1.
11
- * - --highlight-300 behind --highlight-950 text is at least 4.5:1.
12
- * - --tint-hue is the brand hue, or near it, so greys feel related.
13
- * - Look at every Storybook story in light and dark.
6
+ * Contrast is handled by the base for any hue: it fixes each role's
7
+ * lightness, so white text on the accent and accent text on every surface
8
+ * stay at WCAG AA. Overriding a role directly (say --accent) takes it off
9
+ * that guarantee; check it in both modes if you do.
14
10
  */
11
+ :root {
12
+ /* The accent: buttons, links, selection, focus. Hue in degrees, 0 to 360 */
13
+ --accent-hue: 250;
14
+ /* How vivid the accent is: 0 is grey, 0.2 (the cap) is as vivid as it gets */
15
+ --accent-chroma: 0.2;
16
+ /* The hue the greys lean toward, and how far (0 is pure grey, cap 0.03) */
17
+ --neutral-hue: 250;
18
+ --neutral-chroma: 0.008;
19
+ /* Multiplies every corner radius: 0.5 is sharper, 1.5 is softer */
20
+ --radius-scale: 1;
15
21
 
16
- :root,
17
- .force-light-mode,
18
- .dark .force-light-mode {
19
- /* Neutrals lean toward this hue; text and light shadows use --ink-hue */
20
- --tint-hue: 190;
21
- --ink-hue: 195;
22
-
23
- /* System fonts. To use your own, load the files in the app and name them
24
- here, e.g. 'Inter Variable', ui-sans-serif, system-ui, sans-serif */
25
- --font-sans: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
26
- --font-display: var(--font-sans);
27
-
28
- /* Brand ramp. --brand-600 is the primary button. */
29
- --brand-50: 185 45% 96%;
30
- --brand-100: 186 45% 91%;
31
- --brand-200: 187 42% 82%;
32
- --brand-300: 188 40% 69%;
33
- --brand-400: 189 38% 53%;
34
- --brand-500: 190 45% 40%;
35
- --brand-600: 191 55% 31%;
36
- --brand-700: 192 55% 25%;
37
- --brand-800: 193 52% 20%;
38
- --brand-900: 194 50% 16%;
39
- --brand-950: 196 55% 9%;
40
-
41
- /* Highlight ramp. --highlight-300 is the marker fill. */
42
- --highlight-50: 60 100% 96%;
43
- --highlight-100: 58 97% 88%;
44
- --highlight-200: 56 95% 78%;
45
- --highlight-300: 54 92% 66%;
46
- --highlight-400: 51 90% 55%;
47
- --highlight-500: 48 88% 47%;
48
- --highlight-600: 44 85% 39%;
49
- --highlight-700: 40 80% 32%;
50
- --highlight-800: 36 70% 26%;
51
- --highlight-900: 32 64% 21%;
52
- --highlight-950: 30 70% 11%;
53
- }
54
-
55
- .dark {
56
- --tint-hue: 195;
57
- --ink-hue: 190;
58
-
59
- /* Optional: the base uses brand-300 and highlight-300 here */
60
- /* --primary: 188 50% 70%; */
61
- /* --ring: 188 50% 64%; */
22
+ /*
23
+ * Faces. The system font is the default and usually the right one. To use
24
+ * your own, load the files in the app and name them here.
25
+ */
26
+ /* --font-sans: 'Inter Variable', system-ui, sans-serif; */
27
+ /* --font-display: var(--font-sans); */
62
28
  }
package/tokens.css CHANGED
@@ -1,129 +1,316 @@
1
1
  /*
2
- * san-ui base tokens: every color role, light and dark.
2
+ * san-ui tokens: every color, type, size, radius, shadow and motion value.
3
3
  *
4
- * Values are bare HSL channels so Tailwind opacity modifiers keep working
5
- * (bg-success/10, text-brand/80). Tailwind names for them live in
6
- * theme.css.
4
+ * Colors are OKLCH. A theme sets only the inputs below; the base fixes each
5
+ * role's lightness, so every role pair meets WCAG AA for any hue a theme
6
+ * picks (scripts/tokens.test.ts sweeps all 360). Tailwind names for these
7
+ * live in theme.css.
7
8
  *
8
- * This file holds what every app shares: the neutral surfaces, feedback
9
- * colors, lines, radius and the paper reset. The brand comes from a theme
10
- * (themes/*.css), imported after this file. A theme supplies:
11
- *
12
- * --brand-50 … --brand-950 the primary color ramp
13
- * --highlight-50 … --highlight-950 the highlighter ramp
14
- * --tint-hue the hue neutrals lean toward
15
- * --ink-hue the hue of text and light-mode shadows
16
- * --font-sans, --font-display optional, system fonts otherwise
17
- *
18
- * and may override any role below, usually primary, brand, highlight and
19
- * ring in dark mode.
9
+ * Light is the default. `.dark` on an ancestor (usually <html>) switches a
10
+ * subtree to dark, `.light` switches one back.
20
11
  */
21
12
 
22
- /*
23
- * Light values. `.dark .force-light-mode` is listed here so printed pages
24
- * stay light inside dark mode; its specificity beats `.dark`.
25
- */
13
+ :root {
14
+ /* Theme inputs. Hues are unitless degrees. */
15
+ --accent-hue: 250;
16
+ --accent-chroma: 0.2; /* capped at 0.2 */
17
+ --neutral-hue: 250;
18
+ --neutral-chroma: 0.008; /* how far greys lean toward the hue, capped at 0.03 */
19
+ --radius-scale: 1;
20
+
21
+ --font-sans:
22
+ system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
23
+ 'Helvetica Neue', Arial, sans-serif;
24
+ --font-display: var(--font-sans);
25
+ --font-rounded: ui-rounded, var(--font-sans);
26
+ --font-mono:
27
+ ui-monospace, 'SF Mono', SFMono-Regular, Menlo, Consolas, monospace;
28
+
29
+ /* The capped inputs every role reads */
30
+ --a-c: min(var(--accent-chroma), 0.2);
31
+ --n-c: min(var(--neutral-chroma), 0.03);
32
+ /*
33
+ * The lightest accent white text still reads on, at 4.6:1, for this hue:
34
+ * cyans need a darker accent than blues or reds at the same lightness.
35
+ * A fit of the per-hue limit, kept under it at every hue.
36
+ */
37
+ --accent-l: calc(
38
+ 0.5398 + 0.0239 * cos(var(--accent-hue) * 1deg) + 0.002 *
39
+ sin(var(--accent-hue) * 1deg) - 0.0136 * cos(var(--accent-hue) * 2deg) -
40
+ 0.0036 * sin(var(--accent-hue) * 2deg)
41
+ );
42
+ --accent-base: oklch(var(--accent-l) var(--a-c) var(--accent-hue));
43
+ }
44
+
45
+ /* Light */
26
46
  :root,
27
- .force-light-mode,
28
- .dark .force-light-mode {
47
+ .light,
48
+ .dark .light {
29
49
  color-scheme: light;
30
50
 
31
- --font-sans: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
32
- --font-display: var(--font-sans);
51
+ /* Grouped surfaces: a tinted canvas, white groups on it */
52
+ --canvas: oklch(0.967 var(--n-c) var(--neutral-hue));
53
+ --surface: oklch(1 0 0);
54
+ --surface-inset: oklch(0.975 calc(var(--n-c) * 0.6) var(--neutral-hue));
55
+ --overlay: oklch(0.995 calc(var(--n-c) * 0.3) var(--neutral-hue));
56
+
57
+ /* Text, like Apple's label colors */
58
+ --ink: oklch(0.2 calc(var(--n-c) * 1.5) var(--neutral-hue));
59
+ --ink-secondary: oklch(0.46 calc(var(--n-c) * 1.5) var(--neutral-hue));
60
+ --ink-tertiary: oklch(0.68 var(--n-c) var(--neutral-hue));
33
61
 
34
- /* Surfaces: a tinted canvas, white cards that sit on it */
35
- --background: var(--tint-hue) 33% 99%;
36
- --foreground: var(--ink-hue) 30% 13%; /* ink */
37
- --surface: var(--tint-hue) 26% 96.5%; /* soft section cards */
38
- --card: 0 0% 100%; /* outline cards */
39
- --card-foreground: var(--ink-hue) 30% 13%;
40
- --popover: 0 0% 100%; /* menus, popovers, dialogs */
41
- --popover-foreground: var(--ink-hue) 30% 13%;
42
- --muted: var(--tint-hue) 20% 93.5%;
43
- --muted-foreground: var(--tint-hue) 10% 42%; /* secondary text, 5.6:1 on the canvas */
44
-
45
- /* Actions and brand */
46
- --primary: var(--brand-600); /* main buttons, selection, child card tints */
47
- --primary-foreground: 0 0% 100%;
48
- --brand: var(--brand-600);
49
- --brand-foreground: 0 0% 100%;
50
- --highlight: var(--highlight-300); /* highlighter fill */
51
- --highlight-foreground: var(--highlight-950);
52
- --destructive: 2 58% 47%; /* brick red, 5.6:1 on white */
53
- --destructive-foreground: 0 0% 100%;
54
-
55
- /* Feedback */
56
- --success: 152 62% 27%; /* leaf green */
57
- --success-foreground: 0 0% 100%;
58
- --warning: 26 90% 36%; /* burnt orange, kept apart from a yellow highlight */
59
- --warning-foreground: 0 0% 100%;
60
- --info: 214 72% 44%; /* ink blue */
61
- --info-foreground: 0 0% 100%;
62
-
63
- /* Lines */
64
- --border: var(--tint-hue) 20% 90%;
65
- --input: var(--tint-hue) 16% 84%; /* a step darker than border, so fields read as fields */
66
- --ring: var(--brand-500);
67
-
68
- /* Shadows are tinted with ink, never pure black */
69
- --shadow-color: var(--ink-hue) 30% 20%;
70
-
71
- --radius: 0.625rem;
62
+ /* Lines. Control borders keep 3:1 against every surface. */
63
+ --separator: oklch(0.885 var(--n-c) var(--neutral-hue));
64
+ --border-control: oklch(0.62 var(--n-c) var(--neutral-hue));
65
+
66
+ /* Neutral fills are translucent, so they read on any surface */
67
+ --fill: oklch(0.55 calc(var(--n-c) * 2) var(--neutral-hue) / 0.1);
68
+ --fill-hover: oklch(0.55 calc(var(--n-c) * 2) var(--neutral-hue) / 0.15);
69
+ --fill-strong: oklch(0.55 calc(var(--n-c) * 2) var(--neutral-hue) / 0.22);
70
+ --scrim: oklch(0.15 var(--n-c) var(--neutral-hue) / 0.32);
71
+
72
+ /* Accent: `accent` is a fill under white text, `accent-text` reads as text */
73
+ --accent: var(--accent-base);
74
+ --accent-pressed: oklch(
75
+ calc(var(--accent-l) - 0.06) var(--a-c) var(--accent-hue)
76
+ );
77
+ --accent-foreground: oklch(1 0 0);
78
+ --accent-text: oklch(
79
+ calc(var(--accent-l) - 0.06) var(--a-c) var(--accent-hue)
80
+ );
81
+ --accent-soft: oklch(0.955 calc(var(--a-c) * 0.22) var(--accent-hue));
82
+ --accent-soft-hover: oklch(0.935 calc(var(--a-c) * 0.3) var(--accent-hue));
83
+ --focus: var(--accent-base);
84
+
85
+ /* Status: a solid fill, its text color, and a soft fill */
86
+ --success: oklch(0.53 0.14 150);
87
+ --success-foreground: oklch(1 0 0);
88
+ --success-text: oklch(0.49 0.13 150);
89
+ --success-soft: oklch(0.955 0.035 150);
90
+ --warning: oklch(0.56 0.15 55);
91
+ --warning-foreground: oklch(1 0 0);
92
+ --warning-text: oklch(0.51 0.14 55);
93
+ --warning-soft: oklch(0.955 0.035 70);
94
+ --danger: oklch(0.55 0.19 27);
95
+ --danger-foreground: oklch(1 0 0);
96
+ --danger-text: oklch(0.51 0.18 27);
97
+ --danger-soft: oklch(0.955 0.025 20);
98
+ --info: oklch(0.54 0.17 245);
99
+ --info-foreground: oklch(1 0 0);
100
+ --info-text: oklch(0.5 0.16 245);
101
+ --info-soft: oklch(0.955 0.025 245);
102
+
103
+ /* The highlighter, for Mark and text selection */
104
+ --highlight: oklch(0.9 0.15 98);
105
+ --highlight-foreground: oklch(0.22 0.03 80);
106
+
107
+ --shadow-color: oklch(0.25 calc(var(--n-c) * 2) var(--neutral-hue));
72
108
  }
73
109
 
74
- .dark {
110
+ /* Dark: elevation is lightness, so raised and floating surfaces get lighter */
111
+ .dark,
112
+ .light .dark {
75
113
  color-scheme: dark;
76
114
 
77
- /* Surfaces: deep ink, not charcoal */
78
- --background: var(--tint-hue) 20% 8%;
79
- --foreground: var(--ink-hue) 22% 92%;
80
- --surface: var(--tint-hue) 17% 11%;
81
- --card: var(--tint-hue) 17% 11%;
82
- --card-foreground: var(--ink-hue) 22% 92%;
83
- --popover: var(--tint-hue) 17% 12.5%;
84
- --popover-foreground: var(--ink-hue) 22% 92%;
85
- --muted: var(--tint-hue) 14% 16%;
86
- --muted-foreground: var(--ink-hue) 10% 66%;
87
-
88
- /* Light brand steps with dark text on them; themes usually tune these */
89
- --primary: var(--brand-300);
90
- --primary-foreground: var(--brand-950);
91
- --brand: var(--brand-300);
92
- --brand-foreground: var(--brand-950);
93
- --highlight: var(--highlight-300);
94
- --highlight-foreground: var(--highlight-950);
95
- --destructive: 2 62% 67%;
96
- --destructive-foreground: 2 70% 14%;
97
-
98
- --success: 150 46% 58%;
99
- --success-foreground: 152 60% 10%;
100
- --warning: 30 88% 62%;
101
- --warning-foreground: 24 80% 12%;
102
- --info: 214 85% 70%;
103
- --info-foreground: 220 60% 14%;
104
-
105
- --border: var(--tint-hue) 14% 18%;
106
- --input: var(--tint-hue) 13% 25%;
107
- --ring: var(--brand-400);
108
-
109
- --shadow-color: var(--tint-hue) 40% 2%;
115
+ --canvas: oklch(0.155 var(--n-c) var(--neutral-hue));
116
+ --surface: oklch(0.205 var(--n-c) var(--neutral-hue));
117
+ --surface-inset: oklch(0.25 var(--n-c) var(--neutral-hue));
118
+ --overlay: oklch(0.255 var(--n-c) var(--neutral-hue));
119
+
120
+ --ink: oklch(0.965 calc(var(--n-c) * 0.5) var(--neutral-hue));
121
+ --ink-secondary: oklch(0.75 var(--n-c) var(--neutral-hue));
122
+ --ink-tertiary: oklch(0.52 var(--n-c) var(--neutral-hue));
123
+
124
+ --separator: oklch(0.31 var(--n-c) var(--neutral-hue));
125
+ --border-control: oklch(0.57 var(--n-c) var(--neutral-hue));
126
+
127
+ --fill: oklch(0.6 calc(var(--n-c) * 2) var(--neutral-hue) / 0.16);
128
+ --fill-hover: oklch(0.6 calc(var(--n-c) * 2) var(--neutral-hue) / 0.22);
129
+ --fill-strong: oklch(0.6 calc(var(--n-c) * 2) var(--neutral-hue) / 0.3);
130
+ --scrim: oklch(0 0 0 / 0.5);
131
+
132
+ --accent: var(--accent-base);
133
+ --accent-pressed: oklch(
134
+ calc(var(--accent-l) - 0.06) var(--a-c) var(--accent-hue)
135
+ );
136
+ --accent-foreground: oklch(1 0 0);
137
+ --accent-text: oklch(0.77 min(var(--a-c), 0.14) var(--accent-hue));
138
+ --accent-soft: oklch(0.29 calc(var(--a-c) * 0.4) var(--accent-hue));
139
+ --accent-soft-hover: oklch(0.33 calc(var(--a-c) * 0.45) var(--accent-hue));
140
+ --focus: var(--accent-text);
141
+
142
+ --success-text: oklch(0.78 0.13 150);
143
+ --success-soft: oklch(0.29 0.05 150);
144
+ --warning-text: oklch(0.8 0.12 65);
145
+ --warning-soft: oklch(0.29 0.05 60);
146
+ --danger-text: oklch(0.77 0.13 25);
147
+ --danger-soft: oklch(0.29 0.06 25);
148
+ --info-text: oklch(0.77 0.11 245);
149
+ --info-soft: oklch(0.29 0.05 245);
150
+
151
+ --highlight: oklch(0.85 0.15 95);
152
+
153
+ --shadow-color: oklch(0.08 var(--n-c) var(--neutral-hue));
110
154
  }
111
155
 
112
156
  /*
113
- * Printed pages (documents, invoices, resumes) are paper, not app: plain neutral
114
- * greys and a print font, so the preview matches the download.
157
+ * An accent-colored surface, like a promo card. Inside it the roles point at
158
+ * white, so text and every button variant stay legible without special cases.
115
159
  */
116
- .force-light-mode,
117
- .dark .force-light-mode {
118
- --background: 0 0% 100%;
119
- --foreground: 0 0% 3.9%;
120
- --surface: 0 0% 98%;
121
- --card-foreground: 0 0% 3.9%;
122
- --popover-foreground: 0 0% 3.9%;
123
- --muted: 0 0% 96.1%;
124
- --muted-foreground: 0 0% 45.1%;
125
- --border: 0 0% 89.8%;
126
- --input: 0 0% 89.8%;
127
-
128
- font-family: Helvetica, Arial, sans-serif;
160
+ .surface-accent {
161
+ --surface: oklch(calc(var(--accent-l) - 0.05) var(--a-c) var(--accent-hue));
162
+ --surface-inset: oklch(0 0 0 / 0.12);
163
+ --overlay: var(--surface);
164
+ --ink: oklch(1 0 0);
165
+ --ink-secondary: oklch(1 0 0 / 0.88);
166
+ --ink-tertiary: oklch(1 0 0 / 0.6);
167
+ --separator: oklch(1 0 0 / 0.25);
168
+ --border-control: oklch(1 0 0 / 0.7);
169
+ --fill: oklch(0 0 0 / 0.12);
170
+ --fill-hover: oklch(0 0 0 / 0.18);
171
+ --fill-strong: oklch(0 0 0 / 0.24);
172
+ --accent: oklch(1 0 0);
173
+ --accent-pressed: oklch(0.92 0 0);
174
+ --accent-foreground: var(--surface);
175
+ --accent-text: oklch(1 0 0);
176
+ --accent-soft: oklch(0 0 0 / 0.12);
177
+ --accent-soft-hover: oklch(0 0 0 / 0.18);
178
+ --focus: oklch(1 0 0);
179
+ }
180
+
181
+ /* More contrast on request: solid lines, darker secondary text */
182
+ @media (prefers-contrast: more) {
183
+ :root,
184
+ .light {
185
+ --ink-secondary: oklch(0.36 calc(var(--n-c) * 1.5) var(--neutral-hue));
186
+ --separator: oklch(0.7 var(--n-c) var(--neutral-hue));
187
+ --border-control: oklch(0.45 var(--n-c) var(--neutral-hue));
188
+ }
189
+ .dark {
190
+ --ink-secondary: oklch(0.85 var(--n-c) var(--neutral-hue));
191
+ --separator: oklch(0.5 var(--n-c) var(--neutral-hue));
192
+ --border-control: oklch(0.72 var(--n-c) var(--neutral-hue));
193
+ }
194
+ }
195
+
196
+ /*
197
+ * Density: one set of sizes for a desk, one for touch. Regular on a mouse,
198
+ * comfortable on a touch screen; `data-density` on any element overrides it.
199
+ * Type is in rem, so it follows the user's text size.
200
+ */
201
+ :root,
202
+ [data-density='regular'] {
203
+ --type-title-1: 1.75rem;
204
+ --type-title-2: 1.25rem;
205
+ --type-title-3: 1.0625rem;
206
+ --type-body: 0.9375rem;
207
+ --type-subhead: 0.875rem;
208
+ --type-caption: 0.75rem;
209
+
210
+ --control-sm: 1.75rem;
211
+ --control-md: 2.25rem;
212
+ --control-lg: 2.75rem;
213
+ --row: 2.75rem;
214
+ --inset: 1rem;
215
+ --radius-control: calc(9px * var(--radius-scale));
216
+ --radius-card: calc(14px * var(--radius-scale));
217
+ }
218
+
219
+ @media (pointer: coarse) {
220
+ :root:not([data-density]) {
221
+ --type-title-1: 2.125rem;
222
+ --type-title-2: 1.375rem;
223
+ --type-title-3: 1.25rem;
224
+ --type-body: 1.0625rem;
225
+ --type-subhead: 0.9375rem;
226
+ --type-caption: 0.8125rem;
227
+
228
+ --control-sm: 2rem;
229
+ --control-md: 2.75rem;
230
+ --control-lg: 3.25rem;
231
+ --row: 3rem;
232
+ --inset: 1rem;
233
+ --radius-control: calc(12px * var(--radius-scale));
234
+ --radius-card: calc(18px * var(--radius-scale));
235
+ }
236
+ }
237
+
238
+ [data-density='compact'] {
239
+ --type-title-1: 1.5rem;
240
+ --type-title-2: 1.125rem;
241
+ --type-title-3: 0.9375rem;
242
+ --type-body: 0.875rem;
243
+ --type-subhead: 0.8125rem;
244
+ --type-caption: 0.75rem;
245
+
246
+ --control-sm: 1.5rem;
247
+ --control-md: 2rem;
248
+ --control-lg: 2.5rem;
249
+ --row: 2.25rem;
250
+ --inset: 0.75rem;
251
+ --radius-control: calc(7px * var(--radius-scale));
252
+ --radius-card: calc(10px * var(--radius-scale));
253
+ }
254
+
255
+ [data-density='comfortable'] {
256
+ --type-title-1: 2.125rem;
257
+ --type-title-2: 1.375rem;
258
+ --type-title-3: 1.25rem;
259
+ --type-body: 1.0625rem;
260
+ --type-subhead: 0.9375rem;
261
+ --type-caption: 0.8125rem;
262
+
263
+ --control-sm: 2rem;
264
+ --control-md: 2.75rem;
265
+ --control-lg: 3.25rem;
266
+ --row: 3rem;
267
+ --inset: 1rem;
268
+ --radius-control: calc(12px * var(--radius-scale));
269
+ --radius-card: calc(18px * var(--radius-scale));
270
+ }
271
+
272
+ :root {
273
+ /* Floating panels and sheets keep their shape at every density */
274
+ --radius-panel: calc(14px * var(--radius-scale));
275
+ --panel-inset: 0.3125rem;
276
+ --radius-item: max(calc(var(--radius-panel) - var(--panel-inset) - 1px), 4px);
277
+ --radius-sheet: calc(22px * var(--radius-scale));
278
+
279
+ /*
280
+ * Motion. Springs from Apple's defaults as CSS curves: critically damped
281
+ * (no overshoot) for anything that appears, a little bounce only after a
282
+ * gesture. The JS versions for Motion are in foundations/motion.ts.
283
+ */
284
+ --ease-spring: linear(
285
+ 0,
286
+ 0.0454,
287
+ 0.1465,
288
+ 0.2678,
289
+ 0.3895,
290
+ 0.5015,
291
+ 0.5993,
292
+ 0.6819,
293
+ 0.75,
294
+ 0.8052,
295
+ 0.8493,
296
+ 0.884,
297
+ 0.9113,
298
+ 0.9324,
299
+ 0.9487,
300
+ 0.9612,
301
+ 0.9707,
302
+ 0.978,
303
+ 0.9835,
304
+ 0.9877,
305
+ 0.9908,
306
+ 0.9931,
307
+ 0.9949,
308
+ 0.9962,
309
+ 1
310
+ );
311
+ --duration-spring: 450ms;
312
+ --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
313
+ --duration-press: 100ms;
314
+ --duration-fast: 150ms;
315
+ --duration-base: 250ms;
129
316
  }
@@ -1,62 +0,0 @@
1
- /*
2
- * Eloovor theme: "ink and highlighter".
3
- *
4
- * The logo violet does the work of a pen: primary actions, focus,
5
- * selection. Neutrals are that same ink, thinned, so greys lean slightly
6
- * violet instead of reading as dead grey. Marigold is the highlighter: a
7
- * fill for the few things that should jump out, like matched keywords or
8
- * Elo's suggestions. It is never used for text on white.
9
- *
10
- * Import after tokens.css. Fonts are loaded by the app
11
- * (@fontsource-variable/figtree, @fontsource-variable/bricolage-grotesque).
12
- */
13
-
14
- :root,
15
- .force-light-mode,
16
- .dark .force-light-mode {
17
- --tint-hue: 252;
18
- --ink-hue: 255;
19
-
20
- --font-sans:
21
- 'Figtree Variable', ui-sans-serif, system-ui, -apple-system, 'Segoe UI',
22
- sans-serif;
23
- --font-display:
24
- 'Bricolage Grotesque Variable', 'Figtree Variable', ui-sans-serif,
25
- system-ui, sans-serif;
26
-
27
- /* Brand ramp, built around the logo violet #574a7d (brand-600) */
28
- --brand-50: 255 33% 97%;
29
- --brand-100: 255 33% 94%;
30
- --brand-200: 255 30% 87%;
31
- --brand-300: 255 29% 77%;
32
- --brand-400: 255 26% 64%;
33
- --brand-500: 255 24% 51%;
34
- --brand-600: 255 26% 39%;
35
- --brand-700: 255 28% 32%;
36
- --brand-800: 255 30% 25%;
37
- --brand-900: 255 32% 19%;
38
- --brand-950: 255 36% 12%;
39
-
40
- /* Highlight ramp: marigold */
41
- --highlight-50: 46 100% 96%;
42
- --highlight-100: 45 97% 89%;
43
- --highlight-200: 44 95% 79%;
44
- --highlight-300: 42 93% 68%;
45
- --highlight-400: 40 91% 58%;
46
- --highlight-500: 37 88% 50%;
47
- --highlight-600: 33 85% 42%;
48
- --highlight-700: 29 80% 35%;
49
- --highlight-800: 26 70% 29%;
50
- --highlight-900: 24 64% 23%;
51
- --highlight-950: 22 70% 12%;
52
- }
53
-
54
- .dark {
55
- --tint-hue: 255;
56
- --ink-hue: 250;
57
-
58
- --primary: 255 48% 78%; /* lavender, dark text on it */
59
- --brand: 255 44% 74%;
60
- --highlight: 42 86% 62%;
61
- --ring: 255 48% 72%;
62
- }