@frontpanel/css 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +100 -2
- package/base.css +41 -0
- package/components.css +1100 -0
- package/fonts/OFL-michroma.txt +93 -0
- package/fonts/OFL-vt323.txt +93 -0
- package/fonts/michroma-latin-400.woff2 +0 -0
- package/fonts/vt323-latin-400.woff2 +0 -0
- package/fonts.css +24 -0
- package/index.css +3 -0
- package/package.json +59 -3
- package/page.css +103 -0
- package/tailwind-base.css +41 -0
- package/tailwind-page.css +103 -0
- package/tailwind.css +1158 -0
- package/tokens.css +135 -0
package/components.css
ADDED
|
@@ -0,0 +1,1100 @@
|
|
|
1
|
+
/* Generated by scripts/build.ts from src/. Do not edit; edit the source and run `pnpm build`. */
|
|
2
|
+
@layer frontpanel {
|
|
3
|
+
/* An LCD line redrawing left to right; the fp-lcd-redraw class steps through it. */
|
|
4
|
+
@keyframes fp-lcd-redraw {
|
|
5
|
+
from {
|
|
6
|
+
clip-path: inset(0 100% 0 0);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
to {
|
|
10
|
+
clip-path: inset(0 0 0 0);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/* Two blinks 400 ms apart, then steady. Nothing on the device blinks for longer. */
|
|
15
|
+
@keyframes fp-blink-twice {
|
|
16
|
+
0%,
|
|
17
|
+
100% {
|
|
18
|
+
opacity: 1;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
12.5%,
|
|
22
|
+
37.5% {
|
|
23
|
+
opacity: 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
25%,
|
|
27
|
+
50% {
|
|
28
|
+
opacity: 1;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Redraws an LCD line left to right in four steps, as when its value changes. */
|
|
33
|
+
.fp-lcd-redraw {
|
|
34
|
+
animation: fp-lcd-redraw 160ms steps(4, end);
|
|
35
|
+
|
|
36
|
+
@media (prefers-reduced-motion: reduce) {
|
|
37
|
+
animation: none;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Blinks twice, then holds: an LED confirming an action. */
|
|
42
|
+
.fp-blink-twice {
|
|
43
|
+
animation: fp-blink-twice 1.6s steps(1, end) 1;
|
|
44
|
+
|
|
45
|
+
@media (prefers-reduced-motion: reduce) {
|
|
46
|
+
animation: none;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** The page behind the device: quiet blue-grey with a faint Y2K grid. */
|
|
51
|
+
.fp-device-backdrop {
|
|
52
|
+
color-scheme: var(--fp-scheme);
|
|
53
|
+
background-color: var(--fp-color-backdrop);
|
|
54
|
+
background-image:
|
|
55
|
+
linear-gradient(var(--fp-color-grid) 1px, transparent 1px),
|
|
56
|
+
linear-gradient(90deg, var(--fp-color-grid) 1px, transparent 1px),
|
|
57
|
+
radial-gradient(ellipse 80% 60% at 20% 0%, var(--fp-color-backdrop-hi), transparent 70%),
|
|
58
|
+
linear-gradient(180deg, var(--fp-color-backdrop-hi), var(--fp-color-backdrop));
|
|
59
|
+
background-size:
|
|
60
|
+
32px 32px,
|
|
61
|
+
32px 32px,
|
|
62
|
+
auto,
|
|
63
|
+
auto;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Liquid-chrome ring around the device body. Diagonal so the reflection band crosses the corners. */
|
|
67
|
+
.fp-device-rim {
|
|
68
|
+
color-scheme: var(--fp-scheme);
|
|
69
|
+
border-radius: 36px;
|
|
70
|
+
padding: 10px;
|
|
71
|
+
background-image: linear-gradient(
|
|
72
|
+
160deg,
|
|
73
|
+
var(--fp-color-chrome-hi) 0%,
|
|
74
|
+
var(--fp-color-chrome-100) 14%,
|
|
75
|
+
var(--fp-color-chrome-tint) 38%,
|
|
76
|
+
var(--fp-color-chrome-500) 50%,
|
|
77
|
+
var(--fp-color-chrome-300) 70%,
|
|
78
|
+
var(--fp-color-chrome-100) 100%
|
|
79
|
+
);
|
|
80
|
+
box-shadow:
|
|
81
|
+
0 0 0 1px var(--fp-color-chrome-800),
|
|
82
|
+
inset 0 1px 0 var(--fp-color-chrome-hi),
|
|
83
|
+
0 4px 0 var(--fp-color-cast),
|
|
84
|
+
0 14px 18px -10px var(--fp-color-cast);
|
|
85
|
+
|
|
86
|
+
/*
|
|
87
|
+
* A body set in the rim follows its curve (the rim's radius less its padding) and clips to it.
|
|
88
|
+
* No specificity, so a rim sized another way can give its body a matching radius with a class.
|
|
89
|
+
*/
|
|
90
|
+
:where(& > .fp-device-body) {
|
|
91
|
+
overflow: clip;
|
|
92
|
+
border-radius: 26px;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Molded body plastic the modules sit on. */
|
|
97
|
+
.fp-device-body {
|
|
98
|
+
color-scheme: var(--fp-scheme);
|
|
99
|
+
background-image: linear-gradient(180deg, var(--fp-color-shell-hi), var(--fp-color-shell) 240px);
|
|
100
|
+
box-shadow:
|
|
101
|
+
inset 1px 1px 0 var(--fp-color-bevel-hi),
|
|
102
|
+
inset -1px -1px 0 var(--fp-color-bevel-lo);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Liquid chrome with a hard horizon, for title bars and other frames. Never behind text. */
|
|
106
|
+
.fp-chrome {
|
|
107
|
+
background-image: linear-gradient(
|
|
108
|
+
180deg,
|
|
109
|
+
var(--fp-color-chrome-hi) 0%,
|
|
110
|
+
var(--fp-color-chrome-100) 18%,
|
|
111
|
+
var(--fp-color-chrome-tint) 45%,
|
|
112
|
+
var(--fp-color-chrome-500) 52%,
|
|
113
|
+
var(--fp-color-chrome-300) 75%,
|
|
114
|
+
var(--fp-color-chrome-100) 100%
|
|
115
|
+
);
|
|
116
|
+
box-shadow:
|
|
117
|
+
inset 0 1px 0 var(--fp-color-chrome-hi),
|
|
118
|
+
inset 0 -1px 0 var(--fp-color-edge);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Brushed chrome without the horizon, calm enough to print a label in the middle of it. */
|
|
122
|
+
.fp-chrome-soft {
|
|
123
|
+
background-image: linear-gradient(
|
|
124
|
+
180deg,
|
|
125
|
+
color-mix(in srgb, var(--fp-color-chrome-hi) 60%, var(--fp-color-chrome-100)) 0%,
|
|
126
|
+
var(--fp-color-chrome-100) 40%,
|
|
127
|
+
var(--fp-color-chrome-300) 100%
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Small hardware push button in brushed chrome. 44 px tall, 36 px with a mouse, trackpad or pen. */
|
|
132
|
+
.fp-chrome-btn {
|
|
133
|
+
display: inline-flex;
|
|
134
|
+
align-items: center;
|
|
135
|
+
justify-content: center;
|
|
136
|
+
gap: 0.375rem;
|
|
137
|
+
min-height: 2.75rem;
|
|
138
|
+
min-width: 2.75rem;
|
|
139
|
+
padding-inline: 0.625rem;
|
|
140
|
+
border-radius: 6px;
|
|
141
|
+
color: var(--fp-color-ink);
|
|
142
|
+
font-size: 13px;
|
|
143
|
+
font-weight: 700;
|
|
144
|
+
background-image: linear-gradient(
|
|
145
|
+
180deg,
|
|
146
|
+
color-mix(in srgb, var(--fp-color-chrome-hi) 60%, var(--fp-color-chrome-100)) 0%,
|
|
147
|
+
var(--fp-color-chrome-100) 40%,
|
|
148
|
+
var(--fp-color-chrome-300) 100%
|
|
149
|
+
);
|
|
150
|
+
box-shadow:
|
|
151
|
+
0 0 0 1px var(--fp-color-edge),
|
|
152
|
+
inset 1px 1px 0 var(--fp-color-bevel-hi),
|
|
153
|
+
inset -1px -1px 0 var(--fp-color-bevel-lo),
|
|
154
|
+
1px 2px 0 var(--fp-color-cast);
|
|
155
|
+
transition-property: translate, box-shadow, filter;
|
|
156
|
+
transition-duration: 70ms;
|
|
157
|
+
transition-timing-function: linear;
|
|
158
|
+
|
|
159
|
+
@media (prefers-reduced-motion: reduce) {
|
|
160
|
+
transition-duration: 0ms;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
@media (pointer: fine) {
|
|
164
|
+
min-height: 2.25rem;
|
|
165
|
+
min-width: 2.25rem;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
&:hover:not(:disabled) {
|
|
169
|
+
filter: brightness(1.05);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
&:active:not(:disabled),
|
|
173
|
+
&[aria-pressed="true"],
|
|
174
|
+
&[aria-expanded="true"],
|
|
175
|
+
&[aria-current="page"] {
|
|
176
|
+
translate: 0 1px;
|
|
177
|
+
box-shadow:
|
|
178
|
+
0 0 0 1px var(--fp-color-edge),
|
|
179
|
+
inset 1px 1px 3px var(--fp-color-bevel-lo),
|
|
180
|
+
inset -1px -1px 0 var(--fp-color-bevel-hi);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/* A solid ring with a glow outside it: the glow alone fails non-text contrast on light chrome. */
|
|
184
|
+
&:focus-visible {
|
|
185
|
+
outline: 2px solid var(--fp-color-focus-ring);
|
|
186
|
+
outline-offset: 2px;
|
|
187
|
+
filter: drop-shadow(0 0 3px var(--fp-color-focus));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/* The hover filter outranks the focus rule, so a hovered control keeps its glow here. */
|
|
191
|
+
&:focus-visible:hover:not(:disabled) {
|
|
192
|
+
filter: brightness(1.05) drop-shadow(0 0 3px var(--fp-color-focus));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
&:disabled {
|
|
196
|
+
opacity: 0.45;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Glossy gel pill for primary actions. Its white label sits on the base colour at mid-height,
|
|
202
|
+
* where the highlight ellipse has already faded out (7:1).
|
|
203
|
+
*/
|
|
204
|
+
.fp-gel {
|
|
205
|
+
display: inline-flex;
|
|
206
|
+
align-items: center;
|
|
207
|
+
justify-content: center;
|
|
208
|
+
gap: 0.5rem;
|
|
209
|
+
min-height: 2.75rem;
|
|
210
|
+
padding-inline: 1rem;
|
|
211
|
+
border-radius: 9999px;
|
|
212
|
+
color: var(--fp-color-gel-ink);
|
|
213
|
+
font-size: 14px;
|
|
214
|
+
font-weight: 700;
|
|
215
|
+
text-shadow: 0 -1px 0 rgb(0 0 0 / 0.35);
|
|
216
|
+
background-image:
|
|
217
|
+
radial-gradient(ellipse 80% 45% at 50% 0%, rgb(255 255 255 / 0.7), rgb(255 255 255 / 0.08) 90%, transparent 100%),
|
|
218
|
+
linear-gradient(180deg, var(--fp-color-gel-hi) 0%, var(--fp-color-gel) 48%, var(--fp-color-gel-lo) 100%);
|
|
219
|
+
box-shadow:
|
|
220
|
+
0 0 0 1px var(--fp-color-gel-lo),
|
|
221
|
+
inset 0 -3px 5px rgb(140 200 255 / 0.4),
|
|
222
|
+
1px 2px 0 var(--fp-color-cast);
|
|
223
|
+
transition-property: translate, box-shadow, filter;
|
|
224
|
+
transition-duration: 70ms;
|
|
225
|
+
transition-timing-function: linear;
|
|
226
|
+
|
|
227
|
+
@media (prefers-reduced-motion: reduce) {
|
|
228
|
+
transition-duration: 0ms;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
@media (pointer: fine) {
|
|
232
|
+
min-height: 2.5rem;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
&:hover:not(:disabled) {
|
|
236
|
+
filter: brightness(1.08);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
&:active:not(:disabled) {
|
|
240
|
+
translate: 0 1px;
|
|
241
|
+
box-shadow:
|
|
242
|
+
0 0 0 1px var(--fp-color-gel-lo),
|
|
243
|
+
inset 0 2px 4px rgb(0 0 0 / 0.35);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/* A solid ring with a glow outside it: the glow alone fails non-text contrast on light chrome. */
|
|
247
|
+
&:focus-visible {
|
|
248
|
+
outline: 2px solid var(--fp-color-focus-ring);
|
|
249
|
+
outline-offset: 2px;
|
|
250
|
+
filter: drop-shadow(0 0 3px var(--fp-color-focus));
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/* The hover filter outranks the focus rule, so a hovered control keeps its glow here. */
|
|
254
|
+
&:focus-visible:hover:not(:disabled) {
|
|
255
|
+
filter: brightness(1.08) drop-shadow(0 0 3px var(--fp-color-focus));
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
&:disabled {
|
|
259
|
+
opacity: 0.45;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Molded-plastic cap. The cap colour and its label colour come from the fp-cap-* classes
|
|
265
|
+
* (`--fp-cap`, `--fp-cap-ink`); a bare `fp-cap` is white plastic with a dark label. Selected caps
|
|
266
|
+
* stay pressed in and get a ring, so selection never depends on colour.
|
|
267
|
+
*/
|
|
268
|
+
.fp-cap {
|
|
269
|
+
position: relative;
|
|
270
|
+
display: inline-flex;
|
|
271
|
+
align-items: center;
|
|
272
|
+
justify-content: center;
|
|
273
|
+
min-height: 2.75rem;
|
|
274
|
+
padding-inline: 0.5rem;
|
|
275
|
+
border-radius: 6px;
|
|
276
|
+
color: var(--fp-cap-ink, var(--fp-color-cap-ink));
|
|
277
|
+
font-weight: 700;
|
|
278
|
+
background-color: var(--fp-cap, var(--fp-color-cap));
|
|
279
|
+
background-image: radial-gradient(ellipse 110% 80% at 30% 10%, rgb(255 255 255 / 0.28), transparent 65%);
|
|
280
|
+
box-shadow:
|
|
281
|
+
0 0 0 1px var(--fp-color-edge),
|
|
282
|
+
inset 0 2px 0 rgb(255 255 255 / 0.45),
|
|
283
|
+
inset 0 -2px 0 rgb(0 0 0 / 0.25),
|
|
284
|
+
1px 2px 1px var(--fp-color-cast);
|
|
285
|
+
transition-property: translate, box-shadow, filter;
|
|
286
|
+
transition-duration: 70ms;
|
|
287
|
+
transition-timing-function: linear;
|
|
288
|
+
|
|
289
|
+
@media (prefers-reduced-motion: reduce) {
|
|
290
|
+
transition-duration: 0ms;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
@media (pointer: fine) {
|
|
294
|
+
min-height: 2.25rem;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
&:hover:not(:disabled) {
|
|
298
|
+
filter: brightness(1.05);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
&:active:not(:disabled) {
|
|
302
|
+
translate: 0 1px;
|
|
303
|
+
box-shadow:
|
|
304
|
+
0 0 0 1px var(--fp-color-edge),
|
|
305
|
+
inset 0 2px 3px rgb(0 0 0 / 0.35),
|
|
306
|
+
inset 0 -1px 0 rgb(255 255 255 / 0.3);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
&[aria-pressed="true"],
|
|
310
|
+
&[aria-checked="true"] {
|
|
311
|
+
translate: 0 1px;
|
|
312
|
+
box-shadow:
|
|
313
|
+
0 0 0 2px var(--fp-color-select),
|
|
314
|
+
inset 0 2px 3px rgb(0 0 0 / 0.35),
|
|
315
|
+
inset 0 -1px 0 rgb(255 255 255 / 0.3);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/* A solid ring with a glow outside it: the glow alone fails non-text contrast on light chrome. */
|
|
319
|
+
&:focus-visible {
|
|
320
|
+
outline: 2px solid var(--fp-color-focus-ring);
|
|
321
|
+
outline-offset: 2px;
|
|
322
|
+
filter: drop-shadow(0 0 3px var(--fp-color-focus));
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/* The hover filter outranks the focus rule, so a hovered control keeps its glow here. */
|
|
326
|
+
&:focus-visible:hover:not(:disabled) {
|
|
327
|
+
filter: brightness(1.05) drop-shadow(0 0 3px var(--fp-color-focus));
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
&:disabled {
|
|
331
|
+
opacity: 0.5;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/** Blue plastic for an fp-cap, with its own label colour. */
|
|
336
|
+
.fp-cap-blue {
|
|
337
|
+
--fp-cap: var(--fp-color-cap-blue);
|
|
338
|
+
--fp-cap-ink: var(--fp-color-cap-blue-ink);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/** Amber plastic for an fp-cap, with its own label colour. */
|
|
342
|
+
.fp-cap-amber {
|
|
343
|
+
--fp-cap: var(--fp-color-cap-amber);
|
|
344
|
+
--fp-cap-ink: var(--fp-color-cap-amber-ink);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/** Graphite plastic for an fp-cap, with its own label colour. */
|
|
348
|
+
.fp-cap-graphite {
|
|
349
|
+
--fp-cap: var(--fp-color-cap-graphite);
|
|
350
|
+
--fp-cap-ink: var(--fp-color-cap-graphite-ink);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/** Berry plastic for an fp-cap, with its own label colour. */
|
|
354
|
+
.fp-cap-berry {
|
|
355
|
+
--fp-cap: var(--fp-color-cap-berry);
|
|
356
|
+
--fp-cap-ink: var(--fp-color-cap-berry-ink);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* A control with its silkscreen label under it: a knob, fader or meter and an fp-silk. A length
|
|
361
|
+
* set here, such as `--fp-fader-length`, reaches the control inside.
|
|
362
|
+
*/
|
|
363
|
+
.fp-unit {
|
|
364
|
+
display: inline-flex;
|
|
365
|
+
flex-direction: column;
|
|
366
|
+
align-items: center;
|
|
367
|
+
gap: 0.375rem;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Rotary knob: a knurled chrome skirt round a domed cap. The cap is lit from the top left and stays
|
|
372
|
+
* still; only its fp-knob-mark turns, by `rotate`, through 270 degrees. 48 px, 40 px with a mouse,
|
|
373
|
+
* trackpad or pen.
|
|
374
|
+
*/
|
|
375
|
+
.fp-knob {
|
|
376
|
+
position: relative;
|
|
377
|
+
display: inline-block;
|
|
378
|
+
flex-shrink: 0;
|
|
379
|
+
width: 3rem;
|
|
380
|
+
height: 3rem;
|
|
381
|
+
border-radius: 50%;
|
|
382
|
+
background-image: repeating-conic-gradient(var(--fp-color-chrome-100) 0deg 5deg, var(--fp-color-chrome-300) 5deg 10deg);
|
|
383
|
+
box-shadow:
|
|
384
|
+
0 0 0 1px var(--fp-color-edge),
|
|
385
|
+
inset 1px 1px 0 var(--fp-color-bevel-hi),
|
|
386
|
+
inset -1px -1px 0 var(--fp-color-bevel-lo),
|
|
387
|
+
1px 2px 0 var(--fp-color-cast);
|
|
388
|
+
cursor: ns-resize;
|
|
389
|
+
touch-action: none;
|
|
390
|
+
user-select: none;
|
|
391
|
+
|
|
392
|
+
@media (pointer: fine) {
|
|
393
|
+
width: 2.5rem;
|
|
394
|
+
height: 2.5rem;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
&::before {
|
|
398
|
+
content: "";
|
|
399
|
+
position: absolute;
|
|
400
|
+
inset: 16%;
|
|
401
|
+
border-radius: 50%;
|
|
402
|
+
background-image: radial-gradient(
|
|
403
|
+
circle at 30% 25%,
|
|
404
|
+
color-mix(in srgb, var(--fp-color-chrome-hi) 60%, var(--fp-color-chrome-100)) 0%,
|
|
405
|
+
var(--fp-color-chrome-100) 45%,
|
|
406
|
+
var(--fp-color-chrome-300) 100%
|
|
407
|
+
);
|
|
408
|
+
box-shadow:
|
|
409
|
+
0 0 0 1px var(--fp-color-edge),
|
|
410
|
+
inset 1px 1px 0 var(--fp-color-bevel-hi),
|
|
411
|
+
inset -1px -1px 0 var(--fp-color-bevel-lo),
|
|
412
|
+
1px 1px 2px var(--fp-color-cast);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/* A solid ring with a glow outside it: the glow alone fails non-text contrast on light chrome. */
|
|
416
|
+
&:focus-visible {
|
|
417
|
+
outline: 2px solid var(--fp-color-focus-ring);
|
|
418
|
+
outline-offset: 2px;
|
|
419
|
+
filter: drop-shadow(0 0 3px var(--fp-color-focus));
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
&[aria-disabled="true"] {
|
|
423
|
+
opacity: 0.45;
|
|
424
|
+
cursor: not-allowed;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/** The pointer line on a knob's cap. Turn it with `rotate`: -135deg at the minimum, 135deg at the maximum. */
|
|
429
|
+
.fp-knob-mark {
|
|
430
|
+
position: absolute;
|
|
431
|
+
inset: 0;
|
|
432
|
+
pointer-events: none;
|
|
433
|
+
|
|
434
|
+
&::after {
|
|
435
|
+
content: "";
|
|
436
|
+
position: absolute;
|
|
437
|
+
top: 21%;
|
|
438
|
+
left: 50%;
|
|
439
|
+
width: 3px;
|
|
440
|
+
height: 24%;
|
|
441
|
+
translate: -50% 0;
|
|
442
|
+
border-radius: 2px;
|
|
443
|
+
background-color: var(--fp-color-ink);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Fader: a chrome cap on a recessed slot. It stands up by default and lies on its side with
|
|
449
|
+
* `data-orientation="horizontal"`; `--fp-fader-length` sets how far it travels (8rem). 44 px
|
|
450
|
+
* across, 36 px with a mouse, trackpad or pen. Pressing the slot moves the cap there.
|
|
451
|
+
*/
|
|
452
|
+
.fp-fader {
|
|
453
|
+
position: relative;
|
|
454
|
+
display: inline-block;
|
|
455
|
+
flex-shrink: 0;
|
|
456
|
+
width: 2.75rem;
|
|
457
|
+
height: var(--fp-fader-length, 8rem);
|
|
458
|
+
cursor: ns-resize;
|
|
459
|
+
touch-action: none;
|
|
460
|
+
user-select: none;
|
|
461
|
+
|
|
462
|
+
@media (pointer: fine) {
|
|
463
|
+
width: 2.25rem;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
&[data-orientation="horizontal"] {
|
|
467
|
+
width: var(--fp-fader-length, 8rem);
|
|
468
|
+
height: 2.75rem;
|
|
469
|
+
cursor: ew-resize;
|
|
470
|
+
|
|
471
|
+
@media (pointer: fine) {
|
|
472
|
+
height: 2.25rem;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
&[data-disabled] {
|
|
477
|
+
opacity: 0.45;
|
|
478
|
+
cursor: not-allowed;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/** The slot a fader's cap rides in, cut into the panel: shaded on its top and left inside edges. */
|
|
483
|
+
.fp-fader-slot {
|
|
484
|
+
position: absolute;
|
|
485
|
+
inset: 0.5rem auto;
|
|
486
|
+
left: 50%;
|
|
487
|
+
width: 6px;
|
|
488
|
+
translate: -50% 0;
|
|
489
|
+
border-radius: 3px;
|
|
490
|
+
background-color: var(--fp-color-housing);
|
|
491
|
+
box-shadow:
|
|
492
|
+
inset 1px 1px 2px rgb(0 0 0 / 0.5),
|
|
493
|
+
1px 1px 0 var(--fp-color-bevel-hi);
|
|
494
|
+
|
|
495
|
+
&[data-orientation="horizontal"] {
|
|
496
|
+
inset: 50% 0.5rem auto;
|
|
497
|
+
width: auto;
|
|
498
|
+
height: 6px;
|
|
499
|
+
translate: 0 -50%;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* A fader's cap: brushed chrome lit from the top left, with an ink grip line that marks the value.
|
|
505
|
+
* Place it along the slot with `bottom` (or `left` on its side, with `data-orientation="horizontal"`
|
|
506
|
+
* on the slot and cap as well as the fader).
|
|
507
|
+
*/
|
|
508
|
+
.fp-fader-cap {
|
|
509
|
+
position: absolute;
|
|
510
|
+
inset-inline: 0;
|
|
511
|
+
height: 1.5rem;
|
|
512
|
+
border-radius: 4px;
|
|
513
|
+
background-image: linear-gradient(
|
|
514
|
+
180deg,
|
|
515
|
+
color-mix(in srgb, var(--fp-color-chrome-hi) 60%, var(--fp-color-chrome-100)) 0%,
|
|
516
|
+
var(--fp-color-chrome-100) 45%,
|
|
517
|
+
var(--fp-color-chrome-300) 100%
|
|
518
|
+
);
|
|
519
|
+
box-shadow:
|
|
520
|
+
0 0 0 1px var(--fp-color-edge),
|
|
521
|
+
inset 1px 1px 0 var(--fp-color-bevel-hi),
|
|
522
|
+
inset -1px -1px 0 var(--fp-color-bevel-lo),
|
|
523
|
+
1px 2px 0 var(--fp-color-cast);
|
|
524
|
+
|
|
525
|
+
@media (pointer: fine) {
|
|
526
|
+
height: 1.25rem;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
&::after {
|
|
530
|
+
content: "";
|
|
531
|
+
position: absolute;
|
|
532
|
+
inset: 50% 20% auto;
|
|
533
|
+
height: 2px;
|
|
534
|
+
translate: 0 -50%;
|
|
535
|
+
border-radius: 1px;
|
|
536
|
+
background-color: var(--fp-color-ink);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
&[data-orientation="horizontal"] {
|
|
540
|
+
inset-inline: auto;
|
|
541
|
+
inset-block: 0;
|
|
542
|
+
width: 1.5rem;
|
|
543
|
+
height: auto;
|
|
544
|
+
|
|
545
|
+
@media (pointer: fine) {
|
|
546
|
+
width: 1.25rem;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
&::after {
|
|
550
|
+
inset: 20% auto 20% 50%;
|
|
551
|
+
width: 2px;
|
|
552
|
+
height: auto;
|
|
553
|
+
translate: -50% 0;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/* A solid ring with a glow outside it: the glow alone fails non-text contrast on light chrome. */
|
|
558
|
+
&:focus-visible {
|
|
559
|
+
outline: 2px solid var(--fp-color-focus-ring);
|
|
560
|
+
outline-offset: 2px;
|
|
561
|
+
filter: drop-shadow(0 0 3px var(--fp-color-focus));
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Level meter: a strip of LED segments in a dark housing, lit green, then amber, then red. Set
|
|
567
|
+
* `--fp-meter-level` (0 to 1, in whole segments), `--fp-meter-segments` (12), and where amber and
|
|
568
|
+
* red begin, `--fp-meter-amber` (8/12) and `--fp-meter-red` (11/12), as shares of the strip on
|
|
569
|
+
* segment edges, or a segment lights in two colours. It stands
|
|
570
|
+
* up by default and lies on its side with `data-orientation="horizontal"`; `--fp-meter-length`
|
|
571
|
+
* sets how long it is (8rem). Give it `role="meter"` and its values, or `aria-hidden` beside a
|
|
572
|
+
* readout that says the same.
|
|
573
|
+
*/
|
|
574
|
+
.fp-meter {
|
|
575
|
+
--fp-meter-zones: linear-gradient(
|
|
576
|
+
to top,
|
|
577
|
+
var(--fp-color-led-on) 0 calc(var(--fp-meter-amber, calc(8 / 12)) * 100%),
|
|
578
|
+
var(--fp-color-led-amber) 0 calc(var(--fp-meter-red, calc(11 / 12)) * 100%),
|
|
579
|
+
var(--fp-color-led-red) 0
|
|
580
|
+
);
|
|
581
|
+
--fp-meter-gaps: repeating-linear-gradient(
|
|
582
|
+
to top,
|
|
583
|
+
#000 0 calc(100% / var(--fp-meter-segments, 12) - 2px),
|
|
584
|
+
transparent 0 calc(100% / var(--fp-meter-segments, 12))
|
|
585
|
+
);
|
|
586
|
+
|
|
587
|
+
/* Lit segments spill a little light into the gaps beside them: the lens's glow. */
|
|
588
|
+
--fp-meter-lit-gaps: repeating-linear-gradient(
|
|
589
|
+
to top,
|
|
590
|
+
#000 0 calc(100% / var(--fp-meter-segments, 12) - 2px),
|
|
591
|
+
rgb(0 0 0 / calc(0.18 * var(--fp-led-glow, 1))) 0 calc(100% / var(--fp-meter-segments, 12))
|
|
592
|
+
);
|
|
593
|
+
--fp-meter-cell: 100% calc(100% / var(--fp-meter-segments, 12));
|
|
594
|
+
position: relative;
|
|
595
|
+
display: inline-block;
|
|
596
|
+
flex-shrink: 0;
|
|
597
|
+
width: 0.75rem;
|
|
598
|
+
height: var(--fp-meter-length, 8rem);
|
|
599
|
+
border-radius: 3px;
|
|
600
|
+
background-color: var(--fp-color-housing);
|
|
601
|
+
box-shadow:
|
|
602
|
+
inset 1px 1px 2px rgb(0 0 0 / 0.5),
|
|
603
|
+
1px 1px 0 var(--fp-color-bevel-hi);
|
|
604
|
+
|
|
605
|
+
/* Each segment's gap sits at its far end, so the strip's own inset at that end is the gap. */
|
|
606
|
+
&::before,
|
|
607
|
+
&::after {
|
|
608
|
+
content: "";
|
|
609
|
+
position: absolute;
|
|
610
|
+
inset: 0 2px 2px;
|
|
611
|
+
background-image: var(--fp-meter-zones);
|
|
612
|
+
mask-image: var(--fp-meter-gaps);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/* Unlit segments are the LED's off colour with a trace of their zone's, so the zones show. */
|
|
616
|
+
&::before {
|
|
617
|
+
background-image:
|
|
618
|
+
linear-gradient(color-mix(in srgb, var(--fp-color-led-off) 75%, transparent) 0 0),
|
|
619
|
+
var(--fp-meter-zones);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/*
|
|
623
|
+
* A lit segment is brightest over its chip, in the middle, by the lens. The cut sits 1 px into the
|
|
624
|
+
* gap below the next segment, so rounding never lights a sliver of it.
|
|
625
|
+
*/
|
|
626
|
+
&::after {
|
|
627
|
+
background-image:
|
|
628
|
+
radial-gradient(closest-side, rgb(255 255 255 / var(--fp-led-core, 0.3)), transparent),
|
|
629
|
+
var(--fp-meter-zones);
|
|
630
|
+
background-position: left bottom;
|
|
631
|
+
background-size: var(--fp-meter-cell), auto;
|
|
632
|
+
mask-image: var(--fp-meter-lit-gaps);
|
|
633
|
+
clip-path: inset(calc((1 - var(--fp-meter-level, 0)) * 100% + 1px) 0 0 0);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/* Forced colours: the strip as an outline and the lit segments in CanvasText, so the level shows. */
|
|
637
|
+
@media (forced-colors: active) {
|
|
638
|
+
outline: 1px solid CanvasText;
|
|
639
|
+
|
|
640
|
+
&::before {
|
|
641
|
+
content: none;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
&::after {
|
|
645
|
+
forced-color-adjust: none;
|
|
646
|
+
background: CanvasText;
|
|
647
|
+
mask-image: var(--fp-meter-gaps);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
&[data-orientation="horizontal"] {
|
|
652
|
+
--fp-meter-zones: linear-gradient(
|
|
653
|
+
to right,
|
|
654
|
+
var(--fp-color-led-on) 0 calc(var(--fp-meter-amber, calc(8 / 12)) * 100%),
|
|
655
|
+
var(--fp-color-led-amber) 0 calc(var(--fp-meter-red, calc(11 / 12)) * 100%),
|
|
656
|
+
var(--fp-color-led-red) 0
|
|
657
|
+
);
|
|
658
|
+
--fp-meter-gaps: repeating-linear-gradient(
|
|
659
|
+
to right,
|
|
660
|
+
#000 0 calc(100% / var(--fp-meter-segments, 12) - 2px),
|
|
661
|
+
transparent 0 calc(100% / var(--fp-meter-segments, 12))
|
|
662
|
+
);
|
|
663
|
+
--fp-meter-lit-gaps: repeating-linear-gradient(
|
|
664
|
+
to right,
|
|
665
|
+
#000 0 calc(100% / var(--fp-meter-segments, 12) - 2px),
|
|
666
|
+
rgb(0 0 0 / calc(0.18 * var(--fp-led-glow, 1))) 0 calc(100% / var(--fp-meter-segments, 12))
|
|
667
|
+
);
|
|
668
|
+
--fp-meter-cell: calc(100% / var(--fp-meter-segments, 12)) 100%;
|
|
669
|
+
width: var(--fp-meter-length, 8rem);
|
|
670
|
+
height: 0.75rem;
|
|
671
|
+
|
|
672
|
+
&::before,
|
|
673
|
+
&::after {
|
|
674
|
+
inset: 2px 0 2px 2px;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
&::after {
|
|
678
|
+
clip-path: inset(0 calc((1 - var(--fp-meter-level, 0)) * 100% + 1px) 0 0);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Seven-segment readout: LED digits on a near-black face, set in the housing. Each character is an
|
|
685
|
+
* fp-readout-cell, and the value is said in an fp-readout-text inside. Green by default; colour it
|
|
686
|
+
* with fp-led-amber or fp-led-red. Lit digits pass as text on the face at any size.
|
|
687
|
+
*/
|
|
688
|
+
.fp-readout {
|
|
689
|
+
position: relative;
|
|
690
|
+
display: inline-flex;
|
|
691
|
+
align-items: center;
|
|
692
|
+
gap: 0.1875rem;
|
|
693
|
+
padding: 0.3125rem 0.4375rem;
|
|
694
|
+
border: 3px solid var(--fp-color-housing);
|
|
695
|
+
border-radius: 5px;
|
|
696
|
+
background-color: color-mix(in srgb, var(--fp-color-led-off) 70%, black);
|
|
697
|
+
box-shadow:
|
|
698
|
+
inset 1px 1px 3px rgb(0 0 0 / 0.8),
|
|
699
|
+
0 1px 0 var(--fp-color-bevel-hi);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
/**
|
|
703
|
+
* One character of an fp-readout, leaning a little like a real display. It shows its unlit
|
|
704
|
+
* segments itself, a figure eight and a point, faintly in the lit colour; put the lit ones in an
|
|
705
|
+
* fp-readout-lit inside. With `data-colon` it is a narrow colon cell. `--fp-readout-height` sets
|
|
706
|
+
* its height (2rem). Give it aria-hidden="true".
|
|
707
|
+
*/
|
|
708
|
+
.fp-readout-cell {
|
|
709
|
+
position: relative;
|
|
710
|
+
flex-shrink: 0;
|
|
711
|
+
height: var(--fp-readout-height, 2rem);
|
|
712
|
+
aspect-ratio: 15 / 22;
|
|
713
|
+
transform: skewX(-6deg);
|
|
714
|
+
|
|
715
|
+
&::before {
|
|
716
|
+
content: "";
|
|
717
|
+
position: absolute;
|
|
718
|
+
inset: 0;
|
|
719
|
+
background-color: color-mix(in srgb, var(--fp-color-led-off) 75%, var(--fp-led, var(--fp-color-led-on)));
|
|
720
|
+
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;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
&[data-colon] {
|
|
724
|
+
aspect-ratio: 5 / 22;
|
|
725
|
+
|
|
726
|
+
&::before {
|
|
727
|
+
content: none;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
/* With forced colours, only the lit digits show: a ghost in a system colour blurs into them. */
|
|
732
|
+
@media (forced-colors: active) {
|
|
733
|
+
&::before {
|
|
734
|
+
content: none;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* The lit segments of an fp-readout-cell: an element for each, named by `data-segment`, `a` to `g`
|
|
741
|
+
* (top, then clockwise, then the middle), `dp`, or a colon's `colon-upper` and `colon-lower`. Each
|
|
742
|
+
* is brightest over its chip, in the middle, and they glow onto the face, both by the lens.
|
|
743
|
+
*/
|
|
744
|
+
.fp-readout-lit {
|
|
745
|
+
position: absolute;
|
|
746
|
+
inset: 0;
|
|
747
|
+
filter: drop-shadow(
|
|
748
|
+
0 0 calc(var(--fp-readout-height, 2rem) * 0.06 * var(--fp-led-glow, 1))
|
|
749
|
+
color-mix(in srgb, var(--fp-led, var(--fp-color-led-on)) 70%, transparent)
|
|
750
|
+
);
|
|
751
|
+
|
|
752
|
+
& > [data-segment] {
|
|
753
|
+
position: absolute;
|
|
754
|
+
background-color: var(--fp-led, var(--fp-color-led-on));
|
|
755
|
+
background-image: radial-gradient(
|
|
756
|
+
closest-side,
|
|
757
|
+
color-mix(in srgb, var(--fp-led, var(--fp-color-led-on)) calc((1 - var(--fp-led-core, 0.3)) * 100%), white),
|
|
758
|
+
var(--fp-led, var(--fp-color-led-on))
|
|
759
|
+
);
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/* The bars, on the cell's 15 x 22 grid: 9 long and 2 thick, pointed at both ends. */
|
|
763
|
+
& > [data-segment="a"],
|
|
764
|
+
& > [data-segment="d"],
|
|
765
|
+
& > [data-segment="g"] {
|
|
766
|
+
left: 10%;
|
|
767
|
+
width: 60%;
|
|
768
|
+
height: 9.0909%;
|
|
769
|
+
clip-path: polygon(0 50%, 11.1111% 0, 88.8889% 0, 100% 50%, 88.8889% 100%, 11.1111% 100%);
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
& > [data-segment="b"],
|
|
773
|
+
& > [data-segment="c"],
|
|
774
|
+
& > [data-segment="e"],
|
|
775
|
+
& > [data-segment="f"] {
|
|
776
|
+
width: 13.3333%;
|
|
777
|
+
height: 40.9091%;
|
|
778
|
+
clip-path: polygon(50% 0, 100% 11.1111%, 100% 88.8889%, 50% 100%, 0 88.8889%, 0 11.1111%);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
& > [data-segment="a"] {
|
|
782
|
+
top: 0;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
& > [data-segment="g"] {
|
|
786
|
+
top: 45.4545%;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
& > [data-segment="d"] {
|
|
790
|
+
top: 90.9091%;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
& > [data-segment="b"],
|
|
794
|
+
& > [data-segment="c"] {
|
|
795
|
+
left: 66.6667%;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
& > [data-segment="e"],
|
|
799
|
+
& > [data-segment="f"] {
|
|
800
|
+
left: 0;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
& > [data-segment="b"],
|
|
804
|
+
& > [data-segment="f"] {
|
|
805
|
+
top: 6.8182%;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
& > [data-segment="c"],
|
|
809
|
+
& > [data-segment="e"] {
|
|
810
|
+
top: 52.2727%;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
& > [data-segment="dp"],
|
|
814
|
+
& > [data-segment="colon-upper"],
|
|
815
|
+
& > [data-segment="colon-lower"] {
|
|
816
|
+
aspect-ratio: 1;
|
|
817
|
+
border-radius: 50%;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
& > [data-segment="dp"] {
|
|
821
|
+
top: 90%;
|
|
822
|
+
left: 82%;
|
|
823
|
+
width: 16%;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
& > [data-segment="colon-upper"],
|
|
827
|
+
& > [data-segment="colon-lower"] {
|
|
828
|
+
left: 26%;
|
|
829
|
+
width: 48%;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
& > [data-segment="colon-upper"] {
|
|
833
|
+
top: 26.3636%;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
& > [data-segment="colon-lower"] {
|
|
837
|
+
top: 62.7273%;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
@media (forced-colors: active) {
|
|
841
|
+
filter: none;
|
|
842
|
+
|
|
843
|
+
& > [data-segment] {
|
|
844
|
+
forced-color-adjust: none;
|
|
845
|
+
background: CanvasText;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
/** An fp-readout's value as text, for screen readers: hidden from view, read in place of the digits. */
|
|
851
|
+
.fp-readout-text {
|
|
852
|
+
position: absolute;
|
|
853
|
+
width: 1px;
|
|
854
|
+
height: 1px;
|
|
855
|
+
overflow: hidden;
|
|
856
|
+
clip-path: inset(50%);
|
|
857
|
+
white-space: nowrap;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/** Matte content panel. This is where reading happens: no gloss, a 1 px bevel and a short hard shadow. */
|
|
861
|
+
.fp-module {
|
|
862
|
+
color-scheme: var(--fp-scheme);
|
|
863
|
+
border-radius: 10px;
|
|
864
|
+
background-color: var(--fp-color-shell-matte);
|
|
865
|
+
box-shadow:
|
|
866
|
+
0 0 0 1px var(--fp-color-edge),
|
|
867
|
+
inset 1px 1px 0 var(--fp-color-bevel-hi),
|
|
868
|
+
inset -1px -1px 0 var(--fp-color-bevel-lo),
|
|
869
|
+
2px 3px 0 var(--fp-color-cast);
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
/** A module's own little title bar, in brushed chrome. */
|
|
873
|
+
.fp-module-title {
|
|
874
|
+
display: flex;
|
|
875
|
+
align-items: center;
|
|
876
|
+
gap: 0.5rem;
|
|
877
|
+
min-height: 1.875rem;
|
|
878
|
+
padding: 0.25rem 0.625rem;
|
|
879
|
+
border-radius: 10px 10px 0 0;
|
|
880
|
+
border-bottom: 1px solid var(--fp-color-edge);
|
|
881
|
+
background-image: linear-gradient(
|
|
882
|
+
180deg,
|
|
883
|
+
color-mix(in srgb, var(--fp-color-chrome-hi) 60%, var(--fp-color-chrome-100)) 0%,
|
|
884
|
+
var(--fp-color-chrome-100) 40%,
|
|
885
|
+
var(--fp-color-chrome-300) 100%
|
|
886
|
+
);
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
/**
|
|
890
|
+
* A window: a module (or, in the page extra, a sheet panel) whose fp-module-title stays in view
|
|
891
|
+
* while its body scrolls under it.
|
|
892
|
+
*/
|
|
893
|
+
.fp-window {
|
|
894
|
+
/* Its top corners follow the window's, which differ between a module and a sheet. */
|
|
895
|
+
& > .fp-module-title {
|
|
896
|
+
position: sticky;
|
|
897
|
+
top: 0;
|
|
898
|
+
z-index: 1;
|
|
899
|
+
border-start-start-radius: inherit;
|
|
900
|
+
border-start-end-radius: inherit;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
/* Title-bar buttons stay small with a precise pointer; touch keeps the 44 px target. */
|
|
904
|
+
@media (pointer: fine) {
|
|
905
|
+
& > .fp-module-title .fp-chrome-btn {
|
|
906
|
+
min-height: 1.75rem;
|
|
907
|
+
min-width: 1.75rem;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
/** A window's name in its title bar. Pair it with fp-silk; it pushes the title-bar buttons to the far end. */
|
|
913
|
+
.fp-window-title {
|
|
914
|
+
margin: 0;
|
|
915
|
+
margin-inline-end: auto;
|
|
916
|
+
font-weight: 400;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
/** The padded body under a window's title bar. */
|
|
920
|
+
.fp-window-body {
|
|
921
|
+
padding: 0.75rem;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
/** Recessed well inside a matte panel, for secondary content or an empty slot. */
|
|
925
|
+
.fp-well {
|
|
926
|
+
color-scheme: var(--fp-scheme);
|
|
927
|
+
border-radius: 6px;
|
|
928
|
+
background-color: var(--fp-color-well);
|
|
929
|
+
box-shadow:
|
|
930
|
+
inset 1px 1px 2px var(--fp-color-bevel-lo),
|
|
931
|
+
inset -1px -1px 0 var(--fp-color-bevel-hi);
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
/** Silkscreened hardware label. */
|
|
935
|
+
.fp-silk {
|
|
936
|
+
font-family: var(--fp-font-display);
|
|
937
|
+
font-size: 11px;
|
|
938
|
+
line-height: 1.3;
|
|
939
|
+
letter-spacing: 0.08em;
|
|
940
|
+
text-transform: uppercase;
|
|
941
|
+
color: var(--fp-color-ink-soft);
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
/** LCD glass, recessed into the chrome. Text on it uses `--fp-font-lcd`; drawings use `--fp-color-lcd-ink`. */
|
|
945
|
+
.fp-lcd {
|
|
946
|
+
border-radius: 8px;
|
|
947
|
+
color: var(--fp-color-lcd-ink);
|
|
948
|
+
background-color: var(--fp-color-lcd-bg);
|
|
949
|
+
background-image: repeating-linear-gradient(0deg, rgb(0 0 0 / 0.04) 0 1px, transparent 1px 3px);
|
|
950
|
+
box-shadow:
|
|
951
|
+
0 0 0 2px var(--fp-color-chrome-800),
|
|
952
|
+
inset 2px 2px 4px rgb(0 0 0 / 0.35),
|
|
953
|
+
inset -1px -1px 0 rgb(255 255 255 / 0.2);
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
/** One line of LCD text. Cells of fp-lcd-ghost stacked behind it show the unlit segments, as on real glass. */
|
|
957
|
+
.fp-lcd-line {
|
|
958
|
+
display: grid;
|
|
959
|
+
margin: 0;
|
|
960
|
+
font-family: var(--fp-font-lcd);
|
|
961
|
+
white-space: pre;
|
|
962
|
+
|
|
963
|
+
& > * {
|
|
964
|
+
grid-area: 1 / 1;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
/** The unlit segments behind an LCD line. Decorative: give it aria-hidden="true". */
|
|
969
|
+
.fp-lcd-ghost {
|
|
970
|
+
color: var(--fp-color-lcd-ghost);
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
/** Dark housing that LEDs and small readouts sit in. */
|
|
974
|
+
.fp-housing {
|
|
975
|
+
display: inline-flex;
|
|
976
|
+
align-items: center;
|
|
977
|
+
gap: 0.375rem;
|
|
978
|
+
border-radius: 9999px;
|
|
979
|
+
padding: 0.125rem 0.5rem;
|
|
980
|
+
color: var(--fp-color-housing-ink);
|
|
981
|
+
font-size: 12px;
|
|
982
|
+
font-weight: 700;
|
|
983
|
+
background-color: var(--fp-color-housing);
|
|
984
|
+
box-shadow:
|
|
985
|
+
inset 1px 1px 2px rgb(0 0 0 / 0.5),
|
|
986
|
+
0 1px 0 var(--fp-color-bevel-hi);
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
/** An LED with its own dark bezel ring, so a lit one reads on light chrome too. Unlit by default. */
|
|
990
|
+
.fp-led {
|
|
991
|
+
display: inline-block;
|
|
992
|
+
flex-shrink: 0;
|
|
993
|
+
width: 0.5rem;
|
|
994
|
+
height: 0.5rem;
|
|
995
|
+
border-radius: 9999px;
|
|
996
|
+
background-color: var(--fp-color-led-off);
|
|
997
|
+
box-shadow:
|
|
998
|
+
0 0 0 2px var(--fp-color-housing),
|
|
999
|
+
inset 0 -1px 1px rgb(0 0 0 / 0.45);
|
|
1000
|
+
|
|
1001
|
+
/* Forced colours drop the fill and the ring: an outline keeps an unlit LED in view. */
|
|
1002
|
+
@media (forced-colors: active) {
|
|
1003
|
+
outline: 1px solid CanvasText;
|
|
1004
|
+
outline-offset: 1px;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
/** Green for an fp-led or fp-readout, the default. */
|
|
1009
|
+
.fp-led-green {
|
|
1010
|
+
--fp-led: var(--fp-color-led-on);
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
/** Amber for an fp-led or fp-readout. */
|
|
1014
|
+
.fp-led-amber {
|
|
1015
|
+
--fp-led: var(--fp-color-led-amber);
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
/** Red for an fp-led or fp-readout. */
|
|
1019
|
+
.fp-led-red {
|
|
1020
|
+
--fp-led: var(--fp-color-led-red);
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
/**
|
|
1024
|
+
* Lights an fp-led in its colour, brightest over the chip in its middle, with a glow. The lens sets
|
|
1025
|
+
* how strong both are. Say what it means in text nearby, never by colour alone.
|
|
1026
|
+
*/
|
|
1027
|
+
.fp-led-lit {
|
|
1028
|
+
background-color: var(--fp-led, var(--fp-color-led-on));
|
|
1029
|
+
background-image: radial-gradient(
|
|
1030
|
+
closest-side,
|
|
1031
|
+
color-mix(in srgb, var(--fp-led, var(--fp-color-led-on)) calc((1 - var(--fp-led-core, 0.3)) * 100%), white),
|
|
1032
|
+
var(--fp-led, var(--fp-color-led-on))
|
|
1033
|
+
);
|
|
1034
|
+
box-shadow:
|
|
1035
|
+
0 0 0 2px var(--fp-color-housing),
|
|
1036
|
+
0 0 calc(7px * var(--fp-led-glow, 1)) calc(2px * var(--fp-led-glow, 1))
|
|
1037
|
+
color-mix(in srgb, var(--fp-led, var(--fp-color-led-on)) 65%, transparent),
|
|
1038
|
+
inset 0 -1px 1px rgb(0 0 0 / 0.2);
|
|
1039
|
+
|
|
1040
|
+
/* Lit is filled, so it reads apart from unlit by shape as well as colour. */
|
|
1041
|
+
@media (forced-colors: active) {
|
|
1042
|
+
forced-color-adjust: none;
|
|
1043
|
+
background: CanvasText;
|
|
1044
|
+
box-shadow: none;
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
/**
|
|
1049
|
+
* LED lens, flat: an even fill with no hot spot. A lens class works on an fp-led, fp-meter or
|
|
1050
|
+
* fp-readout, or on any element around them, such as a device body, to set every one inside.
|
|
1051
|
+
*/
|
|
1052
|
+
.fp-lens-flat {
|
|
1053
|
+
--fp-led-core: 0;
|
|
1054
|
+
--fp-led-glow: 1;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
/**
|
|
1058
|
+
* LED lens, diffused, the default: like most real displays, each LED is a little brighter over its
|
|
1059
|
+
* chip, in the middle, with a soft glow.
|
|
1060
|
+
*/
|
|
1061
|
+
.fp-lens-diffused {
|
|
1062
|
+
--fp-led-core: 0.3;
|
|
1063
|
+
--fp-led-glow: 1;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
/** LED lens, clear: a bright, pale core over each chip and a wider glow, like a water-clear lens. */
|
|
1067
|
+
.fp-lens-clear {
|
|
1068
|
+
--fp-led-core: 0.5;
|
|
1069
|
+
--fp-led-glow: 1.5;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
/** Decorative screw head. Give it aria-hidden="true". */
|
|
1073
|
+
.fp-screw {
|
|
1074
|
+
display: inline-block;
|
|
1075
|
+
flex-shrink: 0;
|
|
1076
|
+
width: 10px;
|
|
1077
|
+
height: 10px;
|
|
1078
|
+
border-radius: 9999px;
|
|
1079
|
+
background-image:
|
|
1080
|
+
linear-gradient(135deg, transparent 44%, var(--fp-color-chrome-800) 44% 56%, transparent 56%),
|
|
1081
|
+
radial-gradient(circle at 35% 30%, var(--fp-color-chrome-hi), var(--fp-color-chrome-300) 60%, var(--fp-color-chrome-500));
|
|
1082
|
+
box-shadow: 0 0 0 1px var(--fp-color-edge);
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
/** A scrolling panel, such as a list window, with a thin scrollbar that never pushes the layout. */
|
|
1086
|
+
.fp-scroll-thin {
|
|
1087
|
+
color-scheme: var(--fp-scheme);
|
|
1088
|
+
scrollbar-width: thin;
|
|
1089
|
+
scrollbar-color: var(--fp-color-scroll-thumb) transparent;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
/** A 16 px pixel icon. Size it in whole multiples of 16 px so its pixels stay crisp. */
|
|
1093
|
+
.fp-icon {
|
|
1094
|
+
flex-shrink: 0;
|
|
1095
|
+
width: 16px;
|
|
1096
|
+
height: 16px;
|
|
1097
|
+
fill: currentColor;
|
|
1098
|
+
shape-rendering: crispEdges;
|
|
1099
|
+
}
|
|
1100
|
+
}
|