@frontpanel/css 0.2.0 → 0.4.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 +635 -2
- package/package.json +1 -1
- package/page.css +5 -0
- package/tailwind-page.css +5 -0
- package/tailwind.css +635 -2
package/components.css
CHANGED
|
@@ -29,6 +29,19 @@
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/* A menu dropping open from its button: a short slide down as it fades in. */
|
|
33
|
+
@keyframes fp-menu-drop {
|
|
34
|
+
from {
|
|
35
|
+
opacity: 0;
|
|
36
|
+
translate: 0 -4px;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
to {
|
|
40
|
+
opacity: 1;
|
|
41
|
+
translate: 0 0;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
32
45
|
/** Redraws an LCD line left to right in four steps, as when its value changes. */
|
|
33
46
|
.fp-lcd-redraw {
|
|
34
47
|
animation: fp-lcd-redraw 160ms steps(4, end);
|
|
@@ -91,6 +104,11 @@
|
|
|
91
104
|
overflow: clip;
|
|
92
105
|
border-radius: 26px;
|
|
93
106
|
}
|
|
107
|
+
|
|
108
|
+
/* Forced colours drop the chrome: an outline keeps the device's edge. */
|
|
109
|
+
@media (forced-colors: active) {
|
|
110
|
+
outline: 1px solid CanvasText;
|
|
111
|
+
}
|
|
94
112
|
}
|
|
95
113
|
|
|
96
114
|
/** Molded body plastic the modules sit on. */
|
|
@@ -195,6 +213,35 @@
|
|
|
195
213
|
&:disabled {
|
|
196
214
|
opacity: 0.45;
|
|
197
215
|
}
|
|
216
|
+
|
|
217
|
+
/*
|
|
218
|
+
* Forced colours drop the chrome and its shadows: an outline keeps the button's edge, the system's
|
|
219
|
+
* selected colours show it pressed, and GrayText, not a fade, shows it disabled.
|
|
220
|
+
*/
|
|
221
|
+
@media (forced-colors: active) {
|
|
222
|
+
outline: 1px solid ButtonText;
|
|
223
|
+
outline-offset: -1px;
|
|
224
|
+
|
|
225
|
+
&[aria-pressed="true"],
|
|
226
|
+
&[aria-expanded="true"],
|
|
227
|
+
&[aria-current="page"] {
|
|
228
|
+
forced-color-adjust: none;
|
|
229
|
+
background: SelectedItem;
|
|
230
|
+
box-shadow: none;
|
|
231
|
+
color: SelectedItemText;
|
|
232
|
+
|
|
233
|
+
/* Opted out, its focus ring would keep the skin's colour: the system's text colour shows on any ground. */
|
|
234
|
+
&:focus-visible {
|
|
235
|
+
outline-color: CanvasText;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
&:disabled {
|
|
240
|
+
opacity: 1;
|
|
241
|
+
color: GrayText;
|
|
242
|
+
outline-color: GrayText;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
198
245
|
}
|
|
199
246
|
|
|
200
247
|
/**
|
|
@@ -258,6 +305,46 @@
|
|
|
258
305
|
&:disabled {
|
|
259
306
|
opacity: 0.45;
|
|
260
307
|
}
|
|
308
|
+
|
|
309
|
+
/* Forced colours drop the gel and its shadows: an outline keeps its edge, and GrayText, not a fade, shows it disabled. */
|
|
310
|
+
@media (forced-colors: active) {
|
|
311
|
+
outline: 1px solid ButtonText;
|
|
312
|
+
outline-offset: -1px;
|
|
313
|
+
|
|
314
|
+
&:disabled {
|
|
315
|
+
opacity: 1;
|
|
316
|
+
color: GrayText;
|
|
317
|
+
outline-color: GrayText;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Chrome buttons joined side by side into one bezel, as on a transport row: fp-chrome-btn children
|
|
324
|
+
* share their seams, and only the outer corners stay round. Give it `role="group"` and a name.
|
|
325
|
+
*/
|
|
326
|
+
.fp-button-group {
|
|
327
|
+
display: inline-flex;
|
|
328
|
+
align-items: stretch;
|
|
329
|
+
|
|
330
|
+
/* The 1 px edge round each button falls in the gap, so neighbours share one seam. */
|
|
331
|
+
gap: 1px;
|
|
332
|
+
|
|
333
|
+
& > .fp-chrome-btn:not(:first-child) {
|
|
334
|
+
border-start-start-radius: 0;
|
|
335
|
+
border-end-start-radius: 0;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
& > .fp-chrome-btn:not(:last-child) {
|
|
339
|
+
border-start-end-radius: 0;
|
|
340
|
+
border-end-end-radius: 0;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/* A focused button's ring goes over its neighbours, not under them. */
|
|
344
|
+
& > .fp-chrome-btn:focus-visible {
|
|
345
|
+
position: relative;
|
|
346
|
+
z-index: 1;
|
|
347
|
+
}
|
|
261
348
|
}
|
|
262
349
|
|
|
263
350
|
/**
|
|
@@ -330,6 +417,34 @@
|
|
|
330
417
|
&:disabled {
|
|
331
418
|
opacity: 0.5;
|
|
332
419
|
}
|
|
420
|
+
|
|
421
|
+
/*
|
|
422
|
+
* Forced colours drop the plastic and its ring: an outline keeps the cap's edge, the system's
|
|
423
|
+
* selected colours show it selected, and GrayText, not a fade, shows it disabled.
|
|
424
|
+
*/
|
|
425
|
+
@media (forced-colors: active) {
|
|
426
|
+
outline: 1px solid ButtonText;
|
|
427
|
+
outline-offset: -1px;
|
|
428
|
+
|
|
429
|
+
&[aria-pressed="true"],
|
|
430
|
+
&[aria-checked="true"] {
|
|
431
|
+
forced-color-adjust: none;
|
|
432
|
+
background: SelectedItem;
|
|
433
|
+
box-shadow: none;
|
|
434
|
+
color: SelectedItemText;
|
|
435
|
+
|
|
436
|
+
/* Opted out, its focus ring would keep the skin's colour: the system's text colour shows on any ground. */
|
|
437
|
+
&:focus-visible {
|
|
438
|
+
outline-color: CanvasText;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
&:disabled {
|
|
443
|
+
opacity: 1;
|
|
444
|
+
color: GrayText;
|
|
445
|
+
outline-color: GrayText;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
333
448
|
}
|
|
334
449
|
|
|
335
450
|
/** Blue plastic for an fp-cap, with its own label colour. */
|
|
@@ -357,14 +472,21 @@
|
|
|
357
472
|
}
|
|
358
473
|
|
|
359
474
|
/**
|
|
360
|
-
* A control with its silkscreen label under it: a knob, fader or
|
|
361
|
-
*
|
|
475
|
+
* A control with its silkscreen label under it: a knob, fader, meter or switch and then an fp-silk.
|
|
476
|
+
* With `data-label-placement="start"` the label sits level with the control, before it in the
|
|
477
|
+
* line. A length set here, such as `--fp-fader-length`, reaches the control inside.
|
|
362
478
|
*/
|
|
363
479
|
.fp-unit {
|
|
364
480
|
display: inline-flex;
|
|
365
481
|
flex-direction: column;
|
|
366
482
|
align-items: center;
|
|
367
483
|
gap: 0.375rem;
|
|
484
|
+
|
|
485
|
+
/* Reversed, so the markup stays control then label either way. */
|
|
486
|
+
&[data-label-placement="start"] {
|
|
487
|
+
flex-direction: row-reverse;
|
|
488
|
+
gap: 0.5rem;
|
|
489
|
+
}
|
|
368
490
|
}
|
|
369
491
|
|
|
370
492
|
/**
|
|
@@ -423,6 +545,15 @@
|
|
|
423
545
|
opacity: 0.45;
|
|
424
546
|
cursor: not-allowed;
|
|
425
547
|
}
|
|
548
|
+
|
|
549
|
+
/* Forced colours drop the knurling, the dome and their shadows: outlines draw the skirt and the cap. */
|
|
550
|
+
@media (forced-colors: active) {
|
|
551
|
+
outline: 1px solid CanvasText;
|
|
552
|
+
|
|
553
|
+
&::before {
|
|
554
|
+
outline: 1px solid CanvasText;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
426
557
|
}
|
|
427
558
|
|
|
428
559
|
/** The pointer line on a knob's cap. Turn it with `rotate`: -135deg at the minimum, 135deg at the maximum. */
|
|
@@ -442,6 +573,14 @@
|
|
|
442
573
|
border-radius: 2px;
|
|
443
574
|
background-color: var(--fp-color-ink);
|
|
444
575
|
}
|
|
576
|
+
|
|
577
|
+
/* Forced colours would paint the line in the page colour, on the page colour. */
|
|
578
|
+
@media (forced-colors: active) {
|
|
579
|
+
&::after {
|
|
580
|
+
forced-color-adjust: none;
|
|
581
|
+
background-color: CanvasText;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
445
584
|
}
|
|
446
585
|
|
|
447
586
|
/**
|
|
@@ -498,6 +637,13 @@
|
|
|
498
637
|
height: 6px;
|
|
499
638
|
translate: 0 -50%;
|
|
500
639
|
}
|
|
640
|
+
|
|
641
|
+
/* Forced colours: the slot as a solid line, so the cap's travel shows. */
|
|
642
|
+
@media (forced-colors: active) {
|
|
643
|
+
forced-color-adjust: none;
|
|
644
|
+
background-color: CanvasText;
|
|
645
|
+
box-shadow: none;
|
|
646
|
+
}
|
|
501
647
|
}
|
|
502
648
|
|
|
503
649
|
/**
|
|
@@ -560,6 +706,344 @@
|
|
|
560
706
|
outline-offset: 2px;
|
|
561
707
|
filter: drop-shadow(0 0 3px var(--fp-color-focus));
|
|
562
708
|
}
|
|
709
|
+
|
|
710
|
+
/* Forced colours: a cap in the page colour hides the slot under it; an outline and the grip draw it. */
|
|
711
|
+
@media (forced-colors: active) {
|
|
712
|
+
background-color: Canvas;
|
|
713
|
+
outline: 1px solid CanvasText;
|
|
714
|
+
outline-offset: -1px;
|
|
715
|
+
|
|
716
|
+
&::after {
|
|
717
|
+
forced-color-adjust: none;
|
|
718
|
+
background-color: CanvasText;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* Illuminated rocker switch: a lamp-lit paddle in a dark bezel, marked I at the top and O at the
|
|
725
|
+
* bottom. Off, the O half is pressed in; on, the I half is, and the lamp behind the lens lights.
|
|
726
|
+
* Put it on a button with `role="switch"` and `aria-checked`, holding an fp-switch-rocker. The
|
|
727
|
+
* lamp is red; colour it with fp-led-green or fp-led-amber. 44 x 56 px, 36 x 46 px with a mouse,
|
|
728
|
+
* trackpad or pen. With `data-orientation="horizontal"` it lies on its side, 56 x 44 px (46 x 36),
|
|
729
|
+
* O on the left and I on the right. After an illuminated rocker pen by ykadosh (CodePen, MIT), rebuilt on the kit's
|
|
730
|
+
* tokens.
|
|
731
|
+
*/
|
|
732
|
+
.fp-switch {
|
|
733
|
+
--fp-switch-lamp: var(--fp-led, var(--fp-color-led-red));
|
|
734
|
+
position: relative;
|
|
735
|
+
display: inline-block;
|
|
736
|
+
flex-shrink: 0;
|
|
737
|
+
width: 2.75rem;
|
|
738
|
+
height: 3.5rem;
|
|
739
|
+
padding: 5px;
|
|
740
|
+
border: 0;
|
|
741
|
+
border-radius: 4px;
|
|
742
|
+
background-color: color-mix(in srgb, var(--fp-color-housing) 40%, black);
|
|
743
|
+
box-shadow:
|
|
744
|
+
0 0 0 1px var(--fp-color-edge),
|
|
745
|
+
inset 1px 1px 0 rgb(255 255 255 / 0.25),
|
|
746
|
+
inset 0 0 1px 3px var(--fp-color-housing),
|
|
747
|
+
inset 0 0 1px 5px color-mix(in srgb, var(--fp-color-housing) 40%, black),
|
|
748
|
+
1px 2px 0 var(--fp-color-cast);
|
|
749
|
+
perspective: 216px;
|
|
750
|
+
cursor: pointer;
|
|
751
|
+
|
|
752
|
+
@media (pointer: fine) {
|
|
753
|
+
width: 2.25rem;
|
|
754
|
+
height: 2.875rem;
|
|
755
|
+
padding: 4px;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
/* On: the I half goes in, the shine comes up, the shading goes, and the lamp lights as it lands. */
|
|
759
|
+
&[aria-checked="true"] .fp-switch-rocker {
|
|
760
|
+
transform: translateZ(6px) rotateX(25deg);
|
|
761
|
+
box-shadow: 0 -3px 6px var(--fp-switch-lamp);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
&[aria-checked="true"] .fp-switch-face::before {
|
|
765
|
+
opacity: 1;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
&[aria-checked="true"] .fp-switch-face::after {
|
|
769
|
+
opacity: 0;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
&[aria-checked="true"] .fp-switch-lamp {
|
|
773
|
+
opacity: 1;
|
|
774
|
+
transition-duration: 60ms;
|
|
775
|
+
transition-timing-function: linear;
|
|
776
|
+
transition-delay: 250ms;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
/*
|
|
780
|
+
* On its side the paddle tips across instead, and the marks stay upright. With the light at the
|
|
781
|
+
* top left, the right end is now the one in shade that casts the shadow, and the left end is lit.
|
|
782
|
+
*/
|
|
783
|
+
&[data-orientation="horizontal"] {
|
|
784
|
+
width: 3.5rem;
|
|
785
|
+
height: 2.75rem;
|
|
786
|
+
|
|
787
|
+
@media (pointer: fine) {
|
|
788
|
+
width: 2.875rem;
|
|
789
|
+
height: 2.25rem;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
& .fp-switch-rocker {
|
|
793
|
+
transform: translateZ(6px) rotateY(-25deg);
|
|
794
|
+
background: linear-gradient(
|
|
795
|
+
90deg,
|
|
796
|
+
color-mix(in srgb, var(--fp-switch-lamp) 60%, black) 0%,
|
|
797
|
+
color-mix(in srgb, var(--fp-switch-lamp) 44%, black) 30%,
|
|
798
|
+
color-mix(in srgb, var(--fp-switch-lamp) 44%, black) 70%,
|
|
799
|
+
color-mix(in srgb, var(--fp-switch-lamp) 60%, black) 100%
|
|
800
|
+
);
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/* The right end, in shade, with the shadow the paddle casts into the bezel. */
|
|
804
|
+
& .fp-switch-rocker::before {
|
|
805
|
+
left: auto;
|
|
806
|
+
right: 0;
|
|
807
|
+
width: 15px;
|
|
808
|
+
height: 100%;
|
|
809
|
+
transform-origin: right;
|
|
810
|
+
transform: rotateY(-90deg);
|
|
811
|
+
background: linear-gradient(
|
|
812
|
+
270deg,
|
|
813
|
+
color-mix(in srgb, var(--fp-switch-lamp) 40%, black),
|
|
814
|
+
color-mix(in srgb, var(--fp-switch-lamp) 20%, black)
|
|
815
|
+
);
|
|
816
|
+
box-shadow:
|
|
817
|
+
-15px 0 3px 0 black,
|
|
818
|
+
-24px 0 6px 0 rgb(0 0 0 / 0.5);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/* The left end, lit from the left while it stands proud. */
|
|
822
|
+
& .fp-switch-rocker::after {
|
|
823
|
+
top: 0;
|
|
824
|
+
bottom: auto;
|
|
825
|
+
width: 15px;
|
|
826
|
+
height: 100%;
|
|
827
|
+
transform-origin: right;
|
|
828
|
+
transform: translateX(-15px) rotateY(-90deg);
|
|
829
|
+
background:
|
|
830
|
+
linear-gradient(
|
|
831
|
+
270deg,
|
|
832
|
+
rgb(255 255 255 / 0.8) 10%,
|
|
833
|
+
rgb(255 255 255 / 0.3) 30%,
|
|
834
|
+
color-mix(in srgb, var(--fp-switch-lamp) 40%, black) 75%,
|
|
835
|
+
color-mix(in srgb, var(--fp-switch-lamp) 20%, black)
|
|
836
|
+
)
|
|
837
|
+
50% 50% / 97% 97% no-repeat,
|
|
838
|
+
color-mix(in srgb, var(--fp-switch-lamp) 70%, black);
|
|
839
|
+
box-shadow: none;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
& .fp-switch-face {
|
|
843
|
+
background:
|
|
844
|
+
linear-gradient(#fff, #fff) 78% 50% / max(2px, 4%) 28% no-repeat,
|
|
845
|
+
radial-gradient(circle, transparent 50%, #fff 52%, #fff 70%, transparent 72%) 22% 50% / 25% 33% no-repeat,
|
|
846
|
+
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;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
& .fp-switch-face::before {
|
|
850
|
+
background:
|
|
851
|
+
linear-gradient(#fff, transparent 3%) 50% 50% / 97% 97% no-repeat,
|
|
852
|
+
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;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
& .fp-switch-face::after {
|
|
856
|
+
background-image: linear-gradient(270deg, transparent 70%, rgb(0 0 0 / 0.8));
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
&[aria-checked="true"] .fp-switch-rocker {
|
|
860
|
+
transform: translateZ(6px) rotateY(25deg);
|
|
861
|
+
box-shadow: 3px 0 6px var(--fp-switch-lamp);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
/* A solid ring with a glow outside it: the glow alone fails non-text contrast on light chrome. */
|
|
866
|
+
&:focus-visible {
|
|
867
|
+
outline: 2px solid var(--fp-color-focus-ring);
|
|
868
|
+
outline-offset: 2px;
|
|
869
|
+
filter: drop-shadow(0 0 3px var(--fp-color-focus));
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
&:disabled,
|
|
873
|
+
&[aria-disabled="true"] {
|
|
874
|
+
opacity: 0.45;
|
|
875
|
+
cursor: not-allowed;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/*
|
|
879
|
+
* Forced colours drop the paddle's paint: an outline keeps the bezel's edge, and the rocker fills
|
|
880
|
+
* when on, as a lit LED does, so the state reads by shape as well as by the tilt.
|
|
881
|
+
*/
|
|
882
|
+
@media (forced-colors: active) {
|
|
883
|
+
outline: 1px solid ButtonText;
|
|
884
|
+
|
|
885
|
+
&[data-orientation="horizontal"] .fp-switch-rocker {
|
|
886
|
+
background: Canvas;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
&[aria-checked="true"] .fp-switch-rocker,
|
|
890
|
+
&[data-orientation="horizontal"][aria-checked="true"] .fp-switch-rocker {
|
|
891
|
+
background: CanvasText;
|
|
892
|
+
box-shadow: none;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
&:disabled,
|
|
896
|
+
&[aria-disabled="true"] {
|
|
897
|
+
opacity: 1;
|
|
898
|
+
outline-color: GrayText;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
/**
|
|
904
|
+
* The paddle of an fp-switch, tilted in 3D. It holds an fp-switch-lamp and then an fp-switch-face;
|
|
905
|
+
* its top and bottom ends show as it tips.
|
|
906
|
+
*/
|
|
907
|
+
.fp-switch-rocker {
|
|
908
|
+
position: relative;
|
|
909
|
+
display: block;
|
|
910
|
+
width: 100%;
|
|
911
|
+
height: 100%;
|
|
912
|
+
transform-origin: 50% 50% -6px;
|
|
913
|
+
transform: translateZ(6px) rotateX(-25deg);
|
|
914
|
+
transform-style: preserve-3d;
|
|
915
|
+
background: linear-gradient(
|
|
916
|
+
color-mix(in srgb, var(--fp-switch-lamp) 60%, black) 0%,
|
|
917
|
+
color-mix(in srgb, var(--fp-switch-lamp) 44%, black) 30%,
|
|
918
|
+
color-mix(in srgb, var(--fp-switch-lamp) 44%, black) 70%,
|
|
919
|
+
color-mix(in srgb, var(--fp-switch-lamp) 60%, black) 100%
|
|
920
|
+
);
|
|
921
|
+
|
|
922
|
+
/* Snaps over at the end of its travel, as a rocker's spring does. */
|
|
923
|
+
transition: transform 300ms cubic-bezier(1, 0, 1, 1), box-shadow 300ms cubic-bezier(1, 0, 1, 1);
|
|
924
|
+
|
|
925
|
+
@media (prefers-reduced-motion: reduce) {
|
|
926
|
+
transition-duration: 0ms;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
/* The top end, lit from above while it stands proud. */
|
|
930
|
+
&::before {
|
|
931
|
+
content: "";
|
|
932
|
+
position: absolute;
|
|
933
|
+
top: 0;
|
|
934
|
+
left: 0;
|
|
935
|
+
width: 100%;
|
|
936
|
+
height: 15px;
|
|
937
|
+
transform-origin: top;
|
|
938
|
+
transform: rotateX(-90deg);
|
|
939
|
+
background:
|
|
940
|
+
linear-gradient(
|
|
941
|
+
rgb(255 255 255 / 0.8) 10%,
|
|
942
|
+
rgb(255 255 255 / 0.3) 30%,
|
|
943
|
+
color-mix(in srgb, var(--fp-switch-lamp) 40%, black) 75%,
|
|
944
|
+
color-mix(in srgb, var(--fp-switch-lamp) 20%, black)
|
|
945
|
+
)
|
|
946
|
+
50% 50% / 97% 97% no-repeat,
|
|
947
|
+
color-mix(in srgb, var(--fp-switch-lamp) 70%, black);
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
/* The bottom end, in shadow, with the shadow the paddle casts into the bezel. */
|
|
951
|
+
&::after {
|
|
952
|
+
content: "";
|
|
953
|
+
position: absolute;
|
|
954
|
+
bottom: 0;
|
|
955
|
+
left: 0;
|
|
956
|
+
width: 100%;
|
|
957
|
+
height: 15px;
|
|
958
|
+
transform-origin: top;
|
|
959
|
+
transform: translateY(15px) rotateX(-90deg);
|
|
960
|
+
background-image: linear-gradient(
|
|
961
|
+
color-mix(in srgb, var(--fp-switch-lamp) 40%, black),
|
|
962
|
+
color-mix(in srgb, var(--fp-switch-lamp) 20%, black)
|
|
963
|
+
);
|
|
964
|
+
box-shadow:
|
|
965
|
+
0 15px 3px 0 black,
|
|
966
|
+
0 24px 6px 0 rgb(0 0 0 / 0.5);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
/* Forced colours: a plain paddle in the system's colours; fp-switch fills it when on. */
|
|
970
|
+
@media (forced-colors: active) {
|
|
971
|
+
forced-color-adjust: none;
|
|
972
|
+
background: Canvas;
|
|
973
|
+
outline: 1px solid CanvasText;
|
|
974
|
+
outline-offset: -1px;
|
|
975
|
+
|
|
976
|
+
&::before,
|
|
977
|
+
&::after {
|
|
978
|
+
content: none;
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
/** The lamp behind an fp-switch's lens: lit when the switch is on, and slow to fade, like a filament cooling. */
|
|
984
|
+
.fp-switch-lamp {
|
|
985
|
+
position: absolute;
|
|
986
|
+
inset: 0;
|
|
987
|
+
opacity: 0;
|
|
988
|
+
background-image: radial-gradient(
|
|
989
|
+
color-mix(in srgb, var(--fp-switch-lamp) 35%, #fff2c8),
|
|
990
|
+
var(--fp-switch-lamp) 40%,
|
|
991
|
+
transparent 70%
|
|
992
|
+
);
|
|
993
|
+
transition: opacity 600ms ease-out;
|
|
994
|
+
|
|
995
|
+
@media (prefers-reduced-motion: reduce) {
|
|
996
|
+
transition-duration: 0ms;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
/* Forced colours: the rocker's fill shows the state instead. */
|
|
1000
|
+
@media (forced-colors: active) {
|
|
1001
|
+
display: none;
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
/**
|
|
1006
|
+
* The front of an fp-switch's paddle: the dotted lens over the lamp, the I and O marks, and the
|
|
1007
|
+
* shine that comes up on the pressed-in half. Decorative; the switch says its state.
|
|
1008
|
+
*/
|
|
1009
|
+
.fp-switch-face {
|
|
1010
|
+
position: absolute;
|
|
1011
|
+
inset: 0;
|
|
1012
|
+
background:
|
|
1013
|
+
linear-gradient(#fff, #fff) 50% 20% / max(2px, 5%) 20% no-repeat,
|
|
1014
|
+
radial-gradient(circle, transparent 50%, #fff 52%, #fff 70%, transparent 72%) 50% 80% / 33% 25% no-repeat,
|
|
1015
|
+
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;
|
|
1016
|
+
|
|
1017
|
+
/* The shine: faint while the I half stands out, full once it is pressed in. */
|
|
1018
|
+
&::before,
|
|
1019
|
+
&::after {
|
|
1020
|
+
content: "";
|
|
1021
|
+
position: absolute;
|
|
1022
|
+
inset: 0;
|
|
1023
|
+
transition: opacity 300ms cubic-bezier(1, 0, 1, 1);
|
|
1024
|
+
|
|
1025
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1026
|
+
transition-duration: 0ms;
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
&::before {
|
|
1031
|
+
opacity: 0.3;
|
|
1032
|
+
background:
|
|
1033
|
+
linear-gradient(#fff, transparent 3%) 50% 50% / 97% 97% no-repeat,
|
|
1034
|
+
linear-gradient(rgb(255 255 255 / 0.5), transparent 50%, transparent 80%, rgb(255 255 255 / 0.5)) 50% 50% / 97% 97% no-repeat;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
/* The shading on the O half while it is pressed in. */
|
|
1038
|
+
&::after {
|
|
1039
|
+
opacity: 1;
|
|
1040
|
+
background-image: linear-gradient(transparent 70%, rgb(0 0 0 / 0.8));
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
/* Forced colours: no lens or marks; the rocker's fill shows the state. */
|
|
1044
|
+
@media (forced-colors: active) {
|
|
1045
|
+
display: none;
|
|
1046
|
+
}
|
|
563
1047
|
}
|
|
564
1048
|
|
|
565
1049
|
/**
|
|
@@ -867,6 +1351,11 @@
|
|
|
867
1351
|
inset 1px 1px 0 var(--fp-color-bevel-hi),
|
|
868
1352
|
inset -1px -1px 0 var(--fp-color-bevel-lo),
|
|
869
1353
|
2px 3px 0 var(--fp-color-cast);
|
|
1354
|
+
|
|
1355
|
+
/* Forced colours drop the shadow that draws its edge: an outline keeps it. */
|
|
1356
|
+
@media (forced-colors: active) {
|
|
1357
|
+
outline: 1px solid CanvasText;
|
|
1358
|
+
}
|
|
870
1359
|
}
|
|
871
1360
|
|
|
872
1361
|
/** A module's own little title bar, in brushed chrome. */
|
|
@@ -921,6 +1410,130 @@
|
|
|
921
1410
|
padding: 0.75rem;
|
|
922
1411
|
}
|
|
923
1412
|
|
|
1413
|
+
/**
|
|
1414
|
+
* Faceplate menu: a small brushed-chrome plate that drops from the button that opens it, holding
|
|
1415
|
+
* fp-menu-item rows and fp-menu-separator grooves. Placing it is up to you, or to Reka's
|
|
1416
|
+
* DropdownMenu, which also sets the `data-state="open"` it drops in on.
|
|
1417
|
+
*/
|
|
1418
|
+
.fp-menu {
|
|
1419
|
+
color-scheme: var(--fp-scheme);
|
|
1420
|
+
z-index: 50;
|
|
1421
|
+
box-sizing: border-box;
|
|
1422
|
+
min-width: 10rem;
|
|
1423
|
+
overflow-y: auto;
|
|
1424
|
+
padding: 0.25rem;
|
|
1425
|
+
border-radius: 6px;
|
|
1426
|
+
color: var(--fp-color-ink);
|
|
1427
|
+
background-image: linear-gradient(
|
|
1428
|
+
180deg,
|
|
1429
|
+
color-mix(in srgb, var(--fp-color-chrome-hi) 60%, var(--fp-color-chrome-100)) 0%,
|
|
1430
|
+
var(--fp-color-chrome-100) 40%,
|
|
1431
|
+
var(--fp-color-chrome-300) 100%
|
|
1432
|
+
);
|
|
1433
|
+
box-shadow:
|
|
1434
|
+
0 0 0 1px var(--fp-color-edge),
|
|
1435
|
+
inset 1px 1px 0 var(--fp-color-bevel-hi),
|
|
1436
|
+
inset -1px -1px 0 var(--fp-color-bevel-lo),
|
|
1437
|
+
2px 3px 0 var(--fp-color-cast),
|
|
1438
|
+
0 12px 18px -8px var(--fp-color-cast);
|
|
1439
|
+
|
|
1440
|
+
/* The plate takes focus when a pointer opens it; its rows show where the keys are. */
|
|
1441
|
+
outline: none;
|
|
1442
|
+
|
|
1443
|
+
&[data-state="open"] {
|
|
1444
|
+
animation: fp-menu-drop 90ms ease-out;
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1448
|
+
&[data-state="open"] {
|
|
1449
|
+
animation: none;
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
/* Forced colours drop the chrome and its shadows: an outline keeps the plate's edge. */
|
|
1454
|
+
@media (forced-colors: active) {
|
|
1455
|
+
outline: 1px solid CanvasText;
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
/**
|
|
1460
|
+
* A row on an fp-menu: a silkscreened label, with an fp-icon before it if you like. The highlighted
|
|
1461
|
+
* row presses into the plate; Reka sets `data-highlighted` on the row under the pointer or the
|
|
1462
|
+
* arrow keys, and `data-disabled` on one that can't be chosen. 44 px tall, 36 px with a mouse,
|
|
1463
|
+
* trackpad or pen.
|
|
1464
|
+
*/
|
|
1465
|
+
.fp-menu-item {
|
|
1466
|
+
display: flex;
|
|
1467
|
+
align-items: center;
|
|
1468
|
+
gap: 0.5rem;
|
|
1469
|
+
min-height: 2.75rem;
|
|
1470
|
+
padding-inline: 0.625rem;
|
|
1471
|
+
border-radius: 4px;
|
|
1472
|
+
color: var(--fp-color-ink);
|
|
1473
|
+
font-family: var(--fp-font-display);
|
|
1474
|
+
font-size: 11px;
|
|
1475
|
+
line-height: 1.3;
|
|
1476
|
+
letter-spacing: 0.08em;
|
|
1477
|
+
text-transform: uppercase;
|
|
1478
|
+
text-decoration: none;
|
|
1479
|
+
white-space: nowrap;
|
|
1480
|
+
cursor: default;
|
|
1481
|
+
user-select: none;
|
|
1482
|
+
outline: none;
|
|
1483
|
+
|
|
1484
|
+
@media (pointer: fine) {
|
|
1485
|
+
min-height: 2.25rem;
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
&[data-highlighted] {
|
|
1489
|
+
background-color: var(--fp-color-chrome-300);
|
|
1490
|
+
box-shadow:
|
|
1491
|
+
inset 1px 1px 3px var(--fp-color-bevel-lo),
|
|
1492
|
+
inset -1px -1px 0 var(--fp-color-bevel-hi);
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
/* The keyboard's row also gets the ring every focused control has, inside the row. */
|
|
1496
|
+
&:focus-visible {
|
|
1497
|
+
outline: 2px solid var(--fp-color-focus-ring);
|
|
1498
|
+
outline-offset: -2px;
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
&[data-disabled] {
|
|
1502
|
+
opacity: 0.45;
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
/*
|
|
1506
|
+
* Forced colours drop the pressed-in shading: the system's highlight colours show the highlighted
|
|
1507
|
+
* row, and GrayText, not a fade, shows a disabled one.
|
|
1508
|
+
*/
|
|
1509
|
+
@media (forced-colors: active) {
|
|
1510
|
+
&[data-highlighted] {
|
|
1511
|
+
forced-color-adjust: none;
|
|
1512
|
+
background: Highlight;
|
|
1513
|
+
box-shadow: none;
|
|
1514
|
+
color: HighlightText;
|
|
1515
|
+
|
|
1516
|
+
/* Opted out, its focus ring would keep the skin's colour: the system's text colour shows on any ground. */
|
|
1517
|
+
&:focus-visible {
|
|
1518
|
+
outline-color: HighlightText;
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
&[data-disabled] {
|
|
1523
|
+
opacity: 1;
|
|
1524
|
+
color: GrayText;
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
/** A groove between groups of rows on an fp-menu, cut into the plate: shaded above, lit below. */
|
|
1530
|
+
.fp-menu-separator {
|
|
1531
|
+
height: 0;
|
|
1532
|
+
margin: 0.25rem 0.375rem;
|
|
1533
|
+
border-top: 1px solid var(--fp-color-bevel-lo);
|
|
1534
|
+
border-bottom: 1px solid var(--fp-color-bevel-hi);
|
|
1535
|
+
}
|
|
1536
|
+
|
|
924
1537
|
/** Recessed well inside a matte panel, for secondary content or an empty slot. */
|
|
925
1538
|
.fp-well {
|
|
926
1539
|
color-scheme: var(--fp-scheme);
|
|
@@ -929,6 +1542,11 @@
|
|
|
929
1542
|
box-shadow:
|
|
930
1543
|
inset 1px 1px 2px var(--fp-color-bevel-lo),
|
|
931
1544
|
inset -1px -1px 0 var(--fp-color-bevel-hi);
|
|
1545
|
+
|
|
1546
|
+
/* Forced colours drop the shading that recesses it: an outline keeps an empty well in view. */
|
|
1547
|
+
@media (forced-colors: active) {
|
|
1548
|
+
outline: 1px solid CanvasText;
|
|
1549
|
+
}
|
|
932
1550
|
}
|
|
933
1551
|
|
|
934
1552
|
/** Silkscreened hardware label. */
|
|
@@ -951,6 +1569,11 @@
|
|
|
951
1569
|
0 0 0 2px var(--fp-color-chrome-800),
|
|
952
1570
|
inset 2px 2px 4px rgb(0 0 0 / 0.35),
|
|
953
1571
|
inset -1px -1px 0 rgb(255 255 255 / 0.2);
|
|
1572
|
+
|
|
1573
|
+
/* Forced colours drop the chrome bezel: an outline frames the glass. */
|
|
1574
|
+
@media (forced-colors: active) {
|
|
1575
|
+
outline: 2px solid CanvasText;
|
|
1576
|
+
}
|
|
954
1577
|
}
|
|
955
1578
|
|
|
956
1579
|
/** One line of LCD text. Cells of fp-lcd-ghost stacked behind it show the unlit segments, as on real glass. */
|
|
@@ -968,6 +1591,11 @@
|
|
|
968
1591
|
/** The unlit segments behind an LCD line. Decorative: give it aria-hidden="true". */
|
|
969
1592
|
.fp-lcd-ghost {
|
|
970
1593
|
color: var(--fp-color-lcd-ghost);
|
|
1594
|
+
|
|
1595
|
+
/* Forced colours would draw the unlit segments as solid as the text over them. */
|
|
1596
|
+
@media (forced-colors: active) {
|
|
1597
|
+
visibility: hidden;
|
|
1598
|
+
}
|
|
971
1599
|
}
|
|
972
1600
|
|
|
973
1601
|
/** Dark housing that LEDs and small readouts sit in. */
|
|
@@ -984,6 +1612,11 @@
|
|
|
984
1612
|
box-shadow:
|
|
985
1613
|
inset 1px 1px 2px rgb(0 0 0 / 0.5),
|
|
986
1614
|
0 1px 0 var(--fp-color-bevel-hi);
|
|
1615
|
+
|
|
1616
|
+
/* Forced colours turn the dark housing into the page colour: an outline keeps it in view. */
|
|
1617
|
+
@media (forced-colors: active) {
|
|
1618
|
+
outline: 1px solid CanvasText;
|
|
1619
|
+
}
|
|
987
1620
|
}
|
|
988
1621
|
|
|
989
1622
|
/** An LED with its own dark bezel ring, so a lit one reads on light chrome too. Unlit by default. */
|