color-elements 0.0.4 → 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 (44) 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/plain.njk +5 -0
  11. package/_redirects +1 -1
  12. package/assets/css/style.css +4 -4
  13. package/debug.html +46 -0
  14. package/package.json +19 -8
  15. package/src/channel-picker/channel-picker.css +4 -4
  16. package/src/channel-picker/channel-picker.js +15 -11
  17. package/src/channel-slider/channel-slider.css +14 -7
  18. package/src/channel-slider/channel-slider.js +13 -5
  19. package/src/color-chart/README.md +36 -5
  20. package/src/color-chart/color-chart-global.css +13 -12
  21. package/src/color-chart/color-chart-shadow.css +123 -0
  22. package/src/color-chart/color-chart.css +2 -112
  23. package/src/color-chart/color-chart.js +307 -103
  24. package/src/color-inline/color-inline.css +21 -16
  25. package/src/color-inline/color-inline.js +2 -3
  26. package/src/color-inline/style.css +1 -1
  27. package/src/color-picker/color-picker.css +3 -3
  28. package/src/color-picker/color-picker.js +14 -7
  29. package/src/color-scale/README.md +42 -2
  30. package/src/color-scale/color-scale.css +1 -1
  31. package/src/color-scale/color-scale.js +12 -9
  32. package/src/color-slider/README.md +17 -3
  33. package/src/color-slider/color-slider.css +54 -33
  34. package/src/color-slider/color-slider.js +9 -7
  35. package/src/color-swatch/color-swatch.css +41 -32
  36. package/src/color-swatch/color-swatch.js +17 -9
  37. package/src/common/color-element.js +34 -76
  38. package/src/common/dom.js +4 -2
  39. package/src/common/util.js +1 -1
  40. package/src/gamut-badge/gamut-badge.css +33 -13
  41. package/src/gamut-badge/gamut-badge.js +9 -7
  42. package/src/space-picker/space-picker.css +3 -3
  43. package/src/space-picker/space-picker.js +28 -10
  44. package/src/src.json +1 -1
@@ -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 {
@@ -137,7 +145,7 @@ const Self = class ColorSwatch extends ColorElement {
137
145
  return;
138
146
  }
139
147
 
140
- this._el.info ??= Object.assign(document.createElement("dl"), {part: "info"});
148
+ this._el.info ??= Object.assign(document.createElement("dl"), { part: "info" });
141
149
  if (!this._el.info.parentNode) {
142
150
  this._el.colorWrapper.after(this._el.info);
143
151
  }
@@ -153,7 +161,7 @@ const Self = class ColorSwatch extends ColorElement {
153
161
 
154
162
  value = typeof value === "number" ? Number(value.toPrecision(4)) : value;
155
163
 
156
- 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>`);
157
165
  }
158
166
 
159
167
  this._el.info.innerHTML = info.join("\n");
@@ -1,28 +1,32 @@
1
- import NudeElement from "../../node_modules/nude-element/src/Element.js";
2
- import { getType, defer, wait, dynamicAll, noOpTemplateTag as css } 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";
3
5
 
4
6
  const baseGlobalStyles = css`
5
- @keyframes fade-in {
6
- from { opacity: 0; }
7
- }
7
+ @keyframes fade-in {
8
+ from {
9
+ opacity: 0;
10
+ }
11
+ }
8
12
 
9
- :state(color-element) {
10
- &:state(loading) {
11
- content-visibility: hidden;
12
- opacity: 0;
13
+ :state(color-element) {
14
+ &:state(loading) {
15
+ content-visibility: hidden;
16
+ opacity: 0;
13
17
 
14
- &, * {
15
- transition-property: opacity !important;
18
+ &,
19
+ * {
20
+ transition-property: opacity !important;
21
+ }
16
22
  }
17
- }
18
23
 
19
- &:not(:state(loading)) {
20
- xanimation: fade-in 300ms both;
24
+ &:not(:state(loading)) {
25
+ animation: fade-in 300ms both;
26
+ }
21
27
  }
22
- }
23
28
  `;
24
29
 
25
-
26
30
  const Self = class ColorElement extends NudeElement {
27
31
  static url = import.meta.url;
28
32
  // TODO make lazy
@@ -30,7 +34,8 @@ const Self = class ColorElement extends NudeElement {
30
34
  static all = {};
31
35
  static dependencies = new Set();
32
36
 
33
- static globalStyles = [{css: baseGlobalStyles}];
37
+ static globalStyles = [{ css: baseGlobalStyles }];
38
+ static plugins = [states];
34
39
 
35
40
  constructor () {
36
41
  super();
@@ -38,40 +43,15 @@ const Self = class ColorElement extends NudeElement {
38
43
  let Self = this.constructor;
39
44
 
40
45
  if (Self.shadowTemplate !== undefined) {
41
- this.attachShadow({mode: "open"});
46
+ this.attachShadow({ mode: "open" });
42
47
  this.shadowRoot.innerHTML = Self.shadowTemplate;
43
48
  }
44
49
 
45
- this._internals ??= this.attachInternals?.();
46
- if (this._internals.states) {
47
- this._internals.states.add("color-element");
48
-
49
- this._internals.states.add("loading");
50
- Self.whenReady.then(() => {
51
- this._internals.states.delete("loading");
52
- });
53
- }
54
- }
55
-
56
- static init () {
57
- let wasInitialized = super.init();
58
-
59
- if (!wasInitialized) {
60
- return wasInitialized;
61
- }
62
-
63
- if (this.fetchedStyles) {
64
- this.ready.push(...this.fetchedStyles);
65
- }
66
-
67
- if (this.fetchedGlobalStyles) {
68
- this.ready.push(...this.fetchedGlobalStyles );
69
- }
70
-
71
-
72
- this.ready[0].resolve();
73
-
74
- return wasInitialized;
50
+ this.toggleState("color-element", true);
51
+ this.toggleState("loading", true);
52
+ Self.whenReady.then(() => {
53
+ this.toggleState("loading");
54
+ });
75
55
  }
76
56
 
77
57
  static ready = [defer()];
@@ -91,7 +71,7 @@ const Self = class ColorElement extends NudeElement {
91
71
 
92
72
  if (this.shadowTemplate) {
93
73
  // TODO find dependencies
94
- let colorTagRegex = RegExp(`(?<=</)(${ colorTags.join("|") })(?=>)`, "g");
74
+ let colorTagRegex = RegExp(`(?<=</)(${colorTags.join("|")})(?=>)`, "g");
95
75
  (this.shadowTemplate.match(colorTagRegex) ?? []).forEach(tag => {
96
76
  this.dependencies ??= new Set();
97
77
  this.dependencies.add(tag);
@@ -99,41 +79,19 @@ const Self = class ColorElement extends NudeElement {
99
79
  }
100
80
 
101
81
  if (this.dependencies.size > 0) {
102
- let whenDefined = [...this.dependencies].map(tag => customElements.whenDefined(tag).then(Class => Class.whenReady));
82
+ let whenDefined = [...this.dependencies].map(tag =>
83
+ customElements.whenDefined(tag).then(Class => Class.whenReady));
103
84
  this.ready.push(...whenDefined);
104
85
  }
105
86
 
106
87
  // Give other code a chance to overwrite Self.Color
107
88
  await wait();
108
89
 
109
- if (!Self.Color) {
110
- let specifier;
111
-
112
- try {
113
- // Is already loaded? (e.g. via an import map, or if we're in Node)
114
- import.meta.resolve("colorjs.io");
115
- specifier = "colorjs.io";
116
- }
117
- catch (e) {
118
- // specifier = "../../node_modules/colorjs.io/dist/color.js";
119
- specifier = "https://colorjs.io/dist/color.js";
120
- }
121
-
122
- Self.Color = import(specifier).then(module => module.default);
123
- }
124
-
125
- // We can't just use top level await, see https://bugs.webkit.org/show_bug.cgi?id=242740
126
- if (getType(Self.Color) === "Promise") {
127
- let ColorPending = Self.Color;
128
- let Color = await ColorPending;
129
-
130
- if (Self.Color === ColorPending) {
131
- // Hasn't changed
132
- Self.Color = Color;
133
- }
134
- }
90
+ Self.Color ??= Color;
135
91
 
136
92
  customElements.define(this.tagName, this);
93
+
94
+ this.ready[0].resolve();
137
95
  }
138
96
  };
139
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();
@@ -67,7 +67,7 @@ export async function dynamicAll (promises) {
67
67
  * @param {number} max
68
68
  * @param {options} options
69
69
  */
70
- export function getStep (min, max, {minSteps = 100, maxStep = 1} = {}) {
70
+ export function getStep (min, max, { minSteps = 100, maxStep = 1 } = {}) {
71
71
  let range = Math.abs(max - min);
72
72
  let step = range / minSteps;
73
73
 
@@ -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/ */
@@ -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: {
@@ -1,9 +1,9 @@
1
1
  :host {
2
2
  --border-width: 1px;
3
- --border-color: rgb(0 0 0 / .2);
4
- --border-radius: .2em;
3
+ --border-color: rgb(0 0 0 / 0.2);
4
+ --border-radius: 0.2em;
5
5
 
6
- padding: .3em .5em;
6
+ padding: 0.3em 0.5em;
7
7
 
8
8
  border-radius: var(--border-radius);
9
9
  border: var(--border-width) solid var(--border-color);
@@ -29,11 +29,14 @@ const Self = class SpacePicker extends ColorElement {
29
29
  }
30
30
  }
31
31
 
32
- propChangedCallback ({name, prop, detail: change}) {
32
+ propChangedCallback ({ name, prop, detail: change }) {
33
33
  if (name === "spaces") {
34
34
  if (!this.groups) {
35
35
  this._el.picker.innerHTML = Object.entries(this.spaces)
36
- .map(([id, space]) => `<option value="${ id }">${ this.getSpaceLabel(space) }</option>`)
36
+ .map(
37
+ ([id, space]) =>
38
+ `<option value="${id}">${this.getSpaceLabel(space)}</option>`,
39
+ )
37
40
  .join("\n");
38
41
  }
39
42
  else {
@@ -42,7 +45,9 @@ const Self = class SpacePicker extends ColorElement {
42
45
  // Remove empty groups
43
46
  groups = Object.entries(groups).filter(([type, spaces]) => {
44
47
  if (Object.keys(spaces).length === 0) {
45
- console.warn(`Removed empty group of color spaces with the label "${type}."`);
48
+ console.warn(
49
+ `Removed empty group of color spaces with the label "${type}."`,
50
+ );
46
51
  return false;
47
52
  }
48
53
 
@@ -50,17 +55,26 @@ const Self = class SpacePicker extends ColorElement {
50
55
  });
51
56
 
52
57
  if (!groups.length) {
53
- console.warn("All provided groups of color spaces are empty. Falling back to default grouping.");
58
+ console.warn(
59
+ "All provided groups of color spaces are empty. Falling back to default grouping.",
60
+ );
54
61
  groups = [["All spaces", this.spaces]];
55
62
  }
56
63
 
57
- this._el.picker.innerHTML = groups.map(([type, spaces]) => `
64
+ this._el.picker.innerHTML = groups
65
+ .map(
66
+ ([type, spaces]) => `
58
67
  <optgroup label="${type}">
59
68
  ${Object.entries(spaces)
60
- .map(([id, space]) => `<option value="${ id }">${ this.getSpaceLabel(space) }</option>`)
69
+ .map(
70
+ ([id, space]) =>
71
+ `<option value="${id}">${this.getSpaceLabel(space)}</option>`,
72
+ )
61
73
  .join("\n")}
62
74
  </optgroup>
63
- `).join("\n");
75
+ `,
76
+ )
77
+ .join("\n");
64
78
  }
65
79
 
66
80
  this._el.picker.value = this.value;
@@ -76,7 +90,9 @@ const Self = class SpacePicker extends ColorElement {
76
90
  let currentSpace = this._el.picker.value;
77
91
  let fallback = spaces.includes(currentSpace) ? currentSpace : firstSpace;
78
92
 
79
- console.warn(`No color space found with id = "${ value }". Choose one of the following: ${ spaces.join(", ") }. Falling back to "${ fallback }".`);
93
+ console.warn(
94
+ `No color space found with id = "${value}". Choose one of the following: ${spaces.join(", ")}. Falling back to "${fallback}".`,
95
+ );
80
96
  this.value = value = fallback;
81
97
  }
82
98
 
@@ -140,7 +156,9 @@ const Self = class SpacePicker extends ColorElement {
140
156
  return value;
141
157
  },
142
158
  stringify (value) {
143
- return Object.entries(value).map(([id, space]) => id).join(", ");
159
+ return Object.entries(value)
160
+ .map(([id, space]) => id)
161
+ .join(", ");
144
162
  },
145
163
  },
146
164
 
@@ -201,7 +219,7 @@ const Self = class SpacePicker extends ColorElement {
201
219
  },
202
220
  };
203
221
 
204
- static formAssociated = {
222
+ static formBehavior = {
205
223
  like: el => el._el.picker,
206
224
  role: "combobox",
207
225
  changeEvent: "change",
package/src/src.json CHANGED
@@ -1,3 +1,3 @@
1
1
  {
2
2
  "layout": "component"
3
- }
3
+ }