react-x11 2.52.0 → 2.53.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.
Files changed (112) hide show
  1. package/README.md +10 -10
  2. package/docs/README.md +160 -314
  3. package/docs/accessibility.md +176 -229
  4. package/docs/appearance.md +77 -176
  5. package/docs/click-to-component.md +18 -51
  6. package/docs/clipboard.md +40 -59
  7. package/docs/components.md +518 -1036
  8. package/docs/dbus.md +61 -133
  9. package/docs/debugging.md +152 -245
  10. package/docs/desktop-calendar.md +51 -88
  11. package/docs/desktop.md +222 -347
  12. package/docs/devtools.md +50 -167
  13. package/docs/drag-and-drop.md +126 -204
  14. package/docs/ecosystem/animation.md +91 -125
  15. package/docs/ecosystem/data-fetching.md +38 -53
  16. package/docs/ecosystem/dev-tooling.md +159 -202
  17. package/docs/ecosystem/forms.md +61 -97
  18. package/docs/ecosystem/headless.md +64 -89
  19. package/docs/ecosystem/i18n.md +18 -21
  20. package/docs/ecosystem/icons.md +93 -126
  21. package/docs/ecosystem/layout.md +42 -52
  22. package/docs/ecosystem/routing.md +18 -29
  23. package/docs/ecosystem/state.md +71 -88
  24. package/docs/ecosystem/testing.md +131 -157
  25. package/docs/ecosystem/theming.md +119 -165
  26. package/docs/ecosystem.md +145 -179
  27. package/docs/elements.md +1091 -1774
  28. package/docs/embedding.md +83 -132
  29. package/docs/events.md +412 -695
  30. package/docs/extending.md +784 -1326
  31. package/docs/eyedropper.md +63 -122
  32. package/docs/filedialog.md +71 -107
  33. package/docs/frame.md +195 -339
  34. package/docs/gl.md +65 -62
  35. package/docs/globalmenu.md +63 -146
  36. package/docs/macos.md +531 -3234
  37. package/docs/notifications.md +33 -47
  38. package/docs/packaging.md +166 -309
  39. package/docs/permissions.md +9 -14
  40. package/docs/react-features.md +81 -106
  41. package/docs/remote.md +32 -76
  42. package/docs/scale.md +72 -186
  43. package/docs/styling.md +329 -547
  44. package/docs/system.md +200 -280
  45. package/docs/testing.md +63 -83
  46. package/docs/typescript.md +6 -48
  47. package/docs/uri-schemes.md +67 -107
  48. package/docs/wayland-backend.md +200 -459
  49. package/docs/windows-integrations.md +75 -131
  50. package/docs/windows.md +343 -1367
  51. package/package.json +3 -2
  52. package/src/Reconciler.js +2 -2
  53. package/src/a11y.js +1 -1
  54. package/src/backend/context2d.js +3 -3
  55. package/src/bootstrap.js +1 -1
  56. package/src/cocoa/app.js +6 -6
  57. package/src/cocoa/bezels.js +1 -1
  58. package/src/cocoa/fonts.js +1 -1
  59. package/src/cocoa/globalmenu.js +1 -1
  60. package/src/cocoa/keymap.js +1 -1
  61. package/src/cocoa/panewindow.js +1 -1
  62. package/src/cocoa/player.js +1 -1
  63. package/src/cocoa/presenter.js +2 -2
  64. package/src/cocoa/promotion.js +1 -1
  65. package/src/cocoa/relaunch.js +1 -1
  66. package/src/cocoa/threaded.js +1 -1
  67. package/src/cocoa/video.js +1 -1
  68. package/src/cocoa/window.js +4 -4
  69. package/src/components/Menu.js +7 -2
  70. package/src/components/Switch.js +1 -1
  71. package/src/components/native.js +1 -1
  72. package/src/dnd.js +2 -2
  73. package/src/glnodes.js +1 -1
  74. package/src/grid.js +1 -1
  75. package/src/layouts.js +1 -1
  76. package/src/nodes/animation.js +1 -1
  77. package/src/nodes/invalidate.js +1 -1
  78. package/src/nodes/layouthost.js +1 -1
  79. package/src/nodes/video.js +19 -8
  80. package/src/nodes/window/droptarget.js +1 -1
  81. package/src/pacing.js +1 -1
  82. package/src/refresh/index.js +2 -1
  83. package/src/types/video.d.ts +5 -2
  84. package/src/videobackend.js +28 -0
  85. package/src/videoframes.js +29 -16
  86. package/src/wayland/app.js +27 -0
  87. package/src/wayland/video.js +376 -0
  88. package/src/win32/a11y.js +1 -1
  89. package/src/win32/app.js +2 -2
  90. package/src/win32/fonts.js +1 -1
  91. package/src/win32/glarea.js +4 -4
  92. package/src/win32/overlay.js +1 -1
  93. package/src/win32/panewindow.js +3 -3
  94. package/src/win32/shell.js +1 -1
  95. package/src/win32/window.js +2 -2
  96. package/src/windowid.js +1 -1
  97. package/src/xvideo.js +189 -0
  98. package/docs/architecture/accessibility.md +0 -470
  99. package/docs/architecture/animation.md +0 -707
  100. package/docs/architecture/container-queries.md +0 -525
  101. package/docs/architecture/custom-layout.md +0 -334
  102. package/docs/architecture/drag-and-drop.md +0 -941
  103. package/docs/architecture/element-layer-contents.md +0 -622
  104. package/docs/architecture/frame-pacing.md +0 -255
  105. package/docs/architecture/grid-layout.md +0 -358
  106. package/docs/architecture/protocol-efficiency.md +0 -357
  107. package/docs/architecture/video.md +0 -1149
  108. package/docs/architecture/windowed-regions.md +0 -854
  109. package/docs/glx.md +0 -137
  110. package/docs/wayland.md +0 -545
  111. package/docs/windows-embedding.md +0 -271
  112. package/docs/windows-gl.md +0 -221
@@ -2,38 +2,28 @@
2
2
 
3
3
  react-x11's theme is a plain object of hex strings and numbers — see
4
4
  [components](../components.md#theming) for the token list and
5
- [styling](../styling.md#theme-tokens) for how `$token` resolves. So every
6
- palette package on this page is used the same way: pick keys at
7
- theme-build time, hand the result to `<ThemeProvider>` and `<window theme>`,
8
- and the renderer never knows the package exists.
9
-
10
- One rule governs the whole page: **ntk's colour parser accepts hex, `rgb()`,
11
- `rgba()`, `hsl()` and CSS colour names, and returns null for anything else —
12
- and a null colour paints nothing, silently.** Modern token sets are
13
- increasingly `oklch()`-first, so conversion is not optional. That is the
14
- single sharpest edge here.
15
-
16
- CSS-in-JS is not on this page at all. `styled-components` and
17
- `@emotion/styled` both fail, in interestingly different ways, and are written
18
- up in the [negative-results register](../ecosystem.md#what-does-not-work).
19
- Use `createStyles` plus one of the palettes below.
5
+ [styling](../styling.md#theme-tokens) for how `$token` resolves. Every
6
+ palette package here is used the same way: pick keys at theme-build time and
7
+ hand the result to `<ThemeProvider>` and `<window theme>`.
20
8
 
21
- ## `@radix-ui/colors` {#radix-colors}
9
+ **ntk's colour parser accepts hex, `rgb()`, `rgba()`, `hsl()` and CSS colour
10
+ names, and returns null for anything else — and a null colour paints
11
+ nothing, silently.** Modern token sets are increasingly `oklch()`-first, so
12
+ convert them. That is the sharpest edge on this page.
22
13
 
23
- **Out of the box.** @radix-ui/colors@3.0.0.
14
+ CSS-in-JS (`styled-components`, `@emotion/styled`) fails — see the
15
+ [negative-results register](../ecosystem.md#what-does-not-work). Use
16
+ `createStyles` plus one of the palettes below.
24
17
 
25
- Thirty named 12-step colour scales (plus dark, alpha and P3 variants) shipped
26
- as plain JS objects of hex strings — no React, no DOM, no CSS. Each step has
27
- a documented job: 1–2 app backgrounds, 3–5 component backgrounds, 6–8
28
- borders, 9–10 solid fills, 11–12 text. That semantic contract is what makes
29
- it a theme _generator_ rather than a swatch book.
18
+ ## `@radix-ui/colors` {#radix-colors}
30
19
 
31
- Because every scale has identical structure, one function produces any
32
- light/dark pairing.
20
+ **Out of the box.** @radix-ui/colors@3.0.0.
33
21
 
34
- Radix's ladder and the token set line up almost step for step: 1–2 are the
35
- `background`, 3–5 are the `surface` and its hover, 6–8 the `border`, 9–10 the
36
- solid fills, 11–12 the ink.
22
+ Thirty named 12-step colour scales (plus dark, alpha and P3 variants) as
23
+ plain JS objects of hex strings. Each step has a documented job that lines
24
+ up with the token set almost step for step: 1–2 `background`, 3–5 `surface`
25
+ and its hover, 6–8 `border`, 9–10 solid fills, 11–12 text. Every scale has
26
+ the same structure, so one function produces any light/dark pairing.
37
27
 
38
28
  ```jsx
39
29
  import {
@@ -110,19 +100,18 @@ const dark = radixTheme({
110
100
  - `Object.values()` ordering relies on the package's insertion order
111
101
  (`blue1`…`blue12`). It holds in 3.0.0, but indexing by key
112
102
  (``accent[`${name}9`]``) is sturdier if the scale name is handy.
113
- - Nothing here names `accentText`, `hoverText` or any of the `…Text` four:
114
- each is derived from the fill it goes on as the more legible of this
115
- palette's `text` and `background`. That is what used to bite here — steps 9
116
- and 10 of yellow, lime, amber, sky and mint are light enough that a
117
- hard-coded `'#ffffff'` label fails contrast on them, and now the ratio
118
- decides. Name one to overrule it.
103
+ - Nothing here names `accentText`, `hoverText` or the other `…Text` tokens:
104
+ each is derived as the more legible of the palette's `text` and
105
+ `background` on the fill it goes on. Steps 9 and 10 of yellow, lime,
106
+ amber, sky and mint are light enough that a hard-coded `'#ffffff'` label
107
+ fails contrast; the ratio decides instead. Name one to overrule it.
119
108
  - The window needs no `backgroundColor`: with no style of its own it paints
120
- `background`, which is the ground this palette just named.
121
- - The alpha variants (`slateA`) are `#rrggbbaa`, which parses fine, but
122
- remember a translucent widget composites against the window background, not
123
- against whatever is behind the window.
124
- - The P3 variants (`blueP3`) are `color(display-p3 …)` strings. The parser
125
- returns null and nothing is painted. Stick to the sRGB exports.
109
+ `background`.
110
+ - The alpha variants (`slateA`) are `#rrggbbaa`, which parses fine, but a
111
+ translucent widget composites against the window background, not against
112
+ whatever is behind the window.
113
+ - The P3 variants (`blueP3`) are `color(display-p3 …)` strings, which the
114
+ parser rejects, so nothing paints. Stick to the sRGB exports.
126
115
 
127
116
  ## `@catppuccin/palette` {#catppuccin}
128
117
 
@@ -130,13 +119,9 @@ const dark = radixTheme({
130
119
 
131
120
  Four flavours (latte is light; frappé, macchiato and mocha are dark), each
132
121
  with 14 accents and a 12-step neutral ladder whose names _are_ UI roles —
133
- `base`/`mantle`/`crust` for backgrounds, `surface0-2`, `overlay0-2`,
134
- `subtext0-1`, `text`. Of all the palette packages this one maps most directly
135
- onto the theme shape.
136
-
137
- `flavor.dark` decides whether accent-coloured fills get dark or light text.
138
- Catppuccin accents are pastel, so on dark flavours the readable text on an
139
- accent is _dark_, not white.
122
+ `base`/`mantle`/`crust`, `surface0-2`, `overlay0-2`, `subtext0-1`, `text` —
123
+ so it maps most directly onto the theme shape. The accents are pastel, so on
124
+ dark flavours (`flavor.dark`) the readable text on an accent is _dark_.
140
125
 
141
126
  ```jsx
142
127
  import { flavors } from '@catppuccin/palette';
@@ -181,31 +166,26 @@ const mocha = catppuccinTheme(flavors.mocha);
181
166
  const latte = catppuccinTheme(flavors.latte, 'blue');
182
167
  ```
183
168
 
184
- - `accentHover` has no canonical Catppuccin answer — the palette defines flat
185
- accents, not interaction ramps. Picking a neighbouring accent, as above, is
186
- a taste call; deriving a real hover shade is a one-liner with
187
- [colord](#colord) or [culori](#culori).
188
- - The package is ESM-first with an `exports` map, so un-exported subpaths
189
- fail. The root import is all you need.
169
+ - `accentHover` has no canonical Catppuccin answer — the palette has flat
170
+ accents, not interaction ramps. A neighbouring accent, as above, is a
171
+ taste call; a real hover shade is a one-liner with [colord](#colord) or
172
+ [culori](#culori).
173
+ - ESM-first with an `exports` map, so un-exported subpaths fail; the root
174
+ import is all you need.
190
175
  - Latte's accents sit around 4:1 contrast on `base` — fine for fills,
191
- marginal for small accent-coloured _text_. Same caveat as every pastel
192
- scheme.
176
+ marginal for small accent-coloured _text_.
193
177
 
194
178
  ## `@material/material-color-utilities` {#material-color-utilities}
195
179
 
196
180
  **Out of the box from a source colour.**
197
181
  @material/material-color-utilities@0.4.0.
198
182
 
199
- Google's reference implementation of the Material 3 colour system: the HCT
200
- colour space, tonal palettes, and `themeFromSourceColor(argb)`, which turns a
201
- single seed colour into complete light and dark schemes. It is the only
202
- package here that _generates_ a coherent scheme from one input, which is
203
- exactly the shape `<window theme>` wants.
204
-
205
- M3 roles map nearly 1:1 onto the theme shape: `background → background`,
206
- `surface → surface`, `onSurface → text`, `outlineVariant → border`,
207
- `primary → accent`, `onPrimary → accentText` — and `error`, which is the one
208
- status colour M3 defines, is `danger`.
183
+ Google's Material 3 colour system: the HCT colour space, tonal palettes,
184
+ and `themeFromSourceColor(argb)`, which turns one seed colour into complete
185
+ light and dark schemes — the only package here that _generates_ a scheme.
186
+ M3 roles map nearly 1:1 onto the theme: `background`, `surface`,
187
+ `onSurface → text`, `outlineVariant → border`, `primary → accent`,
188
+ `onPrimary → accentText`, and `error` (M3's one status colour) → `danger`.
209
189
 
210
190
  ```jsx
211
191
  import {
@@ -250,35 +230,31 @@ const dark = materialTheme(m3.schemes.dark); // accent #cfbcff
250
230
  ```
251
231
 
252
232
  - **The published ESM uses extensionless relative imports**, so plain `node`
253
- fails with `ERR_MODULE_NOT_FOUND`. Under tsx — which is how react-x11 JSX
254
- runs anyway — or any bundler, it resolves fine.
255
- - `themeFromImage`/`sourceColorFromImage` call `document.createElement('canvas')`
256
- and are dead here. `sourceColorFromImageBytes(rgbaBytes)` is exported and
257
- pure: decode a PNG or JPEG yourself with pngjs or jpeg-js and hand it the
258
- RGBA array. Downscale first — the Celebi quantizer walks every opaque
259
- pixel, and it skips pixels with alpha below 255.
233
+ fails with `ERR_MODULE_NOT_FOUND`. Under tsx (how react-x11 JSX runs
234
+ anyway) or a bundler it resolves.
235
+ - `themeFromImage`/`sourceColorFromImage` call
236
+ `document.createElement('canvas')` and fail here.
237
+ `sourceColorFromImageBytes(rgbaBytes)` is pure: decode a PNG or JPEG with
238
+ pngjs or jpeg-js and pass the RGBA array. Downscale first — the quantizer
239
+ walks every opaque pixel, and skips pixels with alpha below 255.
260
240
  - `accentHover` has no M3 role. `inversePrimary` is serviceable; a tone step
261
241
  via `theme.palettes.primary.tone(n)` is more faithful.
262
- - 0.4.0 has sat unchanged for a while. The upstream repo is active but npm
263
- releases are slow — pin it.
242
+ - npm releases are slow (0.4.0 has sat unchanged for a while) — pin it.
264
243
 
265
244
  ## colord {#colord}
266
245
 
267
246
  **Out of the box.** colord@2.9.3.
268
247
 
269
- A tiny immutable colour-manipulation library: parse, `darken`/`lighten`/
270
- `alpha`/`mix`, `isDark()`, output as hex/rgb/hsl. The `a11y` plugin adds WCAG
271
- `contrast()` and `isReadable()`.
272
-
273
- The theme shape wants colours no palette ships — `accentHover`,
274
- `surfaceHover`, a `dangerHover`. colord answers each in a line, which turns
275
- a single brand colour into a complete theme without hand-tuning.
248
+ A tiny immutable colour library: parse, `darken`/`lighten`/`alpha`/`mix`,
249
+ `isDark()`, output as hex/rgb/hsl; the `a11y` plugin adds WCAG `contrast()`
250
+ and `isReadable()`. It answers the colours no palette ships — `accentHover`,
251
+ `surfaceHover`, `dangerHover` — in a line each, turning one brand colour
252
+ into a complete theme.
276
253
 
277
- The `…Text` inks are the one thing you no longer need it for: `resolveTheme`
278
- already picks the more legible of the palette's own `text` and `background`
279
- for every fill that has one. The version below is still worth reading as the
280
- explicit form — and it is what to write when the two candidates the palette
281
- happens to hold are not the two you want.
254
+ You do not need it for the `…Text` inks: `resolveTheme` already picks the
255
+ more legible of the palette's `text` and `background` for every fill. The
256
+ explicit form below is what to write when those two candidates are not the
257
+ ones you want.
282
258
 
283
259
  ```jsx
284
260
  import { colord, extend } from 'colord';
@@ -305,27 +281,24 @@ const theme = { ...baseTheme, ...accentSlice('#1f883d') };
305
281
 
306
282
  - Plugins must be `extend`-ed before use; forgetting yields
307
283
  `c.contrast is not a function` at theme-build time.
308
- - `darken()` works in HSL, which shifts saturated hues perceptibly. For a 6%
309
- hover nudge nobody notices; for accent _ramps_ that must stay on-hue, use
310
- culori's OKLCH interpolation.
311
- - Maintenance is quiet (2.9.3 since 2023) but the library is complete and
312
- dependency-free. Low risk.
284
+ - `darken()` works in HSL, which shifts saturated hues. Fine for a 6% hover
285
+ nudge; for accent _ramps_ that must stay on-hue, use culori's OKLCH
286
+ interpolation.
287
+ - Quiet (2.9.3 since 2023) but complete and dependency-free.
313
288
 
314
289
  ## culori {#culori}
315
290
 
316
291
  **Out of the box.** culori@4.0.2.
317
292
 
318
- Parses every CSS Color 4 syntax, converts between about thirty colour spaces
319
- (OKLCH and OKLab included), interpolates in any of them, builds scales, and
320
- computes WCAG contrast. Two distinct jobs here:
293
+ Parses every CSS Color 4 syntax, converts between about thirty colour
294
+ spaces (OKLCH and OKLab included), interpolates in any of them, builds
295
+ scales, and computes WCAG contrast. Two jobs here:
321
296
 
322
- 1. **Gateway for modern colour strings.** Growing amounts of design-token
323
- output — Tailwind v4, Open Props' OKLCH variants, design systems
324
- exporting CSS Color 4 — are `oklch()`-first, and ntk's parser returns null
325
- for those. `formatHex(str)` is the one-call fix.
297
+ 1. **Gateway for modern colour strings.** Tailwind v4, Open Props' OKLCH
298
+ variants and other CSS Color 4 token output are `oklch()`-first, which
299
+ ntk's parser rejects. `formatHex(str)` is the one-call fix.
326
300
  2. **Perceptual ramps.** Hover, active and disabled shades interpolated in
327
- OKLCH keep hue and perceived lightness honest where HSL manipulation
328
- drifts.
301
+ OKLCH keep hue and perceived lightness where HSL drifts.
329
302
 
330
303
  ```jsx
331
304
  import { formatHex, interpolate, wcagContrast } from 'culori';
@@ -347,9 +320,8 @@ const theme = {
347
320
  formatHex('oklch(54.6% 0.245 262.881)'); // '#155dfc'
348
321
  ```
349
322
 
350
- - It is the heavyweight of the pair. Prefer colord for "darken this a bit",
351
- culori when you need OKLCH, CSS Color 4 parsing, or scales. Using both is
352
- fine — neither touches the render path.
323
+ - The heavyweight of the pair: colord for "darken this a bit", culori for
324
+ OKLCH, CSS Color 4 parsing or scales. Using both is fine.
353
325
  - `formatHex` clips out-of-sRGB-gamut results channelwise; for scales from
354
326
  vivid P3-ish seeds, `clampChroma` first gives cleaner ramps.
355
327
  - Dual-published: ESM source plus a bundled CJS. The `/fn` and `/css`
@@ -359,20 +331,16 @@ formatHex('oklch(54.6% 0.245 262.881)'); // '#155dfc'
359
331
 
360
332
  **Out of the box for colours.** open-props@1.7.23.
361
333
 
362
- Open Props is a CSS-custom-properties design system, but it does have a JS
363
- export: the root import resolves to a flat frozen object of about 1,830
364
- entries keyed by custom-property name (`'--gray-0'`, `'--indigo-7'`), values
365
- as strings. No DOM, no dependencies.
334
+ A CSS-custom-properties design system whose root JS import is a flat frozen
335
+ object of about 1,830 entries keyed by property name (`'--gray-0'`,
336
+ `'--indigo-7'`), values as strings. Only the colour half is useful here, so
337
+ **prefer [`@radix-ui/colors`](#radix-colors)** for a palette alone (hex
338
+ throughout, documented steps, separate dark scales); Open Props fits a
339
+ project that already uses it.
366
340
 
367
- Only the colour half is worth having here, so **prefer
368
- [`@radix-ui/colors`](#radix-colors)** if all you want is a palette — it is
369
- hex throughout, has documented step semantics, and ships separate dark
370
- scales. Open Props earns its place when a project already uses it and you
371
- want the same swatches on both sides.
372
-
373
- The colour tokens — 13 hues × 13 steps — are plain hex, so building a theme
374
- is pure key-picking. The step convention is roughly: 0–2 backgrounds, 3–5
375
- borders and muted, 6–9 solids, 10–12 deep text.
341
+ The colour tokens — 13 hues × 13 steps — are plain hex, so a theme is
342
+ key-picking. Steps run roughly: 0–2 backgrounds, 3–5 borders and muted, 6–9
343
+ solids, 10–12 deep text.
376
344
 
377
345
  ```jsx
378
346
  import OP from 'open-props';
@@ -407,15 +375,13 @@ const theme = {
407
375
  };
408
376
  ```
409
377
 
410
- - **The sizes read in a style; the palette's own entries are numbers.**
411
- Open Props sizes are `rem` strings, which a style takes as written —
412
- `padding: OP['--size-3']` is `1rem` — and `rem` here is the palette's
413
- `fontSize`, which the theme above sets to Open Props' own 16, so the scale
414
- comes out as it does on the web. The palette entries are another matter:
415
- the widgets do arithmetic with `radius` and `fontSize`, so those are
416
- converted to numbers, as above. Easings are `cubic-bezier()` strings where
417
- react-x11 takes named easings, and shadows reference `var(--shadow-color)`
418
- and have no equivalent at all; ignore the animation and shadow tokens.
378
+ - **Sizes read in a style; the theme's own entries are numbers.** Open Props
379
+ sizes are `rem` strings, which a style takes as written
380
+ (`padding: OP['--size-3']` is `1rem`, and `rem` is the theme's
381
+ `fontSize` — 16 above, so the scale matches the web). The widgets do arithmetic with `radius` and `fontSize`, so
382
+ those are converted to numbers, as above. Easings are `cubic-bezier()`
383
+ strings where react-x11 takes named easings, and shadows reference
384
+ `var(--shadow-color)`; ignore the animation and shadow tokens.
419
385
  - Key names include the `--` prefix, so it is bracket access everywhere;
420
386
  camelCase aliases exist too (`OP.gray0`).
421
387
  - Unlike Radix there are no separate dark scales — a dark theme is your own
@@ -426,17 +392,14 @@ const theme = {
426
392
  **Adapter, ~3 lines — and the failure without it is silent.**
427
393
  tailwindcss@4.3.3.
428
394
 
429
- Tailwind-the-framework is a non-starter here: it emits CSS class utilities
430
- for a CSSOM, and react-x11's style channel is a validated object system. The
431
- _palette_ is a different story — 22 named scales × 11 steps, importable as
432
- plain JS from `tailwindcss/colors` without touching any of the CSS
433
- machinery, and it is the palette half your users already think in. The
434
- package has zero dependencies, so installing it purely for the colours costs
435
- about 770 KB unpacked and pulls in nothing else.
395
+ Tailwind's utility classes need a CSSOM, but the _palette_ — 22 named
396
+ scales × 11 steps — imports as plain JS from `tailwindcss/colors`. The
397
+ package has zero dependencies, so installing it for the colours costs about
398
+ 770 KB unpacked and nothing else.
436
399
 
437
- Since v4 every value is an `oklch()` string. ntk's parser returns null for
438
- those, and a null colour paints **nothing** — no error, just an invisible
439
- widget. Convert at theme-build time:
400
+ Since v4 every value is an `oklch()` string, which ntk's parser rejects, and
401
+ a null colour paints **nothing** — no error, just an invisible widget.
402
+ Convert at theme-build time:
440
403
 
441
404
  ```jsx
442
405
  import colors from 'tailwindcss/colors';
@@ -473,14 +436,11 @@ const theme = {
473
436
  };
474
437
  ```
475
438
 
476
- - **Do not pass the raw values through.** This is the sharpest edge on the
477
- page; everything else here is smooth.
478
- - `formatHex` gamut-clips, but Tailwind's vivid 500s are chosen to survive
479
- sRGB conversion, so in practice the clipped hex closely matches what
480
- Tailwind v3 shipped.
481
- - Installing a whole CSS framework for a data file is aesthetically odd; if
482
- that grates, `@radix-ui/colors` does the same job natively in hex. Zero
483
- dependencies means it is only odd, not costly.
439
+ - **Do not pass the raw values through.**
440
+ - `formatHex` gamut-clips, but Tailwind's vivid 500s survive sRGB
441
+ conversion, so the hex closely matches what Tailwind v3 shipped.
442
+ - If installing a CSS framework for a data file grates, `@radix-ui/colors`
443
+ does the same job natively in hex.
484
444
 
485
445
  ## `daltonize` {#daltonize}
486
446
 
@@ -488,17 +448,13 @@ const theme = {
488
448
  explicit colour-vision variant; for everyday contrast work
489
449
  [colord](#colord)'s `a11y` plugin is the tool.** daltonize@1.0.2.
490
450
 
491
- A single-function package: `daltonize([r, g, b], type)` where `type` is
492
- `'protanope' | 'deuteranope' | 'tritanope'`, returning a corrected triplet.
493
- It implements the classic LMS-space daltonization algorithm — simulate the
494
- deficiency, take the error against the original, redistribute it into
495
- channels the viewer _can_ see. Pure arithmetic, 9 KB, zero dependencies;
496
- despite the README's image-oriented pitch, nothing in it touches a canvas.
497
-
498
- Themes are small closed sets of colours, which makes whole-theme
499
- daltonization trivial: map the correction over every value and offer it as an
500
- accessibility variant next to light and dark. The adapter is only hex ↔
501
- triplet plumbing.
451
+ One function: `daltonize([r, g, b], type)`, `type` being
452
+ `'protanope' | 'deuteranope' | 'tritanope'`, returns a corrected triplet
453
+ (classic LMS-space daltonization). Pure arithmetic, 9 KB, zero dependencies;
454
+ despite the README's image pitch, nothing touches a canvas. A theme is a
455
+ small closed set of colours, so map the correction over every value and
456
+ offer the result as an accessibility variant beside light and dark. The
457
+ adapter is hex ↔ triplet plumbing.
502
458
 
503
459
  ```jsx
504
460
  import { daltonize } from 'daltonize';
@@ -526,15 +482,13 @@ const accessible = daltonizeTheme(githubLight);
526
482
  // '#1f883d' (GitHub green) -> '#1f7206' for deuteranopia
527
483
  ```
528
484
 
529
- - **Correction, not validation.** It shifts colours so information carried by
530
- problem hues survives; it does not check or fix contrast. A low-contrast
531
- theme stays low-contrast. Pair it with colord's `a11y` plugin or culori's
532
- `wcagContrast`.
533
- - Only the three dichromatic extremes are modelled — anomalous trichromacy,
534
- which is the common mild case, is not. Corrections can look aggressive to a
535
- mildly affected viewer, so offer it as an opt-in variant rather than a
536
- default.
537
- - Handles opaque triplets only. Run it on the RGB part and reattach alpha
538
- yourself if the theme uses `#rrggbbaa`.
539
- - The package is small and finished rather than maintained (last release 2023) and its `repository` field is a placeholder. Vendoring the ~40 lines
540
- is a fair alternative to the dependency.
485
+ - **Correction, not validation.** It shifts colours so information carried
486
+ by problem hues survives; it does not check or fix contrast. Pair it with
487
+ colord's `a11y` plugin or culori's `wcagContrast`.
488
+ - Only the three dichromatic extremes are modelled, not the common mild
489
+ anomalous trichromacy, so corrections can look aggressive; offer it as an
490
+ opt-in variant, not a default.
491
+ - Opaque triplets only: run it on the RGB part and reattach alpha yourself
492
+ for `#rrggbbaa`.
493
+ - Finished rather than maintained (last release 2023, placeholder
494
+ `repository` field); vendoring the ~40 lines is a fair alternative.