@xsynaptic/sonic-ui 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.
Files changed (128) hide show
  1. package/README.md +8 -1
  2. package/dist/default/define/dial.js +7 -0
  3. package/dist/default/define/key.js +7 -0
  4. package/dist/default/define/meter.js +7 -0
  5. package/dist/default/define/number.js +7 -0
  6. package/dist/default/define/segmented.js +7 -0
  7. package/dist/default/define/slider.js +7 -0
  8. package/dist/{define.js → default/define.js} +1 -0
  9. package/dist/default/elements/define-once.js +7 -0
  10. package/dist/default/elements/dial.js +81 -0
  11. package/dist/default/elements/form-element.js +56 -0
  12. package/dist/default/elements/key.js +144 -0
  13. package/dist/{elements → default/elements}/meter.js +27 -16
  14. package/dist/default/elements/number.js +46 -0
  15. package/dist/default/elements/range-element.js +433 -0
  16. package/dist/{elements → default/elements}/range-entry.js +5 -4
  17. package/dist/default/elements/segmented.js +173 -0
  18. package/dist/default/elements/slider.js +99 -0
  19. package/dist/default/elements/sonic-element.js +153 -0
  20. package/dist/{index.js → default/index.js} +3 -1
  21. package/dist/default/lib/copy-node.js +30 -0
  22. package/dist/default/lib/percent.js +12 -0
  23. package/dist/default/lib/read-px-property.js +8 -0
  24. package/dist/{lib → default/lib}/render.js +9 -2
  25. package/dist/{lib → default/lib}/taper.js +4 -2
  26. package/dist/default/lib/write-attribute.js +11 -0
  27. package/dist/dev/define/dial.js.map +1 -0
  28. package/dist/dev/define/key.js.map +1 -0
  29. package/dist/dev/define/meter.js.map +1 -0
  30. package/dist/dev/define/number.d.ts +2 -0
  31. package/dist/dev/define/number.js +8 -0
  32. package/dist/dev/define/number.js.map +1 -0
  33. package/dist/dev/define/segmented.js.map +1 -0
  34. package/dist/dev/define/slider.js.map +1 -0
  35. package/dist/{define.d.ts → dev/define.d.ts} +3 -1
  36. package/dist/dev/define.js +6 -0
  37. package/dist/dev/elements/define-once.js.map +1 -0
  38. package/dist/{elements → dev/elements}/dial.d.ts +7 -0
  39. package/dist/{elements → dev/elements}/dial.js +31 -6
  40. package/dist/dev/elements/dial.js.map +1 -0
  41. package/dist/dev/elements/form-element.d.ts +22 -0
  42. package/dist/dev/elements/form-element.js +57 -0
  43. package/dist/dev/elements/form-element.js.map +1 -0
  44. package/dist/dev/elements/key.d.ts +30 -0
  45. package/dist/{elements → dev/elements}/key.js +71 -25
  46. package/dist/dev/elements/key.js.map +1 -0
  47. package/dist/{elements → dev/elements}/meter.d.ts +6 -0
  48. package/dist/dev/elements/meter.js +131 -0
  49. package/dist/dev/elements/meter.js.map +1 -0
  50. package/dist/dev/elements/number.d.ts +16 -0
  51. package/dist/dev/elements/number.js +47 -0
  52. package/dist/dev/elements/number.js.map +1 -0
  53. package/dist/dev/elements/range-element.d.ts +56 -0
  54. package/dist/{elements → dev/elements}/range-element.js +153 -67
  55. package/dist/dev/elements/range-element.js.map +1 -0
  56. package/dist/dev/elements/range-entry.js +75 -0
  57. package/dist/dev/elements/range-entry.js.map +1 -0
  58. package/dist/{elements → dev/elements}/segmented.d.ts +5 -2
  59. package/dist/dev/elements/segmented.js +174 -0
  60. package/dist/dev/elements/segmented.js.map +1 -0
  61. package/dist/{elements → dev/elements}/slider.d.ts +8 -0
  62. package/dist/{elements → dev/elements}/slider.js +37 -7
  63. package/dist/dev/elements/slider.js.map +1 -0
  64. package/dist/dev/elements/sonic-element.d.ts +33 -0
  65. package/dist/dev/elements/sonic-element.js +169 -0
  66. package/dist/dev/elements/sonic-element.js.map +1 -0
  67. package/dist/{index.d.ts → dev/index.d.ts} +3 -1
  68. package/dist/dev/index.js +9 -0
  69. package/dist/dev/lib/copy-node.js +31 -0
  70. package/dist/dev/lib/copy-node.js.map +1 -0
  71. package/dist/dev/lib/percent.d.ts +5 -0
  72. package/dist/dev/lib/percent.js +13 -0
  73. package/dist/dev/lib/percent.js.map +1 -0
  74. package/dist/dev/lib/read-px-property.js.map +1 -0
  75. package/dist/dev/lib/render.js +27 -0
  76. package/dist/dev/lib/render.js.map +1 -0
  77. package/dist/dev/lib/taper.js +36 -0
  78. package/dist/dev/lib/taper.js.map +1 -0
  79. package/dist/dev/lib/write-attribute.js.map +1 -0
  80. package/dist/skins/ivory.css +3 -3
  81. package/dist/skins/slate.css +4 -4
  82. package/dist/styles/controls.css +2 -1
  83. package/dist/styles/dial.css +70 -16
  84. package/dist/styles/key.css +71 -11
  85. package/dist/styles/led.css +75 -0
  86. package/dist/styles/material.css +154 -25
  87. package/dist/styles/meter.css +44 -8
  88. package/dist/styles/number.css +81 -0
  89. package/dist/styles/screen.css +6 -33
  90. package/dist/styles/segmented.css +45 -7
  91. package/dist/styles/slider.css +68 -4
  92. package/package.json +16 -13
  93. package/dist/define/dial.js.map +0 -1
  94. package/dist/define/key.js.map +0 -1
  95. package/dist/define/meter.js.map +0 -1
  96. package/dist/define/segmented.js.map +0 -1
  97. package/dist/define/slider.js.map +0 -1
  98. package/dist/elements/define-once.js.map +0 -1
  99. package/dist/elements/dial.js.map +0 -1
  100. package/dist/elements/key.d.ts +0 -17
  101. package/dist/elements/key.js.map +0 -1
  102. package/dist/elements/meter.js.map +0 -1
  103. package/dist/elements/range-element.d.ts +0 -32
  104. package/dist/elements/range-element.js.map +0 -1
  105. package/dist/elements/range-entry.js.map +0 -1
  106. package/dist/elements/segmented.js +0 -104
  107. package/dist/elements/segmented.js.map +0 -1
  108. package/dist/elements/slider.js.map +0 -1
  109. package/dist/elements/sonic-element.d.ts +0 -20
  110. package/dist/elements/sonic-element.js +0 -94
  111. package/dist/elements/sonic-element.js.map +0 -1
  112. package/dist/lib/read-px-property.js.map +0 -1
  113. package/dist/lib/render.js.map +0 -1
  114. package/dist/lib/taper.js.map +0 -1
  115. package/dist/lib/write-attribute.js.map +0 -1
  116. /package/dist/{define → dev/define}/dial.d.ts +0 -0
  117. /package/dist/{define → dev/define}/dial.js +0 -0
  118. /package/dist/{define → dev/define}/key.d.ts +0 -0
  119. /package/dist/{define → dev/define}/key.js +0 -0
  120. /package/dist/{define → dev/define}/meter.d.ts +0 -0
  121. /package/dist/{define → dev/define}/meter.js +0 -0
  122. /package/dist/{define → dev/define}/segmented.d.ts +0 -0
  123. /package/dist/{define → dev/define}/segmented.js +0 -0
  124. /package/dist/{define → dev/define}/slider.d.ts +0 -0
  125. /package/dist/{define → dev/define}/slider.js +0 -0
  126. /package/dist/{elements → dev/elements}/define-once.js +0 -0
  127. /package/dist/{lib → dev/lib}/read-px-property.js +0 -0
  128. /package/dist/{lib → dev/lib}/write-attribute.js +0 -0
@@ -0,0 +1,27 @@
1
+ //#region src/lib/render.ts
2
+ function requireChild(root, selector, child) {
3
+ const found = root.querySelector(selector);
4
+ if (!(found instanceof child)) throw new Error(`A template is missing its ${selector}`);
5
+ return found;
6
+ }
7
+ function template(html, root) {
8
+ const parsed = document.createElement("template");
9
+ parsed.innerHTML = html;
10
+ return () => {
11
+ const clone = document.importNode(parsed.content, true).firstElementChild;
12
+ if (!(clone instanceof root)) throw new Error(`A template's root is not a ${root.name}`);
13
+ return clone;
14
+ };
15
+ }
16
+ function attachSlot(host) {
17
+ const slot = document.createElement("slot");
18
+ host.attachShadow({
19
+ mode: "open",
20
+ slotAssignment: "manual"
21
+ }).append(slot);
22
+ return slot;
23
+ }
24
+
25
+ //#endregion
26
+ export { attachSlot, requireChild, template };
27
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","names":[],"sources":["../../../src/lib/render.ts"],"sourcesContent":["export function requireChild<Child extends Element>(\n\troot: Element,\n\tselector: string,\n\tchild: new () => Child,\n): Child {\n\tconst found = root.querySelector(selector);\n\tif (!(found instanceof child)) throw new Error(`A template is missing its ${selector}`);\n\n\treturn found;\n}\n\nexport function template<Root extends Element>(html: string, root: new () => Root): () => Root {\n\tconst parsed = document.createElement('template');\n\n\tparsed.innerHTML = html;\n\n\treturn () => {\n\t\tconst clone = document.importNode(parsed.content, true).firstElementChild;\n\t\tif (!(clone instanceof root)) throw new Error(`A template's root is not a ${root.name}`);\n\n\t\treturn clone;\n\t};\n}\n\n// Only the nodes the element assigns render, so a framework's own children stay put, bare text included\nexport function attachSlot(host: HTMLElement): HTMLSlotElement {\n\tconst slot = document.createElement('slot');\n\n\thost.attachShadow({ mode: 'open', slotAssignment: 'manual' }).append(slot);\n\n\treturn slot;\n}\n"],"mappings":";AAAA,SAAgB,aACf,MACA,UACA,OACQ;CACR,MAAM,QAAQ,KAAK,cAAc,QAAQ;CACzC,IAAI,EAAE,iBAAiB,QAAQ,MAAM,IAAI,MAAM,6BAA6B,UAAU;CAEtF,OAAO;AACR;AAEA,SAAgB,SAA+B,MAAc,MAAkC;CAC9F,MAAM,SAAS,SAAS,cAAc,UAAU;CAEhD,OAAO,YAAY;CAEnB,aAAa;EACZ,MAAM,QAAQ,SAAS,WAAW,OAAO,SAAS,IAAI,CAAC,CAAC;EACxD,IAAI,EAAE,iBAAiB,OAAO,MAAM,IAAI,MAAM,8BAA8B,KAAK,MAAM;EAEvF,OAAO;CACR;AACD;AAGA,SAAgB,WAAW,MAAoC;CAC9D,MAAM,OAAO,SAAS,cAAc,MAAM;CAE1C,KAAK,aAAa;EAAE,MAAM;EAAQ,gBAAgB;CAAS,CAAC,CAAC,CAAC,OAAO,IAAI;CAEzE,OAAO;AACR"}
@@ -0,0 +1,36 @@
1
+ //#region src/lib/taper.ts
2
+ function clampUnit(fraction) {
3
+ return Math.min(1, Math.max(0, fraction));
4
+ }
5
+ function wrapUnit(fraction) {
6
+ return fraction - Math.floor(fraction);
7
+ }
8
+ function linearTaper(min, max) {
9
+ const range = max - min;
10
+ return {
11
+ position: (value) => range > 0 ? clampUnit((value - min) / range) : 0,
12
+ value: (position) => min + clampUnit(position) * range
13
+ };
14
+ }
15
+ function logTaper(min, max) {
16
+ if (min <= 0 || max <= min) return void 0;
17
+ const span = Math.log(max / min);
18
+ return {
19
+ position: (value) => clampUnit(Math.log(Math.max(min, value) / min) / span),
20
+ value: (position) => min * Math.exp(clampUnit(position) * span)
21
+ };
22
+ }
23
+ function skewTaper(min, max, midpoint) {
24
+ const range = max - min;
25
+ const proportion = (midpoint - min) / range;
26
+ if (proportion === .5 || !(proportion > 0 && proportion < 1)) return void 0;
27
+ const skew = Math.log(.5) / Math.log(proportion);
28
+ return {
29
+ position: (value) => clampUnit((value - min) / range) ** skew,
30
+ value: (position) => min + range * clampUnit(position) ** (1 / skew)
31
+ };
32
+ }
33
+
34
+ //#endregion
35
+ export { clampUnit, linearTaper, logTaper, skewTaper, wrapUnit };
36
+ //# sourceMappingURL=taper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"taper.js","names":[],"sources":["../../../src/lib/taper.ts"],"sourcesContent":["// Positions run 0 to 1 along the travel\nexport interface Taper {\n\tposition: (value: number) => number;\n\tvalue: (position: number) => number;\n}\n\nexport function clampUnit(fraction: number): number {\n\treturn Math.min(1, Math.max(0, fraction));\n}\n\nexport function wrapUnit(fraction: number): number {\n\treturn fraction - Math.floor(fraction);\n}\n\nexport function linearTaper(min: number, max: number): Taper {\n\tconst range = max - min;\n\n\treturn {\n\t\tposition: (value) => (range > 0 ? clampUnit((value - min) / range) : 0),\n\t\tvalue: (position) => min + clampUnit(position) * range,\n\t};\n}\n\nexport function logTaper(min: number, max: number): Taper | undefined {\n\tif (min <= 0 || max <= min) return undefined;\n\n\tconst span = Math.log(max / min);\n\n\treturn {\n\t\tposition: (value) => clampUnit(Math.log(Math.max(min, value) / min) / span),\n\t\tvalue: (position) => min * Math.exp(clampUnit(position) * span),\n\t};\n}\n\n// JUCE's skew: a power curve through `midpoint` at half the travel\nexport function skewTaper(min: number, max: number, midpoint: number): Taper | undefined {\n\tconst range = max - min;\n\tconst proportion = (midpoint - min) / range;\n\n\tif (proportion === 0.5 || !(proportion > 0 && proportion < 1)) return undefined;\n\n\tconst skew = Math.log(0.5) / Math.log(proportion);\n\n\treturn {\n\t\tposition: (value) => clampUnit((value - min) / range) ** skew,\n\t\tvalue: (position) => min + range * clampUnit(position) ** (1 / skew),\n\t};\n}\n"],"mappings":";AAMA,SAAgB,UAAU,UAA0B;CACnD,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,QAAQ,CAAC;AACzC;AAEA,SAAgB,SAAS,UAA0B;CAClD,OAAO,WAAW,KAAK,MAAM,QAAQ;AACtC;AAEA,SAAgB,YAAY,KAAa,KAAoB;CAC5D,MAAM,QAAQ,MAAM;CAEpB,OAAO;EACN,WAAW,UAAW,QAAQ,IAAI,WAAW,QAAQ,OAAO,KAAK,IAAI;EACrE,QAAQ,aAAa,MAAM,UAAU,QAAQ,IAAI;CAClD;AACD;AAEA,SAAgB,SAAS,KAAa,KAAgC;CACrE,IAAI,OAAO,KAAK,OAAO,KAAK,OAAO;CAEnC,MAAM,OAAO,KAAK,IAAI,MAAM,GAAG;CAE/B,OAAO;EACN,WAAW,UAAU,UAAU,KAAK,IAAI,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,IAAI,IAAI;EAC1E,QAAQ,aAAa,MAAM,KAAK,IAAI,UAAU,QAAQ,IAAI,IAAI;CAC/D;AACD;AAGA,SAAgB,UAAU,KAAa,KAAa,UAAqC;CACxF,MAAM,QAAQ,MAAM;CACpB,MAAM,cAAc,WAAW,OAAO;CAEtC,IAAI,eAAe,MAAO,EAAE,aAAa,KAAK,aAAa,IAAI,OAAO;CAEtE,MAAM,OAAO,KAAK,IAAI,EAAG,IAAI,KAAK,IAAI,UAAU;CAEhD,OAAO;EACN,WAAW,UAAU,WAAW,QAAQ,OAAO,KAAK,KAAK;EACzD,QAAQ,aAAa,MAAM,QAAQ,UAAU,QAAQ,MAAM,IAAI;CAChE;AACD"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write-attribute.js","names":[],"sources":["../../../src/lib/write-attribute.ts"],"sourcesContent":["export function writeAttribute(element: Element, name: string, value: string | undefined): void {\n\tif (value === undefined) {\n\t\telement.removeAttribute(name);\n\t\treturn;\n\t}\n\n\telement.setAttribute(name, value);\n}\n"],"mappings":";AAAA,SAAgB,eAAe,SAAkB,MAAc,OAAiC;CAC/F,IAAI,UAAU,QAAW;EACxB,QAAQ,gBAAgB,IAAI;EAC5B;CACD;CAEA,QAAQ,aAAa,MAAM,KAAK;AACjC"}
@@ -1,8 +1,8 @@
1
1
  /* Ink derived from a pale cap would vanish against it */
2
2
  @layer sonic {
3
3
  .sonic-skin-ivory {
4
- --sonic-cap: oklch(85.5% 0.0281 215deg);
5
- --sonic-cap-hover: oklch(90.5% 0.024 215deg);
6
- --sonic-mark: oklch(18.5% 0.0099 237deg);
4
+ --sonic-cap: oklch(85.5% 0.024 85deg);
5
+ --sonic-cap-hover: oklch(90.5% 0.02 85deg);
6
+ --sonic-mark: oklch(18.5% 0.012 60deg);
7
7
  }
8
8
  }
@@ -1,9 +1,9 @@
1
1
  @layer sonic {
2
2
  .sonic-skin-slate {
3
- --sonic-lit: #22d3ee;
4
- --sonic-unlit: #1e293b;
5
- --sonic-cap: #334155;
6
- --sonic-cap-hover: #475569;
3
+ --sonic-lit: oklch(79.7% 0.134 212deg);
4
+ --sonic-unlit: oklch(27.9% 0.037 260deg);
5
+ --sonic-cap: oklch(37.2% 0.039 257deg);
6
+ --sonic-cap-hover: oklch(44.6% 0.037 257deg);
7
7
  --sonic-dial-ring-ratio: 0.12;
8
8
  --sonic-dial-gap-ratio: 0.07;
9
9
  }
@@ -1,8 +1,9 @@
1
- /* The material first; every control's sheet reads it */
2
1
  @import './material.css';
3
2
  @import './dial.css';
4
3
  @import './key.css';
4
+ @import './led.css';
5
5
  @import './meter.css';
6
+ @import './number.css';
6
7
  @import './screen.css';
7
8
  @import './segmented.css';
8
9
  @import './slider.css';
@@ -1,5 +1,22 @@
1
+ @property --_sonic-dial-size {
2
+ syntax: '<length>';
3
+ inherits: true;
4
+ initial-value: 40px;
5
+ }
6
+
7
+ @property --_sonic-dial-ridge-pitch {
8
+ syntax: '<length>';
9
+ inherits: true;
10
+ initial-value: 4px;
11
+ }
12
+
13
+ @property --_sonic-dial-travel {
14
+ syntax: '<length>';
15
+ inherits: false;
16
+ initial-value: 160px;
17
+ }
18
+
1
19
  @layer sonic {
2
- /* On the host too, for the placeholder */
3
20
  :is(sonic-dial, .sonic-dial) {
4
21
  --_sonic-dial-size: var(--sonic-dial-size, 2.5rem);
5
22
  }
@@ -29,15 +46,13 @@
29
46
 
30
47
  /* Centred on six o'clock */
31
48
  --_sonic-dial-sweep: var(--sonic-dial-sweep, 300deg);
32
-
33
- /* Absolute, so a larger knob gets more ridges rather than wider ones */
49
+ --_sonic-dial-segment: var(--sonic-dial-segment, 30deg);
34
50
  --_sonic-dial-ridge-pitch: var(--sonic-dial-ridge-pitch, 4px);
35
-
36
- /* Read by the element as px */
37
51
  --_sonic-dial-travel: var(--sonic-dial-travel, 160px);
38
52
  --_sonic-unit: calc(var(--_sonic-dial-size) / 40);
39
53
  --_sonic-dial-lit: var(--_sonic-lit);
40
54
  --_sonic-dial-notch-band: 0px;
55
+ --_sonic-dial-notch-ends: 1;
41
56
  --_sonic-dial-start: calc(180deg + (1turn - var(--_sonic-dial-sweep)) / 2);
42
57
  --_sonic-dial-angle: calc(var(--_sonic-dial-value, 0) * var(--_sonic-dial-sweep));
43
58
  --_sonic-dial-ring-width: calc(var(--_sonic-dial-size) * var(--_sonic-dial-ring-ratio));
@@ -75,6 +90,11 @@
75
90
  --_sonic-cap: var(--_sonic-cap-hover);
76
91
  }
77
92
 
93
+ [endless] > .sonic-dial {
94
+ --_sonic-dial-sweep: 1turn;
95
+ --_sonic-dial-notch-ends: 0;
96
+ }
97
+
78
98
  [notched] > .sonic-dial {
79
99
  --_sonic-dial-notch-band: calc(var(--_sonic-dial-size) * var(--_sonic-dial-notch-ratio));
80
100
  }
@@ -84,7 +104,7 @@
84
104
  --_sonic-dial-lit: var(--_sonic-lit-off);
85
105
  }
86
106
 
87
- [disabled] > .sonic-dial {
107
+ :disabled > .sonic-dial {
88
108
  --_sonic-dial-lit: var(--_sonic-lit-disabled);
89
109
 
90
110
  cursor: default;
@@ -93,7 +113,7 @@
93
113
  .sonic-dial-notches {
94
114
  --_sonic-dial-notch-width: atan2(var(--_sonic-unit), var(--_sonic-dial-size) / 2);
95
115
  --_sonic-dial-notch-pitch: calc(
96
- var(--_sonic-dial-sweep) / (var(--_sonic-dial-positions, 2) - 1)
116
+ var(--_sonic-dial-sweep) / (var(--_sonic-dial-positions, 2) - var(--_sonic-dial-notch-ends))
97
117
  );
98
118
 
99
119
  position: absolute;
@@ -140,14 +160,15 @@
140
160
  --_sonic-dial-lit-to: calc(
141
161
  max(var(--_sonic-dial-origin, 0), var(--_sonic-dial-value, 0)) * var(--_sonic-dial-sweep)
142
162
  );
163
+ --_sonic-dial-ring-shade: radial-gradient(
164
+ closest-side,
165
+ rgb(0 0 0 / 55%) calc(100% - var(--_sonic-dial-ring-width)),
166
+ transparent calc(100% - var(--_sonic-dial-ring-width) * 0.4),
167
+ rgb(0 0 0 / 25%) 100%
168
+ );
143
169
 
144
170
  background:
145
- radial-gradient(
146
- closest-side,
147
- rgb(0 0 0 / 55%) calc(100% - var(--_sonic-dial-ring-width)),
148
- transparent calc(100% - var(--_sonic-dial-ring-width) * 0.4),
149
- rgb(0 0 0 / 25%) 100%
150
- ),
171
+ var(--_sonic-dial-ring-shade),
151
172
  conic-gradient(
152
173
  from var(--_sonic-dial-start),
153
174
  var(--_sonic-unlit) var(--_sonic-dial-lit-from),
@@ -157,6 +178,17 @@
157
178
  );
158
179
  }
159
180
 
181
+ [endless] > .sonic-dial > .sonic-dial-ring {
182
+ background:
183
+ var(--_sonic-dial-ring-shade),
184
+ conic-gradient(
185
+ from
186
+ calc(var(--_sonic-dial-start) + var(--_sonic-dial-angle) - var(--_sonic-dial-segment) / 2),
187
+ var(--_sonic-dial-lit) var(--_sonic-dial-segment),
188
+ var(--_sonic-unlit) 0
189
+ );
190
+ }
191
+
160
192
  .sonic-dial-modulation {
161
193
  --_sonic-dial-modulation: var(
162
194
  --sonic-modulation,
@@ -185,7 +217,7 @@
185
217
  border-radius: 50%;
186
218
  box-shadow:
187
219
  calc(2 * var(--_sonic-cast-x)) calc(2 * var(--_sonic-cast-y)) calc(4 * var(--_sonic-unit))
188
- rgb(0 0 0 / 65%),
220
+ var(--_sonic-drop-shade),
189
221
  var(--_sonic-rim),
190
222
  var(--_sonic-focus-shadow);
191
223
 
@@ -236,7 +268,6 @@
236
268
  border-radius: 50%;
237
269
  rotate: calc(var(--_sonic-dial-start) + var(--_sonic-dial-angle));
238
270
 
239
- /* Evenly shaded, since any offset would turn with the knob */
240
271
  &::before {
241
272
  --_sonic-dial-pointer-width: calc(var(--_sonic-dial-size) * var(--_sonic-dial-pointer-ratio));
242
273
 
@@ -259,7 +290,30 @@
259
290
  }
260
291
  }
261
292
 
262
- [disabled] > .sonic-dial > .sonic-dial-pointer::before {
293
+ :disabled > .sonic-dial > .sonic-dial-pointer::before {
263
294
  background: var(--_sonic-etch);
264
295
  }
296
+
297
+ @media (forced-colors: active) {
298
+ :is(.sonic-dial-notches, .sonic-dial-ring, .sonic-dial-modulation, .sonic-dial-pointer) {
299
+ forced-color-adjust: none;
300
+ }
301
+
302
+ .sonic-dial-ring {
303
+ --_sonic-dial-ring-shade: none;
304
+ }
305
+
306
+ .sonic-dial-modulation {
307
+ --_sonic-dial-modulation: CanvasText;
308
+ }
309
+
310
+ .sonic-dial-cap::before,
311
+ .sonic-dial-cap::after {
312
+ display: none;
313
+ }
314
+
315
+ .sonic-dial-pointer::before {
316
+ box-shadow: none;
317
+ }
318
+ }
265
319
  }
@@ -1,5 +1,10 @@
1
+ @property --_sonic-key-size {
2
+ syntax: '<length>';
3
+ inherits: true;
4
+ initial-value: 32px;
5
+ }
6
+
1
7
  @layer sonic {
2
- /* On the host too, for the placeholder */
3
8
  :is(sonic-key, .sonic-key) {
4
9
  --_sonic-key-size: var(--sonic-key-size, 2rem);
5
10
  }
@@ -8,7 +13,6 @@
8
13
  display: contents;
9
14
  }
10
15
 
11
- /* Hides the icon, which has no size before upgrade */
12
16
  sonic-key:not(:defined, [hidden]) {
13
17
  display: block;
14
18
  visibility: hidden;
@@ -18,13 +22,14 @@
18
22
  overflow: hidden;
19
23
  }
20
24
 
21
- /* The button is the well; the cap is the part that moves */
22
25
  .sonic-key {
23
26
  --_sonic-key-gap-ratio: var(--sonic-key-gap-ratio, 0);
24
27
  --_sonic-key-radius-ratio: var(--sonic-key-radius-ratio, 0.2);
25
28
  --_sonic-key-bevel-ratio: var(--sonic-key-bevel-ratio, 0.075);
26
29
  --_sonic-key-face-radius-ratio: var(--sonic-key-face-radius-ratio, 0.15);
27
30
  --_sonic-key-icon-ratio: var(--sonic-key-icon-ratio, 0.5);
31
+ --_sonic-key-led-ratio: var(--sonic-key-led-ratio, 0.2);
32
+ --_sonic-key-led-icon-ratio: var(--sonic-key-led-icon-ratio, 0.35);
28
33
  --_sonic-key-depth-ratio: var(--sonic-key-depth-ratio, 0.03);
29
34
  --_sonic-key-press-scale: var(--sonic-key-press-scale, 0.985);
30
35
  --_sonic-unit: calc(var(--_sonic-key-size) / 32);
@@ -34,7 +39,7 @@
34
39
  --_sonic-key-shift: calc(var(--_sonic-key-depth) / -2);
35
40
  --_sonic-key-scale: 1;
36
41
  --_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%);
42
+ calc(2 * var(--_sonic-unit)) var(--_sonic-drop-shade);
38
43
  --_sonic-key-glow: transparent;
39
44
 
40
45
  position: relative;
@@ -42,6 +47,7 @@
42
47
  inline-size: var(--_sonic-key-size);
43
48
  block-size: var(--_sonic-key-size);
44
49
  padding: 0;
50
+ font: inherit;
45
51
  color: var(--_sonic-ink);
46
52
  cursor: pointer;
47
53
  background: linear-gradient(
@@ -52,6 +58,7 @@
52
58
  border: 0;
53
59
  border-radius: var(--_sonic-key-corner);
54
60
  box-shadow: var(--_sonic-well);
61
+ transition: color 120ms ease-out;
55
62
  }
56
63
 
57
64
  /* A drifting finger would turn a held pad into a pan */
@@ -60,14 +67,14 @@
60
67
  }
61
68
 
62
69
  .sonic-key:active:where(:not(:disabled)),
63
- .sonic-key:is([aria-pressed='true'], :where([momentary][pressed]) > *) {
70
+ .sonic-key:is([aria-pressed='true'], :where([momentary]:state(pressed)) > *) {
64
71
  --_sonic-key-shift: calc(var(--_sonic-key-depth) / 2);
65
72
  --_sonic-key-scale: var(--_sonic-key-press-scale);
66
73
  --_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%);
74
+ var(--_sonic-unit) var(--_sonic-drop-shade);
68
75
  }
69
76
 
70
- .sonic-key:is([aria-pressed='true'], :where([momentary][pressed]) > *) {
77
+ .sonic-key:is([aria-pressed='true'], :where([momentary]:state(pressed)) > *) {
71
78
  --_sonic-key-glow: color-mix(in oklab, var(--_sonic-lit) 22%, transparent);
72
79
 
73
80
  color: var(--_sonic-lit);
@@ -87,10 +94,25 @@
87
94
  cursor: default;
88
95
  }
89
96
 
97
+ .sonic-key:not([aria-pressed='true'], :disabled, :where([momentary]:state(pressed)) > *):is(
98
+ :focus-visible,
99
+ :active
100
+ ) {
101
+ color: var(--_sonic-ink-lift);
102
+ }
103
+
104
+ @media (hover: hover) {
105
+ .sonic-key:not([aria-pressed='true'], :disabled, :where([momentary]:state(pressed)) > *):hover {
106
+ color: var(--_sonic-ink-lift);
107
+ }
108
+ }
109
+
90
110
  .sonic-key-cap {
91
111
  position: absolute;
92
112
  inset: var(--_sonic-key-gap);
93
113
  display: grid;
114
+ gap: calc(var(--_sonic-key-size) * var(--_sonic-key-led-ratio) / 2);
115
+ place-content: center;
94
116
  place-items: center;
95
117
  background: var(--_sonic-skirt);
96
118
  border-radius: max(0px, var(--_sonic-key-corner) - var(--_sonic-key-gap));
@@ -110,8 +132,18 @@
110
132
  box-shadow: var(--_sonic-face-shadow);
111
133
  }
112
134
 
113
- & > svg {
135
+ & > * {
114
136
  position: relative;
137
+ }
138
+
139
+ & > .sonic-led {
140
+ --_sonic-led-size: var(
141
+ --sonic-led-size,
142
+ calc(var(--_sonic-key-size) * var(--_sonic-key-led-ratio))
143
+ );
144
+ }
145
+
146
+ & > svg {
115
147
  inline-size: calc(var(--_sonic-key-size) * var(--_sonic-key-icon-ratio));
116
148
  block-size: calc(var(--_sonic-key-size) * var(--_sonic-key-icon-ratio));
117
149
  filter: var(--_sonic-engraving);
@@ -120,18 +152,46 @@
120
152
  }
121
153
  }
122
154
 
155
+ .sonic-key-cap:has(> .sonic-led) > svg {
156
+ inline-size: calc(var(--_sonic-key-size) * var(--_sonic-key-led-icon-ratio));
157
+ block-size: calc(var(--_sonic-key-size) * var(--_sonic-key-led-icon-ratio));
158
+ }
159
+
123
160
  /* Engraving under the glow keeps a lit icon's edge */
124
- .sonic-key:is([aria-pressed='true'], :where([momentary][pressed]) > *) .sonic-key-cap > svg {
161
+ .sonic-key:is([aria-pressed='true'], :where([momentary]:state(pressed)) > *)
162
+ .sonic-key-cap
163
+ > svg {
125
164
  filter: var(--_sonic-engraving)
126
165
  drop-shadow(0 0 var(--_sonic-unit) color-mix(in oklab, var(--_sonic-lit) 35%, transparent));
127
166
  }
128
167
 
129
- .sonic-key:is([aria-pressed='true'], :where([momentary][pressed]) > *)
168
+ .sonic-key:is([aria-pressed='true'], :where([momentary]:state(pressed)) > *)
130
169
  .sonic-key-cap
131
170
  > [data-sonic-when='off'],
132
- .sonic-key:not([aria-pressed='true'], :where([momentary][pressed]) > *)
171
+ .sonic-key:not([aria-pressed='true'], :where([momentary]:state(pressed)) > *)
133
172
  .sonic-key-cap
134
173
  > [data-sonic-when='on'] {
135
174
  display: none;
136
175
  }
176
+
177
+ @media (forced-colors: active) {
178
+ .sonic-key-cap > svg {
179
+ filter: none;
180
+ mask: none;
181
+ }
182
+
183
+ .sonic-key:is([aria-pressed='true'], :where([momentary]:state(pressed)) > *) {
184
+ --_sonic-skirt: var(--_sonic-lit);
185
+ --_sonic-face: var(--_sonic-lit);
186
+ --_sonic-ink: HighlightText;
187
+
188
+ &:disabled {
189
+ --_sonic-ink: ButtonFace;
190
+ }
191
+
192
+ & .sonic-key-cap > svg {
193
+ filter: none;
194
+ }
195
+ }
196
+ }
137
197
  }
@@ -0,0 +1,75 @@
1
+ @property --_sonic-led-size {
2
+ syntax: '<length>';
3
+ inherits: true;
4
+ initial-value: 8px;
5
+ }
6
+
7
+ @layer sonic {
8
+ .sonic-led {
9
+ --_sonic-led-size: var(--sonic-led-size, 0.5rem);
10
+ --_sonic-led-lens-ratio: var(--sonic-led-lens-ratio, 0.625);
11
+ --_sonic-unit: calc(var(--_sonic-led-size) / 8);
12
+ --_sonic-led-bezel: color-mix(in oklab, var(--_sonic-unlit) 55%, #000);
13
+
14
+ position: relative;
15
+ display: inline-block;
16
+ flex-shrink: 0;
17
+ inline-size: var(--_sonic-led-size);
18
+ block-size: var(--_sonic-led-size);
19
+ background: var(--_sonic-led-bezel);
20
+ border-radius: 50%;
21
+
22
+ &::after {
23
+ position: absolute;
24
+ inset: calc(var(--_sonic-led-size) * (1 - var(--_sonic-led-lens-ratio)) / 2);
25
+ content: '';
26
+ background: var(--_sonic-lens);
27
+ border-radius: inherit;
28
+ }
29
+
30
+ @media (forced-colors: active) {
31
+ forced-color-adjust: none;
32
+ background: none;
33
+
34
+ &::after {
35
+ background: GrayText;
36
+ box-shadow: 0 0 0 1px CanvasText;
37
+ }
38
+ }
39
+ }
40
+
41
+ .sonic-led:is(
42
+ [data-sonic-lit],
43
+ :where(
44
+ .sonic-key[aria-pressed='true'],
45
+ [momentary]:state(pressed) > .sonic-key,
46
+ .sonic-segmented-segment[aria-checked='true']
47
+ )
48
+ *
49
+ ) {
50
+ background:
51
+ radial-gradient(
52
+ closest-side,
53
+ color-mix(in oklab, var(--_sonic-lit) 60%, transparent)
54
+ calc(var(--_sonic-led-lens-ratio) * 100%),
55
+ transparent
56
+ ),
57
+ var(--_sonic-led-bezel);
58
+
59
+ &::after {
60
+ background: radial-gradient(
61
+ closest-side,
62
+ color-mix(in oklab, var(--_sonic-lit) 55%, #fff),
63
+ var(--_sonic-lit)
64
+ );
65
+ }
66
+
67
+ @media (forced-colors: active) {
68
+ background: none;
69
+
70
+ &::after {
71
+ background: Highlight;
72
+ }
73
+ }
74
+ }
75
+ }