color-elements 0.0.1 → 0.0.2

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 (47) hide show
  1. package/README.md +33 -11
  2. package/_build/copy-config.js +11 -1
  3. package/_build/copy-config.json +1 -1
  4. package/_build/eleventy.js +21 -0
  5. package/_build/filters-extra.js +3 -0
  6. package/_data/components.json +8 -0
  7. package/_includes/component.njk +61 -0
  8. package/_includes/partials/_nav-links.njk +12 -0
  9. package/_redirects +1 -1
  10. package/assets/js/index.js +8 -7
  11. package/eslint.config.js +316 -0
  12. package/index.js +6 -4
  13. package/logo.svg +22 -44
  14. package/package.json +11 -5
  15. package/src/channel-slider/README.md +123 -0
  16. package/src/channel-slider/channel-slider.css +19 -0
  17. package/src/channel-slider/channel-slider.js +252 -0
  18. package/{color-gamut → src/color-gamut}/README.md +1 -1
  19. package/{color-gamut → src/color-gamut}/color-gamut.js +1 -1
  20. package/src/color-inline/README.md +31 -0
  21. package/{color-swatch/color-swatch.js → src/color-inline/color-inline.js} +6 -6
  22. package/src/color-inline/style.css +14 -0
  23. package/src/color-picker/README.md +48 -0
  24. package/src/color-picker/color-picker.css +20 -0
  25. package/src/color-picker/color-picker.js +164 -0
  26. package/src/color-slider/README.md +193 -0
  27. package/src/color-slider/color-slider.css +164 -0
  28. package/src/color-slider/color-slider.js +278 -0
  29. package/src/color-swatch/README.md +100 -0
  30. package/src/color-swatch/color-swatch.css +95 -0
  31. package/{css-color/css-color.js → src/color-swatch/color-swatch.js} +30 -8
  32. package/src/common/color.js +12 -0
  33. package/src/common/dom.js +61 -0
  34. package/src/common/util.js +142 -0
  35. package/src/index.js.njk +7 -0
  36. package/src/src.json +3 -0
  37. package/_data/eleventyComputed.11tydata.js +0 -29
  38. package/color-slider/README.md +0 -84
  39. package/color-slider/color-slider.js +0 -79
  40. package/color-slider/style.css +0 -65
  41. package/color-swatch/index.njk +0 -40
  42. package/common/attributes.js +0 -68
  43. package/common/color.js +0 -10
  44. package/css-color/index.njk +0 -43
  45. package/css-color/style.css +0 -67
  46. /package/{color-gamut/style.css → src/color-gamut/color-gamut.css} +0 -0
  47. /package/{color-swatch/color-swatch.css → src/color-inline/color-inline.css} +0 -0
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "color-elements",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "A set of web components for working with color. A Color.js project.",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
7
  "scripts": {
8
+ "eslint": "npx eslint .",
9
+ "eslint:fix": "npx eslint . --fix",
8
10
  "prebuild": "node _build/copy-config.js",
9
- "build:lib": "cp -r node_modules/colorjs.io lib/colorjs.io",
10
- "build": "run-s build:lib build:html",
11
+ "build": "npm run build:html",
11
12
  "watch": "run-p watch:*",
12
13
  "prepack": "npm run build",
13
14
  "release": "release-it",
@@ -31,11 +32,16 @@
31
32
  },
32
33
  "homepage": "https://github.com/color-js/elements#readme",
33
34
  "dependencies": {
34
- "colorjs.io": "^0.5.0"
35
+ "colorjs.io": "^0.5.0",
36
+ "nude-element": "latest"
35
37
  },
36
38
  "devDependencies": {
37
39
  "@11ty/eleventy": "^3.0.0-alpha.6",
40
+ "@stylistic/eslint-plugin": "latest",
41
+ "eslint": "latest",
42
+ "globals": "latest",
43
+ "markdown-it-attrs": "^4.1.6",
38
44
  "npm-run-all": "^4.1.5",
39
45
  "release-it": "^17.2.0"
40
46
  }
41
- }
47
+ }
@@ -0,0 +1,123 @@
1
+ # `<channel-slider>`
2
+
3
+ A [`<color-slider>`](../color-slider) for a specific channel, intended for color picking.
4
+
5
+ ## Usage
6
+
7
+ This is a higher level component than `<color-slider>` for the cases where you want to control a single channel of a color space.
8
+ It offers many conveniences for these cases:
9
+ - It takes care of applying the right `min` and `max` values to the slider
10
+ - It automatically generates the start and end colors,
11
+ - It can provide an editable tooltip as a tooltip that both shows and edits the current value
12
+ - Already includes a suitable label
13
+
14
+ ### Static
15
+
16
+ Basic example:
17
+
18
+ ```html
19
+ <channel-slider space="oklch" channel="h"></channel-slider>
20
+ ```
21
+
22
+ In most cases you’d also want to set a color to set the other channels and the initial value:
23
+
24
+ ```html
25
+ <channel-slider space="oklch" channel="h" color="oklch(80% 20% 130)"></channel-slider>
26
+ ```
27
+
28
+ This will automatically use the whole reference range of that component in the specified color space,
29
+ and use the current value of the component as the starting value (unless `value` is also specified).
30
+
31
+ ---
32
+
33
+ The color does not actually need to be in the same color space, it will be converted if needed:
34
+
35
+ ```html
36
+ <channel-slider space="oklch" channel="h" color="deeppink"></channel-slider>
37
+ ```
38
+
39
+ Colors and color spaces not supported by the browser also work:
40
+
41
+ ```html
42
+ <channel-slider space="okhsl" channel="h" color="color(--okhsl 180 100% 50%)"></channel-slider>
43
+ ```
44
+
45
+
46
+ If you don’t want to show the whole range you can also specify `min` and `max` attributes.
47
+
48
+ ```html
49
+ <channel-slider space="oklch" channel="l" color="red" min=".3" max=".95"></channel-slider>
50
+ ```
51
+
52
+ ### Dynamic
53
+
54
+ You can listen to the `colorchange` event and grab the `color` property to get the current color value.
55
+ Here we are using a [`<color-swatch>`](../color-swatch/) to not just display the CSS code but also the actual color:
56
+
57
+ ```html
58
+ <channel-slider space="oklch" channel="h" color="oklch(50% 50% 180)"
59
+ oncolorchange="this.nextElementSibling.textContent = this.color"></channel-slider>
60
+ <color-swatch></color-swatch>
61
+ ```
62
+
63
+ All attributes are reactive:
64
+
65
+ ```html
66
+ <label>
67
+ Space:
68
+ <select id="space_select" size="3">
69
+ <option>oklch</option>
70
+ <option>oklab</option>
71
+ <option>okhsl</option>
72
+ <option>lab</option>
73
+ <option>lch</option>
74
+ <option>hsl</option>
75
+ <option>srgb</option>
76
+ </select>
77
+ </label>
78
+ <label>
79
+ Channel:
80
+ <select id="channel_select" size=3>
81
+ <option>l</option>
82
+ <option>c</option>
83
+ <option>h</option>
84
+ </select>
85
+ </label>
86
+
87
+ <channel-slider id="dynamic_slider" space="oklch" channel="h" color="oklch(50% 50% 180)"
88
+ oncolorchange="this.nextElementSibling.textContent = this.color"></channel-slider>
89
+ <color-swatch></color-swatch>
90
+ <script>
91
+ function fromSlider () {
92
+ space_select.value = dynamic_slider.space.id;
93
+ channel_select.innerHTML = Object.keys(dynamic_slider.space.coords).map(c => `<option>${c}</option>`).join('\n');
94
+ channel_select.value = dynamic_slider.channel;
95
+ }
96
+
97
+ function fromSelects () {
98
+ dynamic_slider.space = space_select.value;
99
+ dynamic_slider.channel = channel_select.value;
100
+ }
101
+
102
+ space_select.oninput = channel_select.oninput = fromSelects;
103
+
104
+ customElements.whenDefined("channel-slider").then(fromSlider);
105
+ dynamic_slider.addEventListener("propchange", fromSlider);
106
+ </script>
107
+ ```
108
+
109
+
110
+ ## Reference
111
+
112
+ ### Attributes & Properties
113
+
114
+ | Attribute | Property | Property type | Default value | Description |
115
+ |-----------|----------|---------------|---------------|-------------|
116
+ | `space` | `space` | `ColorSpace` &#124; `string` | `oklch` | The color space to use for interpolation. |
117
+ | `channel` | `channel` | `string` | `h` | The component to use for the gradient. |
118
+ | `min` | `min` | `number` | `this.refRange[0]` | The minimum value for the slider. |
119
+ | `max` | `max` | `number` | `this.refRange[1]` | The maximum value for the slider. |
120
+ | `value` | `value` | `number` | `(this.min + this.max) / 2` | The current value of the slider. |
121
+ | `color` | `color` | `Color` &#124; `string` | `oklch(50 50% 180)` | The current color value. |
122
+ | - | `minColor` | `Color` | `oklch(0 50% 180)` | The minimum color value _(read-only)_. |
123
+ | - | `maxColor` | `Color` | `oklch(100 50% 180)` | The maximum color value _(read-only)_. |
@@ -0,0 +1,19 @@
1
+ .color-slider-label {
2
+ display: grid;
3
+ grid-template-columns: 1fr auto;
4
+ gap: .3em;
5
+ position: relative;
6
+
7
+ em {
8
+ opacity: 60%;
9
+ }
10
+ }
11
+
12
+ color-slider {
13
+ grid-column: 1 / -1;
14
+ }
15
+
16
+
17
+
18
+
19
+
@@ -0,0 +1,252 @@
1
+ import "../color-slider/color-slider.js";
2
+ import * as dom from "../common/dom.js";
3
+ import Color from "../common/color.js";
4
+ import NudeElement from "../../node_modules/nude-element/src/Element.js";
5
+ import { getStep } from "../common/util.js";
6
+
7
+ export const tagName = "channel-slider";
8
+
9
+ export default class ChannelSlider extends NudeElement {
10
+ constructor () {
11
+ super();
12
+
13
+ this.attachShadow({mode: "open"});
14
+ let styleURL = new URL(`./${tagName}.css`, import.meta.url);
15
+ this.shadowRoot.innerHTML = `
16
+ <style>@import url("${ styleURL }")</style>
17
+ <label class="color-slider-label" part="label">
18
+ <slot></slot>
19
+ <color-slider part="color_slider" exportparts="slider" id="slider" tooltip></color-slider>
20
+ </label>
21
+ `;
22
+
23
+ this._el = dom.named(this);
24
+ this._el.slot = this.shadowRoot.querySelector("slot");
25
+ }
26
+
27
+ connectedCallback() {
28
+ super.connectedCallback?.();
29
+
30
+ this._el.slider.addEventListener("input", this);
31
+ }
32
+
33
+ disconnectedCallback() {
34
+ this._el.slider.removeEventListener("input", this);
35
+ }
36
+
37
+ handleEvent(event) {
38
+ if (event.type === "input") {
39
+ this.value = event.target.value;
40
+ }
41
+ }
42
+
43
+ colorAt (value) {
44
+ let color = this.defaultColor.clone();
45
+ try {
46
+ color.set(this.channel, value);
47
+ }
48
+ catch (e) {
49
+ console.warn(e);
50
+ }
51
+ return color;
52
+ }
53
+
54
+ colorAtProgress (progress) {
55
+ // Map progress to min - max range
56
+ let value = this.min + progress * (this.max - this.min);
57
+ return this.colorAt(value);
58
+ }
59
+
60
+ get minColor () {
61
+ return this.colorAt(this.min);
62
+ }
63
+
64
+ get maxColor () {
65
+ return this.colorAt(this.max);
66
+ }
67
+
68
+ get stops () {
69
+ return [
70
+ this.minColor,
71
+ this.colorAtProgress(0.25),
72
+ this.colorAtProgress(0.5),
73
+ this.colorAtProgress(0.75),
74
+ this.maxColor,
75
+ ];
76
+ }
77
+
78
+ get progress () {
79
+ return this._el.slider.progress;
80
+ }
81
+
82
+ progressAt (p) {
83
+ return this._el.slider.progressAt(p);
84
+ }
85
+
86
+ propChangedCallback ({name, prop, detail: change}) {
87
+ if (["space", "min", "max", "step", "value", "defaultValue"].includes(name)) {
88
+ prop.applyChange(this._el.slider, change);
89
+ };
90
+
91
+ if (name === "defaultColor" || name === "space" || name === "channel" || name === "min" || name === "max") {
92
+ this._el.slider.stops = this.stops;
93
+
94
+ if (name === "space" || name === "channel" || name === "min" || name === "max") {
95
+ this._el.slot.innerHTML = `${ this.channelName } <em>(${ this.min }&thinsp;&ndash;&thinsp;${ this.max })</em>`;
96
+ }
97
+ }
98
+ }
99
+
100
+
101
+
102
+ get channelName () {
103
+ return this.channelSpec?.name ?? this.channel;
104
+ }
105
+
106
+ static props = {
107
+ space: {
108
+ default: "oklch",
109
+ parse (value) {
110
+ if (value instanceof Color.Space || value === null || value === undefined) {
111
+ return value;
112
+ }
113
+
114
+ value += "";
115
+
116
+ return Color.Space.get(value);
117
+ },
118
+ stringify (value) {
119
+ return value?.id;
120
+ },
121
+ },
122
+ channel: {
123
+ type: String,
124
+ default () {
125
+ return Object.keys(this.space.coords)[0];
126
+ },
127
+ // get () {
128
+ // let value = this.props.channel;
129
+ // let space = this.space;
130
+ // console.log(this.props, value, space);
131
+
132
+ // if (!space || space.coords[value]) {
133
+ // return value;
134
+ // }
135
+
136
+ // return Object.keys(this.space.coords)[0];
137
+ // },
138
+ // set: true,
139
+ // reflect: true,
140
+ },
141
+ channelSpec: {
142
+ get () {
143
+ let channelSpec = this.space?.coords[this.channel];
144
+
145
+ if (!channelSpec && this.space) {
146
+ channelSpec = Object.values(this.space.coords)[0];
147
+ console.warn(`Unknown channel ${ this.channel } in space ${ this.space }. Using first channel (${ channelSpec.name }) instead.`);
148
+ }
149
+
150
+ return channelSpec;
151
+ }
152
+ },
153
+ refRange: {
154
+ get () {
155
+ let channelSpec = this.channelSpec;
156
+ return channelSpec?.refRange ?? channelSpec?.range ?? [0, 100];
157
+ },
158
+ },
159
+ min: {
160
+ type: Number,
161
+ default () {
162
+ return this.refRange[0];
163
+ },
164
+ },
165
+ max: {
166
+ type: Number,
167
+ default () {
168
+ return this.refRange[1];
169
+ },
170
+ },
171
+ step: {
172
+ type: Number,
173
+ default () {
174
+ return getStep(this.max, this.min, { minSteps: 100 });
175
+ },
176
+ },
177
+
178
+ defaultValue: {
179
+ type: Number,
180
+ default () {
181
+ return this.defaultColor.get(this.channel);
182
+ },
183
+ reflect: {
184
+ from: "value",
185
+ },
186
+ },
187
+ value: {
188
+ type: Number,
189
+ defaultProp: "defaultValue",
190
+ reflect: false,
191
+ },
192
+
193
+ defaultColor: {
194
+ type: Color,
195
+ convert (color) {
196
+ return color.to(this.space);
197
+ },
198
+ default () {
199
+ let coords = [];
200
+ for (let channel in this.space.coords) {
201
+ let spec = this.space.coords[channel];
202
+ let range = spec.refRange ?? spec.range;
203
+ coords.push((range[0] + range[1]) / 2);
204
+ }
205
+
206
+ return new Color(this.space, coords);
207
+ },
208
+ reflect: {
209
+ from: "color",
210
+ },
211
+ },
212
+ color: {
213
+ type: Color,
214
+ get () {
215
+ return this.colorAt(this.value);
216
+ },
217
+ dependencies: ["defaultColor", "space", "channel", "value"],
218
+ set (value) {
219
+ this.defaultColor = value;
220
+ this.value = this.defaultValue;
221
+ },
222
+ },
223
+ }
224
+
225
+ static events = {
226
+ change: {
227
+ from () {
228
+ return this._el.slider;
229
+ }
230
+ },
231
+ input: {
232
+ from () {
233
+ return this._el.slider;
234
+ }
235
+ },
236
+ valuechange: {
237
+ propchange: "value",
238
+ },
239
+ colorchange: {
240
+ propchange: "color",
241
+ },
242
+ };
243
+
244
+ static formAssociated = {
245
+ getSource: el => el._el.slider,
246
+ role: "slider",
247
+ valueProp: "value",
248
+ changeEvent: "valuechange",
249
+ };
250
+ }
251
+
252
+ customElements.define(tagName, ChannelSlider);
@@ -1,7 +1,7 @@
1
1
  <script src="color-gamut.js" type="module"></script>
2
2
  # &lt;color-gamut>
3
3
 
4
- Gamut indicator. Used internally by `<css-color>`
4
+ Gamut indicator. Used internally by `<color-swatch>`
5
5
 
6
6
  ## Usage
7
7
 
@@ -1,6 +1,6 @@
1
1
  import Color from "../common/color.js";
2
2
 
3
- let styleURL = new URL("./style.css", import.meta.url);
3
+ let styleURL = new URL("./color-gamut.css", import.meta.url);
4
4
 
5
5
  export default class ColorGamut extends HTMLElement {
6
6
  #label;
@@ -0,0 +1,31 @@
1
+ ---
2
+ layout: component
3
+ css: "style.css"
4
+ ---
5
+
6
+ # `<color-inline>`
7
+
8
+ Basic use:
9
+
10
+ <html-demo adjust="font-size">
11
+
12
+ ```html
13
+ <color-inline>lch(50% 40 30)</color-inline>
14
+ ```
15
+ </html-demo>
16
+
17
+ Editable:
18
+ ```html
19
+ <color-inline contentEditable>lch(50% 40 30)</color-inline>
20
+ ```
21
+
22
+ Semi-transparent color:
23
+ ```html
24
+ <color-inline>hsl(340 90% 50% / .25)</color-inline>
25
+ ```
26
+
27
+ Invalid color:
28
+
29
+ ```html
30
+ <color-inline>foobar</color-inline>
31
+ ```
@@ -1,8 +1,8 @@
1
- import Color from "../../src/index.js";
1
+ import Color from "../common/color.js";
2
2
 
3
- let styleURL = new URL("./color-swatch.css", import.meta.url);
3
+ let styleURL = new URL("./color-inline.css", import.meta.url);
4
4
 
5
- export default class ColorSwatch extends HTMLElement {
5
+ export default class ColorInline extends HTMLElement {
6
6
  #swatch;
7
7
 
8
8
  constructor () {
@@ -19,7 +19,7 @@ export default class ColorSwatch extends HTMLElement {
19
19
 
20
20
  connectedCallback () {
21
21
  this.#render();
22
- ColorSwatch.#mo.observe(this, {childList: true, subtree: true, characterData: true});
22
+ ColorInline.#mo.observe(this, {childList: true, subtree: true, characterData: true});
23
23
  }
24
24
 
25
25
  #value;
@@ -64,7 +64,7 @@ export default class ColorSwatch extends HTMLElement {
64
64
  for (let mutation of mutations) {
65
65
  let target = mutation.target;
66
66
 
67
- while (target && !(target instanceof ColorSwatch)) {
67
+ while (target && !(target instanceof ColorInline)) {
68
68
  target = target.parentNode;
69
69
  }
70
70
 
@@ -76,4 +76,4 @@ export default class ColorSwatch extends HTMLElement {
76
76
  }
77
77
 
78
78
 
79
- customElements.define("color-swatch", ColorSwatch);
79
+ customElements.define("color-inline", ColorInline);
@@ -0,0 +1,14 @@
1
+ html {
2
+ color-scheme: light dark;
3
+ }
4
+
5
+ @media (prefers-color-scheme: dark) {
6
+ html {
7
+ background: hsl(220 5% 20%);
8
+ }
9
+ }
10
+
11
+ color-inline {
12
+ display: block;
13
+ margin: 1em 0;
14
+ }
@@ -0,0 +1,48 @@
1
+ # `<color-picker>`
2
+
3
+ ## Usage
4
+
5
+ ### Basic usage
6
+
7
+ ```html
8
+ <color-picker space="oklch" color="oklch(60% 30% 180)"></color-picker>
9
+ ```
10
+
11
+ Color spaces not supported by the browser also work:
12
+
13
+ ```html
14
+ <color-picker space="okhsl" color="color(--okhsl 180 50% 50%)"></color-picker>
15
+ ```
16
+
17
+ ### Slots
18
+
19
+ ```html
20
+ <color-picker space="oklch" color="oklch(50% 50% 180)">
21
+ Element content goes into the swatch
22
+ </color-picker>
23
+ ```
24
+
25
+ ### Events
26
+
27
+ As with other components, you can listen to the `colorchange` event:
28
+
29
+ ```html
30
+ <color-picker space="oklch" color="oklch(50% 50% 180)"
31
+ oncolorchange="this.firstElementChild.textContent = this.color.oklch.join(' ')">
32
+ <div class="coords" style="font-weight: bold; text-shadow: 0 0 .1em white, 0 0 .1em white, 0 0 .1em white"></div>
33
+ </color-picker>
34
+ ```
35
+
36
+
37
+ ## Reference
38
+
39
+ ### Attributes & Properties
40
+
41
+ | Attribute | Property | Property type | Default value | Description |
42
+ |-----------|----------|---------------|---------------|-------------|
43
+ | `space` | `space` | `ColorSpace` &#124; `string` | `oklch` | The color space to use for interpolation. |
44
+ | `color` | `color` | `Color` &#124; `string` | `oklch(50 50% 180)` | The current color value. |
45
+
46
+ ## To-Do
47
+
48
+ - Alpha
@@ -0,0 +1,20 @@
1
+ :host {
2
+ display: grid;
3
+ grid-template-columns: 3fr 1fr;
4
+ gap: 1em;
5
+ }
6
+
7
+ #sliders {
8
+ display: flex;
9
+ flex-flow: column;
10
+ gap: .3em;
11
+ }
12
+
13
+ color-swatch {
14
+ width: auto;
15
+
16
+ &::part(swatch) {
17
+ flex: 1;
18
+ }
19
+ }
20
+