@lilydesignsystem/themes 0.2.0 → 0.3.1

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/dist/abyss.css +55 -7
  2. package/dist/acid.css +55 -7
  3. package/dist/adobe-spectrum.css +55 -7
  4. package/dist/aqua.css +55 -7
  5. package/dist/autumn.css +55 -7
  6. package/dist/black.css +55 -7
  7. package/dist/bumblebee.css +55 -7
  8. package/dist/business.css +55 -7
  9. package/dist/caramellatte.css +55 -7
  10. package/dist/cmyk.css +55 -7
  11. package/dist/coffee.css +55 -7
  12. package/dist/corporate.css +55 -7
  13. package/dist/cupcake.css +55 -7
  14. package/dist/cyberpunk.css +55 -7
  15. package/dist/dark.css +55 -7
  16. package/dist/dim.css +55 -7
  17. package/dist/dracula.css +55 -7
  18. package/dist/emerald.css +55 -7
  19. package/dist/fantasy.css +55 -7
  20. package/dist/forest.css +55 -7
  21. package/dist/garden.css +55 -7
  22. package/dist/halloween.css +55 -7
  23. package/dist/lemonade.css +55 -7
  24. package/dist/light.css +55 -7
  25. package/dist/lofi.css +55 -7
  26. package/dist/luxury.css +55 -7
  27. package/dist/mozilla-protocol.css +55 -7
  28. package/dist/night.css +55 -7
  29. package/dist/nord.css +55 -7
  30. package/dist/pastel.css +55 -7
  31. package/dist/retro.css +55 -7
  32. package/dist/silk.css +55 -7
  33. package/dist/sunset.css +55 -7
  34. package/dist/synthwave.css +55 -7
  35. package/dist/united-kingdom-government-digital-service.css +55 -7
  36. package/dist/united-kingdom-national-health-service-england-for-patients.css +55 -7
  37. package/dist/united-kingdom-national-health-service-england-for-practitioners.css +55 -7
  38. package/dist/united-kingdom-national-health-service-scotland-for-patients.css +55 -7
  39. package/dist/united-kingdom-national-health-service-scotland-for-practitioners.css +55 -7
  40. package/dist/united-kingdom-national-health-service-wales-for-patients.css +55 -7
  41. package/dist/united-kingdom-national-health-service-wales-for-practitioners.css +55 -7
  42. package/dist/united-states-web-design-system.css +55 -7
  43. package/dist/valentine.css +55 -7
  44. package/dist/winter.css +55 -7
  45. package/dist/wireframe.css +55 -7
  46. package/package.json +1 -1
@@ -196,6 +196,14 @@
196
196
  /* Primary body-copy colour. Maps to the theme's high-contrast
197
197
  reading colour. */
198
198
  --lily-text: var(--color-base-content);
199
+ /* Error text: the theme's error colour pulled toward the theme's own reading colour (`--color-base-content`, not `--lily-text`, which a host page may override) so it
200
+ keeps WCAG contrast as text on the page and on the error-tinted
201
+ callout surface (`--color-error` itself is a fill colour). */
202
+ --lily-error-text: color-mix(
203
+ in oklch,
204
+ var(--color-error) 22%,
205
+ var(--color-base-content)
206
+ );
199
207
  /* Muted / secondary text — mixed 70% with transparent to keep the
200
208
  hue but lower contrast for captions, helptext, placeholders. */
201
209
  --lily-text-muted: color-mix(
@@ -935,7 +943,7 @@
935
943
  }
936
944
  :where(.dont-list-item)::marker {
937
945
  content: "✗ ";
938
- color: var(--color-error);
946
+ color: var(--lily-error-text);
939
947
  }
940
948
 
941
949
  /* --- Tree --------------------------------------------------------------- */
@@ -1017,7 +1025,7 @@
1017
1025
  color: var(--color-success);
1018
1026
  }
1019
1027
  :where(.validation-list-item[data-status="failed"]) {
1020
- color: var(--color-error);
1028
+ color: var(--lily-error-text);
1021
1029
  }
1022
1030
  :where(.validation-list-item[data-status="pending"]) {
1023
1031
  color: var(--lily-text-muted);
@@ -1375,7 +1383,7 @@
1375
1383
 
1376
1384
  /* Error message — inline form-validation message. */
1377
1385
  :where(.error-message) {
1378
- color: var(--color-error);
1386
+ color: var(--lily-error-text);
1379
1387
  font-weight: 600;
1380
1388
  }
1381
1389
  /* Error summary — heavyweight 4px border (GOV.UK pattern) so users
@@ -2589,6 +2597,7 @@
2589
2597
  .theme-picker-list,
2590
2598
  .locale-picker-list,
2591
2599
  .text-size-picker-list,
2600
+ .motion-picker-list,
2592
2601
  .share-picker-list,
2593
2602
  .search-picker-panel
2594
2603
  ) {
@@ -2614,17 +2623,56 @@
2614
2623
  .theme-picker-list,
2615
2624
  .locale-picker-list,
2616
2625
  .text-size-picker-list,
2626
+ .motion-picker-list,
2617
2627
  .share-picker-list,
2618
2628
  .search-picker-panel
2619
2629
  )[hidden] {
2620
2630
  display: none;
2621
2631
  }
2622
2632
 
2623
- :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list)
2633
+ /* Popups open under their button, aligned to its start edge, and flip to
2634
+ its end edge (or above it) when they would run off the viewport. A header
2635
+ picker near the right edge otherwise overflows, and focusing the popup
2636
+ scrolls the whole page sideways. `position: fixed` makes the viewport the
2637
+ containing block that the flip fallbacks are checked against; browsers
2638
+ without anchor positioning keep the absolute placement above. */
2639
+ @supports (position-area: block-end) {
2640
+ :where(.theme-picker) { anchor-name: --lily-theme-picker; }
2641
+ :where(.locale-picker) { anchor-name: --lily-locale-picker; }
2642
+ :where(.text-size-picker) { anchor-name: --lily-text-size-picker; }
2643
+ :where(.motion-picker) { anchor-name: --lily-motion-picker; }
2644
+ :where(.share-picker) { anchor-name: --lily-share-picker; }
2645
+ :where(.search-picker) { anchor-name: --lily-search-picker; }
2646
+ :where(.theme-picker-list) { position-anchor: --lily-theme-picker; }
2647
+ :where(.locale-picker-list) { position-anchor: --lily-locale-picker; }
2648
+ :where(.text-size-picker-list) { position-anchor: --lily-text-size-picker; }
2649
+ :where(.motion-picker-list) { position-anchor: --lily-motion-picker; }
2650
+ :where(.share-picker-list) { position-anchor: --lily-share-picker; }
2651
+ :where(.search-picker-panel) { position-anchor: --lily-search-picker; }
2652
+ :where(
2653
+ .theme-picker-list,
2654
+ .locale-picker-list,
2655
+ .text-size-picker-list,
2656
+ .motion-picker-list,
2657
+ .share-picker-list,
2658
+ .search-picker-panel
2659
+ ) {
2660
+ position: fixed;
2661
+ inset: auto;
2662
+ position-area: block-end span-inline-end;
2663
+ position-try-fallbacks: flip-inline, flip-block, flip-block flip-inline;
2664
+ min-width: 0;
2665
+ width: max-content;
2666
+ max-width: calc(100vw - 1rem);
2667
+ }
2668
+ }
2669
+
2670
+ :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list, .motion-picker-list)
2624
2671
  > :where(
2625
2672
  .theme-picker-option,
2626
2673
  .locale-picker-option,
2627
- .text-size-picker-option
2674
+ .text-size-picker-option,
2675
+ .motion-picker-option
2628
2676
  ) {
2629
2677
  padding: var(--lily-space-xs) var(--lily-space-sm);
2630
2678
  border-radius: var(--lily-radius-sm);
@@ -2633,7 +2681,7 @@
2633
2681
 
2634
2682
  /* data-active is the keyboard cursor (aria-activedescendant); focus
2635
2683
  stays on the listbox, so this is the only visible cue for it. */
2636
- :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list)
2684
+ :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list, .motion-picker-list)
2637
2685
  > :where([data-active]) {
2638
2686
  background-color: var(--lily-surface-alt);
2639
2687
  outline: var(--lily-focus-ring);
@@ -2642,7 +2690,7 @@
2642
2690
 
2643
2691
  /* aria-selected marks the applied theme/locale, which is not the
2644
2692
  same thing as the keyboard cursor. Both can be visible at once. */
2645
- :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list)
2693
+ :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list, .motion-picker-list)
2646
2694
  > :where([aria-selected="true"]) {
2647
2695
  font-weight: 600;
2648
2696
  background-color: var(--lily-surface-sunken);
@@ -245,6 +245,14 @@
245
245
  /* Primary body-copy colour. Maps to the theme's high-contrast
246
246
  reading colour. */
247
247
  --lily-text: var(--color-base-content);
248
+ /* Error text: the theme's error colour pulled toward the theme's own reading colour (`--color-base-content`, not `--lily-text`, which a host page may override) so it
249
+ keeps WCAG contrast as text on the page and on the error-tinted
250
+ callout surface (`--color-error` itself is a fill colour). */
251
+ --lily-error-text: color-mix(
252
+ in oklch,
253
+ var(--color-error) 22%,
254
+ var(--color-base-content)
255
+ );
248
256
  /* Muted / secondary text — mixed 70% with transparent to keep the
249
257
  hue but lower contrast for captions, helptext, placeholders. */
250
258
  --lily-text-muted: color-mix(
@@ -1014,7 +1022,7 @@
1014
1022
  }
1015
1023
  :where(.dont-list-item)::marker {
1016
1024
  content: "✗ ";
1017
- color: var(--color-error);
1025
+ color: var(--lily-error-text);
1018
1026
  }
1019
1027
 
1020
1028
  /* --- Tree --------------------------------------------------------------- */
@@ -1096,7 +1104,7 @@
1096
1104
  color: var(--color-success);
1097
1105
  }
1098
1106
  :where(.validation-list-item[data-status="failed"]) {
1099
- color: var(--color-error);
1107
+ color: var(--lily-error-text);
1100
1108
  }
1101
1109
  :where(.validation-list-item[data-status="pending"]) {
1102
1110
  color: var(--lily-text-muted);
@@ -1460,7 +1468,7 @@
1460
1468
 
1461
1469
  /* Error message — inline form-validation message. */
1462
1470
  :where(.error-message) {
1463
- color: var(--color-error);
1471
+ color: var(--lily-error-text);
1464
1472
  font-weight: 600;
1465
1473
  }
1466
1474
  /* Error summary — heavyweight 4px border (GOV.UK pattern) so users
@@ -2680,6 +2688,7 @@
2680
2688
  .theme-picker-list,
2681
2689
  .locale-picker-list,
2682
2690
  .text-size-picker-list,
2691
+ .motion-picker-list,
2683
2692
  .share-picker-list,
2684
2693
  .search-picker-panel
2685
2694
  ) {
@@ -2705,17 +2714,56 @@
2705
2714
  .theme-picker-list,
2706
2715
  .locale-picker-list,
2707
2716
  .text-size-picker-list,
2717
+ .motion-picker-list,
2708
2718
  .share-picker-list,
2709
2719
  .search-picker-panel
2710
2720
  )[hidden] {
2711
2721
  display: none;
2712
2722
  }
2713
2723
 
2714
- :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list)
2724
+ /* Popups open under their button, aligned to its start edge, and flip to
2725
+ its end edge (or above it) when they would run off the viewport. A header
2726
+ picker near the right edge otherwise overflows, and focusing the popup
2727
+ scrolls the whole page sideways. `position: fixed` makes the viewport the
2728
+ containing block that the flip fallbacks are checked against; browsers
2729
+ without anchor positioning keep the absolute placement above. */
2730
+ @supports (position-area: block-end) {
2731
+ :where(.theme-picker) { anchor-name: --lily-theme-picker; }
2732
+ :where(.locale-picker) { anchor-name: --lily-locale-picker; }
2733
+ :where(.text-size-picker) { anchor-name: --lily-text-size-picker; }
2734
+ :where(.motion-picker) { anchor-name: --lily-motion-picker; }
2735
+ :where(.share-picker) { anchor-name: --lily-share-picker; }
2736
+ :where(.search-picker) { anchor-name: --lily-search-picker; }
2737
+ :where(.theme-picker-list) { position-anchor: --lily-theme-picker; }
2738
+ :where(.locale-picker-list) { position-anchor: --lily-locale-picker; }
2739
+ :where(.text-size-picker-list) { position-anchor: --lily-text-size-picker; }
2740
+ :where(.motion-picker-list) { position-anchor: --lily-motion-picker; }
2741
+ :where(.share-picker-list) { position-anchor: --lily-share-picker; }
2742
+ :where(.search-picker-panel) { position-anchor: --lily-search-picker; }
2743
+ :where(
2744
+ .theme-picker-list,
2745
+ .locale-picker-list,
2746
+ .text-size-picker-list,
2747
+ .motion-picker-list,
2748
+ .share-picker-list,
2749
+ .search-picker-panel
2750
+ ) {
2751
+ position: fixed;
2752
+ inset: auto;
2753
+ position-area: block-end span-inline-end;
2754
+ position-try-fallbacks: flip-inline, flip-block, flip-block flip-inline;
2755
+ min-width: 0;
2756
+ width: max-content;
2757
+ max-width: calc(100vw - 1rem);
2758
+ }
2759
+ }
2760
+
2761
+ :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list, .motion-picker-list)
2715
2762
  > :where(
2716
2763
  .theme-picker-option,
2717
2764
  .locale-picker-option,
2718
- .text-size-picker-option
2765
+ .text-size-picker-option,
2766
+ .motion-picker-option
2719
2767
  ) {
2720
2768
  padding: var(--lily-space-xs) var(--lily-space-sm);
2721
2769
  border-radius: var(--lily-radius-sm);
@@ -2724,7 +2772,7 @@
2724
2772
 
2725
2773
  /* data-active is the keyboard cursor (aria-activedescendant); focus
2726
2774
  stays on the listbox, so this is the only visible cue for it. */
2727
- :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list)
2775
+ :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list, .motion-picker-list)
2728
2776
  > :where([data-active]) {
2729
2777
  background-color: var(--lily-surface-alt);
2730
2778
  outline: var(--lily-focus-ring);
@@ -2733,7 +2781,7 @@
2733
2781
 
2734
2782
  /* aria-selected marks the applied theme/locale, which is not the
2735
2783
  same thing as the keyboard cursor. Both can be visible at once. */
2736
- :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list)
2784
+ :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list, .motion-picker-list)
2737
2785
  > :where([aria-selected="true"]) {
2738
2786
  font-weight: 600;
2739
2787
  background-color: var(--lily-surface-sunken);
@@ -299,6 +299,14 @@
299
299
  /* Primary body-copy colour. Maps to the theme's high-contrast
300
300
  reading colour. */
301
301
  --lily-text: var(--color-base-content);
302
+ /* Error text: the theme's error colour pulled toward the theme's own reading colour (`--color-base-content`, not `--lily-text`, which a host page may override) so it
303
+ keeps WCAG contrast as text on the page and on the error-tinted
304
+ callout surface (`--color-error` itself is a fill colour). */
305
+ --lily-error-text: color-mix(
306
+ in oklch,
307
+ var(--color-error) 22%,
308
+ var(--color-base-content)
309
+ );
302
310
  /* Muted / secondary text — mixed 70% with transparent to keep the
303
311
  hue but lower contrast for captions, helptext, placeholders. */
304
312
  --lily-text-muted: color-mix(
@@ -1038,7 +1046,7 @@
1038
1046
  }
1039
1047
  :where(.dont-list-item)::marker {
1040
1048
  content: "✗ ";
1041
- color: var(--color-error);
1049
+ color: var(--lily-error-text);
1042
1050
  }
1043
1051
 
1044
1052
  /* --- Tree --------------------------------------------------------------- */
@@ -1120,7 +1128,7 @@
1120
1128
  color: var(--color-success);
1121
1129
  }
1122
1130
  :where(.validation-list-item[data-status="failed"]) {
1123
- color: var(--color-error);
1131
+ color: var(--lily-error-text);
1124
1132
  }
1125
1133
  :where(.validation-list-item[data-status="pending"]) {
1126
1134
  color: var(--lily-text-muted);
@@ -1478,7 +1486,7 @@
1478
1486
 
1479
1487
  /* Error message — inline form-validation message. */
1480
1488
  :where(.error-message) {
1481
- color: var(--color-error);
1489
+ color: var(--lily-error-text);
1482
1490
  font-weight: 600;
1483
1491
  }
1484
1492
  /* Error summary — heavyweight 4px border (GOV.UK pattern) so users
@@ -2692,6 +2700,7 @@
2692
2700
  .theme-picker-list,
2693
2701
  .locale-picker-list,
2694
2702
  .text-size-picker-list,
2703
+ .motion-picker-list,
2695
2704
  .share-picker-list,
2696
2705
  .search-picker-panel
2697
2706
  ) {
@@ -2717,17 +2726,56 @@
2717
2726
  .theme-picker-list,
2718
2727
  .locale-picker-list,
2719
2728
  .text-size-picker-list,
2729
+ .motion-picker-list,
2720
2730
  .share-picker-list,
2721
2731
  .search-picker-panel
2722
2732
  )[hidden] {
2723
2733
  display: none;
2724
2734
  }
2725
2735
 
2726
- :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list)
2736
+ /* Popups open under their button, aligned to its start edge, and flip to
2737
+ its end edge (or above it) when they would run off the viewport. A header
2738
+ picker near the right edge otherwise overflows, and focusing the popup
2739
+ scrolls the whole page sideways. `position: fixed` makes the viewport the
2740
+ containing block that the flip fallbacks are checked against; browsers
2741
+ without anchor positioning keep the absolute placement above. */
2742
+ @supports (position-area: block-end) {
2743
+ :where(.theme-picker) { anchor-name: --lily-theme-picker; }
2744
+ :where(.locale-picker) { anchor-name: --lily-locale-picker; }
2745
+ :where(.text-size-picker) { anchor-name: --lily-text-size-picker; }
2746
+ :where(.motion-picker) { anchor-name: --lily-motion-picker; }
2747
+ :where(.share-picker) { anchor-name: --lily-share-picker; }
2748
+ :where(.search-picker) { anchor-name: --lily-search-picker; }
2749
+ :where(.theme-picker-list) { position-anchor: --lily-theme-picker; }
2750
+ :where(.locale-picker-list) { position-anchor: --lily-locale-picker; }
2751
+ :where(.text-size-picker-list) { position-anchor: --lily-text-size-picker; }
2752
+ :where(.motion-picker-list) { position-anchor: --lily-motion-picker; }
2753
+ :where(.share-picker-list) { position-anchor: --lily-share-picker; }
2754
+ :where(.search-picker-panel) { position-anchor: --lily-search-picker; }
2755
+ :where(
2756
+ .theme-picker-list,
2757
+ .locale-picker-list,
2758
+ .text-size-picker-list,
2759
+ .motion-picker-list,
2760
+ .share-picker-list,
2761
+ .search-picker-panel
2762
+ ) {
2763
+ position: fixed;
2764
+ inset: auto;
2765
+ position-area: block-end span-inline-end;
2766
+ position-try-fallbacks: flip-inline, flip-block, flip-block flip-inline;
2767
+ min-width: 0;
2768
+ width: max-content;
2769
+ max-width: calc(100vw - 1rem);
2770
+ }
2771
+ }
2772
+
2773
+ :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list, .motion-picker-list)
2727
2774
  > :where(
2728
2775
  .theme-picker-option,
2729
2776
  .locale-picker-option,
2730
- .text-size-picker-option
2777
+ .text-size-picker-option,
2778
+ .motion-picker-option
2731
2779
  ) {
2732
2780
  padding: var(--lily-space-xs) var(--lily-space-sm);
2733
2781
  border-radius: var(--lily-radius-sm);
@@ -2736,7 +2784,7 @@
2736
2784
 
2737
2785
  /* data-active is the keyboard cursor (aria-activedescendant); focus
2738
2786
  stays on the listbox, so this is the only visible cue for it. */
2739
- :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list)
2787
+ :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list, .motion-picker-list)
2740
2788
  > :where([data-active]) {
2741
2789
  background-color: var(--lily-surface-alt);
2742
2790
  outline: var(--lily-focus-ring);
@@ -2745,7 +2793,7 @@
2745
2793
 
2746
2794
  /* aria-selected marks the applied theme/locale, which is not the
2747
2795
  same thing as the keyboard cursor. Both can be visible at once. */
2748
- :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list)
2796
+ :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list, .motion-picker-list)
2749
2797
  > :where([aria-selected="true"]) {
2750
2798
  font-weight: 600;
2751
2799
  background-color: var(--lily-surface-sunken);
@@ -341,6 +341,14 @@
341
341
  /* Primary body-copy colour. Maps to the theme's high-contrast
342
342
  reading colour. */
343
343
  --lily-text: var(--color-base-content);
344
+ /* Error text: the theme's error colour pulled toward the theme's own reading colour (`--color-base-content`, not `--lily-text`, which a host page may override) so it
345
+ keeps WCAG contrast as text on the page and on the error-tinted
346
+ callout surface (`--color-error` itself is a fill colour). */
347
+ --lily-error-text: color-mix(
348
+ in oklch,
349
+ var(--color-error) 22%,
350
+ var(--color-base-content)
351
+ );
344
352
  /* Muted / secondary text — mixed 70% with transparent to keep the
345
353
  hue but lower contrast for captions, helptext, placeholders. */
346
354
  --lily-text-muted: color-mix(
@@ -1110,7 +1118,7 @@
1110
1118
  }
1111
1119
  :where(.dont-list-item)::marker {
1112
1120
  content: "✗ ";
1113
- color: var(--color-error);
1121
+ color: var(--lily-error-text);
1114
1122
  }
1115
1123
 
1116
1124
  /* --- Tree --------------------------------------------------------------- */
@@ -1192,7 +1200,7 @@
1192
1200
  color: var(--color-success);
1193
1201
  }
1194
1202
  :where(.validation-list-item[data-status="failed"]) {
1195
- color: var(--color-error);
1203
+ color: var(--lily-error-text);
1196
1204
  }
1197
1205
  :where(.validation-list-item[data-status="pending"]) {
1198
1206
  color: var(--lily-text-muted);
@@ -1556,7 +1564,7 @@
1556
1564
 
1557
1565
  /* Error message — inline form-validation message. */
1558
1566
  :where(.error-message) {
1559
- color: var(--color-error);
1567
+ color: var(--lily-error-text);
1560
1568
  font-weight: 600;
1561
1569
  }
1562
1570
  /* Error summary — heavyweight 4px border (GOV.UK pattern) so users
@@ -2776,6 +2784,7 @@
2776
2784
  .theme-picker-list,
2777
2785
  .locale-picker-list,
2778
2786
  .text-size-picker-list,
2787
+ .motion-picker-list,
2779
2788
  .share-picker-list,
2780
2789
  .search-picker-panel
2781
2790
  ) {
@@ -2801,17 +2810,56 @@
2801
2810
  .theme-picker-list,
2802
2811
  .locale-picker-list,
2803
2812
  .text-size-picker-list,
2813
+ .motion-picker-list,
2804
2814
  .share-picker-list,
2805
2815
  .search-picker-panel
2806
2816
  )[hidden] {
2807
2817
  display: none;
2808
2818
  }
2809
2819
 
2810
- :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list)
2820
+ /* Popups open under their button, aligned to its start edge, and flip to
2821
+ its end edge (or above it) when they would run off the viewport. A header
2822
+ picker near the right edge otherwise overflows, and focusing the popup
2823
+ scrolls the whole page sideways. `position: fixed` makes the viewport the
2824
+ containing block that the flip fallbacks are checked against; browsers
2825
+ without anchor positioning keep the absolute placement above. */
2826
+ @supports (position-area: block-end) {
2827
+ :where(.theme-picker) { anchor-name: --lily-theme-picker; }
2828
+ :where(.locale-picker) { anchor-name: --lily-locale-picker; }
2829
+ :where(.text-size-picker) { anchor-name: --lily-text-size-picker; }
2830
+ :where(.motion-picker) { anchor-name: --lily-motion-picker; }
2831
+ :where(.share-picker) { anchor-name: --lily-share-picker; }
2832
+ :where(.search-picker) { anchor-name: --lily-search-picker; }
2833
+ :where(.theme-picker-list) { position-anchor: --lily-theme-picker; }
2834
+ :where(.locale-picker-list) { position-anchor: --lily-locale-picker; }
2835
+ :where(.text-size-picker-list) { position-anchor: --lily-text-size-picker; }
2836
+ :where(.motion-picker-list) { position-anchor: --lily-motion-picker; }
2837
+ :where(.share-picker-list) { position-anchor: --lily-share-picker; }
2838
+ :where(.search-picker-panel) { position-anchor: --lily-search-picker; }
2839
+ :where(
2840
+ .theme-picker-list,
2841
+ .locale-picker-list,
2842
+ .text-size-picker-list,
2843
+ .motion-picker-list,
2844
+ .share-picker-list,
2845
+ .search-picker-panel
2846
+ ) {
2847
+ position: fixed;
2848
+ inset: auto;
2849
+ position-area: block-end span-inline-end;
2850
+ position-try-fallbacks: flip-inline, flip-block, flip-block flip-inline;
2851
+ min-width: 0;
2852
+ width: max-content;
2853
+ max-width: calc(100vw - 1rem);
2854
+ }
2855
+ }
2856
+
2857
+ :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list, .motion-picker-list)
2811
2858
  > :where(
2812
2859
  .theme-picker-option,
2813
2860
  .locale-picker-option,
2814
- .text-size-picker-option
2861
+ .text-size-picker-option,
2862
+ .motion-picker-option
2815
2863
  ) {
2816
2864
  padding: var(--lily-space-xs) var(--lily-space-sm);
2817
2865
  border-radius: var(--lily-radius-sm);
@@ -2820,7 +2868,7 @@
2820
2868
 
2821
2869
  /* data-active is the keyboard cursor (aria-activedescendant); focus
2822
2870
  stays on the listbox, so this is the only visible cue for it. */
2823
- :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list)
2871
+ :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list, .motion-picker-list)
2824
2872
  > :where([data-active]) {
2825
2873
  background-color: var(--lily-surface-alt);
2826
2874
  outline: var(--lily-focus-ring);
@@ -2829,7 +2877,7 @@
2829
2877
 
2830
2878
  /* aria-selected marks the applied theme/locale, which is not the
2831
2879
  same thing as the keyboard cursor. Both can be visible at once. */
2832
- :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list)
2880
+ :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list, .motion-picker-list)
2833
2881
  > :where([aria-selected="true"]) {
2834
2882
  font-weight: 600;
2835
2883
  background-color: var(--lily-surface-sunken);
@@ -483,6 +483,14 @@
483
483
  /* Primary body-copy colour. Maps to the theme's high-contrast
484
484
  reading colour. */
485
485
  --lily-text: var(--color-base-content);
486
+ /* Error text: the theme's error colour pulled toward the theme's own reading colour (`--color-base-content`, not `--lily-text`, which a host page may override) so it
487
+ keeps WCAG contrast as text on the page and on the error-tinted
488
+ callout surface (`--color-error` itself is a fill colour). */
489
+ --lily-error-text: color-mix(
490
+ in oklch,
491
+ var(--color-error) 22%,
492
+ var(--color-base-content)
493
+ );
486
494
  /* Muted / secondary text — mixed 70% with transparent to keep the
487
495
  hue but lower contrast for captions, helptext, placeholders. */
488
496
  --lily-text-muted: color-mix(
@@ -1478,7 +1486,7 @@
1478
1486
  }
1479
1487
  :where(.dont-list-item)::marker {
1480
1488
  content: "✗ ";
1481
- color: var(--color-error);
1489
+ color: var(--lily-error-text);
1482
1490
  }
1483
1491
 
1484
1492
  /* --- Tree --------------------------------------------------------------- */
@@ -1560,7 +1568,7 @@
1560
1568
  color: var(--color-success);
1561
1569
  }
1562
1570
  :where(.validation-list-item[data-status="failed"]) {
1563
- color: var(--color-error);
1571
+ color: var(--lily-error-text);
1564
1572
  }
1565
1573
  :where(.validation-list-item[data-status="pending"]) {
1566
1574
  color: var(--lily-text-muted);
@@ -2032,7 +2040,7 @@
2032
2040
 
2033
2041
  /* Error message — inline form-validation message. */
2034
2042
  :where(.error-message) {
2035
- color: var(--color-error);
2043
+ color: var(--lily-error-text);
2036
2044
  font-weight: 600;
2037
2045
  }
2038
2046
  /* Error summary — heavyweight 4px border (GOV.UK pattern) so users
@@ -3363,6 +3371,7 @@
3363
3371
  .theme-picker-list,
3364
3372
  .locale-picker-list,
3365
3373
  .text-size-picker-list,
3374
+ .motion-picker-list,
3366
3375
  .share-picker-list,
3367
3376
  .search-picker-panel
3368
3377
  ) {
@@ -3388,17 +3397,56 @@
3388
3397
  .theme-picker-list,
3389
3398
  .locale-picker-list,
3390
3399
  .text-size-picker-list,
3400
+ .motion-picker-list,
3391
3401
  .share-picker-list,
3392
3402
  .search-picker-panel
3393
3403
  )[hidden] {
3394
3404
  display: none;
3395
3405
  }
3396
3406
 
3397
- :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list)
3407
+ /* Popups open under their button, aligned to its start edge, and flip to
3408
+ its end edge (or above it) when they would run off the viewport. A header
3409
+ picker near the right edge otherwise overflows, and focusing the popup
3410
+ scrolls the whole page sideways. `position: fixed` makes the viewport the
3411
+ containing block that the flip fallbacks are checked against; browsers
3412
+ without anchor positioning keep the absolute placement above. */
3413
+ @supports (position-area: block-end) {
3414
+ :where(.theme-picker) { anchor-name: --lily-theme-picker; }
3415
+ :where(.locale-picker) { anchor-name: --lily-locale-picker; }
3416
+ :where(.text-size-picker) { anchor-name: --lily-text-size-picker; }
3417
+ :where(.motion-picker) { anchor-name: --lily-motion-picker; }
3418
+ :where(.share-picker) { anchor-name: --lily-share-picker; }
3419
+ :where(.search-picker) { anchor-name: --lily-search-picker; }
3420
+ :where(.theme-picker-list) { position-anchor: --lily-theme-picker; }
3421
+ :where(.locale-picker-list) { position-anchor: --lily-locale-picker; }
3422
+ :where(.text-size-picker-list) { position-anchor: --lily-text-size-picker; }
3423
+ :where(.motion-picker-list) { position-anchor: --lily-motion-picker; }
3424
+ :where(.share-picker-list) { position-anchor: --lily-share-picker; }
3425
+ :where(.search-picker-panel) { position-anchor: --lily-search-picker; }
3426
+ :where(
3427
+ .theme-picker-list,
3428
+ .locale-picker-list,
3429
+ .text-size-picker-list,
3430
+ .motion-picker-list,
3431
+ .share-picker-list,
3432
+ .search-picker-panel
3433
+ ) {
3434
+ position: fixed;
3435
+ inset: auto;
3436
+ position-area: block-end span-inline-end;
3437
+ position-try-fallbacks: flip-inline, flip-block, flip-block flip-inline;
3438
+ min-width: 0;
3439
+ width: max-content;
3440
+ max-width: calc(100vw - 1rem);
3441
+ }
3442
+ }
3443
+
3444
+ :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list, .motion-picker-list)
3398
3445
  > :where(
3399
3446
  .theme-picker-option,
3400
3447
  .locale-picker-option,
3401
- .text-size-picker-option
3448
+ .text-size-picker-option,
3449
+ .motion-picker-option
3402
3450
  ) {
3403
3451
  padding: var(--lily-space-xs) var(--lily-space-sm);
3404
3452
  border-radius: var(--lily-radius-sm);
@@ -3407,7 +3455,7 @@
3407
3455
 
3408
3456
  /* data-active is the keyboard cursor (aria-activedescendant); focus
3409
3457
  stays on the listbox, so this is the only visible cue for it. */
3410
- :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list)
3458
+ :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list, .motion-picker-list)
3411
3459
  > :where([data-active]) {
3412
3460
  background-color: var(--lily-surface-alt);
3413
3461
  outline: var(--lily-focus-ring);
@@ -3416,7 +3464,7 @@
3416
3464
 
3417
3465
  /* aria-selected marks the applied theme/locale, which is not the
3418
3466
  same thing as the keyboard cursor. Both can be visible at once. */
3419
- :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list)
3467
+ :where(.theme-picker-list, .locale-picker-list, .text-size-picker-list, .motion-picker-list)
3420
3468
  > :where([aria-selected="true"]) {
3421
3469
  font-weight: 600;
3422
3470
  background-color: var(--lily-surface-sunken);