Skip to content
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
e13ef18
[color-swatch] Add support for deltas from other color
DmitrySharabin Jun 11, 2024
d6c7f7f
[color-scale] Add support for deltas from other color
DmitrySharabin Jun 11, 2024
8b85dff
Merge branch 'main' into color-swatch-deltas
DmitrySharabin Jun 11, 2024
4525cff
Move vars to the block where they are used
DmitrySharabin Jun 11, 2024
e642891
Don't show the delta if it's zero
DmitrySharabin Jun 11, 2024
85428d8
Minor refactor
DmitrySharabin Jun 11, 2024
6242c06
Support different precision for angles and numbers
DmitrySharabin Jun 11, 2024
9c88652
Add support for the primary use case
DmitrySharabin Jun 11, 2024
bd4df34
🤦‍♂️
DmitrySharabin Jun 11, 2024
5ddf3a1
Increase precision for percents
DmitrySharabin Jun 11, 2024
fea82ad
[color-swatch] Add docs on the `vs` prop (attribute)
DmitrySharabin Jun 12, 2024
5cfe1b5
[color-scale] Add docs on the `coords`, `deltas`, and `vs` attributes
DmitrySharabin Jun 12, 2024
58974c4
Address feedback
DmitrySharabin Jun 12, 2024
84ff6d3
Address feedback
DmitrySharabin Jun 12, 2024
6746cd4
Oops
DmitrySharabin Jun 12, 2024
bdc2ac9
Add links to the page with ΔE algorithms
DmitrySharabin Jun 12, 2024
43fb65b
Update the docs
DmitrySharabin Jun 12, 2024
d278303
Manually calculate deltas
DmitrySharabin Jun 12, 2024
89fdb92
Move to a more appropriate place
DmitrySharabin Jun 12, 2024
5ca9abd
Merge branch 'main' into color-swatch-deltas
DmitrySharabin Jun 13, 2024
1aa5bbe
Merge branch 'main' into color-swatch-deltas
DmitrySharabin Jun 17, 2024
fdcfb61
Leftovers from merging the main branch
DmitrySharabin Jun 17, 2024
9482bcf
Add support for deltaE and contrast
DmitrySharabin Jun 17, 2024
de58fc4
Fix comment
DmitrySharabin Jun 17, 2024
5dfb4a6
Update README.md
DmitrySharabin Jun 17, 2024
a151249
Merge branch 'main' into color-swatch-deltas
DmitrySharabin Jun 17, 2024
09f4136
Update API
DmitrySharabin Jun 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/color-scale/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,42 @@ You can only specify your core colors, and insert steps via interpolation:

If you have more than 2 colors listed, this will insert steps between each pair.


### The `coords`, `delta`, and `vs` attributes

You can show any of the colors coords in _any_ color space the same way you can do it for [`<color-swatch>`](../color-swatch/#the-coords-attribute):

```html
<color-scale
space="oklch"
colors="#e3fafc, #c5f6fa, #99e9f2, #66d9e8, #3bc9db"
coords="L: oklch.l, C: oklch.c, H: oklch.h"
></color-scale>
```

By specifying the `deltas` boolean attribute, you can also show deltas between the current and the previous color:

```html
<color-scale
space="oklch"
colors="#e3fafc, #c5f6fa, #99e9f2, #66d9e8, #3bc9db"
coords="L: oklch.l, C: oklch.c, H: oklch.h"
deltas
></color-scale>
```

If you need to show the difference between every color in the scale and another color, you can specify that color via the `vs` attribute:

```html
<color-scale
space="oklch"
colors="#e3fafc, #c5f6fa, #99e9f2, #66d9e8, #3bc9db"
coords="L: oklch.l, C: oklch.c, H: oklch.h"
vs="oklch(65% 0.15 210)"
deltas
></color-scale>
```

<!--
If you want to insert interpolated colors only in specific places, you can use empty values:

Expand Down
20 changes: 19 additions & 1 deletion src/color-scale/color-scale.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@ const Self = class ColorScale extends NudeElement {
swatch.textContent = name;
if (this.coords) {
swatch.coords = this.coords;

// Deltas don't make sense without coords
if (this.deltas) {
if (this.vs) {
// If there is a vs color, use it as the reference for the deltas
swatch.vs = this.vs;
}
else if (i > 0) {
// Otherwise, use the previous color
swatch.vs = colors[i - 1].color;
}
}
}
i++;
}
Expand Down Expand Up @@ -149,9 +161,15 @@ const Self = class ColorScale extends NudeElement {

return colors;
},
additionalDependencies: ["coords"],
additionalDependencies: ["coords", "vs", "deltas"],
},
coords: {},
vs: {
type: Color,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see why you're doing the #hasVs thing. That’s not the right way; it means that if someone sets it via the property you get a different behavior. You want two properties: one for the raw value, and one for the parsed value, which is either true (previous color), false (no vs), or a Color instance.

And yes, this is another case of rawProp.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'm doing this precisely because I can't see another way to distinguish different cases: color, no color, no vs.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another option would be to use a custom parse function. Up to you.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it may be useful to provide a way to do vs with the previous or next color. In that case we could have vs="previous" and vs="next" which may be more explanatory than a bare vs with no value.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another option would be to use a custom parse function.

Yeah, I was going to try this later. Glad it makes sense. :)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it may be useful to provide a way to do vs with the previous or next color. In that case we could have vs="previous" and vs="next" which may be more explanatory than a bare vs with no value.

I like it

},
deltas: {
type: Boolean,
},
};
}

Expand Down
19 changes: 19 additions & 0 deletions src/color-swatch/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,24 @@ By default, the coord name will be used as a coord label. Add a label before the
</color-swatch>
```

### The `vs` attribute

You can pass another color via the `vs` attribute to show the delta (if it is not zero) between the current and that color.

```html
<color-swatch vs="oklch(55% 0.1 210)" size="large">
oklch(70% 0.25 138)
</color-swatch>
```

If the color coords are also specified, the (not zero) deltas from that color in its coordinates will also be shown.

```html
<color-swatch coords="L: oklch.l, C: oklch.c, H: oklch.h" vs="oklch(70% 0.1 210)" size="large">
oklch(70% 0.25 138)
</color-swatch>
```

### With slot content

Before and after:
Expand Down Expand Up @@ -222,6 +240,7 @@ If you don’t, the `<html>` element will be used.
| `coords` | `coords` | `string` | - | Comma-separated list of coords of the current color to be shown. |
| `value` | `value` | `string` | - | The current value of the swatch. |
| `size` | - | `large` | - | The size of the swatch. Currently, it is used only to make a large swatch. |
| `vs` | `vs` | `Color` &#124; `string` | - | The second color to use when calculating the difference (delta) with the current color. |
| `property` | `property` | `string` | - | CSS property to bind to. |
| `scope` | `scope` | `string` | `:root` | CSS selector to use as the scope for the specified CSS property. |
| `gamuts` | `gamuts` | `string` | `srgb, p3, rec2020: P3+, prophoto: PP` | Comma-separated list of gamuts to be used by the gamut indicator. |
Expand Down
57 changes: 44 additions & 13 deletions src/color-swatch/color-swatch.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
0 0 / calc(2 * var(--_transparency-cell-size)) calc(2 * var(--_transparency-cell-size))
content-box border-box var(--_transparency-background)
);
--_positive-deltaE-color: var(--positive-deltaE-color, hsl(120, 80%, 25%));
--_negative-deltaE-color: var(--negative-deltaE-color, hsl(0, 85%, 40%));

position: relative;
display: inline-flex;
Expand Down Expand Up @@ -49,31 +51,48 @@ slot {
}
}

[part="coords"] {
[part="coords"], [part="deltaE"] {
margin: 0;
display: inline-flex;
gap: .5em;

dd {
margin: 0;
font-weight: bold;
font-variant-numeric: tabular-nums;
}

&:is(:host([size="large"]) &) {
display: grid;
grid-template-columns: max-content auto;
gap: .1em .2em;
font-size: max(9px, 80%);
justify-content: start;

.coord {
display: contents;
}
grid-template-columns: subgrid;
}
}

[part="coords"] {
.coord {
display: flex;
gap: .2em;

dd {
margin: 0;
font-weight: bold;
font-variant-numeric: tabular-nums;
dd.deltaE {
color: var(--_deltaE-color);

&.positive {
--_deltaE-color: var(--_positive-deltaE-color);
}

&.negative {
--_deltaE-color: var(--_negative-deltaE-color);
}
}
}

&:is(:host([size="large"]) &) {
.coord {
display: contents;

dd:not(.deltaE, :has(~ dd)) {
grid-column: span 2;
}
}
}
}
Expand All @@ -97,6 +116,18 @@ slot {
@container color-swatch (width <= 5rem) {
font-size: 80%;
}

&:is(:host([size="large"]) &) {
display: grid;
grid-template-columns: repeat(3, max-content);
gap: .1em .2em;
font-size: max(9px, 80%);
justify-content: start;

& > * {
grid-column: 1 / -1;
}
}
}

[part="color-wrapper"] {
Expand Down
75 changes: 60 additions & 15 deletions src/color-swatch/color-swatch.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,27 +109,68 @@ const Self = class ColorSwatch extends NudeElement {
this.style.setProperty("--color", colorString);
}

if (name === "coords") {
if (!this.coords.length) {
return;
}
if (name === "coords" || name === "vs") {
if (this.coords.length) {
this._el.coords ??= Object.assign(document.createElement("dl"), {part: "coords"});
if (!this._el.coords.parentNode) {
this._el.colorWrapper.after(this._el.coords);
}

this._el.coords ??= Object.assign(document.createElement("dl"), {part: "coords"});
if (!this._el.coords.parentNode) {
this._el.colorWrapper.after(this._el.coords);
}
let coords = [];
for (let coord of this.coords) {
let [label, channel] = Object.entries(coord)[0];
let value = this.color.get(channel);

let deltaString;
if (typeof value === "number" && this.vs) {
let {space, index} = Color.Space.resolveCoord(channel);
let deltas = this.color.deltas(this.vs, {space}).coords;

let delta = deltas[index];
if (typeof delta === "number" && delta !== 0) {
let spaceCoord = Object.values(space.coords)[index];
let isAngle = spaceCoord.type === "angle";
if (!isAngle) {
delta = delta / value * 100;
}
delta = Number(delta.toPrecision(isAngle ? 4 : 2));

let sign = delta > 0 ? "+" : "";
let className = delta > 0 ? "positive" : "negative";
deltaString = `<dd class="deltaE ${ className }">(${ sign }${ delta }${ !isAngle ? "%" : ""})</dd>`;
}
}

value = typeof value === "number" ? Number(value.toPrecision(4)) : value;
let html = `<dt>${ label }</dt><dd>${ value }</dd>`;
if (deltaString) {
html += deltaString;
}

coords.push(`<div class="coord">${ html }</div>`);
}

let coords = [];
for (let coord of this.coords) {
let [label, channel] = Object.entries(coord)[0];
this._el.coords.innerHTML = coords.join("\n");
}
}

let value = this.color.get(channel);
if (name === "vs") {
if (!this.vs) {
this._el.deltaE?.remove();
this._el.deltaE = null;
}
else {
let value = this.color.deltaE(this.vs);
value = typeof value === "number" ? Number(value.toPrecision(4)) : value;
if (value !== 0) {
this._el.deltaE ??= Object.assign(document.createElement("dl"), {part: "deltaE"});
if (!this._el.deltaE.parentNode) {
(this._el.coords ?? this._el.colorWrapper).after(this._el.deltaE);
}

coords.push(`<div class="coord"><dt>${ label }</dt><dd>${ value }</dd></div>`);
this._el.deltaE.innerHTML = `<dt>ΔE</dt><dd>${ value }</dd>`;
}
}

this._el.coords.innerHTML = coords.join("\n");
}
}

Expand Down Expand Up @@ -179,6 +220,10 @@ const Self = class ColorSwatch extends NudeElement {
},
dependencies: ["color"],
},
vs: {
type: Color,
dependencies: ["color"],
},
}

static events = {
Expand Down