@warmio/design 1.6.0 → 1.8.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.
Files changed (46) hide show
  1. package/LICENSES/fluid-functionalism.txt +24 -0
  2. package/LICENSES/{geist-mono-OFL-1.1.txt → jetbrains-mono-OFL-1.1.txt} +1 -1
  3. package/LICENSES/{source-serif-4-OFL-1.1.txt → merriweather-OFL-1.1.txt} +1 -1
  4. package/README.md +7 -3
  5. package/components.css +103 -58
  6. package/dist/button.d.ts +1 -1
  7. package/dist/button.js +18 -8
  8. package/fonts/jetbrains-mono-latin-ext-wght-italic.woff2 +0 -0
  9. package/fonts/jetbrains-mono-latin-ext-wght-normal.woff2 +0 -0
  10. package/fonts/jetbrains-mono-latin-wght-italic.woff2 +0 -0
  11. package/fonts/jetbrains-mono-latin-wght-normal.woff2 +0 -0
  12. package/fonts/merriweather-latin-400-italic.woff2 +0 -0
  13. package/fonts/merriweather-latin-400-normal.woff2 +0 -0
  14. package/fonts/merriweather-latin-700-normal.woff2 +0 -0
  15. package/fonts/merriweather-latin-ext-400-italic.woff2 +0 -0
  16. package/fonts/merriweather-latin-ext-400-normal.woff2 +0 -0
  17. package/fonts/merriweather-latin-ext-700-normal.woff2 +0 -0
  18. package/fonts.css +53 -195
  19. package/package.json +1 -2
  20. package/theme.css +40 -14
  21. package/assets/subtle-dots-dark.svg +0 -8
  22. package/assets/subtle-dots.png +0 -0
  23. package/fonts/geist-mono-cyrillic-ext-wght-italic.woff2 +0 -0
  24. package/fonts/geist-mono-cyrillic-ext-wght-normal.woff2 +0 -0
  25. package/fonts/geist-mono-cyrillic-wght-italic.woff2 +0 -0
  26. package/fonts/geist-mono-cyrillic-wght-normal.woff2 +0 -0
  27. package/fonts/geist-mono-latin-ext-wght-italic.woff2 +0 -0
  28. package/fonts/geist-mono-latin-ext-wght-normal.woff2 +0 -0
  29. package/fonts/geist-mono-latin-wght-italic.woff2 +0 -0
  30. package/fonts/geist-mono-latin-wght-normal.woff2 +0 -0
  31. package/fonts/geist-mono-symbols2-wght-italic.woff2 +0 -0
  32. package/fonts/geist-mono-symbols2-wght-normal.woff2 +0 -0
  33. package/fonts/geist-mono-vietnamese-wght-italic.woff2 +0 -0
  34. package/fonts/geist-mono-vietnamese-wght-normal.woff2 +0 -0
  35. package/fonts/source-serif-4-cyrillic-ext-wght-italic.woff2 +0 -0
  36. package/fonts/source-serif-4-cyrillic-ext-wght-normal.woff2 +0 -0
  37. package/fonts/source-serif-4-cyrillic-wght-italic.woff2 +0 -0
  38. package/fonts/source-serif-4-cyrillic-wght-normal.woff2 +0 -0
  39. package/fonts/source-serif-4-greek-wght-italic.woff2 +0 -0
  40. package/fonts/source-serif-4-greek-wght-normal.woff2 +0 -0
  41. package/fonts/source-serif-4-latin-ext-wght-italic.woff2 +0 -0
  42. package/fonts/source-serif-4-latin-ext-wght-normal.woff2 +0 -0
  43. package/fonts/source-serif-4-latin-wght-italic.woff2 +0 -0
  44. package/fonts/source-serif-4-latin-wght-normal.woff2 +0 -0
  45. package/fonts/source-serif-4-vietnamese-wght-italic.woff2 +0 -0
  46. package/fonts/source-serif-4-vietnamese-wght-normal.woff2 +0 -0
@@ -0,0 +1,24 @@
1
+ Fluid Functionalism
2
+ https://github.com/mickadesign/fluid-functionalism
3
+
4
+ MIT License
5
+
6
+ Copyright (c) 2026 Micka Touillaud
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ of this software and associated documentation files (the "Software"), to deal
10
+ in the Software without restriction, including without limitation the rights
11
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ copies of the Software, and to permit persons to whom the Software is
13
+ furnished to do so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in all
16
+ copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ SOFTWARE.
@@ -1,4 +1,4 @@
1
- Copyright 2024 The Geist Project Authors (https://github.com/vercel/geist-font.git) GeistMono-Italic[wght].ttf: Copyright 2024 The Geist Project Authors (https://github.com/vercel/geist-font.git)
1
+ Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono) JetBrainsMono-Italic[wght].ttf: Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono)
2
2
 
3
3
  This Font Software is licensed under the SIL Open Font License, Version 1.1.
4
4
  This license is copied below, and is also available with a FAQ at:
@@ -1,4 +1,4 @@
1
- Google Inc.
1
+ Copyright 2024 The Merriweather Project Authors (https://github.com/EbenSorkin/Merriweather4) with Reserved Font Name "Merriweather". Merriweather-Italic[opsz,wdth,wght].ttf: Copyright 2024 The Merriweather Project Authors (https://github.com/EbenSorkin/Merriweather4) with Reserved Font Name "Merriweather".
2
2
 
3
3
  This Font Software is licensed under the SIL Open Font License, Version 1.1.
4
4
  This license is copied below, and is also available with a FAQ at:
package/README.md CHANGED
@@ -14,18 +14,22 @@ import { Button, ButtonLink } from '@warmio/design/button';
14
14
  import { Logo, WarmLogoIcon } from '@warmio/design/logo';
15
15
  ```
16
16
 
17
- The product typefaces are Geist Mono for default UI and Source Serif 4 for
17
+ The product typefaces are JetBrains Mono for default UI and Merriweather for
18
18
  headings (`h1`–`h6`). Do not add a third family.
19
19
 
20
20
  `--accent-solid` / `--accent-solid-text` are the chromatic accent for links,
21
21
  badges, and highlights. They derive from `--brand-accent-solid` in `themes.css`.
22
22
 
23
- Package ships `components.css` (token-only styles for the primitives), `assets/`,
24
- and `dist/` (ESM + .d.ts). `dependencies` and `devDependencies` are empty.
23
+ Package ships `components.css` (token-only styles for the primitives) and `dist/` (ESM + .d.ts). `dependencies` and `devDependencies` are empty.
25
24
  `peerDependencies.react` is `^19.0.0`. No root JS export.
26
25
 
27
26
  ## Releases
28
27
 
28
+ The next version, `1.8.0`, adds Fluid-derived surface-only Button press feedback.
29
+ The label, hit target, and layout stay fixed. Reduced motion removes surface travel.
30
+ The implementation uses native CSS and adds no JavaScript dependency.
31
+ Upstream attribution is in `LICENSES/fluid-functionalism.txt`.
32
+
29
33
  Bump this `package.json` version for additive tokens (incl. motion/surface), fonts,
30
34
  spacing, or primitive changes. Follow the exact sequence in
31
35
  [docs/reference/DESIGN_PACKAGE.md](../../docs/reference/DESIGN_PACKAGE.md)
package/components.css CHANGED
@@ -1,11 +1,13 @@
1
- /* @warmio/design components.css - token-only, parity with prior Button/Logo contract.
2
- * Rebuilt from exact UIWarm primitive/button.variants + presentation maps + variant overrides + button-edge recipes.
3
- * Uses only design tokens + data attrs. No literals where tokens exist.
4
- * Motion via --motion-* (aliased); reduced-motion zeros non-essentials.
5
- * All 16 presentations, 6 variants, 3 sizes + full + states produce matching computed geometry/styles.
1
+ /* @warmio/design components.css - Warm Button/Logo recipes.
2
+ * Colors, typography, geometry, and edges remain Warm-owned.
3
+ * Surface-only press feedback adapted from Fluid Functionalism (MIT):
4
+ * https://www.fluidfunctionalism.com/r/base/button.json
5
+ * Native CSS keeps the published primitive free of a motion runtime.
6
6
  */
7
7
 
8
8
  [data-warm-button] {
9
+ position: relative;
10
+ isolation: isolate;
9
11
  /* Strip native button chrome when this stylesheet is used without preflight.
10
12
  * `appearance` is deliberately left at its UA default (matches the
11
13
  * pre-migration PrimitiveButton/buttonVariants recipe, which never applied
@@ -41,6 +43,27 @@
41
43
  /* border handled by variant shadows/tokens; no default regression border */
42
44
  }
43
45
 
46
+ /* Move only the painted surface, never the label or the hit target. Fluid's
47
+ * fixed 1px-per-edge collapse avoids width-dependent scaling of wide buttons.
48
+ * Warm gradients and edge shadows stay on this layer at their original bounds. */
49
+ [data-warm-button]::before {
50
+ content: '';
51
+ position: absolute;
52
+ z-index: -1;
53
+ inset: 0;
54
+ border-radius: inherit;
55
+ background-color: transparent;
56
+ background-image: none;
57
+ background-clip: inherit;
58
+ box-shadow: none;
59
+ pointer-events: none;
60
+ transition-property: var(--button-surface-transition-property);
61
+ transition-duration:
62
+ calc(var(--motion-duration-fast) * 1.8), calc(var(--motion-duration-fast) * 0.8),
63
+ calc(var(--motion-duration-fast) * 1.8);
64
+ transition-timing-function: var(--button-press-easing);
65
+ }
66
+
44
67
  [data-warm-button]:focus-visible {
45
68
  box-shadow: 0 0 0 var(--button-focus-ring)
46
69
  color-mix(in oklab, var(--ring) var(--button-mix-50), transparent);
@@ -50,89 +73,102 @@
50
73
  width: var(--button-width-full);
51
74
  }
52
75
 
53
- /* Active scale + full motion reduce (legacy: active:not-aria-[haspopup]:scale-[0.97] motion-reduce:transition-none motion-reduce:active:scale-100) */
54
- [data-warm-button]:active:not([aria-haspopup]) {
55
- transform: scale(0.97);
76
+ [data-warm-button]:not(:disabled):not([aria-disabled='true']):not(
77
+ [data-variant='arrow']
78
+ ):active::before {
79
+ inset: var(--button-press-inset);
80
+ transition-duration: calc(var(--motion-duration-fast) * 0.8);
56
81
  }
57
82
 
58
83
  @media (prefers-reduced-motion: reduce) {
59
- [data-warm-button] {
84
+ [data-warm-button],
85
+ [data-warm-button]::before {
60
86
  transition: none;
61
87
  }
62
- [data-warm-button]:active:not([aria-haspopup]) {
63
- transform: none;
88
+ [data-warm-button]:not(:disabled):not([aria-disabled='true']):not(
89
+ [data-variant='arrow']
90
+ ):active::before {
91
+ inset: 0;
64
92
  }
65
93
  }
66
94
 
67
95
  /* Variants (from cva + overrides + edge; tokens for color/shadow/radius) */
68
96
  [data-warm-button][data-variant='default'] {
97
+ color: var(--button-primary-foreground);
98
+ }
99
+ [data-warm-button][data-variant='default']::before {
69
100
  background-color: var(--button-primary-bg);
70
101
  background-image: var(--button-primary-gradient);
71
- color: var(--button-primary-foreground);
72
102
  box-shadow: var(--button-primary-shadow);
73
103
  /* no explicit border to avoid regression; shadow provides chrome */
74
104
  }
75
- [data-warm-button][data-variant='default']:hover {
105
+ [data-warm-button][data-variant='default']:hover::before {
76
106
  background-color: var(--button-primary-hover);
77
107
  background-image: var(--button-primary-gradient-hover);
78
108
  }
79
- [data-warm-button][data-variant='default']:active {
109
+ [data-warm-button][data-variant='default']:active::before {
80
110
  background-color: var(--button-primary-active);
81
111
  }
82
112
 
83
113
  [data-warm-button][data-variant='outline'] {
114
+ border-style: none;
115
+ }
116
+ [data-warm-button][data-variant='outline']::before {
84
117
  background-color: var(--background);
85
118
  box-shadow: var(--button-outline-shadow);
86
- border-style: none;
87
119
  }
88
120
  [data-warm-button][data-variant='outline']:hover {
89
- background-color: var(--muted);
90
121
  color: var(--foreground);
91
122
  }
92
- .dark [data-warm-button][data-variant='outline'] {
123
+ [data-warm-button][data-variant='outline']:hover::before {
124
+ background-color: var(--muted);
125
+ }
126
+ .dark [data-warm-button][data-variant='outline']::before {
93
127
  background-color: var(--warm-surface-raised-dark, var(--input));
94
128
  box-shadow: var(--button-outline-shadow-inset);
95
129
  }
96
- .dark [data-warm-button][data-variant='outline']:hover {
130
+ .dark [data-warm-button][data-variant='outline']:hover::before {
97
131
  background-color: var(--warm-surface-raised-hover-dark, var(--muted));
98
132
  }
99
133
  [data-warm-button][data-variant='outline']:disabled {
100
134
  opacity: 1;
101
- background-color: var(--muted);
102
135
  color: var(--muted-foreground);
136
+ }
137
+ [data-warm-button][data-variant='outline']:disabled::before {
138
+ background-color: var(--muted);
103
139
  box-shadow: var(--button-outline-shadow);
104
140
  }
105
- .dark [data-warm-button][data-variant='outline']:disabled {
141
+ .dark [data-warm-button][data-variant='outline']:disabled::before {
106
142
  background-color: color-mix(in oklab, var(--input) var(--button-mix-20), transparent);
107
143
  box-shadow: var(--button-outline-shadow-inset);
108
144
  }
109
145
 
110
- [data-warm-button][data-variant='ghost'] {
111
- /* `background-color` (not the `background` shorthand): the shorthand
112
- implicitly resets `background-clip` to its initial `border-box`,
113
- diverging from the base recipe's `padding-box` (origin: `bg-clip-padding`
114
- with no resting-state Tailwind `bg-*` utility on ghost). */
146
+ [data-warm-button][data-variant='ghost']::before {
115
147
  background-color: transparent;
116
148
  }
117
149
  [data-warm-button][data-variant='ghost']:hover {
118
- background-color: var(--muted);
119
150
  color: var(--foreground);
120
151
  }
121
- .dark [data-warm-button][data-variant='ghost']:hover {
152
+ [data-warm-button][data-variant='ghost']:hover::before {
153
+ background-color: var(--muted);
154
+ }
155
+ .dark [data-warm-button][data-variant='ghost']:hover::before {
122
156
  background-color: color-mix(in oklab, var(--muted) var(--button-mix-50), transparent);
123
157
  }
124
158
 
125
159
  [data-warm-button][data-variant='destructive'] {
126
- background-color: color-mix(in oklab, var(--destructive) var(--button-mix-10), transparent);
127
160
  color: var(--destructive);
128
161
  }
129
- [data-warm-button][data-variant='destructive']:hover {
162
+ [data-warm-button][data-variant='destructive']::before {
163
+ background-color: color-mix(in oklab, var(--destructive) var(--button-mix-10), transparent);
164
+ }
165
+ [data-warm-button][data-variant='destructive']:hover::before {
130
166
  background-color: color-mix(in oklab, var(--destructive) var(--button-mix-20), transparent);
131
167
  }
132
- .dark [data-warm-button][data-variant='destructive'] {
168
+ .dark [data-warm-button][data-variant='destructive']::before {
133
169
  background-color: color-mix(in oklab, var(--destructive) var(--button-mix-20), transparent);
134
170
  }
135
- .dark [data-warm-button][data-variant='destructive']:hover {
171
+ .dark [data-warm-button][data-variant='destructive']:hover::before {
136
172
  background-color: color-mix(in oklab, var(--destructive) var(--button-mix-30), transparent);
137
173
  }
138
174
 
@@ -148,8 +184,10 @@
148
184
  shadow is deliberately NOT `!important`: promptCategory's own
149
185
  `shadow-xs` legitimately overrides apple's outline-style shadow (see
150
186
  the promptCategory rules above), matching origin exactly. */
151
- background-color: var(--apple-button-background) !important;
152
187
  color: var(--apple-button-foreground) !important;
188
+ }
189
+ [data-warm-button][data-variant='apple']::before {
190
+ background-color: var(--apple-button-background) !important;
153
191
  box-shadow: var(--button-outline-shadow);
154
192
  }
155
193
  button[data-warm-button][data-variant='apple'] {
@@ -157,12 +195,14 @@ button[data-warm-button][data-variant='apple'] {
157
195
  background-clip: padding-box;
158
196
  }
159
197
  [data-warm-button][data-variant='apple']:hover {
198
+ color: var(--apple-button-foreground);
199
+ }
200
+ [data-warm-button][data-variant='apple']:hover::before {
160
201
  background-color: color-mix(
161
202
  in oklab,
162
203
  var(--apple-button-hover) var(--button-mix-90),
163
204
  transparent
164
205
  );
165
- color: var(--apple-button-foreground);
166
206
  }
167
207
  [data-warm-button][data-variant='arrow'] {
168
208
  border-width: 0;
@@ -188,6 +228,12 @@ button[data-warm-button][data-variant='apple'] {
188
228
  }
189
229
 
190
230
  /* Sizes (public only; presentation overrides suppressed by explicit size via data attr, matches old resolveButton) */
231
+ [data-warm-button][data-size='compact'] {
232
+ height: var(--button-size-md);
233
+ padding-left: var(--button-padding-x-md);
234
+ padding-right: var(--button-padding-x-md);
235
+ gap: var(--button-gap);
236
+ }
191
237
  [data-warm-button][data-size='default'] {
192
238
  height: var(--button-height-default);
193
239
  padding-left: var(--button-padding-x);
@@ -223,18 +269,6 @@ button[data-warm-button][data-variant='apple'] {
223
269
  height: var(--button-size-xs);
224
270
  padding: var(--button-padding-y-md);
225
271
  }
226
- [data-warm-button][data-presentation='assistantIcon']:not([data-explicit-size]):active:not(
227
- [aria-haspopup]
228
- ) {
229
- transform: scale(0.9);
230
- }
231
- @media (prefers-reduced-motion: reduce) {
232
- [data-warm-button][data-presentation='assistantIcon']:not([data-explicit-size]):active:not(
233
- [aria-haspopup]
234
- ) {
235
- transform: none;
236
- }
237
- }
238
272
  [data-warm-button][data-presentation='assistantRoundIcon']:not([data-explicit-size]) {
239
273
  /* old icon-round 36px */
240
274
  width: var(--button-height-icon);
@@ -265,7 +299,6 @@ button[data-warm-button][data-variant='apple'] {
265
299
  [data-warm-button][data-presentation='promptCategory']:not([data-explicit-size]) {
266
300
  height: var(--button-height-default);
267
301
  gap: var(--button-gap);
268
- background-color: var(--background);
269
302
  padding-left: var(--button-padding-x);
270
303
  padding-right: var(--button-padding-x);
271
304
  font-size: var(--button-font-size);
@@ -273,11 +306,14 @@ button[data-warm-button][data-variant='apple'] {
273
306
  color: var(--foreground);
274
307
  transition-property: var(--button-transition-property-colors);
275
308
  }
309
+ [data-warm-button][data-presentation='promptCategory']:not([data-explicit-size])::before {
310
+ background-color: var(--background);
311
+ }
276
312
  /* legacy's `getButtonEdgeClassName` applies `shadow-xs!` (Tailwind's `!`
277
313
  important modifier) to every `default`-variant promptCategory button
278
314
  regardless of explicit/implicit sizing, overriding the default variant's
279
315
  own gradient shadow unconditionally; mirror that with `!important` here. */
280
- [data-warm-button][data-presentation='promptCategory'][data-variant='default'] {
316
+ [data-warm-button][data-presentation='promptCategory'][data-variant='default']::before {
281
317
  box-shadow: var(--shadow-xs) !important;
282
318
  }
283
319
  /* buttonPresentationClasses.promptCategory's own (non-!important) `shadow-xs`
@@ -287,10 +323,10 @@ button[data-warm-button][data-variant='apple'] {
287
323
  default is already covered, unconditionally, by the rule above. */
288
324
  [data-warm-button][data-presentation='promptCategory']:not([data-explicit-size]):not(
289
325
  [data-variant='arrow']
290
- ) {
326
+ )::before {
291
327
  box-shadow: var(--shadow-xs) !important;
292
328
  }
293
- [data-warm-button][data-presentation='promptCategory']:hover {
329
+ [data-warm-button][data-presentation='promptCategory']:hover::before {
294
330
  background-color: var(--muted);
295
331
  }
296
332
  [data-warm-button][data-presentation='promptOption']:not([data-explicit-size]) {
@@ -307,9 +343,11 @@ button[data-warm-button][data-variant='apple'] {
307
343
  background-clip: border-box;
308
344
  }
309
345
  [data-warm-button][data-presentation='sidebarIcon']:hover {
310
- background-color: var(--sidebar-accent);
311
346
  color: var(--sidebar-accent-foreground);
312
347
  }
348
+ [data-warm-button][data-presentation='sidebarIcon']:hover::before {
349
+ background-color: var(--sidebar-accent);
350
+ }
313
351
  [data-warm-button][data-presentation='tableHeader']:not([data-explicit-size]) {
314
352
  height: auto;
315
353
  min-height: 0;
@@ -326,9 +364,11 @@ button[data-warm-button][data-variant='apple'] {
326
364
  color: var(--muted-foreground);
327
365
  }
328
366
  [data-warm-button][data-presentation='tableHeader']:hover {
329
- background-color: transparent;
330
367
  color: var(--foreground);
331
368
  }
369
+ [data-warm-button][data-presentation='tableHeader']:hover::before {
370
+ background-color: transparent;
371
+ }
332
372
  [data-warm-button][data-presentation='touch']:not([data-explicit-size]) {
333
373
  min-height: var(--button-min-height-touch);
334
374
  }
@@ -346,13 +386,13 @@ button[data-warm-button][data-variant='apple'] {
346
386
  font-size: var(--button-font-size);
347
387
  font-weight: 400;
348
388
  }
349
- [data-warm-button][data-presentation='threadNew']:hover {
389
+ [data-warm-button][data-presentation='threadNew']:hover::before {
350
390
  background-color: var(--muted);
351
391
  }
352
392
  [data-warm-button][data-presentation='compact']:not([data-explicit-size]) {
353
393
  height: var(--button-size-md);
354
- padding-left: var(--button-padding-x-sm);
355
- padding-right: var(--button-padding-x-sm);
394
+ padding-left: var(--button-padding-x-md);
395
+ padding-right: var(--button-padding-x-md);
356
396
  }
357
397
  /* 'default' and 'controlPeer' both resolve to private size 'default' with no
358
398
  own `buttonPresentationClasses` entry, so origin's `resolveLegacyButton`
@@ -394,9 +434,6 @@ button[data-warm-button][data-variant='apple'] {
394
434
  height: auto;
395
435
  padding: 0;
396
436
  gap: var(--button-gap);
397
- background-color: transparent;
398
- background-image: none;
399
- box-shadow: none;
400
437
  border-radius: 0;
401
438
  color: var(--foreground);
402
439
  font-size: var(--button-font-size-base);
@@ -410,6 +447,11 @@ button[data-warm-button][data-variant='apple'] {
410
447
  line-height: var(--button-line-height-base);
411
448
  font-weight: 700;
412
449
  }
450
+ [data-warm-button][data-variant='arrow'][data-variant='arrow']::before {
451
+ background-color: transparent;
452
+ background-image: none;
453
+ box-shadow: none;
454
+ }
413
455
 
414
456
  /* SVG rules (from cva) */
415
457
  [data-warm-button] svg {
@@ -469,9 +511,12 @@ button[data-warm-button][data-variant='apple'] {
469
511
  }
470
512
  [data-warm-button][data-variant='ghost'][aria-expanded='true'],
471
513
  [data-warm-button][data-variant='outline'][aria-expanded='true'] {
472
- background-color: var(--muted);
473
514
  color: var(--foreground);
474
515
  }
516
+ [data-warm-button][data-variant='ghost'][aria-expanded='true']::before,
517
+ [data-warm-button][data-variant='outline'][aria-expanded='true']::before {
518
+ background-color: var(--muted);
519
+ }
475
520
 
476
521
  /* Disabled base + variant overrides (keep full shadow/border for some) */
477
522
  [data-warm-button]:disabled {
package/dist/button.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  export type ButtonPresentation = 'assistantIcon' | 'assistantRoundIcon' | 'default' | 'composerIcon' | 'compact' | 'controlPeer' | 'editPill' | 'hero' | 'menuOption' | 'promptCategory' | 'promptOption' | 'sidebarIcon' | 'tableHeader' | 'threadMore' | 'threadNew' | 'touch';
3
3
  export type ButtonVariant = 'default' | 'outline' | 'ghost' | 'destructive' | 'apple' | 'arrow';
4
- export type ButtonSize = 'default' | 'jumbo' | 'icon';
4
+ export type ButtonSize = 'compact' | 'default' | 'jumbo' | 'icon';
5
5
  export type ButtonProps = Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'className' | 'style'> & {
6
6
  asChild?: never;
7
7
  className?: never;
package/dist/button.js CHANGED
@@ -18,17 +18,27 @@ const presentationToPrivateSize = {
18
18
  threadNew: 'sm',
19
19
  touch: 'default',
20
20
  };
21
+ const presentationAliases = {
22
+ controlPeer: 'default',
23
+ menuOption: 'promptOption',
24
+ touch: 'default',
25
+ };
26
+ function resolvePresentation(presentation) {
27
+ return presentationAliases[presentation] ?? presentation;
28
+ }
21
29
  function resolveButton(presentation, size) {
22
30
  const privateSize = presentationToPrivateSize[presentation];
23
31
  const resolvedSize = size ??
24
32
  (privateSize === 'lg'
25
33
  ? 'jumbo'
26
- : privateSize === 'icon' ||
27
- privateSize === 'icon-round' ||
28
- privateSize === 'icon-sm' ||
29
- privateSize === 'icon-xs'
30
- ? 'icon'
31
- : 'default');
34
+ : privateSize === 'sm'
35
+ ? 'compact'
36
+ : privateSize === 'icon' ||
37
+ privateSize === 'icon-round' ||
38
+ privateSize === 'icon-sm' ||
39
+ privateSize === 'icon-xs'
40
+ ? 'icon'
41
+ : 'default');
32
42
  return { resolvedSize, explicitSize: size ? resolvedSize : undefined };
33
43
  }
34
44
  function withArrow(variant, children) {
@@ -38,12 +48,12 @@ function withArrow(variant, children) {
38
48
  }
39
49
  const Button = React.forwardRef(function Button({ children, className: _className, presentation = 'default', render: _render, size, style: _style, type, variant = 'default', width, asChild: _asChild, ...props }, ref) {
40
50
  const { resolvedSize, explicitSize } = resolveButton(presentation, size);
41
- return (_jsx("button", { ...props, ref: ref, type: type ?? 'button', "data-warm-button": "", "data-slot": "button", "data-presentation": presentation, "data-variant": variant, "data-size": resolvedSize, "data-explicit-size": explicitSize, "data-width": width, children: withArrow(variant, children) }));
51
+ return (_jsx("button", { ...props, ref: ref, type: type ?? 'button', "data-warm-button": "", "data-slot": "button", "data-presentation": resolvePresentation(presentation), "data-variant": variant, "data-size": resolvedSize, "data-explicit-size": explicitSize, "data-width": width, children: withArrow(variant, children) }));
42
52
  });
43
53
  Button.displayName = 'WarmButton';
44
54
  const ButtonLink = React.forwardRef(function ButtonLink({ children, className: _className, disabled: _disabled, presentation = 'default', render: _render, size, style: _style, variant = 'default', width, asChild: _asChild, ...props }, ref) {
45
55
  const { resolvedSize, explicitSize } = resolveButton(presentation, size);
46
- return (_jsx("a", { ...props, ref: ref, "data-warm-button": "", "data-slot": "button", "data-presentation": presentation, "data-variant": variant, "data-size": resolvedSize, "data-explicit-size": explicitSize, "data-width": width, children: withArrow(variant, children) }));
56
+ return (_jsx("a", { ...props, ref: ref, "data-warm-button": "", "data-slot": "button", "data-presentation": resolvePresentation(presentation), "data-variant": variant, "data-size": resolvedSize, "data-explicit-size": explicitSize, "data-width": width, children: withArrow(variant, children) }));
47
57
  });
48
58
  ButtonLink.displayName = 'WarmButtonLink';
49
59
  export { Button, ButtonLink };
package/fonts.css CHANGED
@@ -1,284 +1,142 @@
1
1
  /*
2
- * Self-hosted variable fonts vendored from these exact OFL-1.1 releases:
3
- * @fontsource-variable/geist-mono@5.2.8 and
4
- * @fontsource-variable/source-serif-4@5.3.0.
2
+ * Self-hosted fonts vendored from these exact OFL-1.1 releases:
3
+ * @fontsource-variable/jetbrains-mono@5.2.8 and
4
+ * @fontsource/merriweather@5.2.11.
5
5
  */
6
6
 
7
- /* geist-mono-cyrillic-ext-wght-normal */
7
+ /* jetbrains-mono-latin-ext-wght-normal */
8
8
  @font-face {
9
- font-family: 'Geist Mono';
9
+ font-family: 'JetBrains Mono';
10
10
  font-style: normal;
11
11
  font-display: swap;
12
- font-weight: 100 900;
13
- src: url('./fonts/geist-mono-cyrillic-ext-wght-normal.woff2') format('woff2');
14
- unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
15
- }
16
-
17
- /* geist-mono-cyrillic-wght-normal */
18
- @font-face {
19
- font-family: 'Geist Mono';
20
- font-style: normal;
21
- font-display: swap;
22
- font-weight: 100 900;
23
- src: url('./fonts/geist-mono-cyrillic-wght-normal.woff2') format('woff2');
24
- unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
25
- }
26
-
27
- /* geist-mono-symbols2-wght-normal */
28
- @font-face {
29
- font-family: 'Geist Mono';
30
- font-style: normal;
31
- font-display: swap;
32
- font-weight: 100 900;
33
- src: url('./fonts/geist-mono-symbols2-wght-normal.woff2') format('woff2');
34
- unicode-range: U+2000-2001, U+2004-2008, U+200A, U+23B8-23BD, U+2500-259F;
35
- }
36
-
37
- /* geist-mono-vietnamese-wght-normal */
38
- @font-face {
39
- font-family: 'Geist Mono';
40
- font-style: normal;
41
- font-display: swap;
42
- font-weight: 100 900;
43
- src: url('./fonts/geist-mono-vietnamese-wght-normal.woff2') format('woff2');
44
- unicode-range:
45
- U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301,
46
- U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
47
- }
48
-
49
- /* geist-mono-latin-ext-wght-normal */
50
- @font-face {
51
- font-family: 'Geist Mono';
52
- font-style: normal;
53
- font-display: swap;
54
- font-weight: 100 900;
55
- src: url('./fonts/geist-mono-latin-ext-wght-normal.woff2') format('woff2');
12
+ font-weight: 100 800;
13
+ src: url('./fonts/jetbrains-mono-latin-ext-wght-normal.woff2') format('woff2-variations');
56
14
  unicode-range:
57
15
  U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
58
16
  U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
59
17
  U+A720-A7FF;
60
18
  }
61
19
 
62
- /* geist-mono-latin-wght-normal */
20
+ /* jetbrains-mono-latin-wght-normal */
63
21
  @font-face {
64
- font-family: 'Geist Mono';
22
+ font-family: 'JetBrains Mono';
65
23
  font-style: normal;
66
24
  font-display: swap;
67
- font-weight: 100 900;
68
- src: url('./fonts/geist-mono-latin-wght-normal.woff2') format('woff2');
25
+ font-weight: 100 800;
26
+ src: url('./fonts/jetbrains-mono-latin-wght-normal.woff2') format('woff2-variations');
69
27
  unicode-range:
70
28
  U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
71
29
  U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
72
30
  }
73
31
 
74
- /* geist-mono-cyrillic-ext-wght-italic */
75
- @font-face {
76
- font-family: 'Geist Mono';
77
- font-style: italic;
78
- font-display: swap;
79
- font-weight: 100 900;
80
- src: url('./fonts/geist-mono-cyrillic-ext-wght-italic.woff2') format('woff2');
81
- unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
82
- }
83
-
84
- /* geist-mono-cyrillic-wght-italic */
85
- @font-face {
86
- font-family: 'Geist Mono';
87
- font-style: italic;
88
- font-display: swap;
89
- font-weight: 100 900;
90
- src: url('./fonts/geist-mono-cyrillic-wght-italic.woff2') format('woff2');
91
- unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
92
- }
93
-
94
- /* geist-mono-symbols2-wght-italic */
95
- @font-face {
96
- font-family: 'Geist Mono';
97
- font-style: italic;
98
- font-display: swap;
99
- font-weight: 100 900;
100
- src: url('./fonts/geist-mono-symbols2-wght-italic.woff2') format('woff2');
101
- unicode-range: U+2000-2001, U+2004-2008, U+200A, U+23B8-23BD, U+2500-259F;
102
- }
103
-
104
- /* geist-mono-vietnamese-wght-italic */
32
+ /* jetbrains-mono-latin-ext-wght-italic */
105
33
  @font-face {
106
- font-family: 'Geist Mono';
34
+ font-family: 'JetBrains Mono';
107
35
  font-style: italic;
108
36
  font-display: swap;
109
- font-weight: 100 900;
110
- src: url('./fonts/geist-mono-vietnamese-wght-italic.woff2') format('woff2');
111
- unicode-range:
112
- U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301,
113
- U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
114
- }
115
-
116
- /* geist-mono-latin-ext-wght-italic */
117
- @font-face {
118
- font-family: 'Geist Mono';
119
- font-style: italic;
120
- font-display: swap;
121
- font-weight: 100 900;
122
- src: url('./fonts/geist-mono-latin-ext-wght-italic.woff2') format('woff2');
37
+ font-weight: 100 800;
38
+ src: url('./fonts/jetbrains-mono-latin-ext-wght-italic.woff2') format('woff2-variations');
123
39
  unicode-range:
124
40
  U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
125
41
  U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
126
42
  U+A720-A7FF;
127
43
  }
128
44
 
129
- /* geist-mono-latin-wght-italic */
45
+ /* jetbrains-mono-latin-wght-italic */
130
46
  @font-face {
131
- font-family: 'Geist Mono';
47
+ font-family: 'JetBrains Mono';
132
48
  font-style: italic;
133
49
  font-display: swap;
134
- font-weight: 100 900;
135
- src: url('./fonts/geist-mono-latin-wght-italic.woff2') format('woff2');
50
+ font-weight: 100 800;
51
+ src: url('./fonts/jetbrains-mono-latin-wght-italic.woff2') format('woff2-variations');
136
52
  unicode-range:
137
53
  U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
138
54
  U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
139
55
  }
140
56
 
141
- /* source-serif-4-cyrillic-ext-wght-normal */
142
- @font-face {
143
- font-family: 'Source Serif 4';
144
- font-style: normal;
145
- font-display: swap;
146
- font-weight: 200 900;
147
- src: url('./fonts/source-serif-4-cyrillic-ext-wght-normal.woff2') format('woff2');
148
- unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
149
- }
150
-
151
- /* source-serif-4-cyrillic-wght-normal */
152
- @font-face {
153
- font-family: 'Source Serif 4';
154
- font-style: normal;
155
- font-display: swap;
156
- font-weight: 200 900;
157
- src: url('./fonts/source-serif-4-cyrillic-wght-normal.woff2') format('woff2');
158
- unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
159
- }
160
-
161
- /* source-serif-4-greek-wght-normal */
57
+ /* merriweather-latin-ext-400-normal */
162
58
  @font-face {
163
- font-family: 'Source Serif 4';
59
+ font-family: 'Merriweather';
164
60
  font-style: normal;
165
61
  font-display: swap;
166
- font-weight: 200 900;
167
- src: url('./fonts/source-serif-4-greek-wght-normal.woff2') format('woff2');
168
- unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
62
+ font-weight: 400;
63
+ src: url('./fonts/merriweather-latin-ext-400-normal.woff2') format('woff2');
64
+ unicode-range:
65
+ U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
66
+ U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
67
+ U+A720-A7FF;
169
68
  }
170
69
 
171
- /* source-serif-4-vietnamese-wght-normal */
70
+ /* merriweather-latin-400-normal */
172
71
  @font-face {
173
- font-family: 'Source Serif 4';
72
+ font-family: 'Merriweather';
174
73
  font-style: normal;
175
74
  font-display: swap;
176
- font-weight: 200 900;
177
- src: url('./fonts/source-serif-4-vietnamese-wght-normal.woff2') format('woff2');
75
+ font-weight: 400;
76
+ src: url('./fonts/merriweather-latin-400-normal.woff2') format('woff2');
178
77
  unicode-range:
179
- U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301,
180
- U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
78
+ U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
79
+ U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
181
80
  }
182
81
 
183
- /* source-serif-4-latin-ext-wght-normal */
82
+ /* merriweather-latin-ext-700-normal */
184
83
  @font-face {
185
- font-family: 'Source Serif 4';
84
+ font-family: 'Merriweather';
186
85
  font-style: normal;
187
86
  font-display: swap;
188
- font-weight: 200 900;
189
- src: url('./fonts/source-serif-4-latin-ext-wght-normal.woff2') format('woff2');
87
+ font-weight: 700;
88
+ src: url('./fonts/merriweather-latin-ext-700-normal.woff2') format('woff2');
190
89
  unicode-range:
191
90
  U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
192
91
  U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
193
92
  U+A720-A7FF;
194
93
  }
195
94
 
196
- /* source-serif-4-latin-wght-normal */
95
+ /* merriweather-latin-700-normal */
197
96
  @font-face {
198
- font-family: 'Source Serif 4';
97
+ font-family: 'Merriweather';
199
98
  font-style: normal;
200
99
  font-display: swap;
201
- font-weight: 200 900;
202
- src: url('./fonts/source-serif-4-latin-wght-normal.woff2') format('woff2');
100
+ font-weight: 700;
101
+ src: url('./fonts/merriweather-latin-700-normal.woff2') format('woff2');
203
102
  unicode-range:
204
103
  U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
205
104
  U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
206
105
  }
207
106
 
208
- /* source-serif-4-cyrillic-ext-wght-italic */
209
- @font-face {
210
- font-family: 'Source Serif 4';
211
- font-style: italic;
212
- font-display: swap;
213
- font-weight: 200 900;
214
- src: url('./fonts/source-serif-4-cyrillic-ext-wght-italic.woff2') format('woff2');
215
- unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
216
- }
217
-
218
- /* source-serif-4-cyrillic-wght-italic */
219
- @font-face {
220
- font-family: 'Source Serif 4';
221
- font-style: italic;
222
- font-display: swap;
223
- font-weight: 200 900;
224
- src: url('./fonts/source-serif-4-cyrillic-wght-italic.woff2') format('woff2');
225
- unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
226
- }
227
-
228
- /* source-serif-4-greek-wght-italic */
229
- @font-face {
230
- font-family: 'Source Serif 4';
231
- font-style: italic;
232
- font-display: swap;
233
- font-weight: 200 900;
234
- src: url('./fonts/source-serif-4-greek-wght-italic.woff2') format('woff2');
235
- unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
236
- }
237
-
238
- /* source-serif-4-vietnamese-wght-italic */
239
- @font-face {
240
- font-family: 'Source Serif 4';
241
- font-style: italic;
242
- font-display: swap;
243
- font-weight: 200 900;
244
- src: url('./fonts/source-serif-4-vietnamese-wght-italic.woff2') format('woff2');
245
- unicode-range:
246
- U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301,
247
- U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
248
- }
249
-
250
- /* source-serif-4-latin-ext-wght-italic */
107
+ /* merriweather-latin-ext-400-italic */
251
108
  @font-face {
252
- font-family: 'Source Serif 4';
109
+ font-family: 'Merriweather';
253
110
  font-style: italic;
254
111
  font-display: swap;
255
- font-weight: 200 900;
256
- src: url('./fonts/source-serif-4-latin-ext-wght-italic.woff2') format('woff2');
112
+ font-weight: 400;
113
+ src: url('./fonts/merriweather-latin-ext-400-italic.woff2') format('woff2');
257
114
  unicode-range:
258
115
  U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
259
116
  U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
260
117
  U+A720-A7FF;
261
118
  }
262
119
 
263
- /* source-serif-4-latin-wght-italic */
120
+ /* merriweather-latin-400-italic */
264
121
  @font-face {
265
- font-family: 'Source Serif 4';
122
+ font-family: 'Merriweather';
266
123
  font-style: italic;
267
124
  font-display: swap;
268
- font-weight: 200 900;
269
- src: url('./fonts/source-serif-4-latin-wght-italic.woff2') format('woff2');
125
+ font-weight: 400;
126
+ src: url('./fonts/merriweather-latin-400-italic.woff2') format('woff2');
270
127
  unicode-range:
271
128
  U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
272
129
  U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
273
130
  }
274
131
 
275
132
  :root {
276
- --font-mono: 'Geist Mono', ui-monospace, monospace;
133
+ --font-mono: 'JetBrains Mono', ui-monospace, monospace;
277
134
  --font-sans: var(--font-mono);
278
135
  --font-geist: var(--font-mono);
279
136
  --font-display: var(--font-mono);
280
137
  --font-editorial-sans: var(--font-mono);
281
- --font-marketing-sans: var(--font-mono);
282
- --font-serif: 'Source Serif 4', ui-serif, Georgia, 'Times New Roman', serif;
138
+ --font-default: var(--font-mono);
139
+ --font-serif: 'Merriweather', ui-serif, Georgia, 'Times New Roman', serif;
140
+ --font-marketing-sans: var(--font-serif);
283
141
  --font-heading: var(--font-serif);
284
142
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@warmio/design",
3
- "version": "1.6.0",
3
+ "version": "1.8.0",
4
4
  "description": "Warm's public design contract: CSS tokens and Button/Logo primitives.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -25,7 +25,6 @@
25
25
  "themes.css",
26
26
  "components.css",
27
27
  "fonts",
28
- "assets",
29
28
  "dist",
30
29
  "LICENSES"
31
30
  ],
package/theme.css CHANGED
@@ -58,10 +58,10 @@
58
58
  SURFACES — From brand-neutral
59
59
  Backgrounds, cards, borders - subtle tinted neutrals.
60
60
  ══════════════════════════════════════════════════════════════════════════ */
61
- --background: oklch(1 0 0);
61
+ --background: rgba(230, 230, 230, 1);
62
62
  --foreground: oklch(0.147 0 0);
63
- --card: oklch(1 0 0);
64
- --card-gradient-start: oklch(1 0 0);
63
+ --card: rgba(240, 240, 240, 1);
64
+ --card-gradient-start: rgba(240, 240, 240, 1);
65
65
  --card-foreground: var(--foreground);
66
66
  --popover: var(--card);
67
67
  --popover-foreground: var(--foreground);
@@ -75,8 +75,17 @@
75
75
 
76
76
  /* Marketing surface and dot patterns (owned by design) */
77
77
  --marketing-surface: #f7f7f7;
78
- --marketing-pattern-dots: url('./assets/subtle-dots.png');
79
- --marketing-pattern-dots-dark: url('./assets/subtle-dots-dark.svg');
78
+ --marketing-pattern-dots-size: 5px 5px;
79
+ --marketing-pattern-dots: radial-gradient(
80
+ circle at 0.5px 0.5px,
81
+ oklch(0 0 0 / 0.08) 0.5px,
82
+ transparent 0.5px
83
+ );
84
+ --marketing-pattern-dots-dark: radial-gradient(
85
+ circle at 0.5px 0.5px,
86
+ oklch(1 0 0 / 0.08) 0.5px,
87
+ transparent 0.5px
88
+ );
80
89
 
81
90
  /* Charts */
82
91
  --chart-1: oklch(0.32 0 none);
@@ -91,7 +100,7 @@
91
100
  /* ══════════════════════════════════════════════════════════════════════════
92
101
  SIDEBAR — Transparent to show body texture
93
102
  ══════════════════════════════════════════════════════════════════════════ */
94
- --sidebar: oklch(0.986 0 0);
103
+ --sidebar: rgba(230, 230, 230, 1);
95
104
  --sidebar-foreground: var(--foreground);
96
105
  --sidebar-primary: var(--primary);
97
106
  --sidebar-primary-foreground: var(--primary-foreground);
@@ -194,11 +203,11 @@
194
203
  NON-COLOR TOKENS (shared across all themes)
195
204
  ══════════════════════════════════════════════════════════════════════════ */
196
205
 
197
- /* Typography — two families in fonts.css: Geist Mono default, Source Serif 4 headings.
198
- Role aliases (--font-sans, --font-display, --font-editorial-sans) resolve to those two. */
206
+ /* Typography — two families in fonts.css: JetBrains Mono default, Merriweather headings.
207
+ Role aliases (--font-sans, --font-display, --font-editorial-sans) resolve to mono; marketing/headings use serif. */
199
208
  --tracking-normal: 0em;
200
209
 
201
- /* Spacing Scale */
210
+ /* Spacing Scale — --spacing is the Tailwind seed (0.25rem). Product surfaces use 0 / 8px / 16px only; xs/md/xl/2xl below are leftover advertised names, not product steps. */
202
211
  --spacing: 0.25rem;
203
212
  --spacing-xs: 0.25rem;
204
213
  --spacing-sm: 0.5rem;
@@ -218,6 +227,8 @@
218
227
  --motion-distance-sm: 0.25rem;
219
228
  --motion-distance-md: 0.5rem;
220
229
  --motion-distance-lg: 1rem;
230
+ --button-press-inset: 1px;
231
+ --button-press-easing: cubic-bezier(0.23, 1, 0.32, 1);
221
232
 
222
233
  /* Aliases keep one scale; legacy names resolve to motion; chart uses slow (nonessential scale) */
223
234
  --transition-fast: var(--motion-duration-fast);
@@ -295,6 +306,7 @@
295
306
  --logo-letter-spacing: 0.025em;
296
307
  --logo-gap: 0.5rem;
297
308
  --button-transition-property: color, background-color, box-shadow, border-color, transform;
309
+ --button-surface-transition-property: inset, background-color, box-shadow;
298
310
  --button-transition-property-colors:
299
311
  color, background-color, border-color, outline-color, text-decoration-color, fill, stroke,
300
312
  --tw-gradient-from, --tw-gradient-via, --tw-gradient-to;
@@ -545,12 +557,12 @@
545
557
  ════════════════════════════════════════════════════════════════════════════ */
546
558
  .light {
547
559
  /* ─── Surfaces (light backgrounds from neutral) ─── */
548
- --background: oklch(0.99 0 0);
560
+ --background: rgba(230, 230, 230, 1);
549
561
  --foreground: oklch(0.35 0 0);
550
- --card: oklch(0.998 0 0);
551
- --card-gradient-start: oklch(1 0 0);
552
- --card-foreground: var(--foreground);
562
+ --card: rgba(240, 240, 240, 1);
563
+ --card-gradient-start: rgba(240, 240, 240, 1);
553
564
  --muted: oklch(0.917 0 0);
565
+ --sidebar: rgba(230, 230, 230, 1);
554
566
  --muted-foreground: oklch(0.532 0 0 / 0.8);
555
567
  --border: oklch(0.852 0 0);
556
568
 
@@ -787,5 +799,19 @@
787
799
  --shadow-lg: var(--shadow-lg);
788
800
  --shadow-xl: var(--shadow-xl);
789
801
  --shadow-2xl: var(--shadow-2xl);
790
- --shadow-card-edge: var(--shadow-border);
802
+ --shadow-card-edge:
803
+ var(--shadow-border), inset 0 1px rgba(255, 255, 255, 1), inset 1px 0 rgba(255, 255, 255, 0.5),
804
+ inset -1px 0 rgba(255, 255, 255, 0.5);
805
+ }
806
+
807
+ /* Dark cards use an outer highlight instead of the light inset highlight. */
808
+ .dark {
809
+ --shadow-card-edge:
810
+ var(--shadow-border), 0 1px rgba(255, 255, 255, 0.2), 1px 0 rgba(255, 255, 255, 0.1),
811
+ -1px 0 rgba(255, 255, 255, 0.1);
812
+ }
813
+
814
+ /* Dark cards use one simple outer edge highlight. */
815
+ .dark .shadow-card-edge {
816
+ --tw-shadow: 0 -1px 0 #333;
791
817
  }
@@ -1,8 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="27" height="15" viewBox="0 0 27 15">
2
- <defs>
3
- <pattern id="dots" width="3" height="3" patternUnits="userSpaceOnUse">
4
- <rect width="1" height="1" fill="#fff" fill-opacity=".14" />
5
- </pattern>
6
- </defs>
7
- <rect width="27" height="15" fill="url(#dots)" />
8
- </svg>
Binary file