@cssints/rybitten 0.0.0-stage → 0.1.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/LICENSE +47 -0
- package/README.md +125 -2
- package/dist/index.d.mts +54 -0
- package/dist/index.mjs +55 -0
- package/package.json +33 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alexei Accio
|
|
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.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
This package depends on RYBitten (https://github.com/meodai/RYBitten), distributed under this licence:
|
|
26
|
+
|
|
27
|
+
MIT License
|
|
28
|
+
|
|
29
|
+
Copyright (c) 2024 David Aerne
|
|
30
|
+
|
|
31
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
32
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
33
|
+
in the Software without restriction, including without limitation the rights
|
|
34
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
35
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
36
|
+
furnished to do so, subject to the following conditions:
|
|
37
|
+
|
|
38
|
+
The above copyright notice and this permission notice shall be included in all
|
|
39
|
+
copies or substantial portions of the Software.
|
|
40
|
+
|
|
41
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
42
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
43
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
44
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
45
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
46
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
47
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,126 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @cssints/rybitten
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[RYBitten](https://rybitten.space/) colours for [cssints](../../README.md), computed at build time. RYBitten (by meodai, David Aerne in its
|
|
4
|
+
licence; MIT) turns red-yellow-blue coordinates into RGB through a _cube_: the eight corners of a colour model (white, red, yellow,
|
|
5
|
+
orange, blue, violet, green, black). It ships 36 of them, from Boutet (1708) and Goethe to Itten, Munsell and Albers, so
|
|
6
|
+
the same hue is another colour in each. `color(hue, options)` gives one hex colour, `wheel(steps, options)` and
|
|
7
|
+
`shades(hue, lightness, options)` return plain DTCG data (a colour group, steps `"1"` to `"N"`) that you pass to
|
|
8
|
+
`createTokens` yourself, and `overrides(group)` turns a group into the override object of `createGlobalTheme`. The package
|
|
9
|
+
has no runtime and no `scope()`: like [@cssints/ramp](../ramp/README.md) and the sets of
|
|
10
|
+
[@cssints/tokens](../tokens/README.md), a group is data, so it goes under any name, sits beside other tokens in one call,
|
|
11
|
+
and its steps get `@property` rules, themes and `check.contrast` from cssints.
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
// tokens.ts
|
|
15
|
+
import { createGlobalTheme, createTokens, media } from "cssints" with { type: "cssints" };
|
|
16
|
+
import { overrides, shades, wheel } from "@cssints/rybitten";
|
|
17
|
+
|
|
18
|
+
export const t = createTokens({
|
|
19
|
+
hue: wheel(12, { cube: "itten" }),
|
|
20
|
+
paper: shades(0, [1, 0.96, 0], { cube: "itten", saturation: 0 }),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// the paper and the ink swap in the dark
|
|
24
|
+
createGlobalTheme(t, media("(prefers-color-scheme: dark)"), {
|
|
25
|
+
paper: overrides(shades(0, [0, 0.04, 1], { cube: "itten", saturation: 0 })),
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
// text.ts
|
|
31
|
+
import { bg, check, cn, color } from "cssints" with { type: "cssints" };
|
|
32
|
+
|
|
33
|
+
import { t } from "./tokens.ts";
|
|
34
|
+
|
|
35
|
+
check.contrast(t.paper._3, t.paper._1, { min: 12 }); // the ink on the paper, light and dark
|
|
36
|
+
export const text = cn(color(t.hue._9), bg(t.paper._1));
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The sheet of `text.ts`:
|
|
40
|
+
|
|
41
|
+
```css
|
|
42
|
+
@property --hue-9 {
|
|
43
|
+
syntax: "<color>";
|
|
44
|
+
inherits: true;
|
|
45
|
+
initial-value: #1699da;
|
|
46
|
+
}
|
|
47
|
+
@property --paper-1 {
|
|
48
|
+
syntax: "<color>";
|
|
49
|
+
inherits: true;
|
|
50
|
+
initial-value: #fdf6ed;
|
|
51
|
+
}
|
|
52
|
+
@layer _.k {
|
|
53
|
+
@media (prefers-color-scheme: dark) {
|
|
54
|
+
:root {
|
|
55
|
+
--paper-1: #1d1c1c;
|
|
56
|
+
--paper-2: #1e1d1d;
|
|
57
|
+
--paper-3: #fdf6ed;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
@layer _.a {
|
|
62
|
+
._14bfx2y {
|
|
63
|
+
color: var(--hue-9);
|
|
64
|
+
}
|
|
65
|
+
._1j4wng5 {
|
|
66
|
+
background-color: var(--paper-1);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## The functions
|
|
72
|
+
|
|
73
|
+
- **`color(hue, { cube?, saturation?, lightness? })`**: the hex colour of a hue. The hue is degrees on a painter's wheel,
|
|
74
|
+
not the HSL wheel: `0` red, `120` yellow, `240` blue (Itten: `#e32421`, `#f3e600`, `#1699da`), any number, `360` is `0`.
|
|
75
|
+
Saturation is `0` to `1` (default `1`; `0` gives the cube's greys). Lightness is `0` to `1` (default `0.5`): `1` is the
|
|
76
|
+
cube's white and `0` its black, because RYB is subtractive (RYBitten's `invertLightness`). The cube is a key of
|
|
77
|
+
RYBitten's `cubes` map (default `"itten"`) or your own eight corners.
|
|
78
|
+
- **`wheel(steps, { from?, cube?, saturation?, lightness? })`**: `steps` hues evenly round the wheel from `from` degrees
|
|
79
|
+
(default `0`): `wheel(12)` is Itten's twelve-part circle, red, red-orange, orange, ... `"1"` to `"12"`.
|
|
80
|
+
- **`shades(hue, lightness, { cube?, saturation? })`**: one hue at the lightnesses you list, in that order, `"1"` to
|
|
81
|
+
`"N"` for N values. With `saturation: 0` it is the cube's own grey scale, from its paper white to its ink black.
|
|
82
|
+
- **`overrides(group)`**: `{ _1: hex, … }` for `createGlobalTheme`, as cssints reads a key that starts with a digit.
|
|
83
|
+
|
|
84
|
+
A group is not a contrast ramp: the colours of a cube are what its makers painted, and no ratio is promised. For a
|
|
85
|
+
colour that must pass `check.contrast`, use a hue of the wheel as the base of an `@cssints/ramp` contrast ramp, which
|
|
86
|
+
finds the step with the ratio you need (the docs site does this for its accent, see below), or check the pair as above.
|
|
87
|
+
|
|
88
|
+
## Cubes
|
|
89
|
+
|
|
90
|
+
`cubes` is RYBitten's own map (`import { cubes } from "rybitten/cubes"`): `itten` (the default), `munsell`, `bezold`,
|
|
91
|
+
`goethe`, `albers`, `apple90s`, `harris`, `cmy`, ... with title, author, year and reference. The hue `240` in four of them:
|
|
92
|
+
|
|
93
|
+
| Cube | Blue | Paper | Ink |
|
|
94
|
+
| --------- | --------- | --------- | --------- |
|
|
95
|
+
| `itten` | `#1699da` | `#fdf6ed` | `#1d1c1c` |
|
|
96
|
+
| `munsell` | `#3b8fab` | `#e4dac5` | `#2e2c26` |
|
|
97
|
+
| `albers` | `#1161aa` | `#e7ebed` | `#000001` |
|
|
98
|
+
| `goethe` | `#5f9dbf` | `#efebe1` | `#08090d` |
|
|
99
|
+
|
|
100
|
+
## Limits
|
|
101
|
+
|
|
102
|
+
- **Pinned.** `rybitten` is a dependency at `1.3.0`: RYBitten's contract does not cover the numbers of a cube, which a
|
|
103
|
+
minor release may refine, so a colour can move with an upgrade (the tests pin Itten's corners).
|
|
104
|
+
- **Errors are thrown** when the module is evaluated, so they are build errors: a cube that is not in the map
|
|
105
|
+
(`@cssints/rybitten: no cube "nope" in RYBitten's cubes`), a hue that is not a number, a saturation or lightness outside
|
|
106
|
+
0..1, `steps` that is not a whole number from 1.
|
|
107
|
+
- **sRGB hex only.** A cube holds sRGB corners, so the colours are in sRGB (trilinear interpolation stays inside the
|
|
108
|
+
cube), and a hex is what `check.contrast` reads exactly and the types accept cheaply.
|
|
109
|
+
- **No harmonies.** A complement is `hue + 180`, a triad `+ 120`: `color(h + 180)`, or the steps of a `wheel(3)`.
|
|
110
|
+
- **The editor shows `` `#${string}` ``.** A value is computed when the module runs, so its type is a hex string, not the
|
|
111
|
+
literal (as for @cssints/ramp). The steps are known (`t.hue._13` of a twelve-step wheel is a type error).
|
|
112
|
+
- **Type cost.** A step is a `` `#${string}` `` token like a step of @cssints/ramp: the whole type test (a wheel of twelve, a
|
|
113
|
+
run of four, their overrides in a theme) is 0.19 M instantiations in tsgo, as many as the type test of ramp.
|
|
114
|
+
- **No runtime.** A hue picked in the browser needs one of the steps built ahead (the docs site builds a theme per step of
|
|
115
|
+
a twelve-hue wheel and switches them with an attribute) or `rybitten` itself in the page, which this package does not ship.
|
|
116
|
+
|
|
117
|
+
## Credits
|
|
118
|
+
|
|
119
|
+
Colour maths and the historical cubes are [RYBitten](https://github.com/meodai/RYBitten) by meodai (David Aerne; MIT), a
|
|
120
|
+
dependency of this package, whose licence is reproduced in LICENSE here. The cubes
|
|
121
|
+
cite their historical sources: see RYBitten's README.
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
cd packages/rybitten && bun run test # the colours and the errors, then a module through the engine (check.contrast passes, a hue on paper fails)
|
|
125
|
+
cd packages/rybitten && bun run typecheck # the type test (test/types/rybitten.ts)
|
|
126
|
+
```
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { ColorCube } from "rybitten/cubes";
|
|
2
|
+
//#region src/index.d.ts
|
|
3
|
+
/** A hex colour: cheap in the types (createTokens checks it without the walk of every colour syntax). */
|
|
4
|
+
export type Hex = `#${string}`;
|
|
5
|
+
/** The step names of an N-step group: `"1"` to `"N"` (`t.wheel._1` in code, `--wheel-1` in CSS). */
|
|
6
|
+
export type Keys<N extends number, A extends string[] = []> = number extends N ? `${number}` : A["length"] extends N ? A[number] : Keys<N, [...A, `${[...A, 0]["length"]}`]>;
|
|
7
|
+
/** A colour group: a DTCG group whose tokens are the steps. */
|
|
8
|
+
export type Palette<K extends string> = {
|
|
9
|
+
readonly $type: "color";
|
|
10
|
+
} & { readonly [P in K]: {
|
|
11
|
+
readonly $value: Hex;
|
|
12
|
+
}; };
|
|
13
|
+
/** The overrides of a group for createGlobalTheme: a step `"1"` is `_1`, as cssints reads a key that starts with a digit. */
|
|
14
|
+
export type PaletteOverrides<K extends string> = { readonly [P in K as `_${P}`]: Hex; };
|
|
15
|
+
/** Which colours a hue becomes. */
|
|
16
|
+
export interface Options {
|
|
17
|
+
/** A key of RYBitten's `cubes` (`"itten"`, `"munsell"`, `"albers"`, ...) or a cube of eight RGB corners; default `"itten"`. */
|
|
18
|
+
readonly cube?: string | ColorCube;
|
|
19
|
+
/** HSL saturation from 0 (the cube's greys) to 1; default 1. */
|
|
20
|
+
readonly saturation?: number;
|
|
21
|
+
/** HSL lightness from 0 (the cube's black) to 1 (its white); default 0.5. */
|
|
22
|
+
readonly lightness?: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The sRGB hex colour of a hue in a cube.
|
|
26
|
+
* @param {number} hue degrees on the RYB wheel, any number (360 is 0)
|
|
27
|
+
* @param {Options} options the cube, saturation and lightness
|
|
28
|
+
* @returns {Hex} `#rrggbb`
|
|
29
|
+
*/
|
|
30
|
+
export declare const color: (hue: number, options?: Options) => `#${string}`;
|
|
31
|
+
/**
|
|
32
|
+
* `steps` hues evenly round the wheel, from `from` degrees (default 0, red), as a colour group `"1"` to `"N"`.
|
|
33
|
+
* @param {number} steps how many hues, a whole number from 1
|
|
34
|
+
* @param {Options & { from?: number }} options the cube, saturation and lightness of every step
|
|
35
|
+
* @returns {Palette<Keys<N>>} a DTCG colour group for createTokens
|
|
36
|
+
*/
|
|
37
|
+
export declare const wheel: <const N extends number>(steps: N, { from, ...options }?: Options & {
|
|
38
|
+
readonly from?: number;
|
|
39
|
+
}) => Palette<Keys<N, []>>;
|
|
40
|
+
/**
|
|
41
|
+
* One hue at several lightnesses, one step per value, in the order given (1 is the cube's white, 0 its black).
|
|
42
|
+
* @param {number} hue degrees on the RYB wheel
|
|
43
|
+
* @param {readonly number[]} lightness the lightness of each step, from 0 to 1
|
|
44
|
+
* @param {Omit<Options, "lightness">} options the cube and the saturation
|
|
45
|
+
* @returns {Palette<Keys<R["length"]>>} a DTCG colour group for createTokens
|
|
46
|
+
*/
|
|
47
|
+
export declare const shades: <const R extends readonly number[]>(hue: number, lightness: R, options?: Omit<Options, "lightness">) => Palette<Keys<R["length"], []>>;
|
|
48
|
+
/**
|
|
49
|
+
* A group as the overrides of a theme: `createGlobalTheme(t.wheel, dark, overrides(wheel(12, { cube: "munsell" })))`.
|
|
50
|
+
* @param {Palette<K>} p a group of `wheel` or `shades`
|
|
51
|
+
* @returns {PaletteOverrides<K>} its steps as `{ _1: hex, … }`
|
|
52
|
+
*/
|
|
53
|
+
export declare const overrides: <K extends string>(p: Palette<K>) => PaletteOverrides<K>;
|
|
54
|
+
//#endregion
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { rybHsl2rgb } from "rybitten";
|
|
2
|
+
import { cubes } from "rybitten/cubes";
|
|
3
|
+
//#region src/index.ts
|
|
4
|
+
const fail = (why) => {
|
|
5
|
+
throw new Error(`@cssints/rybitten: ${why}`);
|
|
6
|
+
};
|
|
7
|
+
const cubeOf = (cube = "itten") => typeof cube === "string" ? cubes.get(cube)?.cube ?? fail(`no cube "${cube}" in RYBitten's cubes`) : cube;
|
|
8
|
+
const unit = (x, what) => x >= 0 && x <= 1 ? x : fail(`${what} is from 0 to 1, got ${x}`);
|
|
9
|
+
const channel = (x) => Math.round(Math.min(1, Math.max(0, x)) * 255).toString(16).padStart(2, "0");
|
|
10
|
+
const hex = (rgb) => `#${rgb.map(channel).join("")}`;
|
|
11
|
+
/**
|
|
12
|
+
* The sRGB hex colour of a hue in a cube.
|
|
13
|
+
* @param {number} hue degrees on the RYB wheel, any number (360 is 0)
|
|
14
|
+
* @param {Options} options the cube, saturation and lightness
|
|
15
|
+
* @returns {Hex} `#rrggbb`
|
|
16
|
+
*/
|
|
17
|
+
const color = (hue, options = {}) => {
|
|
18
|
+
const { cube, saturation = 1, lightness = .5 } = options;
|
|
19
|
+
if (!Number.isFinite(hue)) fail(`hue is a number of degrees, got ${hue}`);
|
|
20
|
+
return hex(rybHsl2rgb([
|
|
21
|
+
(hue % 360 + 360) % 360,
|
|
22
|
+
unit(saturation, "saturation"),
|
|
23
|
+
unit(lightness, "lightness")
|
|
24
|
+
], { cube: cubeOf(cube) }));
|
|
25
|
+
};
|
|
26
|
+
const group = (colors) => Object.fromEntries([["$type", "color"], ...colors.map((c, i) => [String(i + 1), { $value: c }])]);
|
|
27
|
+
/**
|
|
28
|
+
* `steps` hues evenly round the wheel, from `from` degrees (default 0, red), as a colour group `"1"` to `"N"`.
|
|
29
|
+
* @param {number} steps how many hues, a whole number from 1
|
|
30
|
+
* @param {Options & { from?: number }} options the cube, saturation and lightness of every step
|
|
31
|
+
* @returns {Palette<Keys<N>>} a DTCG colour group for createTokens
|
|
32
|
+
*/
|
|
33
|
+
const wheel = (steps, { from = 0, ...options } = {}) => {
|
|
34
|
+
if (!Number.isInteger(steps) || steps < 1) fail(`steps is a whole number from 1, got ${steps}`);
|
|
35
|
+
return group(Array.from({ length: steps }, (_, i) => color(from + 360 * i / steps, options)));
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* One hue at several lightnesses, one step per value, in the order given (1 is the cube's white, 0 its black).
|
|
39
|
+
* @param {number} hue degrees on the RYB wheel
|
|
40
|
+
* @param {readonly number[]} lightness the lightness of each step, from 0 to 1
|
|
41
|
+
* @param {Omit<Options, "lightness">} options the cube and the saturation
|
|
42
|
+
* @returns {Palette<Keys<R["length"]>>} a DTCG colour group for createTokens
|
|
43
|
+
*/
|
|
44
|
+
const shades = (hue, lightness, options = {}) => group(lightness.map((l) => color(hue, {
|
|
45
|
+
...options,
|
|
46
|
+
lightness: l
|
|
47
|
+
})));
|
|
48
|
+
/**
|
|
49
|
+
* A group as the overrides of a theme: `createGlobalTheme(t.wheel, dark, overrides(wheel(12, { cube: "munsell" })))`.
|
|
50
|
+
* @param {Palette<K>} p a group of `wheel` or `shades`
|
|
51
|
+
* @returns {PaletteOverrides<K>} its steps as `{ _1: hex, … }`
|
|
52
|
+
*/
|
|
53
|
+
const overrides = (p) => Object.fromEntries(Object.entries(p).flatMap(([k, v]) => k === "$type" ? [] : [[`_${k}`, v.$value]]));
|
|
54
|
+
//#endregion
|
|
55
|
+
export { color, overrides, shades, wheel };
|
package/package.json
CHANGED
|
@@ -1,6 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cssints/rybitten",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "RYB palettes for cssints as DTCG data: a hue wheel and ramps in RYBitten's historical colour cubes, at build time",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/alexeiaccio/css-in-ts-compiler.git",
|
|
9
|
+
"directory": "packages/rybitten"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://cssints.dev",
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"type": "module",
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.mts",
|
|
20
|
+
"default": "./dist/index.mjs"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"rybitten": "1.3.0"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"cssints": "^0.1.0"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=22"
|
|
31
|
+
},
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
}
|
|
35
|
+
}
|