okchroma 0.8.2 → 0.9.1
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/README.md +136 -101
- package/dist-lib/index.cjs +107 -127
- package/dist-lib/index.mjs +107 -127
- package/dist-lib/types/engine/archetypes.d.ts +1 -1
- package/dist-lib/types/engine/dtcgRender.d.ts +2 -2
- package/dist-lib/types/engine/figmaRender.d.ts +0 -1
- package/dist-lib/types/engine/requirements/producers.d.ts +4 -0
- package/dist-lib/types/engine/tokenNames.d.ts +8 -12
- package/dist-lib/types/index.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,30 +1,18 @@
|
|
|
1
1
|
# OKChroma
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
Every family (neutral, brand, brand-alt, critical, warning, positive, info) carries the
|
|
9
|
-
same scale: 3 papers, 4 chalks, 1 highlighter, 1 pencil, 2 pens, plus the stamp (the
|
|
10
|
-
solid fill with its hover and pressed states, its edge, and its text). The neutral adds
|
|
11
|
-
the two poles. Two link trios and the two brand seeds complete the set. Light and dark
|
|
12
|
-
resolve together and ship on the same names, so a token is mapped once and holds for any
|
|
13
|
-
brand.
|
|
14
|
-
|
|
15
|
-
Contrast is built into the math, not checked afterwards: highlighter reads on every paper at
|
|
16
|
-
3:1 (the non-text bar), pencil at 4.5:1, pen at 4.5:1 on every paper and chalk in
|
|
17
|
-
both directions; the stamp's text passes 4.5:1 on its fill. APCA is used once, as a booster
|
|
18
|
-
that nudges the stamp fill until its text reads at Lc 65. Each claim, its scope, and the audit that
|
|
19
|
-
proves it: [Guarantees](https://egerrity.github.io/okchroma/#/docs/guarantees).
|
|
20
|
-
|
|
21
|
-
The engine emits primitives only: values it calculates from the seed. Elevation planes,
|
|
22
|
-
shadows, opacity ladders and state layers are a design system's to author on top of these,
|
|
23
|
-
and [docs/agents.md](docs/agents.md) says which primitive each of those decisions reads.
|
|
3
|
+
okchroma is a color engine. A fixed set of formulas generates a color system's base
|
|
4
|
+
tokens from a seed color and a set of options. Each token gets a light and a dark value,
|
|
5
|
+
and the contrast requirements are solved while the values are generated. The set is
|
|
6
|
+
emitted as CSS custom properties, as DTCG documents, and as Figma variables.
|
|
24
7
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
8
|
+
The seed is one brand hex. The options add a second seed or derive a companion from the
|
|
9
|
+
first, pin a family's fill to a lightness anchor, and pick the contrast profile. They are
|
|
10
|
+
listed with the API: [Install and API](https://egerrity.github.io/okchroma/#/docs/install).
|
|
11
|
+
|
|
12
|
+
okchroma writes base tokens, designed for use under any semantic layer, including one
|
|
13
|
+
that must cover many themes. It writes no semantic tokens. The semantic tokens in this
|
|
14
|
+
repo, in [demo/semantic.css](demo/semantic.css), style the demo site and are not engine
|
|
15
|
+
output.
|
|
28
16
|
|
|
29
17
|
## Install
|
|
30
18
|
|
|
@@ -32,8 +20,9 @@ and [docs/agents.md](docs/agents.md) says which primitive each of those decision
|
|
|
32
20
|
npm install okchroma
|
|
33
21
|
```
|
|
34
22
|
|
|
35
|
-
ESM and CommonJS builds with TypeScript declarations
|
|
36
|
-
|
|
23
|
+
The package ships ESM and CommonJS builds with TypeScript declarations. It has no
|
|
24
|
+
runtime dependencies: the one library the engine uses, for perceptual color distance, is
|
|
25
|
+
bundled at build time. Node 18 or later; no DOM.
|
|
37
26
|
|
|
38
27
|
```ts
|
|
39
28
|
import { resolveTheme, brandCss, signalsCss, themeTokens, tokensToDtcg, neutralTintHue } from 'okchroma'
|
|
@@ -58,103 +47,149 @@ const { light, dark } = tokensToDtcg(tokens)
|
|
|
58
47
|
The Figma tree comes from `themeToFigma`. Signatures, the full input, and an end-to-end
|
|
59
48
|
example: [Install and API](https://egerrity.github.io/okchroma/#/docs/install).
|
|
60
49
|
|
|
61
|
-
##
|
|
62
|
-
|
|
63
|
-
Every
|
|
64
|
-
`neutral
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
|
71
|
-
|
|
72
|
-
| `
|
|
73
|
-
| `
|
|
74
|
-
| `
|
|
75
|
-
| `
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
mode with every reference resolved, for consumers with no cascade.
|
|
102
|
-
- DTCG: `tokensToDtcg` returns one Design Tokens Format Module 2025.10 document per mode,
|
|
103
|
-
every token with `$type`, an sRGB `$value` whose components and hex are the same 8-bit
|
|
104
|
-
color (or an alias where the CSS writes a reference), and a `$description`. The two join
|
|
105
|
-
through a resolver document. The format: [docs/schema.md](docs/schema.md). The repo's
|
|
106
|
-
`npm run tokens:emit -- '#E93D82' acme` writes both files. Pass `{ rootGroup: 'color' }`,
|
|
107
|
-
or `--root color` to the script, to nest every path and alias under that group.
|
|
108
|
-
- Figma: `themeToFigma` returns a light and a dark group tree on the same paths; the
|
|
109
|
-
extended plugin writes it into a file.
|
|
50
|
+
## The tokens
|
|
51
|
+
|
|
52
|
+
Every family emits the same set of names, so a name says the token's job whichever
|
|
53
|
+
family carries it. The families are `neutral`, `brand`, `brand-alt`, `critical`,
|
|
54
|
+
`warning`, `positive` and `info`; the neutral adds the two poles. The link rows and the
|
|
55
|
+
seeds sit outside the families.
|
|
56
|
+
|
|
57
|
+
| Token | Job | Contrast guarantee |
|
|
58
|
+
|---|---|---|
|
|
59
|
+
| `{family}/paper-1` | background | none; the contrast stops are measured against the papers |
|
|
60
|
+
| `{family}/paper-3` | background | none |
|
|
61
|
+
| `{family}/paper-5` | background; the paper the contrast stops are solved against | none |
|
|
62
|
+
| `{family}/chalk-8` | decorative border, illustration, ground for inverted text; never text | none; the pens are measured against the chalks |
|
|
63
|
+
| `{family}/chalk-11` | the same | none |
|
|
64
|
+
| `{family}/chalk-15` | the same | none |
|
|
65
|
+
| `{family}/chalk-20` | the same | none |
|
|
66
|
+
| `{family}/highlighter-26` | focus ring, icon, large text; at a translucency, the state layer | 3:1 on the papers, the non-text bar |
|
|
67
|
+
| `{family}/pencil-47` | regular text; the emphasis fill | 4.5:1 on the papers |
|
|
68
|
+
| `{family}/pen-58` | text; inverted background | 4.5:1 on the papers and the chalks, both directions |
|
|
69
|
+
| `{family}/pen-70` | heavy-emphasis text; inverted background | 4.5:1 on the papers and the chalks, both directions |
|
|
70
|
+
| `neutral/paper-0` | white in light; in dark, the plane one seam below `paper-1` | none stated |
|
|
71
|
+
| `neutral/pen-100` | black in light, white in dark; the strongest text | none stated |
|
|
72
|
+
| `{family}/stamp-0` | the call-to-action fill at rest, solved per family and mode | `stamp-on` reads on it at 4.5:1 |
|
|
73
|
+
| `{family}/stamp-1` | the fill one step from rest | `stamp-on` reads on it at 4.5:1 |
|
|
74
|
+
| `{family}/stamp-2` | the fill two steps from rest | `stamp-on` reads on it at 4.5:1 |
|
|
75
|
+
| `{family}/stamp-edge` | a stroke: an offset where the fill sits close to the page, transparent elsewhere, so a component always renders it | none stated |
|
|
76
|
+
| `{family}/stamp-on` | the text on the fill, whichever pole passes; at alpha on a quiet fill | 4.5:1 on the three fills |
|
|
77
|
+
| `link/default-0` | link text on the papers, at rest | none stated |
|
|
78
|
+
| `link/default-1` | one step from rest | none stated |
|
|
79
|
+
| `link/default-2` | two steps from rest | none stated |
|
|
80
|
+
| `link/inverse-0` | link text on the pen ground, at rest | none stated |
|
|
81
|
+
| `link/inverse-1` | one step from rest | none stated |
|
|
82
|
+
| `link/inverse-2` | two steps from rest | none stated |
|
|
83
|
+
| `absolute/brand` | the seed as given; a reference value, not a UI color | none stated |
|
|
84
|
+
| `absolute/brand-alt` | the second seed as given | none stated |
|
|
85
|
+
|
|
86
|
+
The papers and chalks a stop is measured against are its own family's and the neutral's.
|
|
87
|
+
APCA is used once: the stamp fill also moves until its text clears Lc 65. The full
|
|
88
|
+
claims and their audits: [Guarantees](https://egerrity.github.io/okchroma/#/docs/guarantees).
|
|
89
|
+
The lightness targets behind the numbers: [docs/scale.md](docs/scale.md).
|
|
110
90
|
|
|
111
|
-
|
|
91
|
+
> The reserved-role-per-stop model is a conceptual nod to
|
|
92
|
+
> [Radix Colors](https://www.radix-ui.com/colors/docs/palette-composition/understanding-the-scale).
|
|
93
|
+
> It is not a dependency and does not touch the math; the color computation is original.
|
|
94
|
+
|
|
95
|
+
## The grammar
|
|
96
|
+
|
|
97
|
+
A name is a group and a leaf:
|
|
112
98
|
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
npm run demo:build # writes dist/signals.css and bundles the demo
|
|
116
|
-
npx serve . # open http://localhost:3000/demo/index.html
|
|
117
|
-
npm run dev # watch mode
|
|
99
|
+
```
|
|
100
|
+
group/leaf
|
|
118
101
|
```
|
|
119
102
|
|
|
120
|
-
`
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
103
|
+
The group is a family, `link`, or `absolute`. The leaf names a position, never a state;
|
|
104
|
+
a consumer's semantic layer names the states and aliases the positions.
|
|
105
|
+
|
|
106
|
+
| Leaf | Pattern | Example |
|
|
107
|
+
|---|---|---|
|
|
108
|
+
| a scale stop | `{instrument}-{number}` | `pen-58` |
|
|
109
|
+
| a stamp fill | `stamp-{step}` | `stamp-0` |
|
|
110
|
+
| the stamp's edge and text | `stamp-edge`, `stamp-on` | `stamp-on` |
|
|
111
|
+
| a link | `{posture}-{step}`, the postures `default` and `inverse` | `default-1` |
|
|
112
|
+
| a seed | the family word | `brand-alt` |
|
|
113
|
+
|
|
114
|
+
The instrument is the job. On the scale (paper, chalk, highlighter, pencil, pen) the
|
|
115
|
+
number is 100 minus the light-mode lightness target, so a bigger number is a stronger
|
|
116
|
+
color. In the brand identity (stamp, link) the number is the step from rest: 0 at rest,
|
|
117
|
+
1 one step away, 2 two steps.
|
|
118
|
+
|
|
119
|
+
Each output spells the same name its own way:
|
|
120
|
+
|
|
121
|
+
| Output | Spelling | `brand/stamp-0` becomes |
|
|
122
|
+
|---|---|---|
|
|
123
|
+
| CSS custom property | `--group-leaf` | `--brand-stamp-0` |
|
|
124
|
+
| Figma variable | `group/leaf`; the extended plugin writes it under `color/` | `color/brand/stamp-0` |
|
|
125
|
+
| DTCG token | nested as `group.leaf`, referenced as `{group.leaf}`; `rootGroup: 'color'` nests it under `color` | `{brand.stamp-0}` |
|
|
126
|
+
|
|
127
|
+
Which token to use for which job: [docs/agents.md](docs/agents.md).
|
|
128
|
+
|
|
129
|
+
## The outputs
|
|
130
|
+
|
|
131
|
+
- CSS: `brandCss` writes a `[data-brand="your-brand"]` block and a
|
|
132
|
+
`[data-brand="your-brand"][data-theme="dark"]` block; `signalsCss` writes the
|
|
133
|
+
brand-independent signal block at `:root`. Values are sRGB hexes, or `rgba()` where a
|
|
134
|
+
token carries alpha. A stop whose chroma exceeds sRGB gets a `color(display-p3 …)`
|
|
135
|
+
override behind a browser gate. In the repo, `npm run generate` writes the signal block
|
|
136
|
+
to `dist/signals.css`.
|
|
137
|
+
- Objects: `themeTokens` returns one object per mode with every reference resolved, for a
|
|
138
|
+
consumer with no cascade.
|
|
139
|
+
- DTCG: `tokensToDtcg` returns one document per mode. Each token has a `$type`, an sRGB
|
|
140
|
+
`$value` (or an alias where the CSS writes a reference) and a `$description`; a
|
|
141
|
+
resolver document joins the two modes. The format: [docs/schema.md](docs/schema.md).
|
|
142
|
+
In the repo, `npm run tokens:emit -- '#E93D82' your-brand` writes both files; `--root color`
|
|
143
|
+
(or `{ rootGroup: 'color' }` in code) nests the paths under that group.
|
|
144
|
+
- Figma: `themeToFigma` returns a light and a dark group tree on the same paths; the
|
|
145
|
+
extended plugin writes them into a file.
|
|
124
146
|
|
|
125
147
|
## Documentation
|
|
126
148
|
|
|
127
149
|
The docs site renders the mechanisms with live values from the engine:
|
|
128
150
|
[egerrity.github.io/okchroma/#/docs](https://egerrity.github.io/okchroma/#/docs).
|
|
129
151
|
|
|
130
|
-
- [Overview](https://egerrity.github.io/okchroma/#/docs/overview):
|
|
152
|
+
- [Overview](https://egerrity.github.io/okchroma/#/docs/overview): the input the engine needs, and the token roster it makes from it.
|
|
131
153
|
- [Output contract](https://egerrity.github.io/okchroma/#/docs/output): the naming grammar, families and prefixes, modes and selectors, a live CSS block and Figma tree.
|
|
132
|
-
- [Guarantees](https://egerrity.github.io/okchroma/#/docs/guarantees):
|
|
154
|
+
- [Guarantees](https://egerrity.github.io/okchroma/#/docs/guarantees): each claim as stated, and how it is verified.
|
|
133
155
|
- [How the theme is generated](https://egerrity.github.io/okchroma/#/docs/generation): the pipeline in execution order, with the constants and the code that runs each step.
|
|
134
156
|
- [Signals and companions](https://egerrity.github.io/okchroma/#/docs/signals) and the [Reference](https://egerrity.github.io/okchroma/#/docs/reference): glossary, constants, option types.
|
|
135
157
|
|
|
136
|
-
In the repo:
|
|
137
|
-
|
|
138
|
-
[docs/
|
|
139
|
-
[docs/
|
|
140
|
-
|
|
141
|
-
[
|
|
158
|
+
In the repo:
|
|
159
|
+
|
|
160
|
+
- [docs/agents.md](docs/agents.md): the consumer contract, what each token means and the rules for using it.
|
|
161
|
+
- [docs/schema.md](docs/schema.md): the DTCG documents.
|
|
162
|
+
- [docs/scale.md](docs/scale.md): the scale and its declared targets.
|
|
163
|
+
- [docs/architecture.md](docs/architecture.md): the maintainer's map, modules, pipeline stages, data structures, the extended plugin's write path.
|
|
164
|
+
- [CHANGELOG.md](CHANGELOG.md).
|
|
165
|
+
|
|
166
|
+
## Run from source
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
npm install
|
|
170
|
+
npm run demo:build # writes dist/signals.css and bundles the demo
|
|
171
|
+
npx serve . # open http://localhost:3000/demo/index.html
|
|
172
|
+
npm run dev # watch mode
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
`npm run typecheck` runs the compiler. The audit scripts in `package.json`
|
|
176
|
+
(`npm run req:audit`, `npm run audit:guarantee`, `npm run audit:dtcg`, and the rest)
|
|
177
|
+
check the guarantees and the outputs. What each proves:
|
|
178
|
+
[How it is verified](https://egerrity.github.io/okchroma/#/docs/guarantees/how-it-is-verified).
|
|
142
179
|
|
|
143
180
|
## The Figma plugins
|
|
144
181
|
|
|
145
|
-
-
|
|
146
|
-
|
|
147
|
-
one
|
|
182
|
+
- OKChroma Extended (`plugin-ext/`) is the shipped Figma plugin. It needs the Figma
|
|
183
|
+
desktop app and a Figma Enterprise plan, because it writes extended variable
|
|
184
|
+
collections: one collection with light and dark modes, and an extension per brand that
|
|
148
185
|
overrides only what differs. Download and install steps:
|
|
149
186
|
[install page](https://egerrity.github.io/okchroma/install.html). Build from source with
|
|
150
187
|
`npm run plugin-ext:build`; see [plugin-ext/README.md](plugin-ext/README.md).
|
|
151
|
-
-
|
|
152
|
-
|
|
153
|
-
|
|
188
|
+
- OKChroma (`plugin/`, the community plugin) is not offered for download. It has no
|
|
189
|
+
rename table for the last scale change, so updating an installed copy would leave the
|
|
190
|
+
old names in the file. It builds with `npm run plugin:build` and imports from
|
|
154
191
|
`plugin/manifest.json`; a fresh file gets the current shape.
|
|
155
192
|
|
|
156
|
-
The demo and the plugins are front-ends. The product is the engine and what it emits.
|
|
157
|
-
|
|
158
193
|
## License
|
|
159
194
|
|
|
160
195
|
MIT
|