hextimator 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/CHANGELOG.md ADDED
@@ -0,0 +1,2 @@
1
+ ## 0.1.0
2
+ - initial release
package/LICENSE.md ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (c) 2026 Contributors
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,137 @@
1
+ # hextimator
2
+
3
+ <p align="center">
4
+ <picture>
5
+ <img src="https://github.com/fgrgic/hextimator/blob/main/packages/hextimator/docs/assets/gh-cover.webp?raw=true" alt="hextimator" width="500">
6
+ </picture>
7
+ </p>
8
+
9
+ One brand color in, full accessible theme out.
10
+
11
+ - Input: any single color. Output: complete light + dark theme with accessibility guarantees (AAA contrast by default).
12
+ - Works at runtime — built for multi-tenant apps that need per-brand themes on the fly.
13
+ - Perceptually uniform (OKLCH) — blue and yellow palettes look equally balanced, unlike HSL-based generators.
14
+
15
+ ## Installation
16
+
17
+ Add to your project:
18
+
19
+ ```bash
20
+ npm i hextimator
21
+ ```
22
+
23
+ Or quickly get a one-off theme:
24
+
25
+ ```bash
26
+ npx hextimator "#ff6677"
27
+ ```
28
+
29
+ ## Quick start
30
+
31
+ ```typescript
32
+ import { hextimate } from "hextimator";
33
+
34
+ const theme = hextimate("#6A5ACD").format();
35
+ ```
36
+
37
+ ### With a preset
38
+
39
+ Presets configure hextimator for a specific framework in one call:
40
+
41
+ ```typescript
42
+ import { hextimate, presets } from "hextimator";
43
+
44
+ // shadcn/ui — generates --background, --primary, --destructive, etc.
45
+ const theme = hextimate("#6366F1")
46
+ .preset(presets.shadcn)
47
+ .format();
48
+ ```
49
+
50
+ Presets set sensible defaults but you can override anything in `.format()`:
51
+
52
+ ```typescript
53
+ const theme = hextimate("#6366F1")
54
+ .preset(presets.shadcn)
55
+ .format({ colors: "hsl-raw" }); // older shadcn format
56
+ ```
57
+
58
+ See [Presets](https://github.com/fgrgic/hextimator/blob/main/packages/hextimator/docs/presets.md) for the full list and how to create your own.
59
+
60
+ ## Two-step API: generate, then format
61
+
62
+ hextimator separates **palette generation** (color math) from **formatting** (output shape). This lets you extend the palette before choosing how to serialize it.
63
+
64
+ ```typescript
65
+ const theme = hextimate("#6A5ACD")
66
+ .format({ as: "css", colors: "oklch" });
67
+ ```
68
+
69
+ ### Output formats
70
+
71
+ All formats return `{ light: { ... }, dark: { ... } }`.
72
+
73
+ - **`"object"`** (default) — plain keys: `accent`, `accent-strong`, …
74
+ - **`"css"`** — CSS custom properties: `--accent`, `--accent-strong`, …
75
+ - **`"tailwind"`** — nested tokens: `{ accent: { DEFAULT, strong, … } }`
76
+ - **`"tailwind-css"`** — `@theme` block with `--color-accent`, `--color-accent-strong`, …
77
+ - **`"scss"`** — SCSS variables: `$accent`, `$accent-strong`, …
78
+ - **`"json"`** — JSON string of the plain object
79
+
80
+ ### Color value formats
81
+
82
+ | `colors` | Example output |
83
+ |---|---|
84
+ | `"hex"` (default) | `"#6a5acd"` |
85
+ | `"oklch"` | `"oklch(0.54 0.18 276)"` |
86
+ | `"oklch-raw"` | `"0.54 0.18 276"` |
87
+ | `"rgb"` | `"rgb(106, 90, 205)"` |
88
+ | `"rgb-raw"` | `"106 90 205"` |
89
+ | `"hsl"` | `"hsl(248, 53%, 58%)"` |
90
+ | `"hsl-raw"` | `"248 53% 58%"` |
91
+ | `"p3"` | `"color(display-p3 0.39 0.34 0.79)"` |
92
+ | `"p3-raw"` | `"0.39 0.34 0.79"` |
93
+
94
+ ### Flexible input
95
+
96
+ ```typescript
97
+ hextimate("#FF6666"); // hex string
98
+ hextimate("rgb(255, 102, 102)"); // CSS function
99
+ hextimate([255, 102, 102]); // RGB tuple
100
+ hextimate(0xff6666); // numeric hex
101
+ ```
102
+
103
+ > **Note on alpha**: Alpha values are intentionally ignored — `rgba(255, 0, 0, 0.5)` is treated as fully opaque `rgb(255, 0, 0)`. Alpha tokens undermine accessibility guarantees because contrast ratios depend on the background, which hextimator does not control.
104
+
105
+ ## How it works
106
+
107
+ 1. **Parse** any color input into a normalized `Color` object.
108
+ 2. **Convert** to OKLCH (perceptual color space) so lightness and chroma adjustments look uniform across hues.
109
+ 3. **Generate** accent, base, and semantic color scales — each with DEFAULT, strong, weak, and foreground variants.
110
+ 4. **Gamut-map** back to sRGB via binary-search chroma reduction (preserves lightness and hue).
111
+ 5. **Format** into your chosen output (CSS vars, Tailwind, SCSS, JSON, or plain object).
112
+
113
+ ## Utilities
114
+
115
+ hextimator also exports its parsing and conversion functions for standalone use:
116
+
117
+ ```typescript
118
+ import { parseColor, convertColor } from "hextimator";
119
+
120
+ const color = parseColor("rgb(255, 102, 102)");
121
+ const oklch = convertColor(color, "oklch");
122
+ ```
123
+
124
+ ## Documentation
125
+
126
+ - [Extending the palette](https://github.com/fgrgic/hextimator/blob/main/packages/hextimator/docs/extending-the-palette.md) — `addRole`, `addVariant`, `addToken`
127
+ - [Presets](https://github.com/fgrgic/hextimator/blob/main/packages/hextimator/docs/presets.md) — drop-in configs for shadcn/ui, or create your own
128
+ - [Multiple themes](https://github.com/fgrgic/hextimator/blob/main/packages/hextimator/docs/multiple-themes.md) — dynamic theming and `.fork()`
129
+ - [Customization](https://github.com/fgrgic/hextimator/blob/main/packages/hextimator/docs/customization.md) — generation and format options reference
130
+ - [Color vision deficiency](https://github.com/fgrgic/hextimator/blob/main/packages/hextimator/docs/color-vision-deficiency.md) — simulate and adapt for CVD
131
+ - [React](https://github.com/fgrgic/hextimator/blob/main/packages/hextimator/docs/react.md) — `useHextimator` hook, provider, dark mode strategies
132
+ - [Tailwind CSS v4](https://github.com/fgrgic/hextimator/blob/main/packages/hextimator/docs/tailwind.md) — setup and usage with Tailwind
133
+ - [Real-world examples](https://github.com/fgrgic/hextimator/blob/main/packages/hextimator/docs/examples.md) — shadcn/ui, Stripe, Slack configurations
134
+
135
+ ## Contributing
136
+
137
+ Issues and PRs are welcome at [github.com/fgrgic/hextimator](https://github.com/fgrgic/hextimator/issues).
@@ -0,0 +1,540 @@
1
+ /** A color in the sRGB color space. */
2
+ interface RGB {
3
+ readonly space: 'srgb';
4
+ readonly r: number;
5
+ readonly g: number;
6
+ readonly b: number;
7
+ readonly alpha: number;
8
+ }
9
+ /** A color in the HSL color space. */
10
+ interface HSL {
11
+ readonly space: 'hsl';
12
+ readonly h: number;
13
+ readonly s: number;
14
+ readonly l: number;
15
+ readonly alpha: number;
16
+ }
17
+ /** A color in the OKLCH color space. */
18
+ interface OKLCH {
19
+ readonly space: 'oklch';
20
+ readonly l: number;
21
+ readonly c: number;
22
+ readonly h: number;
23
+ readonly alpha: number;
24
+ }
25
+ /** A color in the OKLab color space. */
26
+ interface OKLab {
27
+ readonly space: 'oklab';
28
+ readonly l: number;
29
+ readonly a: number;
30
+ readonly b: number;
31
+ readonly alpha: number;
32
+ }
33
+ /** A color in the linear RGB color space. */
34
+ interface LinearRGB {
35
+ readonly space: 'linear-rgb';
36
+ readonly r: number;
37
+ readonly g: number;
38
+ readonly b: number;
39
+ readonly alpha: number;
40
+ }
41
+ /** A color in the Display P3 color space (wide gamut). */
42
+ interface DisplayP3 {
43
+ readonly space: 'display-p3';
44
+ readonly r: number;
45
+ readonly g: number;
46
+ readonly b: number;
47
+ readonly alpha: number;
48
+ }
49
+ /** A color in any supported color space. */
50
+ type Color = RGB | HSL | OKLCH | OKLab | LinearRGB | DisplayP3;
51
+ /** A color in a specific color space, strongly typed. */
52
+ type ColorInSpace<S extends Color['space']> = Extract<Color, {
53
+ space: S;
54
+ }>;
55
+ /** The name of a color space, e.g. "srgb", "hsl", "oklch". */
56
+ type ColorSpace = Color['space'];
57
+ /** "FF6666", "#FF6666", "0xFF6666", "#F66" with optional alpha. */
58
+ type HexString = string;
59
+ /** e.g. `rgb(255, 102, 102)`, `rgba(255, 102, 102, 0.5)`. */
60
+ type CSSColorString = string;
61
+ /** Loose tuple: [255, 102, 102], [255, 102, 102, 0.5] */
62
+ type ColorTuple = readonly [number, number, number, number?];
63
+ /**
64
+ * Any supported color input: hex string, CSS function string, RGB tuple, numeric hex, or a parsed `Color` object.
65
+ *
66
+ * @example
67
+ * "#FF6666"
68
+ * "rgb(255, 102, 102)"
69
+ * [255, 102, 102]
70
+ * 0xFF6666
71
+ */
72
+ type ColorInput = HexString | CSSColorString | ColorTuple | Color | number;
73
+ /**
74
+ * Per-theme adjustments for lightness and chroma.
75
+ */
76
+ interface ThemeAdjustments {
77
+ /**
78
+ * Absolute OKLCH lightness for this theme (0–1).
79
+ *
80
+ * Default: 0.7 for light, 0.6 for dark.
81
+ */
82
+ lightness?: number;
83
+ /**
84
+ * Maximum chroma for accent/semantic colors in this theme.
85
+ * Colors with higher chroma will be clamped to this value.
86
+ */
87
+ maxChroma?: number;
88
+ /**
89
+ * Minimum WCAG contrast ratio for this theme.
90
+ * Overrides the global `minContrastRatio` for this theme only.
91
+ *
92
+ * - `"AAA"` → 7
93
+ * - `"AA"` → 4.5
94
+ * - any number → exact ratio
95
+ */
96
+ minContrastRatio?: 'AAA' | 'AA' | number;
97
+ /**
98
+ * Maximum chroma for the baseline colors (base, strong, weak) in this theme.
99
+ * Overrides the global `baseMaxChroma` for this theme only.
100
+ */
101
+ baseMaxChroma?: number;
102
+ /**
103
+ * Maximum chroma for foreground colors in this theme.
104
+ * Overrides the global `foregroundMaxChroma` for this theme only.
105
+ */
106
+ foregroundMaxChroma?: number;
107
+ }
108
+ /**
109
+ * Options that affect color generation (the math)
110
+ */
111
+ interface HextimateGenerationOptions {
112
+ /**
113
+ * Preferred base color for dark and light mode.
114
+ * It will be used as a baseline to generate the rest of base colors (strong, weak).
115
+ * If not provided, it will be derived from the main input color with very low chroma.
116
+ *
117
+ * Takes precedence over `baseHueShift` when both are set.
118
+ */
119
+ baseColor?: ColorInput;
120
+ /**
121
+ * Rotate the base color's hue relative to the accent color (in degrees).
122
+ *
123
+ * Examples: 180 for complementary, 30 for analogous, -30 for the other direction.
124
+ *
125
+ * Ignored when an explicit `baseColor` is provided.
126
+ *
127
+ * Default: 0 (same hue as the accent).
128
+ */
129
+ baseHueShift?: number;
130
+ /**
131
+ * Semantic colors to use for the theme
132
+ * If not provided, they will be generated from the provided main color,
133
+ * and the semantic color ranges.
134
+ */
135
+ semanticColors?: {
136
+ positive?: ColorInput;
137
+ negative?: ColorInput;
138
+ warning?: ColorInput;
139
+ };
140
+ /**
141
+ * Invert the hue used for the accent color in dark mode.
142
+ * Uses base hue as accent, and accent hue as base.
143
+ * Only has effect if `baseColor` is provided alongside the main accent color
144
+ *
145
+ * Default: false.
146
+ */
147
+ invertDarkModeBaseAccent?: boolean;
148
+ /**
149
+ * Degree ranges for the semantic colors.
150
+ * Determines where to look for "green", "red", "yellow" in the color space.
151
+ * If not provided, the default ranges will be used:
152
+ * - positive: [90, 150] greens
153
+ * - negative: [345, 15] reds
154
+ * - warning: [35, 55] ambers
155
+ */
156
+ semanticColorRanges?: {
157
+ positive?: [number, number];
158
+ negative?: [number, number];
159
+ warning?: [number, number];
160
+ };
161
+ /**
162
+ * Maximum chroma for the baseline colors (base, strong, weak).
163
+ * Higher values will produce more colorful baseline colors, lower values will produce more gray baseline colors.
164
+ *
165
+ * Default: 0.01.
166
+ */
167
+ baseMaxChroma?: number;
168
+ /**
169
+ * Maximum chroma for all the foreground colors (e.g. base-accent-foreground)
170
+ * Higher values will produce more colorful foreground colors, lower values will produce more gray foreground colors.
171
+ *
172
+ * Default: 0.01.
173
+ */
174
+ foregroundMaxChroma?: number;
175
+ /**
176
+ * Per-theme adjustments for the light theme.
177
+ */
178
+ light?: ThemeAdjustments;
179
+ /**
180
+ * Per-theme adjustments for the dark theme.
181
+ */
182
+ dark?: ThemeAdjustments;
183
+ /**
184
+ * Minimum WCAG contrast ratio between non-foreground variants and the
185
+ * foreground variant.
186
+ *
187
+ * - `"AAA"` (default) → 7
188
+ * - `"AA"` → 4.5
189
+ * - any number → exact ratio (e.g. 3 for large text)
190
+ */
191
+ minContrastRatio?: 'AAA' | 'AA' | number;
192
+ /**
193
+ * Shift the hue (in degrees) from variant to variant.
194
+ *
195
+ * - Positive value: strong-side variants shift toward higher hues,
196
+ * weak-side variants shift toward lower hues.
197
+ * - Negative value: flips the direction.
198
+ * - Each successive variant on a side shifts by an additional step
199
+ * (e.g. hueShift: 5 → strong +5°, stronger +10°, weak −5°, weaker −10°).
200
+ *
201
+ * Clamped to `360 / (totalVariants + 1)` so the palette never wraps
202
+ * past a full rotation.
203
+ *
204
+ * Default: 0 (no hue shift).
205
+ */
206
+ hueShift?: number;
207
+ }
208
+ /**
209
+ * Options that affect output formatting (serialization)
210
+ */
211
+ interface HextimateFormatOptions {
212
+ /**
213
+ * Rename roles in the output token keys.
214
+ * Internal name → your custom name.
215
+ *
216
+ * Examples:
217
+ * - base: "bg"
218
+ * - accent: "button"
219
+ * - positive: "success"
220
+ * - negative: "error"
221
+ * - warning: "warning"
222
+ *
223
+ * If not provided, the default role names will be used.
224
+ * The default role names are:
225
+ * - base: "base"
226
+ * - accent: "accent"
227
+ * - positive: "positive"
228
+ * - negative: "negative"
229
+ * - warning: "warning"
230
+ */
231
+ roleNames?: Record<string, string>;
232
+ /**
233
+ * Rename variant suffixes in the output token keys.
234
+ * Internal name → your custom name.
235
+ *
236
+ * Examples:
237
+ * - DEFAULT: "secondary"
238
+ * - strong: "primary"
239
+ * - weak: "tertiary"
240
+ * - foreground: "text"
241
+ */
242
+ variantNames?: Record<string, string>;
243
+ /**
244
+ * Separator to use between the role and the variant in the output token keys.
245
+ * If not provided, the default separator will be used.
246
+ * The default separator is: "-"
247
+ *
248
+ * Use "_" for "base_strong", "/" for "base/strong", etc.
249
+ */
250
+ separator?: string;
251
+ /**
252
+ * Output format.
253
+ * - "object" (default): { base: "#f2eee8", "base-strong": "#d4cfc8", ...}
254
+ * - "css": { "--base": "#f2eee8", "--base-strong": "#d4cfc8", ...}
255
+ * - "tailwind": { base: { DEFAULT: "#f2eee8", strong: "#d4cfc8", weak: "#faf8f6" } }
256
+ * - "scss": { $base: "#f2eee8", $base-strong: "#d4cfc8", ...}
257
+ * - "json": '{ "base": "#f2eee8", "base-strong": "#d4cfc8", ...}'
258
+ * - "tailwind-css": '@theme { --color-base: #f2eee8; --color-base-strong: #d4cfc8; ... }'
259
+ */
260
+ as?: 'object' | 'css' | 'tailwind' | 'tailwind-css' | 'scss' | 'json';
261
+ /**
262
+ * How color values are serialized in the output.
263
+ *
264
+ * - "hex" (default) → "#f2eee8"
265
+ * - "hsl" → "hsl(30, 10%, 94%)"
266
+ * - "hsl-raw" → "30 10% 94%" (shadcn / CSS variable style)
267
+ * - "oklch" → "oklch(0.96 0.01 70)"
268
+ * - "oklch-raw" → "0.96 0.01 70"
269
+ * - "p3" → "color(display-p3 0.94 0.93 0.91)" (wide gamut)
270
+ * - "p3-raw" → "0.94 0.93 0.91"
271
+ * - "rgb" → "rgb(242, 238, 232)"
272
+ * - "rgb-raw" → "242 238 232"
273
+ */
274
+ colors?: ColorFormat;
275
+ }
276
+ /**
277
+ * Combined options for the convenience API.
278
+ * Allows passing both generation and format options in one call.
279
+ */
280
+ type HextimateOptions = HextimateGenerationOptions & HextimateFormatOptions;
281
+ /** How color values are serialized in the output (e.g. "hex", "rgb", "oklch", "hsl", "p3", and their "-raw" variants). */
282
+ type ColorFormat = 'hex' | 'hsl' | 'hsl-raw' | 'oklch' | 'oklch-raw' | 'p3' | 'p3-raw' | 'rgb' | 'rgb-raw';
283
+
284
+ type CVDType = 'protanopia' | 'deuteranopia' | 'tritanopia' | 'achromatopsia';
285
+
286
+ type FlatTokenMap = Record<string, string>;
287
+ type NestedTokenMap = Record<string, Record<string, string>>;
288
+ type FormatResult = FlatTokenMap | NestedTokenMap | string;
289
+
290
+ /**
291
+ * A reusable preset that configures the palette builder with
292
+ * framework-specific roles, tokens, and format defaults.
293
+ *
294
+ * Presets are data-only objects — no circular dependencies, fully serializable.
295
+ *
296
+ * @example
297
+ * import { hextimate, presets } from 'hextimator';
298
+ *
299
+ * const theme = hextimate('#6366F1')
300
+ * .preset(presets.shadcn)
301
+ * .format();
302
+ */
303
+ interface HextimatePreset {
304
+ /** Generation options (contrast, hue shifts, lightness, chroma). Applied before roles/variants/tokens. */
305
+ generation?: HextimateGenerationOptions;
306
+ /** Extra roles to add to the palette (each generates DEFAULT, strong, weak, foreground variants). */
307
+ roles?: Array<{
308
+ name: string;
309
+ color: ColorInput | DerivedToken;
310
+ }>;
311
+ /** Extra variants to add across all roles. */
312
+ variants?: Array<{
313
+ name: string;
314
+ placement: VariantPlacement;
315
+ }>;
316
+ /** Standalone tokens derived from existing palette values or explicit colors. */
317
+ tokens?: Array<{
318
+ name: string;
319
+ value: TokenValue;
320
+ }>;
321
+ /** Default format options. Can be overridden in `.format()`. */
322
+ format?: HextimateFormatOptions;
323
+ }
324
+
325
+ /** The result of formatting a palette, containing both light and dark theme tokens. */
326
+ interface HextimateResult<F = FormatResult> {
327
+ light: F;
328
+ dark: F;
329
+ }
330
+ /**
331
+ * Where to place a new variant relative to existing ones.
332
+ * - `{ from: "strong" }` — one step past the named variant (redistributes to respect contrast)
333
+ * - `{ from: "weak", chroma: -0.02 }` — one step past weak, with a chroma offset
334
+ * - `{ between: ["DEFAULT", "weak"] }` — midpoint between two variants
335
+ */
336
+ type VariantPlacement = {
337
+ from: string;
338
+ emphasis?: number;
339
+ chroma?: number;
340
+ hue?: number;
341
+ } | {
342
+ between: [string, string];
343
+ };
344
+ /**
345
+ * A token derived from an existing role+variant, with optional offsets.
346
+ *
347
+ * `emphasis` is theme-aware: positive = more contrast with background,
348
+ * negative = softer/closer to background. It flips direction automatically
349
+ * between light and dark themes, so you never need per-theme splits for
350
+ * simple contrast adjustments.
351
+ *
352
+ * @example
353
+ * { from: "base.weak", lightness: -0.05 }
354
+ * { from: "accent", hue: -20 }
355
+ * { from: "base", emphasis: 0.12 }
356
+ * { from: "base.foreground", emphasis: -0.2 }
357
+ */
358
+ interface DerivedToken {
359
+ from: string;
360
+ emphasis?: number;
361
+ lightness?: number;
362
+ chroma?: number;
363
+ hue?: number;
364
+ }
365
+ /**
366
+ * Value for a standalone token: a raw color, a derived reference, or per-theme values.
367
+ *
368
+ * @example
369
+ * "#FF6600"
370
+ * { from: "accent", lightness: +0.1 }
371
+ * { light: { from: "base.weak", lightness: -0.05 }, dark: { from: "base.weak", lightness: +0.05 } }
372
+ */
373
+ type TokenValue = ColorInput | DerivedToken | {
374
+ light: DerivedToken | ColorInput;
375
+ dark: DerivedToken | ColorInput;
376
+ };
377
+ /**
378
+ * Palette builder.
379
+ *
380
+ * Supports adding roles, variants, and standalone tokens, as well as simulating and adapting for CVD.
381
+ * All operations are recorded and replayed in order on fork, ensuring consistent results.
382
+ * The internal palette is stored in a raw form with OKLCH color objects, allowing for precise adjustments and transformations.
383
+ */
384
+ declare class HextimatePaletteBuilder {
385
+ private lightPalette;
386
+ private darkPalette;
387
+ private readonly inputColor;
388
+ private readonly options;
389
+ private readonly operations;
390
+ private readonly standaloneTokens;
391
+ private readonly weakSideVariants;
392
+ private readonly strongSideVariants;
393
+ private readonly betweenVariants;
394
+ private presetFormatDefaults?;
395
+ constructor(color: Color, options?: HextimateGenerationOptions);
396
+ /**
397
+ * Adds a custom role with given name and color.
398
+ * The color is expanded into a full scale and added to both light and dark palettes.
399
+ *
400
+ * The color can be a direct color value or a derived token referencing an existing role.
401
+ *
402
+ * e.g. `addRole('cta', '#ff0066')` adds a "cta" role with the specified hue as the base.
403
+ * `addRole('cta', { from: 'accent', hue: 180 })` adds a "cta" role with a complementary hue to accent.
404
+ *
405
+ * @param name Role name (e.g. "cta", "banner")
406
+ * @param color Base color or derived token for the role
407
+ */
408
+ addRole(name: string, color: ColorInput | DerivedToken): this;
409
+ /**
410
+ * Adds a variant to all roles, derived from an existing variant or placed between two variants.
411
+ *
412
+ * e.g. `addVariant('placeholder', { from: 'weak' })` adds a "placeholder" variant one step past "weak" across all roles and themes.
413
+ * `addVariant('highlight', { between: ['DEFAULT', 'strong'] })` adds a "highlight" variant that is exactly between "DEFAULT" and "strong" across all roles and themes.
414
+ *
415
+ * @param name Variant name (e.g. "placeholder", "highlight")
416
+ * @param placement Placement of the variant, either `{ from: 'weak' }` or `{ between: ['DEFAULT', 'strong'] }`
417
+ */
418
+ addVariant(name: string, placement: VariantPlacement): this;
419
+ /**
420
+ * Adds a standalone/one-off token that doesn't fit the role+variant structure.
421
+ * The value can be a direct color, a derived token based on an existing role+variant, or an object specifying different values for light and dark themes.
422
+ *
423
+ * e.g. `addToken('brand', '#3a86ff')` adds a "brand" token with the specified color in both themes.
424
+ * `addToken('brand', { light: '#3a86ff', dark: '#ff0066' })` adds a "brand" token with different colors in light and dark themes.
425
+ *
426
+ * It can also be used to override specific tokens after generation.
427
+ * `addToken('base-strong', '#ff0066')` overrides the automatically generated "base-strong" variant with a custom color.
428
+ *
429
+ * @param name Token name (e.g. "brand", "logo")
430
+ * @param value Token value, which can be an exact color, or derived from an existing role+variant.
431
+ */
432
+ addToken(name: string, value: TokenValue): this;
433
+ /**
434
+ * Applies a preset that configures roles, tokens, and format defaults
435
+ * for a specific framework or convention (e.g. shadcn/ui).
436
+ *
437
+ * Preset format defaults are used as a base in `.format()` — any options
438
+ * you pass to `.format()` will override the preset's defaults.
439
+ *
440
+ * @example
441
+ * import { hextimate, presets } from 'hextimator';
442
+ *
443
+ * const theme = hextimate('#6366F1')
444
+ * .preset(presets.shadcn)
445
+ * .format();
446
+ *
447
+ * // Override preset's color format:
448
+ * const theme = hextimate('#6366F1')
449
+ * .preset(presets.shadcn)
450
+ * .format({ colors: 'hsl-raw' });
451
+ */
452
+ preset(preset: HextimatePreset): this;
453
+ /**
454
+ *
455
+ * Simulates how the palette would look for a given type and severity of CVD.
456
+ * This is a destructive operation that permanently alters the palette, but can be useful for testing and previewing.
457
+ *
458
+ * It should not be used to generate the final output for users with CVD. For that, use `adaptFor` instead.
459
+ *
460
+ * e.g. `simulate('deuteranopia', 0.5)` simulates moderate deuteranopia, allowing you to see how the colors would appear to users with that condition.
461
+ * @param type Type of CVD to simulate (e.g. "protanopia", "deuteranopia", "tritanopia")
462
+ * @param severity Severity of the CVD simulation, from 0 (no effect) to 1 (full simulation). Defaults to 1 for a complete simulation.
463
+ */
464
+ simulate(type: CVDType, severity?: number): this;
465
+ /**
466
+ * Adapts the palette for a given type and severity of CVD,
467
+ * altering the colors to improve accessibility while maintaining as much of the original intent as possible.
468
+ *
469
+ * To preview how the original palette would appear to users with CVD, use `simulate`.
470
+ *
471
+ * A typical use case would be to generate a normal palette, then fork it and adapt the fork for CVD.
472
+ * Then you can also preview by chaining `adaptFor` and `simulate`
473
+ *
474
+ * e.g.
475
+ * ```ts
476
+ * const normalTheme = hextimate('#ff6600');
477
+ * const cvdTheme = normalTheme.fork().adaptFor('deuteranopia');
478
+ * const simulatedCVD = normalTheme.fork().simulate('deuteranopia');
479
+ * ````
480
+ *
481
+ * @param type Type of CVD to adapt for (e.g. "protanopia", "deuteranopia", "tritanopia")
482
+ * @param severity Severity of the CVD adaptation, from 0 (no change) to 1 (full adaptation). Defaults to 1 for a complete adaptation.
483
+ */
484
+ adaptFor(type: CVDType, severity?: number): this;
485
+ /**
486
+ * Creates a new builder instance with the same operations history, allowing you to generate a related palette with a different base color or options.
487
+ *
488
+ * e.g. `fork('#ff6677')` creates a new builder with the same
489
+ * roles, variants, tokens, and adjustments, but based on a different input color.
490
+ *
491
+ * `fork({ light: { lightness: 0.8 } })` creates a new builder with the same color but different light theme adjustments.
492
+ *
493
+ * @param colorOrOptions Either a new base color for the palette, or an object with new generation options to override (e.g. light/dark adjustments, hue shift, contrast ratio).
494
+ * @param maybeOptions If the first argument is a color, this can be an optional second argument with generation options to override.
495
+ * @returns A new builder instance with the same operations history but different base color and/or options.
496
+ */
497
+ fork(colorOrOptions?: ColorInput | Partial<HextimateGenerationOptions>, maybeOptions?: Partial<HextimateGenerationOptions>): HextimatePaletteBuilder;
498
+ /**
499
+ * Serializes the palette into the chosen output format.
500
+ *
501
+ * When a preset has been applied, its format options are used as defaults.
502
+ * Any options passed here override the preset's defaults.
503
+ *
504
+ * @param options Format options controlling output shape (`as`), color serialization (`colors`), role/variant renaming, and separator.
505
+ */
506
+ format(options: HextimateFormatOptions & {
507
+ as: 'tailwind';
508
+ }): HextimateResult<NestedTokenMap>;
509
+ format(options: HextimateFormatOptions & {
510
+ as: 'json' | 'tailwind-css';
511
+ }): HextimateResult<string>;
512
+ format(options: HextimateFormatOptions & {
513
+ as: 'object' | 'css' | 'scss';
514
+ }): HextimateResult<FlatTokenMap>;
515
+ format(options?: HextimateFormatOptions): HextimateResult;
516
+ private applyRole;
517
+ private applyVariant;
518
+ private applyToken;
519
+ private resolveStandaloneTokens;
520
+ private resolveTokenValue;
521
+ private resolveTokenSingle;
522
+ private isDerivedToken;
523
+ private resolveDerivedToken;
524
+ private resolveTokenValueWithChain;
525
+ private resolveTokenSingleWithChain;
526
+ private getSideVariantsFor;
527
+ private redistributeAllScales;
528
+ /**
529
+ * Distributes variants evenly between DEFAULT and the lightness boundary.
530
+ * Uses (i+1)/(n+1) spacing so variants never sit exactly at the boundary,
531
+ * preserving AAA contrast margin.
532
+ */
533
+ private redistributeVariants;
534
+ private recomputeBetweenVariants;
535
+ private computeBeyondVariant;
536
+ private computeBetweenVariant;
537
+ private resolvedOptions;
538
+ }
539
+
540
+ export { type Color as C, type DerivedToken as D, type FlatTokenMap as F, type HextimatePreset as H, type NestedTokenMap as N, type OKLCH as O, type ThemeAdjustments as T, type VariantPlacement as V, type CVDType as a, type ColorSpace as b, type ColorInSpace as c, type ColorInput as d, type HextimateGenerationOptions as e, HextimatePaletteBuilder as f, type FormatResult as g, type HextimateFormatOptions as h, type HextimateOptions as i, type HextimateResult as j, type TokenValue as k };