@xsynaptic/sonic-ui 0.1.0 → 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.
Files changed (117) 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/segmented.js +7 -0
  6. package/dist/default/define/slider.js +7 -0
  7. package/dist/default/elements/define-once.js +7 -0
  8. package/dist/{elements → default/elements}/dial.js +15 -3
  9. package/dist/default/elements/form-element.js +56 -0
  10. package/dist/default/elements/key.js +144 -0
  11. package/dist/{elements → default/elements}/meter.js +27 -16
  12. package/dist/default/elements/range-element.js +410 -0
  13. package/dist/{elements → default/elements}/range-entry.js +5 -4
  14. package/dist/default/elements/segmented.js +173 -0
  15. package/dist/{elements → default/elements}/slider.js +23 -8
  16. package/dist/default/elements/sonic-element.js +153 -0
  17. package/dist/{index.js → default/index.js} +2 -1
  18. package/dist/default/lib/copy-node.js +30 -0
  19. package/dist/default/lib/percent.js +12 -0
  20. package/dist/default/lib/read-px-property.js +8 -0
  21. package/dist/{lib → default/lib}/render.js +9 -2
  22. package/dist/default/lib/taper.js +32 -0
  23. package/dist/default/lib/write-attribute.js +11 -0
  24. package/dist/dev/define/dial.js.map +1 -0
  25. package/dist/dev/define/key.js.map +1 -0
  26. package/dist/dev/define/meter.js.map +1 -0
  27. package/dist/dev/define/segmented.js.map +1 -0
  28. package/dist/dev/define/slider.js.map +1 -0
  29. package/dist/dev/define.js +5 -0
  30. package/dist/dev/elements/define-once.js.map +1 -0
  31. package/dist/{elements → dev/elements}/dial.d.ts +4 -0
  32. package/dist/dev/elements/dial.js +70 -0
  33. package/dist/dev/elements/dial.js.map +1 -0
  34. package/dist/dev/elements/form-element.d.ts +22 -0
  35. package/dist/dev/elements/form-element.js +57 -0
  36. package/dist/dev/elements/form-element.js.map +1 -0
  37. package/dist/dev/elements/key.d.ts +30 -0
  38. package/dist/{elements → dev/elements}/key.js +71 -25
  39. package/dist/dev/elements/key.js.map +1 -0
  40. package/dist/{elements → dev/elements}/meter.d.ts +6 -0
  41. package/dist/dev/elements/meter.js +131 -0
  42. package/dist/dev/elements/meter.js.map +1 -0
  43. package/dist/{elements → dev/elements}/range-element.d.ts +25 -5
  44. package/dist/{elements → dev/elements}/range-element.js +116 -53
  45. package/dist/dev/elements/range-element.js.map +1 -0
  46. package/dist/dev/elements/range-entry.js +75 -0
  47. package/dist/dev/elements/range-entry.js.map +1 -0
  48. package/dist/{elements → dev/elements}/segmented.d.ts +5 -2
  49. package/dist/dev/elements/segmented.js +174 -0
  50. package/dist/dev/elements/segmented.js.map +1 -0
  51. package/dist/{elements → dev/elements}/slider.d.ts +6 -0
  52. package/dist/dev/elements/slider.js +86 -0
  53. package/dist/dev/elements/slider.js.map +1 -0
  54. package/dist/dev/elements/sonic-element.d.ts +33 -0
  55. package/dist/dev/elements/sonic-element.js +169 -0
  56. package/dist/dev/elements/sonic-element.js.map +1 -0
  57. package/dist/{index.d.ts → dev/index.d.ts} +2 -1
  58. package/dist/dev/index.js +8 -0
  59. package/dist/dev/lib/copy-node.js +31 -0
  60. package/dist/dev/lib/copy-node.js.map +1 -0
  61. package/dist/dev/lib/percent.d.ts +5 -0
  62. package/dist/dev/lib/percent.js +13 -0
  63. package/dist/dev/lib/percent.js.map +1 -0
  64. package/dist/dev/lib/read-px-property.js.map +1 -0
  65. package/dist/dev/lib/render.js +27 -0
  66. package/dist/dev/lib/render.js.map +1 -0
  67. package/dist/dev/lib/taper.js.map +1 -0
  68. package/dist/dev/lib/write-attribute.js.map +1 -0
  69. package/dist/skins/ivory.css +3 -3
  70. package/dist/skins/slate.css +4 -4
  71. package/dist/styles/controls.css +0 -1
  72. package/dist/styles/dial.css +33 -13
  73. package/dist/styles/key.css +30 -10
  74. package/dist/styles/material.css +58 -10
  75. package/dist/styles/meter.css +25 -5
  76. package/dist/styles/screen.css +7 -1
  77. package/dist/styles/segmented.css +24 -7
  78. package/dist/styles/slider.css +17 -4
  79. package/package.json +13 -12
  80. package/dist/define/dial.js.map +0 -1
  81. package/dist/define/key.js.map +0 -1
  82. package/dist/define/meter.js.map +0 -1
  83. package/dist/define/segmented.js.map +0 -1
  84. package/dist/define/slider.js.map +0 -1
  85. package/dist/elements/define-once.js.map +0 -1
  86. package/dist/elements/dial.js.map +0 -1
  87. package/dist/elements/key.d.ts +0 -17
  88. package/dist/elements/key.js.map +0 -1
  89. package/dist/elements/meter.js.map +0 -1
  90. package/dist/elements/range-element.js.map +0 -1
  91. package/dist/elements/range-entry.js.map +0 -1
  92. package/dist/elements/segmented.js +0 -104
  93. package/dist/elements/segmented.js.map +0 -1
  94. package/dist/elements/slider.js.map +0 -1
  95. package/dist/elements/sonic-element.d.ts +0 -20
  96. package/dist/elements/sonic-element.js +0 -94
  97. package/dist/elements/sonic-element.js.map +0 -1
  98. package/dist/lib/read-px-property.js.map +0 -1
  99. package/dist/lib/render.js.map +0 -1
  100. package/dist/lib/taper.js.map +0 -1
  101. package/dist/lib/write-attribute.js.map +0 -1
  102. /package/dist/{define.js → default/define.js} +0 -0
  103. /package/dist/{define → dev/define}/dial.d.ts +0 -0
  104. /package/dist/{define → dev/define}/dial.js +0 -0
  105. /package/dist/{define → dev/define}/key.d.ts +0 -0
  106. /package/dist/{define → dev/define}/key.js +0 -0
  107. /package/dist/{define → dev/define}/meter.d.ts +0 -0
  108. /package/dist/{define → dev/define}/meter.js +0 -0
  109. /package/dist/{define → dev/define}/segmented.d.ts +0 -0
  110. /package/dist/{define → dev/define}/segmented.js +0 -0
  111. /package/dist/{define → dev/define}/slider.d.ts +0 -0
  112. /package/dist/{define → dev/define}/slider.js +0 -0
  113. /package/dist/{define.d.ts → dev/define.d.ts} +0 -0
  114. /package/dist/{elements → dev/elements}/define-once.js +0 -0
  115. /package/dist/{lib → dev/lib}/read-px-property.js +0 -0
  116. /package/dist/{lib → dev/lib}/taper.js +0 -0
  117. /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 @@
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 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,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,4 +1,3 @@
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';
@@ -1,5 +1,4 @@
1
1
  @layer sonic {
2
- /* On the host too, for the placeholder */
3
2
  :is(sonic-dial, .sonic-dial) {
4
3
  --_sonic-dial-size: var(--sonic-dial-size, 2.5rem);
5
4
  }
@@ -29,8 +28,6 @@
29
28
 
30
29
  /* Centred on six o'clock */
31
30
  --_sonic-dial-sweep: var(--sonic-dial-sweep, 300deg);
32
-
33
- /* Absolute, so a larger knob gets more ridges rather than wider ones */
34
31
  --_sonic-dial-ridge-pitch: var(--sonic-dial-ridge-pitch, 4px);
35
32
 
36
33
  /* Read by the element as px */
@@ -84,7 +81,7 @@
84
81
  --_sonic-dial-lit: var(--_sonic-lit-off);
85
82
  }
86
83
 
87
- [disabled] > .sonic-dial {
84
+ :disabled > .sonic-dial {
88
85
  --_sonic-dial-lit: var(--_sonic-lit-disabled);
89
86
 
90
87
  cursor: default;
@@ -140,14 +137,15 @@
140
137
  --_sonic-dial-lit-to: calc(
141
138
  max(var(--_sonic-dial-origin, 0), var(--_sonic-dial-value, 0)) * var(--_sonic-dial-sweep)
142
139
  );
140
+ --_sonic-dial-ring-shade: radial-gradient(
141
+ closest-side,
142
+ rgb(0 0 0 / 55%) calc(100% - var(--_sonic-dial-ring-width)),
143
+ transparent calc(100% - var(--_sonic-dial-ring-width) * 0.4),
144
+ rgb(0 0 0 / 25%) 100%
145
+ );
143
146
 
144
147
  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
- ),
148
+ var(--_sonic-dial-ring-shade),
151
149
  conic-gradient(
152
150
  from var(--_sonic-dial-start),
153
151
  var(--_sonic-unlit) var(--_sonic-dial-lit-from),
@@ -185,7 +183,7 @@
185
183
  border-radius: 50%;
186
184
  box-shadow:
187
185
  calc(2 * var(--_sonic-cast-x)) calc(2 * var(--_sonic-cast-y)) calc(4 * var(--_sonic-unit))
188
- rgb(0 0 0 / 65%),
186
+ var(--_sonic-drop-shade),
189
187
  var(--_sonic-rim),
190
188
  var(--_sonic-focus-shadow);
191
189
 
@@ -236,7 +234,6 @@
236
234
  border-radius: 50%;
237
235
  rotate: calc(var(--_sonic-dial-start) + var(--_sonic-dial-angle));
238
236
 
239
- /* Evenly shaded, since any offset would turn with the knob */
240
237
  &::before {
241
238
  --_sonic-dial-pointer-width: calc(var(--_sonic-dial-size) * var(--_sonic-dial-pointer-ratio));
242
239
 
@@ -259,7 +256,30 @@
259
256
  }
260
257
  }
261
258
 
262
- [disabled] > .sonic-dial > .sonic-dial-pointer::before {
259
+ :disabled > .sonic-dial > .sonic-dial-pointer::before {
263
260
  background: var(--_sonic-etch);
264
261
  }
262
+
263
+ @media (forced-colors: active) {
264
+ :is(.sonic-dial-notches, .sonic-dial-ring, .sonic-dial-modulation, .sonic-dial-pointer) {
265
+ forced-color-adjust: none;
266
+ }
267
+
268
+ .sonic-dial-ring {
269
+ --_sonic-dial-ring-shade: none;
270
+ }
271
+
272
+ .sonic-dial-modulation {
273
+ --_sonic-dial-modulation: CanvasText;
274
+ }
275
+
276
+ .sonic-dial-cap::before,
277
+ .sonic-dial-cap::after {
278
+ display: none;
279
+ }
280
+
281
+ .sonic-dial-pointer::before {
282
+ box-shadow: none;
283
+ }
284
+ }
265
285
  }
@@ -1,5 +1,4 @@
1
1
  @layer sonic {
2
- /* On the host too, for the placeholder */
3
2
  :is(sonic-key, .sonic-key) {
4
3
  --_sonic-key-size: var(--sonic-key-size, 2rem);
5
4
  }
@@ -8,7 +7,6 @@
8
7
  display: contents;
9
8
  }
10
9
 
11
- /* Hides the icon, which has no size before upgrade */
12
10
  sonic-key:not(:defined, [hidden]) {
13
11
  display: block;
14
12
  visibility: hidden;
@@ -18,7 +16,6 @@
18
16
  overflow: hidden;
19
17
  }
20
18
 
21
- /* The button is the well; the cap is the part that moves */
22
19
  .sonic-key {
23
20
  --_sonic-key-gap-ratio: var(--sonic-key-gap-ratio, 0);
24
21
  --_sonic-key-radius-ratio: var(--sonic-key-radius-ratio, 0.2);
@@ -34,7 +31,7 @@
34
31
  --_sonic-key-shift: calc(var(--_sonic-key-depth) / -2);
35
32
  --_sonic-key-scale: 1;
36
33
  --_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%);
34
+ calc(2 * var(--_sonic-unit)) var(--_sonic-drop-shade);
38
35
  --_sonic-key-glow: transparent;
39
36
 
40
37
  position: relative;
@@ -60,14 +57,14 @@
60
57
  }
61
58
 
62
59
  .sonic-key:active:where(:not(:disabled)),
63
- .sonic-key:is([aria-pressed='true'], :where([momentary][pressed]) > *) {
60
+ .sonic-key:is([aria-pressed='true'], :where([momentary]:state(pressed)) > *) {
64
61
  --_sonic-key-shift: calc(var(--_sonic-key-depth) / 2);
65
62
  --_sonic-key-scale: var(--_sonic-key-press-scale);
66
63
  --_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%);
64
+ var(--_sonic-unit) var(--_sonic-drop-shade);
68
65
  }
69
66
 
70
- .sonic-key:is([aria-pressed='true'], :where([momentary][pressed]) > *) {
67
+ .sonic-key:is([aria-pressed='true'], :where([momentary]:state(pressed)) > *) {
71
68
  --_sonic-key-glow: color-mix(in oklab, var(--_sonic-lit) 22%, transparent);
72
69
 
73
70
  color: var(--_sonic-lit);
@@ -121,17 +118,40 @@
121
118
  }
122
119
 
123
120
  /* Engraving under the glow keeps a lit icon's edge */
124
- .sonic-key:is([aria-pressed='true'], :where([momentary][pressed]) > *) .sonic-key-cap > svg {
121
+ .sonic-key:is([aria-pressed='true'], :where([momentary]:state(pressed)) > *)
122
+ .sonic-key-cap
123
+ > svg {
125
124
  filter: var(--_sonic-engraving)
126
125
  drop-shadow(0 0 var(--_sonic-unit) color-mix(in oklab, var(--_sonic-lit) 35%, transparent));
127
126
  }
128
127
 
129
- .sonic-key:is([aria-pressed='true'], :where([momentary][pressed]) > *)
128
+ .sonic-key:is([aria-pressed='true'], :where([momentary]:state(pressed)) > *)
130
129
  .sonic-key-cap
131
130
  > [data-sonic-when='off'],
132
- .sonic-key:not([aria-pressed='true'], :where([momentary][pressed]) > *)
131
+ .sonic-key:not([aria-pressed='true'], :where([momentary]:state(pressed)) > *)
133
132
  .sonic-key-cap
134
133
  > [data-sonic-when='on'] {
135
134
  display: none;
136
135
  }
136
+
137
+ @media (forced-colors: active) {
138
+ .sonic-key-cap > svg {
139
+ filter: none;
140
+ mask: none;
141
+ }
142
+
143
+ .sonic-key:is([aria-pressed='true'], :where([momentary]:state(pressed)) > *) {
144
+ --_sonic-skirt: var(--_sonic-lit);
145
+ --_sonic-face: var(--_sonic-lit);
146
+ --_sonic-ink: HighlightText;
147
+
148
+ &:disabled {
149
+ --_sonic-ink: ButtonFace;
150
+ }
151
+
152
+ & .sonic-key-cap > svg {
153
+ filter: none;
154
+ }
155
+ }
156
+ }
137
157
  }
@@ -10,7 +10,7 @@
10
10
  .sonic-slider,
11
11
  .sonic-slider-readout
12
12
  ) {
13
- --_sonic-unlit: var(--sonic-unlit, #27272a);
13
+ --_sonic-unlit: var(--sonic-unlit, oklch(27.4% 0.005 286deg));
14
14
 
15
15
  /* Degrees left of overhead */
16
16
  --_sonic-light-tilt: var(--sonic-light-tilt, 17deg);
@@ -20,20 +20,18 @@
20
20
  }
21
21
 
22
22
  :is(.sonic-dial, .sonic-key, .sonic-meter, .sonic-segmented-segment, .sonic-slider) {
23
- --_sonic-lit: var(--sonic-lit, #f59e0b);
23
+ --_sonic-lit: var(--sonic-lit, oklch(76.9% 0.165 70deg));
24
24
  }
25
25
 
26
26
  .sonic-meter {
27
- --_sonic-hot: var(--sonic-hot, #f97316);
28
- --_sonic-clip: var(--sonic-clip, #ef4444);
27
+ --_sonic-hot: var(--sonic-hot, oklch(70.5% 0.187 48deg));
28
+ --_sonic-clip: var(--sonic-clip, oklch(63.7% 0.208 25deg));
29
29
  }
30
30
 
31
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);
32
+ --_sonic-cap: var(--sonic-cap, oklch(37% 0.012 286deg));
33
+ --_sonic-cap-hover: var(--sonic-cap-hover, oklch(44.2% 0.015 286deg));
34
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
35
  --_sonic-ink: var(--sonic-mark, color-mix(in oklab, var(--_sonic-cap) 30%, #fff));
38
36
  --_sonic-etch: color-mix(in oklab, var(--_sonic-ink) 30%, transparent);
39
37
  --_sonic-lit-disabled: color-mix(in oklab, var(--_sonic-ink) 25%, var(--_sonic-unlit));
@@ -66,6 +64,7 @@
66
64
  rgb(255 255 255 / 12%);
67
65
  --_sonic-rim: 0 0 0 max(1px, 0.75 * var(--_sonic-unit))
68
66
  color-mix(in oklab, var(--_sonic-cap) 40%, #000);
67
+ --_sonic-drop-shade: rgb(0 0 0 / 65%);
69
68
  --_sonic-focus-shadow: 0 0 transparent;
70
69
  --_sonic-focus-glow: 0 0 max(4px, 3 * var(--_sonic-unit)) max(1px, 0.5 * var(--_sonic-unit))
71
70
  color-mix(in oklab, var(--_sonic-focus) 60%, transparent);
@@ -120,7 +119,6 @@
120
119
  --_sonic-cap: var(--_sonic-cap-hover);
121
120
  --_sonic-focus-shadow: var(--_sonic-focus-glow);
122
121
 
123
- /* Painted only in forced colours; inset so it adds no size */
124
122
  outline: 2px solid transparent;
125
123
  outline-offset: -2px;
126
124
  }
@@ -136,7 +134,6 @@
136
134
  --_sonic-unit: calc(var(--_sonic-readout-size) / 32);
137
135
 
138
136
  box-sizing: border-box;
139
- display: grid;
140
137
  place-items: center;
141
138
  min-inline-size: calc(1.75 * var(--_sonic-readout-size));
142
139
  block-size: var(--_sonic-readout-size);
@@ -173,7 +170,9 @@
173
170
  display 120ms allow-discrete,
174
171
  overlay 120ms allow-discrete;
175
172
 
173
+ /* Grid only while open; a closed readout left rendered is placed against a filtered ancestor, and engines keep the flip it took there */
176
174
  &:popover-open {
175
+ display: grid;
177
176
  opacity: 1;
178
177
  translate: 0;
179
178
 
@@ -226,4 +225,53 @@
226
225
  field-sizing: content;
227
226
  }
228
227
  }
228
+
229
+ @media (forced-colors: active) {
230
+ :is(.sonic-dial, .sonic-key, .sonic-meter, .sonic-segmented-segment, .sonic-slider) {
231
+ --_sonic-lit: Highlight;
232
+ --_sonic-unlit: GrayText;
233
+ }
234
+
235
+ /* Tokens rather than properties, so a state rule that restates one still paints in system colours */
236
+ :is(.sonic-dial, .sonic-key, .sonic-segmented-segment, .sonic-slider) {
237
+ --_sonic-ink: ButtonText;
238
+ --_sonic-etch: ButtonText;
239
+ --_sonic-lit-disabled: GrayText;
240
+ --_sonic-skirt: ButtonFace;
241
+ --_sonic-face: ButtonFace;
242
+ --_sonic-sheen: none;
243
+ --_sonic-face-shadow: none;
244
+ --_sonic-rim: 0 0 0 max(1px, 0.75 * var(--_sonic-unit)) var(--_sonic-ink);
245
+ --_sonic-drop-shade: transparent;
246
+ --_sonic-focus-glow: 0 0 transparent;
247
+ }
248
+
249
+ /* Dimmed is a hint, not a state; the value stays readable */
250
+ :is(.sonic-dial, .sonic-slider) {
251
+ --_sonic-lit-off: Highlight;
252
+ }
253
+
254
+ :is(
255
+ :disabled > :is(.sonic-dial, .sonic-slider),
256
+ .sonic-key:disabled,
257
+ .sonic-segmented-segment:disabled
258
+ ) {
259
+ --_sonic-ink: GrayText;
260
+ --_sonic-etch: GrayText;
261
+ }
262
+
263
+ /* Stands in for the glow, which forced colours drop; inset, it merged with the cap's rim */
264
+ :is(.sonic-dial, .sonic-key, .sonic-segmented-segment, .sonic-slider):focus-visible {
265
+ outline-offset: 2px;
266
+ }
267
+
268
+ :is(.sonic-dial-cap, .sonic-key-cap, .sonic-segmented-cap, .sonic-slider-cap) {
269
+ color: var(--_sonic-ink);
270
+ forced-color-adjust: none;
271
+ }
272
+
273
+ :is(.sonic-dial-readout, .sonic-slider-readout) {
274
+ border: 1px solid CanvasText;
275
+ }
276
+ }
229
277
  }
@@ -1,6 +1,5 @@
1
1
  /* The size is the thickness */
2
2
  @layer sonic {
3
- /* On the host too, for the placeholder */
4
3
  :is(sonic-meter, .sonic-meter) {
5
4
  --_sonic-meter-size: var(--sonic-meter-size, 0.5rem);
6
5
  --_sonic-meter-length: var(--sonic-meter-length, 8rem);
@@ -22,7 +21,6 @@
22
21
  block-size: var(--_sonic-meter-size);
23
22
  }
24
23
 
25
- /* Laid on its side rather than rotated, as the slider is */
26
24
  .sonic-meter {
27
25
  /* Absolute, so a longer meter gains segments rather than stretching them */
28
26
  --_sonic-meter-pitch: var(--sonic-meter-segment-pitch, 4px);
@@ -46,8 +44,6 @@
46
44
  --_sonic-meter-segment-radius: calc(
47
45
  var(--_sonic-meter-segment) * var(--_sonic-meter-segment-radius-ratio)
48
46
  );
49
-
50
- /* Whole segments only, so the last one ends flush */
51
47
  --_sonic-meter-segments: round(
52
48
  down,
53
49
  var(--_sonic-meter-length) - 3 * var(--_sonic-meter-inset) - var(--_sonic-meter-lens) +
@@ -62,7 +58,6 @@
62
58
  background: color-mix(in oklab, var(--_sonic-unlit) 55%, #000);
63
59
  border-radius: calc(var(--_sonic-meter-size) * var(--_sonic-meter-radius-ratio));
64
60
 
65
- /* Over the segments, so the lip shades them too */
66
61
  &::after {
67
62
  position: absolute;
68
63
  inset: 0;
@@ -268,4 +263,29 @@
268
263
  inline-size: var(--_sonic-meter-lens);
269
264
  block-size: auto;
270
265
  }
266
+
267
+ /* Takes no focus, so the whole meter opts out */
268
+ @media (forced-colors: active) {
269
+ .sonic-meter {
270
+ forced-color-adjust: none;
271
+ background: Canvas;
272
+
273
+ &::after {
274
+ border: 1px solid CanvasText;
275
+ box-shadow: none;
276
+ }
277
+ }
278
+
279
+ :is(.sonic-meter-segments, .sonic-meter-clip) {
280
+ background: var(--_sonic-unlit);
281
+ }
282
+
283
+ .sonic-meter-level {
284
+ background: var(--_sonic-lit);
285
+ }
286
+
287
+ .sonic-meter-clip::after {
288
+ background: Mark;
289
+ }
290
+ }
271
291
  }
@@ -31,7 +31,6 @@
31
31
  var(--_sonic-glass);
32
32
  border-radius: calc(var(--_sonic-screen-size) * var(--_sonic-screen-radius-ratio));
33
33
 
34
- /* Over the content, so the lip shades the picture too */
35
34
  &::after {
36
35
  position: absolute;
37
36
  inset: 0;
@@ -44,4 +43,11 @@
44
43
  inset 0 0 calc(3 * var(--_sonic-unit)) rgb(0 0 0 / 45%);
45
44
  }
46
45
  }
46
+
47
+ /* The glass is forced to Canvas like the page, so only a border shows its edge */
48
+ @media (forced-colors: active) {
49
+ .sonic-screen {
50
+ border: 1px solid CanvasText;
51
+ }
52
+ }
47
53
  }
@@ -1,5 +1,4 @@
1
1
  @layer sonic {
2
- /* On the host too, for the placeholder */
3
2
  :is(sonic-segmented, .sonic-segmented) {
4
3
  --_sonic-segmented-size: var(--sonic-segmented-size, 2rem);
5
4
  }
@@ -8,7 +7,6 @@
8
7
  display: contents;
9
8
  }
10
9
 
11
- /* Hides the options, which have no size before upgrade */
12
10
  sonic-segmented:not(:defined, [hidden]) {
13
11
  display: block;
14
12
  visibility: hidden;
@@ -34,8 +32,6 @@
34
32
  var(--_sonic-segmented-size) * var(--_sonic-segmented-depth-ratio)
35
33
  );
36
34
  --_sonic-segmented-gap: calc(var(--_sonic-segmented-size) * var(--_sonic-segmented-gap-ratio));
37
-
38
- /* Concentric with the well's corner */
39
35
  --_sonic-segmented-inner-corner: max(
40
36
  0px,
41
37
  var(--_sonic-segmented-corner) - var(--_sonic-segmented-gap)
@@ -47,6 +43,7 @@
47
43
  gap: var(--_sonic-segmented-gap);
48
44
  block-size: var(--_sonic-segmented-size);
49
45
  padding: var(--_sonic-segmented-gap);
46
+ touch-action: pan-y;
50
47
  user-select: none;
51
48
  background: linear-gradient(
52
49
  var(--_sonic-light-fall),
@@ -61,7 +58,7 @@
61
58
  --_sonic-segmented-shift: calc(var(--_sonic-segmented-depth) / -2);
62
59
  --_sonic-segmented-scale: 1;
63
60
  --_sonic-segmented-drop: calc(1.5 * var(--_sonic-cast-x)) calc(1.5 * var(--_sonic-cast-y))
64
- calc(2 * var(--_sonic-unit)) rgb(0 0 0 / 65%);
61
+ calc(2 * var(--_sonic-unit)) var(--_sonic-drop-shade);
65
62
  --_sonic-segmented-glow: transparent;
66
63
 
67
64
  display: grid;
@@ -77,12 +74,12 @@
77
74
  border-radius: var(--_sonic-segmented-inner-corner);
78
75
  }
79
76
 
80
- .sonic-segmented-segment:active:where(:not(:disabled)),
77
+ .sonic-segmented-segment:where([data-sonic-pressed], :focus-visible:active):not(:disabled),
81
78
  .sonic-segmented-segment[aria-checked='true'] {
82
79
  --_sonic-segmented-shift: calc(var(--_sonic-segmented-depth) / 2);
83
80
  --_sonic-segmented-scale: var(--_sonic-segmented-press-scale);
84
81
  --_sonic-segmented-drop: calc(0.5 * var(--_sonic-cast-x)) calc(0.5 * var(--_sonic-cast-y))
85
- var(--_sonic-unit) rgb(0 0 0 / 65%);
82
+ var(--_sonic-unit) var(--_sonic-drop-shade);
86
83
  }
87
84
 
88
85
  .sonic-segmented-segment[aria-checked='true'] {
@@ -144,4 +141,24 @@
144
141
  filter: var(--_sonic-engraving)
145
142
  drop-shadow(0 0 var(--_sonic-unit) color-mix(in oklab, var(--_sonic-lit) 35%, transparent));
146
143
  }
144
+
145
+ @media (forced-colors: active) {
146
+ .sonic-segmented-cap > * {
147
+ filter: none;
148
+ }
149
+
150
+ .sonic-segmented-segment[aria-checked='true'] {
151
+ --_sonic-skirt: var(--_sonic-lit);
152
+ --_sonic-face: var(--_sonic-lit);
153
+ --_sonic-ink: HighlightText;
154
+
155
+ &:disabled {
156
+ --_sonic-ink: ButtonFace;
157
+ }
158
+
159
+ & .sonic-segmented-cap > * {
160
+ filter: none;
161
+ }
162
+ }
163
+ }
147
164
  }
@@ -1,6 +1,5 @@
1
1
  /* The size is the thickness across the travel */
2
2
  @layer sonic {
3
- /* On the host too, for the placeholder */
4
3
  :is(sonic-slider, .sonic-slider) {
5
4
  --_sonic-slider-size: var(--sonic-slider-size, 2rem);
6
5
  --_sonic-slider-length: var(--sonic-slider-length, 8rem);
@@ -67,7 +66,7 @@
67
66
  --_sonic-slider-lit: var(--_sonic-lit-off);
68
67
  }
69
68
 
70
- [disabled] > .sonic-slider {
69
+ :disabled > .sonic-slider {
71
70
  --_sonic-slider-lit: var(--_sonic-lit-disabled);
72
71
  --_sonic-slider-cursor: default;
73
72
  }
@@ -156,7 +155,7 @@
156
155
  border-radius: var(--_sonic-slider-corner);
157
156
  box-shadow:
158
157
  calc(2 * var(--_sonic-cast-x)) calc(2 * var(--_sonic-cast-y)) calc(3 * var(--_sonic-unit))
159
- rgb(0 0 0 / 65%),
158
+ var(--_sonic-drop-shade),
160
159
  var(--_sonic-rim),
161
160
  var(--_sonic-focus-shadow);
162
161
  translate: var(--_sonic-slider-offset) 0;
@@ -194,11 +193,25 @@
194
193
  }
195
194
  }
196
195
 
197
- [disabled] > .sonic-slider > .sonic-slider-cap::after {
196
+ :disabled > .sonic-slider > .sonic-slider-cap::after {
198
197
  background: var(--_sonic-etch);
199
198
  }
200
199
 
201
200
  [groove-press='none'] > .sonic-slider > .sonic-slider-cap {
202
201
  cursor: var(--_sonic-slider-cursor);
203
202
  }
203
+
204
+ @media (forced-colors: active) {
205
+ :is(.sonic-slider-marks, .sonic-slider-groove) {
206
+ forced-color-adjust: none;
207
+ }
208
+
209
+ .sonic-slider-groove {
210
+ box-shadow: none;
211
+ }
212
+
213
+ .sonic-slider-cap::after {
214
+ box-shadow: none;
215
+ }
216
+ }
204
217
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xsynaptic/sonic-ui",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Native custom elements for audio interfaces: dials, sliders, keys, a segmented switch, a peak meter and a screen panel, skinned with CSS custom properties",
5
5
  "keywords": [
6
6
  "custom-elements",
@@ -28,24 +28,27 @@
28
28
  "node": ">=24.0.0"
29
29
  },
30
30
  "sideEffects": [
31
- "./dist/define.js",
32
- "./dist/define/*.js",
31
+ "./dist/*/define.js",
32
+ "./dist/*/define/*.js",
33
33
  "./src/define.ts",
34
34
  "./src/define/*.ts",
35
35
  "*.css"
36
36
  ],
37
37
  "exports": {
38
38
  ".": {
39
- "types": "./dist/index.d.ts",
40
- "import": "./dist/index.js"
39
+ "types": "./dist/dev/index.d.ts",
40
+ "development": "./dist/dev/index.js",
41
+ "import": "./dist/default/index.js"
41
42
  },
42
43
  "./define": {
43
- "types": "./dist/define.d.ts",
44
- "import": "./dist/define.js"
44
+ "types": "./dist/dev/define.d.ts",
45
+ "development": "./dist/dev/define.js",
46
+ "import": "./dist/default/define.js"
45
47
  },
46
48
  "./define/*": {
47
- "types": "./dist/define/*.d.ts",
48
- "import": "./dist/define/*.js"
49
+ "types": "./dist/dev/define/*.d.ts",
50
+ "development": "./dist/dev/define/*.js",
51
+ "import": "./dist/default/define/*.js"
49
52
  },
50
53
  "./controls.css": "./dist/styles/controls.css",
51
54
  "./material.css": "./dist/styles/material.css",
@@ -57,9 +60,7 @@
57
60
  "./slider.css": "./dist/styles/slider.css",
58
61
  "./skins/*.css": "./dist/skins/*.css"
59
62
  },
60
- "imports": {
61
- "#*": "./src/*"
62
- },
63
+ "imports": {},
63
64
  "files": [
64
65
  "dist"
65
66
  ],
@@ -1 +0,0 @@
1
- {"version":3,"file":"dial.js","names":[],"sources":["../../src/define/dial.ts"],"sourcesContent":["import { defineOnce } from '#elements/define-once.ts';\nimport { SonicDial } from '#elements/dial.ts';\n\ndefineOnce('sonic-dial', SonicDial);\n\n// Type-only, so the tag map augmentation ships without a runtime export\n/** @public */\nexport type { SonicDial } from '#elements/dial.ts';\n"],"mappings":";;;;AAGA,WAAW,cAAc,SAAS"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"key.js","names":[],"sources":["../../src/define/key.ts"],"sourcesContent":["import { defineOnce } from '#elements/define-once.ts';\nimport { SonicKey } from '#elements/key.ts';\n\ndefineOnce('sonic-key', SonicKey);\n\n// Type-only, so the tag map augmentation ships without a runtime export\n/** @public */\nexport type { SonicKey } from '#elements/key.ts';\n"],"mappings":";;;;AAGA,WAAW,aAAa,QAAQ"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"meter.js","names":[],"sources":["../../src/define/meter.ts"],"sourcesContent":["import { defineOnce } from '#elements/define-once.ts';\nimport { SonicMeter } from '#elements/meter.ts';\n\ndefineOnce('sonic-meter', SonicMeter);\n\n// Type-only, so the tag map augmentation ships without a runtime export\n/** @public */\nexport type { SonicMeter } from '#elements/meter.ts';\n"],"mappings":";;;;AAGA,WAAW,eAAe,UAAU"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"segmented.js","names":[],"sources":["../../src/define/segmented.ts"],"sourcesContent":["import { defineOnce } from '#elements/define-once.ts';\nimport { SonicSegmented } from '#elements/segmented.ts';\n\ndefineOnce('sonic-segmented', SonicSegmented);\n\n// Type-only, so the tag map augmentation ships without a runtime export\n/** @public */\nexport type { SonicSegmented } from '#elements/segmented.ts';\n"],"mappings":";;;;AAGA,WAAW,mBAAmB,cAAc"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"slider.js","names":[],"sources":["../../src/define/slider.ts"],"sourcesContent":["import { defineOnce } from '#elements/define-once.ts';\nimport { SonicSlider } from '#elements/slider.ts';\n\ndefineOnce('sonic-slider', SonicSlider);\n\n// Type-only, so the tag map augmentation ships without a runtime export\n/** @public */\nexport type { SonicSlider } from '#elements/slider.ts';\n"],"mappings":";;;;AAGA,WAAW,gBAAgB,WAAW"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"define-once.js","names":[],"sources":["../../src/elements/define-once.ts"],"sourcesContent":["export function defineOnce(tag: string, elementClass: CustomElementConstructor): void {\n\tif (!customElements.get(tag)) customElements.define(tag, elementClass);\n}\n"],"mappings":";AAAA,SAAgB,WAAW,KAAa,cAA8C;CACrF,IAAI,CAAC,eAAe,IAAI,GAAG,GAAG,eAAe,OAAO,KAAK,YAAY;AACtE"}