@xsynaptic/sonic-ui 0.1.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 +38 -0
- package/dist/define/dial.d.ts +2 -0
- package/dist/define/dial.js +8 -0
- package/dist/define/dial.js.map +1 -0
- package/dist/define/key.d.ts +2 -0
- package/dist/define/key.js +8 -0
- package/dist/define/key.js.map +1 -0
- package/dist/define/meter.d.ts +2 -0
- package/dist/define/meter.js +8 -0
- package/dist/define/meter.js.map +1 -0
- package/dist/define/segmented.d.ts +2 -0
- package/dist/define/segmented.js +8 -0
- package/dist/define/segmented.js.map +1 -0
- package/dist/define/slider.d.ts +2 -0
- package/dist/define/slider.js +8 -0
- package/dist/define/slider.js.map +1 -0
- package/dist/define.d.ts +11 -0
- package/dist/define.js +5 -0
- package/dist/elements/define-once.js +8 -0
- package/dist/elements/define-once.js.map +1 -0
- package/dist/elements/dial.d.ts +15 -0
- package/dist/elements/dial.js +57 -0
- package/dist/elements/dial.js.map +1 -0
- package/dist/elements/key.d.ts +17 -0
- package/dist/elements/key.js +99 -0
- package/dist/elements/key.js.map +1 -0
- package/dist/elements/meter.d.ts +17 -0
- package/dist/elements/meter.js +119 -0
- package/dist/elements/meter.js.map +1 -0
- package/dist/elements/range-element.d.ts +32 -0
- package/dist/elements/range-element.js +348 -0
- package/dist/elements/range-element.js.map +1 -0
- package/dist/elements/range-entry.js +73 -0
- package/dist/elements/range-entry.js.map +1 -0
- package/dist/elements/segmented.d.ts +17 -0
- package/dist/elements/segmented.js +104 -0
- package/dist/elements/segmented.js.map +1 -0
- package/dist/elements/slider.d.ts +15 -0
- package/dist/elements/slider.js +70 -0
- package/dist/elements/slider.js.map +1 -0
- package/dist/elements/sonic-element.d.ts +20 -0
- package/dist/elements/sonic-element.js +94 -0
- package/dist/elements/sonic-element.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +7 -0
- package/dist/lib/read-px-property.js +9 -0
- package/dist/lib/read-px-property.js.map +1 -0
- package/dist/lib/render.js +19 -0
- package/dist/lib/render.js.map +1 -0
- package/dist/lib/taper.js +33 -0
- package/dist/lib/taper.js.map +1 -0
- package/dist/lib/write-attribute.js +12 -0
- package/dist/lib/write-attribute.js.map +1 -0
- package/dist/skins/ivory.css +8 -0
- package/dist/skins/lime.css +10 -0
- package/dist/skins/slate.css +10 -0
- package/dist/styles/controls.css +8 -0
- package/dist/styles/dial.css +265 -0
- package/dist/styles/key.css +137 -0
- package/dist/styles/material.css +229 -0
- package/dist/styles/meter.css +271 -0
- package/dist/styles/screen.css +47 -0
- package/dist/styles/segmented.css +147 -0
- package/dist/styles/slider.css +204 -0
- package/package.json +76 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
@layer sonic {
|
|
2
|
+
/* On the host too, for the placeholder */
|
|
3
|
+
:is(sonic-key, .sonic-key) {
|
|
4
|
+
--_sonic-key-size: var(--sonic-key-size, 2rem);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
:has(> .sonic-key):not([hidden]) {
|
|
8
|
+
display: contents;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/* Hides the icon, which has no size before upgrade */
|
|
12
|
+
sonic-key:not(:defined, [hidden]) {
|
|
13
|
+
display: block;
|
|
14
|
+
visibility: hidden;
|
|
15
|
+
flex-shrink: 0;
|
|
16
|
+
inline-size: var(--_sonic-key-size);
|
|
17
|
+
block-size: var(--_sonic-key-size);
|
|
18
|
+
overflow: hidden;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* The button is the well; the cap is the part that moves */
|
|
22
|
+
.sonic-key {
|
|
23
|
+
--_sonic-key-gap-ratio: var(--sonic-key-gap-ratio, 0);
|
|
24
|
+
--_sonic-key-radius-ratio: var(--sonic-key-radius-ratio, 0.2);
|
|
25
|
+
--_sonic-key-bevel-ratio: var(--sonic-key-bevel-ratio, 0.075);
|
|
26
|
+
--_sonic-key-face-radius-ratio: var(--sonic-key-face-radius-ratio, 0.15);
|
|
27
|
+
--_sonic-key-icon-ratio: var(--sonic-key-icon-ratio, 0.5);
|
|
28
|
+
--_sonic-key-depth-ratio: var(--sonic-key-depth-ratio, 0.03);
|
|
29
|
+
--_sonic-key-press-scale: var(--sonic-key-press-scale, 0.985);
|
|
30
|
+
--_sonic-unit: calc(var(--_sonic-key-size) / 32);
|
|
31
|
+
--_sonic-key-corner: calc(var(--_sonic-key-size) * var(--_sonic-key-radius-ratio));
|
|
32
|
+
--_sonic-key-gap: calc(var(--_sonic-key-size) * var(--_sonic-key-gap-ratio));
|
|
33
|
+
--_sonic-key-depth: calc(var(--_sonic-key-size) * var(--_sonic-key-depth-ratio));
|
|
34
|
+
--_sonic-key-shift: calc(var(--_sonic-key-depth) / -2);
|
|
35
|
+
--_sonic-key-scale: 1;
|
|
36
|
+
--_sonic-key-drop: calc(1.5 * var(--_sonic-cast-x)) calc(1.5 * var(--_sonic-cast-y))
|
|
37
|
+
calc(2 * var(--_sonic-unit)) rgb(0 0 0 / 65%);
|
|
38
|
+
--_sonic-key-glow: transparent;
|
|
39
|
+
|
|
40
|
+
position: relative;
|
|
41
|
+
flex-shrink: 0;
|
|
42
|
+
inline-size: var(--_sonic-key-size);
|
|
43
|
+
block-size: var(--_sonic-key-size);
|
|
44
|
+
padding: 0;
|
|
45
|
+
color: var(--_sonic-ink);
|
|
46
|
+
cursor: pointer;
|
|
47
|
+
background: linear-gradient(
|
|
48
|
+
var(--_sonic-light-fall),
|
|
49
|
+
color-mix(in oklab, var(--_sonic-unlit) 55%, #000),
|
|
50
|
+
color-mix(in oklab, var(--_sonic-unlit) 85%, #000)
|
|
51
|
+
);
|
|
52
|
+
border: 0;
|
|
53
|
+
border-radius: var(--_sonic-key-corner);
|
|
54
|
+
box-shadow: var(--_sonic-well);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* A drifting finger would turn a held pad into a pan */
|
|
58
|
+
:where([momentary]) > .sonic-key {
|
|
59
|
+
touch-action: none;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.sonic-key:active:where(:not(:disabled)),
|
|
63
|
+
.sonic-key:is([aria-pressed='true'], :where([momentary][pressed]) > *) {
|
|
64
|
+
--_sonic-key-shift: calc(var(--_sonic-key-depth) / 2);
|
|
65
|
+
--_sonic-key-scale: var(--_sonic-key-press-scale);
|
|
66
|
+
--_sonic-key-drop: calc(0.5 * var(--_sonic-cast-x)) calc(0.5 * var(--_sonic-cast-y))
|
|
67
|
+
var(--_sonic-unit) rgb(0 0 0 / 65%);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.sonic-key:is([aria-pressed='true'], :where([momentary][pressed]) > *) {
|
|
71
|
+
--_sonic-key-glow: color-mix(in oklab, var(--_sonic-lit) 22%, transparent);
|
|
72
|
+
|
|
73
|
+
color: var(--_sonic-lit);
|
|
74
|
+
|
|
75
|
+
/* A full-strength floor shows as a hard line where the sunk cap uncovers it */
|
|
76
|
+
background: linear-gradient(
|
|
77
|
+
var(--_sonic-light-fall),
|
|
78
|
+
color-mix(in oklab, var(--_sonic-lit) 25%, #000),
|
|
79
|
+
color-mix(in oklab, var(--_sonic-lit) 60%, #000)
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.sonic-key:disabled {
|
|
84
|
+
--_sonic-lit: var(--_sonic-lit-disabled);
|
|
85
|
+
|
|
86
|
+
color: var(--_sonic-etch);
|
|
87
|
+
cursor: default;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.sonic-key-cap {
|
|
91
|
+
position: absolute;
|
|
92
|
+
inset: var(--_sonic-key-gap);
|
|
93
|
+
display: grid;
|
|
94
|
+
place-items: center;
|
|
95
|
+
background: var(--_sonic-skirt);
|
|
96
|
+
border-radius: max(0px, var(--_sonic-key-corner) - var(--_sonic-key-gap));
|
|
97
|
+
box-shadow: var(--_sonic-key-drop), var(--_sonic-rim), var(--_sonic-focus-shadow);
|
|
98
|
+
scale: var(--_sonic-key-scale);
|
|
99
|
+
translate: 0 var(--_sonic-key-shift);
|
|
100
|
+
transition: var(--_sonic-press-transition);
|
|
101
|
+
|
|
102
|
+
&::before {
|
|
103
|
+
position: absolute;
|
|
104
|
+
inset: calc(var(--_sonic-key-size) * var(--_sonic-key-bevel-ratio));
|
|
105
|
+
content: '';
|
|
106
|
+
background:
|
|
107
|
+
radial-gradient(closest-side, var(--_sonic-key-glow), transparent), var(--_sonic-sheen),
|
|
108
|
+
var(--_sonic-face);
|
|
109
|
+
border-radius: calc(var(--_sonic-key-size) * var(--_sonic-key-face-radius-ratio));
|
|
110
|
+
box-shadow: var(--_sonic-face-shadow);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
& > svg {
|
|
114
|
+
position: relative;
|
|
115
|
+
inline-size: calc(var(--_sonic-key-size) * var(--_sonic-key-icon-ratio));
|
|
116
|
+
block-size: calc(var(--_sonic-key-size) * var(--_sonic-key-icon-ratio));
|
|
117
|
+
filter: var(--_sonic-engraving);
|
|
118
|
+
mask-image: linear-gradient(var(--_sonic-light-fall), #000 15%, rgb(0 0 0 / 60%));
|
|
119
|
+
mask-repeat: no-repeat;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* Engraving under the glow keeps a lit icon's edge */
|
|
124
|
+
.sonic-key:is([aria-pressed='true'], :where([momentary][pressed]) > *) .sonic-key-cap > svg {
|
|
125
|
+
filter: var(--_sonic-engraving)
|
|
126
|
+
drop-shadow(0 0 var(--_sonic-unit) color-mix(in oklab, var(--_sonic-lit) 35%, transparent));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.sonic-key:is([aria-pressed='true'], :where([momentary][pressed]) > *)
|
|
130
|
+
.sonic-key-cap
|
|
131
|
+
> [data-sonic-when='off'],
|
|
132
|
+
.sonic-key:not([aria-pressed='true'], :where([momentary][pressed]) > *)
|
|
133
|
+
.sonic-key-cap
|
|
134
|
+
> [data-sonic-when='on'] {
|
|
135
|
+
display: none;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
@layer sonic {
|
|
2
|
+
/* Resolved per control rather than on `:root`, so each follows its own cap and unit */
|
|
3
|
+
:is(
|
|
4
|
+
.sonic-dial,
|
|
5
|
+
.sonic-dial-readout,
|
|
6
|
+
.sonic-key,
|
|
7
|
+
.sonic-meter,
|
|
8
|
+
.sonic-screen,
|
|
9
|
+
.sonic-segmented,
|
|
10
|
+
.sonic-slider,
|
|
11
|
+
.sonic-slider-readout
|
|
12
|
+
) {
|
|
13
|
+
--_sonic-unlit: var(--sonic-unlit, #27272a);
|
|
14
|
+
|
|
15
|
+
/* Degrees left of overhead */
|
|
16
|
+
--_sonic-light-tilt: var(--sonic-light-tilt, 17deg);
|
|
17
|
+
--_sonic-light-fall: calc(180deg - var(--_sonic-light-tilt));
|
|
18
|
+
--_sonic-cast-x: calc(sin(var(--_sonic-light-tilt)) * var(--_sonic-unit));
|
|
19
|
+
--_sonic-cast-y: calc(cos(var(--_sonic-light-tilt)) * var(--_sonic-unit));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
:is(.sonic-dial, .sonic-key, .sonic-meter, .sonic-segmented-segment, .sonic-slider) {
|
|
23
|
+
--_sonic-lit: var(--sonic-lit, #f59e0b);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.sonic-meter {
|
|
27
|
+
--_sonic-hot: var(--sonic-hot, #f97316);
|
|
28
|
+
--_sonic-clip: var(--sonic-clip, #ef4444);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
:is(.sonic-dial, .sonic-key, .sonic-segmented-segment, .sonic-slider) {
|
|
32
|
+
--_sonic-cap: var(--sonic-cap, #3f3f46);
|
|
33
|
+
--_sonic-cap-hover: var(--sonic-cap-hover, #52525b);
|
|
34
|
+
--_sonic-focus: var(--sonic-focus, oklch(90% 0.23 128deg));
|
|
35
|
+
|
|
36
|
+
/* Derived from the cap, so a reskinned cap brings its marks along */
|
|
37
|
+
--_sonic-ink: var(--sonic-mark, color-mix(in oklab, var(--_sonic-cap) 30%, #fff));
|
|
38
|
+
--_sonic-etch: color-mix(in oklab, var(--_sonic-ink) 30%, transparent);
|
|
39
|
+
--_sonic-lit-disabled: color-mix(in oklab, var(--_sonic-ink) 25%, var(--_sonic-unlit));
|
|
40
|
+
--_sonic-skirt: linear-gradient(
|
|
41
|
+
var(--_sonic-light-fall),
|
|
42
|
+
color-mix(in oklab, var(--_sonic-cap) 70%, #fff),
|
|
43
|
+
color-mix(in oklab, var(--_sonic-cap) 55%, #000)
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
/* Inverted from the skirt, as a dished face takes light */
|
|
47
|
+
--_sonic-face: linear-gradient(
|
|
48
|
+
var(--_sonic-light-fall),
|
|
49
|
+
color-mix(in oklab, var(--_sonic-cap) 85%, #000),
|
|
50
|
+
color-mix(in oklab, var(--_sonic-cap) 90%, #fff)
|
|
51
|
+
);
|
|
52
|
+
--_sonic-sheen: linear-gradient(
|
|
53
|
+
105deg,
|
|
54
|
+
transparent 20%,
|
|
55
|
+
rgb(255 255 255 / 10%) 45%,
|
|
56
|
+
transparent 55%,
|
|
57
|
+
rgb(0 0 0 / 6%) 80%
|
|
58
|
+
);
|
|
59
|
+
--_sonic-press-transition:
|
|
60
|
+
scale 60ms cubic-bezier(0.4, 0, 0.2, 1), translate 60ms cubic-bezier(0.4, 0, 0.2, 1),
|
|
61
|
+
box-shadow 60ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
62
|
+
--_sonic-face-shadow:
|
|
63
|
+
inset var(--_sonic-cast-x) var(--_sonic-cast-y) calc(1.5 * var(--_sonic-unit))
|
|
64
|
+
rgb(0 0 0 / 35%),
|
|
65
|
+
calc(0.5 * var(--_sonic-cast-x)) calc(0.5 * var(--_sonic-cast-y)) var(--_sonic-unit)
|
|
66
|
+
rgb(255 255 255 / 12%);
|
|
67
|
+
--_sonic-rim: 0 0 0 max(1px, 0.75 * var(--_sonic-unit))
|
|
68
|
+
color-mix(in oklab, var(--_sonic-cap) 40%, #000);
|
|
69
|
+
--_sonic-focus-shadow: 0 0 transparent;
|
|
70
|
+
--_sonic-focus-glow: 0 0 max(4px, 3 * var(--_sonic-unit)) max(1px, 0.5 * var(--_sonic-unit))
|
|
71
|
+
color-mix(in oklab, var(--_sonic-focus) 60%, transparent);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
:is(.sonic-dial, .sonic-slider) {
|
|
75
|
+
--_sonic-lit-off: var(
|
|
76
|
+
--sonic-dimmed,
|
|
77
|
+
color-mix(in oklab, var(--_sonic-lit) 50%, var(--_sonic-unlit))
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
:is(.sonic-dial-readout, .sonic-screen, .sonic-slider-readout) {
|
|
82
|
+
--_sonic-glass: var(--sonic-glass, color-mix(in oklab, var(--_sonic-unlit) 45%, #000));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
:is(.sonic-key, .sonic-screen, .sonic-segmented) {
|
|
86
|
+
--_sonic-well:
|
|
87
|
+
inset var(--_sonic-cast-x) var(--_sonic-cast-y) calc(2 * var(--_sonic-unit)) rgb(0 0 0 / 60%),
|
|
88
|
+
calc(0.5 * var(--_sonic-cast-x)) calc(0.5 * var(--_sonic-cast-y)) 0 rgb(255 255 255 / 6%);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
:is(.sonic-key, .sonic-segmented) {
|
|
92
|
+
--_sonic-engraving: drop-shadow(
|
|
93
|
+
calc(-0.4 * var(--_sonic-cast-x)) calc(-0.4 * var(--_sonic-cast-y))
|
|
94
|
+
calc(0.4 * var(--_sonic-unit)) rgb(0 0 0 / 30%)
|
|
95
|
+
)
|
|
96
|
+
drop-shadow(
|
|
97
|
+
calc(0.3 * var(--_sonic-cast-x)) calc(0.3 * var(--_sonic-cast-y)) 0 rgb(255 255 255 / 7%)
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* The well's full blur would fill a slot this narrow */
|
|
102
|
+
:is(.sonic-meter, .sonic-slider) {
|
|
103
|
+
--_sonic-groove:
|
|
104
|
+
inset var(--_sonic-cast-x) var(--_sonic-cast-y) calc(0.75 * var(--_sonic-unit))
|
|
105
|
+
rgb(0 0 0 / 55%),
|
|
106
|
+
inset 0 0 calc(0.5 * var(--_sonic-unit)) rgb(0 0 0 / 30%),
|
|
107
|
+
calc(0.5 * var(--_sonic-cast-x)) calc(0.5 * var(--_sonic-cast-y)) 0 rgb(255 255 255 / 6%);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@media (hover: hover) {
|
|
111
|
+
:is(.sonic-dial, .sonic-key, .sonic-segmented-segment, .sonic-slider):not(
|
|
112
|
+
:disabled,
|
|
113
|
+
[aria-disabled='true']
|
|
114
|
+
):hover {
|
|
115
|
+
--_sonic-cap: var(--_sonic-cap-hover);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
:is(.sonic-dial, .sonic-key, .sonic-segmented-segment, .sonic-slider):focus-visible {
|
|
120
|
+
--_sonic-cap: var(--_sonic-cap-hover);
|
|
121
|
+
--_sonic-focus-shadow: var(--_sonic-focus-glow);
|
|
122
|
+
|
|
123
|
+
/* Painted only in forced colours; inset so it adds no size */
|
|
124
|
+
outline: 2px solid transparent;
|
|
125
|
+
outline-offset: -2px;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* Keeps the glow while focus is in the entry; its own rule, since an engine without `:state()` drops a whole selector list */
|
|
129
|
+
:state(editing) > :is(.sonic-dial, .sonic-slider) {
|
|
130
|
+
--_sonic-cap: var(--_sonic-cap-hover);
|
|
131
|
+
--_sonic-focus-shadow: var(--_sonic-focus-glow);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
:is(.sonic-dial-readout, .sonic-slider-readout) {
|
|
135
|
+
--_sonic-readout-size: var(--sonic-readout-size, 1.5rem);
|
|
136
|
+
--_sonic-unit: calc(var(--_sonic-readout-size) / 32);
|
|
137
|
+
|
|
138
|
+
box-sizing: border-box;
|
|
139
|
+
display: grid;
|
|
140
|
+
place-items: center;
|
|
141
|
+
min-inline-size: calc(1.75 * var(--_sonic-readout-size));
|
|
142
|
+
block-size: var(--_sonic-readout-size);
|
|
143
|
+
padding: 0 calc(0.3 * var(--_sonic-readout-size));
|
|
144
|
+
margin: 0 0 calc(0.25 * var(--_sonic-readout-size));
|
|
145
|
+
position-area: block-start;
|
|
146
|
+
position-try-fallbacks: flip-block;
|
|
147
|
+
overflow: visible;
|
|
148
|
+
font: inherit;
|
|
149
|
+
font-size: calc(0.5 * var(--_sonic-readout-size));
|
|
150
|
+
font-variant-numeric: tabular-nums;
|
|
151
|
+
line-height: 1;
|
|
152
|
+
color: color-mix(in oklab, var(--_sonic-glass) 20%, #fff);
|
|
153
|
+
pointer-events: none;
|
|
154
|
+
cursor: default;
|
|
155
|
+
user-select: none;
|
|
156
|
+
background: radial-gradient(
|
|
157
|
+
closest-side,
|
|
158
|
+
color-mix(in oklab, var(--_sonic-glass) 88%, #fff),
|
|
159
|
+
var(--_sonic-glass)
|
|
160
|
+
)
|
|
161
|
+
var(--_sonic-glass);
|
|
162
|
+
border: 0;
|
|
163
|
+
border-radius: calc(0.25 * var(--_sonic-readout-size));
|
|
164
|
+
box-shadow:
|
|
165
|
+
calc(2 * var(--_sonic-cast-x)) calc(2 * var(--_sonic-cast-y)) calc(6 * var(--_sonic-unit))
|
|
166
|
+
rgb(0 0 0 / 55%),
|
|
167
|
+
inset 0 0 0 max(1px, var(--_sonic-unit)) rgb(255 255 255 / 8%);
|
|
168
|
+
opacity: 0;
|
|
169
|
+
translate: 0 calc(0.15 * var(--_sonic-readout-size));
|
|
170
|
+
transition:
|
|
171
|
+
opacity 120ms ease-out,
|
|
172
|
+
translate 120ms ease-out,
|
|
173
|
+
display 120ms allow-discrete,
|
|
174
|
+
overlay 120ms allow-discrete;
|
|
175
|
+
|
|
176
|
+
&:popover-open {
|
|
177
|
+
opacity: 1;
|
|
178
|
+
translate: 0;
|
|
179
|
+
|
|
180
|
+
@starting-style {
|
|
181
|
+
opacity: 0;
|
|
182
|
+
translate: 0 calc(0.15 * var(--_sonic-readout-size));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
@media (prefers-reduced-motion: reduce) {
|
|
187
|
+
translate: 0;
|
|
188
|
+
|
|
189
|
+
&:popover-open {
|
|
190
|
+
@starting-style {
|
|
191
|
+
translate: 0;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
:state(editing)
|
|
198
|
+
> :is(.sonic-dial, .sonic-slider)
|
|
199
|
+
> :is(.sonic-dial-readout, .sonic-slider-readout) {
|
|
200
|
+
pointer-events: auto;
|
|
201
|
+
cursor: text;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
:is(.sonic-dial-entry, .sonic-slider-entry) {
|
|
205
|
+
inline-size: 6ch;
|
|
206
|
+
padding: 0;
|
|
207
|
+
font: inherit;
|
|
208
|
+
color: inherit;
|
|
209
|
+
text-align: center;
|
|
210
|
+
background: transparent;
|
|
211
|
+
border: 0;
|
|
212
|
+
|
|
213
|
+
&::selection {
|
|
214
|
+
color: var(--_sonic-lit);
|
|
215
|
+
background: transparent;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
&:focus-visible {
|
|
219
|
+
outline: 2px solid transparent;
|
|
220
|
+
outline-offset: -2px;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
@supports (field-sizing: content) {
|
|
224
|
+
inline-size: auto;
|
|
225
|
+
min-inline-size: 3ch;
|
|
226
|
+
field-sizing: content;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/* The size is the thickness */
|
|
2
|
+
@layer sonic {
|
|
3
|
+
/* On the host too, for the placeholder */
|
|
4
|
+
:is(sonic-meter, .sonic-meter) {
|
|
5
|
+
--_sonic-meter-size: var(--sonic-meter-size, 0.5rem);
|
|
6
|
+
--_sonic-meter-length: var(--sonic-meter-length, 8rem);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
:has(> .sonic-meter):not([hidden]) {
|
|
10
|
+
display: contents;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
sonic-meter:not(:defined, [hidden]) {
|
|
14
|
+
display: block;
|
|
15
|
+
flex-shrink: 0;
|
|
16
|
+
inline-size: var(--_sonic-meter-size);
|
|
17
|
+
block-size: var(--_sonic-meter-length);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
sonic-meter[orientation='horizontal']:not(:defined, [hidden]) {
|
|
21
|
+
inline-size: var(--_sonic-meter-length);
|
|
22
|
+
block-size: var(--_sonic-meter-size);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* Laid on its side rather than rotated, as the slider is */
|
|
26
|
+
.sonic-meter {
|
|
27
|
+
/* Absolute, so a longer meter gains segments rather than stretching them */
|
|
28
|
+
--_sonic-meter-pitch: var(--sonic-meter-segment-pitch, 4px);
|
|
29
|
+
|
|
30
|
+
/* A fraction of the pitch */
|
|
31
|
+
--_sonic-meter-gap-ratio: var(--sonic-meter-gap-ratio, 0.25);
|
|
32
|
+
--_sonic-meter-radius-ratio: var(--sonic-meter-radius-ratio, 0.25);
|
|
33
|
+
|
|
34
|
+
/* A fraction of the light's length along the meter */
|
|
35
|
+
--_sonic-meter-segment-radius-ratio: var(--sonic-meter-segment-radius-ratio, 0.35);
|
|
36
|
+
|
|
37
|
+
/* dBFS */
|
|
38
|
+
--_sonic-meter-hot-from: var(--sonic-meter-hot-from, -12);
|
|
39
|
+
--_sonic-meter-clip-from: var(--sonic-meter-clip-from, -3);
|
|
40
|
+
--_sonic-unit: calc(var(--_sonic-meter-size) / 8);
|
|
41
|
+
--_sonic-meter-toward: to top;
|
|
42
|
+
--_sonic-meter-inset: calc(var(--_sonic-meter-size) * 0.125);
|
|
43
|
+
--_sonic-meter-lens: calc(var(--_sonic-meter-size) - 2 * var(--_sonic-meter-inset));
|
|
44
|
+
--_sonic-meter-gap: calc(var(--_sonic-meter-pitch) * var(--_sonic-meter-gap-ratio));
|
|
45
|
+
--_sonic-meter-segment: calc(var(--_sonic-meter-pitch) - var(--_sonic-meter-gap));
|
|
46
|
+
--_sonic-meter-segment-radius: calc(
|
|
47
|
+
var(--_sonic-meter-segment) * var(--_sonic-meter-segment-radius-ratio)
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
/* Whole segments only, so the last one ends flush */
|
|
51
|
+
--_sonic-meter-segments: round(
|
|
52
|
+
down,
|
|
53
|
+
var(--_sonic-meter-length) - 3 * var(--_sonic-meter-inset) - var(--_sonic-meter-lens) +
|
|
54
|
+
var(--_sonic-meter-gap),
|
|
55
|
+
var(--_sonic-meter-pitch)
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
position: relative;
|
|
59
|
+
flex-shrink: 0;
|
|
60
|
+
inline-size: var(--_sonic-meter-size);
|
|
61
|
+
block-size: var(--_sonic-meter-length);
|
|
62
|
+
background: color-mix(in oklab, var(--_sonic-unlit) 55%, #000);
|
|
63
|
+
border-radius: calc(var(--_sonic-meter-size) * var(--_sonic-meter-radius-ratio));
|
|
64
|
+
|
|
65
|
+
/* Over the segments, so the lip shades them too */
|
|
66
|
+
&::after {
|
|
67
|
+
position: absolute;
|
|
68
|
+
inset: 0;
|
|
69
|
+
pointer-events: none;
|
|
70
|
+
content: '';
|
|
71
|
+
border-radius: inherit;
|
|
72
|
+
box-shadow: var(--_sonic-groove);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
[orientation='horizontal'] > .sonic-meter {
|
|
77
|
+
--_sonic-meter-toward: to right;
|
|
78
|
+
|
|
79
|
+
inline-size: var(--_sonic-meter-length);
|
|
80
|
+
block-size: var(--_sonic-meter-size);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* Snapped to whole lights, so none is split between two colours */
|
|
84
|
+
:is(.sonic-meter-segments, .sonic-meter-level) {
|
|
85
|
+
--_sonic-meter-span: calc(var(--_sonic-meter-max, 0) - var(--_sonic-meter-min, -60));
|
|
86
|
+
--_sonic-meter-hot-at: round(
|
|
87
|
+
clamp(
|
|
88
|
+
0,
|
|
89
|
+
(var(--_sonic-meter-hot-from) - var(--_sonic-meter-min, -60)) / var(--_sonic-meter-span),
|
|
90
|
+
1
|
|
91
|
+
) *
|
|
92
|
+
var(--_sonic-meter-segments),
|
|
93
|
+
var(--_sonic-meter-pitch)
|
|
94
|
+
);
|
|
95
|
+
--_sonic-meter-clip-at: round(
|
|
96
|
+
clamp(
|
|
97
|
+
0,
|
|
98
|
+
(var(--_sonic-meter-clip-from) - var(--_sonic-meter-min, -60)) / var(--_sonic-meter-span),
|
|
99
|
+
1
|
|
100
|
+
) *
|
|
101
|
+
var(--_sonic-meter-segments),
|
|
102
|
+
var(--_sonic-meter-pitch)
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
/* Each rounded light is two crossed bands and four corner dots added together */
|
|
106
|
+
--_sonic-meter-lights:
|
|
107
|
+
linear-gradient(to top, #000 var(--_sonic-meter-segment), transparent 0)
|
|
108
|
+
var(--_sonic-meter-segment-radius) 100% /
|
|
109
|
+
calc(100% - 2 * var(--_sonic-meter-segment-radius)) var(--_sonic-meter-pitch) no-repeat
|
|
110
|
+
repeat,
|
|
111
|
+
linear-gradient(
|
|
112
|
+
to top,
|
|
113
|
+
transparent var(--_sonic-meter-segment-radius),
|
|
114
|
+
#000 0 calc(var(--_sonic-meter-segment) - var(--_sonic-meter-segment-radius)),
|
|
115
|
+
transparent 0
|
|
116
|
+
)
|
|
117
|
+
0 100% / 100% var(--_sonic-meter-pitch) no-repeat repeat,
|
|
118
|
+
radial-gradient(
|
|
119
|
+
circle var(--_sonic-meter-segment-radius) at left var(--_sonic-meter-segment-radius)
|
|
120
|
+
bottom var(--_sonic-meter-segment-radius),
|
|
121
|
+
#000 calc(100% - 0.5px),
|
|
122
|
+
transparent
|
|
123
|
+
)
|
|
124
|
+
0 100% / 100% var(--_sonic-meter-pitch) no-repeat repeat,
|
|
125
|
+
radial-gradient(
|
|
126
|
+
circle var(--_sonic-meter-segment-radius) at right var(--_sonic-meter-segment-radius)
|
|
127
|
+
bottom var(--_sonic-meter-segment-radius),
|
|
128
|
+
#000 calc(100% - 0.5px),
|
|
129
|
+
transparent
|
|
130
|
+
)
|
|
131
|
+
0 100% / 100% var(--_sonic-meter-pitch) no-repeat repeat,
|
|
132
|
+
radial-gradient(
|
|
133
|
+
circle var(--_sonic-meter-segment-radius) at left var(--_sonic-meter-segment-radius)
|
|
134
|
+
bottom calc(var(--_sonic-meter-segment) - var(--_sonic-meter-segment-radius)),
|
|
135
|
+
#000 calc(100% - 0.5px),
|
|
136
|
+
transparent
|
|
137
|
+
)
|
|
138
|
+
0 100% / 100% var(--_sonic-meter-pitch) no-repeat repeat,
|
|
139
|
+
radial-gradient(
|
|
140
|
+
circle var(--_sonic-meter-segment-radius) at right var(--_sonic-meter-segment-radius)
|
|
141
|
+
bottom calc(var(--_sonic-meter-segment) - var(--_sonic-meter-segment-radius)),
|
|
142
|
+
#000 calc(100% - 0.5px),
|
|
143
|
+
transparent
|
|
144
|
+
)
|
|
145
|
+
0 100% / 100% var(--_sonic-meter-pitch) no-repeat repeat;
|
|
146
|
+
|
|
147
|
+
position: absolute;
|
|
148
|
+
inset: auto var(--_sonic-meter-inset) var(--_sonic-meter-inset);
|
|
149
|
+
block-size: calc(var(--_sonic-meter-segments) - var(--_sonic-meter-gap));
|
|
150
|
+
mask: var(--_sonic-meter-lights);
|
|
151
|
+
mask-composite: add;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
[orientation='horizontal'] > .sonic-meter > :is(.sonic-meter-segments, .sonic-meter-level) {
|
|
155
|
+
--_sonic-meter-lights:
|
|
156
|
+
linear-gradient(to right, #000 var(--_sonic-meter-segment), transparent 0) 0
|
|
157
|
+
var(--_sonic-meter-segment-radius) / var(--_sonic-meter-pitch)
|
|
158
|
+
calc(100% - 2 * var(--_sonic-meter-segment-radius)) repeat no-repeat,
|
|
159
|
+
linear-gradient(
|
|
160
|
+
to right,
|
|
161
|
+
transparent var(--_sonic-meter-segment-radius),
|
|
162
|
+
#000 0 calc(var(--_sonic-meter-segment) - var(--_sonic-meter-segment-radius)),
|
|
163
|
+
transparent 0
|
|
164
|
+
)
|
|
165
|
+
0 0 / var(--_sonic-meter-pitch) 100% repeat no-repeat,
|
|
166
|
+
radial-gradient(
|
|
167
|
+
circle var(--_sonic-meter-segment-radius) at left var(--_sonic-meter-segment-radius) top
|
|
168
|
+
var(--_sonic-meter-segment-radius),
|
|
169
|
+
#000 calc(100% - 0.5px),
|
|
170
|
+
transparent
|
|
171
|
+
)
|
|
172
|
+
0 0 / var(--_sonic-meter-pitch) 100% repeat no-repeat,
|
|
173
|
+
radial-gradient(
|
|
174
|
+
circle var(--_sonic-meter-segment-radius) at left var(--_sonic-meter-segment-radius)
|
|
175
|
+
/* fallow-ignore-next-line code-duplication -- one gradient per segment corner is geometry, not copied logic */
|
|
176
|
+
bottom var(--_sonic-meter-segment-radius),
|
|
177
|
+
#000 calc(100% - 0.5px),
|
|
178
|
+
transparent
|
|
179
|
+
)
|
|
180
|
+
0 0 / var(--_sonic-meter-pitch) 100% repeat no-repeat,
|
|
181
|
+
radial-gradient(
|
|
182
|
+
circle var(--_sonic-meter-segment-radius) at left
|
|
183
|
+
calc(var(--_sonic-meter-segment) - var(--_sonic-meter-segment-radius)) top
|
|
184
|
+
var(--_sonic-meter-segment-radius),
|
|
185
|
+
#000 calc(100% - 0.5px),
|
|
186
|
+
transparent
|
|
187
|
+
)
|
|
188
|
+
0 0 / var(--_sonic-meter-pitch) 100% repeat no-repeat,
|
|
189
|
+
radial-gradient(
|
|
190
|
+
circle var(--_sonic-meter-segment-radius) at left
|
|
191
|
+
calc(var(--_sonic-meter-segment) - var(--_sonic-meter-segment-radius)) bottom
|
|
192
|
+
var(--_sonic-meter-segment-radius),
|
|
193
|
+
#000 calc(100% - 0.5px),
|
|
194
|
+
transparent
|
|
195
|
+
)
|
|
196
|
+
0 0 / var(--_sonic-meter-pitch) 100% repeat no-repeat;
|
|
197
|
+
|
|
198
|
+
inset: var(--_sonic-meter-inset) auto var(--_sonic-meter-inset) var(--_sonic-meter-inset);
|
|
199
|
+
inline-size: calc(var(--_sonic-meter-segments) - var(--_sonic-meter-gap));
|
|
200
|
+
block-size: auto;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.sonic-meter-segments {
|
|
204
|
+
background: linear-gradient(
|
|
205
|
+
var(--_sonic-meter-toward),
|
|
206
|
+
color-mix(in oklab, var(--_sonic-lit) 12%, var(--_sonic-unlit)) 0 var(--_sonic-meter-hot-at),
|
|
207
|
+
color-mix(in oklab, var(--_sonic-hot) 12%, var(--_sonic-unlit)) 0 var(--_sonic-meter-clip-at),
|
|
208
|
+
color-mix(in oklab, var(--_sonic-clip) 12%, var(--_sonic-unlit)) 0
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* One mask for the bar and peak, so a frame repaints without layout */
|
|
213
|
+
.sonic-meter-level {
|
|
214
|
+
--_sonic-meter-lit: round(
|
|
215
|
+
down,
|
|
216
|
+
var(--_sonic-meter-level, 0) * var(--_sonic-meter-segments),
|
|
217
|
+
var(--_sonic-meter-pitch)
|
|
218
|
+
);
|
|
219
|
+
--_sonic-meter-held: round(
|
|
220
|
+
down,
|
|
221
|
+
var(--_sonic-meter-peak, 0) * var(--_sonic-meter-segments),
|
|
222
|
+
var(--_sonic-meter-pitch)
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
background: linear-gradient(
|
|
226
|
+
var(--_sonic-meter-toward),
|
|
227
|
+
var(--_sonic-lit) 0 var(--_sonic-meter-hot-at),
|
|
228
|
+
var(--_sonic-hot) 0 var(--_sonic-meter-clip-at),
|
|
229
|
+
var(--_sonic-clip) 0
|
|
230
|
+
);
|
|
231
|
+
mask:
|
|
232
|
+
linear-gradient(
|
|
233
|
+
var(--_sonic-meter-toward),
|
|
234
|
+
#000 var(--_sonic-meter-lit),
|
|
235
|
+
transparent 0 calc(var(--_sonic-meter-held) - var(--_sonic-meter-pitch)),
|
|
236
|
+
#000 0 var(--_sonic-meter-held),
|
|
237
|
+
transparent 0
|
|
238
|
+
),
|
|
239
|
+
var(--_sonic-meter-lights);
|
|
240
|
+
mask-composite: intersect, add, add, add, add, add, add;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.sonic-meter-clip {
|
|
244
|
+
position: absolute;
|
|
245
|
+
inset: var(--_sonic-meter-inset) var(--_sonic-meter-inset) auto;
|
|
246
|
+
block-size: var(--_sonic-meter-lens);
|
|
247
|
+
background:
|
|
248
|
+
linear-gradient(var(--_sonic-light-fall), rgb(255 255 255 / 10%), transparent 60%),
|
|
249
|
+
color-mix(in oklab, var(--_sonic-unlit) 60%, #000);
|
|
250
|
+
border-radius: 50%;
|
|
251
|
+
|
|
252
|
+
&::after {
|
|
253
|
+
position: absolute;
|
|
254
|
+
inset: 0;
|
|
255
|
+
content: '';
|
|
256
|
+
background: radial-gradient(
|
|
257
|
+
closest-side,
|
|
258
|
+
color-mix(in oklab, var(--_sonic-clip) 55%, #fff),
|
|
259
|
+
var(--_sonic-clip)
|
|
260
|
+
);
|
|
261
|
+
border-radius: inherit;
|
|
262
|
+
opacity: var(--_sonic-meter-clipped, 0);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
[orientation='horizontal'] > .sonic-meter > .sonic-meter-clip {
|
|
267
|
+
inset: var(--_sonic-meter-inset) var(--_sonic-meter-inset) var(--_sonic-meter-inset) auto;
|
|
268
|
+
inline-size: var(--_sonic-meter-lens);
|
|
269
|
+
block-size: auto;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
@layer sonic {
|
|
2
|
+
.sonic-screen {
|
|
3
|
+
--_sonic-screen-size: var(--sonic-screen-size, 2.5rem);
|
|
4
|
+
--_sonic-screen-aspect-ratio: var(--sonic-screen-aspect-ratio, 3);
|
|
5
|
+
--_sonic-screen-radius-ratio: var(--sonic-screen-radius-ratio, 0.15);
|
|
6
|
+
--_sonic-screen-inset-ratio: var(--sonic-screen-inset-ratio, 0.1);
|
|
7
|
+
--_sonic-unit: calc(var(--_sonic-screen-size) / 32);
|
|
8
|
+
|
|
9
|
+
position: relative;
|
|
10
|
+
box-sizing: border-box;
|
|
11
|
+
display: grid;
|
|
12
|
+
flex-shrink: 0;
|
|
13
|
+
|
|
14
|
+
/* A definite cell, so percentage-sized content fills the glass; a canvas sized to its own box would feed back */
|
|
15
|
+
grid-template: minmax(0, 1fr) / minmax(0, 1fr);
|
|
16
|
+
place-items: center;
|
|
17
|
+
inline-size: calc(var(--_sonic-screen-size) * var(--_sonic-screen-aspect-ratio));
|
|
18
|
+
block-size: var(--_sonic-screen-size);
|
|
19
|
+
padding: calc(var(--_sonic-screen-size) * var(--_sonic-screen-inset-ratio));
|
|
20
|
+
|
|
21
|
+
/* Not the cap's ink, which a skin may darken */
|
|
22
|
+
color: color-mix(in oklab, var(--_sonic-glass) 20%, #fff);
|
|
23
|
+
|
|
24
|
+
/* A double click aimed at a neighbouring control would highlight it */
|
|
25
|
+
user-select: none;
|
|
26
|
+
background: radial-gradient(
|
|
27
|
+
closest-side,
|
|
28
|
+
color-mix(in oklab, var(--_sonic-glass) 88%, #fff),
|
|
29
|
+
var(--_sonic-glass)
|
|
30
|
+
)
|
|
31
|
+
var(--_sonic-glass);
|
|
32
|
+
border-radius: calc(var(--_sonic-screen-size) * var(--_sonic-screen-radius-ratio));
|
|
33
|
+
|
|
34
|
+
/* Over the content, so the lip shades the picture too */
|
|
35
|
+
&::after {
|
|
36
|
+
position: absolute;
|
|
37
|
+
inset: 0;
|
|
38
|
+
pointer-events: none;
|
|
39
|
+
content: '';
|
|
40
|
+
background: linear-gradient(var(--_sonic-light-fall), rgb(255 255 255 / 7%), transparent 45%);
|
|
41
|
+
border-radius: inherit;
|
|
42
|
+
box-shadow:
|
|
43
|
+
var(--_sonic-well),
|
|
44
|
+
inset 0 0 calc(3 * var(--_sonic-unit)) rgb(0 0 0 / 45%);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|