@sckyzo/cosmos-theme 0.1.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/CHANGELOG.md CHANGED
@@ -5,6 +5,23 @@ All notable changes to `@sckyzo/cosmos-theme` are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.3.0](https://github.com/SckyzO/cosmos/compare/@sckyzo/cosmos-theme@0.2.0...@sckyzo/cosmos-theme@0.3.0) (2026-10-27)
9
+
10
+
11
+ ### Features
12
+
13
+ * **theme:** always emit the colors the charts read ([38fed2a](https://github.com/SckyzO/cosmos/commit/38fed2a70a6f9729b856f8911cdcd104d360867f))
14
+
15
+ ## [0.2.0](https://github.com/SckyzO/cosmos/compare/@sckyzo/cosmos-theme@0.1.0...@sckyzo/cosmos-theme@0.2.0) (2026-10-21)
16
+
17
+
18
+ ### Features
19
+
20
+ * **theme:** add a data-font setting with five more fonts ([6135b66](https://github.com/SckyzO/cosmos/commit/6135b6681f669bdcd178b113fb7b0cbf82aef5d0)), closes [#157](https://github.com/SckyzO/cosmos/issues/157)
21
+ * **theme:** add a data-text-size setting ([cd6ed5c](https://github.com/SckyzO/cosmos/commit/cd6ed5c07b15a9df12b56a3638ff0ca477cf1392)), closes [#157](https://github.com/SckyzO/cosmos/issues/157)
22
+ * **theme:** add syntax highlighting tokens ([8589ef1](https://github.com/SckyzO/cosmos/commit/8589ef1434fafdd20e7ae042372ff89a534a30f3))
23
+ * **theme:** add the text-2xs and text-3xs sizes ([57decb3](https://github.com/SckyzO/cosmos/commit/57decb3bc301ec65ce9966c84b8412cd9791b32a)), closes [#157](https://github.com/SckyzO/cosmos/issues/157)
24
+
8
25
  ## [0.1.0](https://github.com/SckyzO/cosmos/compare/@sckyzo/cosmos-theme@0.0.2...@sckyzo/cosmos-theme@0.1.0) (2026-10-20)
9
26
 
10
27
 
package/README.md CHANGED
@@ -4,13 +4,14 @@
4
4
 
5
5
  Cosmos design tokens — CSS only.
6
6
 
7
- Provides three CSS files consumable by any framework:
7
+ Provides CSS files consumable by any framework:
8
8
 
9
- | Import | Purpose |
10
- | ------------------------------- | ------------------------------------------------------------------------------ |
11
- | `@sckyzo/cosmos-theme/tailwind` | Tailwind 4 `@theme` block (palette, fonts, shadows, brand colors) |
12
- | `@sckyzo/cosmos-theme/vars` | Universal CSS custom properties (bg, text, border, accent), themes and accents |
13
- | `@sckyzo/cosmos-theme/fonts` | `@fontsource` font imports (Outfit + JetBrains Mono) |
9
+ | Import | Purpose |
10
+ | ----------------------------------- | ------------------------------------------------------------------------------ |
11
+ | `@sckyzo/cosmos-theme/tailwind` | Tailwind 4 `@theme` block (palette, fonts, shadows, brand colors) |
12
+ | `@sckyzo/cosmos-theme/vars` | Universal CSS custom properties (bg, text, border, accent), themes and accents |
13
+ | `@sckyzo/cosmos-theme/fonts` | `@fontsource` font imports (Outfit + JetBrains Mono) |
14
+ | `@sckyzo/cosmos-theme/fonts/<name>` | One variable font for `data-font`: `inter`, `plex`, `montserrat`, `open-sans` |
14
15
 
15
16
  ## Usage
16
17
 
@@ -71,18 +72,81 @@ Every theme × accent pair meets WCAG AA for body text, links and white text on
71
72
  the accent. `pnpm --filter @sckyzo/cosmos-theme test` runs the contrast check
72
73
  (`scripts/check-contrast.mjs`) against the CSS files.
73
74
 
75
+ ## Focus indicator
76
+
77
+ Four Tailwind utilities, shipped with `@sckyzo/cosmos-theme/tailwind`, give
78
+ every control the same keyboard focus look: a **solid accent indicator** plus
79
+ the `--shadow-focus-ring` halo (12% of the accent). The halo alone is too faint
80
+ to count: the solid part is what reaches 3:1 against the surface (WCAG 1.4.11;
81
+ `brand-500` is checked on every theme × accent pair by `pnpm test`).
82
+
83
+ | Utility | Paints | For |
84
+ | --------------------------- | --------------------------- | --------------------------------------------------------------------- |
85
+ | `focus-indicator` | 2px `brand-500` ring + halo | buttons, links, toggles, tabs, menu items, cards… |
86
+ | `field-focus` | `brand-500` border + halo | text inputs, textarea, select and their wrappers |
87
+ | `field-focus-error` | `red-500` border + red halo | the same fields in error (`--shadow-focus-ring-error`) |
88
+ | `focus-indicator-on-accent` | 2px white ring, no halo | a control filled with `brand-500`, where the accent ring would vanish |
89
+
90
+ They hold no state: put the variant that fits in front of them.
91
+
92
+ ```html
93
+ <button class="focus-visible:focus-indicator">Save</button>
94
+ <input class="focus:field-focus border border-gray-200" />
95
+ <div class="focus-within:field-focus border border-gray-200"><input /></div>
96
+ <!-- a control drawn by a sibling of a visually hidden input -->
97
+ <input type="checkbox" class="peer sr-only" />
98
+ <span class="peer-focus-visible:focus-indicator"></span>
99
+ ```
100
+
101
+ There is no ring offset. Add `ring-inset` where an ancestor clips with
102
+ `overflow-hidden`, or a neighbour covers the outer ring. The utilities use
103
+ `outline-hidden`, so the indicator survives forced-colors mode, where box
104
+ shadows are not painted.
105
+
106
+ ## Fonts & text size
107
+
108
+ Two more settings on `<html>`, both optional:
109
+
110
+ - **Font**: a `data-font` attribute that swaps `--font-sans`. Leave it off for
111
+ Outfit. The others are `inter`, `plex` (IBM Plex Sans), `montserrat`,
112
+ `open-sans`, and `system`, the platform's UI font. Each one loads from its own
113
+ subpath, so import only the fonts you offer; `system` loads nothing. The mono
114
+ font stays JetBrains Mono.
115
+
116
+ ```css
117
+ @import '@sckyzo/cosmos-theme/fonts'; /* Outfit + JetBrains Mono */
118
+ @import '@sckyzo/cosmos-theme/fonts/inter';
119
+ ```
120
+
121
+ - **Text size**: a `data-text-size` attribute, `sm`, `lg` or `xl`, that scales
122
+ every `text-*` size to 93.75%, 112.5% or 125%. Leave it off for 100%. Only the
123
+ text grows: spacing keeps its `rem` values, so layouts keep their density.
124
+ Sizes written in pixels, such as `text-[13px]`, don't scale; the `text-*`
125
+ scale includes `text-2xs` (11px) and `text-3xs` (10px) for small labels.
126
+
127
+ ```html
128
+ <html class="sand" data-accent="teal" data-font="inter" data-text-size="lg"></html>
129
+ ```
130
+
131
+ Both attributes also work on an element below `<html>`, for one part of a page.
132
+
74
133
  ## Tokens
75
134
 
76
135
  See [`css/tailwind-tokens.css`](css/tailwind-tokens.css) for the full source.
77
136
 
137
+ The brand palette, the status colors and the semantic colors are always in the CSS (`@theme static`): Tailwind
138
+ only emits a theme variable that a utility uses, and the charts read these as plain `var(--color-…)`.
139
+
78
140
  Categories:
79
141
 
80
142
  - **Brand palette** : `--color-brand-25` to `--color-brand-950`
143
+ - **Chart series** : `--color-chart-2` to `--color-chart-8`, the series colors after the accent (the first series is `--color-brand-500`). Each accent lists its own seven, skipping the hues that look like it
81
144
  - **Semantic colors** : `--color-success-*`, `--color-error-*`, `--color-warning-*`
82
145
  - **Status** : `--color-status-{ok,warn,crit,unknown}`
83
- - **Typography** : `--font-sans` (Outfit), `--font-mono` (JetBrains Mono)
84
- - **Shadows** : `--shadow-card`, `--shadow-card-hover`
146
+ - **Typography** : `--font-sans` (Outfit), `--font-mono` (JetBrains Mono), `--text-2xs` (11px), `--text-3xs` (10px)
147
+ - **Shadows** : `--shadow-card`, `--shadow-card-hover`, `--shadow-focus-ring` and `--shadow-focus-ring-error` (the focus halos)
85
148
  - **Layout vars** (theme-aware): `--color-bg-{base,panel,elevated}`, `--color-text-{base,primary,secondary,muted}`, `--color-border`, `--color-accent`
149
+ - **Code highlighting** (theme-aware): `--color-code-foreground` and `--color-code-token-*` (keyword, string, comment, constant, function, parameter, string-expression, punctuation, link, inserted, deleted, changed). The names match Shiki's CSS variables theme with `variablePrefix: '--color-code-'`. Each one keeps 4.5:1 on CodeBlock's background in all four themes (checked by `pnpm test`).
86
150
 
87
151
  ## License
88
152
 
package/css/css-vars.css CHANGED
@@ -9,8 +9,10 @@
9
9
  * dark .dark — neutral black/gray dark theme
10
10
  * deep-blue .deep-blue — navy dark theme
11
11
  * accent → data-accent="indigo" etc. — no attribute means blue
12
+ * font → data-font="inter" etc. — no attribute means Outfit
13
+ * text → data-text-size="sm" | "lg" | "xl" — no attribute means normal
12
14
  *
13
- * <html class="sand" data-accent="teal">
15
+ * <html class="sand" data-accent="teal" data-font="inter" data-text-size="lg">
14
16
  *
15
17
  * Themes and accents redefine Tailwind's palette variables (--color-white,
16
18
  * --color-gray-*, --color-brand-*), so components styled with bg-white /
@@ -35,10 +37,40 @@
35
37
  --color-text-base: #101828;
36
38
  --color-text-primary: #101828;
37
39
  --color-text-secondary: #667085;
38
- --color-text-muted: #98a2b3;
40
+ --color-text-muted: #677185;
41
+
42
+ /* ── Code ──────────────────────────────────────────────────────────────── */
43
+ /* Syntax highlighting on CodeBlock (gray-50 / gray-900), read by Shiki's
44
+ * CSS variables theme with variablePrefix '--color-code-'. */
45
+ --color-code-foreground: oklch(27.8% 0.033 256.848);
46
+ --color-code-token-keyword: #6d28d9;
47
+ --color-code-token-string: #15803d;
48
+ --color-code-token-comment: #5b6472;
49
+ --color-code-token-constant: #b45309;
50
+ --color-code-token-function: #1d4ed8;
51
+ --color-code-token-parameter: #c2410c;
52
+ --color-code-token-string-expression: #0f766e;
53
+ --color-code-token-punctuation: #4b5563;
54
+ --color-code-token-link: #1d4ed8;
55
+ --color-code-token-inserted: #15803d;
56
+ --color-code-token-deleted: #b91c1c;
57
+ --color-code-token-changed: #b45309;
39
58
 
40
59
  /* ── Accent ────────────────────────────────────────────────────────────── */
41
60
  --color-accent: var(--color-brand-500);
61
+
62
+ /* ── Chart series ──────────────────────────────────────────────────────── */
63
+ /* Series 2 to 8 of the charts, after the accent (series 1 is
64
+ * --color-brand-500, which the accents redefine). Each accent below lists its
65
+ * own seven: the hues that look like it are skipped and a reserve takes
66
+ * their place, so no series is mistaken for the accent. */
67
+ --color-chart-2: #06b6d4;
68
+ --color-chart-3: #8b5cf6;
69
+ --color-chart-4: #ec4899;
70
+ --color-chart-5: #64748b;
71
+ --color-chart-6: #f97316;
72
+ --color-chart-7: #14b8a6;
73
+ --color-chart-8: #84cc16;
42
74
  }
43
75
 
44
76
  /* ── Themes ────────────────────────────────────────────────────────────── */
@@ -53,7 +85,14 @@
53
85
  --color-text-base: #1e1711;
54
86
  --color-text-primary: #1e1711;
55
87
  --color-text-secondary: #746656;
56
- --color-text-muted: #a39684;
88
+ --color-text-muted: #7a6c5b;
89
+
90
+ /* ── Code ──────────────────────────────────────────────────────────────── */
91
+ /* Syntax highlighting on CodeBlock (gray-50 / gray-900), read by Shiki's
92
+ * CSS variables theme with variablePrefix '--color-code-'. */
93
+ --color-code-foreground: oklch(27.5% 0.02 60);
94
+ --color-code-token-comment: #6e5f4f;
95
+ --color-code-token-punctuation: #57493c;
57
96
 
58
97
  /* Warm gray scale — same lightness steps as Tailwind gray, beige hue. */
59
98
  --color-white: oklch(99.3% 0.008 85);
@@ -84,6 +123,23 @@
84
123
  --color-text-secondary: #a3a3a3;
85
124
  --color-text-muted: #8f8f8f;
86
125
 
126
+ /* ── Code ──────────────────────────────────────────────────────────────── */
127
+ /* Syntax highlighting on CodeBlock (gray-50 / gray-900), read by Shiki's
128
+ * CSS variables theme with variablePrefix '--color-code-'. */
129
+ --color-code-foreground: #e5e5e5;
130
+ --color-code-token-comment: #8f8f8f;
131
+ --color-code-token-punctuation: #a3a3a3;
132
+ --color-code-token-keyword: #c4b5fd;
133
+ --color-code-token-string: #86efac;
134
+ --color-code-token-constant: #fcd34d;
135
+ --color-code-token-function: #93c5fd;
136
+ --color-code-token-parameter: #fdba74;
137
+ --color-code-token-string-expression: #5eead4;
138
+ --color-code-token-link: #93c5fd;
139
+ --color-code-token-inserted: #86efac;
140
+ --color-code-token-deleted: #fca5a5;
141
+ --color-code-token-changed: #fcd34d;
142
+
87
143
  /* Neutral gray scale, softened near-black (no pure #000). */
88
144
  --color-gray-50: #fafafa;
89
145
  --color-gray-100: #f5f5f5;
@@ -112,7 +168,24 @@
112
168
  --color-text-base: #e4e7ec;
113
169
  --color-text-primary: #fff;
114
170
  --color-text-secondary: #98a2b3;
115
- --color-text-muted: #667085;
171
+ --color-text-muted: #8792a5;
172
+
173
+ /* ── Code ──────────────────────────────────────────────────────────────── */
174
+ /* Syntax highlighting on CodeBlock (gray-50 / gray-900), read by Shiki's
175
+ * CSS variables theme with variablePrefix '--color-code-'. */
176
+ --color-code-foreground: oklch(92.8% 0.006 264.531);
177
+ --color-code-token-comment: #98a2b3;
178
+ --color-code-token-punctuation: #b8c0cc;
179
+ --color-code-token-keyword: #c4b5fd;
180
+ --color-code-token-string: #86efac;
181
+ --color-code-token-constant: #fcd34d;
182
+ --color-code-token-function: #93c5fd;
183
+ --color-code-token-parameter: #fdba74;
184
+ --color-code-token-string-expression: #5eead4;
185
+ --color-code-token-link: #93c5fd;
186
+ --color-code-token-inserted: #86efac;
187
+ --color-code-token-deleted: #fca5a5;
188
+ --color-code-token-changed: #fcd34d;
116
189
  }
117
190
 
118
191
  /* ── Accents ───────────────────────────────────────────────────────────────
@@ -132,6 +205,13 @@
132
205
  --color-brand-800: #2f25ab;
133
206
  --color-brand-900: #24257a;
134
207
  --color-brand-950: #160b63;
208
+ --color-chart-2: #06b6d4;
209
+ --color-chart-3: #ec4899;
210
+ --color-chart-4: #64748b;
211
+ --color-chart-5: #f97316;
212
+ --color-chart-6: #14b8a6;
213
+ --color-chart-7: #84cc16;
214
+ --color-chart-8: #d946ef;
135
215
  }
136
216
 
137
217
  [data-accent='violet'] {
@@ -141,12 +221,19 @@
141
221
  --color-brand-200: #d8ceff;
142
222
  --color-brand-300: #c0abff;
143
223
  --color-brand-400: #a57eff;
144
- --color-brand-500: #8745f1;
224
+ --color-brand-500: #8a49f5;
145
225
  --color-brand-600: #752cdb;
146
226
  --color-brand-700: #6508c6;
147
227
  --color-brand-800: #5200a4;
148
228
  --color-brand-900: #3f097e;
149
229
  --color-brand-950: #2b005b;
230
+ --color-chart-2: #06b6d4;
231
+ --color-chart-3: #ec4899;
232
+ --color-chart-4: #64748b;
233
+ --color-chart-5: #f97316;
234
+ --color-chart-6: #14b8a6;
235
+ --color-chart-7: #84cc16;
236
+ --color-chart-8: #3b82f6;
150
237
  }
151
238
 
152
239
  [data-accent='purple'] {
@@ -156,7 +243,7 @@
156
243
  --color-brand-200: #f0c2ff;
157
244
  --color-brand-300: #e696ff;
158
245
  --color-brand-400: #d55bf8;
159
- --color-brand-500: #ad2ecf;
246
+ --color-brand-500: #b234d4;
160
247
  --color-brand-600: #990aba;
161
248
  --color-brand-700: #81019e;
162
249
  --color-brand-800: #690181;
@@ -171,12 +258,19 @@
171
258
  --color-brand-200: #ffc3d2;
172
259
  --color-brand-300: #ff96b4;
173
260
  --color-brand-400: #ff4e90;
174
- --color-brand-500: #d4166d;
261
+ --color-brand-500: #d81e70;
175
262
  --color-brand-600: #b8005c;
176
263
  --color-brand-700: #9b004c;
177
264
  --color-brand-800: #7f013d;
178
265
  --color-brand-900: #64002f;
179
266
  --color-brand-950: #45001e;
267
+ --color-chart-2: #06b6d4;
268
+ --color-chart-3: #8b5cf6;
269
+ --color-chart-4: #64748b;
270
+ --color-chart-5: #f97316;
271
+ --color-chart-6: #14b8a6;
272
+ --color-chart-7: #84cc16;
273
+ --color-chart-8: #3b82f6;
180
274
  }
181
275
 
182
276
  [data-accent='teal'] {
@@ -192,6 +286,13 @@
192
286
  --color-brand-800: #024a43;
193
287
  --color-brand-900: #003833;
194
288
  --color-brand-950: #002723;
289
+ --color-chart-2: #8b5cf6;
290
+ --color-chart-3: #ec4899;
291
+ --color-chart-4: #64748b;
292
+ --color-chart-5: #f97316;
293
+ --color-chart-6: #84cc16;
294
+ --color-chart-7: #3b82f6;
295
+ --color-chart-8: #d946ef;
195
296
  }
196
297
 
197
298
  [data-accent='cyan'] {
@@ -207,6 +308,13 @@
207
308
  --color-brand-800: #014759;
208
309
  --color-brand-900: #003645;
209
310
  --color-brand-950: #002631;
311
+ --color-chart-2: #8b5cf6;
312
+ --color-chart-3: #ec4899;
313
+ --color-chart-4: #64748b;
314
+ --color-chart-5: #f97316;
315
+ --color-chart-6: #84cc16;
316
+ --color-chart-7: #3b82f6;
317
+ --color-chart-8: #d946ef;
210
318
  }
211
319
 
212
320
  [data-accent='graphite'] {
@@ -222,6 +330,13 @@
222
330
  --color-brand-800: #3c414b;
223
331
  --color-brand-900: #2e3138;
224
332
  --color-brand-950: #1f2228;
333
+ --color-chart-2: #06b6d4;
334
+ --color-chart-3: #8b5cf6;
335
+ --color-chart-4: #ec4899;
336
+ --color-chart-5: #f97316;
337
+ --color-chart-6: #14b8a6;
338
+ --color-chart-7: #84cc16;
339
+ --color-chart-8: #3b82f6;
225
340
  }
226
341
 
227
342
  [data-accent='red'] {
@@ -231,7 +346,7 @@
231
346
  --color-brand-200: #ffc6c0;
232
347
  --color-brand-300: #ff9b94;
233
348
  --color-brand-400: #ff5a57;
234
- --color-brand-500: #db192a;
349
+ --color-brand-500: #de1f2d;
235
350
  --color-brand-600: #be021e;
236
351
  --color-brand-700: #a00118;
237
352
  --color-brand-800: #830011;
@@ -252,6 +367,13 @@
252
367
  --color-brand-800: #712b00;
253
368
  --color-brand-900: #592000;
254
369
  --color-brand-950: #3d1300;
370
+ --color-chart-2: #06b6d4;
371
+ --color-chart-3: #8b5cf6;
372
+ --color-chart-4: #ec4899;
373
+ --color-chart-5: #64748b;
374
+ --color-chart-6: #14b8a6;
375
+ --color-chart-7: #84cc16;
376
+ --color-chart-8: #3b82f6;
255
377
  }
256
378
 
257
379
  [data-accent='amber'] {
@@ -267,6 +389,13 @@
267
389
  --color-brand-800: #5d3900;
268
390
  --color-brand-900: #482b00;
269
391
  --color-brand-950: #321c00;
392
+ --color-chart-2: #06b6d4;
393
+ --color-chart-3: #8b5cf6;
394
+ --color-chart-4: #ec4899;
395
+ --color-chart-5: #64748b;
396
+ --color-chart-6: #14b8a6;
397
+ --color-chart-7: #84cc16;
398
+ --color-chart-8: #3b82f6;
270
399
  }
271
400
 
272
401
  [data-accent='green'] {
@@ -284,6 +413,93 @@
284
413
  --color-brand-950: #00280d;
285
414
  }
286
415
 
416
+ /* ── Fonts ─────────────────────────────────────────────────────────────────
417
+ * data-font swaps the sans-serif family; no attribute keeps Outfit. Each
418
+ * font loads from its own subpath (@sckyzo/cosmos-theme/fonts/inter, …), so
419
+ * an app only ships the ones it offers; `system` uses the platform's UI font
420
+ * and loads nothing. The mono font stays JetBrains Mono.
421
+ *
422
+ * font-family is set here too: plain text inherits the family <html>
423
+ * computed, so an element below <html> with data-font needs its own
424
+ * font-family to switch. In the base layer, so font-mono on the same element
425
+ * still wins. */
426
+ @layer base {
427
+ [data-font] {
428
+ font-family: var(--font-sans);
429
+ }
430
+ }
431
+
432
+ [data-font='inter'] {
433
+ --font-sans: 'Inter Variable', system-ui, sans-serif;
434
+ }
435
+
436
+ [data-font='plex'] {
437
+ --font-sans: 'IBM Plex Sans Variable', system-ui, sans-serif;
438
+ }
439
+
440
+ [data-font='montserrat'] {
441
+ --font-sans: 'Montserrat Variable', system-ui, sans-serif;
442
+ }
443
+
444
+ [data-font='open-sans'] {
445
+ --font-sans: 'Open Sans Variable', system-ui, sans-serif;
446
+ }
447
+
448
+ [data-font='system'] {
449
+ --font-sans: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
450
+ }
451
+
452
+ /* ── Text size ─────────────────────────────────────────────────────────────
453
+ * data-text-size scales every text-* size: sm 93.75%, lg 112.5%, xl 125%;
454
+ * no attribute keeps 100%. Spacing stays the same, so layouts keep their
455
+ * density, and line heights follow (they are ratios). Sizes written in
456
+ * pixels, such as text-[9px], don't scale.
457
+ *
458
+ * The sizes are declared again here, not scaled in the @theme block: a
459
+ * custom property resolves var() on the element that declares it, so a
460
+ * --text-sm declared on :root would ignore an attribute set below <html>.
461
+ * Same values as Tailwind's, plus text-2xs and text-3xs. */
462
+ [data-text-size] {
463
+ --text-3xs: calc(0.625rem * var(--cosmos-text-scale, 1));
464
+ --text-2xs: calc(0.6875rem * var(--cosmos-text-scale, 1));
465
+ --text-xs: calc(0.75rem * var(--cosmos-text-scale, 1));
466
+ --text-sm: calc(0.875rem * var(--cosmos-text-scale, 1));
467
+ --text-base: calc(1rem * var(--cosmos-text-scale, 1));
468
+ --text-lg: calc(1.125rem * var(--cosmos-text-scale, 1));
469
+ --text-xl: calc(1.25rem * var(--cosmos-text-scale, 1));
470
+ --text-2xl: calc(1.5rem * var(--cosmos-text-scale, 1));
471
+ --text-3xl: calc(1.875rem * var(--cosmos-text-scale, 1));
472
+ --text-4xl: calc(2.25rem * var(--cosmos-text-scale, 1));
473
+ --text-5xl: calc(3rem * var(--cosmos-text-scale, 1));
474
+ --text-6xl: calc(3.75rem * var(--cosmos-text-scale, 1));
475
+ --text-7xl: calc(4.5rem * var(--cosmos-text-scale, 1));
476
+ --text-8xl: calc(6rem * var(--cosmos-text-scale, 1));
477
+ --text-9xl: calc(8rem * var(--cosmos-text-scale, 1));
478
+ }
479
+
480
+ [data-text-size='sm'] {
481
+ --cosmos-text-scale: 0.9375;
482
+ }
483
+
484
+ [data-text-size='lg'] {
485
+ --cosmos-text-scale: 1.125;
486
+ }
487
+
488
+ [data-text-size='xl'] {
489
+ --cosmos-text-scale: 1.25;
490
+ }
491
+
492
+ /* Text without a text-* class: body under <html data-text-size>, or the
493
+ element carrying the attribute below <html>. Not <html> itself, whose
494
+ font size sets rem and would scale the spacing too. In the base layer, so
495
+ a text-* class on the same element still wins. */
496
+ @layer base {
497
+ [data-text-size] body,
498
+ [data-text-size]:not(:root) {
499
+ font-size: var(--text-base);
500
+ }
501
+ }
502
+
287
503
  /* Smooth theme switching transition — opt-in via .cosmos-bg-transition class
288
504
  on the app shell (not body, to avoid transitioning every DOM element). */
289
505
  .cosmos-bg-transition {
@@ -0,0 +1,2 @@
1
+ /* Font for data-font='inter': one variable file, weights 100–900. */
2
+ @import '@fontsource-variable/inter/wght.css';
@@ -0,0 +1,2 @@
1
+ /* Font for data-font='montserrat': one variable file, weights 100–900. */
2
+ @import '@fontsource-variable/montserrat/wght.css';
@@ -0,0 +1,2 @@
1
+ /* Font for data-font='open-sans': one variable file, weights 300–800. */
2
+ @import '@fontsource-variable/open-sans/wght.css';
@@ -0,0 +1,2 @@
1
+ /* Font for data-font='plex': one variable file, weights 100–700. */
2
+ @import '@fontsource-variable/ibm-plex-sans/wght.css';
package/css/fonts.css CHANGED
@@ -1,5 +1,6 @@
1
- /* Cosmos fonts — self-hosted via @fontsource (no internet required at runtime).
2
- Consumers must install @fontsource/outfit and @fontsource/jetbrains-mono as deps. */
1
+ /* Cosmos default fonts, Outfit and JetBrains Mono, self-hosted via @fontsource
2
+ (no network needed at runtime). The @fontsource packages are dependencies of
3
+ this package. The other data-font choices load from fonts/<name>.css. */
3
4
 
4
5
  @import '@fontsource/outfit/300.css';
5
6
  @import '@fontsource/outfit/400.css';
@@ -10,32 +10,23 @@
10
10
  */
11
11
 
12
12
  /* Tailwind dark variant: respond to the .dark / .deep-blue class set by the consumer's
13
- ThemeContext, instead of (or in addition to) prefers-color-scheme. */
14
- @custom-variant dark (&:is(.dark, .dark *, .deep-blue, .deep-blue *));
15
-
16
- @theme {
17
- /* ── Typography ─────────────────────────────────────────────────────────── */
18
- --font-sans: 'Outfit', system-ui, sans-serif;
19
- --font-mono: 'JetBrains Mono', monospace;
13
+ ThemeContext, instead of (or in addition to) prefers-color-scheme.
14
+ `:where()` keeps the variant at zero specificity, as Tailwind documents it: with `:is()` it
15
+ weighed as much as `focus:` or `hover:` and, emitted later, `dark:border-gray-700` beat
16
+ `focus:border-brand-500`, so form controls lost their focus border in the dark themes. */
17
+ @custom-variant dark (&:where(.dark, .dark *, .deep-blue, .deep-blue *));
20
18
 
19
+ /* The colors the charts read as plain CSS variables (var(--color-brand-500),
20
+ * var(--color-status-warn)…), with no utility behind them. Tailwind only emits
21
+ * a theme variable that a utility uses, so these are `static`: always in the
22
+ * CSS, whatever the app's classes are. */
23
+ @theme static {
21
24
  /* ── Status colors — health indicators, badges, alerts ─────────────────── */
22
25
  --color-status-ok: #10b981;
23
26
  --color-status-warn: #f59e0b;
24
27
  --color-status-crit: #ef4444;
25
28
  --color-status-unknown: #6b7280;
26
29
 
27
- /* ── Card shadows — work on both light and dark backgrounds ────────────── */
28
- --shadow-card: 0 1px 3px rgb(0 0 0 / 40%), 0 1px 2px rgb(0 0 0 / 30%);
29
- --shadow-card-hover: 0 10px 25px rgb(0 0 0 / 50%), 0 4px 6px rgb(0 0 0 / 20%);
30
-
31
- /* ── TailAdmin elevation shadows (theme.shadow-{xs,sm,md,lg,xl}) ───────── */
32
- --shadow-theme-xs: 0px 1px 2px 0px rgb(16 24 40 / 5%);
33
- --shadow-theme-sm: 0px 1px 3px 0px rgb(16 24 40 / 10%), 0px 1px 2px 0px rgb(16 24 40 / 6%);
34
- --shadow-theme-md: 0px 4px 8px -2px rgb(16 24 40 / 10%), 0px 2px 4px -2px rgb(16 24 40 / 6%);
35
- --shadow-theme-lg: 0px 12px 16px -4px rgb(16 24 40 / 8%), 0px 4px 6px -2px rgb(16 24 40 / 3%);
36
- --shadow-theme-xl: 0px 20px 24px -4px rgb(16 24 40 / 8%), 0px 8px 8px -4px rgb(16 24 40 / 3%);
37
- --shadow-focus-ring: 0px 0px 0px 4px color-mix(in srgb, var(--color-brand-500) 12%, transparent);
38
-
39
30
  /* ── Brand palette (TailAdmin blue) — [data-accent] in css-vars.css swaps it ── */
40
31
  --color-brand-25: #f2f7ff;
41
32
  --color-brand-50: #ecf3ff;
@@ -50,9 +41,6 @@
50
41
  --color-brand-900: #262e89;
51
42
  --color-brand-950: #161950;
52
43
 
53
- /* Dark gray aligned with Tailwind gray-900 — keeps SectionCard bg matching body bg */
54
- --color-gray-dark: #111827;
55
-
56
44
  /* ── Semantic colors ───────────────────────────────────────────────────── */
57
45
  --color-success-25: #f6fef9;
58
46
  --color-success-50: #ecfdf3;
@@ -71,8 +59,79 @@
71
59
  --color-warning-100: #fef0c7;
72
60
  --color-warning-500: #f79009;
73
61
  --color-warning-600: #dc6803;
62
+ }
63
+
64
+ @theme {
65
+ /* ── Typography ─────────────────────────────────────────────────────────── */
66
+ --font-sans: 'Outfit', system-ui, sans-serif;
67
+ --font-mono: 'JetBrains Mono', monospace;
68
+
69
+ /* Sizes under text-xs (12px) for dense labels: 11px and 10px. Without a
70
+ --line-height they set only font-size, like text-[11px] would, and the
71
+ text size setting in css-vars.css can scale them. */
72
+ --text-2xs: 0.6875rem;
73
+ --text-3xs: 0.625rem;
74
+
75
+ /* ── Card shadows — work on both light and dark backgrounds ────────────── */
76
+ --shadow-card: 0 1px 3px rgb(0 0 0 / 40%), 0 1px 2px rgb(0 0 0 / 30%);
77
+ --shadow-card-hover: 0 10px 25px rgb(0 0 0 / 50%), 0 4px 6px rgb(0 0 0 / 20%);
78
+
79
+ /* ── TailAdmin elevation shadows (theme.shadow-{xs,sm,md,lg,xl}) ───────── */
80
+ --shadow-theme-xs: 0px 1px 2px 0px rgb(16 24 40 / 5%);
81
+ --shadow-theme-sm: 0px 1px 3px 0px rgb(16 24 40 / 10%), 0px 1px 2px 0px rgb(16 24 40 / 6%);
82
+ --shadow-theme-md: 0px 4px 8px -2px rgb(16 24 40 / 10%), 0px 2px 4px -2px rgb(16 24 40 / 6%);
83
+ --shadow-theme-lg: 0px 12px 16px -4px rgb(16 24 40 / 8%), 0px 4px 6px -2px rgb(16 24 40 / 3%);
84
+ --shadow-theme-xl: 0px 20px 24px -4px rgb(16 24 40 / 8%), 0px 8px 8px -4px rgb(16 24 40 / 3%);
85
+ --shadow-focus-ring: 0px 0px 0px 4px color-mix(in srgb, var(--color-brand-500) 12%, transparent);
86
+ --shadow-focus-ring-error: 0 0 0 4px color-mix(in srgb, var(--color-red-500) 12%, transparent);
87
+
88
+ /* Dark gray aligned with Tailwind gray-900 — keeps SectionCard bg matching body bg */
89
+ --color-gray-dark: #111827;
74
90
 
75
91
  /* ── Bridges: aliases that pull values from CSS vars (defined in css-vars.css)
76
92
  so utilities like bg-accent-primary stay in sync with theme switcher. */
77
93
  --color-accent-primary: var(--color-accent);
78
94
  }
95
+
96
+ /* ── Focus indicator ───────────────────────────────────────────────────────
97
+ * One focus look for every control: a solid accent indicator plus the
98
+ * --shadow-focus-ring halo. The halo is 12% of the accent, too faint to be the
99
+ * only cue, so the solid part is what carries WCAG 1.4.11 (brand-500 reaches
100
+ * 3:1 on the surfaces of the four themes, see scripts/check-contrast.mjs).
101
+ *
102
+ * The utilities hold no state: put the variant that fits in front of them.
103
+ *
104
+ * focus-visible:focus-indicator a button, link, toggle, tab, menu item…:
105
+ * 2px accent ring + halo, keyboard focus only
106
+ * peer-focus-visible:…, group-focus-visible:…, focus-within:…
107
+ * the same, for a control drawn by a sibling
108
+ * or a wrapper (checkbox, radio, card)
109
+ * focus:field-focus a text field: accent border + halo
110
+ * focus-within:field-focus the same on a wrapper of the field
111
+ * focus:field-focus-error the field in error: red border + red halo
112
+ * focus-visible:focus-indicator-on-accent
113
+ * a control filled with brand-500 (the active
114
+ * segment, the selected day), where a brand
115
+ * ring would vanish: a 2px white ring, no halo
116
+ * (white on brand-500 is checked at 4.5:1)
117
+ *
118
+ * No ring offset. Add ring-inset where an ancestor clips (overflow-hidden) or a
119
+ * neighbour hides the outer ring.
120
+ *
121
+ * outline-hidden hides the browser outline but keeps it in forced-colors mode,
122
+ * where box-shadow is not painted: the indicator never disappears there. */
123
+ @utility focus-indicator {
124
+ @apply ring-brand-500 shadow-focus-ring ring-2 outline-hidden;
125
+ }
126
+
127
+ @utility field-focus {
128
+ @apply border-brand-500 shadow-focus-ring outline-hidden;
129
+ }
130
+
131
+ @utility field-focus-error {
132
+ @apply shadow-focus-ring-error border-red-500 outline-hidden;
133
+ }
134
+
135
+ @utility focus-indicator-on-accent {
136
+ @apply ring-2 ring-white outline-hidden;
137
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sckyzo/cosmos-theme",
3
- "version": "0.1.0",
4
- "description": "Tailwind 4 theme and design tokens for the Cosmos design system — palette, typography, radii and light/dark CSS variables.",
3
+ "version": "0.3.0",
4
+ "description": "Tailwind 4 theme and design tokens for the Cosmos design system — palette, typography, radii and CSS variables for its light, sand, dark and deep-blue themes.",
5
5
  "license": "MIT",
6
6
  "author": "SckyzO",
7
7
  "homepage": "https://github.com/SckyzO/cosmos/tree/main/packages/theme",
@@ -18,6 +18,10 @@
18
18
  "./tailwind": "./css/tailwind-tokens.css",
19
19
  "./vars": "./css/css-vars.css",
20
20
  "./fonts": "./css/fonts.css",
21
+ "./fonts/inter": "./css/fonts/inter.css",
22
+ "./fonts/plex": "./css/fonts/plex.css",
23
+ "./fonts/montserrat": "./css/fonts/montserrat.css",
24
+ "./fonts/open-sans": "./css/fonts/open-sans.css",
21
25
  "./package.json": "./package.json"
22
26
  },
23
27
  "files": [
@@ -34,6 +38,10 @@
34
38
  "build": "echo 'CSS-only package, no build step needed'"
35
39
  },
36
40
  "dependencies": {
41
+ "@fontsource-variable/ibm-plex-sans": "^5.3.0",
42
+ "@fontsource-variable/inter": "^5.3.0",
43
+ "@fontsource-variable/montserrat": "^5.3.0",
44
+ "@fontsource-variable/open-sans": "^5.3.0",
37
45
  "@fontsource/jetbrains-mono": "^5.3.0",
38
46
  "@fontsource/outfit": "^5.3.0"
39
47
  },