@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/base.css +1 -9
- package/components.css +1026 -5
- package/package.json +1 -1
- package/page.css +7 -0
- package/tailwind-base.css +1 -9
- package/tailwind-page.css +7 -0
- package/tailwind.css +1026 -5
- package/tokens.css +6 -3
package/components.css
CHANGED
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
|
|
50
50
|
/** The page behind the device: quiet blue-grey with a faint Y2K grid. */
|
|
51
51
|
.fp-device-backdrop {
|
|
52
|
+
color-scheme: var(--fp-scheme);
|
|
52
53
|
background-color: var(--fp-color-backdrop);
|
|
53
54
|
background-image:
|
|
54
55
|
linear-gradient(var(--fp-color-grid) 1px, transparent 1px),
|
|
@@ -64,6 +65,7 @@
|
|
|
64
65
|
|
|
65
66
|
/** Liquid-chrome ring around the device body. Diagonal so the reflection band crosses the corners. */
|
|
66
67
|
.fp-device-rim {
|
|
68
|
+
color-scheme: var(--fp-scheme);
|
|
67
69
|
border-radius: 36px;
|
|
68
70
|
padding: 10px;
|
|
69
71
|
background-image: linear-gradient(
|
|
@@ -89,10 +91,16 @@
|
|
|
89
91
|
overflow: clip;
|
|
90
92
|
border-radius: 26px;
|
|
91
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
|
+
}
|
|
92
99
|
}
|
|
93
100
|
|
|
94
101
|
/** Molded body plastic the modules sit on. */
|
|
95
102
|
.fp-device-body {
|
|
103
|
+
color-scheme: var(--fp-scheme);
|
|
96
104
|
background-image: linear-gradient(180deg, var(--fp-color-shell-hi), var(--fp-color-shell) 240px);
|
|
97
105
|
box-shadow:
|
|
98
106
|
inset 1px 1px 0 var(--fp-color-bevel-hi),
|
|
@@ -192,6 +200,35 @@
|
|
|
192
200
|
&:disabled {
|
|
193
201
|
opacity: 0.45;
|
|
194
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
|
+
}
|
|
195
232
|
}
|
|
196
233
|
|
|
197
234
|
/**
|
|
@@ -255,6 +292,18 @@
|
|
|
255
292
|
&:disabled {
|
|
256
293
|
opacity: 0.45;
|
|
257
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
|
+
}
|
|
258
307
|
}
|
|
259
308
|
|
|
260
309
|
/**
|
|
@@ -327,6 +376,34 @@
|
|
|
327
376
|
&:disabled {
|
|
328
377
|
opacity: 0.5;
|
|
329
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
|
+
}
|
|
330
407
|
}
|
|
331
408
|
|
|
332
409
|
/** Blue plastic for an fp-cap, with its own label colour. */
|
|
@@ -353,8 +430,879 @@
|
|
|
353
430
|
--fp-cap-ink: var(--fp-color-cap-berry-ink);
|
|
354
431
|
}
|
|
355
432
|
|
|
433
|
+
/**
|
|
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.
|
|
437
|
+
*/
|
|
438
|
+
.fp-unit {
|
|
439
|
+
display: inline-flex;
|
|
440
|
+
flex-direction: column;
|
|
441
|
+
align-items: center;
|
|
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
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Rotary knob: a knurled chrome skirt round a domed cap. The cap is lit from the top left and stays
|
|
453
|
+
* still; only its fp-knob-mark turns, by `rotate`, through 270 degrees. 48 px, 40 px with a mouse,
|
|
454
|
+
* trackpad or pen.
|
|
455
|
+
*/
|
|
456
|
+
.fp-knob {
|
|
457
|
+
position: relative;
|
|
458
|
+
display: inline-block;
|
|
459
|
+
flex-shrink: 0;
|
|
460
|
+
width: 3rem;
|
|
461
|
+
height: 3rem;
|
|
462
|
+
border-radius: 50%;
|
|
463
|
+
background-image: repeating-conic-gradient(var(--fp-color-chrome-100) 0deg 5deg, var(--fp-color-chrome-300) 5deg 10deg);
|
|
464
|
+
box-shadow:
|
|
465
|
+
0 0 0 1px var(--fp-color-edge),
|
|
466
|
+
inset 1px 1px 0 var(--fp-color-bevel-hi),
|
|
467
|
+
inset -1px -1px 0 var(--fp-color-bevel-lo),
|
|
468
|
+
1px 2px 0 var(--fp-color-cast);
|
|
469
|
+
cursor: ns-resize;
|
|
470
|
+
touch-action: none;
|
|
471
|
+
user-select: none;
|
|
472
|
+
|
|
473
|
+
@media (pointer: fine) {
|
|
474
|
+
width: 2.5rem;
|
|
475
|
+
height: 2.5rem;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
&::before {
|
|
479
|
+
content: "";
|
|
480
|
+
position: absolute;
|
|
481
|
+
inset: 16%;
|
|
482
|
+
border-radius: 50%;
|
|
483
|
+
background-image: radial-gradient(
|
|
484
|
+
circle at 30% 25%,
|
|
485
|
+
color-mix(in srgb, var(--fp-color-chrome-hi) 60%, var(--fp-color-chrome-100)) 0%,
|
|
486
|
+
var(--fp-color-chrome-100) 45%,
|
|
487
|
+
var(--fp-color-chrome-300) 100%
|
|
488
|
+
);
|
|
489
|
+
box-shadow:
|
|
490
|
+
0 0 0 1px var(--fp-color-edge),
|
|
491
|
+
inset 1px 1px 0 var(--fp-color-bevel-hi),
|
|
492
|
+
inset -1px -1px 0 var(--fp-color-bevel-lo),
|
|
493
|
+
1px 1px 2px var(--fp-color-cast);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/* A solid ring with a glow outside it: the glow alone fails non-text contrast on light chrome. */
|
|
497
|
+
&:focus-visible {
|
|
498
|
+
outline: 2px solid var(--fp-color-focus-ring);
|
|
499
|
+
outline-offset: 2px;
|
|
500
|
+
filter: drop-shadow(0 0 3px var(--fp-color-focus));
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
&[aria-disabled="true"] {
|
|
504
|
+
opacity: 0.45;
|
|
505
|
+
cursor: not-allowed;
|
|
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
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/** The pointer line on a knob's cap. Turn it with `rotate`: -135deg at the minimum, 135deg at the maximum. */
|
|
519
|
+
.fp-knob-mark {
|
|
520
|
+
position: absolute;
|
|
521
|
+
inset: 0;
|
|
522
|
+
pointer-events: none;
|
|
523
|
+
|
|
524
|
+
&::after {
|
|
525
|
+
content: "";
|
|
526
|
+
position: absolute;
|
|
527
|
+
top: 21%;
|
|
528
|
+
left: 50%;
|
|
529
|
+
width: 3px;
|
|
530
|
+
height: 24%;
|
|
531
|
+
translate: -50% 0;
|
|
532
|
+
border-radius: 2px;
|
|
533
|
+
background-color: var(--fp-color-ink);
|
|
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
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Fader: a chrome cap on a recessed slot. It stands up by default and lies on its side with
|
|
547
|
+
* `data-orientation="horizontal"`; `--fp-fader-length` sets how far it travels (8rem). 44 px
|
|
548
|
+
* across, 36 px with a mouse, trackpad or pen. Pressing the slot moves the cap there.
|
|
549
|
+
*/
|
|
550
|
+
.fp-fader {
|
|
551
|
+
position: relative;
|
|
552
|
+
display: inline-block;
|
|
553
|
+
flex-shrink: 0;
|
|
554
|
+
width: 2.75rem;
|
|
555
|
+
height: var(--fp-fader-length, 8rem);
|
|
556
|
+
cursor: ns-resize;
|
|
557
|
+
touch-action: none;
|
|
558
|
+
user-select: none;
|
|
559
|
+
|
|
560
|
+
@media (pointer: fine) {
|
|
561
|
+
width: 2.25rem;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
&[data-orientation="horizontal"] {
|
|
565
|
+
width: var(--fp-fader-length, 8rem);
|
|
566
|
+
height: 2.75rem;
|
|
567
|
+
cursor: ew-resize;
|
|
568
|
+
|
|
569
|
+
@media (pointer: fine) {
|
|
570
|
+
height: 2.25rem;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
&[data-disabled] {
|
|
575
|
+
opacity: 0.45;
|
|
576
|
+
cursor: not-allowed;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/** The slot a fader's cap rides in, cut into the panel: shaded on its top and left inside edges. */
|
|
581
|
+
.fp-fader-slot {
|
|
582
|
+
position: absolute;
|
|
583
|
+
inset: 0.5rem auto;
|
|
584
|
+
left: 50%;
|
|
585
|
+
width: 6px;
|
|
586
|
+
translate: -50% 0;
|
|
587
|
+
border-radius: 3px;
|
|
588
|
+
background-color: var(--fp-color-housing);
|
|
589
|
+
box-shadow:
|
|
590
|
+
inset 1px 1px 2px rgb(0 0 0 / 0.5),
|
|
591
|
+
1px 1px 0 var(--fp-color-bevel-hi);
|
|
592
|
+
|
|
593
|
+
&[data-orientation="horizontal"] {
|
|
594
|
+
inset: 50% 0.5rem auto;
|
|
595
|
+
width: auto;
|
|
596
|
+
height: 6px;
|
|
597
|
+
translate: 0 -50%;
|
|
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
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* A fader's cap: brushed chrome lit from the top left, with an ink grip line that marks the value.
|
|
610
|
+
* Place it along the slot with `bottom` (or `left` on its side, with `data-orientation="horizontal"`
|
|
611
|
+
* on the slot and cap as well as the fader).
|
|
612
|
+
*/
|
|
613
|
+
.fp-fader-cap {
|
|
614
|
+
position: absolute;
|
|
615
|
+
inset-inline: 0;
|
|
616
|
+
height: 1.5rem;
|
|
617
|
+
border-radius: 4px;
|
|
618
|
+
background-image: linear-gradient(
|
|
619
|
+
180deg,
|
|
620
|
+
color-mix(in srgb, var(--fp-color-chrome-hi) 60%, var(--fp-color-chrome-100)) 0%,
|
|
621
|
+
var(--fp-color-chrome-100) 45%,
|
|
622
|
+
var(--fp-color-chrome-300) 100%
|
|
623
|
+
);
|
|
624
|
+
box-shadow:
|
|
625
|
+
0 0 0 1px var(--fp-color-edge),
|
|
626
|
+
inset 1px 1px 0 var(--fp-color-bevel-hi),
|
|
627
|
+
inset -1px -1px 0 var(--fp-color-bevel-lo),
|
|
628
|
+
1px 2px 0 var(--fp-color-cast);
|
|
629
|
+
|
|
630
|
+
@media (pointer: fine) {
|
|
631
|
+
height: 1.25rem;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
&::after {
|
|
635
|
+
content: "";
|
|
636
|
+
position: absolute;
|
|
637
|
+
inset: 50% 20% auto;
|
|
638
|
+
height: 2px;
|
|
639
|
+
translate: 0 -50%;
|
|
640
|
+
border-radius: 1px;
|
|
641
|
+
background-color: var(--fp-color-ink);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
&[data-orientation="horizontal"] {
|
|
645
|
+
inset-inline: auto;
|
|
646
|
+
inset-block: 0;
|
|
647
|
+
width: 1.5rem;
|
|
648
|
+
height: auto;
|
|
649
|
+
|
|
650
|
+
@media (pointer: fine) {
|
|
651
|
+
width: 1.25rem;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
&::after {
|
|
655
|
+
inset: 20% auto 20% 50%;
|
|
656
|
+
width: 2px;
|
|
657
|
+
height: auto;
|
|
658
|
+
translate: -50% 0;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/* A solid ring with a glow outside it: the glow alone fails non-text contrast on light chrome. */
|
|
663
|
+
&:focus-visible {
|
|
664
|
+
outline: 2px solid var(--fp-color-focus-ring);
|
|
665
|
+
outline-offset: 2px;
|
|
666
|
+
filter: drop-shadow(0 0 3px var(--fp-color-focus));
|
|
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
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
/**
|
|
1009
|
+
* Level meter: a strip of LED segments in a dark housing, lit green, then amber, then red. Set
|
|
1010
|
+
* `--fp-meter-level` (0 to 1, in whole segments), `--fp-meter-segments` (12), and where amber and
|
|
1011
|
+
* red begin, `--fp-meter-amber` (8/12) and `--fp-meter-red` (11/12), as shares of the strip on
|
|
1012
|
+
* segment edges, or a segment lights in two colours. It stands
|
|
1013
|
+
* up by default and lies on its side with `data-orientation="horizontal"`; `--fp-meter-length`
|
|
1014
|
+
* sets how long it is (8rem). Give it `role="meter"` and its values, or `aria-hidden` beside a
|
|
1015
|
+
* readout that says the same.
|
|
1016
|
+
*/
|
|
1017
|
+
.fp-meter {
|
|
1018
|
+
--fp-meter-zones: linear-gradient(
|
|
1019
|
+
to top,
|
|
1020
|
+
var(--fp-color-led-on) 0 calc(var(--fp-meter-amber, calc(8 / 12)) * 100%),
|
|
1021
|
+
var(--fp-color-led-amber) 0 calc(var(--fp-meter-red, calc(11 / 12)) * 100%),
|
|
1022
|
+
var(--fp-color-led-red) 0
|
|
1023
|
+
);
|
|
1024
|
+
--fp-meter-gaps: repeating-linear-gradient(
|
|
1025
|
+
to top,
|
|
1026
|
+
#000 0 calc(100% / var(--fp-meter-segments, 12) - 2px),
|
|
1027
|
+
transparent 0 calc(100% / var(--fp-meter-segments, 12))
|
|
1028
|
+
);
|
|
1029
|
+
|
|
1030
|
+
/* Lit segments spill a little light into the gaps beside them: the lens's glow. */
|
|
1031
|
+
--fp-meter-lit-gaps: repeating-linear-gradient(
|
|
1032
|
+
to top,
|
|
1033
|
+
#000 0 calc(100% / var(--fp-meter-segments, 12) - 2px),
|
|
1034
|
+
rgb(0 0 0 / calc(0.18 * var(--fp-led-glow, 1))) 0 calc(100% / var(--fp-meter-segments, 12))
|
|
1035
|
+
);
|
|
1036
|
+
--fp-meter-cell: 100% calc(100% / var(--fp-meter-segments, 12));
|
|
1037
|
+
position: relative;
|
|
1038
|
+
display: inline-block;
|
|
1039
|
+
flex-shrink: 0;
|
|
1040
|
+
width: 0.75rem;
|
|
1041
|
+
height: var(--fp-meter-length, 8rem);
|
|
1042
|
+
border-radius: 3px;
|
|
1043
|
+
background-color: var(--fp-color-housing);
|
|
1044
|
+
box-shadow:
|
|
1045
|
+
inset 1px 1px 2px rgb(0 0 0 / 0.5),
|
|
1046
|
+
1px 1px 0 var(--fp-color-bevel-hi);
|
|
1047
|
+
|
|
1048
|
+
/* Each segment's gap sits at its far end, so the strip's own inset at that end is the gap. */
|
|
1049
|
+
&::before,
|
|
1050
|
+
&::after {
|
|
1051
|
+
content: "";
|
|
1052
|
+
position: absolute;
|
|
1053
|
+
inset: 0 2px 2px;
|
|
1054
|
+
background-image: var(--fp-meter-zones);
|
|
1055
|
+
mask-image: var(--fp-meter-gaps);
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
/* Unlit segments are the LED's off colour with a trace of their zone's, so the zones show. */
|
|
1059
|
+
&::before {
|
|
1060
|
+
background-image:
|
|
1061
|
+
linear-gradient(color-mix(in srgb, var(--fp-color-led-off) 75%, transparent) 0 0),
|
|
1062
|
+
var(--fp-meter-zones);
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
/*
|
|
1066
|
+
* A lit segment is brightest over its chip, in the middle, by the lens. The cut sits 1 px into the
|
|
1067
|
+
* gap below the next segment, so rounding never lights a sliver of it.
|
|
1068
|
+
*/
|
|
1069
|
+
&::after {
|
|
1070
|
+
background-image:
|
|
1071
|
+
radial-gradient(closest-side, rgb(255 255 255 / var(--fp-led-core, 0.3)), transparent),
|
|
1072
|
+
var(--fp-meter-zones);
|
|
1073
|
+
background-position: left bottom;
|
|
1074
|
+
background-size: var(--fp-meter-cell), auto;
|
|
1075
|
+
mask-image: var(--fp-meter-lit-gaps);
|
|
1076
|
+
clip-path: inset(calc((1 - var(--fp-meter-level, 0)) * 100% + 1px) 0 0 0);
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
/* Forced colours: the strip as an outline and the lit segments in CanvasText, so the level shows. */
|
|
1080
|
+
@media (forced-colors: active) {
|
|
1081
|
+
outline: 1px solid CanvasText;
|
|
1082
|
+
|
|
1083
|
+
&::before {
|
|
1084
|
+
content: none;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
&::after {
|
|
1088
|
+
forced-color-adjust: none;
|
|
1089
|
+
background: CanvasText;
|
|
1090
|
+
mask-image: var(--fp-meter-gaps);
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
&[data-orientation="horizontal"] {
|
|
1095
|
+
--fp-meter-zones: linear-gradient(
|
|
1096
|
+
to right,
|
|
1097
|
+
var(--fp-color-led-on) 0 calc(var(--fp-meter-amber, calc(8 / 12)) * 100%),
|
|
1098
|
+
var(--fp-color-led-amber) 0 calc(var(--fp-meter-red, calc(11 / 12)) * 100%),
|
|
1099
|
+
var(--fp-color-led-red) 0
|
|
1100
|
+
);
|
|
1101
|
+
--fp-meter-gaps: repeating-linear-gradient(
|
|
1102
|
+
to right,
|
|
1103
|
+
#000 0 calc(100% / var(--fp-meter-segments, 12) - 2px),
|
|
1104
|
+
transparent 0 calc(100% / var(--fp-meter-segments, 12))
|
|
1105
|
+
);
|
|
1106
|
+
--fp-meter-lit-gaps: repeating-linear-gradient(
|
|
1107
|
+
to right,
|
|
1108
|
+
#000 0 calc(100% / var(--fp-meter-segments, 12) - 2px),
|
|
1109
|
+
rgb(0 0 0 / calc(0.18 * var(--fp-led-glow, 1))) 0 calc(100% / var(--fp-meter-segments, 12))
|
|
1110
|
+
);
|
|
1111
|
+
--fp-meter-cell: calc(100% / var(--fp-meter-segments, 12)) 100%;
|
|
1112
|
+
width: var(--fp-meter-length, 8rem);
|
|
1113
|
+
height: 0.75rem;
|
|
1114
|
+
|
|
1115
|
+
&::before,
|
|
1116
|
+
&::after {
|
|
1117
|
+
inset: 2px 0 2px 2px;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
&::after {
|
|
1121
|
+
clip-path: inset(0 calc((1 - var(--fp-meter-level, 0)) * 100% + 1px) 0 0);
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
/**
|
|
1127
|
+
* Seven-segment readout: LED digits on a near-black face, set in the housing. Each character is an
|
|
1128
|
+
* fp-readout-cell, and the value is said in an fp-readout-text inside. Green by default; colour it
|
|
1129
|
+
* with fp-led-amber or fp-led-red. Lit digits pass as text on the face at any size.
|
|
1130
|
+
*/
|
|
1131
|
+
.fp-readout {
|
|
1132
|
+
position: relative;
|
|
1133
|
+
display: inline-flex;
|
|
1134
|
+
align-items: center;
|
|
1135
|
+
gap: 0.1875rem;
|
|
1136
|
+
padding: 0.3125rem 0.4375rem;
|
|
1137
|
+
border: 3px solid var(--fp-color-housing);
|
|
1138
|
+
border-radius: 5px;
|
|
1139
|
+
background-color: color-mix(in srgb, var(--fp-color-led-off) 70%, black);
|
|
1140
|
+
box-shadow:
|
|
1141
|
+
inset 1px 1px 3px rgb(0 0 0 / 0.8),
|
|
1142
|
+
0 1px 0 var(--fp-color-bevel-hi);
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
/**
|
|
1146
|
+
* One character of an fp-readout, leaning a little like a real display. It shows its unlit
|
|
1147
|
+
* segments itself, a figure eight and a point, faintly in the lit colour; put the lit ones in an
|
|
1148
|
+
* fp-readout-lit inside. With `data-colon` it is a narrow colon cell. `--fp-readout-height` sets
|
|
1149
|
+
* its height (2rem). Give it aria-hidden="true".
|
|
1150
|
+
*/
|
|
1151
|
+
.fp-readout-cell {
|
|
1152
|
+
position: relative;
|
|
1153
|
+
flex-shrink: 0;
|
|
1154
|
+
height: var(--fp-readout-height, 2rem);
|
|
1155
|
+
aspect-ratio: 15 / 22;
|
|
1156
|
+
transform: skewX(-6deg);
|
|
1157
|
+
|
|
1158
|
+
&::before {
|
|
1159
|
+
content: "";
|
|
1160
|
+
position: absolute;
|
|
1161
|
+
inset: 0;
|
|
1162
|
+
background-color: color-mix(in srgb, var(--fp-color-led-off) 75%, var(--fp-led, var(--fp-color-led-on)));
|
|
1163
|
+
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;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
&[data-colon] {
|
|
1167
|
+
aspect-ratio: 5 / 22;
|
|
1168
|
+
|
|
1169
|
+
&::before {
|
|
1170
|
+
content: none;
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
/* With forced colours, only the lit digits show: a ghost in a system colour blurs into them. */
|
|
1175
|
+
@media (forced-colors: active) {
|
|
1176
|
+
&::before {
|
|
1177
|
+
content: none;
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
/**
|
|
1183
|
+
* The lit segments of an fp-readout-cell: an element for each, named by `data-segment`, `a` to `g`
|
|
1184
|
+
* (top, then clockwise, then the middle), `dp`, or a colon's `colon-upper` and `colon-lower`. Each
|
|
1185
|
+
* is brightest over its chip, in the middle, and they glow onto the face, both by the lens.
|
|
1186
|
+
*/
|
|
1187
|
+
.fp-readout-lit {
|
|
1188
|
+
position: absolute;
|
|
1189
|
+
inset: 0;
|
|
1190
|
+
filter: drop-shadow(
|
|
1191
|
+
0 0 calc(var(--fp-readout-height, 2rem) * 0.06 * var(--fp-led-glow, 1))
|
|
1192
|
+
color-mix(in srgb, var(--fp-led, var(--fp-color-led-on)) 70%, transparent)
|
|
1193
|
+
);
|
|
1194
|
+
|
|
1195
|
+
& > [data-segment] {
|
|
1196
|
+
position: absolute;
|
|
1197
|
+
background-color: var(--fp-led, var(--fp-color-led-on));
|
|
1198
|
+
background-image: radial-gradient(
|
|
1199
|
+
closest-side,
|
|
1200
|
+
color-mix(in srgb, var(--fp-led, var(--fp-color-led-on)) calc((1 - var(--fp-led-core, 0.3)) * 100%), white),
|
|
1201
|
+
var(--fp-led, var(--fp-color-led-on))
|
|
1202
|
+
);
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
/* The bars, on the cell's 15 x 22 grid: 9 long and 2 thick, pointed at both ends. */
|
|
1206
|
+
& > [data-segment="a"],
|
|
1207
|
+
& > [data-segment="d"],
|
|
1208
|
+
& > [data-segment="g"] {
|
|
1209
|
+
left: 10%;
|
|
1210
|
+
width: 60%;
|
|
1211
|
+
height: 9.0909%;
|
|
1212
|
+
clip-path: polygon(0 50%, 11.1111% 0, 88.8889% 0, 100% 50%, 88.8889% 100%, 11.1111% 100%);
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
& > [data-segment="b"],
|
|
1216
|
+
& > [data-segment="c"],
|
|
1217
|
+
& > [data-segment="e"],
|
|
1218
|
+
& > [data-segment="f"] {
|
|
1219
|
+
width: 13.3333%;
|
|
1220
|
+
height: 40.9091%;
|
|
1221
|
+
clip-path: polygon(50% 0, 100% 11.1111%, 100% 88.8889%, 50% 100%, 0 88.8889%, 0 11.1111%);
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
& > [data-segment="a"] {
|
|
1225
|
+
top: 0;
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
& > [data-segment="g"] {
|
|
1229
|
+
top: 45.4545%;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
& > [data-segment="d"] {
|
|
1233
|
+
top: 90.9091%;
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
& > [data-segment="b"],
|
|
1237
|
+
& > [data-segment="c"] {
|
|
1238
|
+
left: 66.6667%;
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
& > [data-segment="e"],
|
|
1242
|
+
& > [data-segment="f"] {
|
|
1243
|
+
left: 0;
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
& > [data-segment="b"],
|
|
1247
|
+
& > [data-segment="f"] {
|
|
1248
|
+
top: 6.8182%;
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
& > [data-segment="c"],
|
|
1252
|
+
& > [data-segment="e"] {
|
|
1253
|
+
top: 52.2727%;
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
& > [data-segment="dp"],
|
|
1257
|
+
& > [data-segment="colon-upper"],
|
|
1258
|
+
& > [data-segment="colon-lower"] {
|
|
1259
|
+
aspect-ratio: 1;
|
|
1260
|
+
border-radius: 50%;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
& > [data-segment="dp"] {
|
|
1264
|
+
top: 90%;
|
|
1265
|
+
left: 82%;
|
|
1266
|
+
width: 16%;
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
& > [data-segment="colon-upper"],
|
|
1270
|
+
& > [data-segment="colon-lower"] {
|
|
1271
|
+
left: 26%;
|
|
1272
|
+
width: 48%;
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
& > [data-segment="colon-upper"] {
|
|
1276
|
+
top: 26.3636%;
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
& > [data-segment="colon-lower"] {
|
|
1280
|
+
top: 62.7273%;
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
@media (forced-colors: active) {
|
|
1284
|
+
filter: none;
|
|
1285
|
+
|
|
1286
|
+
& > [data-segment] {
|
|
1287
|
+
forced-color-adjust: none;
|
|
1288
|
+
background: CanvasText;
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
/** An fp-readout's value as text, for screen readers: hidden from view, read in place of the digits. */
|
|
1294
|
+
.fp-readout-text {
|
|
1295
|
+
position: absolute;
|
|
1296
|
+
width: 1px;
|
|
1297
|
+
height: 1px;
|
|
1298
|
+
overflow: hidden;
|
|
1299
|
+
clip-path: inset(50%);
|
|
1300
|
+
white-space: nowrap;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
356
1303
|
/** Matte content panel. This is where reading happens: no gloss, a 1 px bevel and a short hard shadow. */
|
|
357
1304
|
.fp-module {
|
|
1305
|
+
color-scheme: var(--fp-scheme);
|
|
358
1306
|
border-radius: 10px;
|
|
359
1307
|
background-color: var(--fp-color-shell-matte);
|
|
360
1308
|
box-shadow:
|
|
@@ -362,6 +1310,11 @@
|
|
|
362
1310
|
inset 1px 1px 0 var(--fp-color-bevel-hi),
|
|
363
1311
|
inset -1px -1px 0 var(--fp-color-bevel-lo),
|
|
364
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
|
+
}
|
|
365
1318
|
}
|
|
366
1319
|
|
|
367
1320
|
/** A module's own little title bar, in brushed chrome. */
|
|
@@ -418,11 +1371,17 @@
|
|
|
418
1371
|
|
|
419
1372
|
/** Recessed well inside a matte panel, for secondary content or an empty slot. */
|
|
420
1373
|
.fp-well {
|
|
1374
|
+
color-scheme: var(--fp-scheme);
|
|
421
1375
|
border-radius: 6px;
|
|
422
1376
|
background-color: var(--fp-color-well);
|
|
423
1377
|
box-shadow:
|
|
424
1378
|
inset 1px 1px 2px var(--fp-color-bevel-lo),
|
|
425
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
|
+
}
|
|
426
1385
|
}
|
|
427
1386
|
|
|
428
1387
|
/** Silkscreened hardware label. */
|
|
@@ -445,6 +1404,11 @@
|
|
|
445
1404
|
0 0 0 2px var(--fp-color-chrome-800),
|
|
446
1405
|
inset 2px 2px 4px rgb(0 0 0 / 0.35),
|
|
447
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
|
+
}
|
|
448
1412
|
}
|
|
449
1413
|
|
|
450
1414
|
/** One line of LCD text. Cells of fp-lcd-ghost stacked behind it show the unlit segments, as on real glass. */
|
|
@@ -462,6 +1426,11 @@
|
|
|
462
1426
|
/** The unlit segments behind an LCD line. Decorative: give it aria-hidden="true". */
|
|
463
1427
|
.fp-lcd-ghost {
|
|
464
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
|
+
}
|
|
465
1434
|
}
|
|
466
1435
|
|
|
467
1436
|
/** Dark housing that LEDs and small readouts sit in. */
|
|
@@ -478,6 +1447,11 @@
|
|
|
478
1447
|
box-shadow:
|
|
479
1448
|
inset 1px 1px 2px rgb(0 0 0 / 0.5),
|
|
480
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
|
+
}
|
|
481
1455
|
}
|
|
482
1456
|
|
|
483
1457
|
/** An LED with its own dark bezel ring, so a lit one reads on light chrome too. Unlit by default. */
|
|
@@ -491,30 +1465,76 @@
|
|
|
491
1465
|
box-shadow:
|
|
492
1466
|
0 0 0 2px var(--fp-color-housing),
|
|
493
1467
|
inset 0 -1px 1px rgb(0 0 0 / 0.45);
|
|
1468
|
+
|
|
1469
|
+
/* Forced colours drop the fill and the ring: an outline keeps an unlit LED in view. */
|
|
1470
|
+
@media (forced-colors: active) {
|
|
1471
|
+
outline: 1px solid CanvasText;
|
|
1472
|
+
outline-offset: 1px;
|
|
1473
|
+
}
|
|
494
1474
|
}
|
|
495
1475
|
|
|
496
|
-
/** Green for an fp-led, the default. */
|
|
1476
|
+
/** Green for an fp-led or fp-readout, the default. */
|
|
497
1477
|
.fp-led-green {
|
|
498
1478
|
--fp-led: var(--fp-color-led-on);
|
|
499
1479
|
}
|
|
500
1480
|
|
|
501
|
-
/** Amber for an fp-led. */
|
|
1481
|
+
/** Amber for an fp-led or fp-readout. */
|
|
502
1482
|
.fp-led-amber {
|
|
503
1483
|
--fp-led: var(--fp-color-led-amber);
|
|
504
1484
|
}
|
|
505
1485
|
|
|
506
|
-
/** Red for an fp-led. */
|
|
1486
|
+
/** Red for an fp-led or fp-readout. */
|
|
507
1487
|
.fp-led-red {
|
|
508
1488
|
--fp-led: var(--fp-color-led-red);
|
|
509
1489
|
}
|
|
510
1490
|
|
|
511
|
-
/**
|
|
1491
|
+
/**
|
|
1492
|
+
* Lights an fp-led in its colour, brightest over the chip in its middle, with a glow. The lens sets
|
|
1493
|
+
* how strong both are. Say what it means in text nearby, never by colour alone.
|
|
1494
|
+
*/
|
|
512
1495
|
.fp-led-lit {
|
|
513
1496
|
background-color: var(--fp-led, var(--fp-color-led-on));
|
|
1497
|
+
background-image: radial-gradient(
|
|
1498
|
+
closest-side,
|
|
1499
|
+
color-mix(in srgb, var(--fp-led, var(--fp-color-led-on)) calc((1 - var(--fp-led-core, 0.3)) * 100%), white),
|
|
1500
|
+
var(--fp-led, var(--fp-color-led-on))
|
|
1501
|
+
);
|
|
514
1502
|
box-shadow:
|
|
515
1503
|
0 0 0 2px var(--fp-color-housing),
|
|
516
|
-
0 0 7px
|
|
1504
|
+
0 0 calc(7px * var(--fp-led-glow, 1)) calc(2px * var(--fp-led-glow, 1))
|
|
1505
|
+
color-mix(in srgb, var(--fp-led, var(--fp-color-led-on)) 65%, transparent),
|
|
517
1506
|
inset 0 -1px 1px rgb(0 0 0 / 0.2);
|
|
1507
|
+
|
|
1508
|
+
/* Lit is filled, so it reads apart from unlit by shape as well as colour. */
|
|
1509
|
+
@media (forced-colors: active) {
|
|
1510
|
+
forced-color-adjust: none;
|
|
1511
|
+
background: CanvasText;
|
|
1512
|
+
box-shadow: none;
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
/**
|
|
1517
|
+
* LED lens, flat: an even fill with no hot spot. A lens class works on an fp-led, fp-meter or
|
|
1518
|
+
* fp-readout, or on any element around them, such as a device body, to set every one inside.
|
|
1519
|
+
*/
|
|
1520
|
+
.fp-lens-flat {
|
|
1521
|
+
--fp-led-core: 0;
|
|
1522
|
+
--fp-led-glow: 1;
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
/**
|
|
1526
|
+
* LED lens, diffused, the default: like most real displays, each LED is a little brighter over its
|
|
1527
|
+
* chip, in the middle, with a soft glow.
|
|
1528
|
+
*/
|
|
1529
|
+
.fp-lens-diffused {
|
|
1530
|
+
--fp-led-core: 0.3;
|
|
1531
|
+
--fp-led-glow: 1;
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
/** LED lens, clear: a bright, pale core over each chip and a wider glow, like a water-clear lens. */
|
|
1535
|
+
.fp-lens-clear {
|
|
1536
|
+
--fp-led-core: 0.5;
|
|
1537
|
+
--fp-led-glow: 1.5;
|
|
518
1538
|
}
|
|
519
1539
|
|
|
520
1540
|
/** Decorative screw head. Give it aria-hidden="true". */
|
|
@@ -532,6 +1552,7 @@
|
|
|
532
1552
|
|
|
533
1553
|
/** A scrolling panel, such as a list window, with a thin scrollbar that never pushes the layout. */
|
|
534
1554
|
.fp-scroll-thin {
|
|
1555
|
+
color-scheme: var(--fp-scheme);
|
|
535
1556
|
scrollbar-width: thin;
|
|
536
1557
|
scrollbar-color: var(--fp-color-scroll-thumb) transparent;
|
|
537
1558
|
}
|