@sckyzo/cosmos-theme 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/CHANGELOG.md +20 -0
- package/README.md +97 -1
- package/css/css-vars.css +181 -7
- package/css/tailwind-tokens.css +115 -27
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,26 @@ 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.4.0](https://github.com/SckyzO/cosmos/compare/@sckyzo/cosmos-theme@0.3.0...@sckyzo/cosmos-theme@0.4.0) (2026-10-30)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **react:** add density to appearance settings and a compact DataTable ([000d1c2](https://github.com/SckyzO/cosmos/commit/000d1c211c4a4bfffd22e1b119558d0e0cb57235))
|
|
14
|
+
* **theme:** add 20px and 24px row padding tokens ([e4acd79](https://github.com/SckyzO/cosmos/commit/e4acd79ac1cc8e47551e1b2d82efe032c7ed95fa))
|
|
15
|
+
* **theme:** add a 4px row padding token ([4886cf3](https://github.com/SckyzO/cosmos/commit/4886cf3c65edbdef99ee88a4f9dc822ae8ea3000))
|
|
16
|
+
* **theme:** add a 5px row token that stops at 4px ([842804d](https://github.com/SckyzO/cosmos/commit/842804d10ab958a7c3f083ebd842597ffb16bd78))
|
|
17
|
+
* **theme:** add data-density and its spacing tokens ([070f74a](https://github.com/SckyzO/cosmos/commit/070f74a8135fe3564b4609a8a53755fc9e12d002))
|
|
18
|
+
* **theme:** follow the nearest dir attribute in the rtl variant ([6e25a80](https://github.com/SckyzO/cosmos/commit/6e25a800d64c3c3208c1d0eea940fe60980842d3))
|
|
19
|
+
* **theme:** rescale every corner with data-radius ([2bdea86](https://github.com/SckyzO/cosmos/commit/2bdea865a341d3052e9a1fe10a1c7875f6793f9f))
|
|
20
|
+
|
|
21
|
+
## [0.3.0](https://github.com/SckyzO/cosmos/compare/@sckyzo/cosmos-theme@0.2.0...@sckyzo/cosmos-theme@0.3.0) (2026-10-27)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Features
|
|
25
|
+
|
|
26
|
+
* **theme:** always emit the colors the charts read ([38fed2a](https://github.com/SckyzO/cosmos/commit/38fed2a70a6f9729b856f8911cdcd104d360867f))
|
|
27
|
+
|
|
8
28
|
## [0.2.0](https://github.com/SckyzO/cosmos/compare/@sckyzo/cosmos-theme@0.1.0...@sckyzo/cosmos-theme@0.2.0) (2026-10-21)
|
|
9
29
|
|
|
10
30
|
|
package/README.md
CHANGED
|
@@ -55,6 +55,11 @@ Two independent settings, both on the `<html>` element:
|
|
|
55
55
|
|
|
56
56
|
`.dark` and `.deep-blue` both enable Tailwind's `dark:` variant.
|
|
57
57
|
|
|
58
|
+
- **Direction**: the native `dir` attribute, on `<html>` or any ancestor. Tailwind's `rtl:` variant is
|
|
59
|
+
redefined to follow the nearest `dir` attribute, so a `dir="ltr"` island inside a right-to-left
|
|
60
|
+
page is not flipped. It does not follow `dir="auto"`, nor a `dir="rtl"` nested inside such an
|
|
61
|
+
island.
|
|
62
|
+
|
|
58
63
|
- **Accent**: a `data-accent` attribute that swaps the whole `--color-brand-*`
|
|
59
64
|
scale (and with it `--color-accent` and the focus ring). Leave it off for
|
|
60
65
|
`blue`. The others are `indigo`, `violet`, `purple`, `rose`, `teal`, `cyan`,
|
|
@@ -72,6 +77,37 @@ Every theme × accent pair meets WCAG AA for body text, links and white text on
|
|
|
72
77
|
the accent. `pnpm --filter @sckyzo/cosmos-theme test` runs the contrast check
|
|
73
78
|
(`scripts/check-contrast.mjs`) against the CSS files.
|
|
74
79
|
|
|
80
|
+
## Focus indicator
|
|
81
|
+
|
|
82
|
+
Four Tailwind utilities, shipped with `@sckyzo/cosmos-theme/tailwind`, give
|
|
83
|
+
every control the same keyboard focus look: a **solid accent indicator** plus
|
|
84
|
+
the `--shadow-focus-ring` halo (12% of the accent). The halo alone is too faint
|
|
85
|
+
to count: the solid part is what reaches 3:1 against the surface (WCAG 1.4.11;
|
|
86
|
+
`brand-500` is checked on every theme × accent pair by `pnpm test`).
|
|
87
|
+
|
|
88
|
+
| Utility | Paints | For |
|
|
89
|
+
| --------------------------- | --------------------------- | --------------------------------------------------------------------- |
|
|
90
|
+
| `focus-indicator` | 2px `brand-500` ring + halo | buttons, links, toggles, tabs, menu items, cards… |
|
|
91
|
+
| `field-focus` | `brand-500` border + halo | text inputs, textarea, select and their wrappers |
|
|
92
|
+
| `field-focus-error` | `red-500` border + red halo | the same fields in error (`--shadow-focus-ring-error`) |
|
|
93
|
+
| `focus-indicator-on-accent` | 2px white ring, no halo | a control filled with `brand-500`, where the accent ring would vanish |
|
|
94
|
+
|
|
95
|
+
They hold no state: put the variant that fits in front of them.
|
|
96
|
+
|
|
97
|
+
```html
|
|
98
|
+
<button class="focus-visible:focus-indicator">Save</button>
|
|
99
|
+
<input class="focus:field-focus border border-gray-200" />
|
|
100
|
+
<div class="focus-within:field-focus border border-gray-200"><input /></div>
|
|
101
|
+
<!-- a control drawn by a sibling of a visually hidden input -->
|
|
102
|
+
<input type="checkbox" class="peer sr-only" />
|
|
103
|
+
<span class="peer-focus-visible:focus-indicator"></span>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
There is no ring offset. Add `ring-inset` where an ancestor clips with
|
|
107
|
+
`overflow-hidden`, or a neighbour covers the outer ring. The utilities use
|
|
108
|
+
`outline-hidden`, so the indicator survives forced-colors mode, where box
|
|
109
|
+
shadows are not painted.
|
|
110
|
+
|
|
75
111
|
## Fonts & text size
|
|
76
112
|
|
|
77
113
|
Two more settings on `<html>`, both optional:
|
|
@@ -99,17 +135,77 @@ Two more settings on `<html>`, both optional:
|
|
|
99
135
|
|
|
100
136
|
Both attributes also work on an element below `<html>`, for one part of a page.
|
|
101
137
|
|
|
138
|
+
## Density
|
|
139
|
+
|
|
140
|
+
A `data-density` attribute on `<html>` tightens or loosens the vertical rhythm of
|
|
141
|
+
every control and row at once: `compact`, none for the default, or `comfortable`.
|
|
142
|
+
Only the heights and the vertical padding of rows move; panel padding, gaps and
|
|
143
|
+
horizontal padding stay. It applies on touch screens too: the user's choice wins.
|
|
144
|
+
|
|
145
|
+
| Token | Default | `compact` | `comfortable` | Used by |
|
|
146
|
+
| ------------------------------ | ---------- | --------- | ------------- | --------------------------- |
|
|
147
|
+
| `h-ctrl-28` … `h-ctrl-48` | 28–48px | 4px less | 4px more | buttons, fields, segments |
|
|
148
|
+
| `py-row-4` … `py-row-24` | 4–24px | 2px less | 2px more | list rows, menu items, tabs |
|
|
149
|
+
| `py-cell-head`, `py-cell-body` | 12px, 14px | 8px, 10px | 16px, 20px | table cells |
|
|
150
|
+
|
|
151
|
+
The token is named by its default size: `h-ctrl-36` is today's `h-9`. The smallest
|
|
152
|
+
control, 28px, stays at 24px in `compact`, the WCAG 2.5.8 floor, and `py-row-5`
|
|
153
|
+
stops at 4px for the same reason. They are `--spacing-ctrl-*`, `--spacing-row-*` and
|
|
154
|
+
`--spacing-cell-*` theme variables, so any utility of the spacing scale takes
|
|
155
|
+
them (`h-ctrl-36`, `min-h-ctrl-36`, `size-ctrl-32`, `py-row-8`). Use them for a
|
|
156
|
+
control or a row of your own and it follows the page.
|
|
157
|
+
|
|
158
|
+
```html
|
|
159
|
+
<html class="sand" data-density="compact"></html>
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The attribute also works on an element below `<html>`; `data-density="default"`
|
|
163
|
+
opts a part of a compact or comfortable page out (a `DataTable` with its own
|
|
164
|
+
`density` does this).
|
|
165
|
+
|
|
166
|
+
## Radius
|
|
167
|
+
|
|
168
|
+
A `data-radius` attribute on `<html>` rescales every corner at once: `none`,
|
|
169
|
+
`sm`, none for the default, or `lg`. It rebuilds Tailwind's own `--radius-*`
|
|
170
|
+
variables, the ones `rounded-sm` to `rounded-4xl` read, so a component needs
|
|
171
|
+
nothing more than those utilities.
|
|
172
|
+
|
|
173
|
+
| Step | `--radius-*` | `rounded-lg` (8px by default) |
|
|
174
|
+
| --------- | ------------------------------- | ----------------------------- |
|
|
175
|
+
| `none` | 2px, every one | 2px |
|
|
176
|
+
| `sm` | half of Tailwind's | 4px |
|
|
177
|
+
| (default) | Tailwind's, nothing moves | 8px |
|
|
178
|
+
| `lg` | one and a half times Tailwind's | 12px |
|
|
179
|
+
|
|
180
|
+
`rounded-full` and `rounded-none` are literals and never change: avatars,
|
|
181
|
+
status dots, switches, badges and pill buttons stay round at every step. A
|
|
182
|
+
corner that must follow the page goes through the scale. A bare `rounded` is a
|
|
183
|
+
fixed `0.25rem` in Tailwind 4, so write `rounded-sm` (the same value, through
|
|
184
|
+
the variable); an arbitrary `rounded-[10px]` is fixed too, and
|
|
185
|
+
`rounded-[calc(var(--radius-lg)*1.25)]` is not.
|
|
186
|
+
|
|
187
|
+
```html
|
|
188
|
+
<html class="sand" data-radius="lg"></html>
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
The attribute also works on an element below `<html>`; `data-radius="default"`
|
|
192
|
+
opts a part of the page out of its parent's radius.
|
|
193
|
+
|
|
102
194
|
## Tokens
|
|
103
195
|
|
|
104
196
|
See [`css/tailwind-tokens.css`](css/tailwind-tokens.css) for the full source.
|
|
105
197
|
|
|
198
|
+
The brand palette, the status colors and the semantic colors are always in the CSS (`@theme static`): Tailwind
|
|
199
|
+
only emits a theme variable that a utility uses, and the charts read these as plain `var(--color-…)`.
|
|
200
|
+
|
|
106
201
|
Categories:
|
|
107
202
|
|
|
108
203
|
- **Brand palette** : `--color-brand-25` to `--color-brand-950`
|
|
204
|
+
- **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
|
|
109
205
|
- **Semantic colors** : `--color-success-*`, `--color-error-*`, `--color-warning-*`
|
|
110
206
|
- **Status** : `--color-status-{ok,warn,crit,unknown}`
|
|
111
207
|
- **Typography** : `--font-sans` (Outfit), `--font-mono` (JetBrains Mono), `--text-2xs` (11px), `--text-3xs` (10px)
|
|
112
|
-
- **Shadows** : `--shadow-card`, `--shadow-card-hover`
|
|
208
|
+
- **Shadows** : `--shadow-card`, `--shadow-card-hover`, `--shadow-focus-ring` and `--shadow-focus-ring-error` (the focus halos)
|
|
113
209
|
- **Layout vars** (theme-aware): `--color-bg-{base,panel,elevated}`, `--color-text-{base,primary,secondary,muted}`, `--color-border`, `--color-accent`
|
|
114
210
|
- **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`).
|
|
115
211
|
|
package/css/css-vars.css
CHANGED
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
--color-text-base: #101828;
|
|
38
38
|
--color-text-primary: #101828;
|
|
39
39
|
--color-text-secondary: #667085;
|
|
40
|
-
--color-text-muted: #
|
|
40
|
+
--color-text-muted: #677185;
|
|
41
41
|
|
|
42
42
|
/* ── Code ──────────────────────────────────────────────────────────────── */
|
|
43
43
|
/* Syntax highlighting on CodeBlock (gray-50 / gray-900), read by Shiki's
|
|
@@ -58,6 +58,19 @@
|
|
|
58
58
|
|
|
59
59
|
/* ── Accent ────────────────────────────────────────────────────────────── */
|
|
60
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;
|
|
61
74
|
}
|
|
62
75
|
|
|
63
76
|
/* ── Themes ────────────────────────────────────────────────────────────── */
|
|
@@ -72,7 +85,7 @@
|
|
|
72
85
|
--color-text-base: #1e1711;
|
|
73
86
|
--color-text-primary: #1e1711;
|
|
74
87
|
--color-text-secondary: #746656;
|
|
75
|
-
--color-text-muted: #
|
|
88
|
+
--color-text-muted: #7a6c5b;
|
|
76
89
|
|
|
77
90
|
/* ── Code ──────────────────────────────────────────────────────────────── */
|
|
78
91
|
/* Syntax highlighting on CodeBlock (gray-50 / gray-900), read by Shiki's
|
|
@@ -155,7 +168,7 @@
|
|
|
155
168
|
--color-text-base: #e4e7ec;
|
|
156
169
|
--color-text-primary: #fff;
|
|
157
170
|
--color-text-secondary: #98a2b3;
|
|
158
|
-
--color-text-muted: #
|
|
171
|
+
--color-text-muted: #8792a5;
|
|
159
172
|
|
|
160
173
|
/* ── Code ──────────────────────────────────────────────────────────────── */
|
|
161
174
|
/* Syntax highlighting on CodeBlock (gray-50 / gray-900), read by Shiki's
|
|
@@ -192,6 +205,13 @@
|
|
|
192
205
|
--color-brand-800: #2f25ab;
|
|
193
206
|
--color-brand-900: #24257a;
|
|
194
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;
|
|
195
215
|
}
|
|
196
216
|
|
|
197
217
|
[data-accent='violet'] {
|
|
@@ -201,12 +221,19 @@
|
|
|
201
221
|
--color-brand-200: #d8ceff;
|
|
202
222
|
--color-brand-300: #c0abff;
|
|
203
223
|
--color-brand-400: #a57eff;
|
|
204
|
-
--color-brand-500: #
|
|
224
|
+
--color-brand-500: #8a49f5;
|
|
205
225
|
--color-brand-600: #752cdb;
|
|
206
226
|
--color-brand-700: #6508c6;
|
|
207
227
|
--color-brand-800: #5200a4;
|
|
208
228
|
--color-brand-900: #3f097e;
|
|
209
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;
|
|
210
237
|
}
|
|
211
238
|
|
|
212
239
|
[data-accent='purple'] {
|
|
@@ -216,7 +243,7 @@
|
|
|
216
243
|
--color-brand-200: #f0c2ff;
|
|
217
244
|
--color-brand-300: #e696ff;
|
|
218
245
|
--color-brand-400: #d55bf8;
|
|
219
|
-
--color-brand-500: #
|
|
246
|
+
--color-brand-500: #b234d4;
|
|
220
247
|
--color-brand-600: #990aba;
|
|
221
248
|
--color-brand-700: #81019e;
|
|
222
249
|
--color-brand-800: #690181;
|
|
@@ -231,12 +258,19 @@
|
|
|
231
258
|
--color-brand-200: #ffc3d2;
|
|
232
259
|
--color-brand-300: #ff96b4;
|
|
233
260
|
--color-brand-400: #ff4e90;
|
|
234
|
-
--color-brand-500: #
|
|
261
|
+
--color-brand-500: #d81e70;
|
|
235
262
|
--color-brand-600: #b8005c;
|
|
236
263
|
--color-brand-700: #9b004c;
|
|
237
264
|
--color-brand-800: #7f013d;
|
|
238
265
|
--color-brand-900: #64002f;
|
|
239
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;
|
|
240
274
|
}
|
|
241
275
|
|
|
242
276
|
[data-accent='teal'] {
|
|
@@ -252,6 +286,13 @@
|
|
|
252
286
|
--color-brand-800: #024a43;
|
|
253
287
|
--color-brand-900: #003833;
|
|
254
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;
|
|
255
296
|
}
|
|
256
297
|
|
|
257
298
|
[data-accent='cyan'] {
|
|
@@ -267,6 +308,13 @@
|
|
|
267
308
|
--color-brand-800: #014759;
|
|
268
309
|
--color-brand-900: #003645;
|
|
269
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;
|
|
270
318
|
}
|
|
271
319
|
|
|
272
320
|
[data-accent='graphite'] {
|
|
@@ -282,6 +330,13 @@
|
|
|
282
330
|
--color-brand-800: #3c414b;
|
|
283
331
|
--color-brand-900: #2e3138;
|
|
284
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;
|
|
285
340
|
}
|
|
286
341
|
|
|
287
342
|
[data-accent='red'] {
|
|
@@ -291,7 +346,7 @@
|
|
|
291
346
|
--color-brand-200: #ffc6c0;
|
|
292
347
|
--color-brand-300: #ff9b94;
|
|
293
348
|
--color-brand-400: #ff5a57;
|
|
294
|
-
--color-brand-500: #
|
|
349
|
+
--color-brand-500: #de1f2d;
|
|
295
350
|
--color-brand-600: #be021e;
|
|
296
351
|
--color-brand-700: #a00118;
|
|
297
352
|
--color-brand-800: #830011;
|
|
@@ -312,6 +367,13 @@
|
|
|
312
367
|
--color-brand-800: #712b00;
|
|
313
368
|
--color-brand-900: #592000;
|
|
314
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;
|
|
315
377
|
}
|
|
316
378
|
|
|
317
379
|
[data-accent='amber'] {
|
|
@@ -327,6 +389,13 @@
|
|
|
327
389
|
--color-brand-800: #5d3900;
|
|
328
390
|
--color-brand-900: #482b00;
|
|
329
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;
|
|
330
399
|
}
|
|
331
400
|
|
|
332
401
|
[data-accent='green'] {
|
|
@@ -431,6 +500,111 @@
|
|
|
431
500
|
}
|
|
432
501
|
}
|
|
433
502
|
|
|
503
|
+
/* ── Density ───────────────────────────────────────────────────────────────
|
|
504
|
+
* data-density moves the vertical rhythm only: the height of controls
|
|
505
|
+
* (--spacing-ctrl-*), the vertical padding of rows (--spacing-row-*) and of
|
|
506
|
+
* table cells (--spacing-cell-*). Panel padding, gaps and the horizontal
|
|
507
|
+
* padding stay. compact takes 4px off a control (the smallest, 28px, stays at
|
|
508
|
+
* 24px, the WCAG 2.5.8 floor) and 2px off each side of a row; comfortable adds
|
|
509
|
+
* as much. No attribute keeps today's values; 'default' is the same, spelled
|
|
510
|
+
* out so a part of the page (a DataTable) can opt out of its parent's density.
|
|
511
|
+
*
|
|
512
|
+
* Declared again here for the same reason as the text sizes: a custom property
|
|
513
|
+
* resolves var() on the element that declares it, so the tokens are rebuilt
|
|
514
|
+
* on every element that carries the attribute. */
|
|
515
|
+
[data-density] {
|
|
516
|
+
--spacing-ctrl-28: calc(1.75rem + var(--cosmos-density-control, 0rem));
|
|
517
|
+
--spacing-ctrl-32: calc(2rem + var(--cosmos-density-control, 0rem));
|
|
518
|
+
--spacing-ctrl-36: calc(2.25rem + var(--cosmos-density-control, 0rem));
|
|
519
|
+
--spacing-ctrl-40: calc(2.5rem + var(--cosmos-density-control, 0rem));
|
|
520
|
+
--spacing-ctrl-44: calc(2.75rem + var(--cosmos-density-control, 0rem));
|
|
521
|
+
--spacing-ctrl-48: calc(3rem + var(--cosmos-density-control, 0rem));
|
|
522
|
+
--spacing-row-4: calc(0.25rem + var(--cosmos-density-row, 0rem));
|
|
523
|
+
/* 5px rows are 26px sidebar rows of 16px text: compact stops at 4px, the 24px floor. */
|
|
524
|
+
--spacing-row-5: max(0.25rem, calc(0.3125rem + var(--cosmos-density-row, 0rem)));
|
|
525
|
+
--spacing-row-6: calc(0.375rem + var(--cosmos-density-row, 0rem));
|
|
526
|
+
--spacing-row-8: calc(0.5rem + var(--cosmos-density-row, 0rem));
|
|
527
|
+
--spacing-row-10: calc(0.625rem + var(--cosmos-density-row, 0rem));
|
|
528
|
+
--spacing-row-12: calc(0.75rem + var(--cosmos-density-row, 0rem));
|
|
529
|
+
--spacing-row-14: calc(0.875rem + var(--cosmos-density-row, 0rem));
|
|
530
|
+
--spacing-row-16: calc(1rem + var(--cosmos-density-row, 0rem));
|
|
531
|
+
--spacing-row-20: calc(1.25rem + var(--cosmos-density-row, 0rem));
|
|
532
|
+
--spacing-row-24: calc(1.5rem + var(--cosmos-density-row, 0rem));
|
|
533
|
+
--spacing-cell-head: calc(0.75rem + var(--cosmos-density-cell-head, 0rem));
|
|
534
|
+
--spacing-cell-body: calc(0.875rem + var(--cosmos-density-cell-body, 0rem));
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
[data-density='default'] {
|
|
538
|
+
--cosmos-density-control: 0rem;
|
|
539
|
+
--cosmos-density-row: 0rem;
|
|
540
|
+
--cosmos-density-cell-head: 0rem;
|
|
541
|
+
--cosmos-density-cell-body: 0rem;
|
|
542
|
+
--cosmos-density-virtual-cell: calc(48em / 14);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
[data-density='compact'] {
|
|
546
|
+
--cosmos-density-control: -0.25rem;
|
|
547
|
+
--cosmos-density-row: -0.125rem;
|
|
548
|
+
--cosmos-density-cell-head: -0.25rem;
|
|
549
|
+
--cosmos-density-cell-body: -0.25rem;
|
|
550
|
+
--cosmos-density-virtual-cell: calc(40em / 14);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
[data-density='comfortable'] {
|
|
554
|
+
--cosmos-density-control: 0.25rem;
|
|
555
|
+
--cosmos-density-row: 0.125rem;
|
|
556
|
+
--cosmos-density-cell-head: 0.25rem;
|
|
557
|
+
--cosmos-density-cell-body: 0.375rem;
|
|
558
|
+
--cosmos-density-virtual-cell: calc(60em / 14);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/* ── Radius ────────────────────────────────────────────────────────────────
|
|
562
|
+
* data-radius rescales Tailwind's own --radius-* tokens, the ones rounded-sm
|
|
563
|
+
* to rounded-4xl read, so every corner of a component follows. none squares
|
|
564
|
+
* them all to 2px (a scale cannot get there from 4px to 32px), sm halves
|
|
565
|
+
* them, lg takes them one and a half times. No attribute keeps today's
|
|
566
|
+
* values; 'default' is the same, spelled out so a part of the page can opt
|
|
567
|
+
* out of its parent's radius. rounded-full and rounded-none are literals and
|
|
568
|
+
* never move: avatars, dots, switches, badges and pill buttons stay round.
|
|
569
|
+
*
|
|
570
|
+
* Declared again here for the same reason as the text sizes and the density:
|
|
571
|
+
* a custom property resolves var() on the element that declares it, so the
|
|
572
|
+
* tokens are rebuilt on every element that carries the attribute. The values
|
|
573
|
+
* are Tailwind's (theme.css); check-contrast.mjs fails if one goes missing. */
|
|
574
|
+
[data-radius] {
|
|
575
|
+
--radius-xs: calc(0.125rem * var(--cosmos-radius-scale, 1));
|
|
576
|
+
--radius-sm: calc(0.25rem * var(--cosmos-radius-scale, 1));
|
|
577
|
+
--radius-md: calc(0.375rem * var(--cosmos-radius-scale, 1));
|
|
578
|
+
--radius-lg: calc(0.5rem * var(--cosmos-radius-scale, 1));
|
|
579
|
+
--radius-xl: calc(0.75rem * var(--cosmos-radius-scale, 1));
|
|
580
|
+
--radius-2xl: calc(1rem * var(--cosmos-radius-scale, 1));
|
|
581
|
+
--radius-3xl: calc(1.5rem * var(--cosmos-radius-scale, 1));
|
|
582
|
+
--radius-4xl: calc(2rem * var(--cosmos-radius-scale, 1));
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
[data-radius='default'] {
|
|
586
|
+
--cosmos-radius-scale: 1;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
[data-radius='sm'] {
|
|
590
|
+
--cosmos-radius-scale: 0.5;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
[data-radius='lg'] {
|
|
594
|
+
--cosmos-radius-scale: 1.5;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
[data-radius='none'] {
|
|
598
|
+
--radius-xs: 2px;
|
|
599
|
+
--radius-sm: 2px;
|
|
600
|
+
--radius-md: 2px;
|
|
601
|
+
--radius-lg: 2px;
|
|
602
|
+
--radius-xl: 2px;
|
|
603
|
+
--radius-2xl: 2px;
|
|
604
|
+
--radius-3xl: 2px;
|
|
605
|
+
--radius-4xl: 2px;
|
|
606
|
+
}
|
|
607
|
+
|
|
434
608
|
/* Smooth theme switching transition — opt-in via .cosmos-bg-transition class
|
|
435
609
|
on the app shell (not body, to avoid transitioning every DOM element). */
|
|
436
610
|
.cosmos-bg-transition {
|
package/css/tailwind-tokens.css
CHANGED
|
@@ -10,38 +10,32 @@
|
|
|
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
|
-
|
|
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 *));
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
--text-2xs: 0.6875rem;
|
|
25
|
-
--text-3xs: 0.625rem;
|
|
19
|
+
/* Tailwind rtl variant: the nearest `dir` attribute. Not `:dir(rtl)`: Tailwind's build lowers
|
|
20
|
+
it to a list of `:lang()` for its browser targets, which a page that only sets `dir` never
|
|
21
|
+
matches (the dev server skips that step, so only a build shows it). Tailwind's default adds
|
|
22
|
+
`[dir="rtl"] *`, which also holds in a `dir="ltr"` island inside a right-to-left page (a code
|
|
23
|
+
sample, a phone number field) and would flip its icons: the `:not()` leaves the island out.
|
|
24
|
+
Not followed: `dir="auto"`, and a `dir="rtl"` nested in such an island.
|
|
25
|
+
`tests/e2e/specs/rtl-variant.spec.ts` checks it in the static build. */
|
|
26
|
+
@custom-variant rtl (&:where([dir="rtl"], [dir="rtl"] *):not(:where([dir="rtl"] [dir="ltr"], [dir="rtl"] [dir="ltr"] *)));
|
|
26
27
|
|
|
28
|
+
/* The colors the charts read as plain CSS variables (var(--color-brand-500),
|
|
29
|
+
* var(--color-status-warn)…), with no utility behind them. Tailwind only emits
|
|
30
|
+
* a theme variable that a utility uses, so these are `static`: always in the
|
|
31
|
+
* CSS, whatever the app's classes are. */
|
|
32
|
+
@theme static {
|
|
27
33
|
/* ── Status colors — health indicators, badges, alerts ─────────────────── */
|
|
28
34
|
--color-status-ok: #10b981;
|
|
29
35
|
--color-status-warn: #f59e0b;
|
|
30
36
|
--color-status-crit: #ef4444;
|
|
31
37
|
--color-status-unknown: #6b7280;
|
|
32
38
|
|
|
33
|
-
/* ── Card shadows — work on both light and dark backgrounds ────────────── */
|
|
34
|
-
--shadow-card: 0 1px 3px rgb(0 0 0 / 40%), 0 1px 2px rgb(0 0 0 / 30%);
|
|
35
|
-
--shadow-card-hover: 0 10px 25px rgb(0 0 0 / 50%), 0 4px 6px rgb(0 0 0 / 20%);
|
|
36
|
-
|
|
37
|
-
/* ── TailAdmin elevation shadows (theme.shadow-{xs,sm,md,lg,xl}) ───────── */
|
|
38
|
-
--shadow-theme-xs: 0px 1px 2px 0px rgb(16 24 40 / 5%);
|
|
39
|
-
--shadow-theme-sm: 0px 1px 3px 0px rgb(16 24 40 / 10%), 0px 1px 2px 0px rgb(16 24 40 / 6%);
|
|
40
|
-
--shadow-theme-md: 0px 4px 8px -2px rgb(16 24 40 / 10%), 0px 2px 4px -2px rgb(16 24 40 / 6%);
|
|
41
|
-
--shadow-theme-lg: 0px 12px 16px -4px rgb(16 24 40 / 8%), 0px 4px 6px -2px rgb(16 24 40 / 3%);
|
|
42
|
-
--shadow-theme-xl: 0px 20px 24px -4px rgb(16 24 40 / 8%), 0px 8px 8px -4px rgb(16 24 40 / 3%);
|
|
43
|
-
--shadow-focus-ring: 0px 0px 0px 4px color-mix(in srgb, var(--color-brand-500) 12%, transparent);
|
|
44
|
-
|
|
45
39
|
/* ── Brand palette (TailAdmin blue) — [data-accent] in css-vars.css swaps it ── */
|
|
46
40
|
--color-brand-25: #f2f7ff;
|
|
47
41
|
--color-brand-50: #ecf3ff;
|
|
@@ -56,9 +50,6 @@
|
|
|
56
50
|
--color-brand-900: #262e89;
|
|
57
51
|
--color-brand-950: #161950;
|
|
58
52
|
|
|
59
|
-
/* Dark gray aligned with Tailwind gray-900 — keeps SectionCard bg matching body bg */
|
|
60
|
-
--color-gray-dark: #111827;
|
|
61
|
-
|
|
62
53
|
/* ── Semantic colors ───────────────────────────────────────────────────── */
|
|
63
54
|
--color-success-25: #f6fef9;
|
|
64
55
|
--color-success-50: #ecfdf3;
|
|
@@ -77,8 +68,105 @@
|
|
|
77
68
|
--color-warning-100: #fef0c7;
|
|
78
69
|
--color-warning-500: #f79009;
|
|
79
70
|
--color-warning-600: #dc6803;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@theme {
|
|
74
|
+
/* ── Typography ─────────────────────────────────────────────────────────── */
|
|
75
|
+
--font-sans: 'Outfit', system-ui, sans-serif;
|
|
76
|
+
--font-mono: 'JetBrains Mono', monospace;
|
|
77
|
+
|
|
78
|
+
/* Sizes under text-xs (12px) for dense labels: 11px and 10px. Without a
|
|
79
|
+
--line-height they set only font-size, like text-[11px] would, and the
|
|
80
|
+
text size setting in css-vars.css can scale them. */
|
|
81
|
+
--text-2xs: 0.6875rem;
|
|
82
|
+
--text-3xs: 0.625rem;
|
|
83
|
+
|
|
84
|
+
/* ── Density ────────────────────────────────────────────────────────────── */
|
|
85
|
+
/* The vertical rhythm that data-density moves (css-vars.css redefines these
|
|
86
|
+
under the attribute): the height of a control, named by its default
|
|
87
|
+
height in px (h-ctrl-36 is h-9), and the vertical padding of a row, named
|
|
88
|
+
the same way (py-row-8 is py-2). A table cell has its own two. The
|
|
89
|
+
horizontal padding and the gaps stay where they are. Without the
|
|
90
|
+
attribute these are the values the components always had. */
|
|
91
|
+
--spacing-ctrl-28: 1.75rem;
|
|
92
|
+
--spacing-ctrl-32: 2rem;
|
|
93
|
+
--spacing-ctrl-36: 2.25rem;
|
|
94
|
+
--spacing-ctrl-40: 2.5rem;
|
|
95
|
+
--spacing-ctrl-44: 2.75rem;
|
|
96
|
+
--spacing-ctrl-48: 3rem;
|
|
97
|
+
--spacing-row-4: 0.25rem;
|
|
98
|
+
--spacing-row-5: 0.3125rem;
|
|
99
|
+
--spacing-row-6: 0.375rem;
|
|
100
|
+
--spacing-row-8: 0.5rem;
|
|
101
|
+
--spacing-row-10: 0.625rem;
|
|
102
|
+
--spacing-row-12: 0.75rem;
|
|
103
|
+
--spacing-row-14: 0.875rem;
|
|
104
|
+
--spacing-row-16: 1rem;
|
|
105
|
+
--spacing-row-20: 1.25rem;
|
|
106
|
+
--spacing-row-24: 1.5rem;
|
|
107
|
+
--spacing-cell-head: 0.75rem;
|
|
108
|
+
--spacing-cell-body: 0.875rem;
|
|
109
|
+
|
|
110
|
+
/* ── Card shadows — work on both light and dark backgrounds ────────────── */
|
|
111
|
+
--shadow-card: 0 1px 3px rgb(0 0 0 / 40%), 0 1px 2px rgb(0 0 0 / 30%);
|
|
112
|
+
--shadow-card-hover: 0 10px 25px rgb(0 0 0 / 50%), 0 4px 6px rgb(0 0 0 / 20%);
|
|
113
|
+
|
|
114
|
+
/* ── TailAdmin elevation shadows (theme.shadow-{xs,sm,md,lg,xl}) ───────── */
|
|
115
|
+
--shadow-theme-xs: 0px 1px 2px 0px rgb(16 24 40 / 5%);
|
|
116
|
+
--shadow-theme-sm: 0px 1px 3px 0px rgb(16 24 40 / 10%), 0px 1px 2px 0px rgb(16 24 40 / 6%);
|
|
117
|
+
--shadow-theme-md: 0px 4px 8px -2px rgb(16 24 40 / 10%), 0px 2px 4px -2px rgb(16 24 40 / 6%);
|
|
118
|
+
--shadow-theme-lg: 0px 12px 16px -4px rgb(16 24 40 / 8%), 0px 4px 6px -2px rgb(16 24 40 / 3%);
|
|
119
|
+
--shadow-theme-xl: 0px 20px 24px -4px rgb(16 24 40 / 8%), 0px 8px 8px -4px rgb(16 24 40 / 3%);
|
|
120
|
+
--shadow-focus-ring: 0px 0px 0px 4px color-mix(in srgb, var(--color-brand-500) 12%, transparent);
|
|
121
|
+
--shadow-focus-ring-error: 0 0 0 4px color-mix(in srgb, var(--color-red-500) 12%, transparent);
|
|
122
|
+
|
|
123
|
+
/* Dark gray aligned with Tailwind gray-900 — keeps SectionCard bg matching body bg */
|
|
124
|
+
--color-gray-dark: #111827;
|
|
80
125
|
|
|
81
126
|
/* ── Bridges: aliases that pull values from CSS vars (defined in css-vars.css)
|
|
82
127
|
so utilities like bg-accent-primary stay in sync with theme switcher. */
|
|
83
128
|
--color-accent-primary: var(--color-accent);
|
|
84
129
|
}
|
|
130
|
+
|
|
131
|
+
/* ── Focus indicator ───────────────────────────────────────────────────────
|
|
132
|
+
* One focus look for every control: a solid accent indicator plus the
|
|
133
|
+
* --shadow-focus-ring halo. The halo is 12% of the accent, too faint to be the
|
|
134
|
+
* only cue, so the solid part is what carries WCAG 1.4.11 (brand-500 reaches
|
|
135
|
+
* 3:1 on the surfaces of the four themes, see scripts/check-contrast.mjs).
|
|
136
|
+
*
|
|
137
|
+
* The utilities hold no state: put the variant that fits in front of them.
|
|
138
|
+
*
|
|
139
|
+
* focus-visible:focus-indicator a button, link, toggle, tab, menu item…:
|
|
140
|
+
* 2px accent ring + halo, keyboard focus only
|
|
141
|
+
* peer-focus-visible:…, group-focus-visible:…, focus-within:…
|
|
142
|
+
* the same, for a control drawn by a sibling
|
|
143
|
+
* or a wrapper (checkbox, radio, card)
|
|
144
|
+
* focus:field-focus a text field: accent border + halo
|
|
145
|
+
* focus-within:field-focus the same on a wrapper of the field
|
|
146
|
+
* focus:field-focus-error the field in error: red border + red halo
|
|
147
|
+
* focus-visible:focus-indicator-on-accent
|
|
148
|
+
* a control filled with brand-500 (the active
|
|
149
|
+
* segment, the selected day), where a brand
|
|
150
|
+
* ring would vanish: a 2px white ring, no halo
|
|
151
|
+
* (white on brand-500 is checked at 4.5:1)
|
|
152
|
+
*
|
|
153
|
+
* No ring offset. Add ring-inset where an ancestor clips (overflow-hidden) or a
|
|
154
|
+
* neighbour hides the outer ring.
|
|
155
|
+
*
|
|
156
|
+
* outline-hidden hides the browser outline but keeps it in forced-colors mode,
|
|
157
|
+
* where box-shadow is not painted: the indicator never disappears there. */
|
|
158
|
+
@utility focus-indicator {
|
|
159
|
+
@apply ring-brand-500 shadow-focus-ring ring-2 outline-hidden;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
@utility field-focus {
|
|
163
|
+
@apply border-brand-500 shadow-focus-ring outline-hidden;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
@utility field-focus-error {
|
|
167
|
+
@apply shadow-focus-ring-error border-red-500 outline-hidden;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
@utility focus-indicator-on-accent {
|
|
171
|
+
@apply ring-2 ring-white outline-hidden;
|
|
172
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sckyzo/cosmos-theme",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Tailwind 4 theme and design tokens for the Cosmos design system — palette, typography, radii and light
|
|
3
|
+
"version": "0.4.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",
|