@tristan2828/ui-foundation 4.62.0 → 4.63.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 (37) hide show
  1. package/conventions/docs/design-language.md +132 -23
  2. package/dist/components/app/app-shell.js +28 -2
  3. package/dist/components/app/board.js +1 -1
  4. package/dist/components/app/data-table.js +1 -1
  5. package/dist/components/app/foundation-providers.d.ts +14 -1
  6. package/dist/components/app/foundation-providers.js +4 -3
  7. package/dist/components/app/module-card.js +1 -1
  8. package/dist/components/app/palette-provider.d.ts +14 -0
  9. package/dist/components/app/palette-provider.js +35 -0
  10. package/dist/components/app/widget-card.js +2 -2
  11. package/dist/components/ui/badge.d.ts +1 -1
  12. package/dist/components/ui/badge.js +5 -2
  13. package/dist/components/ui/button.js +10 -10
  14. package/dist/components/ui/card.js +3 -3
  15. package/dist/components/ui/checkbox.js +1 -1
  16. package/dist/components/ui/combobox.js +1 -1
  17. package/dist/components/ui/dialog.js +1 -1
  18. package/dist/components/ui/input-group.js +1 -1
  19. package/dist/components/ui/input.js +1 -1
  20. package/dist/components/ui/progress.d.ts +7 -0
  21. package/dist/components/ui/progress.js +19 -0
  22. package/dist/components/ui/select.js +1 -1
  23. package/dist/components/ui/sheet.js +1 -1
  24. package/dist/components/ui/slider.d.ts +3 -0
  25. package/dist/components/ui/slider.js +12 -0
  26. package/dist/components/ui/textarea.js +1 -1
  27. package/dist/hooks/use-palette.d.ts +14 -0
  28. package/dist/hooks/use-palette.js +7 -0
  29. package/dist/index.d.ts +2 -0
  30. package/dist/index.js +2 -0
  31. package/dist/lib/palettes.d.ts +21 -0
  32. package/dist/lib/palettes.js +74 -0
  33. package/package.json +1 -1
  34. package/styles/index.css +60 -0
  35. package/styles/theme.css +321 -11
  36. package/vite/index.d.ts +7 -1
  37. package/vite/pwa.js +21 -7
package/styles/theme.css CHANGED
@@ -112,7 +112,97 @@
112
112
  --category-12-400: oklch(0.648 0.232 337);
113
113
  --blue-500: oklch(0.488 0.243 264.376);
114
114
 
115
- --radius-base: 0.625rem;
115
+ /*
116
+ * Palette primitives (design-language.md "Palettes"). A palette is an
117
+ * app's own dark look: these four were chosen for the shared style
118
+ * guide, and an app names one (FoundationProviders' `palette`). Each is
119
+ * the guide's colours as written, with one change: Violet's text on its
120
+ * accent was #130925 (4.496:1, just under 4.5) and is #110823 (4.54:1).
121
+ * The semantic blocks under `.dark[data-palette]`, at the end of this
122
+ * file, map them onto the tokens components use. Ratios are against the
123
+ * palette's own background (bg), card and raised surfaces (WCAG 2.x,
124
+ * sRGB), listed 1 Indigo and Cyan / 2 Indigo and Rose / 3 Graphite and
125
+ * Periwinkle / 4 Violet:
126
+ *
127
+ * text on bg 15.0 / 15.3 / 15.0 / 15.0, on card 13.4 / 13.9 / 13.4 / 13.4
128
+ * dim on bg 7.47 / 7.42 / 7.54 / 7.47, on raised 5.87 / 6.06 / 5.79 / 5.87
129
+ * accent on bg 5.25 / 4.33 / 4.87 / 4.24, on card 4.68 / 3.94 / 4.32 / 3.78:
130
+ * never small text (2 and 4 fail); the ring and fills only
131
+ * on-accent on accent 5.49 / 4.51 / 5.28 / 4.54
132
+ * accent-light on bg 10.3 / 9.65 / 10.4 / 8.53, on card 9.16 / 8.77 / 9.19 / 7.61: links
133
+ * strong-border on bg 2.79 / 2.53 / 2.89 / 2.79: under 3:1, the guide's
134
+ * lightest grey, kept as designed (axe doesn't check borders)
135
+ * second on card 9.47 / 5.85 / 5.08 / 4.79
136
+ */
137
+ --palette-indigo-cyan-background: #13161b;
138
+ --palette-indigo-cyan-panel: #1c2129;
139
+ --palette-indigo-cyan-raised: #252b35;
140
+ --palette-indigo-cyan-border: #38414d;
141
+ --palette-indigo-cyan-strong-border: #535f6f;
142
+ --palette-indigo-cyan-text: #e7eaee;
143
+ --palette-indigo-cyan-dim: #a0a7b1;
144
+ --palette-indigo-cyan-accent: #6682f5;
145
+ --palette-indigo-cyan-accent-light: #b2c1fa;
146
+ --palette-indigo-cyan-accent-deep: #224cf1;
147
+ --palette-indigo-cyan-on-accent: #090f25;
148
+ --palette-indigo-cyan-second: #6dd4ee;
149
+
150
+ --palette-indigo-rose-background: #101219;
151
+ --palette-indigo-rose-panel: #181c28;
152
+ --palette-indigo-rose-raised: #202534;
153
+ --palette-indigo-rose-border: #32394e;
154
+ --palette-indigo-rose-strong-border: #4c5570;
155
+ --palette-indigo-rose-text: #e6e8ef;
156
+ --palette-indigo-rose-dim: #9da3b4;
157
+ --palette-indigo-rose-accent: #6868f3;
158
+ --palette-indigo-rose-accent-light: #b4b4f9;
159
+ --palette-indigo-rose-accent-deep: #2626ed;
160
+ --palette-indigo-rose-on-accent: #090925;
161
+ --palette-indigo-rose-second: #ec6f8e;
162
+
163
+ --palette-graphite-periwinkle-background: #171717;
164
+ --palette-graphite-periwinkle-panel: #222222;
165
+ --palette-graphite-periwinkle-raised: #2d2d2d;
166
+ --palette-graphite-periwinkle-border: #424242;
167
+ --palette-graphite-periwinkle-strong-border: #616161;
168
+ --palette-graphite-periwinkle-text: #ebebeb;
169
+ --palette-graphite-periwinkle-dim: #a8a8a8;
170
+ --palette-graphite-periwinkle-accent: #7f75f0;
171
+ --palette-graphite-periwinkle-accent-light: #c3bff8;
172
+ --palette-graphite-periwinkle-accent-deep: #4435e9;
173
+ --palette-graphite-periwinkle-on-accent: #0b0925;
174
+ --palette-graphite-periwinkle-second: #a97bea;
175
+
176
+ --palette-violet-background: #13161b;
177
+ --palette-violet-panel: #1c2129;
178
+ --palette-violet-raised: #252b35;
179
+ --palette-violet-border: #38414d;
180
+ --palette-violet-strong-border: #535f6f;
181
+ --palette-violet-text: #e7eaee;
182
+ --palette-violet-dim: #a0a7b1;
183
+ --palette-violet-accent: #905ceb;
184
+ --palette-violet-accent-light: #c1a4f4;
185
+ --palette-violet-accent-deep: #651ce3;
186
+ --palette-violet-on-accent: #110823;
187
+ --palette-violet-second: #c363e4;
188
+
189
+ /*
190
+ * The status colours every palette shares, as TEXT shades (--*-text):
191
+ * soft badges, icons, coloured words. Never fills: white on the success
192
+ * shade measures 1.80, so solid fills keep the deeper -500/-700 shades
193
+ * above, with white text. On bg / card / raised, the lowest palette:
194
+ * success 9.95 / 8.83 / 7.64, warning 9.03 / 8.02 / 6.94,
195
+ * danger 6.12 / 5.44 / 4.70.
196
+ * Danger is a shade lighter than the guide's #dc726a, which measured
197
+ * 4.37 on Graphite's raised surface and 4.46 on an outline button's fill.
198
+ */
199
+ --palette-success: #66d69a;
200
+ --palette-warning: #e0b152;
201
+ --palette-danger: #df7a72;
202
+
203
+ /* 9px (the shared style guide): buttons and cards on --radius, fields
204
+ on --radius-md (about 7px). */
205
+ --radius-base: 0.5625rem;
116
206
 
117
207
  /*
118
208
  * Typography roles (conventions/docs/design-language.md "Typography").
@@ -123,14 +213,17 @@
123
213
  * The values are what the package and template already used, so moving
124
214
  * onto a role changes nothing visible.
125
215
  */
126
- --type-page-title-size: 1.125rem; /* was text-lg font-semibold */
127
- --type-page-title-leading: 1.75rem;
216
+ --type-page-title-size: 1.75rem; /* 28px, the shared style guide's */
217
+ --type-page-title-leading: 1.2;
128
218
  --type-page-title-weight: 600;
129
219
  --type-section-title-size: 1rem;
130
220
  --type-section-title-leading: 1.5rem;
131
221
  --type-section-title-weight: 600;
222
+ /* Headings draw in a little (the page and section titles, and written
223
+ text's # and ##); labels and body keep the font's own spacing. */
224
+ --type-heading-tracking: -0.02em;
132
225
  --type-body-size: 0.875rem; /* text-sm: tables, forms, most UI text */
133
- --type-body-leading: 1.25rem;
226
+ --type-body-leading: 1.5;
134
227
  --type-body-weight: 400;
135
228
  --type-label-size: 0.875rem; /* column headers, field labels */
136
229
  --type-label-leading: 1.25rem;
@@ -166,8 +259,26 @@
166
259
  * The default is what the table always had.
167
260
  */
168
261
  --table-cell-px: 0.5rem;
169
- --table-cell-py: 0.5rem;
170
- --table-head-height: 2.5rem;
262
+ /* Compact rows by default (the shared style guide, chosen from the
263
+ review screenshots on 2026-11-04): 4px around the line, a 32px
264
+ header. data-density="compact" keeps them so and tightens a hub's
265
+ cards; comfortable opens them up. */
266
+ --table-cell-py: 0.25rem;
267
+ --table-head-height: 2rem;
268
+ /*
269
+ * Glow (the shared style guide), drawn only under a palette (index.css's
270
+ * `palette:` variant and glow-edge): the accent as one soft layer, from
271
+ * --primary, so each palette's glow is its own accent.
272
+ * --primary-glow a primary button on hover, keyboard focus and press:
273
+ * 38px blur, 2px spread, 38%
274
+ * --edge-glow a hovered hub tile, widget or card: a softer halo
275
+ * beside its accent edge
276
+ * --thumb-glow a slider's thumb
277
+ */
278
+ --primary-glow: 0 0 38px 2px color-mix(in srgb, var(--primary) 38%, transparent);
279
+ --edge-glow: 0 0 18px 0 color-mix(in srgb, var(--primary) 28%, transparent);
280
+ --thumb-glow: 0 0 10px 2px color-mix(in srgb, var(--primary) 50%, transparent);
281
+
171
282
  /* The small size of Button and Toggle (and the calendar's weekdays):
172
283
  between text-xs and text-sm, so it has a token of its own for the
173
284
  large density to scale. */
@@ -258,6 +369,16 @@
258
369
  --chart-3: var(--gray-600);
259
370
  --chart-4: var(--gray-700);
260
371
  --chart-5: var(--gray-900);
372
+ /* A palette's second colour (a neighbouring hue): a chart's fourth
373
+ series, anything that needs a colour beside the accent. Without a
374
+ palette it is a grey, as the charts are. */
375
+ --accent-second: var(--gray-700);
376
+ /* A card (the shared style guide): a soft shadow under its border, and
377
+ its header a tinted bar. The shadow is 30% in dark, where the guide
378
+ set it, and lighter on white. Text on the bar: foreground and muted
379
+ foreground as on --muted. */
380
+ --card-shadow: 0 6px 14px oklch(0 0 0 / 8%);
381
+ --card-header: color-mix(in srgb, var(--muted) 60%, var(--card));
261
382
  --radius: var(--radius-base);
262
383
  --sidebar: var(--gray-50);
263
384
  --sidebar-foreground: var(--black);
@@ -270,7 +391,7 @@
270
391
  }
271
392
 
272
393
  [data-density="compact"] {
273
- --table-cell-py: 0.25rem;
394
+ --table-cell-py: 0.25rem; /* the default's, since the default is compact */
274
395
  --table-head-height: 2rem;
275
396
  }
276
397
 
@@ -307,14 +428,12 @@
307
428
  --text-xl: 1.5625rem; /* 20 → 25px */
308
429
  --text-2xl: 1.875rem; /* 24 → 30px */
309
430
  --control-text-sm: 1rem; /* 12.8 → 16px */
310
- --radius: 0.75rem; /* 10 → 12px, so larger controls keep their shape */
431
+ --radius: 0.6875rem; /* 9 → 11px, so larger controls keep their shape */
311
432
 
312
- --type-page-title-size: 1.375rem;
313
- --type-page-title-leading: 2.125rem;
433
+ --type-page-title-size: 2.1875rem; /* 28 → 35px */
314
434
  --type-section-title-size: 1.25rem;
315
435
  --type-section-title-leading: 1.875rem;
316
436
  --type-body-size: 1.125rem;
317
- --type-body-leading: 1.625rem;
318
437
  --type-label-size: 1.125rem;
319
438
  --type-label-leading: 1.625rem;
320
439
  --type-caption-size: 0.9375rem;
@@ -398,6 +517,9 @@
398
517
  --chart-3: var(--gray-600);
399
518
  --chart-4: var(--gray-700);
400
519
  --chart-5: var(--gray-900);
520
+ --accent-second: var(--gray-700);
521
+ --card-shadow: 0 6px 14px oklch(0 0 0 / 30%);
522
+ --card-header: color-mix(in srgb, var(--muted) 60%, var(--card));
401
523
  --sidebar: var(--gray-950);
402
524
  --sidebar-foreground: var(--gray-50);
403
525
  --sidebar-primary: var(--blue-500);
@@ -407,3 +529,191 @@
407
529
  --sidebar-border: var(--white-a10);
408
530
  --sidebar-ring: var(--gray-500);
409
531
  }
532
+
533
+ /*
534
+ * Palettes (design-language.md "Palettes"): an app's own dark look.
535
+ * FoundationProviders' `palette` puts data-palette on <html> and forces
536
+ * the dark theme, so a block below always sits on top of .dark and sets
537
+ * only what differs from it. The solid fills (--destructive, --success,
538
+ * --warning, --info, each with white -foreground), --info-text, the
539
+ * highlight washes and the categorical slots stay .dark's.
540
+ *
541
+ * --ring is the accent (4.24 to 5.25 on the page), not the guide's
542
+ * accent deep (2.30 to 2.91, under the 3:1 a focus ring needs).
543
+ * --link is accent light: the accent fails 4.5 as small text on 2 and 4.
544
+ * --*-text are the shared status shades; the fills are unchanged.
545
+ * --sidebar-accent, the active and hovered item, is a soft accent tint
546
+ * on the panel (16%): text on it 10.8 / 11.6 / 10.9 / 11.2, dim 5.35
547
+ * at the least.
548
+ * Derived in .dark and resolved with these values, since both blocks
549
+ * apply to <html>: --table-stripe (dim on it 6.86 at the
550
+ * least), --table-highlight (dim 6.30), --heading-band (text 7.79).
551
+ * --rule is the strong border (2.53 to 2.89; .dark's was 2.54).
552
+ *
553
+ * Printed notes still print light: index.css's print override is more
554
+ * specific than these blocks (tests: entity-view-print.spec.ts).
555
+ */
556
+
557
+ .dark[data-palette="indigo-cyan"] {
558
+ --background: var(--palette-indigo-cyan-background);
559
+ --foreground: var(--palette-indigo-cyan-text);
560
+ --card: var(--palette-indigo-cyan-panel);
561
+ --card-foreground: var(--palette-indigo-cyan-text);
562
+ --popover: var(--palette-indigo-cyan-panel);
563
+ --popover-foreground: var(--palette-indigo-cyan-text);
564
+ --primary: var(--palette-indigo-cyan-accent);
565
+ --primary-foreground: var(--palette-indigo-cyan-on-accent);
566
+ --secondary: var(--palette-indigo-cyan-raised);
567
+ --secondary-foreground: var(--palette-indigo-cyan-text);
568
+ --muted: var(--palette-indigo-cyan-raised);
569
+ --muted-foreground: var(--palette-indigo-cyan-dim);
570
+ --accent: var(--palette-indigo-cyan-raised);
571
+ --accent-foreground: var(--palette-indigo-cyan-text);
572
+ --accent-second: var(--palette-indigo-cyan-second);
573
+ --destructive-text: var(--palette-danger);
574
+ --success-text: var(--palette-success);
575
+ --warning-text: var(--palette-warning);
576
+ --link: var(--palette-indigo-cyan-accent-light);
577
+ --heading-bar: var(--palette-indigo-cyan-accent-light);
578
+ --rule: var(--palette-indigo-cyan-strong-border);
579
+ --table-highlight-muted-foreground: var(--palette-indigo-cyan-dim);
580
+ --border: var(--palette-indigo-cyan-border);
581
+ --input: var(--palette-indigo-cyan-strong-border);
582
+ --ring: var(--palette-indigo-cyan-accent);
583
+ --chart-1: var(--palette-indigo-cyan-accent-light);
584
+ --chart-2: var(--palette-indigo-cyan-accent);
585
+ --chart-3: var(--palette-indigo-cyan-accent-deep);
586
+ --chart-4: var(--palette-indigo-cyan-second);
587
+ --chart-5: var(--palette-indigo-cyan-border);
588
+ --sidebar: var(--palette-indigo-cyan-panel);
589
+ --sidebar-foreground: var(--palette-indigo-cyan-text);
590
+ --sidebar-primary: var(--palette-indigo-cyan-accent);
591
+ --sidebar-primary-foreground: var(--palette-indigo-cyan-on-accent);
592
+ --sidebar-accent: color-mix(in srgb, var(--palette-indigo-cyan-accent) 16%, var(--palette-indigo-cyan-panel));
593
+ --sidebar-accent-foreground: var(--palette-indigo-cyan-text);
594
+ --sidebar-border: var(--palette-indigo-cyan-border);
595
+ --sidebar-ring: var(--palette-indigo-cyan-accent);
596
+ }
597
+
598
+ .dark[data-palette="indigo-rose"] {
599
+ --background: var(--palette-indigo-rose-background);
600
+ --foreground: var(--palette-indigo-rose-text);
601
+ --card: var(--palette-indigo-rose-panel);
602
+ --card-foreground: var(--palette-indigo-rose-text);
603
+ --popover: var(--palette-indigo-rose-panel);
604
+ --popover-foreground: var(--palette-indigo-rose-text);
605
+ --primary: var(--palette-indigo-rose-accent);
606
+ --primary-foreground: var(--palette-indigo-rose-on-accent);
607
+ --secondary: var(--palette-indigo-rose-raised);
608
+ --secondary-foreground: var(--palette-indigo-rose-text);
609
+ --muted: var(--palette-indigo-rose-raised);
610
+ --muted-foreground: var(--palette-indigo-rose-dim);
611
+ --accent: var(--palette-indigo-rose-raised);
612
+ --accent-foreground: var(--palette-indigo-rose-text);
613
+ --accent-second: var(--palette-indigo-rose-second);
614
+ --destructive-text: var(--palette-danger);
615
+ --success-text: var(--palette-success);
616
+ --warning-text: var(--palette-warning);
617
+ --link: var(--palette-indigo-rose-accent-light);
618
+ --heading-bar: var(--palette-indigo-rose-accent-light);
619
+ --rule: var(--palette-indigo-rose-strong-border);
620
+ --table-highlight-muted-foreground: var(--palette-indigo-rose-dim);
621
+ --border: var(--palette-indigo-rose-border);
622
+ --input: var(--palette-indigo-rose-strong-border);
623
+ --ring: var(--palette-indigo-rose-accent);
624
+ --chart-1: var(--palette-indigo-rose-accent-light);
625
+ --chart-2: var(--palette-indigo-rose-accent);
626
+ --chart-3: var(--palette-indigo-rose-accent-deep);
627
+ --chart-4: var(--palette-indigo-rose-second);
628
+ --chart-5: var(--palette-indigo-rose-border);
629
+ --sidebar: var(--palette-indigo-rose-panel);
630
+ --sidebar-foreground: var(--palette-indigo-rose-text);
631
+ --sidebar-primary: var(--palette-indigo-rose-accent);
632
+ --sidebar-primary-foreground: var(--palette-indigo-rose-on-accent);
633
+ --sidebar-accent: color-mix(in srgb, var(--palette-indigo-rose-accent) 16%, var(--palette-indigo-rose-panel));
634
+ --sidebar-accent-foreground: var(--palette-indigo-rose-text);
635
+ --sidebar-border: var(--palette-indigo-rose-border);
636
+ --sidebar-ring: var(--palette-indigo-rose-accent);
637
+ }
638
+
639
+ .dark[data-palette="graphite-periwinkle"] {
640
+ --background: var(--palette-graphite-periwinkle-background);
641
+ --foreground: var(--palette-graphite-periwinkle-text);
642
+ --card: var(--palette-graphite-periwinkle-panel);
643
+ --card-foreground: var(--palette-graphite-periwinkle-text);
644
+ --popover: var(--palette-graphite-periwinkle-panel);
645
+ --popover-foreground: var(--palette-graphite-periwinkle-text);
646
+ --primary: var(--palette-graphite-periwinkle-accent);
647
+ --primary-foreground: var(--palette-graphite-periwinkle-on-accent);
648
+ --secondary: var(--palette-graphite-periwinkle-raised);
649
+ --secondary-foreground: var(--palette-graphite-periwinkle-text);
650
+ --muted: var(--palette-graphite-periwinkle-raised);
651
+ --muted-foreground: var(--palette-graphite-periwinkle-dim);
652
+ --accent: var(--palette-graphite-periwinkle-raised);
653
+ --accent-foreground: var(--palette-graphite-periwinkle-text);
654
+ --accent-second: var(--palette-graphite-periwinkle-second);
655
+ --destructive-text: var(--palette-danger);
656
+ --success-text: var(--palette-success);
657
+ --warning-text: var(--palette-warning);
658
+ --link: var(--palette-graphite-periwinkle-accent-light);
659
+ --heading-bar: var(--palette-graphite-periwinkle-accent-light);
660
+ --rule: var(--palette-graphite-periwinkle-strong-border);
661
+ --table-highlight-muted-foreground: var(--palette-graphite-periwinkle-dim);
662
+ --border: var(--palette-graphite-periwinkle-border);
663
+ --input: var(--palette-graphite-periwinkle-strong-border);
664
+ --ring: var(--palette-graphite-periwinkle-accent);
665
+ --chart-1: var(--palette-graphite-periwinkle-accent-light);
666
+ --chart-2: var(--palette-graphite-periwinkle-accent);
667
+ --chart-3: var(--palette-graphite-periwinkle-accent-deep);
668
+ --chart-4: var(--palette-graphite-periwinkle-second);
669
+ --chart-5: var(--palette-graphite-periwinkle-border);
670
+ --sidebar: var(--palette-graphite-periwinkle-panel);
671
+ --sidebar-foreground: var(--palette-graphite-periwinkle-text);
672
+ --sidebar-primary: var(--palette-graphite-periwinkle-accent);
673
+ --sidebar-primary-foreground: var(--palette-graphite-periwinkle-on-accent);
674
+ --sidebar-accent: color-mix(in srgb, var(--palette-graphite-periwinkle-accent) 16%, var(--palette-graphite-periwinkle-panel));
675
+ --sidebar-accent-foreground: var(--palette-graphite-periwinkle-text);
676
+ --sidebar-border: var(--palette-graphite-periwinkle-border);
677
+ --sidebar-ring: var(--palette-graphite-periwinkle-accent);
678
+ }
679
+
680
+ .dark[data-palette="violet"] {
681
+ --background: var(--palette-violet-background);
682
+ --foreground: var(--palette-violet-text);
683
+ --card: var(--palette-violet-panel);
684
+ --card-foreground: var(--palette-violet-text);
685
+ --popover: var(--palette-violet-panel);
686
+ --popover-foreground: var(--palette-violet-text);
687
+ --primary: var(--palette-violet-accent);
688
+ --primary-foreground: var(--palette-violet-on-accent);
689
+ --secondary: var(--palette-violet-raised);
690
+ --secondary-foreground: var(--palette-violet-text);
691
+ --muted: var(--palette-violet-raised);
692
+ --muted-foreground: var(--palette-violet-dim);
693
+ --accent: var(--palette-violet-raised);
694
+ --accent-foreground: var(--palette-violet-text);
695
+ --accent-second: var(--palette-violet-second);
696
+ --destructive-text: var(--palette-danger);
697
+ --success-text: var(--palette-success);
698
+ --warning-text: var(--palette-warning);
699
+ --link: var(--palette-violet-accent-light);
700
+ --heading-bar: var(--palette-violet-accent-light);
701
+ --rule: var(--palette-violet-strong-border);
702
+ --table-highlight-muted-foreground: var(--palette-violet-dim);
703
+ --border: var(--palette-violet-border);
704
+ --input: var(--palette-violet-strong-border);
705
+ --ring: var(--palette-violet-accent);
706
+ --chart-1: var(--palette-violet-accent-light);
707
+ --chart-2: var(--palette-violet-accent);
708
+ --chart-3: var(--palette-violet-accent-deep);
709
+ --chart-4: var(--palette-violet-second);
710
+ --chart-5: var(--palette-violet-border);
711
+ --sidebar: var(--palette-violet-panel);
712
+ --sidebar-foreground: var(--palette-violet-text);
713
+ --sidebar-primary: var(--palette-violet-accent);
714
+ --sidebar-primary-foreground: var(--palette-violet-on-accent);
715
+ --sidebar-accent: color-mix(in srgb, var(--palette-violet-accent) 16%, var(--palette-violet-panel));
716
+ --sidebar-accent-foreground: var(--palette-violet-text);
717
+ --sidebar-border: var(--palette-violet-border);
718
+ --sidebar-ring: var(--palette-violet-accent);
719
+ }
package/vite/index.d.ts CHANGED
@@ -31,7 +31,13 @@ export type PwaOptions = {
31
31
  /** A shorter one for under the icon. Defaults to `name`; its first letter is the SVG icon's. */
32
32
  shortName?: string
33
33
  description?: string
34
- /** `#rrggbb`. Defaults to the package's `--primary` token. */
34
+ /**
35
+ * The app's palette, the same one FoundationProviders names: the colours
36
+ * below default to its accent, its text on the accent and its background
37
+ * instead of the light theme's.
38
+ */
39
+ palette?: 'indigo-cyan' | 'indigo-rose' | 'graphite-periwinkle' | 'violet'
40
+ /** `#rrggbb`. Defaults to the package's `--primary` token (or the palette's accent). */
35
41
  themeColor?: string
36
42
  /** The letter's colour on the SVG icon, `#rrggbb`. Defaults to `--primary-foreground`. */
37
43
  themeForegroundColor?: string
package/vite/pwa.js CHANGED
@@ -18,7 +18,8 @@
18
18
  //
19
19
  // Colours default to the package's tokens (styles/theme.css: --primary,
20
20
  // --primary-foreground, --background), converted to hex as a manifest
21
- // needs. An app that changed its tokens passes its own.
21
+ // needs, or to its palette's with `palette` (the one FoundationProviders
22
+ // names). An app that changed its tokens passes its own.
22
23
 
23
24
  import fs from 'node:fs'
24
25
  import path from 'node:path'
@@ -34,7 +35,10 @@ const HEX = /^#[0-9a-f]{6}$/i
34
35
 
35
36
  /** The value of `--name` in theme.css's :root block, var() followed. */
36
37
  export function themeToken(css, name, seen = new Set()) {
37
- const root = css.slice(css.indexOf(':root'), css.indexOf('.dark'))
38
+ // The first :root block, to its closing brace: a comment inside it may
39
+ // name other selectors (.dark, a palette's), so it can't end at one.
40
+ const start = css.indexOf(':root {')
41
+ const root = css.slice(start, css.indexOf('\n}', start))
38
42
  const match = new RegExp(`--${name}:\\s*([^;]+);`).exec(root)
39
43
  if (!match) throw new Error(`pwa: no --${name} in the theme.`)
40
44
  const value = match[1].trim()
@@ -71,12 +75,22 @@ export function oklchToHex(value) {
71
75
  return `#${linear.map((x) => toByte(x).toString(16).padStart(2, '0')).join('')}`
72
76
  }
73
77
 
74
- function colour(value, token) {
78
+ // A palette's own colours for each token pwa() reads (styles/theme.css's
79
+ // --palette-<name>-* primitives, already #rrggbb).
80
+ const PALETTE_PARTS = { primary: 'accent', 'primary-foreground': 'on-accent', background: 'background' }
81
+
82
+ function colour(value, token, palette) {
75
83
  if (value !== undefined) {
76
84
  if (!HEX.test(value)) throw new Error(`pwa: ${token} must be #rrggbb, not ${value}.`)
77
85
  return value.toLowerCase()
78
86
  }
79
- return oklchToHex(themeToken(fs.readFileSync(THEME_CSS, 'utf8'), token))
87
+ const css = fs.readFileSync(THEME_CSS, 'utf8')
88
+ if (palette !== undefined) {
89
+ const hex = themeToken(css, `palette-${palette}-${PALETTE_PARTS[token]}`)
90
+ if (!HEX.test(hex)) throw new Error(`pwa: no palette "${palette}" in the theme.`)
91
+ return hex.toLowerCase()
92
+ }
93
+ return oklchToHex(themeToken(css, token))
80
94
  }
81
95
 
82
96
  // ---------------------------------------------------------------- icons
@@ -145,9 +159,9 @@ export function pwaFiles(options, base = '/', root = process.cwd()) {
145
159
  if (!options?.name?.trim()) throw new Error('pwa: `name` is required (the app as the Home Screen names it).')
146
160
  const name = options.name.trim()
147
161
  const shortName = options.shortName?.trim() || name
148
- const theme = colour(options.themeColor, 'primary')
149
- const onTheme = colour(options.themeForegroundColor, 'primary-foreground')
150
- const background = colour(options.backgroundColor, 'background')
162
+ const theme = colour(options.themeColor, 'primary', options.palette)
163
+ const onTheme = colour(options.themeForegroundColor, 'primary-foreground', options.palette)
164
+ const background = colour(options.backgroundColor, 'background', options.palette)
151
165
  const at = (file) => `${base}${file}`
152
166
 
153
167
  /** @type {Map<string, { type: string, source: string | Buffer }>} */