@frontpanel/css 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/tailwind.css CHANGED
@@ -108,6 +108,7 @@
108
108
 
109
109
  /** The page behind the device: quiet blue-grey with a faint Y2K grid. */
110
110
  @utility fp-device-backdrop {
111
+ color-scheme: var(--fp-scheme);
111
112
  background-color: var(--fp-color-backdrop);
112
113
  background-image:
113
114
  linear-gradient(var(--fp-color-grid) 1px, transparent 1px),
@@ -123,6 +124,7 @@
123
124
 
124
125
  /** Liquid-chrome ring around the device body. Diagonal so the reflection band crosses the corners. */
125
126
  @utility fp-device-rim {
127
+ color-scheme: var(--fp-scheme);
126
128
  border-radius: 36px;
127
129
  padding: 10px;
128
130
  background-image: linear-gradient(
@@ -148,10 +150,16 @@
148
150
  overflow: clip;
149
151
  border-radius: 26px;
150
152
  }
153
+
154
+ /* Forced colours drop the chrome: an outline keeps the device's edge. */
155
+ @media (forced-colors: active) {
156
+ outline: 1px solid CanvasText;
157
+ }
151
158
  }
152
159
 
153
160
  /** Molded body plastic the modules sit on. */
154
161
  @utility fp-device-body {
162
+ color-scheme: var(--fp-scheme);
155
163
  background-image: linear-gradient(180deg, var(--fp-color-shell-hi), var(--fp-color-shell) 240px);
156
164
  box-shadow:
157
165
  inset 1px 1px 0 var(--fp-color-bevel-hi),
@@ -251,6 +259,35 @@
251
259
  &:disabled {
252
260
  opacity: 0.45;
253
261
  }
262
+
263
+ /*
264
+ * Forced colours drop the chrome and its shadows: an outline keeps the button's edge, the system's
265
+ * selected colours show it pressed, and GrayText, not a fade, shows it disabled.
266
+ */
267
+ @media (forced-colors: active) {
268
+ outline: 1px solid ButtonText;
269
+ outline-offset: -1px;
270
+
271
+ &[aria-pressed="true"],
272
+ &[aria-expanded="true"],
273
+ &[aria-current="page"] {
274
+ forced-color-adjust: none;
275
+ background: SelectedItem;
276
+ box-shadow: none;
277
+ color: SelectedItemText;
278
+
279
+ /* Opted out, its focus ring would keep the skin's colour: the system's text colour shows on any ground. */
280
+ &:focus-visible {
281
+ outline-color: CanvasText;
282
+ }
283
+ }
284
+
285
+ &:disabled {
286
+ opacity: 1;
287
+ color: GrayText;
288
+ outline-color: GrayText;
289
+ }
290
+ }
254
291
  }
255
292
 
256
293
  /**
@@ -314,6 +351,18 @@
314
351
  &:disabled {
315
352
  opacity: 0.45;
316
353
  }
354
+
355
+ /* Forced colours drop the gel and its shadows: an outline keeps its edge, and GrayText, not a fade, shows it disabled. */
356
+ @media (forced-colors: active) {
357
+ outline: 1px solid ButtonText;
358
+ outline-offset: -1px;
359
+
360
+ &:disabled {
361
+ opacity: 1;
362
+ color: GrayText;
363
+ outline-color: GrayText;
364
+ }
365
+ }
317
366
  }
318
367
 
319
368
  /**
@@ -386,6 +435,34 @@
386
435
  &:disabled {
387
436
  opacity: 0.5;
388
437
  }
438
+
439
+ /*
440
+ * Forced colours drop the plastic and its ring: an outline keeps the cap's edge, the system's
441
+ * selected colours show it selected, and GrayText, not a fade, shows it disabled.
442
+ */
443
+ @media (forced-colors: active) {
444
+ outline: 1px solid ButtonText;
445
+ outline-offset: -1px;
446
+
447
+ &[aria-pressed="true"],
448
+ &[aria-checked="true"] {
449
+ forced-color-adjust: none;
450
+ background: SelectedItem;
451
+ box-shadow: none;
452
+ color: SelectedItemText;
453
+
454
+ /* Opted out, its focus ring would keep the skin's colour: the system's text colour shows on any ground. */
455
+ &:focus-visible {
456
+ outline-color: CanvasText;
457
+ }
458
+ }
459
+
460
+ &:disabled {
461
+ opacity: 1;
462
+ color: GrayText;
463
+ outline-color: GrayText;
464
+ }
465
+ }
389
466
  }
390
467
 
391
468
  /** Blue plastic for an fp-cap, with its own label colour. */
@@ -412,8 +489,879 @@
412
489
  --fp-cap-ink: var(--fp-color-cap-berry-ink);
413
490
  }
414
491
 
492
+ /**
493
+ * A control with its silkscreen label under it: a knob, fader, meter or switch and then an fp-silk.
494
+ * With `data-label-placement="start"` the label sits level with the control, before it in the
495
+ * line. A length set here, such as `--fp-fader-length`, reaches the control inside.
496
+ */
497
+ @utility fp-unit {
498
+ display: inline-flex;
499
+ flex-direction: column;
500
+ align-items: center;
501
+ gap: 0.375rem;
502
+
503
+ /* Reversed, so the markup stays control then label either way. */
504
+ &[data-label-placement="start"] {
505
+ flex-direction: row-reverse;
506
+ gap: 0.5rem;
507
+ }
508
+ }
509
+
510
+ /**
511
+ * Rotary knob: a knurled chrome skirt round a domed cap. The cap is lit from the top left and stays
512
+ * still; only its fp-knob-mark turns, by `rotate`, through 270 degrees. 48 px, 40 px with a mouse,
513
+ * trackpad or pen.
514
+ */
515
+ @utility fp-knob {
516
+ position: relative;
517
+ display: inline-block;
518
+ flex-shrink: 0;
519
+ width: 3rem;
520
+ height: 3rem;
521
+ border-radius: 50%;
522
+ background-image: repeating-conic-gradient(var(--fp-color-chrome-100) 0deg 5deg, var(--fp-color-chrome-300) 5deg 10deg);
523
+ box-shadow:
524
+ 0 0 0 1px var(--fp-color-edge),
525
+ inset 1px 1px 0 var(--fp-color-bevel-hi),
526
+ inset -1px -1px 0 var(--fp-color-bevel-lo),
527
+ 1px 2px 0 var(--fp-color-cast);
528
+ cursor: ns-resize;
529
+ touch-action: none;
530
+ user-select: none;
531
+
532
+ @media (pointer: fine) {
533
+ width: 2.5rem;
534
+ height: 2.5rem;
535
+ }
536
+
537
+ &::before {
538
+ content: "";
539
+ position: absolute;
540
+ inset: 16%;
541
+ border-radius: 50%;
542
+ background-image: radial-gradient(
543
+ circle at 30% 25%,
544
+ color-mix(in srgb, var(--fp-color-chrome-hi) 60%, var(--fp-color-chrome-100)) 0%,
545
+ var(--fp-color-chrome-100) 45%,
546
+ var(--fp-color-chrome-300) 100%
547
+ );
548
+ box-shadow:
549
+ 0 0 0 1px var(--fp-color-edge),
550
+ inset 1px 1px 0 var(--fp-color-bevel-hi),
551
+ inset -1px -1px 0 var(--fp-color-bevel-lo),
552
+ 1px 1px 2px var(--fp-color-cast);
553
+ }
554
+
555
+ /* A solid ring with a glow outside it: the glow alone fails non-text contrast on light chrome. */
556
+ &:focus-visible {
557
+ outline: 2px solid var(--fp-color-focus-ring);
558
+ outline-offset: 2px;
559
+ filter: drop-shadow(0 0 3px var(--fp-color-focus));
560
+ }
561
+
562
+ &[aria-disabled="true"] {
563
+ opacity: 0.45;
564
+ cursor: not-allowed;
565
+ }
566
+
567
+ /* Forced colours drop the knurling, the dome and their shadows: outlines draw the skirt and the cap. */
568
+ @media (forced-colors: active) {
569
+ outline: 1px solid CanvasText;
570
+
571
+ &::before {
572
+ outline: 1px solid CanvasText;
573
+ }
574
+ }
575
+ }
576
+
577
+ /** The pointer line on a knob's cap. Turn it with `rotate`: -135deg at the minimum, 135deg at the maximum. */
578
+ @utility fp-knob-mark {
579
+ position: absolute;
580
+ inset: 0;
581
+ pointer-events: none;
582
+
583
+ &::after {
584
+ content: "";
585
+ position: absolute;
586
+ top: 21%;
587
+ left: 50%;
588
+ width: 3px;
589
+ height: 24%;
590
+ translate: -50% 0;
591
+ border-radius: 2px;
592
+ background-color: var(--fp-color-ink);
593
+ }
594
+
595
+ /* Forced colours would paint the line in the page colour, on the page colour. */
596
+ @media (forced-colors: active) {
597
+ &::after {
598
+ forced-color-adjust: none;
599
+ background-color: CanvasText;
600
+ }
601
+ }
602
+ }
603
+
604
+ /**
605
+ * Fader: a chrome cap on a recessed slot. It stands up by default and lies on its side with
606
+ * `data-orientation="horizontal"`; `--fp-fader-length` sets how far it travels (8rem). 44 px
607
+ * across, 36 px with a mouse, trackpad or pen. Pressing the slot moves the cap there.
608
+ */
609
+ @utility fp-fader {
610
+ position: relative;
611
+ display: inline-block;
612
+ flex-shrink: 0;
613
+ width: 2.75rem;
614
+ height: var(--fp-fader-length, 8rem);
615
+ cursor: ns-resize;
616
+ touch-action: none;
617
+ user-select: none;
618
+
619
+ @media (pointer: fine) {
620
+ width: 2.25rem;
621
+ }
622
+
623
+ &[data-orientation="horizontal"] {
624
+ width: var(--fp-fader-length, 8rem);
625
+ height: 2.75rem;
626
+ cursor: ew-resize;
627
+
628
+ @media (pointer: fine) {
629
+ height: 2.25rem;
630
+ }
631
+ }
632
+
633
+ &[data-disabled] {
634
+ opacity: 0.45;
635
+ cursor: not-allowed;
636
+ }
637
+ }
638
+
639
+ /** The slot a fader's cap rides in, cut into the panel: shaded on its top and left inside edges. */
640
+ @utility fp-fader-slot {
641
+ position: absolute;
642
+ inset: 0.5rem auto;
643
+ left: 50%;
644
+ width: 6px;
645
+ translate: -50% 0;
646
+ border-radius: 3px;
647
+ background-color: var(--fp-color-housing);
648
+ box-shadow:
649
+ inset 1px 1px 2px rgb(0 0 0 / 0.5),
650
+ 1px 1px 0 var(--fp-color-bevel-hi);
651
+
652
+ &[data-orientation="horizontal"] {
653
+ inset: 50% 0.5rem auto;
654
+ width: auto;
655
+ height: 6px;
656
+ translate: 0 -50%;
657
+ }
658
+
659
+ /* Forced colours: the slot as a solid line, so the cap's travel shows. */
660
+ @media (forced-colors: active) {
661
+ forced-color-adjust: none;
662
+ background-color: CanvasText;
663
+ box-shadow: none;
664
+ }
665
+ }
666
+
667
+ /**
668
+ * A fader's cap: brushed chrome lit from the top left, with an ink grip line that marks the value.
669
+ * Place it along the slot with `bottom` (or `left` on its side, with `data-orientation="horizontal"`
670
+ * on the slot and cap as well as the fader).
671
+ */
672
+ @utility fp-fader-cap {
673
+ position: absolute;
674
+ inset-inline: 0;
675
+ height: 1.5rem;
676
+ border-radius: 4px;
677
+ background-image: linear-gradient(
678
+ 180deg,
679
+ color-mix(in srgb, var(--fp-color-chrome-hi) 60%, var(--fp-color-chrome-100)) 0%,
680
+ var(--fp-color-chrome-100) 45%,
681
+ var(--fp-color-chrome-300) 100%
682
+ );
683
+ box-shadow:
684
+ 0 0 0 1px var(--fp-color-edge),
685
+ inset 1px 1px 0 var(--fp-color-bevel-hi),
686
+ inset -1px -1px 0 var(--fp-color-bevel-lo),
687
+ 1px 2px 0 var(--fp-color-cast);
688
+
689
+ @media (pointer: fine) {
690
+ height: 1.25rem;
691
+ }
692
+
693
+ &::after {
694
+ content: "";
695
+ position: absolute;
696
+ inset: 50% 20% auto;
697
+ height: 2px;
698
+ translate: 0 -50%;
699
+ border-radius: 1px;
700
+ background-color: var(--fp-color-ink);
701
+ }
702
+
703
+ &[data-orientation="horizontal"] {
704
+ inset-inline: auto;
705
+ inset-block: 0;
706
+ width: 1.5rem;
707
+ height: auto;
708
+
709
+ @media (pointer: fine) {
710
+ width: 1.25rem;
711
+ }
712
+
713
+ &::after {
714
+ inset: 20% auto 20% 50%;
715
+ width: 2px;
716
+ height: auto;
717
+ translate: -50% 0;
718
+ }
719
+ }
720
+
721
+ /* A solid ring with a glow outside it: the glow alone fails non-text contrast on light chrome. */
722
+ &:focus-visible {
723
+ outline: 2px solid var(--fp-color-focus-ring);
724
+ outline-offset: 2px;
725
+ filter: drop-shadow(0 0 3px var(--fp-color-focus));
726
+ }
727
+
728
+ /* Forced colours: a cap in the page colour hides the slot under it; an outline and the grip draw it. */
729
+ @media (forced-colors: active) {
730
+ background-color: Canvas;
731
+ outline: 1px solid CanvasText;
732
+ outline-offset: -1px;
733
+
734
+ &::after {
735
+ forced-color-adjust: none;
736
+ background-color: CanvasText;
737
+ }
738
+ }
739
+ }
740
+
741
+ /**
742
+ * Illuminated rocker switch: a lamp-lit paddle in a dark bezel, marked I at the top and O at the
743
+ * bottom. Off, the O half is pressed in; on, the I half is, and the lamp behind the lens lights.
744
+ * Put it on a button with `role="switch"` and `aria-checked`, holding an fp-switch-rocker. The
745
+ * lamp is red; colour it with fp-led-green or fp-led-amber. 44 x 56 px, 36 x 46 px with a mouse,
746
+ * trackpad or pen. With `data-orientation="horizontal"` it lies on its side, 56 x 44 px (46 x 36),
747
+ * O on the left and I on the right. After an illuminated rocker pen by ykadosh (CodePen, MIT), rebuilt on the kit's
748
+ * tokens.
749
+ */
750
+ @utility fp-switch {
751
+ --fp-switch-lamp: var(--fp-led, var(--fp-color-led-red));
752
+ position: relative;
753
+ display: inline-block;
754
+ flex-shrink: 0;
755
+ width: 2.75rem;
756
+ height: 3.5rem;
757
+ padding: 5px;
758
+ border: 0;
759
+ border-radius: 4px;
760
+ background-color: color-mix(in srgb, var(--fp-color-housing) 40%, black);
761
+ box-shadow:
762
+ 0 0 0 1px var(--fp-color-edge),
763
+ inset 1px 1px 0 rgb(255 255 255 / 0.25),
764
+ inset 0 0 1px 3px var(--fp-color-housing),
765
+ inset 0 0 1px 5px color-mix(in srgb, var(--fp-color-housing) 40%, black),
766
+ 1px 2px 0 var(--fp-color-cast);
767
+ perspective: 216px;
768
+ cursor: pointer;
769
+
770
+ @media (pointer: fine) {
771
+ width: 2.25rem;
772
+ height: 2.875rem;
773
+ padding: 4px;
774
+ }
775
+
776
+ /* On: the I half goes in, the shine comes up, the shading goes, and the lamp lights as it lands. */
777
+ &[aria-checked="true"] .fp-switch-rocker {
778
+ transform: translateZ(6px) rotateX(25deg);
779
+ box-shadow: 0 -3px 6px var(--fp-switch-lamp);
780
+ }
781
+
782
+ &[aria-checked="true"] .fp-switch-face::before {
783
+ opacity: 1;
784
+ }
785
+
786
+ &[aria-checked="true"] .fp-switch-face::after {
787
+ opacity: 0;
788
+ }
789
+
790
+ &[aria-checked="true"] .fp-switch-lamp {
791
+ opacity: 1;
792
+ transition-duration: 60ms;
793
+ transition-timing-function: linear;
794
+ transition-delay: 250ms;
795
+ }
796
+
797
+ /*
798
+ * On its side the paddle tips across instead, and the marks stay upright. With the light at the
799
+ * top left, the right end is now the one in shade that casts the shadow, and the left end is lit.
800
+ */
801
+ &[data-orientation="horizontal"] {
802
+ width: 3.5rem;
803
+ height: 2.75rem;
804
+
805
+ @media (pointer: fine) {
806
+ width: 2.875rem;
807
+ height: 2.25rem;
808
+ }
809
+
810
+ & .fp-switch-rocker {
811
+ transform: translateZ(6px) rotateY(-25deg);
812
+ background: linear-gradient(
813
+ 90deg,
814
+ color-mix(in srgb, var(--fp-switch-lamp) 60%, black) 0%,
815
+ color-mix(in srgb, var(--fp-switch-lamp) 44%, black) 30%,
816
+ color-mix(in srgb, var(--fp-switch-lamp) 44%, black) 70%,
817
+ color-mix(in srgb, var(--fp-switch-lamp) 60%, black) 100%
818
+ );
819
+ }
820
+
821
+ /* The right end, in shade, with the shadow the paddle casts into the bezel. */
822
+ & .fp-switch-rocker::before {
823
+ left: auto;
824
+ right: 0;
825
+ width: 15px;
826
+ height: 100%;
827
+ transform-origin: right;
828
+ transform: rotateY(-90deg);
829
+ background: linear-gradient(
830
+ 270deg,
831
+ color-mix(in srgb, var(--fp-switch-lamp) 40%, black),
832
+ color-mix(in srgb, var(--fp-switch-lamp) 20%, black)
833
+ );
834
+ box-shadow:
835
+ -15px 0 3px 0 black,
836
+ -24px 0 6px 0 rgb(0 0 0 / 0.5);
837
+ }
838
+
839
+ /* The left end, lit from the left while it stands proud. */
840
+ & .fp-switch-rocker::after {
841
+ top: 0;
842
+ bottom: auto;
843
+ width: 15px;
844
+ height: 100%;
845
+ transform-origin: right;
846
+ transform: translateX(-15px) rotateY(-90deg);
847
+ background:
848
+ linear-gradient(
849
+ 270deg,
850
+ rgb(255 255 255 / 0.8) 10%,
851
+ rgb(255 255 255 / 0.3) 30%,
852
+ color-mix(in srgb, var(--fp-switch-lamp) 40%, black) 75%,
853
+ color-mix(in srgb, var(--fp-switch-lamp) 20%, black)
854
+ )
855
+ 50% 50% / 97% 97% no-repeat,
856
+ color-mix(in srgb, var(--fp-switch-lamp) 70%, black);
857
+ box-shadow: none;
858
+ }
859
+
860
+ & .fp-switch-face {
861
+ background:
862
+ linear-gradient(#fff, #fff) 78% 50% / max(2px, 4%) 28% no-repeat,
863
+ radial-gradient(circle, transparent 50%, #fff 52%, #fff 70%, transparent 72%) 22% 50% / 25% 33% no-repeat,
864
+ radial-gradient(transparent 30%, color-mix(in srgb, color-mix(in srgb, var(--fp-switch-lamp) 40%, black) 70%, transparent) 70%) 0 0 / 3px 3px;
865
+ }
866
+
867
+ & .fp-switch-face::before {
868
+ background:
869
+ linear-gradient(#fff, transparent 3%) 50% 50% / 97% 97% no-repeat,
870
+ linear-gradient(270deg, rgb(255 255 255 / 0.5), transparent 50%, transparent 80%, rgb(255 255 255 / 0.5)) 50% 50% / 97% 97% no-repeat;
871
+ }
872
+
873
+ & .fp-switch-face::after {
874
+ background-image: linear-gradient(270deg, transparent 70%, rgb(0 0 0 / 0.8));
875
+ }
876
+
877
+ &[aria-checked="true"] .fp-switch-rocker {
878
+ transform: translateZ(6px) rotateY(25deg);
879
+ box-shadow: 3px 0 6px var(--fp-switch-lamp);
880
+ }
881
+ }
882
+
883
+ /* A solid ring with a glow outside it: the glow alone fails non-text contrast on light chrome. */
884
+ &:focus-visible {
885
+ outline: 2px solid var(--fp-color-focus-ring);
886
+ outline-offset: 2px;
887
+ filter: drop-shadow(0 0 3px var(--fp-color-focus));
888
+ }
889
+
890
+ &:disabled,
891
+ &[aria-disabled="true"] {
892
+ opacity: 0.45;
893
+ cursor: not-allowed;
894
+ }
895
+
896
+ /*
897
+ * Forced colours drop the paddle's paint: an outline keeps the bezel's edge, and the rocker fills
898
+ * when on, as a lit LED does, so the state reads by shape as well as by the tilt.
899
+ */
900
+ @media (forced-colors: active) {
901
+ outline: 1px solid ButtonText;
902
+
903
+ &[data-orientation="horizontal"] .fp-switch-rocker {
904
+ background: Canvas;
905
+ }
906
+
907
+ &[aria-checked="true"] .fp-switch-rocker,
908
+ &[data-orientation="horizontal"][aria-checked="true"] .fp-switch-rocker {
909
+ background: CanvasText;
910
+ box-shadow: none;
911
+ }
912
+
913
+ &:disabled,
914
+ &[aria-disabled="true"] {
915
+ opacity: 1;
916
+ outline-color: GrayText;
917
+ }
918
+ }
919
+ }
920
+
921
+ /**
922
+ * The paddle of an fp-switch, tilted in 3D. It holds an fp-switch-lamp and then an fp-switch-face;
923
+ * its top and bottom ends show as it tips.
924
+ */
925
+ @utility fp-switch-rocker {
926
+ position: relative;
927
+ display: block;
928
+ width: 100%;
929
+ height: 100%;
930
+ transform-origin: 50% 50% -6px;
931
+ transform: translateZ(6px) rotateX(-25deg);
932
+ transform-style: preserve-3d;
933
+ background: linear-gradient(
934
+ color-mix(in srgb, var(--fp-switch-lamp) 60%, black) 0%,
935
+ color-mix(in srgb, var(--fp-switch-lamp) 44%, black) 30%,
936
+ color-mix(in srgb, var(--fp-switch-lamp) 44%, black) 70%,
937
+ color-mix(in srgb, var(--fp-switch-lamp) 60%, black) 100%
938
+ );
939
+
940
+ /* Snaps over at the end of its travel, as a rocker's spring does. */
941
+ transition: transform 300ms cubic-bezier(1, 0, 1, 1), box-shadow 300ms cubic-bezier(1, 0, 1, 1);
942
+
943
+ @media (prefers-reduced-motion: reduce) {
944
+ transition-duration: 0ms;
945
+ }
946
+
947
+ /* The top end, lit from above while it stands proud. */
948
+ &::before {
949
+ content: "";
950
+ position: absolute;
951
+ top: 0;
952
+ left: 0;
953
+ width: 100%;
954
+ height: 15px;
955
+ transform-origin: top;
956
+ transform: rotateX(-90deg);
957
+ background:
958
+ linear-gradient(
959
+ rgb(255 255 255 / 0.8) 10%,
960
+ rgb(255 255 255 / 0.3) 30%,
961
+ color-mix(in srgb, var(--fp-switch-lamp) 40%, black) 75%,
962
+ color-mix(in srgb, var(--fp-switch-lamp) 20%, black)
963
+ )
964
+ 50% 50% / 97% 97% no-repeat,
965
+ color-mix(in srgb, var(--fp-switch-lamp) 70%, black);
966
+ }
967
+
968
+ /* The bottom end, in shadow, with the shadow the paddle casts into the bezel. */
969
+ &::after {
970
+ content: "";
971
+ position: absolute;
972
+ bottom: 0;
973
+ left: 0;
974
+ width: 100%;
975
+ height: 15px;
976
+ transform-origin: top;
977
+ transform: translateY(15px) rotateX(-90deg);
978
+ background-image: linear-gradient(
979
+ color-mix(in srgb, var(--fp-switch-lamp) 40%, black),
980
+ color-mix(in srgb, var(--fp-switch-lamp) 20%, black)
981
+ );
982
+ box-shadow:
983
+ 0 15px 3px 0 black,
984
+ 0 24px 6px 0 rgb(0 0 0 / 0.5);
985
+ }
986
+
987
+ /* Forced colours: a plain paddle in the system's colours; fp-switch fills it when on. */
988
+ @media (forced-colors: active) {
989
+ forced-color-adjust: none;
990
+ background: Canvas;
991
+ outline: 1px solid CanvasText;
992
+ outline-offset: -1px;
993
+
994
+ &::before,
995
+ &::after {
996
+ content: none;
997
+ }
998
+ }
999
+ }
1000
+
1001
+ /** The lamp behind an fp-switch's lens: lit when the switch is on, and slow to fade, like a filament cooling. */
1002
+ @utility fp-switch-lamp {
1003
+ position: absolute;
1004
+ inset: 0;
1005
+ opacity: 0;
1006
+ background-image: radial-gradient(
1007
+ color-mix(in srgb, var(--fp-switch-lamp) 35%, #fff2c8),
1008
+ var(--fp-switch-lamp) 40%,
1009
+ transparent 70%
1010
+ );
1011
+ transition: opacity 600ms ease-out;
1012
+
1013
+ @media (prefers-reduced-motion: reduce) {
1014
+ transition-duration: 0ms;
1015
+ }
1016
+
1017
+ /* Forced colours: the rocker's fill shows the state instead. */
1018
+ @media (forced-colors: active) {
1019
+ display: none;
1020
+ }
1021
+ }
1022
+
1023
+ /**
1024
+ * The front of an fp-switch's paddle: the dotted lens over the lamp, the I and O marks, and the
1025
+ * shine that comes up on the pressed-in half. Decorative; the switch says its state.
1026
+ */
1027
+ @utility fp-switch-face {
1028
+ position: absolute;
1029
+ inset: 0;
1030
+ background:
1031
+ linear-gradient(#fff, #fff) 50% 20% / max(2px, 5%) 20% no-repeat,
1032
+ radial-gradient(circle, transparent 50%, #fff 52%, #fff 70%, transparent 72%) 50% 80% / 33% 25% no-repeat,
1033
+ radial-gradient(transparent 30%, color-mix(in srgb, color-mix(in srgb, var(--fp-switch-lamp) 40%, black) 70%, transparent) 70%) 0 0 / 3px 3px;
1034
+
1035
+ /* The shine: faint while the I half stands out, full once it is pressed in. */
1036
+ &::before,
1037
+ &::after {
1038
+ content: "";
1039
+ position: absolute;
1040
+ inset: 0;
1041
+ transition: opacity 300ms cubic-bezier(1, 0, 1, 1);
1042
+
1043
+ @media (prefers-reduced-motion: reduce) {
1044
+ transition-duration: 0ms;
1045
+ }
1046
+ }
1047
+
1048
+ &::before {
1049
+ opacity: 0.3;
1050
+ background:
1051
+ linear-gradient(#fff, transparent 3%) 50% 50% / 97% 97% no-repeat,
1052
+ linear-gradient(rgb(255 255 255 / 0.5), transparent 50%, transparent 80%, rgb(255 255 255 / 0.5)) 50% 50% / 97% 97% no-repeat;
1053
+ }
1054
+
1055
+ /* The shading on the O half while it is pressed in. */
1056
+ &::after {
1057
+ opacity: 1;
1058
+ background-image: linear-gradient(transparent 70%, rgb(0 0 0 / 0.8));
1059
+ }
1060
+
1061
+ /* Forced colours: no lens or marks; the rocker's fill shows the state. */
1062
+ @media (forced-colors: active) {
1063
+ display: none;
1064
+ }
1065
+ }
1066
+
1067
+ /**
1068
+ * Level meter: a strip of LED segments in a dark housing, lit green, then amber, then red. Set
1069
+ * `--fp-meter-level` (0 to 1, in whole segments), `--fp-meter-segments` (12), and where amber and
1070
+ * red begin, `--fp-meter-amber` (8/12) and `--fp-meter-red` (11/12), as shares of the strip on
1071
+ * segment edges, or a segment lights in two colours. It stands
1072
+ * up by default and lies on its side with `data-orientation="horizontal"`; `--fp-meter-length`
1073
+ * sets how long it is (8rem). Give it `role="meter"` and its values, or `aria-hidden` beside a
1074
+ * readout that says the same.
1075
+ */
1076
+ @utility fp-meter {
1077
+ --fp-meter-zones: linear-gradient(
1078
+ to top,
1079
+ var(--fp-color-led-on) 0 calc(var(--fp-meter-amber, calc(8 / 12)) * 100%),
1080
+ var(--fp-color-led-amber) 0 calc(var(--fp-meter-red, calc(11 / 12)) * 100%),
1081
+ var(--fp-color-led-red) 0
1082
+ );
1083
+ --fp-meter-gaps: repeating-linear-gradient(
1084
+ to top,
1085
+ #000 0 calc(100% / var(--fp-meter-segments, 12) - 2px),
1086
+ transparent 0 calc(100% / var(--fp-meter-segments, 12))
1087
+ );
1088
+
1089
+ /* Lit segments spill a little light into the gaps beside them: the lens's glow. */
1090
+ --fp-meter-lit-gaps: repeating-linear-gradient(
1091
+ to top,
1092
+ #000 0 calc(100% / var(--fp-meter-segments, 12) - 2px),
1093
+ rgb(0 0 0 / calc(0.18 * var(--fp-led-glow, 1))) 0 calc(100% / var(--fp-meter-segments, 12))
1094
+ );
1095
+ --fp-meter-cell: 100% calc(100% / var(--fp-meter-segments, 12));
1096
+ position: relative;
1097
+ display: inline-block;
1098
+ flex-shrink: 0;
1099
+ width: 0.75rem;
1100
+ height: var(--fp-meter-length, 8rem);
1101
+ border-radius: 3px;
1102
+ background-color: var(--fp-color-housing);
1103
+ box-shadow:
1104
+ inset 1px 1px 2px rgb(0 0 0 / 0.5),
1105
+ 1px 1px 0 var(--fp-color-bevel-hi);
1106
+
1107
+ /* Each segment's gap sits at its far end, so the strip's own inset at that end is the gap. */
1108
+ &::before,
1109
+ &::after {
1110
+ content: "";
1111
+ position: absolute;
1112
+ inset: 0 2px 2px;
1113
+ background-image: var(--fp-meter-zones);
1114
+ mask-image: var(--fp-meter-gaps);
1115
+ }
1116
+
1117
+ /* Unlit segments are the LED's off colour with a trace of their zone's, so the zones show. */
1118
+ &::before {
1119
+ background-image:
1120
+ linear-gradient(color-mix(in srgb, var(--fp-color-led-off) 75%, transparent) 0 0),
1121
+ var(--fp-meter-zones);
1122
+ }
1123
+
1124
+ /*
1125
+ * A lit segment is brightest over its chip, in the middle, by the lens. The cut sits 1 px into the
1126
+ * gap below the next segment, so rounding never lights a sliver of it.
1127
+ */
1128
+ &::after {
1129
+ background-image:
1130
+ radial-gradient(closest-side, rgb(255 255 255 / var(--fp-led-core, 0.3)), transparent),
1131
+ var(--fp-meter-zones);
1132
+ background-position: left bottom;
1133
+ background-size: var(--fp-meter-cell), auto;
1134
+ mask-image: var(--fp-meter-lit-gaps);
1135
+ clip-path: inset(calc((1 - var(--fp-meter-level, 0)) * 100% + 1px) 0 0 0);
1136
+ }
1137
+
1138
+ /* Forced colours: the strip as an outline and the lit segments in CanvasText, so the level shows. */
1139
+ @media (forced-colors: active) {
1140
+ outline: 1px solid CanvasText;
1141
+
1142
+ &::before {
1143
+ content: none;
1144
+ }
1145
+
1146
+ &::after {
1147
+ forced-color-adjust: none;
1148
+ background: CanvasText;
1149
+ mask-image: var(--fp-meter-gaps);
1150
+ }
1151
+ }
1152
+
1153
+ &[data-orientation="horizontal"] {
1154
+ --fp-meter-zones: linear-gradient(
1155
+ to right,
1156
+ var(--fp-color-led-on) 0 calc(var(--fp-meter-amber, calc(8 / 12)) * 100%),
1157
+ var(--fp-color-led-amber) 0 calc(var(--fp-meter-red, calc(11 / 12)) * 100%),
1158
+ var(--fp-color-led-red) 0
1159
+ );
1160
+ --fp-meter-gaps: repeating-linear-gradient(
1161
+ to right,
1162
+ #000 0 calc(100% / var(--fp-meter-segments, 12) - 2px),
1163
+ transparent 0 calc(100% / var(--fp-meter-segments, 12))
1164
+ );
1165
+ --fp-meter-lit-gaps: repeating-linear-gradient(
1166
+ to right,
1167
+ #000 0 calc(100% / var(--fp-meter-segments, 12) - 2px),
1168
+ rgb(0 0 0 / calc(0.18 * var(--fp-led-glow, 1))) 0 calc(100% / var(--fp-meter-segments, 12))
1169
+ );
1170
+ --fp-meter-cell: calc(100% / var(--fp-meter-segments, 12)) 100%;
1171
+ width: var(--fp-meter-length, 8rem);
1172
+ height: 0.75rem;
1173
+
1174
+ &::before,
1175
+ &::after {
1176
+ inset: 2px 0 2px 2px;
1177
+ }
1178
+
1179
+ &::after {
1180
+ clip-path: inset(0 calc((1 - var(--fp-meter-level, 0)) * 100% + 1px) 0 0);
1181
+ }
1182
+ }
1183
+ }
1184
+
1185
+ /**
1186
+ * Seven-segment readout: LED digits on a near-black face, set in the housing. Each character is an
1187
+ * fp-readout-cell, and the value is said in an fp-readout-text inside. Green by default; colour it
1188
+ * with fp-led-amber or fp-led-red. Lit digits pass as text on the face at any size.
1189
+ */
1190
+ @utility fp-readout {
1191
+ position: relative;
1192
+ display: inline-flex;
1193
+ align-items: center;
1194
+ gap: 0.1875rem;
1195
+ padding: 0.3125rem 0.4375rem;
1196
+ border: 3px solid var(--fp-color-housing);
1197
+ border-radius: 5px;
1198
+ background-color: color-mix(in srgb, var(--fp-color-led-off) 70%, black);
1199
+ box-shadow:
1200
+ inset 1px 1px 3px rgb(0 0 0 / 0.8),
1201
+ 0 1px 0 var(--fp-color-bevel-hi);
1202
+ }
1203
+
1204
+ /**
1205
+ * One character of an fp-readout, leaning a little like a real display. It shows its unlit
1206
+ * segments itself, a figure eight and a point, faintly in the lit colour; put the lit ones in an
1207
+ * fp-readout-lit inside. With `data-colon` it is a narrow colon cell. `--fp-readout-height` sets
1208
+ * its height (2rem). Give it aria-hidden="true".
1209
+ */
1210
+ @utility fp-readout-cell {
1211
+ position: relative;
1212
+ flex-shrink: 0;
1213
+ height: var(--fp-readout-height, 2rem);
1214
+ aspect-ratio: 15 / 22;
1215
+ transform: skewX(-6deg);
1216
+
1217
+ &::before {
1218
+ content: "";
1219
+ position: absolute;
1220
+ inset: 0;
1221
+ background-color: color-mix(in srgb, var(--fp-color-led-off) 75%, var(--fp-led, var(--fp-color-led-on)));
1222
+ mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 15 22'%3E%3Cpolygon points='1.5,1 2.5,0 9.5,0 10.5,1 9.5,2 2.5,2'/%3E%3Cpolygon points='11,1.5 12,2.5 12,9.5 11,10.5 10,9.5 10,2.5'/%3E%3Cpolygon points='11,11.5 12,12.5 12,19.5 11,20.5 10,19.5 10,12.5'/%3E%3Cpolygon points='1.5,21 2.5,20 9.5,20 10.5,21 9.5,22 2.5,22'/%3E%3Cpolygon points='1,11.5 2,12.5 2,19.5 1,20.5 0,19.5 0,12.5'/%3E%3Cpolygon points='1,1.5 2,2.5 2,9.5 1,10.5 0,9.5 0,2.5'/%3E%3Cpolygon points='1.5,11 2.5,10 9.5,10 10.5,11 9.5,12 2.5,12'/%3E%3Ccircle cx='13.5' cy='21' r='1.2'/%3E%3C/svg%3E") 0 0 / 100% 100% no-repeat;
1223
+ }
1224
+
1225
+ &[data-colon] {
1226
+ aspect-ratio: 5 / 22;
1227
+
1228
+ &::before {
1229
+ content: none;
1230
+ }
1231
+ }
1232
+
1233
+ /* With forced colours, only the lit digits show: a ghost in a system colour blurs into them. */
1234
+ @media (forced-colors: active) {
1235
+ &::before {
1236
+ content: none;
1237
+ }
1238
+ }
1239
+ }
1240
+
1241
+ /**
1242
+ * The lit segments of an fp-readout-cell: an element for each, named by `data-segment`, `a` to `g`
1243
+ * (top, then clockwise, then the middle), `dp`, or a colon's `colon-upper` and `colon-lower`. Each
1244
+ * is brightest over its chip, in the middle, and they glow onto the face, both by the lens.
1245
+ */
1246
+ @utility fp-readout-lit {
1247
+ position: absolute;
1248
+ inset: 0;
1249
+ filter: drop-shadow(
1250
+ 0 0 calc(var(--fp-readout-height, 2rem) * 0.06 * var(--fp-led-glow, 1))
1251
+ color-mix(in srgb, var(--fp-led, var(--fp-color-led-on)) 70%, transparent)
1252
+ );
1253
+
1254
+ & > [data-segment] {
1255
+ position: absolute;
1256
+ background-color: var(--fp-led, var(--fp-color-led-on));
1257
+ background-image: radial-gradient(
1258
+ closest-side,
1259
+ color-mix(in srgb, var(--fp-led, var(--fp-color-led-on)) calc((1 - var(--fp-led-core, 0.3)) * 100%), white),
1260
+ var(--fp-led, var(--fp-color-led-on))
1261
+ );
1262
+ }
1263
+
1264
+ /* The bars, on the cell's 15 x 22 grid: 9 long and 2 thick, pointed at both ends. */
1265
+ & > [data-segment="a"],
1266
+ & > [data-segment="d"],
1267
+ & > [data-segment="g"] {
1268
+ left: 10%;
1269
+ width: 60%;
1270
+ height: 9.0909%;
1271
+ clip-path: polygon(0 50%, 11.1111% 0, 88.8889% 0, 100% 50%, 88.8889% 100%, 11.1111% 100%);
1272
+ }
1273
+
1274
+ & > [data-segment="b"],
1275
+ & > [data-segment="c"],
1276
+ & > [data-segment="e"],
1277
+ & > [data-segment="f"] {
1278
+ width: 13.3333%;
1279
+ height: 40.9091%;
1280
+ clip-path: polygon(50% 0, 100% 11.1111%, 100% 88.8889%, 50% 100%, 0 88.8889%, 0 11.1111%);
1281
+ }
1282
+
1283
+ & > [data-segment="a"] {
1284
+ top: 0;
1285
+ }
1286
+
1287
+ & > [data-segment="g"] {
1288
+ top: 45.4545%;
1289
+ }
1290
+
1291
+ & > [data-segment="d"] {
1292
+ top: 90.9091%;
1293
+ }
1294
+
1295
+ & > [data-segment="b"],
1296
+ & > [data-segment="c"] {
1297
+ left: 66.6667%;
1298
+ }
1299
+
1300
+ & > [data-segment="e"],
1301
+ & > [data-segment="f"] {
1302
+ left: 0;
1303
+ }
1304
+
1305
+ & > [data-segment="b"],
1306
+ & > [data-segment="f"] {
1307
+ top: 6.8182%;
1308
+ }
1309
+
1310
+ & > [data-segment="c"],
1311
+ & > [data-segment="e"] {
1312
+ top: 52.2727%;
1313
+ }
1314
+
1315
+ & > [data-segment="dp"],
1316
+ & > [data-segment="colon-upper"],
1317
+ & > [data-segment="colon-lower"] {
1318
+ aspect-ratio: 1;
1319
+ border-radius: 50%;
1320
+ }
1321
+
1322
+ & > [data-segment="dp"] {
1323
+ top: 90%;
1324
+ left: 82%;
1325
+ width: 16%;
1326
+ }
1327
+
1328
+ & > [data-segment="colon-upper"],
1329
+ & > [data-segment="colon-lower"] {
1330
+ left: 26%;
1331
+ width: 48%;
1332
+ }
1333
+
1334
+ & > [data-segment="colon-upper"] {
1335
+ top: 26.3636%;
1336
+ }
1337
+
1338
+ & > [data-segment="colon-lower"] {
1339
+ top: 62.7273%;
1340
+ }
1341
+
1342
+ @media (forced-colors: active) {
1343
+ filter: none;
1344
+
1345
+ & > [data-segment] {
1346
+ forced-color-adjust: none;
1347
+ background: CanvasText;
1348
+ }
1349
+ }
1350
+ }
1351
+
1352
+ /** An fp-readout's value as text, for screen readers: hidden from view, read in place of the digits. */
1353
+ @utility fp-readout-text {
1354
+ position: absolute;
1355
+ width: 1px;
1356
+ height: 1px;
1357
+ overflow: hidden;
1358
+ clip-path: inset(50%);
1359
+ white-space: nowrap;
1360
+ }
1361
+
415
1362
  /** Matte content panel. This is where reading happens: no gloss, a 1 px bevel and a short hard shadow. */
416
1363
  @utility fp-module {
1364
+ color-scheme: var(--fp-scheme);
417
1365
  border-radius: 10px;
418
1366
  background-color: var(--fp-color-shell-matte);
419
1367
  box-shadow:
@@ -421,6 +1369,11 @@
421
1369
  inset 1px 1px 0 var(--fp-color-bevel-hi),
422
1370
  inset -1px -1px 0 var(--fp-color-bevel-lo),
423
1371
  2px 3px 0 var(--fp-color-cast);
1372
+
1373
+ /* Forced colours drop the shadow that draws its edge: an outline keeps it. */
1374
+ @media (forced-colors: active) {
1375
+ outline: 1px solid CanvasText;
1376
+ }
424
1377
  }
425
1378
 
426
1379
  /** A module's own little title bar, in brushed chrome. */
@@ -477,11 +1430,17 @@
477
1430
 
478
1431
  /** Recessed well inside a matte panel, for secondary content or an empty slot. */
479
1432
  @utility fp-well {
1433
+ color-scheme: var(--fp-scheme);
480
1434
  border-radius: 6px;
481
1435
  background-color: var(--fp-color-well);
482
1436
  box-shadow:
483
1437
  inset 1px 1px 2px var(--fp-color-bevel-lo),
484
1438
  inset -1px -1px 0 var(--fp-color-bevel-hi);
1439
+
1440
+ /* Forced colours drop the shading that recesses it: an outline keeps an empty well in view. */
1441
+ @media (forced-colors: active) {
1442
+ outline: 1px solid CanvasText;
1443
+ }
485
1444
  }
486
1445
 
487
1446
  /** Silkscreened hardware label. */
@@ -504,6 +1463,11 @@
504
1463
  0 0 0 2px var(--fp-color-chrome-800),
505
1464
  inset 2px 2px 4px rgb(0 0 0 / 0.35),
506
1465
  inset -1px -1px 0 rgb(255 255 255 / 0.2);
1466
+
1467
+ /* Forced colours drop the chrome bezel: an outline frames the glass. */
1468
+ @media (forced-colors: active) {
1469
+ outline: 2px solid CanvasText;
1470
+ }
507
1471
  }
508
1472
 
509
1473
  /** One line of LCD text. Cells of fp-lcd-ghost stacked behind it show the unlit segments, as on real glass. */
@@ -521,6 +1485,11 @@
521
1485
  /** The unlit segments behind an LCD line. Decorative: give it aria-hidden="true". */
522
1486
  @utility fp-lcd-ghost {
523
1487
  color: var(--fp-color-lcd-ghost);
1488
+
1489
+ /* Forced colours would draw the unlit segments as solid as the text over them. */
1490
+ @media (forced-colors: active) {
1491
+ visibility: hidden;
1492
+ }
524
1493
  }
525
1494
 
526
1495
  /** Dark housing that LEDs and small readouts sit in. */
@@ -537,6 +1506,11 @@
537
1506
  box-shadow:
538
1507
  inset 1px 1px 2px rgb(0 0 0 / 0.5),
539
1508
  0 1px 0 var(--fp-color-bevel-hi);
1509
+
1510
+ /* Forced colours turn the dark housing into the page colour: an outline keeps it in view. */
1511
+ @media (forced-colors: active) {
1512
+ outline: 1px solid CanvasText;
1513
+ }
540
1514
  }
541
1515
 
542
1516
  /** An LED with its own dark bezel ring, so a lit one reads on light chrome too. Unlit by default. */
@@ -550,30 +1524,76 @@
550
1524
  box-shadow:
551
1525
  0 0 0 2px var(--fp-color-housing),
552
1526
  inset 0 -1px 1px rgb(0 0 0 / 0.45);
1527
+
1528
+ /* Forced colours drop the fill and the ring: an outline keeps an unlit LED in view. */
1529
+ @media (forced-colors: active) {
1530
+ outline: 1px solid CanvasText;
1531
+ outline-offset: 1px;
1532
+ }
553
1533
  }
554
1534
 
555
- /** Green for an fp-led, the default. */
1535
+ /** Green for an fp-led or fp-readout, the default. */
556
1536
  @utility fp-led-green {
557
1537
  --fp-led: var(--fp-color-led-on);
558
1538
  }
559
1539
 
560
- /** Amber for an fp-led. */
1540
+ /** Amber for an fp-led or fp-readout. */
561
1541
  @utility fp-led-amber {
562
1542
  --fp-led: var(--fp-color-led-amber);
563
1543
  }
564
1544
 
565
- /** Red for an fp-led. */
1545
+ /** Red for an fp-led or fp-readout. */
566
1546
  @utility fp-led-red {
567
1547
  --fp-led: var(--fp-color-led-red);
568
1548
  }
569
1549
 
570
- /** Lights an fp-led in its colour, with a glow. Say what it means in text nearby, never by colour alone. */
1550
+ /**
1551
+ * Lights an fp-led in its colour, brightest over the chip in its middle, with a glow. The lens sets
1552
+ * how strong both are. Say what it means in text nearby, never by colour alone.
1553
+ */
571
1554
  @utility fp-led-lit {
572
1555
  background-color: var(--fp-led, var(--fp-color-led-on));
1556
+ background-image: radial-gradient(
1557
+ closest-side,
1558
+ color-mix(in srgb, var(--fp-led, var(--fp-color-led-on)) calc((1 - var(--fp-led-core, 0.3)) * 100%), white),
1559
+ var(--fp-led, var(--fp-color-led-on))
1560
+ );
573
1561
  box-shadow:
574
1562
  0 0 0 2px var(--fp-color-housing),
575
- 0 0 7px 2px color-mix(in srgb, var(--fp-led, var(--fp-color-led-on)) 65%, transparent),
1563
+ 0 0 calc(7px * var(--fp-led-glow, 1)) calc(2px * var(--fp-led-glow, 1))
1564
+ color-mix(in srgb, var(--fp-led, var(--fp-color-led-on)) 65%, transparent),
576
1565
  inset 0 -1px 1px rgb(0 0 0 / 0.2);
1566
+
1567
+ /* Lit is filled, so it reads apart from unlit by shape as well as colour. */
1568
+ @media (forced-colors: active) {
1569
+ forced-color-adjust: none;
1570
+ background: CanvasText;
1571
+ box-shadow: none;
1572
+ }
1573
+ }
1574
+
1575
+ /**
1576
+ * LED lens, flat: an even fill with no hot spot. A lens class works on an fp-led, fp-meter or
1577
+ * fp-readout, or on any element around them, such as a device body, to set every one inside.
1578
+ */
1579
+ @utility fp-lens-flat {
1580
+ --fp-led-core: 0;
1581
+ --fp-led-glow: 1;
1582
+ }
1583
+
1584
+ /**
1585
+ * LED lens, diffused, the default: like most real displays, each LED is a little brighter over its
1586
+ * chip, in the middle, with a soft glow.
1587
+ */
1588
+ @utility fp-lens-diffused {
1589
+ --fp-led-core: 0.3;
1590
+ --fp-led-glow: 1;
1591
+ }
1592
+
1593
+ /** LED lens, clear: a bright, pale core over each chip and a wider glow, like a water-clear lens. */
1594
+ @utility fp-lens-clear {
1595
+ --fp-led-core: 0.5;
1596
+ --fp-led-glow: 1.5;
577
1597
  }
578
1598
 
579
1599
  /** Decorative screw head. Give it aria-hidden="true". */
@@ -591,6 +1611,7 @@
591
1611
 
592
1612
  /** A scrolling panel, such as a list window, with a thin scrollbar that never pushes the layout. */
593
1613
  @utility fp-scroll-thin {
1614
+ color-scheme: var(--fp-scheme);
594
1615
  scrollbar-width: thin;
595
1616
  scrollbar-color: var(--fp-color-scroll-thumb) transparent;
596
1617
  }