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