@frontpanel/css 0.2.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/components.css +470 -2
- package/package.json +1 -1
- package/page.css +5 -0
- package/tailwind-page.css +5 -0
- package/tailwind.css +470 -2
package/components.css
CHANGED
|
@@ -91,6 +91,11 @@
|
|
|
91
91
|
overflow: clip;
|
|
92
92
|
border-radius: 26px;
|
|
93
93
|
}
|
|
94
|
+
|
|
95
|
+
/* Forced colours drop the chrome: an outline keeps the device's edge. */
|
|
96
|
+
@media (forced-colors: active) {
|
|
97
|
+
outline: 1px solid CanvasText;
|
|
98
|
+
}
|
|
94
99
|
}
|
|
95
100
|
|
|
96
101
|
/** Molded body plastic the modules sit on. */
|
|
@@ -195,6 +200,35 @@
|
|
|
195
200
|
&:disabled {
|
|
196
201
|
opacity: 0.45;
|
|
197
202
|
}
|
|
203
|
+
|
|
204
|
+
/*
|
|
205
|
+
* Forced colours drop the chrome and its shadows: an outline keeps the button's edge, the system's
|
|
206
|
+
* selected colours show it pressed, and GrayText, not a fade, shows it disabled.
|
|
207
|
+
*/
|
|
208
|
+
@media (forced-colors: active) {
|
|
209
|
+
outline: 1px solid ButtonText;
|
|
210
|
+
outline-offset: -1px;
|
|
211
|
+
|
|
212
|
+
&[aria-pressed="true"],
|
|
213
|
+
&[aria-expanded="true"],
|
|
214
|
+
&[aria-current="page"] {
|
|
215
|
+
forced-color-adjust: none;
|
|
216
|
+
background: SelectedItem;
|
|
217
|
+
box-shadow: none;
|
|
218
|
+
color: SelectedItemText;
|
|
219
|
+
|
|
220
|
+
/* Opted out, its focus ring would keep the skin's colour: the system's text colour shows on any ground. */
|
|
221
|
+
&:focus-visible {
|
|
222
|
+
outline-color: CanvasText;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
&:disabled {
|
|
227
|
+
opacity: 1;
|
|
228
|
+
color: GrayText;
|
|
229
|
+
outline-color: GrayText;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
198
232
|
}
|
|
199
233
|
|
|
200
234
|
/**
|
|
@@ -258,6 +292,18 @@
|
|
|
258
292
|
&:disabled {
|
|
259
293
|
opacity: 0.45;
|
|
260
294
|
}
|
|
295
|
+
|
|
296
|
+
/* Forced colours drop the gel and its shadows: an outline keeps its edge, and GrayText, not a fade, shows it disabled. */
|
|
297
|
+
@media (forced-colors: active) {
|
|
298
|
+
outline: 1px solid ButtonText;
|
|
299
|
+
outline-offset: -1px;
|
|
300
|
+
|
|
301
|
+
&:disabled {
|
|
302
|
+
opacity: 1;
|
|
303
|
+
color: GrayText;
|
|
304
|
+
outline-color: GrayText;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
261
307
|
}
|
|
262
308
|
|
|
263
309
|
/**
|
|
@@ -330,6 +376,34 @@
|
|
|
330
376
|
&:disabled {
|
|
331
377
|
opacity: 0.5;
|
|
332
378
|
}
|
|
379
|
+
|
|
380
|
+
/*
|
|
381
|
+
* Forced colours drop the plastic and its ring: an outline keeps the cap's edge, the system's
|
|
382
|
+
* selected colours show it selected, and GrayText, not a fade, shows it disabled.
|
|
383
|
+
*/
|
|
384
|
+
@media (forced-colors: active) {
|
|
385
|
+
outline: 1px solid ButtonText;
|
|
386
|
+
outline-offset: -1px;
|
|
387
|
+
|
|
388
|
+
&[aria-pressed="true"],
|
|
389
|
+
&[aria-checked="true"] {
|
|
390
|
+
forced-color-adjust: none;
|
|
391
|
+
background: SelectedItem;
|
|
392
|
+
box-shadow: none;
|
|
393
|
+
color: SelectedItemText;
|
|
394
|
+
|
|
395
|
+
/* Opted out, its focus ring would keep the skin's colour: the system's text colour shows on any ground. */
|
|
396
|
+
&:focus-visible {
|
|
397
|
+
outline-color: CanvasText;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
&:disabled {
|
|
402
|
+
opacity: 1;
|
|
403
|
+
color: GrayText;
|
|
404
|
+
outline-color: GrayText;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
333
407
|
}
|
|
334
408
|
|
|
335
409
|
/** Blue plastic for an fp-cap, with its own label colour. */
|
|
@@ -357,14 +431,21 @@
|
|
|
357
431
|
}
|
|
358
432
|
|
|
359
433
|
/**
|
|
360
|
-
* A control with its silkscreen label under it: a knob, fader or
|
|
361
|
-
*
|
|
434
|
+
* A control with its silkscreen label under it: a knob, fader, meter or switch and then an fp-silk.
|
|
435
|
+
* With `data-label-placement="start"` the label sits level with the control, before it in the
|
|
436
|
+
* line. A length set here, such as `--fp-fader-length`, reaches the control inside.
|
|
362
437
|
*/
|
|
363
438
|
.fp-unit {
|
|
364
439
|
display: inline-flex;
|
|
365
440
|
flex-direction: column;
|
|
366
441
|
align-items: center;
|
|
367
442
|
gap: 0.375rem;
|
|
443
|
+
|
|
444
|
+
/* Reversed, so the markup stays control then label either way. */
|
|
445
|
+
&[data-label-placement="start"] {
|
|
446
|
+
flex-direction: row-reverse;
|
|
447
|
+
gap: 0.5rem;
|
|
448
|
+
}
|
|
368
449
|
}
|
|
369
450
|
|
|
370
451
|
/**
|
|
@@ -423,6 +504,15 @@
|
|
|
423
504
|
opacity: 0.45;
|
|
424
505
|
cursor: not-allowed;
|
|
425
506
|
}
|
|
507
|
+
|
|
508
|
+
/* Forced colours drop the knurling, the dome and their shadows: outlines draw the skirt and the cap. */
|
|
509
|
+
@media (forced-colors: active) {
|
|
510
|
+
outline: 1px solid CanvasText;
|
|
511
|
+
|
|
512
|
+
&::before {
|
|
513
|
+
outline: 1px solid CanvasText;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
426
516
|
}
|
|
427
517
|
|
|
428
518
|
/** The pointer line on a knob's cap. Turn it with `rotate`: -135deg at the minimum, 135deg at the maximum. */
|
|
@@ -442,6 +532,14 @@
|
|
|
442
532
|
border-radius: 2px;
|
|
443
533
|
background-color: var(--fp-color-ink);
|
|
444
534
|
}
|
|
535
|
+
|
|
536
|
+
/* Forced colours would paint the line in the page colour, on the page colour. */
|
|
537
|
+
@media (forced-colors: active) {
|
|
538
|
+
&::after {
|
|
539
|
+
forced-color-adjust: none;
|
|
540
|
+
background-color: CanvasText;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
445
543
|
}
|
|
446
544
|
|
|
447
545
|
/**
|
|
@@ -498,6 +596,13 @@
|
|
|
498
596
|
height: 6px;
|
|
499
597
|
translate: 0 -50%;
|
|
500
598
|
}
|
|
599
|
+
|
|
600
|
+
/* Forced colours: the slot as a solid line, so the cap's travel shows. */
|
|
601
|
+
@media (forced-colors: active) {
|
|
602
|
+
forced-color-adjust: none;
|
|
603
|
+
background-color: CanvasText;
|
|
604
|
+
box-shadow: none;
|
|
605
|
+
}
|
|
501
606
|
}
|
|
502
607
|
|
|
503
608
|
/**
|
|
@@ -560,6 +665,344 @@
|
|
|
560
665
|
outline-offset: 2px;
|
|
561
666
|
filter: drop-shadow(0 0 3px var(--fp-color-focus));
|
|
562
667
|
}
|
|
668
|
+
|
|
669
|
+
/* Forced colours: a cap in the page colour hides the slot under it; an outline and the grip draw it. */
|
|
670
|
+
@media (forced-colors: active) {
|
|
671
|
+
background-color: Canvas;
|
|
672
|
+
outline: 1px solid CanvasText;
|
|
673
|
+
outline-offset: -1px;
|
|
674
|
+
|
|
675
|
+
&::after {
|
|
676
|
+
forced-color-adjust: none;
|
|
677
|
+
background-color: CanvasText;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* Illuminated rocker switch: a lamp-lit paddle in a dark bezel, marked I at the top and O at the
|
|
684
|
+
* bottom. Off, the O half is pressed in; on, the I half is, and the lamp behind the lens lights.
|
|
685
|
+
* Put it on a button with `role="switch"` and `aria-checked`, holding an fp-switch-rocker. The
|
|
686
|
+
* lamp is red; colour it with fp-led-green or fp-led-amber. 44 x 56 px, 36 x 46 px with a mouse,
|
|
687
|
+
* trackpad or pen. With `data-orientation="horizontal"` it lies on its side, 56 x 44 px (46 x 36),
|
|
688
|
+
* O on the left and I on the right. After an illuminated rocker pen by ykadosh (CodePen, MIT), rebuilt on the kit's
|
|
689
|
+
* tokens.
|
|
690
|
+
*/
|
|
691
|
+
.fp-switch {
|
|
692
|
+
--fp-switch-lamp: var(--fp-led, var(--fp-color-led-red));
|
|
693
|
+
position: relative;
|
|
694
|
+
display: inline-block;
|
|
695
|
+
flex-shrink: 0;
|
|
696
|
+
width: 2.75rem;
|
|
697
|
+
height: 3.5rem;
|
|
698
|
+
padding: 5px;
|
|
699
|
+
border: 0;
|
|
700
|
+
border-radius: 4px;
|
|
701
|
+
background-color: color-mix(in srgb, var(--fp-color-housing) 40%, black);
|
|
702
|
+
box-shadow:
|
|
703
|
+
0 0 0 1px var(--fp-color-edge),
|
|
704
|
+
inset 1px 1px 0 rgb(255 255 255 / 0.25),
|
|
705
|
+
inset 0 0 1px 3px var(--fp-color-housing),
|
|
706
|
+
inset 0 0 1px 5px color-mix(in srgb, var(--fp-color-housing) 40%, black),
|
|
707
|
+
1px 2px 0 var(--fp-color-cast);
|
|
708
|
+
perspective: 216px;
|
|
709
|
+
cursor: pointer;
|
|
710
|
+
|
|
711
|
+
@media (pointer: fine) {
|
|
712
|
+
width: 2.25rem;
|
|
713
|
+
height: 2.875rem;
|
|
714
|
+
padding: 4px;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
/* On: the I half goes in, the shine comes up, the shading goes, and the lamp lights as it lands. */
|
|
718
|
+
&[aria-checked="true"] .fp-switch-rocker {
|
|
719
|
+
transform: translateZ(6px) rotateX(25deg);
|
|
720
|
+
box-shadow: 0 -3px 6px var(--fp-switch-lamp);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
&[aria-checked="true"] .fp-switch-face::before {
|
|
724
|
+
opacity: 1;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
&[aria-checked="true"] .fp-switch-face::after {
|
|
728
|
+
opacity: 0;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
&[aria-checked="true"] .fp-switch-lamp {
|
|
732
|
+
opacity: 1;
|
|
733
|
+
transition-duration: 60ms;
|
|
734
|
+
transition-timing-function: linear;
|
|
735
|
+
transition-delay: 250ms;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/*
|
|
739
|
+
* On its side the paddle tips across instead, and the marks stay upright. With the light at the
|
|
740
|
+
* top left, the right end is now the one in shade that casts the shadow, and the left end is lit.
|
|
741
|
+
*/
|
|
742
|
+
&[data-orientation="horizontal"] {
|
|
743
|
+
width: 3.5rem;
|
|
744
|
+
height: 2.75rem;
|
|
745
|
+
|
|
746
|
+
@media (pointer: fine) {
|
|
747
|
+
width: 2.875rem;
|
|
748
|
+
height: 2.25rem;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
& .fp-switch-rocker {
|
|
752
|
+
transform: translateZ(6px) rotateY(-25deg);
|
|
753
|
+
background: linear-gradient(
|
|
754
|
+
90deg,
|
|
755
|
+
color-mix(in srgb, var(--fp-switch-lamp) 60%, black) 0%,
|
|
756
|
+
color-mix(in srgb, var(--fp-switch-lamp) 44%, black) 30%,
|
|
757
|
+
color-mix(in srgb, var(--fp-switch-lamp) 44%, black) 70%,
|
|
758
|
+
color-mix(in srgb, var(--fp-switch-lamp) 60%, black) 100%
|
|
759
|
+
);
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/* The right end, in shade, with the shadow the paddle casts into the bezel. */
|
|
763
|
+
& .fp-switch-rocker::before {
|
|
764
|
+
left: auto;
|
|
765
|
+
right: 0;
|
|
766
|
+
width: 15px;
|
|
767
|
+
height: 100%;
|
|
768
|
+
transform-origin: right;
|
|
769
|
+
transform: rotateY(-90deg);
|
|
770
|
+
background: linear-gradient(
|
|
771
|
+
270deg,
|
|
772
|
+
color-mix(in srgb, var(--fp-switch-lamp) 40%, black),
|
|
773
|
+
color-mix(in srgb, var(--fp-switch-lamp) 20%, black)
|
|
774
|
+
);
|
|
775
|
+
box-shadow:
|
|
776
|
+
-15px 0 3px 0 black,
|
|
777
|
+
-24px 0 6px 0 rgb(0 0 0 / 0.5);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/* The left end, lit from the left while it stands proud. */
|
|
781
|
+
& .fp-switch-rocker::after {
|
|
782
|
+
top: 0;
|
|
783
|
+
bottom: auto;
|
|
784
|
+
width: 15px;
|
|
785
|
+
height: 100%;
|
|
786
|
+
transform-origin: right;
|
|
787
|
+
transform: translateX(-15px) rotateY(-90deg);
|
|
788
|
+
background:
|
|
789
|
+
linear-gradient(
|
|
790
|
+
270deg,
|
|
791
|
+
rgb(255 255 255 / 0.8) 10%,
|
|
792
|
+
rgb(255 255 255 / 0.3) 30%,
|
|
793
|
+
color-mix(in srgb, var(--fp-switch-lamp) 40%, black) 75%,
|
|
794
|
+
color-mix(in srgb, var(--fp-switch-lamp) 20%, black)
|
|
795
|
+
)
|
|
796
|
+
50% 50% / 97% 97% no-repeat,
|
|
797
|
+
color-mix(in srgb, var(--fp-switch-lamp) 70%, black);
|
|
798
|
+
box-shadow: none;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
& .fp-switch-face {
|
|
802
|
+
background:
|
|
803
|
+
linear-gradient(#fff, #fff) 78% 50% / max(2px, 4%) 28% no-repeat,
|
|
804
|
+
radial-gradient(circle, transparent 50%, #fff 52%, #fff 70%, transparent 72%) 22% 50% / 25% 33% no-repeat,
|
|
805
|
+
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;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
& .fp-switch-face::before {
|
|
809
|
+
background:
|
|
810
|
+
linear-gradient(#fff, transparent 3%) 50% 50% / 97% 97% no-repeat,
|
|
811
|
+
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;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
& .fp-switch-face::after {
|
|
815
|
+
background-image: linear-gradient(270deg, transparent 70%, rgb(0 0 0 / 0.8));
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
&[aria-checked="true"] .fp-switch-rocker {
|
|
819
|
+
transform: translateZ(6px) rotateY(25deg);
|
|
820
|
+
box-shadow: 3px 0 6px var(--fp-switch-lamp);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
/* A solid ring with a glow outside it: the glow alone fails non-text contrast on light chrome. */
|
|
825
|
+
&:focus-visible {
|
|
826
|
+
outline: 2px solid var(--fp-color-focus-ring);
|
|
827
|
+
outline-offset: 2px;
|
|
828
|
+
filter: drop-shadow(0 0 3px var(--fp-color-focus));
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
&:disabled,
|
|
832
|
+
&[aria-disabled="true"] {
|
|
833
|
+
opacity: 0.45;
|
|
834
|
+
cursor: not-allowed;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
/*
|
|
838
|
+
* Forced colours drop the paddle's paint: an outline keeps the bezel's edge, and the rocker fills
|
|
839
|
+
* when on, as a lit LED does, so the state reads by shape as well as by the tilt.
|
|
840
|
+
*/
|
|
841
|
+
@media (forced-colors: active) {
|
|
842
|
+
outline: 1px solid ButtonText;
|
|
843
|
+
|
|
844
|
+
&[data-orientation="horizontal"] .fp-switch-rocker {
|
|
845
|
+
background: Canvas;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
&[aria-checked="true"] .fp-switch-rocker,
|
|
849
|
+
&[data-orientation="horizontal"][aria-checked="true"] .fp-switch-rocker {
|
|
850
|
+
background: CanvasText;
|
|
851
|
+
box-shadow: none;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
&:disabled,
|
|
855
|
+
&[aria-disabled="true"] {
|
|
856
|
+
opacity: 1;
|
|
857
|
+
outline-color: GrayText;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
/**
|
|
863
|
+
* The paddle of an fp-switch, tilted in 3D. It holds an fp-switch-lamp and then an fp-switch-face;
|
|
864
|
+
* its top and bottom ends show as it tips.
|
|
865
|
+
*/
|
|
866
|
+
.fp-switch-rocker {
|
|
867
|
+
position: relative;
|
|
868
|
+
display: block;
|
|
869
|
+
width: 100%;
|
|
870
|
+
height: 100%;
|
|
871
|
+
transform-origin: 50% 50% -6px;
|
|
872
|
+
transform: translateZ(6px) rotateX(-25deg);
|
|
873
|
+
transform-style: preserve-3d;
|
|
874
|
+
background: linear-gradient(
|
|
875
|
+
color-mix(in srgb, var(--fp-switch-lamp) 60%, black) 0%,
|
|
876
|
+
color-mix(in srgb, var(--fp-switch-lamp) 44%, black) 30%,
|
|
877
|
+
color-mix(in srgb, var(--fp-switch-lamp) 44%, black) 70%,
|
|
878
|
+
color-mix(in srgb, var(--fp-switch-lamp) 60%, black) 100%
|
|
879
|
+
);
|
|
880
|
+
|
|
881
|
+
/* Snaps over at the end of its travel, as a rocker's spring does. */
|
|
882
|
+
transition: transform 300ms cubic-bezier(1, 0, 1, 1), box-shadow 300ms cubic-bezier(1, 0, 1, 1);
|
|
883
|
+
|
|
884
|
+
@media (prefers-reduced-motion: reduce) {
|
|
885
|
+
transition-duration: 0ms;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
/* The top end, lit from above while it stands proud. */
|
|
889
|
+
&::before {
|
|
890
|
+
content: "";
|
|
891
|
+
position: absolute;
|
|
892
|
+
top: 0;
|
|
893
|
+
left: 0;
|
|
894
|
+
width: 100%;
|
|
895
|
+
height: 15px;
|
|
896
|
+
transform-origin: top;
|
|
897
|
+
transform: rotateX(-90deg);
|
|
898
|
+
background:
|
|
899
|
+
linear-gradient(
|
|
900
|
+
rgb(255 255 255 / 0.8) 10%,
|
|
901
|
+
rgb(255 255 255 / 0.3) 30%,
|
|
902
|
+
color-mix(in srgb, var(--fp-switch-lamp) 40%, black) 75%,
|
|
903
|
+
color-mix(in srgb, var(--fp-switch-lamp) 20%, black)
|
|
904
|
+
)
|
|
905
|
+
50% 50% / 97% 97% no-repeat,
|
|
906
|
+
color-mix(in srgb, var(--fp-switch-lamp) 70%, black);
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
/* The bottom end, in shadow, with the shadow the paddle casts into the bezel. */
|
|
910
|
+
&::after {
|
|
911
|
+
content: "";
|
|
912
|
+
position: absolute;
|
|
913
|
+
bottom: 0;
|
|
914
|
+
left: 0;
|
|
915
|
+
width: 100%;
|
|
916
|
+
height: 15px;
|
|
917
|
+
transform-origin: top;
|
|
918
|
+
transform: translateY(15px) rotateX(-90deg);
|
|
919
|
+
background-image: linear-gradient(
|
|
920
|
+
color-mix(in srgb, var(--fp-switch-lamp) 40%, black),
|
|
921
|
+
color-mix(in srgb, var(--fp-switch-lamp) 20%, black)
|
|
922
|
+
);
|
|
923
|
+
box-shadow:
|
|
924
|
+
0 15px 3px 0 black,
|
|
925
|
+
0 24px 6px 0 rgb(0 0 0 / 0.5);
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
/* Forced colours: a plain paddle in the system's colours; fp-switch fills it when on. */
|
|
929
|
+
@media (forced-colors: active) {
|
|
930
|
+
forced-color-adjust: none;
|
|
931
|
+
background: Canvas;
|
|
932
|
+
outline: 1px solid CanvasText;
|
|
933
|
+
outline-offset: -1px;
|
|
934
|
+
|
|
935
|
+
&::before,
|
|
936
|
+
&::after {
|
|
937
|
+
content: none;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
/** The lamp behind an fp-switch's lens: lit when the switch is on, and slow to fade, like a filament cooling. */
|
|
943
|
+
.fp-switch-lamp {
|
|
944
|
+
position: absolute;
|
|
945
|
+
inset: 0;
|
|
946
|
+
opacity: 0;
|
|
947
|
+
background-image: radial-gradient(
|
|
948
|
+
color-mix(in srgb, var(--fp-switch-lamp) 35%, #fff2c8),
|
|
949
|
+
var(--fp-switch-lamp) 40%,
|
|
950
|
+
transparent 70%
|
|
951
|
+
);
|
|
952
|
+
transition: opacity 600ms ease-out;
|
|
953
|
+
|
|
954
|
+
@media (prefers-reduced-motion: reduce) {
|
|
955
|
+
transition-duration: 0ms;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
/* Forced colours: the rocker's fill shows the state instead. */
|
|
959
|
+
@media (forced-colors: active) {
|
|
960
|
+
display: none;
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
/**
|
|
965
|
+
* The front of an fp-switch's paddle: the dotted lens over the lamp, the I and O marks, and the
|
|
966
|
+
* shine that comes up on the pressed-in half. Decorative; the switch says its state.
|
|
967
|
+
*/
|
|
968
|
+
.fp-switch-face {
|
|
969
|
+
position: absolute;
|
|
970
|
+
inset: 0;
|
|
971
|
+
background:
|
|
972
|
+
linear-gradient(#fff, #fff) 50% 20% / max(2px, 5%) 20% no-repeat,
|
|
973
|
+
radial-gradient(circle, transparent 50%, #fff 52%, #fff 70%, transparent 72%) 50% 80% / 33% 25% no-repeat,
|
|
974
|
+
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;
|
|
975
|
+
|
|
976
|
+
/* The shine: faint while the I half stands out, full once it is pressed in. */
|
|
977
|
+
&::before,
|
|
978
|
+
&::after {
|
|
979
|
+
content: "";
|
|
980
|
+
position: absolute;
|
|
981
|
+
inset: 0;
|
|
982
|
+
transition: opacity 300ms cubic-bezier(1, 0, 1, 1);
|
|
983
|
+
|
|
984
|
+
@media (prefers-reduced-motion: reduce) {
|
|
985
|
+
transition-duration: 0ms;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
&::before {
|
|
990
|
+
opacity: 0.3;
|
|
991
|
+
background:
|
|
992
|
+
linear-gradient(#fff, transparent 3%) 50% 50% / 97% 97% no-repeat,
|
|
993
|
+
linear-gradient(rgb(255 255 255 / 0.5), transparent 50%, transparent 80%, rgb(255 255 255 / 0.5)) 50% 50% / 97% 97% no-repeat;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
/* The shading on the O half while it is pressed in. */
|
|
997
|
+
&::after {
|
|
998
|
+
opacity: 1;
|
|
999
|
+
background-image: linear-gradient(transparent 70%, rgb(0 0 0 / 0.8));
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
/* Forced colours: no lens or marks; the rocker's fill shows the state. */
|
|
1003
|
+
@media (forced-colors: active) {
|
|
1004
|
+
display: none;
|
|
1005
|
+
}
|
|
563
1006
|
}
|
|
564
1007
|
|
|
565
1008
|
/**
|
|
@@ -867,6 +1310,11 @@
|
|
|
867
1310
|
inset 1px 1px 0 var(--fp-color-bevel-hi),
|
|
868
1311
|
inset -1px -1px 0 var(--fp-color-bevel-lo),
|
|
869
1312
|
2px 3px 0 var(--fp-color-cast);
|
|
1313
|
+
|
|
1314
|
+
/* Forced colours drop the shadow that draws its edge: an outline keeps it. */
|
|
1315
|
+
@media (forced-colors: active) {
|
|
1316
|
+
outline: 1px solid CanvasText;
|
|
1317
|
+
}
|
|
870
1318
|
}
|
|
871
1319
|
|
|
872
1320
|
/** A module's own little title bar, in brushed chrome. */
|
|
@@ -929,6 +1377,11 @@
|
|
|
929
1377
|
box-shadow:
|
|
930
1378
|
inset 1px 1px 2px var(--fp-color-bevel-lo),
|
|
931
1379
|
inset -1px -1px 0 var(--fp-color-bevel-hi);
|
|
1380
|
+
|
|
1381
|
+
/* Forced colours drop the shading that recesses it: an outline keeps an empty well in view. */
|
|
1382
|
+
@media (forced-colors: active) {
|
|
1383
|
+
outline: 1px solid CanvasText;
|
|
1384
|
+
}
|
|
932
1385
|
}
|
|
933
1386
|
|
|
934
1387
|
/** Silkscreened hardware label. */
|
|
@@ -951,6 +1404,11 @@
|
|
|
951
1404
|
0 0 0 2px var(--fp-color-chrome-800),
|
|
952
1405
|
inset 2px 2px 4px rgb(0 0 0 / 0.35),
|
|
953
1406
|
inset -1px -1px 0 rgb(255 255 255 / 0.2);
|
|
1407
|
+
|
|
1408
|
+
/* Forced colours drop the chrome bezel: an outline frames the glass. */
|
|
1409
|
+
@media (forced-colors: active) {
|
|
1410
|
+
outline: 2px solid CanvasText;
|
|
1411
|
+
}
|
|
954
1412
|
}
|
|
955
1413
|
|
|
956
1414
|
/** One line of LCD text. Cells of fp-lcd-ghost stacked behind it show the unlit segments, as on real glass. */
|
|
@@ -968,6 +1426,11 @@
|
|
|
968
1426
|
/** The unlit segments behind an LCD line. Decorative: give it aria-hidden="true". */
|
|
969
1427
|
.fp-lcd-ghost {
|
|
970
1428
|
color: var(--fp-color-lcd-ghost);
|
|
1429
|
+
|
|
1430
|
+
/* Forced colours would draw the unlit segments as solid as the text over them. */
|
|
1431
|
+
@media (forced-colors: active) {
|
|
1432
|
+
visibility: hidden;
|
|
1433
|
+
}
|
|
971
1434
|
}
|
|
972
1435
|
|
|
973
1436
|
/** Dark housing that LEDs and small readouts sit in. */
|
|
@@ -984,6 +1447,11 @@
|
|
|
984
1447
|
box-shadow:
|
|
985
1448
|
inset 1px 1px 2px rgb(0 0 0 / 0.5),
|
|
986
1449
|
0 1px 0 var(--fp-color-bevel-hi);
|
|
1450
|
+
|
|
1451
|
+
/* Forced colours turn the dark housing into the page colour: an outline keeps it in view. */
|
|
1452
|
+
@media (forced-colors: active) {
|
|
1453
|
+
outline: 1px solid CanvasText;
|
|
1454
|
+
}
|
|
987
1455
|
}
|
|
988
1456
|
|
|
989
1457
|
/** An LED with its own dark bezel ring, so a lit one reads on light chrome too. Unlit by default. */
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontpanel/css",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"description": "Early-2000s hardware skin as plain CSS: liquid-chrome bezels, gel buttons, LCDs and LEDs. Tokens, classes and an optional Tailwind 4 entry.",
|
|
6
6
|
"author": "Dulan Katukurundage",
|
|
7
7
|
"license": "MIT",
|
package/page.css
CHANGED
|
@@ -70,6 +70,11 @@
|
|
|
70
70
|
0 0 0 1px var(--fp-color-edge),
|
|
71
71
|
inset 1px 1px 0 var(--fp-color-bevel-hi),
|
|
72
72
|
0 -10px 28px var(--fp-color-cast);
|
|
73
|
+
|
|
74
|
+
/* Forced colours drop the shadow that lifts it off the device: an outline keeps its edge. */
|
|
75
|
+
@media (forced-colors: active) {
|
|
76
|
+
outline: 1px solid CanvasText;
|
|
77
|
+
}
|
|
73
78
|
}
|
|
74
79
|
|
|
75
80
|
/** Inset text field or select on a matte panel. Its border keeps 3:1 against the panel in both skins. */
|
package/tailwind-page.css
CHANGED
|
@@ -71,6 +71,11 @@
|
|
|
71
71
|
0 0 0 1px var(--fp-color-edge),
|
|
72
72
|
inset 1px 1px 0 var(--fp-color-bevel-hi),
|
|
73
73
|
0 -10px 28px var(--fp-color-cast);
|
|
74
|
+
|
|
75
|
+
/* Forced colours drop the shadow that lifts it off the device: an outline keeps its edge. */
|
|
76
|
+
@media (forced-colors: active) {
|
|
77
|
+
outline: 1px solid CanvasText;
|
|
78
|
+
}
|
|
74
79
|
}
|
|
75
80
|
|
|
76
81
|
/** Inset text field or select on a matte panel. Its border keeps 3:1 against the panel in both skins. */
|
package/tailwind.css
CHANGED
|
@@ -150,6 +150,11 @@
|
|
|
150
150
|
overflow: clip;
|
|
151
151
|
border-radius: 26px;
|
|
152
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
|
+
}
|
|
153
158
|
}
|
|
154
159
|
|
|
155
160
|
/** Molded body plastic the modules sit on. */
|
|
@@ -254,6 +259,35 @@
|
|
|
254
259
|
&:disabled {
|
|
255
260
|
opacity: 0.45;
|
|
256
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
|
+
}
|
|
257
291
|
}
|
|
258
292
|
|
|
259
293
|
/**
|
|
@@ -317,6 +351,18 @@
|
|
|
317
351
|
&:disabled {
|
|
318
352
|
opacity: 0.45;
|
|
319
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
|
+
}
|
|
320
366
|
}
|
|
321
367
|
|
|
322
368
|
/**
|
|
@@ -389,6 +435,34 @@
|
|
|
389
435
|
&:disabled {
|
|
390
436
|
opacity: 0.5;
|
|
391
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
|
+
}
|
|
392
466
|
}
|
|
393
467
|
|
|
394
468
|
/** Blue plastic for an fp-cap, with its own label colour. */
|
|
@@ -416,14 +490,21 @@
|
|
|
416
490
|
}
|
|
417
491
|
|
|
418
492
|
/**
|
|
419
|
-
* A control with its silkscreen label under it: a knob, fader or
|
|
420
|
-
*
|
|
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.
|
|
421
496
|
*/
|
|
422
497
|
@utility fp-unit {
|
|
423
498
|
display: inline-flex;
|
|
424
499
|
flex-direction: column;
|
|
425
500
|
align-items: center;
|
|
426
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
|
+
}
|
|
427
508
|
}
|
|
428
509
|
|
|
429
510
|
/**
|
|
@@ -482,6 +563,15 @@
|
|
|
482
563
|
opacity: 0.45;
|
|
483
564
|
cursor: not-allowed;
|
|
484
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
|
+
}
|
|
485
575
|
}
|
|
486
576
|
|
|
487
577
|
/** The pointer line on a knob's cap. Turn it with `rotate`: -135deg at the minimum, 135deg at the maximum. */
|
|
@@ -501,6 +591,14 @@
|
|
|
501
591
|
border-radius: 2px;
|
|
502
592
|
background-color: var(--fp-color-ink);
|
|
503
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
|
+
}
|
|
504
602
|
}
|
|
505
603
|
|
|
506
604
|
/**
|
|
@@ -557,6 +655,13 @@
|
|
|
557
655
|
height: 6px;
|
|
558
656
|
translate: 0 -50%;
|
|
559
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
|
+
}
|
|
560
665
|
}
|
|
561
666
|
|
|
562
667
|
/**
|
|
@@ -619,6 +724,344 @@
|
|
|
619
724
|
outline-offset: 2px;
|
|
620
725
|
filter: drop-shadow(0 0 3px var(--fp-color-focus));
|
|
621
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
|
+
}
|
|
622
1065
|
}
|
|
623
1066
|
|
|
624
1067
|
/**
|
|
@@ -926,6 +1369,11 @@
|
|
|
926
1369
|
inset 1px 1px 0 var(--fp-color-bevel-hi),
|
|
927
1370
|
inset -1px -1px 0 var(--fp-color-bevel-lo),
|
|
928
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
|
+
}
|
|
929
1377
|
}
|
|
930
1378
|
|
|
931
1379
|
/** A module's own little title bar, in brushed chrome. */
|
|
@@ -988,6 +1436,11 @@
|
|
|
988
1436
|
box-shadow:
|
|
989
1437
|
inset 1px 1px 2px var(--fp-color-bevel-lo),
|
|
990
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
|
+
}
|
|
991
1444
|
}
|
|
992
1445
|
|
|
993
1446
|
/** Silkscreened hardware label. */
|
|
@@ -1010,6 +1463,11 @@
|
|
|
1010
1463
|
0 0 0 2px var(--fp-color-chrome-800),
|
|
1011
1464
|
inset 2px 2px 4px rgb(0 0 0 / 0.35),
|
|
1012
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
|
+
}
|
|
1013
1471
|
}
|
|
1014
1472
|
|
|
1015
1473
|
/** One line of LCD text. Cells of fp-lcd-ghost stacked behind it show the unlit segments, as on real glass. */
|
|
@@ -1027,6 +1485,11 @@
|
|
|
1027
1485
|
/** The unlit segments behind an LCD line. Decorative: give it aria-hidden="true". */
|
|
1028
1486
|
@utility fp-lcd-ghost {
|
|
1029
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
|
+
}
|
|
1030
1493
|
}
|
|
1031
1494
|
|
|
1032
1495
|
/** Dark housing that LEDs and small readouts sit in. */
|
|
@@ -1043,6 +1506,11 @@
|
|
|
1043
1506
|
box-shadow:
|
|
1044
1507
|
inset 1px 1px 2px rgb(0 0 0 / 0.5),
|
|
1045
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
|
+
}
|
|
1046
1514
|
}
|
|
1047
1515
|
|
|
1048
1516
|
/** An LED with its own dark bezel ring, so a lit one reads on light chrome too. Unlit by default. */
|