color-elements 0.0.3 → 0.0.5

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 (45) hide show
  1. package/.claude/settings.local.json +30 -0
  2. package/.editorconfig +8 -0
  3. package/.prettierrc +17 -0
  4. package/.vscode/settings.json +7 -0
  5. package/README.md +27 -18
  6. package/_build/copy-config.js +15 -14
  7. package/_build/copy-config.json +4 -9
  8. package/_build/eleventy.js +8 -8
  9. package/_includes/component.njk +9 -14
  10. package/_includes/partials/_nav-links.njk +1 -0
  11. package/_includes/plain.njk +5 -0
  12. package/_redirects +1 -1
  13. package/assets/css/style.css +4 -4
  14. package/debug.html +38 -439
  15. package/package.json +24 -8
  16. package/src/channel-picker/channel-picker.css +4 -4
  17. package/src/channel-picker/channel-picker.js +16 -12
  18. package/src/channel-slider/channel-slider.css +14 -7
  19. package/src/channel-slider/channel-slider.js +14 -6
  20. package/src/color-chart/README.md +36 -5
  21. package/src/color-chart/color-chart-global.css +19 -14
  22. package/src/color-chart/color-chart-shadow.css +123 -0
  23. package/src/color-chart/color-chart.css +2 -112
  24. package/src/color-chart/color-chart.js +309 -107
  25. package/src/color-inline/color-inline.css +21 -16
  26. package/src/color-inline/color-inline.js +3 -4
  27. package/src/color-inline/style.css +1 -1
  28. package/src/color-picker/color-picker.css +3 -3
  29. package/src/color-picker/color-picker.js +15 -8
  30. package/src/color-scale/README.md +42 -2
  31. package/src/color-scale/color-scale.css +8 -13
  32. package/src/color-scale/color-scale.js +14 -11
  33. package/src/color-slider/README.md +17 -3
  34. package/src/color-slider/color-slider.css +54 -33
  35. package/src/color-slider/color-slider.js +10 -8
  36. package/src/color-swatch/color-swatch.css +52 -34
  37. package/src/color-swatch/color-swatch.js +20 -10
  38. package/src/common/color-element.js +63 -51
  39. package/src/common/dom.js +4 -2
  40. package/src/common/util.js +66 -1
  41. package/src/gamut-badge/gamut-badge.css +33 -13
  42. package/src/gamut-badge/gamut-badge.js +10 -8
  43. package/src/space-picker/space-picker.css +3 -3
  44. package/src/space-picker/space-picker.js +29 -11
  45. package/src/src.json +1 -1
@@ -1,24 +1,28 @@
1
1
  :host {
2
- --_transparency-cell-size: var(--transparency-cell-size, .5em);
2
+ --_transparency-cell-size: var(--transparency-cell-size, 0.5em);
3
3
  --_transparency-background: var(--transparency-background, transparent);
4
4
  --_transparency-darkness: var(--transparency-darkness, 5%);
5
- --_transparency-grid: var(--transparency-grid,
6
- repeating-conic-gradient(transparent 0 25%, rgb(0 0 0 / var(--_transparency-darkness)) 0 50%)
7
- 0 0 / calc(2 * var(--_transparency-cell-size)) calc(2 * var(--_transparency-cell-size))
8
- content-box border-box var(--_transparency-background)
5
+ --_transparency-grid: var(
6
+ --transparency-grid,
7
+ repeating-conic-gradient(
8
+ transparent 0 25%,
9
+ rgb(0 0 0 / var(--_transparency-darkness)) 0 50%
10
+ )
11
+ 0 0 / calc(2 * var(--_transparency-cell-size)) calc(2 * var(--_transparency-cell-size))
12
+ content-box border-box var(--_transparency-background)
9
13
  );
10
14
 
11
15
  position: relative;
12
16
  display: inline-flex;
13
- gap: .3em;
14
- width: fit-content;
15
- margin: .3em;
16
- border-radius: .2rem;
17
+ gap: 0.3em;
18
+ width: var(--color-swatch-width, fit-content);
19
+ margin: 0.3em;
20
+ border-radius: var(--color-swatch-radius, 0.2rem);
17
21
  }
18
22
 
19
23
  :host([size="large"]) {
20
24
  flex-flow: column;
21
- inline-size: 11em;
25
+ inline-size: min(11em, 100%);
22
26
  min-block-size: 6em;
23
27
  contain: inline-size;
24
28
  container-name: color-swatch;
@@ -37,7 +41,7 @@ slot {
37
41
  position: absolute;
38
42
  top: 0;
39
43
  right: 0;
40
- margin: .5rem;
44
+ margin: 0.5rem;
41
45
  }
42
46
 
43
47
  &:not(:host([size="large"]) *) {
@@ -46,7 +50,7 @@ slot {
46
50
  font-size: 50%;
47
51
  top: 0;
48
52
  right: 0;
49
- margin: .2rem;
53
+ margin: 0.2rem;
50
54
  }
51
55
 
52
56
  &:is(.static *) {
@@ -54,8 +58,6 @@ slot {
54
58
  }
55
59
  }
56
60
 
57
-
58
-
59
61
  &[style*="--gamut-level: 0"] {
60
62
  display: none;
61
63
  }
@@ -65,12 +67,12 @@ slot {
65
67
  margin: 0;
66
68
  display: inline-flex;
67
69
  display: none;
68
- gap: .5em;
70
+ gap: 0.5em;
69
71
 
70
72
  &:is(:host([size="large"]) &) {
71
73
  display: grid;
72
74
  grid-template-columns: max-content auto;
73
- gap: .1em .2em;
75
+ gap: 0.1em 0.2em;
74
76
  font-size: max(9px, 80%);
75
77
  justify-content: start;
76
78
 
@@ -81,7 +83,7 @@ slot {
81
83
 
82
84
  .coord {
83
85
  display: flex;
84
- gap: .2em;
86
+ gap: 0.2em;
85
87
 
86
88
  dd {
87
89
  margin: 0;
@@ -96,9 +98,12 @@ slot {
96
98
  flex-flow: inherit;
97
99
  gap: inherit;
98
100
 
101
+ /* Prevent flex items from overflowing */
102
+ min-inline-size: 0;
103
+
99
104
  &.static {
100
105
  &:is(:host([size="large"]) *) {
101
- background: white;
106
+ background: canvas;
102
107
  }
103
108
  }
104
109
 
@@ -111,8 +116,11 @@ slot {
111
116
  }
112
117
 
113
118
  @container style(--details-style: compact) {
114
- --_border-color: var(--border-color, color-mix(in oklab, buttonborder 20%, oklab(none none none / 0%)));
115
- --_pointer-height: var(--pointer-height, .5em);
119
+ --_border-color: var(
120
+ --border-color,
121
+ color-mix(in oklab, buttonborder 20%, oklab(none none none / 0%))
122
+ );
123
+ --_pointer-height: var(--pointer-height, 0.5em);
116
124
  --_transition-duration: var(--transition-duration, 400ms);
117
125
  --_details-popup-width: var(--details-popup-width, max-content);
118
126
 
@@ -121,13 +129,13 @@ slot {
121
129
  z-index: 2;
122
130
  translate: -50% 0;
123
131
  bottom: 100%;
124
- margin-bottom: calc(var(--_pointer-height) * .8);
132
+ margin-bottom: calc(var(--_pointer-height) * 0.8);
125
133
  width: var(--_details-popup-width);
126
134
  background: canvas;
127
135
  border: 1px solid var(--_border-color);
128
- padding: .6em 1em;
129
- border-radius: .2rem;
130
- box-shadow: 0 .05em 1em -.7em black;
136
+ padding: 0.6em 1em;
137
+ border-radius: 0.2rem;
138
+ box-shadow: 0 0.05em 1em -0.7em black;
131
139
  transition: var(--_transition-duration) allow-discrete;
132
140
  transition-property: all, display;
133
141
  transition-delay: 0s, var(--_transition-duration);
@@ -162,8 +170,14 @@ slot {
162
170
  clip-path: polygon(0 0, 0 100%, 100% 100%);
163
171
  }
164
172
 
165
- &:not(:is(:host(:hover), :host(:focus-within), :host(:active), :host(:target), :host([open])) *),
166
- &:is(:host([open=false]) *) {
173
+ /*
174
+ More straightforward selector:
175
+ &:not(:is(:host(:hover), :host(:focus-within), :host(:active), :host(:target), :host([open])) *)
176
+ doesn't work in Safari!
177
+ See https://bugs.webkit.org/show_bug.cgi?id=296577
178
+ */
179
+ &:is(:host(:not(:hover):not(:focus-within):not(:active):not(:target):not([open])) *),
180
+ &:is(:host([open="false"]) *) {
167
181
  display: none;
168
182
  opacity: 0;
169
183
  scale: 0;
@@ -173,7 +187,13 @@ slot {
173
187
 
174
188
  [part="color"] {
175
189
  display: flex;
176
- gap: .2em;
190
+ gap: 0.2em;
191
+ }
192
+
193
+ [part="label"] {
194
+ overflow: hidden;
195
+ white-space: nowrap;
196
+ text-overflow: ellipsis;
177
197
  }
178
198
 
179
199
  slot:not([name]) {
@@ -190,25 +210,23 @@ slot:not([name]) {
190
210
  }
191
211
 
192
212
  [part="color-wrapper"],
193
- slot[name=swatch]::slotted(*),
213
+ slot[name="swatch"]::slotted(*),
194
214
  #swatch {
195
215
  border-radius: inherit;
196
216
  }
197
217
 
198
- slot[name=swatch]::slotted(*),
218
+ slot[name="swatch"]::slotted(*),
199
219
  #swatch {
200
220
  flex: 1;
201
221
  display: flex;
202
222
  flex-flow: column;
203
223
  align-items: center;
204
224
  justify-content: center;
205
- padding: .5em;
225
+ padding: 0.5em;
206
226
  display: flex;
207
227
  flex-flow: column;
208
228
  flex: 1;
209
- background:
210
- linear-gradient(var(--color) 0 100%),
211
- var(--_transparency-grid);
229
+ background: linear-gradient(var(--color) 0 100%), var(--_transparency-grid);
212
230
 
213
231
  &:is(:host([size="large"]) *) {
214
232
  min-block-size: 3em;
@@ -223,7 +241,7 @@ slot[name=swatch]::slotted(*),
223
241
  }
224
242
  }
225
243
 
226
- slot[name=swatch-content] {
244
+ slot[name="swatch-content"] {
227
245
  /* See https://lea.verou.me/blog/2024/contrast-color/ */
228
246
  --l: clamp(0, (var(--l-threshold, 0.7) / l - 1) * infinity, 1);
229
247
  color: oklch(from var(--color) var(--l) 0 h);
@@ -7,7 +7,7 @@ const Self = class ColorSwatch extends ColorElement {
7
7
  static tagName = "color-swatch";
8
8
  static url = import.meta.url;
9
9
  static dependencies = new Set(["gamut-badge"]);
10
- static shadowStyle = true;
10
+ static styles = "./color-swatch.css";
11
11
  static shadowTemplate = `
12
12
  <slot name="swatch">
13
13
  <div id="swatch" part="swatch">
@@ -42,7 +42,7 @@ const Self = class ColorSwatch extends ColorElement {
42
42
 
43
43
  #updateStatic () {
44
44
  let previousInput = this._el.input;
45
- let input = this._el.input = this.querySelector("input");
45
+ let input = (this._el.input = this.querySelector("input"));
46
46
 
47
47
  this.static = !input;
48
48
 
@@ -50,7 +50,9 @@ const Self = class ColorSwatch extends ColorElement {
50
50
  this._el.wrapper.classList.toggle("static", this.static);
51
51
 
52
52
  if (input && input !== previousInput) {
53
- importIncrementable ??= import("https://incrementable.verou.me/incrementable.mjs").then(m => m.default);
53
+ importIncrementable ??= import("https://incrementable.verou.me/incrementable.mjs").then(
54
+ m => m.default,
55
+ );
54
56
  importIncrementable?.then(Incrementable => new Incrementable(input));
55
57
 
56
58
  input.addEventListener("input", evt => {
@@ -65,10 +67,14 @@ const Self = class ColorSwatch extends ColorElement {
65
67
 
66
68
  get swatchTextContent () {
67
69
  // Children that are not assigned to another slot
68
- return [...this.childNodes].filter(n => !n.slot).map(n => n.textContent).join("").trim();
70
+ return [...this.childNodes]
71
+ .filter(n => !n.slot)
72
+ .map(n => n.textContent)
73
+ .join("")
74
+ .trim();
69
75
  }
70
76
 
71
- propChangedCallback ({name, prop, detail: change}) {
77
+ propChangedCallback ({ name, prop, detail: change }) {
72
78
  let input = this._el.input;
73
79
 
74
80
  if (name === "gamuts") {
@@ -91,9 +97,11 @@ const Self = class ColorSwatch extends ColorElement {
91
97
  this._el.gamutIndicator.addEventListener("gamutchange", evt => {
92
98
  let gamut = this._el.gamutIndicator.gamut;
93
99
  this.setAttribute("gamut", gamut);
94
- this.dispatchEvent(new CustomEvent("gamutchange", {
95
- detail: gamut,
96
- }));
100
+ this.dispatchEvent(
101
+ new CustomEvent("gamutchange", {
102
+ detail: gamut,
103
+ }),
104
+ );
97
105
  });
98
106
  }
99
107
  else {
@@ -111,9 +119,11 @@ const Self = class ColorSwatch extends ColorElement {
111
119
  if (name === "label") {
112
120
  if (this.label.length && this.label !== this.swatchTextContent) {
113
121
  this._el.label.textContent = this.label;
122
+ this._el.label.title = this.label;
114
123
  }
115
124
  else {
116
125
  this._el.label.textContent = "";
126
+ this._el.label.title = "";
117
127
  }
118
128
  }
119
129
 
@@ -135,7 +145,7 @@ const Self = class ColorSwatch extends ColorElement {
135
145
  return;
136
146
  }
137
147
 
138
- this._el.info ??= Object.assign(document.createElement("dl"), {part: "info"});
148
+ this._el.info ??= Object.assign(document.createElement("dl"), { part: "info" });
139
149
  if (!this._el.info.parentNode) {
140
150
  this._el.colorWrapper.after(this._el.info);
141
151
  }
@@ -151,7 +161,7 @@ const Self = class ColorSwatch extends ColorElement {
151
161
 
152
162
  value = typeof value === "number" ? Number(value.toPrecision(4)) : value;
153
163
 
154
- info.push(`<div class="coord"><dt>${ label }</dt><dd>${ value }</dd></div>`);
164
+ info.push(`<div class="coord"><dt>${label}</dt><dd>${value}</dd></div>`);
155
165
  }
156
166
 
157
167
  this._el.info.innerHTML = info.join("\n");
@@ -1,85 +1,97 @@
1
- import NudeElement from "../../node_modules/nude-element/src/Element.js";
2
- import { getType, wait } from "./util.js";
1
+ import Color from "colorjs.io";
2
+ import NudeElement from "nude-element";
3
+ import { states } from "nude-element/plugins";
4
+ import { defer, wait, dynamicAll, noOpTemplateTag as css } from "./util.js";
5
+
6
+ const baseGlobalStyles = css`
7
+ @keyframes fade-in {
8
+ from {
9
+ opacity: 0;
10
+ }
11
+ }
12
+
13
+ :state(color-element) {
14
+ &:state(loading) {
15
+ content-visibility: hidden;
16
+ opacity: 0;
17
+
18
+ &,
19
+ * {
20
+ transition-property: opacity !important;
21
+ }
22
+ }
23
+
24
+ &:not(:state(loading)) {
25
+ animation: fade-in 300ms both;
26
+ }
27
+ }
28
+ `;
3
29
 
4
30
  const Self = class ColorElement extends NudeElement {
31
+ static url = import.meta.url;
5
32
  // TODO make lazy
6
33
  static Color;
7
34
  static all = {};
8
35
  static dependencies = new Set();
9
36
 
37
+ static globalStyles = [{ css: baseGlobalStyles }];
38
+ static plugins = [states];
39
+
10
40
  constructor () {
11
41
  super();
12
42
 
13
- if (this.constructor.shadowTemplate !== undefined) {
14
- this.attachShadow({mode: "open"});
15
- this.shadowRoot.innerHTML = this.constructor.shadowTemplate;
43
+ let Self = this.constructor;
44
+
45
+ if (Self.shadowTemplate !== undefined) {
46
+ this.attachShadow({ mode: "open" });
47
+ this.shadowRoot.innerHTML = Self.shadowTemplate;
16
48
  }
49
+
50
+ this.toggleState("color-element", true);
51
+ this.toggleState("loading", true);
52
+ Self.whenReady.then(() => {
53
+ this.toggleState("loading");
54
+ });
17
55
  }
18
56
 
57
+ static ready = [defer()];
58
+ static whenReady = dynamicAll(this.ready);
59
+
19
60
  static async define () {
61
+ // Overwrite static properties, otherwise they will be shared across subclasses
62
+ this.ready = [defer()];
63
+ this.whenReady = dynamicAll(this.ready);
64
+
65
+ if (!Object.hasOwn(this, "dependencies")) {
66
+ this.dependencies = new Set();
67
+ }
68
+
20
69
  Self.all[this.tagName] = this;
21
70
  let colorTags = Object.keys(Self.all);
22
71
 
23
72
  if (this.shadowTemplate) {
24
73
  // TODO find dependencies
25
- let colorTagRegex = RegExp(`(?<=</)(${ colorTags.join("|") })(?=>)`, "g");
74
+ let colorTagRegex = RegExp(`(?<=</)(${colorTags.join("|")})(?=>)`, "g");
26
75
  (this.shadowTemplate.match(colorTagRegex) ?? []).forEach(tag => {
27
76
  this.dependencies ??= new Set();
28
77
  this.dependencies.add(tag);
29
78
  });
30
-
31
- if (this.shadowStyle) {
32
- let url = this.shadowStyle;
33
- url = url === true ? `./${this.tagName}.css` : url;
34
- url = new URL(url, this.url);
35
- this.shadowTemplate = `<style>@import url("${ url }")</style>` + "\n" + this.shadowTemplate;
36
- }
37
- }
38
-
39
- // Hide elements before they are defined
40
- let style = document.getElementById("color-element-styles")
41
- ?? Object.assign(document.createElement("style"), {id: "color-element-styles"});
42
- style.textContent = `:is(${ colorTags.join(", ") }):not(:defined) {display: none}`;
43
- if (!style.parentNode) {
44
- document.head.append(style);
45
79
  }
46
80
 
47
81
  if (this.dependencies.size > 0) {
48
- await Promise.all([...this.dependencies].map(tag => customElements.whenDefined(tag)));
49
- }
50
- else {
51
- // Give other code a chance to overwrite Self.Color
52
- await wait();
82
+ let whenDefined = [...this.dependencies].map(tag =>
83
+ customElements.whenDefined(tag).then(Class => Class.whenReady));
84
+ this.ready.push(...whenDefined);
53
85
  }
54
86
 
55
- if (!Self.Color) {
56
- let specifier;
87
+ // Give other code a chance to overwrite Self.Color
88
+ await wait();
57
89
 
58
- try {
59
- // Is already loaded? (e.g. via an import map, or if we're in Node)
60
- import.meta.resolve("colorjs.io");
61
- specifier = "colorjs.io";
62
- }
63
- catch (e) {
64
- // specifier = "../../node_modules/colorjs.io/dist/color.js";
65
- specifier = "https://colorjs.io/dist/color.js";
66
- }
67
-
68
- Self.Color = import(specifier).then(module => module.default);
69
- }
70
-
71
- // We can't just use top level await, see https://bugs.webkit.org/show_bug.cgi?id=242740
72
- if (getType(Self.Color) === "Promise") {
73
- let ColorPending = Self.Color;
74
- let Color = await ColorPending;
75
-
76
- if (Self.Color === ColorPending) {
77
- // Hasn't changed
78
- Self.Color = Color;
79
- }
80
- }
90
+ Self.Color ??= Color;
81
91
 
82
92
  customElements.define(this.tagName, this);
93
+
94
+ this.ready[0].resolve();
83
95
  }
84
96
  };
85
97
 
package/src/common/dom.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export function named (host, attributes = ["id", "part"]) {
2
2
  let ret = {};
3
- let selector = attributes.map(attr => `[${ attr }]`).join(", ");
3
+ let selector = attributes.map(attr => `[${attr}]`).join(", ");
4
4
 
5
5
  for (let el of host.shadowRoot.querySelectorAll(selector)) {
6
6
  // Get the value of the first attribute in attributes that has a value
@@ -27,7 +27,9 @@ export function slots (host) {
27
27
 
28
28
  export function toSlots ({
29
29
  slots = this._slots,
30
- slotElements = slots ? Object.values(slots) : Array.from(this.shadowRoot.querySelectorAll("slot")),
30
+ slotElements = slots
31
+ ? Object.values(slots)
32
+ : Array.from(this.shadowRoot.querySelectorAll("slot")),
31
33
  }) {
32
34
  let children = this.childNodes;
33
35
  let assignments = new WeakMap();
@@ -6,13 +6,68 @@ export async function wait (ms) {
6
6
  return new Promise(resolve => setTimeout(resolve, ms));
7
7
  }
8
8
 
9
+ export function defer (executor) {
10
+ let res, rej;
11
+
12
+ let promise = new Promise((resolve, reject) => {
13
+ res = resolve;
14
+ rej = reject;
15
+
16
+ executor?.(res, rej);
17
+ });
18
+
19
+ promise.resolve = res;
20
+ promise.reject = rej;
21
+
22
+ return promise;
23
+ }
24
+
25
+ /**
26
+ * Wait for all promises to resolve. Supports dynamically adding promises to the list after the initial call.
27
+ * @param {Promise[] | Set<Promise>} promises
28
+ * @returns {Promise}
29
+ */
30
+ export async function dynamicAll (promises) {
31
+ let all = new Set([...promises]);
32
+ let unresolved = new Set();
33
+
34
+ for (let promise of promises) {
35
+ if (promise?.then) {
36
+ unresolved.add(promise);
37
+ promise.then(() => {
38
+ // Remove the promise from the list
39
+ unresolved.delete(promise);
40
+ });
41
+ }
42
+ }
43
+
44
+ return Promise.all(unresolved).then(resolved => {
45
+ // Check if the array has new items
46
+ for (let promise of promises) {
47
+ if (!all.has(promise)) {
48
+ all.add(promise);
49
+
50
+ if (promise?.then) {
51
+ unresolved.add(promise);
52
+ }
53
+ }
54
+ }
55
+
56
+ if (unresolved.size > 0) {
57
+ return dynamicAll(unresolved).then(r => [...resolved, ...r]);
58
+ }
59
+
60
+ return resolved;
61
+ });
62
+ }
63
+
9
64
  /**
10
65
  * Compute the ideal step for a range, to be used as a default in spinners and sliders
11
66
  * @param {number} min
12
67
  * @param {number} max
13
68
  * @param {options} options
14
69
  */
15
- export function getStep (min, max, {minSteps = 100, maxStep = 1} = {}) {
70
+ export function getStep (min, max, { minSteps = 100, maxStep = 1 } = {}) {
16
71
  let range = Math.abs(max - min);
17
72
  let step = range / minSteps;
18
73
 
@@ -53,3 +108,13 @@ export function getType (value) {
53
108
 
54
109
  return Object.prototype.toString.call(value).slice(8, -1);
55
110
  }
111
+
112
+ /**
113
+ * Template tag that does nothing. Useful for importing under different names (e.g. `css`) for syntax highlighting.
114
+ * @param {string[]} strings
115
+ * @param {...any} values
116
+ * @returns {string}
117
+ */
118
+ export function noOpTemplateTag (strings, ...values) {
119
+ return strings.reduce((acc, string, i) => acc + string + (values[i] ?? ""), "");
120
+ }
@@ -7,27 +7,47 @@
7
7
  --_color-invalid: var(--color-invalid, hsl(220 10% 60%));
8
8
 
9
9
  /* Low-level scales, i.e. between pairs of our core colors */
10
- --color-scale-1: color-mix(in oklch, var(--_color-green), var(--_color-yellow) var(--progress-1));
11
- --color-scale-2: color-mix(in oklch, var(--_color-yellow), var(--_color-orange) var(--progress-2));
10
+ --color-scale-1: color-mix(
11
+ in oklch,
12
+ var(--_color-green),
13
+ var(--_color-yellow) var(--progress-1)
14
+ );
15
+ --color-scale-2: color-mix(
16
+ in oklch,
17
+ var(--_color-yellow),
18
+ var(--_color-orange) var(--progress-2)
19
+ );
12
20
  --color-scale-3: color-mix(in oklch, var(--_color-orange), var(--_color-red) var(--progress-3));
13
21
 
14
22
  /* Recursive scales: their only purpose is to select one of the low-level scales, and will only ever have 0%/100% positions
15
23
  For N colors there are N-2 + N-3 + ... + 1 = (N-1)(N-2) / 2 of these
16
24
  */
17
- --color-scale-12: color-mix(in oklch, var(--color-scale-1), var(--color-scale-2) var(--progress-12));
18
- --color-scale-23: color-mix(in oklch, var(--color-scale-2), var(--color-scale-3) var(--progress-23));
19
- --color-scale-123: color-mix(in oklch, var(--color-scale-12), var(--color-scale-23) var(--progress-123));
25
+ --color-scale-12: color-mix(
26
+ in oklch,
27
+ var(--color-scale-1),
28
+ var(--color-scale-2) var(--progress-12)
29
+ );
30
+ --color-scale-23: color-mix(
31
+ in oklch,
32
+ var(--color-scale-2),
33
+ var(--color-scale-3) var(--progress-23)
34
+ );
35
+ --color-scale-123: color-mix(
36
+ in oklch,
37
+ var(--color-scale-12),
38
+ var(--color-scale-23) var(--progress-123)
39
+ );
20
40
 
21
41
  --gamut-progress: calc(var(--gamut-level) / (var(--gamut-count) - 1));
22
42
  --progress-ext: calc(var(--gamut-progress) * 300%);
23
43
 
24
- --progress-123: clamp(0%, (var(--progress-ext) - 150%) * infinity, 100%);
25
- --progress-12: clamp(0%, (var(--progress-ext) - 150% + 75%) * infinity, 100%);
26
- --progress-23: clamp(0%, (var(--progress-ext) - 150% - 75%) * infinity, 100%);
44
+ --progress-123: clamp(0%, (var(--progress-ext) - 150%) * infinity, 100%);
45
+ --progress-12: clamp(0%, (var(--progress-ext) - 150% + 75%) * infinity, 100%);
46
+ --progress-23: clamp(0%, (var(--progress-ext) - 150% - 75%) * infinity, 100%);
27
47
 
28
- --progress-1: clamp(0%, var(--progress-ext), 100%);
29
- --progress-2: calc(clamp(100%, var(--progress-ext), 200%) - 100%);
30
- --progress-3: calc(clamp(200%, var(--progress-ext), 300%) - 200%);
48
+ --progress-1: clamp(0%, var(--progress-ext), 100%);
49
+ --progress-2: calc(clamp(100%, var(--progress-ext), 200%) - 100%);
50
+ --progress-3: calc(clamp(200%, var(--progress-ext), 300%) - 200%);
31
51
 
32
52
  --color-level-infinity: var(--_color-red-dark);
33
53
  --color: var(--color-scale-123, var(--_color-invalid));
@@ -35,11 +55,11 @@
35
55
  display: inline-flex;
36
56
  align-items: center;
37
57
  justify-content: center;
38
- border-radius: .2em;
58
+ border-radius: 0.2em;
39
59
  color: white;
40
60
  background-color: var(--color);
41
61
  font-weight: bold;
42
- padding-inline: .4em;
62
+ padding-inline: 0.4em;
43
63
  line-height: 1.4;
44
64
 
45
65
  /* See https://lea.verou.me/blog/2024/contrast-color/ */
@@ -3,7 +3,7 @@ import ColorElement from "../common/color-element.js";
3
3
  const Self = class GamutBadge extends ColorElement {
4
4
  static tagName = "gamut-badge";
5
5
  static url = import.meta.url;
6
- static shadowStyle = true;
6
+ static styles = "./gamut-badge.css";
7
7
  static shadowTemplate = `
8
8
  <slot>
9
9
  <span id="label" part="label"></span>
@@ -23,7 +23,7 @@ const Self = class GamutBadge extends ColorElement {
23
23
  return this.gamutInfo?.label ?? "";
24
24
  }
25
25
 
26
- propChangedCallback ({name, prop, detail: change}) {
26
+ propChangedCallback ({ name, prop, detail: change }) {
27
27
  if (name === "gamuts") {
28
28
  this.style.setProperty("--gamut-count", this.gamuts.length - 1);
29
29
  }
@@ -31,8 +31,8 @@ const Self = class GamutBadge extends ColorElement {
31
31
  if (name === "gamutInfo") {
32
32
  if (this.gamutInfo) {
33
33
  this.style.setProperty("--gamut-level", this.gamutInfo.level);
34
- this.style.setProperty("--gamut-label", `"${ this.gamutInfo.label }"`);
35
- this.style.setProperty("--gamut-id", `"${ this.gamutInfo.id }"`);
34
+ this.style.setProperty("--gamut-label", `"${this.gamutInfo.label}"`);
35
+ this.style.setProperty("--gamut-id", `"${this.gamutInfo.id}"`);
36
36
  }
37
37
  else {
38
38
  this.style.removeProperty("--gamut-level");
@@ -61,7 +61,7 @@ const Self = class GamutBadge extends ColorElement {
61
61
  }
62
62
  else if (!Array.isArray(gamuts) && typeof gamuts === "object") {
63
63
  // Object
64
- return Object.entries(gamuts).map(([id, label]) => ({id, label}));
64
+ return Object.entries(gamuts).map(([id, label]) => ({ id, label }));
65
65
  }
66
66
 
67
67
  let ret = gamuts.map((gamut, level) => {
@@ -73,7 +73,7 @@ const Self = class GamutBadge extends ColorElement {
73
73
  gamut = gamut.trim().split(/\s*:\s*/);
74
74
  let id = gamut[0];
75
75
  let label = gamut[1] ?? Self.Color.spaces[id]?.name ?? id;
76
- return {id, label, level};
76
+ return { id, label, level };
77
77
  });
78
78
 
79
79
  if (!ret.find(gamut => gamut.id === "none")) {
@@ -89,7 +89,7 @@ const Self = class GamutBadge extends ColorElement {
89
89
  return ret;
90
90
  },
91
91
  stringify (gamuts) {
92
- return gamuts.map(({id, label}) => `${ id }: ${ label }`).join(", ");
92
+ return gamuts.map(({ id, label }) => `${id}: ${label}`).join(", ");
93
93
  },
94
94
  },
95
95
  gamutInfo: {
@@ -98,7 +98,9 @@ const Self = class GamutBadge extends ColorElement {
98
98
  return null;
99
99
  }
100
100
 
101
- return this.gamuts?.find(gamut => gamut.id === "none" || this.color?.inGamut(gamut.id));
101
+ return this.gamuts?.find(
102
+ gamut => gamut.id === "none" || this.color?.inGamut(gamut.id),
103
+ );
102
104
  },
103
105
  },
104
106
  gamut: {