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 CHANGED
@@ -1,30 +1,18 @@
1
1
  # OKChroma
2
2
 
3
- **A color-system engine.** Give it one brand hex, or two, and it resolves a complete set
4
- of light and dark color primitives whose contrast requirements are solved during
5
- generation, then emits it as CSS custom properties, as DTCG documents, and as Figma
6
- variables, every token spelled the same way in each.
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
- > The reserved-role-per-stop model is a conceptual nod to
26
- > [Radix Colors](https://www.radix-ui.com/colors/docs/palette-composition/understanding-the-scale).
27
- > It is not a dependency and does not touch the math; the color computation is original.
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; no runtime dependencies (the one
36
- perceptual-distance library the engine uses is bundled in). Node 18 or later; no DOM.
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
- ## What it emits
62
-
63
- Every token has one path, and every output spells it: `brand/stamp/fill`,
64
- `neutral/paper-0`, `link/default/enabled`, `absolute/brand`. Joined with hyphens it is the
65
- CSS custom property (`--brand-stamp-fill`); joined with slashes it is the Figma variable
66
- and the DTCG token path. A consumer whose paths start with the category puts a `color`
67
- group in front: the extended plugin writes `color/brand/stamp/fill`, and the DTCG
68
- documents nest under the same word when asked. The CSS names never carry it.
69
-
70
- | Group | Tokens |
71
- |---|---|
72
- | `neutral` | `paper-0`, `paper-1`, `paper-3`, `paper-5`, `chalk-8`, `chalk-11`, `chalk-15`, `chalk-20`, `highlighter-26`, `pencil-47`, `pen-58`, `pen-70`, `pen-100`, and the stamp: `stamp/fill`, `stamp/fill-hover`, `stamp/fill-pressed`, `stamp/edge`, `stamp/on` |
73
- | `brand`, `brand-alt`, `critical`, `warning`, `positive`, `info` | the same eleven stops and the same five stamp tokens |
74
- | `link` | `default/enabled`, `default/hover`, `default/pressed` for text on the papers; `inverse/enabled`, `inverse/hover`, `inverse/pressed` for text on the pen ground |
75
- | `absolute` | `brand`, `brand-alt`: the seeds as given, reference values |
76
-
77
- What each name means, which stop to read for which job, and the rules for an agent
78
- writing UI against these: [docs/agents.md](docs/agents.md). The scale and its declared
79
- targets: [docs/scale.md](docs/scale.md).
80
-
81
- **The stops.** The instrument word is the job, the number is 100 minus the light-mode
82
- lightness target, and bigger is stronger. Papers are backgrounds; chalks are decorative
83
- borders and illustration, never text; the highlighter is the non-text contrast stop (focus
84
- rings, icons, large text, and the one stop to build translucent state layers on); the
85
- pencil and the pens are text, and the pencil doubles as the emphasis fill.
86
-
87
- **The stamp.** `stamp/fill` is the call-to-action fill, re-solved per family and theme;
88
- `fill-hover` and `fill-pressed` are its states; `stamp/on` is the text over it, the pole
89
- that passes (the pole at alpha on a quiet fill); `stamp/edge` is a stroke that resolves to
90
- a visible offset only where the fill sits close to the page, else transparent, so a
91
- component renders the border unconditionally and layout never shifts.
92
-
93
- **The outputs.**
94
-
95
- - CSS: `brandCss` writes `[data-brand="acme"]` and `[data-brand="acme"][data-theme="dark"]`
96
- blocks, `signalsCss` the brand-independent signal block at `:root`. Values are sRGB
97
- hexes (or `rgba()` where a token carries alpha); stops whose chroma exceeds sRGB get a
98
- `color(display-p3 …)` override behind a browser gate. The repo's `npm run generate`
99
- writes the signal block to `dist/signals.css`.
100
- - The structured emit: `themeTokens` reads the CSS emission back into one object per
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
- ## Run from source
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
- ```bash
114
- npm install
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
- `npm run typecheck` runs the compiler; the audit gates (`npm run req:audit`,
121
- `npm run audit:guarantee`, `npm run audit:dtcg`, and the rest of `package.json`) each
122
- sweep agnostic seeds and fail on the worst case. What each proves:
123
- [How it is verified](https://egerrity.github.io/okchroma/#/docs/guarantees/how-it-is-verified).
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): what goes in, what comes out, the token roster.
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): every claim stated exactly, and how it is verified.
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: [docs/agents.md](docs/agents.md) (the consumer contract: what every token
137
- means and the rules for using it), [docs/schema.md](docs/schema.md) (the DTCG documents),
138
- [docs/scale.md](docs/scale.md) (the scale and its declared targets),
139
- [docs/architecture.md](docs/architecture.md) (the maintainer's map: modules, pipeline
140
- stages, data structures, the extended plugin's write path), and
141
- [CHANGELOG.md](CHANGELOG.md).
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
- - **OKChroma Extended** (`plugin-ext/`) is the shipped Figma front-end. It requires the
146
- Figma desktop app and a Figma Enterprise plan: it writes extended variable collections,
147
- one base collection with light and dark modes plus one extension per brand that
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
- - **OKChroma** (`plugin/`, the community plugin) is withdrawn from download until it
152
- carries the rename table that migrates an existing file across the scale change of July
153
- 2026. It still builds from source with `npm run plugin:build` and imports from
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