@luntta/swatch 3.0.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.
- package/CHANGELOG.md +56 -0
- package/CONTRIBUTING.md +89 -0
- package/LICENSE +21 -0
- package/MIGRATING.md +189 -0
- package/README.md +463 -0
- package/package.json +57 -0
- package/scripts/pack-check.mjs +18 -0
- package/src/bootstrap.js +159 -0
- package/src/core/registry.js +81 -0
- package/src/core/state.js +36 -0
- package/src/core/swatch-class.js +524 -0
- package/src/data/cvd-matrices.js +179 -0
- package/src/data/named-colors.js +157 -0
- package/src/format/css.js +256 -0
- package/src/operations/accessibility.js +103 -0
- package/src/operations/apca.js +80 -0
- package/src/operations/blend.js +72 -0
- package/src/operations/channels.js +123 -0
- package/src/operations/cvd.js +119 -0
- package/src/operations/deltaE.js +207 -0
- package/src/operations/gamut.js +206 -0
- package/src/operations/image.js +192 -0
- package/src/operations/manipulation.js +100 -0
- package/src/operations/mix.js +129 -0
- package/src/operations/naming.js +158 -0
- package/src/operations/palette.js +133 -0
- package/src/operations/random.js +75 -0
- package/src/operations/temperature.js +126 -0
- package/src/operations/tint-shade.js +42 -0
- package/src/palettes/colorbrewer.js +232 -0
- package/src/palettes/index.js +58 -0
- package/src/palettes/viridis.js +59 -0
- package/src/parse/css.js +241 -0
- package/src/parse/hex.js +38 -0
- package/src/parse/index.js +43 -0
- package/src/parse/legacy.js +88 -0
- package/src/parse/named.js +11 -0
- package/src/parse/objects.js +125 -0
- package/src/scale/index.js +382 -0
- package/src/scale/interpolators.js +83 -0
- package/src/spaces/a98.js +55 -0
- package/src/spaces/cmyk.js +75 -0
- package/src/spaces/display-p3.js +50 -0
- package/src/spaces/hsl.js +93 -0
- package/src/spaces/hsluv.js +211 -0
- package/src/spaces/hsv.js +78 -0
- package/src/spaces/hwb.js +48 -0
- package/src/spaces/lab.js +70 -0
- package/src/spaces/lch.js +65 -0
- package/src/spaces/oklab.js +79 -0
- package/src/spaces/oklch.js +53 -0
- package/src/spaces/prophoto.js +72 -0
- package/src/spaces/rec2020.js +65 -0
- package/src/spaces/srgb.js +85 -0
- package/src/spaces/xyz.js +71 -0
- package/src/swatch.js +57 -0
- package/src/util/math.js +53 -0
- package/src/util/matrix.js +92 -0
- package/src/util/suggest.js +66 -0
- package/types/swatch.d.ts +664 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 3.0.0 — BREAKING
|
|
4
|
+
|
|
5
|
+
A complete rewrite. Storage moved to a canonical `{ space, coords, alpha }` state (colorjs.io / culori model) so wide-gamut inputs are preserved losslessly, conversions go through a lazy CIE XYZ D65 hub, and the monolithic `src/swatch.js` has been split into `src/core`, `src/spaces`, `src/parse`, `src/format`, `src/operations`, `src/scale`, `src/palettes`, and `src/data`.
|
|
6
|
+
|
|
7
|
+
See [`MIGRATING.md`](./MIGRATING.md) for the v2 → v3 cookbook.
|
|
8
|
+
|
|
9
|
+
### Breaking
|
|
10
|
+
|
|
11
|
+
- **Manipulation moved to OKLCh.** `lighten`, `darken`, `saturate`, `desaturate`, `spin`, `greyscale`, and `complement` now operate in OKLCh instead of HSL, with amounts in `0..1` OKLCh L/C units instead of `0..100` HSL %. `swatch("#ff0000").lighten(20)` (v2) becomes `swatch("#ff0000").lighten(0.1)` (v3). Rationale: HSL lightness is not perceptually uniform, so v2's `swatch("#ffff00").lighten(0.1)` looked almost unchanged while `swatch("#0000ff").lighten(0.1)` jumped.
|
|
12
|
+
- **Space conversions are getters, not methods.** `c.toLab()` → `c.lab`, `c.toOklch()` → `c.oklch`, `c.toRGB()` → `c.srgb`, etc. Getters are lazily memoized on the instance.
|
|
13
|
+
- **Wide-gamut inputs are no longer clamped at parse.** `swatch("color(display-p3 1 0 0)")` keeps its P3 coordinates; `.srgb` on that swatch returns a raw, possibly out-of-range conversion. Use `.toGamut({ space: "srgb", method: "css4" })` to get a properly chroma-reduced in-gamut version.
|
|
14
|
+
- **`toJSON()` shape changed** from `{ hex, rgb, hsl, isValid, format }` to `{ space, coords, alpha }`. The new shape round-trips cleanly through `swatch(...)`.
|
|
15
|
+
- **`equals()` signature changed** from `equals(other, { tolerance, space })` to `equals(other, epsilon)`. `other` must now be a `Swatch` instance.
|
|
16
|
+
- **Removed v2 fields:** `isValid`, `hasAlpha`, `_original`, `_originalFormat`. Invalid input now throws; check `alpha < 1` for alpha presence; use `c.space` / `c.coords` to introspect state.
|
|
17
|
+
- **`mix()` takes an options bag** for the interpolation space: `a.mix(b, 0.5, "lab")` → `a.mix(b, 0.5, { space: "lab" })`.
|
|
18
|
+
- **Harmonies not yet ported.** `complementary`, `triad`, `tetrad`, `splitComplement`, `analogous`, `monochromatic` are not in v3.0. They will return in a follow-up release. Workaround: `[c, c.spin(120), c.spin(240)]`.
|
|
19
|
+
- **Internal rename.** The v2 monolith lives at `src/_v2-monolith.js` as an internal artifact and is not part of the public API. Do not import from it.
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- **CSS Color 4 parsing and serialization.** `swatch("oklch(0.7 0.15 240)")`, `swatch("lab(52.2% 40 -70)")`, `swatch("hwb(240 0% 0%)")`, `swatch("color(display-p3 1 0 0)")`, `swatch("color(rec2020 1 0 0)")`, `swatch("color(xyz-d65 0.4 0.2 0.2)")`, modern slash-alpha, `none` keyword, percentage values, the whole set. `c.toString({ format: "oklch" })` / `"lab"` / `"hwb"` / `"lch"` / `"oklab"` / `"color"` for output.
|
|
24
|
+
- **Wide-gamut spaces.** Display P3, Rec2020, Adobe RGB 1998, ProPhoto RGB. Each registered in the conversion graph with their correct primaries and transfer functions; ProPhoto carries a Bradford D50 → D65 CAT internally.
|
|
25
|
+
- **Additional spaces.** HSV, HWB, CMYK (naive), HSLuv, Luv, linear sRGB, XYZ D65 and D50.
|
|
26
|
+
- **Channel get/set.** `c.get("oklch.l")`, `c.set("hsl.h", 120)`, `c.get("alpha")`.
|
|
27
|
+
- **Gamut mapping.** `c.inGamut("srgb")`, `c.toGamut({ space, method })`, with `"clip"`, `"css4"` (CSS Color 4 binary chroma reduction in OKLCh, default), and `"oklch-chroma"` (alias).
|
|
28
|
+
- **Tint, shade, tone.** `c.tint(0.2)`, `c.shade(0.2)`, `c.tone(0.2)` — mix toward white / black / mid-grey in OKLab.
|
|
29
|
+
- **W3C Compositing and Blending Level 1 blend modes.** `normal`, `multiply`, `screen`, `darken`, `lighten`, `overlay`, `color-dodge`, `color-burn`, `hard-light`, `soft-light`, `difference`, `exclusion`. Per-channel in linear sRGB, then re-encoded.
|
|
30
|
+
- **Color scales.** `swatch.scale([c1, c2, ...])` returns a chroma.js-style `Scale` with chainable `.domain([a, b])`, `.classes(n)`, `.padding([l, r])`, `.gamma(g)`, `.mode(spaceId)`, `.correctLightness()`, `.cache(on)`, plus `.colors(n, format?)`.
|
|
31
|
+
- **Built-in palettes.** Matplotlib perceptually-uniform colormaps (`viridis`, `magma`, `plasma`, `inferno`, `cividis`) and the ColorBrewer 2.0 sequential (`Blues`, `Greens`, `Reds`, `Oranges`, `Purples`, `Greys`), diverging (`RdBu`, `RdYlBu`, `PiYG`, `BrBG`, `Spectral`), and qualitative (`Set1`, `Set2`, `Set3`, `Pastel1`, `Dark2`) sets. Look up by name: `swatch.scale("viridis")`, `swatch.scale("RdBu")`, etc. List all: `swatch.palettes()`.
|
|
32
|
+
- **Scale interpolators.** `swatch.bezier([colors])` (de Casteljau in Lab), `swatch.cubehelix({ start, rotations, hue, gamma, lightness })` (Green 2011).
|
|
33
|
+
- **Color naming.** `c.name()` returns `{ name, hex, deltaE }` of the nearest CSS named color measured in ΔE2000; `c.toName()` returns just the string.
|
|
34
|
+
- **Temperature.** `swatch.temperature(6500)` (Krystek 1985 blackbody polynomial) and `c.temperature()` (McCamy 1992 inverse approximation). Range 1000 K – 40000 K.
|
|
35
|
+
- **Random color generation.** `swatch.random({ space, hue, lightness, chroma, saturation, seed })` with a seeded xorshift32 PRNG for reproducibility.
|
|
36
|
+
- **Extra ΔE metrics.** `"94"` (CIE94 graphic arts), `"cmc"` (CMC l:c with optional `{ l, c }` coefficients), `"hyab"` (Abasi & Fairchild HyAB). Existing `"76"`, `"2000"`, `"ok"` still supported.
|
|
37
|
+
- **TypeScript definitions rewritten.** `types/swatch.d.ts` now covers the full v3 surface including the `SpaceId` literal union, all per-space channel shapes, the widened `ColorInput`, every option bag, the `Scale` interface, and the `SwatchFactory` statics.
|
|
38
|
+
- **New docs.** [`README.md`](./README.md) rewritten for v3. [`MIGRATING.md`](./MIGRATING.md) added with the v2 → v3 cookbook.
|
|
39
|
+
|
|
40
|
+
### Unchanged
|
|
41
|
+
|
|
42
|
+
The CVD / APCA / WCAG story is preserved end-to-end:
|
|
43
|
+
|
|
44
|
+
- `simulate(type, { severity })` — Brettel/Viénot dichromat simulation with a severity continuum
|
|
45
|
+
- `daltonize(type, { severity })` — Fidaner error redistribution
|
|
46
|
+
- `swatch.checkPalette(colors, { cvd, minDeltaE, mode })`
|
|
47
|
+
- `swatch.nearestDistinguishable(target, against, { cvd, minDeltaE, step })`
|
|
48
|
+
- `swatch.mostReadable(bg, candidates, { level, size })`
|
|
49
|
+
- `contrast(a, b)`, `isReadable(a, b, { level, size })`, `ensureContrast(a, b, { minRatio, direction, step })`
|
|
50
|
+
- `apcaContrast(text, background)`
|
|
51
|
+
|
|
52
|
+
All of these moved to new module paths internally but kept their public signatures.
|
|
53
|
+
|
|
54
|
+
## 2.0.0-alpha.2 and earlier
|
|
55
|
+
|
|
56
|
+
See the git history.
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Contributing to Swatch
|
|
2
|
+
|
|
3
|
+
Thanks for working on Swatch. This repo contains the library, tests, TypeScript
|
|
4
|
+
declarations, and the Eleventy docs/playground.
|
|
5
|
+
|
|
6
|
+
## Requirements
|
|
7
|
+
|
|
8
|
+
- Node.js 20 or newer.
|
|
9
|
+
- npm.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install
|
|
15
|
+
npm --prefix docs install
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Common commands
|
|
19
|
+
|
|
20
|
+
From the repository root:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm test # Run the library test suite
|
|
24
|
+
npm run typecheck # Type-check the public .d.ts against types/swatch.test-d.ts
|
|
25
|
+
npm run docs:dev # Start the docs/playground at http://localhost:8080
|
|
26
|
+
npm run docs:build # Build the static docs site
|
|
27
|
+
npm run check # Run tests, typecheck, and build docs
|
|
28
|
+
npm run pack:check # Show the npm package contents without publishing
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The docs package also has its own local scripts under `docs/package.json`.
|
|
32
|
+
|
|
33
|
+
Reference-page search is powered by [Pagefind](https://pagefind.app), whose
|
|
34
|
+
index is generated by `npm run docs:build` (not by `docs:dev`), so the search
|
|
35
|
+
box only returns results against a production build.
|
|
36
|
+
|
|
37
|
+
## Project layout
|
|
38
|
+
|
|
39
|
+
- `src/swatch.js` is the public package entry point.
|
|
40
|
+
- `src/bootstrap.js` registers built-in color spaces, parsers, operations,
|
|
41
|
+
palettes, scales, and factory statics.
|
|
42
|
+
- `src/core/` contains the registry, immutable state helpers, and `Swatch`
|
|
43
|
+
class.
|
|
44
|
+
- `src/spaces/` contains color-space conversions.
|
|
45
|
+
- `src/parse/` contains CSS/object/named-color parsing.
|
|
46
|
+
- `src/operations/` contains manipulation, gamut, contrast, CVD, ΔE, palette,
|
|
47
|
+
naming, random, and temperature helpers.
|
|
48
|
+
- `src/scale/` and `src/palettes/` contain scale/interpolator and built-in
|
|
49
|
+
palette support.
|
|
50
|
+
- `types/swatch.d.ts` is hand-written and should be updated with public API
|
|
51
|
+
changes.
|
|
52
|
+
- `tests/` contains Vitest coverage for v2 compatibility and v3 APIs.
|
|
53
|
+
- `docs/src/` contains the Eleventy site and interactive playground.
|
|
54
|
+
|
|
55
|
+
## API design notes
|
|
56
|
+
|
|
57
|
+
- `Swatch` instances are immutable. Operations should return a new `Swatch`.
|
|
58
|
+
- The canonical state shape is `{ space, coords, alpha }`.
|
|
59
|
+
- Space conversions route through the registry and are lazily memoized on the
|
|
60
|
+
instance.
|
|
61
|
+
- Raw space getters such as `.srgb` return mathematical coordinates and may be
|
|
62
|
+
out of gamut. Display helpers such as `.hex()` and `.rgb()` gamut-map to sRGB
|
|
63
|
+
by default.
|
|
64
|
+
- Public string options should have tests and helpful error messages.
|
|
65
|
+
|
|
66
|
+
## Documentation changes
|
|
67
|
+
|
|
68
|
+
When adding or changing public API:
|
|
69
|
+
|
|
70
|
+
1. Update `README.md`.
|
|
71
|
+
2. Update or add a page in `docs/src/reference/`.
|
|
72
|
+
3. Add or update playground affordances if the feature is visual.
|
|
73
|
+
4. Update `types/swatch.d.ts`.
|
|
74
|
+
5. Add tests that exercise the documented examples.
|
|
75
|
+
|
|
76
|
+
Run `npm run check` before opening a PR.
|
|
77
|
+
|
|
78
|
+
## Release checklist
|
|
79
|
+
|
|
80
|
+
Before publishing:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npm run check
|
|
84
|
+
npm run pack:check
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Then review the package contents printed by `pack:check` to make sure only the
|
|
88
|
+
intended source, declarations, docs, changelog, migration guide, license, and
|
|
89
|
+
package metadata are included.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019 Raine Luntta
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/MIGRATING.md
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Migrating from swatch 2.x to 3.0
|
|
2
|
+
|
|
3
|
+
Swatch 3.0 is a breaking rewrite. Storage moved to a canonical `{ space, coords, alpha }` state, conversions are lazy and memoized, manipulation became OKLCh-based by default, and the library gained CSS Color 4 parsing, wide-gamut spaces, color scales, built-in palettes, and more.
|
|
4
|
+
|
|
5
|
+
Most existing code will work with only small changes. The sections below cover the breakages, from most visible to most obscure.
|
|
6
|
+
|
|
7
|
+
## 1. Manipulation amounts and space (the big one)
|
|
8
|
+
|
|
9
|
+
In v2 `lighten`/`darken`/`saturate`/`desaturate` operated in HSL with amounts in `0..100` (percentage-ish). In v3 they operate in OKLCh with amounts in `0..1` (OKLCh L and C units).
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
// v2
|
|
13
|
+
swatch("#ff0000").lighten(20); // +20 HSL L
|
|
14
|
+
swatch("#ff0000").saturate(10); // +10 HSL S
|
|
15
|
+
|
|
16
|
+
// v3
|
|
17
|
+
swatch("#ff0000").lighten(0.1); // +0.1 OKLCh L
|
|
18
|
+
swatch("#ff0000").saturate(0.05); // +0.05 OKLCh C
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Why the change?** HSL lightness isn't perceptually uniform — `#ffff00` barely moves when you lighten it in HSL, because yellow already has near-maximum HSL L. OKLCh L is perceptually uniform, so `swatch("#ffff00").lighten(0.05)` and `swatch("#0000ff").lighten(0.05)` look equally brighter.
|
|
22
|
+
|
|
23
|
+
**Rough conversion:** v2 HSL amounts divide by about 200 to get a comparable v3 OKLCh amount, but the results are not numerically equivalent — they're perceptually better. Re-test visually.
|
|
24
|
+
|
|
25
|
+
`spin(degrees)` is unchanged (still degrees). `greyscale`, `complement`, and `invert` keep their v2 behavior.
|
|
26
|
+
|
|
27
|
+
## 2. Space conversions are getters, not methods
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
// v2
|
|
31
|
+
c.toRGB();
|
|
32
|
+
c.toLab();
|
|
33
|
+
c.toOklch();
|
|
34
|
+
|
|
35
|
+
// v3
|
|
36
|
+
c.srgb;
|
|
37
|
+
c.lab;
|
|
38
|
+
c.oklch;
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
v3 also adds a large set of new space getters: `c.hsv`, `c.hwb`, `c.cmyk`, `c.hsluv`, `c.luv`, `c.displayP3`, `c.rec2020`, `c.a98`, `c.prophoto`, `c.linearSrgb`, `c.xyz`.
|
|
42
|
+
|
|
43
|
+
Use `c.to("<spaceId>")` to move the canonical space itself (not just read in another space):
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
const p3 = swatch("#ff0000").to("display-p3");
|
|
47
|
+
p3.space; // "display-p3"
|
|
48
|
+
p3.coords; // [r, g, b] in Display P3, not sRGB
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## 3. Wide-gamut inputs are no longer clamped at parse
|
|
52
|
+
|
|
53
|
+
v2 parsed `color(display-p3 1 0 0)` by immediately projecting into sRGB (which clipped the chromaticity). v3 preserves the original Display P3 coordinates losslessly in the canonical state.
|
|
54
|
+
|
|
55
|
+
```js
|
|
56
|
+
const p3red = swatch("color(display-p3 1 0 0)");
|
|
57
|
+
p3red.space; // "display-p3"
|
|
58
|
+
p3red.inGamut("srgb"); // false
|
|
59
|
+
p3red.displayP3; // { r: 1, g: 0, b: 0 }
|
|
60
|
+
p3red.srgb; // { r: 1.093, g: -0.227, b: -0.150 } — raw conversion, out of range
|
|
61
|
+
p3red.toString({ format: "hex" }); // "#ff0000" (naive clip for presentation)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The `.srgb` / `.linearSrgb` / `.hsl` getters no longer gamut-map. For a wide-gamut source, they will return out-of-range coordinates. Call `.toGamut({ space: "srgb", method: "css4" })` explicitly to get a properly chroma-reduced in-gamut Swatch, then read its `.srgb` / `.hex`.
|
|
65
|
+
|
|
66
|
+
## 4. New `toJSON()` shape
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
// v2
|
|
70
|
+
c.toJSON();
|
|
71
|
+
// { hex: "#3366cc", rgb: { r, g, b }, hsl: { h, s, l }, isValid: true, format: "HEX" }
|
|
72
|
+
|
|
73
|
+
// v3
|
|
74
|
+
c.toJSON();
|
|
75
|
+
// { space: "srgb", coords: [0.2, 0.4, 0.8], alpha: 1 }
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The new shape round-trips cleanly: `swatch(c.toJSON()).equals(c)` is true.
|
|
79
|
+
|
|
80
|
+
If you stored v2 JSON blobs, read them through an adapter that extracts `hex` and passes it to `swatch()`.
|
|
81
|
+
|
|
82
|
+
## 5. `equals()` signature changed
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
// v2
|
|
86
|
+
c.equals(other);
|
|
87
|
+
c.equals(other, { tolerance: 1 });
|
|
88
|
+
c.equals(other, { space: "oklab", tolerance: 0.01 });
|
|
89
|
+
|
|
90
|
+
// v3
|
|
91
|
+
c.equals(other); // default epsilon 1e-6 in the source space
|
|
92
|
+
c.equals(other, 1e-4); // custom epsilon
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
`other` must be a `Swatch` instance in v3 (wrap strings with `swatch(...)` first).
|
|
96
|
+
|
|
97
|
+
## 6. Removed: `isValid`, `hasAlpha`, `_original`, `_originalFormat`
|
|
98
|
+
|
|
99
|
+
In v2, invalid input produced a swatch with `isValid: false`. In v3, invalid input throws. Use `try`/`catch` to validate:
|
|
100
|
+
|
|
101
|
+
```js
|
|
102
|
+
// v2
|
|
103
|
+
if (swatch(input).isValid) { ... }
|
|
104
|
+
|
|
105
|
+
// v3
|
|
106
|
+
try {
|
|
107
|
+
const c = swatch(input);
|
|
108
|
+
...
|
|
109
|
+
} catch {
|
|
110
|
+
// invalid
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
For alpha presence, check the numeric channel directly:
|
|
115
|
+
|
|
116
|
+
```js
|
|
117
|
+
c.alpha < 1 // v3 replacement for v2's `c.hasAlpha`
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`_original` and `_originalFormat` (v2 internals) are gone; use `c.space` and `c.coords` to introspect the canonical state.
|
|
121
|
+
|
|
122
|
+
## 7. `mix()` takes an options bag for the space
|
|
123
|
+
|
|
124
|
+
```js
|
|
125
|
+
// v2
|
|
126
|
+
a.mix(b, 0.5, "lab");
|
|
127
|
+
a.mix(b, 0.5, "rgb");
|
|
128
|
+
|
|
129
|
+
// v3
|
|
130
|
+
a.mix(b, 0.5, { space: "lab" });
|
|
131
|
+
a.mix(b, 0.5, { space: "srgb" });
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Default space is still `oklab`.
|
|
135
|
+
|
|
136
|
+
## 8. Harmonies are not yet ported
|
|
137
|
+
|
|
138
|
+
v2's `complementary()`, `triad()`, `tetrad()`, `splitComplement()`, `analogous()`, `monochromatic()` are not in v3.0. They will return in a follow-up release. As a workaround, `spin()` + an array literal covers most cases:
|
|
139
|
+
|
|
140
|
+
```js
|
|
141
|
+
// v2
|
|
142
|
+
c.triad();
|
|
143
|
+
|
|
144
|
+
// v3 workaround
|
|
145
|
+
const s = swatch(c);
|
|
146
|
+
[s, s.spin(120), s.spin(240)];
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Tracked for follow-up. If you rely on harmonies, pin to v2 or file an issue.
|
|
150
|
+
|
|
151
|
+
## 9. Named color list moved
|
|
152
|
+
|
|
153
|
+
v2 exported `named-colors.js` directly. v3 moved it to `src/data/named-colors.js`. If you imported the raw table, update the path. For most users, `c.toName()` / `c.name()` are the preferred API.
|
|
154
|
+
|
|
155
|
+
## 10. New APIs worth learning
|
|
156
|
+
|
|
157
|
+
Things v2 didn't have at all:
|
|
158
|
+
|
|
159
|
+
| Feature | API |
|
|
160
|
+
|---|---|
|
|
161
|
+
| CSS Color 4 parsing | `swatch("oklch(0.7 0.15 240)")`, `swatch("color(display-p3 1 0 0)")` |
|
|
162
|
+
| Channel get/set | `c.get("oklch.l")`, `c.set("oklch.h", 120)` |
|
|
163
|
+
| Gamut mapping | `c.inGamut("srgb")`, `c.toGamut({ space, method })` |
|
|
164
|
+
| Tint / shade / tone | `c.tint(0.2)`, `c.shade(0.2)`, `c.tone(0.2)` |
|
|
165
|
+
| Blend modes | `c.blend(other, "multiply")` — all W3C Level 1 modes |
|
|
166
|
+
| Color scales | `swatch.scale([...])`, `.domain`, `.classes`, `.padding`, `.gamma`, `.mode`, `.correctLightness`, `.cache`, `.colors(n)` |
|
|
167
|
+
| Built-in palettes | `swatch.scale("viridis")`, `swatch.scale("RdBu")`, `swatch.palettes()` |
|
|
168
|
+
| Interpolators | `swatch.bezier([...])`, `swatch.cubehelix({...})` |
|
|
169
|
+
| Color naming | `c.name()`, `c.toName()` |
|
|
170
|
+
| Temperature | `swatch.temperature(6500)`, `c.temperature()` |
|
|
171
|
+
| Random | `swatch.random({ space, hue, lightness, chroma, seed })` |
|
|
172
|
+
| Extra ΔE | `"94"`, `"cmc"`, `"hyab"` on top of `"76"`, `"2000"`, `"ok"` |
|
|
173
|
+
| New spaces | HSV, HWB, CMYK, HSLuv, Luv, Display P3, Rec2020, A98, ProPhoto |
|
|
174
|
+
|
|
175
|
+
See [README.md](./README.md) for examples of each.
|
|
176
|
+
|
|
177
|
+
## 11. Unchanged
|
|
178
|
+
|
|
179
|
+
The CVD/APCA/WCAG story — the heart of swatch — is unchanged:
|
|
180
|
+
|
|
181
|
+
- `simulate(type, { severity })` — Brettel/Viénot dichromat simulation
|
|
182
|
+
- `daltonize(type, { severity })` — Fidaner error redistribution
|
|
183
|
+
- `swatch.checkPalette(colors, { cvd, minDeltaE, mode })`
|
|
184
|
+
- `swatch.nearestDistinguishable(target, against, { cvd, minDeltaE, step })`
|
|
185
|
+
- `swatch.mostReadable(bg, candidates, { level, size })`
|
|
186
|
+
- `contrast(a, b)`, `isReadable(a, b, { level, size })`, `ensureContrast(a, b, { minRatio, direction, step })`
|
|
187
|
+
- `apcaContrast(text, background)`
|
|
188
|
+
|
|
189
|
+
All of these moved to new module paths internally but kept their public signatures.
|