@taprootio/espalier 3.7.0 → 3.8.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 CHANGED
@@ -1,3 +1,18 @@
1
+ # 3.8.0
2
+
3
+ One-off semantic lightness without moving a shared ramp stop.
4
+
5
+ - Themes and contexts can declare named `tones`; an explicit semantic mapping
6
+ references one as `tone:<name>` and keeps the ordinary chroma, gamut, APCA,
7
+ and fit-report pipeline.
8
+ - Tones are deliberately isolated: they do not emit `--esp-l-*`, join the
9
+ eleven-stop ramp, or influence roles and automatic action placement. A
10
+ context can override a root tone for its own semantic table.
11
+ - Validation rejects malformed or missing tone references and now catches
12
+ unknown built-in lightness keys instead of silently discarding them.
13
+ - The theming and lightness-ramp guides show when to move a shared stop and
14
+ when to use a custom tone for a recessed, pressed, or hovered ground.
15
+
1
16
  # 3.7.0
2
17
 
3
18
  Subtle category color behind ordinary copy.
@@ -919,6 +919,22 @@
919
919
  "module": "./shared/theme.js"
920
920
  }
921
921
  },
922
+ {
923
+ "kind": "js",
924
+ "name": "LightnessKey",
925
+ "declaration": {
926
+ "name": "LightnessKey",
927
+ "module": "./shared/theme.js"
928
+ }
929
+ },
930
+ {
931
+ "kind": "js",
932
+ "name": "LightnessReference",
933
+ "declaration": {
934
+ "name": "LightnessReference",
935
+ "module": "./shared/theme.js"
936
+ }
937
+ },
922
938
  {
923
939
  "kind": "js",
924
940
  "name": "PartialTheme",
@@ -935,6 +951,14 @@
935
951
  "module": "./shared/theme.js"
936
952
  }
937
953
  },
954
+ {
955
+ "kind": "js",
956
+ "name": "PartialThemeTones",
957
+ "declaration": {
958
+ "name": "PartialThemeTones",
959
+ "module": "./shared/theme.js"
960
+ }
961
+ },
938
962
  {
939
963
  "kind": "js",
940
964
  "name": "ThemeContext",
@@ -951,6 +975,22 @@
951
975
  "module": "./shared/theme.js"
952
976
  }
953
977
  },
978
+ {
979
+ "kind": "js",
980
+ "name": "ThemeTones",
981
+ "declaration": {
982
+ "name": "ThemeTones",
983
+ "module": "./shared/theme.js"
984
+ }
985
+ },
986
+ {
987
+ "kind": "js",
988
+ "name": "ToneReference",
989
+ "declaration": {
990
+ "name": "ToneReference",
991
+ "module": "./shared/theme.js"
992
+ }
993
+ },
954
994
  {
955
995
  "kind": "js",
956
996
  "name": "VariantColorSource",
@@ -73988,6 +74028,72 @@
73988
74028
  "default": "[ \"surface\", \"raised1\", \"raised2\", \"raised3\", \"raised4\", \"accent\", \"muted\", \"text\", \"border\", \"ink\", \"shadow\", ]",
73989
74029
  "description": "Valid keys for the lightness ramp."
73990
74030
  },
74031
+ {
74032
+ "kind": "function",
74033
+ "name": "toneReferenceName",
74034
+ "return": {
74035
+ "type": {
74036
+ "text": "string | null"
74037
+ }
74038
+ },
74039
+ "parameters": [
74040
+ {
74041
+ "name": "reference",
74042
+ "type": {
74043
+ "text": "unknown"
74044
+ }
74045
+ }
74046
+ ],
74047
+ "description": "Return the custom tone name carried by a valid `tone:<slug>` reference."
74048
+ },
74049
+ {
74050
+ "kind": "function",
74051
+ "name": "isLightnessKey",
74052
+ "return": {
74053
+ "type": {
74054
+ "text": "reference is LightnessKey"
74055
+ }
74056
+ },
74057
+ "parameters": [
74058
+ {
74059
+ "name": "reference",
74060
+ "type": {
74061
+ "text": "unknown"
74062
+ }
74063
+ }
74064
+ ],
74065
+ "description": "True when a string names one of the eleven engine-owned ramp stops."
74066
+ },
74067
+ {
74068
+ "kind": "function",
74069
+ "name": "resolveLightnessReference",
74070
+ "return": {
74071
+ "type": {
74072
+ "text": "number"
74073
+ }
74074
+ },
74075
+ "parameters": [
74076
+ {
74077
+ "name": "lightness",
74078
+ "type": {
74079
+ "text": "LightnessMap"
74080
+ }
74081
+ },
74082
+ {
74083
+ "name": "tones",
74084
+ "type": {
74085
+ "text": "ThemeTones | undefined"
74086
+ }
74087
+ },
74088
+ {
74089
+ "name": "reference",
74090
+ "type": {
74091
+ "text": "LightnessReference"
74092
+ }
74093
+ }
74094
+ ],
74095
+ "description": "Resolve the lightness an explicit semantic mapping requested.\n\nCustom tones are a lookup namespace only: this function never adds them to\nLIGHTNESS_KEYS, so automatic role and action-stop algorithms continue\nto consider exactly the built-in eleven. An invalid runtime reference falls\nback to `surface`; validation reports the authoring error before mounting."
74096
+ },
73991
74097
  {
73992
74098
  "kind": "variable",
73993
74099
  "name": "DEFAULT_LIGHT_LIGHTNESS",
@@ -74021,7 +74127,7 @@
74021
74127
  "type": {
74022
74128
  "text": "Readonly<EspalierTheme>"
74023
74129
  },
74024
- "default": "{ ...SHARED_DEFAULTS, chroma: buildDefaultChroma(), semanticMappings: { ...DEFAULT_SEMANTIC_MAPPINGS }, anchors: {}, roles: {}, contexts: {}, dataPalette: { ...DEFAULT_DATA_PALETTE }, dataRamps: {}, lightness: { ...DEFAULT_LIGHT_LIGHTNESS }, }",
74130
+ "default": "{ ...SHARED_DEFAULTS, chroma: buildDefaultChroma(), semanticMappings: { ...DEFAULT_SEMANTIC_MAPPINGS }, anchors: {}, roles: {}, contexts: {}, tones: {}, dataPalette: { ...DEFAULT_DATA_PALETTE }, dataRamps: {}, lightness: { ...DEFAULT_LIGHT_LIGHTNESS }, }",
74025
74131
  "description": "The built-in **light** theme.\n\nIdentical to the dark theme in every field except `lightness`,\nwhich uses a high-surface / low-text ramp suited for bright\nbackgrounds."
74026
74132
  },
74027
74133
  {
@@ -74030,7 +74136,7 @@
74030
74136
  "type": {
74031
74137
  "text": "Readonly<EspalierTheme>"
74032
74138
  },
74033
- "default": "{ ...SHARED_DEFAULTS, chroma: buildDefaultChroma(), semanticMappings: { ...DEFAULT_SEMANTIC_MAPPINGS }, anchors: {}, roles: {}, contexts: {}, dataPalette: { ...DEFAULT_DATA_PALETTE }, dataRamps: {}, lightness: { ...DEFAULT_DARK_LIGHTNESS }, }",
74139
+ "default": "{ ...SHARED_DEFAULTS, chroma: buildDefaultChroma(), semanticMappings: { ...DEFAULT_SEMANTIC_MAPPINGS }, anchors: {}, roles: {}, contexts: {}, tones: {}, dataPalette: { ...DEFAULT_DATA_PALETTE }, dataRamps: {}, lightness: { ...DEFAULT_DARK_LIGHTNESS }, }",
74034
74140
  "description": "The built-in **dark** theme.\n\nIdentical to the light theme in every field except `lightness`,\nwhich uses a low-surface / high-text ramp suited for dark\nbackgrounds."
74035
74141
  },
74036
74142
  {
@@ -74131,7 +74237,7 @@
74131
74237
  "description": "User-supplied partial overrides."
74132
74238
  }
74133
74239
  ],
74134
- "description": "Deep-merge a PartialTheme over a set of defaults.\n\nPrimitive fields are replaced; nested objects (`angles`,\n`semanticHues`, `lightness`, `chroma`, `semanticMappings`, `contexts`)\nare merged key-by-key. Arrays (`stylesheets`) are replaced\nwholesale."
74240
+ "description": "Deep-merge a PartialTheme over a set of defaults.\n\nPrimitive fields are replaced; nested objects (`angles`,\n`semanticHues`, `lightness`, `tones`, `chroma`, `semanticMappings`, `contexts`)\nare merged key-by-key. Arrays (`stylesheets`) are replaced\nwholesale."
74135
74241
  },
74136
74242
  {
74137
74243
  "kind": "function",
@@ -74157,7 +74263,7 @@
74157
74263
  "description": "The context name a zone would set in its attribute."
74158
74264
  }
74159
74265
  ],
74160
- "description": "Compile a named context over a resolved theme — the exact theme a\n`context=\"<name>\"` zone renders (ESP0175).\n\nThis is the same compilation `esp-element-base` performs for a zone\nhost, exported so a check script or the fit report can inspect a\ncontext surface without a DOM: role rebindings and the partial\nlightness ramp merge over the root theme, and any context-level\n`semanticMappings` layer on top as explicit token pins. Returns `null`\nwhen the theme does not define the context."
74266
+ "description": "Compile a named context over a resolved theme — the exact theme a\n`context=\"<name>\"` zone renders (ESP0175).\n\nThis is the same compilation `esp-element-base` performs for a zone\nhost, exported so a check script or the fit report can inspect a\ncontext surface without a DOM: role rebindings, the partial lightness ramp,\nand custom tones merge over the root theme, and any context-level\n`semanticMappings` layer on top as explicit token pins. Returns `null` when\nthe theme does not define the context."
74161
74267
  },
74162
74268
  {
74163
74269
  "kind": "function",
@@ -74205,9 +74311,9 @@
74205
74311
  "kind": "variable",
74206
74312
  "name": "NESTED_THEME_KEYS",
74207
74313
  "type": {
74208
- "text": "[\n \"anchors\",\n \"angles\",\n \"contexts\",\n \"dataPalette\",\n \"dataRamps\",\n \"intents\",\n \"roles\",\n \"chroma\",\n \"lightness\",\n \"semanticHues\",\n \"semanticMappings\",\n \"variantChroma\",\n]"
74314
+ "text": "[\n \"anchors\",\n \"angles\",\n \"contexts\",\n \"dataPalette\",\n \"dataRamps\",\n \"intents\",\n \"roles\",\n \"chroma\",\n \"lightness\",\n \"tones\",\n \"semanticHues\",\n \"semanticMappings\",\n \"variantChroma\",\n]"
74209
74315
  },
74210
- "default": "[ \"anchors\", \"angles\", \"contexts\", \"dataPalette\", \"dataRamps\", \"intents\", \"roles\", \"chroma\", \"lightness\", \"semanticHues\", \"semanticMappings\", \"variantChroma\", ]",
74316
+ "default": "[ \"anchors\", \"angles\", \"contexts\", \"dataPalette\", \"dataRamps\", \"intents\", \"roles\", \"chroma\", \"lightness\", \"tones\", \"semanticHues\", \"semanticMappings\", \"variantChroma\", ]",
74211
74317
  "description": "Keys of EspalierTheme whose values are nested objects\nand therefore need key-by-key merging instead of wholesale\nreplacement when combining two PartialTheme objects."
74212
74318
  },
74213
74319
  {
@@ -74407,6 +74513,30 @@
74407
74513
  "module": "dist/shared/theme.js"
74408
74514
  }
74409
74515
  },
74516
+ {
74517
+ "kind": "js",
74518
+ "name": "toneReferenceName",
74519
+ "declaration": {
74520
+ "name": "toneReferenceName",
74521
+ "module": "dist/shared/theme.js"
74522
+ }
74523
+ },
74524
+ {
74525
+ "kind": "js",
74526
+ "name": "isLightnessKey",
74527
+ "declaration": {
74528
+ "name": "isLightnessKey",
74529
+ "module": "dist/shared/theme.js"
74530
+ }
74531
+ },
74532
+ {
74533
+ "kind": "js",
74534
+ "name": "resolveLightnessReference",
74535
+ "declaration": {
74536
+ "name": "resolveLightnessReference",
74537
+ "module": "dist/shared/theme.js"
74538
+ }
74539
+ },
74410
74540
  {
74411
74541
  "kind": "js",
74412
74542
  "name": "DEFAULT_LIGHT_LIGHTNESS",
package/dist/index.d.ts CHANGED
@@ -73,7 +73,7 @@ export * from "./shared/events.js";
73
73
  export { getImageDetails, releasePreviewUrl, type EspalierUploadImage, type ImageDetailsOptions, type SelectedUploadImage, type ExistingUploadImage, type ExistingImage, type ResponsiveImageUrl, type UploadCallbacks, type UploadEventDetail, } from "./image-upload/image-helpers.js";
74
74
  export { calculatePhotoLayout, type LayoutImage, type PhotoRow, } from "./shared/justified-layout.js";
75
75
  export { type TypeaheadFetchItems } from "./pickers/types.js";
76
- export { type EspalierTheme, type PartialTheme, type PartialThemeContexts, type ThemeContext, type ThemeContexts, type VariantColorSource, encodeTheme, parseTheme, mergePartials, layerThemes, validateThemePair, resolveContextTheme, buildTaprootLightTheme, buildTaprootDarkTheme, NESTED_THEME_KEYS, } from "./shared/theme.js";
76
+ export { type EspalierTheme, type LightnessKey, type LightnessReference, type PartialTheme, type PartialThemeContexts, type PartialThemeTones, type ThemeContext, type ThemeContexts, type ThemeTones, type ToneReference, type VariantColorSource, encodeTheme, parseTheme, mergePartials, layerThemes, validateThemePair, resolveContextTheme, buildTaprootLightTheme, buildTaprootDarkTheme, NESTED_THEME_KEYS, } from "./shared/theme.js";
77
77
  export { auditDataPalette, generateSequentialRamp, generateDivergingRamp, COLOR_VISION_SIMULATIONS, DATA_SERIES_KEYS, DEFAULT_DATA_PALETTE, DEFAULT_DATA_RAMP_STEPS, DEFAULT_DIVERGING_NEUTRAL, MAX_DATA_RAMP_STEPS, MIN_DATA_COLOR_DISTANCE, MIN_DATA_RAMP_LIGHTNESS_STEP, MIN_DATA_RAMP_STEPS, type ColorVisionSimulation, type DataPalette, type DataPaletteIssue, type DataRamp, type DataRamps, type DataSeriesKey, type DivergingDataRamp, type DivergingRampOptions, type PartialDataRamp, type PartialDataRamps, type SequentialDataRamp, type SequentialRampOptions, } from "./shared/data-colors.js";
78
78
  export { WEIGHT_LABELS, extractWeights, normalizeWeight, bestAvailableWeight, extractFamily, getFallbackFont, } from "./shared/font-helpers.js";
79
79
  export { getGoogleFonts } from "./font-picker/esp-font-picker.js";
@@ -1 +1 @@
1
- import{deriveSemantic as O,deriveSemanticWithContrast as k,serializeOklch as C}from"../../shared/color-engine.js";import{SEMANTIC_COLOR_NAMES as M,TOKEN_PAIRINGS as R,semanticToCSS as y}from"../../shared/theme.js";import{resolveMappingSource as A}from"./compute-variants.js";function E(s,u,r,n,i,g){return[s.l,s.c,s.h,u,r,n,i.l,i.c,i.h,g].join("|")}function q(s,u,r={}){const n={},i={},g=new Map,h=(e,o)=>{const t=r.effectiveSource?.(e,o)??o,c=A(t,u,s.anchors);return c||r.onUnresolvedSource?.(t),{color:c??u.primary,resolved:c!==null,source:t}};for(const e of M){if(R[e])continue;const o=s.semanticMappings[e],t=s.chroma[e],c=h(e,o.source),a=s.lightness[o.lightness],m=O(c.color,a,t.min,t.max),l=y(e);i[e]=m,n[l]=C(m),r.trace&&r.trace.push({token:e,property:l,resolved:n[l],source:c.source,lightness:o.lightness,sourceResolved:c.resolved,base:c.color})}for(const e of M){const o=R[e];if(!o)continue;const t=s.semanticMappings[e],c=s.chroma[e],a=h(e,t.source),m=s.lightness[t.lightness],l=i[o.bg],v=E(a.color,m,c.min,c.max,l,o.targetLc);let f=g.get(v);if(!f){let S;const x=k(a.color,m,c.min,c.max,l,o.targetLc,r.trace&&(N=>{S=N}));f={requested:S,semantic:x},g.set(v,f)}const{requested:L,semantic:d}=f,p=y(e);i[e]=d,n[p]=C(d),r.trace&&r.trace.push({token:e,property:p,resolved:n[p],source:a.source,lightness:t.lightness,sourceResolved:a.resolved,base:a.color,requested:L,contrast:{against:o.bg,background:l,targetLc:o.targetLc}})}return n}export{q as computeSemanticProperties};
1
+ import{deriveSemantic as k,deriveSemanticWithContrast as A,serializeOklch as R}from"../../shared/color-engine.js";import{SEMANTIC_COLOR_NAMES as C,TOKEN_PAIRINGS as L,resolveLightnessReference as M,semanticToCSS as y}from"../../shared/theme.js";import{resolveMappingSource as E}from"./compute-variants.js";function I(o,g,r,t,i,m){return[o.l,o.c,o.h,g,r,t,i.l,i.c,i.h,m].join("|")}function K(o,g,r={}){const t={},i={},m=new Map,h=(e,s)=>{const n=r.effectiveSource?.(e,s)??s,c=E(n,g,o.anchors);return c||r.onUnresolvedSource?.(n),{color:c??g.primary,resolved:c!==null,source:n}};for(const e of C){if(L[e])continue;const s=o.semanticMappings[e],n=o.chroma[e],c=h(e,s.source),a=M(o.lightness,o.tones,s.lightness),u=k(c.color,a,n.min,n.max),l=y(e);i[e]=u,t[l]=R(u),r.trace&&r.trace.push({token:e,property:l,resolved:t[l],source:c.source,lightness:s.lightness,sourceResolved:c.resolved,base:c.color})}for(const e of C){const s=L[e];if(!s)continue;const n=o.semanticMappings[e],c=o.chroma[e],a=h(e,n.source),u=M(o.lightness,o.tones,n.lightness),l=i[s.bg],v=I(a.color,u,c.min,c.max,l,s.targetLc);let f=m.get(v);if(!f){let S;const N=A(a.color,u,c.min,c.max,l,s.targetLc,r.trace&&(O=>{S=O}));f={requested:S,semantic:N},m.set(v,f)}const{requested:x,semantic:d}=f,p=y(e);i[e]=d,t[p]=R(d),r.trace&&r.trace.push({token:e,property:p,resolved:t[p],source:a.source,lightness:n.lightness,sourceResolved:a.resolved,base:a.color,requested:x,contrast:{against:s.bg,background:l,targetLc:s.targetLc}})}return t}export{K as computeSemanticProperties};
@@ -48,7 +48,7 @@
48
48
  * report.adjustedTokens; // ['linkHover']
49
49
  * ```
50
50
  */
51
- import { type EspalierTheme, type LightnessKey, type MappingSource, type PartialTheme, type SemanticColorName } from "./theme.js";
51
+ import { type EspalierTheme, type LightnessReference, type MappingSource, type PartialTheme, type SemanticColorName } from "./theme.js";
52
52
  /** What APCA enforcement did to one token. */
53
53
  export interface ThemeFitApcaAction {
54
54
  /** The requested value, as an `oklch()` string. */
@@ -75,8 +75,8 @@ export interface ThemeFitAnchor {
75
75
  /**
76
76
  * ΔE-OK between the declared anchor and the emitted token. Expect a
77
77
  * non-zero number even on a perfect fit: lightness always comes from
78
- * the mapping's ramp stop, so a swatch used as a source is re-lit by
79
- * design (ADR-015).
78
+ * the mapping's built-in stop or custom tone, so a swatch used as a
79
+ * source is re-lit by design (ADR-015, ADR-020).
80
80
  */
81
81
  deltaE: number;
82
82
  }
@@ -88,8 +88,8 @@ export interface ThemeFitToken {
88
88
  property: string;
89
89
  /** The mapping's declared source. */
90
90
  source: MappingSource;
91
- /** The lightness ramp stop the mapping selected. */
92
- lightness: LightnessKey;
91
+ /** The built-in ramp stop or custom tone reference the mapping selected. */
92
+ lightness: LightnessReference;
93
93
  /** The value the mapping asked for, before contrast enforcement. */
94
94
  requested: string;
95
95
  /** The value actually emitted. */
@@ -35,6 +35,14 @@ import { type DataPalette, type DataRamps, type PartialDataRamps } from "./data-
35
35
  export type LightnessKey = "surface" | "raised1" | "raised2" | "raised3" | "raised4" | "accent" | "muted" | "text" | "border" | "ink" | "shadow";
36
36
  /** Lightness values (0–1) for every ramp position. */
37
37
  export type LightnessMap = Record<LightnessKey, number>;
38
+ /** A named, opt-in lightness value outside the engine-owned ramp. */
39
+ export type ToneReference = `tone:${string}`;
40
+ /** A built-in ramp stop or an explicitly declared custom tone. */
41
+ export type LightnessReference = LightnessKey | ToneReference;
42
+ /** Named custom lightness values. They are inert until a mapping references one. */
43
+ export type ThemeTones = Record<string, number>;
44
+ /** Tone declarations while partial themes are being layered. */
45
+ export type PartialThemeTones = Record<string, number | undefined>;
38
46
  /**
39
47
  * A geometric color-theory variant **or** a fixed status family.
40
48
  *
@@ -110,11 +118,11 @@ export type ThemeAnchors = Record<string, ThemeAnchor>;
110
118
  * base color, `anchor:<name>.<slot>` for a sub-slot.
111
119
  *
112
120
  * An anchor source contributes its own hue **and chroma**; lightness
113
- * always comes from the mapping's ramp stop, and APCA enforcement
114
- * applies unchanged. Anchors are absolute sources rather than seed
115
- * rotations — a brand color is a color, not a relationship to the
116
- * seed — so they hold steady while contexts rebind roles around them
117
- * and intents replace the filled-action source.
121
+ * comes from the mapping's built-in stop or custom tone, and APCA
122
+ * enforcement applies unchanged. Anchors are absolute sources rather
123
+ * than seed rotations — a brand color is a color, not a relationship
124
+ * to the seed — so they hold steady while contexts rebind roles around
125
+ * them and intents replace the filled-action source.
118
126
  */
119
127
  export type MappingSource = ColorSource | `anchor:${string}`;
120
128
  /** Anchor names and slot names share one slug grammar. */
@@ -167,8 +175,8 @@ export interface SemanticMapping {
167
175
  * declared anchor as `anchor:<name>` / `anchor:<name>.<slot>`.
168
176
  */
169
177
  source: MappingSource;
170
- /** The lightness ramp position to apply. */
171
- lightness: LightnessKey;
178
+ /** A built-in ramp stop, or a named custom tone as `tone:<name>`. */
179
+ lightness: LightnessReference;
172
180
  }
173
181
  /** Full mapping table — one entry per semantic color token. */
174
182
  export type SemanticMappings = Record<SemanticColorName, SemanticMapping>;
@@ -229,14 +237,14 @@ export type ThemeRoles = {
229
237
  * cannot make a dark zone inside a light scheme because semantic derivation
230
238
  * deliberately takes lightness from the ramp rather than from an anchor.
231
239
  *
232
- * A context may also carry token-level `semanticMappings` (ESP0175).
233
- * Root-level explicit mappings survive into every context by design —
234
- * they are deliberate token pins so a zone that needs a different
235
- * value for one of them declares its own mapping here, which layers over
236
- * the inherited pin exactly as a root-level mapping layers over roles.
240
+ * A context may also carry custom `tones` and token-level
241
+ * `semanticMappings` (ESP0175, ESP0180). Root-level tones and explicit
242
+ * mappings survive into every context by design, while a zone may add or
243
+ * override a tone and pin a token locally. Each record layers by name.
237
244
  */
238
245
  export type ThemeContext = ThemeRoles & {
239
246
  lightness?: Partial<LightnessMap>;
247
+ tones?: PartialThemeTones;
240
248
  semanticMappings?: Partial<SemanticMappings>;
241
249
  };
242
250
  /** Named role-rebinding zones available to `context` attributes. */
@@ -279,6 +287,8 @@ export declare const ROLE_PAIRED_INK: Readonly<Record<string, {
279
287
  interface CompileRoleOptions {
280
288
  /** Token mappings declared explicitly at this merge boundary. */
281
289
  explicitMappings?: Partial<SemanticMappings>;
290
+ /** Custom tones available to explicit surface mappings. */
291
+ tones?: ThemeTones;
282
292
  /** Exact maximum paired-ink contrast of an action surface candidate. */
283
293
  actionSurfaceContrast?: (source: MappingSource, stop: LightnessKey) => number;
284
294
  /** ΔE-OK between an action surface candidate and the theme's background. */
@@ -458,6 +468,14 @@ export interface EspalierTheme {
458
468
  variantChroma: Partial<Record<VariantColorSource, number>>;
459
469
  /** Lightness values (0–1) for the eleven ramp positions. */
460
470
  lightness: LightnessMap;
471
+ /**
472
+ * Named custom lightness values for explicit semantic mappings.
473
+ *
474
+ * Refer to one as `tone:<name>` from `semanticMappings`. Tones do not
475
+ * become built-in ramp stops, emit `--esp-l-*` properties, or participate
476
+ * in automatic role and action-stop selection.
477
+ */
478
+ tones: ThemeTones;
461
479
  /** Per-semantic-token chroma min / max. */
462
480
  chroma: Record<SemanticColorName, ChromaRange>;
463
481
  /** Maps each semantic token to its color source + lightness. */
@@ -515,10 +533,11 @@ export interface EspalierTheme {
515
533
  * carry slot-only anchor overrides, which the resolved
516
534
  * {@link ThemeAnchor} deliberately does not permit.
517
535
  */
518
- export type PartialTheme = Omit<DeepPartial<EspalierTheme>, "anchors" | "contexts" | "dataRamps"> & {
536
+ export type PartialTheme = Omit<DeepPartial<EspalierTheme>, "anchors" | "contexts" | "dataRamps" | "tones"> & {
519
537
  anchors?: PartialThemeAnchors;
520
538
  contexts?: PartialThemeContexts;
521
539
  dataRamps?: PartialDataRamps;
540
+ tones?: PartialThemeTones;
522
541
  };
523
542
  /** Validation result returned by {@link validateTheme}. */
524
543
  export interface ThemeValidationResult {
@@ -566,6 +585,19 @@ export declare const STATUS_COLOR_SOURCES: readonly StatusColorSource[];
566
585
  export declare const VARIANT_COLOR_SOURCES: readonly VariantColorSource[];
567
586
  /** Valid keys for the lightness ramp. */
568
587
  export declare const LIGHTNESS_KEYS: readonly LightnessKey[];
588
+ /** Return the custom tone name carried by a valid `tone:<slug>` reference. */
589
+ export declare function toneReferenceName(reference: unknown): string | null;
590
+ /** True when a string names one of the eleven engine-owned ramp stops. */
591
+ export declare function isLightnessKey(reference: unknown): reference is LightnessKey;
592
+ /**
593
+ * Resolve the lightness an explicit semantic mapping requested.
594
+ *
595
+ * Custom tones are a lookup namespace only: this function never adds them to
596
+ * {@link LIGHTNESS_KEYS}, so automatic role and action-stop algorithms continue
597
+ * to consider exactly the built-in eleven. An invalid runtime reference falls
598
+ * back to `surface`; validation reports the authoring error before mounting.
599
+ */
600
+ export declare function resolveLightnessReference(lightness: LightnessMap, tones: ThemeTones | undefined, reference: LightnessReference): number;
569
601
  /**
570
602
  * Default lightness ramp for the **light** scheme.
571
603
  *
@@ -660,7 +692,7 @@ export declare function explicitMappingTokens(theme: EspalierTheme): Set<Semanti
660
692
  * Deep-merge a {@link PartialTheme} over a set of defaults.
661
693
  *
662
694
  * Primitive fields are replaced; nested objects (`angles`,
663
- * `semanticHues`, `lightness`, `chroma`, `semanticMappings`, `contexts`)
695
+ * `semanticHues`, `lightness`, `tones`, `chroma`, `semanticMappings`, `contexts`)
664
696
  * are merged key-by-key. Arrays (`stylesheets`) are replaced
665
697
  * wholesale.
666
698
  *
@@ -675,10 +707,10 @@ export declare function mergeTheme(defaults: EspalierTheme, overrides: PartialTh
675
707
  *
676
708
  * This is the same compilation `esp-element-base` performs for a zone
677
709
  * host, exported so a check script or the fit report can inspect a
678
- * context surface without a DOM: role rebindings and the partial
679
- * lightness ramp merge over the root theme, and any context-level
680
- * `semanticMappings` layer on top as explicit token pins. Returns `null`
681
- * when the theme does not define the context.
710
+ * context surface without a DOM: role rebindings, the partial lightness ramp,
711
+ * and custom tones merge over the root theme, and any context-level
712
+ * `semanticMappings` layer on top as explicit token pins. Returns `null` when
713
+ * the theme does not define the context.
682
714
  *
683
715
  * @param theme A resolved theme (as `mergeTheme` returns).
684
716
  * @param name The context name a zone would set in its attribute.
@@ -711,7 +743,7 @@ export declare function validateThemePair(lightBase64: string, darkBase64: strin
711
743
  * and therefore need key-by-key merging instead of wholesale
712
744
  * replacement when combining two {@link PartialTheme} objects.
713
745
  */
714
- export declare const NESTED_THEME_KEYS: readonly ["anchors", "angles", "contexts", "dataPalette", "dataRamps", "intents", "roles", "chroma", "lightness", "semanticHues", "semanticMappings", "variantChroma"];
746
+ export declare const NESTED_THEME_KEYS: readonly ["anchors", "angles", "contexts", "dataPalette", "dataRamps", "intents", "roles", "chroma", "lightness", "tones", "semanticHues", "semanticMappings", "variantChroma"];
715
747
  /**
716
748
  * Deep-merge two {@link PartialTheme} objects.
717
749
  *
@@ -1 +1 @@
1
- import{UNSAFE_KEYS as st,isSafeKey as C}from"./unsafe-keys.js";import{ACCEPTED_COLOR_FORMS as ne,apcaContrast as rt,clampChannel as de,deltaEOK as at,deriveSemantic as ae,gamutMapToSRGB as me,linearRgbToOklab as it,oklchToSRGB as ct,parseCssColor as W,parseOklch as ut,serializeOklch as ie,srgbToLinearRGB as ft}from"./color-engine.js";import{computeVariants as he,parseAnchorSource as lt,resolveAnchorColor as pt,resolveMappingSource as ce}from"./variant-engine.js";import{auditColorDistances as Le,auditDataPalette as dt,DATA_SERIES_KEYS as ue,DEFAULT_DATA_PALETTE as ge,DEFAULT_DATA_RAMP_STEPS as mt,MAX_DATA_RAMP_STEPS as ye,MIN_DATA_RAMP_LIGHTNESS_STEP as He,MIN_DATA_RAMP_STEPS as be}from"./data-colors.js";const V=/^[a-z][a-z0-9-]*$/;function $e(e){return lt(e)}function Pe(e,n){return pt(e,n)}function ht(e,n){const t=$e(e);if(t){const o=Pe(n,t);return o&&W(o)?o:null}return W(e)?e:null}const oe=["canvas","ink","accent","action","structure"],xe={canvas:{color:[["background","surface"],["layer1","raised1"],["layer2","raised2"],["layer3","raised3"],["layer4","raised4"]]},ink:{color:[["text","text"],["typeOverline","text"],["typeLabel","text"],["typeLead","text"],["typeCaption","text"],["inputSelection","text"]],heading:[["headings","muted"],["headingsHover","ink"]]},accent:{color:[["linkHoverBg","raised2"],["inputSelectionBg","raised2"],["inputCaret","ink"]],text:[["link","accent"]],hover:[["linkHover","text"]]},action:{color:[["actionBackground","muted"]],ink:[["actionText","surface"]]},structure:{color:[["border","border"],["shadow","shadow"]]}},gt=new Set(["action.color"]),yt={canvas:"background",ink:"text",accent:"link",action:"actionBackground",structure:"border"},bt={"action.ink":{role:"canvas",slot:"color"}},$t=.45,xt=.045;function De(e,n,t){const o=We.actionText,a=[...j].sort((g,A)=>e[g]-e[A]||j.indexOf(g)-j.indexOf(A)),c=n?a.filter(g=>n(g)>=o.targetLc):a,f=t?c.filter(g=>t(g)>=xt):c,p=f.length>0?f:c,d=p.length>0?p:a;let O,k=1/0;for(const g of d){const A=Math.abs(e[g]-$t);A<k&&(k=A,O=g)}return O??"muted"}function St(e,n){const t=n[e]>.5;let o=j[0];for(const a of j)(t?n[a]<n[o]:n[a]>n[o])&&(o=a);return o}const kt={"accent.hover":"text"};function At(e,n){const t=new Map;try{const o=le(n,e),a=(c,f,p,d)=>{const O=$e(f),k=O?Pe(d,O):null,g=k===null?null:W(k),A=t.get(c)??[];A.some(E=>E.source===f&&E.chroma===g?.c)||(A.push({source:f,where:p,chroma:g?.c}),t.set(c,A))};for(const[c,f]of Object.entries(o.semanticMappings))f.source.startsWith("anchor:")&&a(c,f.source,"the root table",o.anchors);for(const c of Object.keys(o.contexts)){const f=Xe(o,c);if(f)for(const[p,d]of Object.entries(f.semanticMappings))d.source.startsWith("anchor:")&&a(p,d.source,`contexts.${c}`,f.anchors)}}catch{return new Map}return t}function q(e,n){if(e===void 0)return;if(typeof e=="string")return n==="color"?e:void 0;const t=e[n];return typeof t=="string"?t:void 0}function Ne(e,n,t,o={}){const a={},c={};for(const[f,p]of Object.entries(o.explicitMappings??{}))p!==void 0&&(c[f]=p.lightness);for(const f of oe){const p=e[f];if(p!==void 0)for(const[d,O]of Object.entries(xe[f])){const k=bt[`${f}.${d}`];let g=q(p,d),A=!1;if(g===void 0){k&&(g=q(e[k.role],k.slot),g??=t[yt[k.role]]?.source,A=g!==void 0);const E=kt[`${f}.${d}`];E!==void 0&&(g??=q(p,E)),g??=q(p,"color")}if(g!==void 0)for(const[E,s]of O){let r=s;if(gt.has(`${f}.${d}`))r=De(n,o.actionSurfaceContrast?i=>o.actionSurfaceContrast(g,i):void 0,o.actionSurfaceSeparation?i=>o.actionSurfaceSeparation(g,i):void 0);else if(A&&k){const i=We[E],u=(i&&c[i.bg])??De(n,o.actionSurfaceContrast?m=>o.actionSurfaceContrast(q(p,"color"),m):void 0,o.actionSurfaceSeparation?m=>o.actionSurfaceSeparation(q(p,"color"),m):void 0);r=St(u,n)}a[E]={source:g,lightness:r}}}}return a}const We={text:{bg:"background",targetLc:75},typeOverline:{bg:"background",targetLc:90},typeLabel:{bg:"background",targetLc:75},typeLead:{bg:"background",targetLc:75},typeCaption:{bg:"background",targetLc:90},dangerText:{bg:"background",targetLc:75},headings:{bg:"background",targetLc:60},headingsHover:{bg:"background",targetLc:60},link:{bg:"background",targetLc:75},linkHover:{bg:"linkHoverBg",targetLc:75},actionText:{bg:"actionBackground",targetLc:75},inputCaret:{bg:"layer2",targetLc:60},inputSelection:{bg:"inputSelectionBg",targetLc:60}},Q=["background","layer1","layer2","layer3","layer4","actionBackground","actionText","border","shadow","text","typeOverline","typeLabel","typeLead","typeCaption","dangerText","headings","headingsHover","link","linkHover","linkHoverBg","inputCaret","inputSelection","inputSelectionBg"],vt=["typeOverline","typeLabel","typeLead","typeCaption"],Se=["primary","analogous-left","analogous-right","complementary","split-complementary-left","split-complementary-right","triadic-left","triadic-right","danger","success","warning","info"],ee=["danger","success","warning","info"],Mt=["analogous-left","analogous-right","complementary","split-complementary-left","split-complementary-right","triadic-left","triadic-right","danger","success","warning","info"],j=["surface","raised1","raised2","raised3","raised4","accent","muted","text","border","ink","shadow"],Ct={surface:.99,raised1:.97,raised2:.9,raised3:.8,raised4:.72,accent:.5,muted:.45,text:.35,border:.3,ink:.25,shadow:.2},Ot={surface:.15,raised1:.2,raised2:.3,raised3:.4,raised4:.36,accent:.6,muted:.75,text:.85,border:.7,ink:.95,shadow:.6},ke={background:{source:"primary",lightness:"surface"},layer1:{source:"primary",lightness:"raised1"},layer2:{source:"primary",lightness:"raised2"},layer3:{source:"primary",lightness:"raised3"},layer4:{source:"primary",lightness:"raised4"},actionBackground:{source:"primary",lightness:"raised3"},actionText:{source:"primary",lightness:"ink"},border:{source:"primary",lightness:"border"},shadow:{source:"primary",lightness:"shadow"},text:{source:"primary",lightness:"text"},typeOverline:{source:"primary",lightness:"text"},typeLabel:{source:"primary",lightness:"text"},typeLead:{source:"primary",lightness:"text"},typeCaption:{source:"primary",lightness:"text"},dangerText:{source:"danger",lightness:"text"},headings:{source:"primary",lightness:"muted"},headingsHover:{source:"primary",lightness:"text"},link:{source:"complementary",lightness:"accent"},linkHover:{source:"complementary",lightness:"text"},linkHoverBg:{source:"triadic-left",lightness:"raised2"},inputCaret:{source:"triadic-right",lightness:"ink"},inputSelection:{source:"complementary",lightness:"text"},inputSelectionBg:{source:"triadic-left",lightness:"raised2"}},J=.4;function Ae(){const e={};for(const n of Q)e[n]={min:0,max:J};return e}const Fe={seedColor:"oklch(0.7 0.125 216)",fontBody:"",fontHeadings:"",fontBrand:"",fontMonospace:"",fontWeightBody:"normal",fontWeightHeadings:"bold",fontWeightBrand:"bold",fontWeightMonospace:"normal",stylesheets:[],rootFontSize:16,typeRatio:1.25,spaceRatio:1.618,borderRadius:.2,viewportMin:320,viewportMax:1200,angles:{analogous:30,complementary:180,splitComplementary:30,triadic:120},semanticHues:{danger:27,success:150,warning:90,info:244},variantChroma:{},intents:{},chroma:Ae(),semanticMappings:{...ke},anchors:{},roles:{},contexts:{},dataPalette:{...ge},dataRamps:{}},ve={...Fe,chroma:Ae(),semanticMappings:{...ke},anchors:{},roles:{},contexts:{},dataPalette:{...ge},dataRamps:{},lightness:{...Ct}},je={...Fe,chroma:Ae(),semanticMappings:{...ke},anchors:{},roles:{},contexts:{},dataPalette:{...ge},dataRamps:{},lightness:{...Ot}},fe=new WeakMap;fe.set(ve,new Set),fe.set(je,new Set);function Rt(e,n){return st.has(e)?void 0:n}function Yt(e){return`--esp-color-${e.replace(/([A-Z])/g,"-$1").replace(/(\d)/g,"-$1").toLowerCase()}`}function Tt(e){try{const n=Ke(e),t=JSON.parse(n,Rt);return typeof t!="object"||t===null||Array.isArray(t)?null:t}catch{return null}}const Me="\xEF\xBB\xBF";function Bt(e){if(/^[\u0000-\u007f]*$/.test(e))return btoa(e);const n=new TextEncoder().encode(e);let t=Me;for(const o of n)t+=String.fromCharCode(o);return btoa(t)}function Ke(e){const n=atob(e);if(!n.startsWith(Me))return n;const t=Uint8Array.from(n.slice(Me.length),o=>o.charCodeAt(0));return new TextDecoder("utf-8",{fatal:!0}).decode(t)}function Ce(e){return Bt(JSON.stringify(e))}function wt(e){const n={};for(const t of ee){const o=e[t];typeof o=="string"&&(n[t]=o.startsWith("anchor:")?o:se(o))}return n}function se(e){if(ut(e))return e;const n=W(e);return n?ie(n):e}function Ue(e,n){const t={};for(const[o,a]of Object.entries(e))C(o)&&(t[o]=a);if(!n)return t;for(const[o,a]of Object.entries(n)){if(a===void 0||!C(o))continue;const c=t[o];if(c!==void 0&&typeof a=="object"&&a!==null){const f={};if(typeof c=="string")f.color=c;else for(const[p,d]of Object.entries(c))C(p)&&(f[p]=d);for(const[p,d]of Object.entries(a))d!==void 0&&C(p)&&(f[p]=d);t[o]=f;continue}t[o]=a}return t}function ze(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;const n=e,t={};for(const o of oe){const a=n[o];a!==void 0&&(t[o]=a)}if(typeof n.lightness=="object"&&n.lightness!==null&&!Array.isArray(n.lightness)){const o={};for(const a of j){const c=n.lightness[a];c!==void 0&&(o[a]=c)}t.lightness=o}if(typeof n.semanticMappings=="object"&&n.semanticMappings!==null&&!Array.isArray(n.semanticMappings)){const o={};for(const[a,c]of Object.entries(n.semanticMappings)){if(!C(a)||!Q.includes(a)||typeof c!="object"||c===null||Array.isArray(c))continue;const{source:f,lightness:p}=c;typeof f=="string"&&(typeof p!="string"||!j.includes(p)||(o[a]={source:f,lightness:p}))}Object.keys(o).length>0&&(t.semanticMappings=o)}return t}function Et(e,n){const t={};for(const[o,a]of Object.entries(e)){if(!C(o)||!V.test(o))continue;const c=ze(a);c&&(t[o]=c)}if(!n)return t;for(const[o,a]of Object.entries(n)){if(!C(o)||!V.test(o)||a===void 0)continue;const c=ze(a);if(!c)continue;const f=t[o];t[o]={...f??{},...c,...f?.lightness||c.lightness?{lightness:{...f?.lightness,...c.lightness}}:{},...f?.semanticMappings||c.semanticMappings?{semanticMappings:{...f?.semanticMappings,...c.semanticMappings}}:{}}}return t}function K(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function U(e){const n={};for(const[t,o]of Object.entries(e))o!==void 0&&C(t)&&(n[t]=o);return n}function Ge(e){if(!K(e))return e;const n=U(e);return K(e.lightness)&&(n.lightness=U(e.lightness)),n}function _t(e,n){const t={};for(const[o,a]of Object.entries(e))C(o)&&(t[o]=Ge(a));if(!n)return t;for(const[o,a]of Object.entries(n)){if(!C(o)||a===void 0)continue;const c=t[o];if(!K(c)||!K(a)){t[o]=Ge(a);continue}const f={...U(c),...U(a)};K(c.lightness)&&K(a.lightness)?f.lightness={...U(c.lightness),...U(a.lightness)}:K(a.lightness)&&(f.lightness=U(a.lightness)),K(c.semanticMappings)&&K(a.semanticMappings)?f.semanticMappings={...U(c.semanticMappings),...U(a.semanticMappings)}:K(a.semanticMappings)&&(f.semanticMappings=U(a.semanticMappings)),t[o]=f}return t}function It(e){const n={};for(const[t,o]of Object.entries(e)){if(!C(t))continue;if(typeof o=="string"){n[t]=se(o);continue}const a={};for(const[c,f]of Object.entries(o))C(c)&&(a[c]=typeof f=="string"?se(f):f);n[t]=a}return n}function re(e){return typeof e!="string"?"":e.startsWith("anchor:")?e:se(e)}function Lt(e){const n={};for(const t of ue)n[t]=re(e[t]);return n}function Ve(e,n){const t={};for(const[o,a]of Object.entries(e))C(o)&&V.test(o)&&a&&typeof a=="object"&&!Array.isArray(a)&&(t[o]={...a});if(!n)return t;for(const[o,a]of Object.entries(n)){if(!C(o)||!V.test(o)||!a||typeof a!="object"||Array.isArray(a))continue;const c=t[o],f=c!==void 0&&a.type!==void 0&&a.type!==c.type;t[o]={...f?{}:c??{},...a}}return t}function Ht(e){const n={};for(const[t,o]of Object.entries(e))if(C(t)){if(o.type==="sequential"&&typeof o.source=="string"){n[t]={...o,source:re(o.source)};continue}if(o.type==="diverging"&&typeof o.start=="string"&&typeof o.end=="string"){n[t]={...o,start:re(o.start),end:re(o.end),...typeof o.neutral=="string"?{neutral:re(o.neutral)}:{}};continue}n[t]={...o}}return n}function Je(e){const n=W(e.seedColor);if(!n)return;const t=he(n,e);return(o,a)=>{const c=ce(o,t,e.anchors)??t.primary,f=e.chroma.actionBackground,p=ae(c,e.lightness[a],f.min,f.max),d={l:p.l>.5?0:1,c:0,h:0};return Math.abs(rt(d,p))}}function Pt(e,n,t){if(n?.background)return n.background;const o=q(e.canvas,"color");if(o!==void 0){const a=xe.canvas.color?.find(([c])=>c==="background");if(a)return{source:o,lightness:a[1]}}return t.background}function Ye(e,n){const t=W(e.seedColor);if(!t||!n)return;const o=he(t,e),a=ce(n.source,o,e.anchors)??o.primary,c=e.chroma.background,f=ae(a,e.lightness[n.lightness],c.min,c.max);return(p,d)=>{const O=ce(p,o,e.anchors)??o.primary,k=e.chroma.actionBackground,g=ae(O,e.lightness[d],k.min,k.max);return at(g,f)}}function Dt(e){const n=fe.get(e);if(n)return new Set(n);const t=new Set,o=Ne(e.roles,e.lightness,e.semanticMappings,{actionSurfaceContrast:Je(e),actionSurfaceSeparation:Ye(e,e.semanticMappings.background)});for(const[a,c]of Object.entries(o)){const f=e.semanticMappings[a];(f.source!==c.source||f.lightness!==c.lightness)&&t.add(a)}return t}function le(e,n){const t={...e.roles,...n.roles},o=Et(e.contexts,n.contexts),a={...e.lightness,...n.lightness},c=It(Ue(e.anchors,n.anchors)),f=typeof n.seedColor=="string"?se(n.seedColor):e.seedColor,p=n.semanticMappings,d={...e.angles,...n.angles},O={...e.semanticHues,...n.semanticHues},k={...e.variantChroma,...n.variantChroma},g=wt({...e.intents,...n.intents}),A=n.chroma,E=te(e.chroma,A),s=Lt(te(e.dataPalette,n.dataPalette)),r=Ht(Ve(e.dataRamps,n.dataRamps)),i=Dt(e),u=new Set(i),m={};for(const x of i)m[x]=e.semanticMappings[x];for(const[x,v]of Object.entries(p??{}))v!==void 0&&(u.add(x),m[x]=v);const h=te(e.semanticMappings,p),_={angles:d,anchors:c,chroma:E,intents:g,lightness:a,seedColor:f,semanticHues:O,variantChroma:k},D=Je(_),l=Ye(_,Pt(t,m,h)),y=Ne(t,a,h,{explicitMappings:m,actionSurfaceContrast:D,actionSurfaceSeparation:l}),b={};for(const[x,v]of Object.entries(y))u.has(x)||(b[x]=v);const $={};for(const x of vt)!u.has(x)&&b[x]===void 0&&($[x]={source:h.text.source,lightness:h.text.lightness},A?.text!==void 0&&A[x]===void 0&&(E[x]={...E.text}));const R=te(te(te(e.semanticMappings,$),b),p),S={...e,seedColor:f,fontBody:n.fontBody??e.fontBody,fontHeadings:n.fontHeadings??e.fontHeadings,fontBrand:n.fontBrand??e.fontBrand,fontMonospace:n.fontMonospace??e.fontMonospace,fontWeightBody:String(n.fontWeightBody??e.fontWeightBody),fontWeightHeadings:String(n.fontWeightHeadings??e.fontWeightHeadings),fontWeightBrand:String(n.fontWeightBrand??e.fontWeightBrand),fontWeightMonospace:String(n.fontWeightMonospace??e.fontWeightMonospace),stylesheets:n.stylesheets??[...e.stylesheets],rootFontSize:n.rootFontSize??e.rootFontSize,typeRatio:n.typeRatio??e.typeRatio,spaceRatio:n.spaceRatio??e.spaceRatio,borderRadius:n.borderRadius??e.borderRadius,viewportMin:n.viewportMin??e.viewportMin,viewportMax:n.viewportMax??e.viewportMax,angles:d,semanticHues:O,variantChroma:k,intents:g,lightness:a,chroma:E,semanticMappings:R,anchors:c,roles:t,contexts:o,dataPalette:s,dataRamps:r};return n.pageBackgroundImage!==void 0&&(S.pageBackgroundImage=n.pageBackgroundImage),n.pageBackgroundImageOpacity!==void 0&&(S.pageBackgroundImageOpacity=n.pageBackgroundImageOpacity),n.boxBackgroundImage!==void 0&&(S.boxBackgroundImage=n.boxBackgroundImage),n.boxBackgroundImageOpacity!==void 0&&(S.boxBackgroundImageOpacity=n.boxBackgroundImageOpacity),n.vellumOpacity!==void 0&&(S.vellumOpacity=n.vellumOpacity),n.vellumBackgroundImage!==void 0&&(S.vellumBackgroundImage=n.vellumBackgroundImage),n.vellumBackgroundImageOpacity!==void 0&&(S.vellumBackgroundImageOpacity=n.vellumBackgroundImageOpacity),fe.set(S,new Set(u)),S}const qe=new WeakMap;function Xe(e,n){const t=e.contexts&&Object.prototype.hasOwnProperty.call(e.contexts,n)?e.contexts[n]:void 0;if(!t)return null;let o=qe.get(e);const a=o?.get(n);if(a)return a;const c={};for(const p of oe){const d=t[p];d!==void 0&&(c[p]=d)}const f=le(e,{lightness:t.lightness,roles:c,semanticMappings:t.semanticMappings});return o||(o=new Map,qe.set(e,o)),o.set(n,f),f}function te(e,n){if(!n)return{...e};const t={...e};for(const o of Object.keys(n)){const a=n[o];a!==void 0&&(t[o]=a)}return t}function Oe(e){let n;try{n=Ke(e)}catch{return{error:"Failed to decode Base64 string."}}let t;try{t=JSON.parse(n)}catch{return{error:"Decoded string is not valid JSON."}}return typeof t!="object"||t===null||Array.isArray(t)?{error:"Theme must be a JSON object."}:{value:t}}function qt(e){const n=Oe(e);return n.error!==void 0?{valid:!1,errors:[n.error],warnings:[]}:Re(n.value)}function Re(e,n=ve){const t=[],o=[];"seedColor"in e&&(typeof e.seedColor!="string"?t.push("seedColor must be a string."):W(e.seedColor)||t.push(`seedColor is not a valid CSS color: "${e.seedColor}". Accepted forms: ${ne}.`));for(const s of["fontBody","fontHeadings","fontBrand","fontMonospace"])s in e&&typeof e[s]!="string"&&t.push(`${s} must be a string.`);const a=["normal","bold","lighter","bolder"],c=["inherit","initial","unset","revert","revert-layer"];for(const s of["fontWeightBody","fontWeightHeadings","fontWeightBrand","fontWeightMonospace"])if(s in e){const r=e[s];if(typeof r=="number")(r<1||r>1e3)&&t.push(`${s} numeric value must be 1\u20131000 (got ${r}).`);else if(typeof r=="string"){const i=a.includes(r)||c.includes(r);if(/^\d+$/.test(r)){const m=Number(r);(m<1||m>1e3)&&t.push(`${s} numeric value must be 1\u20131000 (got "${r}").`)}else i||o.push(`${s} = "${r}" is not a standard font-weight value.`)}else t.push(`${s} must be a string or number.`)}"stylesheets"in e&&(Array.isArray(e.stylesheets)?e.stylesheets.some(s=>typeof s!="string")&&t.push("Every entry in stylesheets must be a string."):t.push("stylesheets must be an array of strings."));const f=[["rootFontSize",1,100],["borderRadius",0,10],["viewportMin",100,1e4],["viewportMax",200,1e4]];for(const[s,r,i]of f)if(s in e)if(typeof e[s]!="number"||!isFinite(e[s]))t.push(`${s} must be a finite number.`);else{const u=e[s];r!==void 0&&u<r&&t.push(`${s} must be \u2265 ${r} (got ${u}).`),i!==void 0&&u>i&&o.push(`${s} = ${u} is unusually high (max ${i}).`)}for(const s of["typeRatio","spaceRatio"])if(s in e)if(typeof e[s]!="number"||!isFinite(e[s]))t.push(`${s} must be a finite number.`);else{const r=e[s];r<=1&&t.push(`${s} must be > 1 (got ${r}).`);const i=s==="typeRatio"?1.3:2;r>i&&o.push(`${s} = ${r} is very high (max ${i}); scales may be extreme.`)}if("viewportMin"in e&&"viewportMax"in e){const s=e.viewportMin,r=e.viewportMax;typeof s=="number"&&typeof r=="number"&&s>=r&&t.push(`viewportMin (${s}) must be less than viewportMax (${r}).`)}if("angles"in e)if(typeof e.angles!="object"||e.angles===null)t.push("angles must be an object.");else{const s=e.angles;for(const r of["analogous","complementary","splitComplementary","triadic"])if(r in s)if(typeof s[r]!="number"||!isFinite(s[r]))t.push(`angles.${r} must be a finite number.`);else{const i=s[r];(i<0||i>360)&&o.push(`angles.${r} = ${i} is outside 0\u2013360.`)}}if("semanticHues"in e)if(typeof e.semanticHues!="object"||e.semanticHues===null)t.push("semanticHues must be an object.");else{const s=e.semanticHues;for(const r of["danger","success","warning","info"])if(r in s)if(typeof s[r]!="number"||!isFinite(s[r]))t.push(`semanticHues.${r} must be a finite number.`);else{const i=s[r];(i<0||i>360)&&o.push(`semanticHues.${r} = ${i} is outside 0\u2013360.`)}}if("variantChroma"in e)if(typeof e.variantChroma!="object"||e.variantChroma===null)t.push("variantChroma must be an object.");else{const s=e.variantChroma;for(const r of Mt)if(r in s)if(typeof s[r]!="number"||!isFinite(s[r]))t.push(`variantChroma["${r}"] must be a finite number.`);else{const i=s[r];i<0&&t.push(`variantChroma["${r}"] must be \u2265 0 (got ${i}).`),i>J&&o.push(`variantChroma["${r}"] = ${i} exceeds ${J}.`)}}function p(s,r){if(typeof s!="object"||s===null||Array.isArray(s)){t.push(`${r} must be an object.`);return}const i=s;for(const u of j)if(u in i)if(typeof i[u]!="number"||!isFinite(i[u]))t.push(`${r}.${u} must be a finite number.`);else{const m=i[u];(m<0||m>1)&&t.push(`${r}.${u} must be 0\u20131 (got ${m}).`)}}if("lightness"in e&&p(e.lightness,"lightness"),"chroma"in e)if(typeof e.chroma!="object"||e.chroma===null)t.push("chroma must be an object.");else{const s=e.chroma,r=At(e,n);for(const i of Q)if(i in s){const u=s[i];if(typeof u!="object"||u===null){t.push(`chroma.${i} must be { min, max }.`);continue}const m=u;for(const _ of r.get(i)??[])_.chroma!==void 0&&typeof m.min=="number"&&typeof m.max=="number"&&(_.chroma<m.min-1e-9||_.chroma>m.max+1e-9)&&o.push(`chroma.${i} clamps the anchor-sourced token ${i} ("${_.source}", effective in ${_.where}) and moves it off its declared swatch. Remove the band if that is unintended \u2014 anchors otherwise keep their own chroma.`);const h=u;(typeof h.min!="number"||h.min<0)&&t.push(`chroma.${i}.min must be \u2265 0.`),(typeof h.max!="number"||h.max<0||h.max>J)&&t.push(`chroma.${i}.max must be 0\u2013${J}.`),typeof h.min=="number"&&typeof h.max=="number"&&h.min>h.max&&t.push(`chroma.${i}.min (${h.min}) must be \u2264 max (${h.max}).`)}}if("anchors"in e)if(typeof e.anchors!="object"||e.anchors===null||Array.isArray(e.anchors))t.push("anchors must be an object of named colors.");else for(const[s,r]of Object.entries(e.anchors))if(C(s)?V.test(s)||t.push(`anchors: "${s}" is not a valid anchor name; use a lowercase slug (letters, digits, hyphens).`):t.push(`anchors: "${s}" is a reserved JavaScript property name and cannot be an anchor name; rename it.`),Se.includes(s)&&t.push(`anchors: "${s}" collides with a reserved color source name.`),typeof r=="string")W(r)||t.push(`anchors.${s} is not a valid CSS color: "${r}". Accepted forms: ${ne}.`);else if(typeof r=="object"&&r!==null&&!Array.isArray(r)){const i=r;typeof i.color!="string"&&t.push(`anchors.${s} must declare its base "color".`);for(const[u,m]of Object.entries(i))C(u)?u!=="color"&&!V.test(u)&&t.push(`anchors.${s}: "${u}" is not a valid slot name; use a lowercase slug.`):t.push(`anchors.${s}: "${u}" is a reserved JavaScript property name and cannot be a slot name; rename it.`),typeof m!="string"?t.push(`anchors.${s}.${u} must be a CSS color string.`):W(m)||t.push(`anchors.${s}.${u} is not a valid CSS color: "${m}". Accepted forms: ${ne}.`)}else t.push(`anchors.${s} must be a color string or a { color, \u2026slots } object.`);const d=new Map;if("anchors"in e&&typeof e.anchors=="object"&&e.anchors!==null&&!Array.isArray(e.anchors))for(const[s,r]of Object.entries(e.anchors))d.set(s,r);function O(s,r){const i=$e(s);if(!i){t.push(`${r} "${s}" is not valid anchor syntax; use anchor:<name> or anchor:<name>.<slot>.`);return}if(!d.has(i.name)){const u=[...d.keys()];t.push(`${r} references undeclared anchor "${i.name}". Declared anchors: ${u.length>0?u.join(", "):"(none)"}.`);return}if(i.slot!==void 0){const u=d.get(i.name);if(typeof(typeof u=="object"&&u!==null&&!Array.isArray(u)?u[i.slot]:void 0)!="string"){const h=typeof u=="object"&&u!==null?Object.keys(u).filter(_=>_!=="color"):[];t.push(`${r} references unknown slot "${i.slot}" on anchor "${i.name}". Declared slots: ${h.length>0?h.join(", "):"(none)"}.`)}}}function k(s,r){if(s.startsWith("anchor:")){O(s,r);return}Se.includes(s)||t.push(`${r} must be one of: ${Se.join(", ")}, or a declared anchor as anchor:<name> / anchor:<name>.<slot>.`)}if("intents"in e)if(typeof e.intents!="object"||e.intents===null||Array.isArray(e.intents))t.push("intents must be an object.");else for(const[s,r]of Object.entries(e.intents)){const i=`intents.${s}`;if(!ee.includes(s)){t.push(`${i} is not a status family; expected one of: ${ee.join(", ")}.`);continue}if(typeof r!="string"){t.push(`${i} must be a string (a CSS color or an anchor: reference).`);continue}if(r.startsWith("anchor:")){O(r,i);continue}W(r)||t.push(`${i} "${r}" is not a supported color form or anchor: reference. Accepted forms: ${ne}, or anchor:<name> / anchor:<name>.<slot>. An override retunes a family with a declared color; it never points one family at another \u2014 retune, never reassign.`)}function g(s,r){if(s.startsWith("anchor:")){O(s,r);return}W(s)||t.push(`${r} is not a valid CSS color: "${s}". Accepted forms: ${ne}, or a declared anchor as anchor:<name> / anchor:<name>.<slot>.`)}if("dataPalette"in e)if(typeof e.dataPalette!="object"||e.dataPalette===null||Array.isArray(e.dataPalette))t.push("dataPalette must be an object with series1\u2013series8 color values.");else{const s=e.dataPalette;for(const[r,i]of Object.entries(s)){if(!ue.includes(r)){t.push(`dataPalette.${r} is not a series slot; expected one of: ${ue.join(", ")}.`);continue}typeof i!="string"?t.push(`dataPalette.${r} must be a CSS color or anchor reference string.`):g(i,`dataPalette.${r}`)}}if("dataRamps"in e)if(typeof e.dataRamps!="object"||e.dataRamps===null||Array.isArray(e.dataRamps))t.push("dataRamps must be an object of named ramp declarations.");else for(const[s,r]of Object.entries(e.dataRamps)){if(C(s)?V.test(s)||t.push(`dataRamps: "${s}" is not a valid ramp name; use a lowercase slug (letters, digits, hyphens).`):t.push(`dataRamps: "${s}" is a reserved JavaScript property name and cannot be a ramp name; rename it.`),typeof r!="object"||r===null||Array.isArray(r)){t.push(`dataRamps.${s} must be a sequential or diverging ramp object.`);continue}const i=r,u=i.steps??mt;if((typeof u!="number"||!Number.isInteger(u)||u<be||u>ye)&&t.push(`dataRamps.${s}.steps must be an integer from ${be} through ${ye}.`),i.type==="sequential"){typeof i.source!="string"?t.push(`dataRamps.${s}.source must be a CSS color or anchor reference string.`):g(i.source,`dataRamps.${s}.source`);const m=i.lightnessStart??.95,h=i.lightnessEnd??.25;for(const[_,D]of[["lightnessStart",m],["lightnessEnd",h]])(typeof D!="number"||!Number.isFinite(D)||D<0||D>1)&&t.push(`dataRamps.${s}.${_} must be a finite number from 0 through 1.`);typeof m=="number"&&typeof h=="number"&&m<=h?t.push(`dataRamps.${s}.lightnessStart must be greater than lightnessEnd.`):typeof m=="number"&&Number.isFinite(m)&&typeof h=="number"&&Number.isFinite(h)&&typeof u=="number"&&Number.isInteger(u)&&u>=be&&u<=ye&&(m-h)/(u-1)<=He&&t.push(`dataRamps.${s} lightness bounds must leave more than ${He} lightness between serialized stops.`);continue}if(i.type==="diverging"){typeof u=="number"&&Number.isInteger(u)&&u%2===0&&t.push(`dataRamps.${s}.steps must be odd so the neutral is the exact midpoint.`);for(const m of["start","end"])typeof i[m]!="string"?t.push(`dataRamps.${s}.${m} must be a CSS color or anchor reference string.`):g(i[m],`dataRamps.${s}.${m}`);"neutral"in i&&(typeof i.neutral!="string"?t.push(`dataRamps.${s}.neutral must be a CSS color or anchor reference string.`):g(i.neutral,`dataRamps.${s}.neutral`));continue}t.push(`dataRamps.${s}.type must be "sequential" or "diverging".`)}if("dataPalette"in e&&t.length===0){const s=le(ve,e),r={};let i=!0;for(const u of ue){const m=ht(s.dataPalette[u],s.anchors);if(!m){i=!1;break}r[u]=m}if(i)for(const u of dt(r))o.push(`dataPalette.${u.series[0]} and dataPalette.${u.series[1]} are only ${u.distance.toFixed(4)} apart under ${u.simulation} simulation (minimum ${u.threshold.toFixed(4)}); also distinguish the series with labels, shapes, or patterns.`)}function A(s,r){if(typeof s!="object"||s===null||Array.isArray(s)){t.push(`${r} must be an object.`);return}const i=s;for(const[u,m]of Object.entries(i)){const h=`${r}.${u}`;if(!oe.includes(u)){t.push(`${h} is not a role; expected one of: ${oe.join(", ")}. (The status family is reserved and is not a role.)`);continue}const _=u,D=xe[_];if(typeof m=="string"){k(m,h);continue}if(typeof m!="object"||m===null||Array.isArray(m)){t.push(`${h} must be a color source or a { color, \u2026slots } object.`);continue}const l=m;typeof l.color!="string"&&t.push(`${h} must declare its base "color".`);for(const[y,b]of Object.entries(l)){if(!Object.prototype.hasOwnProperty.call(D,y)||!C(y)){const $=Object.keys(D).filter(R=>R!=="color");t.push(`${h}.${y} is not a slot of the ${_} role. Declared slots: ${$.length>0?$.join(", "):"(none)"}.`);continue}typeof b!="string"?t.push(`${h}.${y} must be a color source string.`):k(b,`${h}.${y}`)}}}if("roles"in e&&A(e.roles,"roles"),"contexts"in e)if(typeof e.contexts!="object"||e.contexts===null||Array.isArray(e.contexts))t.push("contexts must be an object.");else for(const[s,r]of Object.entries(e.contexts)){if(!C(s)||!V.test(s)){t.push(`contexts.${s} must be a lowercase slug matching ${V.source}.`);continue}if(typeof r!="object"||r===null||Array.isArray(r)){A(r,`contexts.${s}`);continue}const i=r,u=Object.fromEntries(Object.entries(i).filter(([m])=>m!=="lightness"&&m!=="semanticMappings"));A(u,`contexts.${s}`),"lightness"in i&&p(i.lightness,`contexts.${s}.lightness`),"semanticMappings"in i&&E(i.semanticMappings,`contexts.${s}.semanticMappings`)}function E(s,r){if(typeof s!="object"||s===null||Array.isArray(s)){t.push(`${r} must be an object.`);return}const i=s;for(const u of Object.keys(i))Q.includes(u)||t.push(`${r}.${u} is not a semantic token; expected one of: ${Q.join(", ")}.`);for(const u of Q)if(u in i){const m=i[u];if(typeof m!="object"||m===null){t.push(`${r}.${u} must be { source, lightness }.`);continue}const h=m;typeof h.source!="string"?t.push(`${r}.${u}.source must be a string.`):k(h.source,`${r}.${u}.source`),(typeof h.lightness!="string"||!j.includes(h.lightness))&&t.push(`${r}.${u}.lightness must be one of: ${j.join(", ")}.`)}}"semanticMappings"in e&&E(e.semanticMappings,"semanticMappings");for(const s of["pageBackgroundImage","boxBackgroundImage","vellumBackgroundImage"])s in e&&typeof e[s]!="string"&&t.push(`${s} must be a string.`);for(const s of["pageBackgroundImageOpacity","boxBackgroundImageOpacity","vellumOpacity","vellumBackgroundImageOpacity"])if(s in e)if(typeof e[s]!="number"||!isFinite(e[s]))t.push(`${s} must be a finite number.`);else{const r=e[s];(r<0||r>1)&&t.push(`${s} must be 0\u20131 (got ${r}).`)}return o.push(...Nt(e,n)),{valid:t.length===0,errors:t,warnings:o}}const w=.045;function Nt(e,n){try{return Wt(e,n)}catch{return[]}}function Wt(e,n){const t=le(n,e),o=et(t),a=[...o],c=new Set(o);for(const f of Object.keys(t.contexts)){const p=Xe(t,f);if(p)for(const d of et(p))c.has(d)||a.push(`contexts.${f}: ${d}`)}return a}function Ft(e){const n=ct(me(e)),t=ft(n);return it({r:de(t.r),g:de(t.g),b:de(t.b)})}function X(e,n){return Math.hypot(e.L-n.L,e.a-n.a,e.b-n.b)}function Y(e){return Ft(W(ie(me(e))))}function Ze(e){return(Math.floor(e*1e4)/1e4).toFixed(4)}const Z=2,Qe=1e-4;function et(e){const n=W(e.seedColor);if(!n)return[];const t=he(n,e),o=e.semanticMappings.actionBackground,a=e.chroma.actionBackground,c=e.lightness[o.lightness],f=(l,y,b,$)=>me(ae(l,y,b,$)),p=ee.map(l=>({name:l,base:t[l]})),d=[];if(ee.includes(o.source))d.push(`actionBackground is sourced from the "${o.source}" status family: ordinary and ${o.source}-intent action surfaces render identically (their labels may still differ). Remap the action to a non-status source \u2014 retuning "${o.source}" via intents moves both colors together.`);else{const l=ce(o.source,t,e.anchors);l&&p.push({name:"action",base:l})}const O=Le(p.map(({name:l,base:y})=>[l,ie(f(y,c,a.min,a.max))]),w,["normal"]);if(O.length===0)return d;const k=new Map,g=(l,y)=>{const b=`${l}:${y}`;let $=k.get(b);if(!$){$=[];for(let R=0;R<360;R+=Z)$.push(Y({l,c:y,h:R}));k.set(b,$)}return $},A=w-.002,E=(l,y,b,$)=>{const R=Math.hypot(l.a,l.b),S=Math.atan2(l.b,l.a)*180/Math.PI+180;let x=0;for(const v of b===$?[$]:[b,$]){if(v+R<w-1e-4)continue;let H=S,B=X(Y({l:y,c:v,h:S}),l);if(B>=w)return B;const N=g(y,v);for(let T=0;T<N.length;T+=1){const M=X(N[T],l);if(M>=w)return M;M>B&&(B=M,H=T*Z)}let P=H;for(let T=H-Z;T<=H+Z;T+=.1){const M=X(Y({l:y,c:v,h:T}),l);if(M>=w)return M;M>B&&(B=M,P=T)}if(B>=A)for(let T=P-.1;T<=P+.1;T+=.001){const M=X(Y({l:y,c:v,h:T}),l);if(M>=w)return M;M>B&&(B=M)}B>x&&(x=B)}return x},s=(l,y,b)=>{const $=y===b?[b]:[y,b];let R=0;for(const S of $)for(const x of $){if(S+x<w-1e-4)continue;const v=g(l,S),H=g(l,x);let B=0,N=0,P=-1;for(let L=0;L<v.length;L+=1)for(let F=0;F<H.length;F+=1){const z=X(v[L],H[F]);if(z>=w)return z;z>P&&(P=z,B=L*Z,N=F*Z)}const T=(L,F,z,Te)=>{const Be=[];for(let G=F-z;G<=F+z;G+=Te)Be.push({h:G,lab:Y({l,c:x,h:G})});let pe=-1,we=L,Ee=F;for(let G=L-z;G<=L+z;G+=Te){const ot=Y({l,c:S,h:G});for(const _e of Be){const Ie=X(ot,_e.lab);Ie>pe&&(pe=Ie,we=G,Ee=_e.h)}}return{distance:pe,hueA:we,hueB:Ee}},M=T(B,N,Z,.1);if(M.distance>=w)return M.distance;let I=Math.max(P,M.distance);if(I>=A){const L=T(M.hueA,M.hueB,.1,.001);if(L.distance>I&&(I=L.distance),I>=w)return I}I>R&&(R=I)}return R},r=l=>ee.includes(l),i=(l,y,b,$,R)=>{const S=Y(f(l.base,b,$,R)),x=Y(f(y.base,b,$,R));if(X(S,x)>=w)return{outcome:"direct"};const v=r(l.name)&&r(y.name),H=[l,y].filter(P=>r(P.name)),B=v?"one of them":`"${r(l.name)?l.name:y.name}"`;let N;for(const P of H){const T=E(P===l?x:S,b,$,R);if(T>=w)return{outcome:"retune",target:B};T>=w-Qe&&(N=N??B)}if(v){const P=s(b,$,R);if(P>=w)return{outcome:"retune",target:"both"};P>=w-Qe&&(N=N??"both")}return N!==void 0?{outcome:"near",target:N}:{outcome:"no"}},u=l=>l.outcome==="direct"?4:l.outcome==="retune"?l.target==="both"?2:3:l.outcome==="near"?1:0,m=l=>[...l].sort().join("|"),h=new Set(Le(p.map(({name:l,base:y})=>[l,ie(f(y,c,0,J))]),w,["normal"]).map(l=>m(l.pair))),_=new Map(p.map(l=>[l.name,l])),D=new Map;for(const{pair:l,distance:y}of O){const b=_.get(l[0]),$=_.get(l[1]),R=r(b.name)&&r($.name),S=i(b,$,c,a.min,a.max);if(S.outcome==="direct"||S.outcome==="retune"){const I=S.target??(R?"one of them":`"${r(b.name)?b.name:$.name}"`);h.has(m(l))?d.push(`Status colors "${l[0]}" and "${l[1]}" are hard to distinguish under normal vision (distance ${Ze(y)} < ${w}); status meanings must stay distinguishable \u2014 retune ${I} via intents.`):d.push(`Status colors "${l[0]}" and "${l[1]}" are hard to distinguish as rendered (distance ${Ze(y)} < ${w}); the actionBackground chroma band ({ min: ${a.min}, max: ${a.max} }) pushes them together \u2014 widen the band, or retune ${I} via intents.`);continue}const x=i(b,$,c,0,J);let v={outcome:"no"};for(let I=1;I<=19&&v.outcome!=="direct";I+=1){const L=I*.05;if(Math.abs(L-c)<.001)continue;const F=i(b,$,L,a.min,a.max);u(F)>u(v)&&(v=F)}let H={outcome:"no"};const B=x.outcome==="direct"||x.outcome==="retune",N=v.outcome==="direct"||v.outcome==="retune";if(!B&&!N)for(let I=1;I<=19&&H.outcome!=="direct";I+=1){const L=i(b,$,I*.05,0,J);u(L)>u(H)&&(H=L)}const P=jt(x,v,H),T=`${S.outcome}|${P}`;let M=D.get(T);M||(M=[],D.set(T,M)),M.push(l)}for(const[l,y]of D){const b=l.indexOf("|"),$=l.slice(0,b),R=l.slice(b+1),S=y.map(([H,B])=>`"${H}"/"${B}"`),x=S.length>1?`${S.slice(0,-1).join(", ")} and ${S[S.length-1]}`:S[0],v=$==="near"?`Status colors ${x} may not separate by retuning as rendered: at the action stop's lightness (${c}) with the actionBackground chroma band ({ min: ${a.min}, max: ${a.max} }), the closest reachable retune found comes within the audit's search margin of the ${w} threshold`:`Status colors ${x} cannot be separated as rendered: at the action stop's lightness (${c}) with the actionBackground chroma band ({ min: ${a.min}, max: ${a.max} }), no intent retuning can pull ${y.length>1?"these pairs":"the pair"} ${w} apart`;d.push(v+R)}return d}function jt(e,n,t){if(e.outcome==="direct"&&n.outcome==="direct")return" \u2014 widen the chroma band or move the action lightness stop.";if(e.outcome==="direct")return n.outcome==="no"?"; no other action lightness stop can help while the band holds \u2014 widen the chroma band.":" \u2014 widen the chroma band.";if(n.outcome==="direct")return e.outcome==="no"?"; no wider chroma band can help at this lightness \u2014 move the action lightness stop.":" \u2014 move the action lightness stop.";if(e.outcome==="retune"&&n.outcome==="retune")return` \u2014 widen the chroma band or move the action lightness stop, then retune ${e.target} via intents.`;if(e.outcome==="retune")return n.outcome==="no"?`; no other action lightness stop can help while the band holds \u2014 widen the chroma band, then retune ${e.target} via intents.`:` \u2014 widen the chroma band, then retune ${e.target} via intents.`;if(n.outcome==="retune")return e.outcome==="no"?`; no wider chroma band can help at this lightness \u2014 move the action lightness stop, then retune ${n.target} via intents.`:` \u2014 move the action lightness stop, then retune ${n.target} via intents.`;const o=e.outcome==="no"&&n.outcome==="no"?"; neither a wider chroma band alone nor another stop alone can help":"; neither a wider chroma band alone nor another stop alone provably helps";return t.outcome==="direct"?`${o} \u2014 change the band and the stop together.`:t.outcome==="retune"?`${o} \u2014 change the band and the stop together, then retune ${t.target} via intents.`:"; no explored band or stop change provably separates this pair."}function Xt(e,n){const t=Oe(e),o=Oe(n),a=t.error!==void 0?{valid:!1,errors:[t.error],warnings:[]}:Re(t.value),c=o.error!==void 0?{valid:!1,errors:[o.error],warnings:[]}:Re(o.value,je),f=[...a.errors.map(d=>`light: ${d}`),...c.errors.map(d=>`dark: ${d}`)],p=[...a.warnings.map(d=>`light: ${d}`),...c.warnings.map(d=>`dark: ${d}`)];if(a.valid&&c.valid){const d=new Set(Object.keys(t.value?.contexts??{})),O=new Set(Object.keys(o.value?.contexts??{})),k=[...O].filter(A=>!d.has(A)).sort(),g=[...d].filter(A=>!O.has(A)).sort();(k.length>0||g.length>0)&&f.push(`contexts must declare the same names in both schemes. Missing from light: ${k.join(", ")||"(none)"}. Missing from dark: ${g.join(", ")||"(none)"}.`)}return{valid:f.length===0,errors:f,warnings:p}}const Kt=["anchors","angles","contexts","dataPalette","dataRamps","intents","roles","chroma","lightness","semanticHues","semanticMappings","variantChroma"];function Ut(e,n){const t={};for(const[o,a]of Object.entries(e))C(o)&&(t[o]=a);for(const[o,a]of Object.entries(n))a!==void 0&&C(o)&&(t[o]=a);for(const o of Kt){const a=e[o],c=n[o];if(a&&typeof a=="object"&&!Array.isArray(a)&&c&&typeof c=="object"&&!Array.isArray(c)){if(o==="anchors"){t[o]=Ue(a,c);continue}if(o==="contexts"){t[o]=_t(a,c);continue}if(o==="dataRamps"){t[o]=Ve(a,c);continue}const f={};for(const[p,d]of Object.entries(a))C(p)&&(f[p]=d);for(const[p,d]of Object.entries(c))d!==void 0&&C(p)&&(f[p]=d);t[o]=f}}return t}function Zt(...e){let n={};for(const t of e){if(!t)continue;const o=Tt(t);o&&(n=Ut(n,o))}return Ce(n)}function tt(e){return e.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r")}const nt={borderRadius:.1,fontBody:'"Noto Sans", sans-serif',fontWeightBody:"normal",fontHeadings:'"Inika", serif',fontWeightHeadings:"normal",fontBrand:'"Inika", serif',fontWeightBrand:"normal",fontMonospace:'"JetBrains Mono", monospace',fontWeightMonospace:"normal",seedColor:"oklch(.07 0.15 216)"};function Qt(e){return Ce({...nt,pageBackgroundImage:`url("${tt(e)}")`,pageBackgroundImageOpacity:.5})}function en(e){return Ce({...nt,pageBackgroundImage:`url("${tt(e)}")`,pageBackgroundImageOpacity:.55})}export{V as ANCHOR_SLUG_PATTERN,Se as COLOR_SOURCES,Ot as DEFAULT_DARK_LIGHTNESS,je as DEFAULT_DARK_THEME,Ct as DEFAULT_LIGHT_LIGHTNESS,ve as DEFAULT_LIGHT_THEME,ke as DEFAULT_SEMANTIC_MAPPINGS,j as LIGHTNESS_KEYS,Kt as NESTED_THEME_KEYS,oe as ROLE_NAMES,bt as ROLE_PAIRED_INK,xe as ROLE_TOKEN_PLAN,Q as SEMANTIC_COLOR_NAMES,ee as STATUS_COLOR_SOURCES,We as TOKEN_PAIRINGS,Mt as VARIANT_COLOR_SOURCES,en as buildTaprootDarkTheme,Qt as buildTaprootLightTheme,Ne as compileRoles,Ce as encodeTheme,Dt as explicitMappingTokens,Zt as layerThemes,Ut as mergePartials,le as mergeTheme,$e as parseAnchorSource,Tt as parseTheme,Pe as resolveAnchorColor,Xe as resolveContextTheme,ht as resolveDataColorSource,Yt as semanticToCSS,qt as validateTheme,Xt as validateThemePair};
1
+ import{UNSAFE_KEYS as ut,isSafeKey as A}from"./unsafe-keys.js";import{ACCEPTED_COLOR_FORMS as oe,apcaContrast as ft,clampChannel as de,deltaEOK as lt,deriveSemantic as ae,gamutMapToSRGB as he,linearRgbToOklab as pt,oklchToSRGB as mt,parseCssColor as W,parseOklch as dt,serializeOklch as ce,srgbToLinearRGB as ht}from"./color-engine.js";import{computeVariants as ge,parseAnchorSource as gt,resolveAnchorColor as yt,resolveMappingSource as ue}from"./variant-engine.js";import{auditColorDistances as De,auditDataPalette as bt,DATA_SERIES_KEYS as fe,DEFAULT_DATA_PALETTE as ye,DEFAULT_DATA_RAMP_STEPS as $t,MAX_DATA_RAMP_STEPS as be,MIN_DATA_RAMP_LIGHTNESS_STEP as Fe,MIN_DATA_RAMP_STEPS as $e}from"./data-colors.js";const j=/^[a-z][a-z0-9-]*$/;function xe(e){return gt(e)}function We(e,n){return yt(e,n)}function xt(e,n){const t=xe(e);if(t){const o=We(n,t);return o&&W(o)?o:null}return W(e)?e:null}const se=["canvas","ink","accent","action","structure"],Se={canvas:{color:[["background","surface"],["layer1","raised1"],["layer2","raised2"],["layer3","raised3"],["layer4","raised4"]]},ink:{color:[["text","text"],["typeOverline","text"],["typeLabel","text"],["typeLead","text"],["typeCaption","text"],["inputSelection","text"]],heading:[["headings","muted"],["headingsHover","ink"]]},accent:{color:[["linkHoverBg","raised2"],["inputSelectionBg","raised2"],["inputCaret","ink"]],text:[["link","accent"]],hover:[["linkHover","text"]]},action:{color:[["actionBackground","muted"]],ink:[["actionText","surface"]]},structure:{color:[["border","border"],["shadow","shadow"]]}},St=new Set(["action.color"]),kt={canvas:"background",ink:"text",accent:"link",action:"actionBackground",structure:"border"},At={"action.ink":{role:"canvas",slot:"color"}},Mt=.45,Ot=.045;function je(e,n,t){const o=Ue.actionText,r=[...U].sort((y,M)=>e[y]-e[M]||U.indexOf(y)-U.indexOf(M)),c=n?r.filter(y=>n(y)>=o.targetLc):r,f=t?c.filter(y=>t(y)>=Ot):c,m=f.length>0?f:c,d=m.length>0?m:r;let R,k=1/0;for(const y of d){const M=Math.abs(e[y]-Mt);M<k&&(k=M,R=y)}return R??"muted"}function vt(e,n,t){const o=Me(n,t,e)>.5;let r=U[0];for(const c of U)(o?n[c]<n[r]:n[c]>n[r])&&(r=c);return r}const Ct={"accent.hover":"text"};function Rt(e,n){const t=new Map;try{const o=pe(n,e),r=(c,f,m,d)=>{const R=xe(f),k=R?We(d,R):null,y=k===null?null:W(k),M=t.get(c)??[];M.some(_=>_.source===f&&_.chroma===y?.c)||(M.push({source:f,where:m,chroma:y?.c}),t.set(c,M))};for(const[c,f]of Object.entries(o.semanticMappings))f.source.startsWith("anchor:")&&r(c,f.source,"the root table",o.anchors);for(const c of Object.keys(o.contexts)){const f=nt(o,c);if(f)for(const[m,d]of Object.entries(f.semanticMappings))d.source.startsWith("anchor:")&&r(m,d.source,`contexts.${c}`,f.anchors)}}catch{return new Map}return t}function q(e,n){if(e===void 0)return;if(typeof e=="string")return n==="color"?e:void 0;const t=e[n];return typeof t=="string"?t:void 0}function Ke(e,n,t,o={}){const r={},c={};for(const[f,m]of Object.entries(o.explicitMappings??{}))m!==void 0&&(c[f]=m.lightness);for(const f of se){const m=e[f];if(m!==void 0)for(const[d,R]of Object.entries(Se[f])){const k=At[`${f}.${d}`];let y=q(m,d),M=!1;if(y===void 0){k&&(y=q(e[k.role],k.slot),y??=t[kt[k.role]]?.source,M=y!==void 0);const _=Ct[`${f}.${d}`];_!==void 0&&(y??=q(m,_)),y??=q(m,"color")}if(y!==void 0)for(const[_,K]of R){let N=K;if(St.has(`${f}.${d}`))N=je(n,o.actionSurfaceContrast?s=>o.actionSurfaceContrast(y,s):void 0,o.actionSurfaceSeparation?s=>o.actionSurfaceSeparation(y,s):void 0);else if(M&&k){const s=Ue[_],i=(s&&c[s.bg])??je(n,o.actionSurfaceContrast?a=>o.actionSurfaceContrast(q(m,"color"),a):void 0,o.actionSurfaceSeparation?a=>o.actionSurfaceSeparation(q(m,"color"),a):void 0);N=vt(i,n,o.tones??{})}r[_]={source:y,lightness:N}}}}return r}const Ue={text:{bg:"background",targetLc:75},typeOverline:{bg:"background",targetLc:90},typeLabel:{bg:"background",targetLc:75},typeLead:{bg:"background",targetLc:75},typeCaption:{bg:"background",targetLc:90},dangerText:{bg:"background",targetLc:75},headings:{bg:"background",targetLc:60},headingsHover:{bg:"background",targetLc:60},link:{bg:"background",targetLc:75},linkHover:{bg:"linkHoverBg",targetLc:75},actionText:{bg:"actionBackground",targetLc:75},inputCaret:{bg:"layer2",targetLc:60},inputSelection:{bg:"inputSelectionBg",targetLc:60}},Q=["background","layer1","layer2","layer3","layer4","actionBackground","actionText","border","shadow","text","typeOverline","typeLabel","typeLead","typeCaption","dangerText","headings","headingsHover","link","linkHover","linkHoverBg","inputCaret","inputSelection","inputSelectionBg"],Tt=["typeOverline","typeLabel","typeLead","typeCaption"],ke=["primary","analogous-left","analogous-right","complementary","split-complementary-left","split-complementary-right","triadic-left","triadic-right","danger","success","warning","info"],ee=["danger","success","warning","info"],wt=["analogous-left","analogous-right","complementary","split-complementary-left","split-complementary-right","triadic-left","triadic-right","danger","success","warning","info"],U=["surface","raised1","raised2","raised3","raised4","accent","muted","text","border","ink","shadow"],ze="tone:";function Ae(e){if(typeof e!="string"||!e.startsWith(ze))return null;const n=e.slice(ze.length);return A(n)&&j.test(n)?n:null}function te(e){return typeof e!="string"?!1:U.includes(e)}function Me(e,n,t){if(te(t))return e[t];const o=Ae(t);return o===null?e.surface:n?.[o]??e.surface}const Bt={surface:.99,raised1:.97,raised2:.9,raised3:.8,raised4:.72,accent:.5,muted:.45,text:.35,border:.3,ink:.25,shadow:.2},Et={surface:.15,raised1:.2,raised2:.3,raised3:.4,raised4:.36,accent:.6,muted:.75,text:.85,border:.7,ink:.95,shadow:.6},Oe={background:{source:"primary",lightness:"surface"},layer1:{source:"primary",lightness:"raised1"},layer2:{source:"primary",lightness:"raised2"},layer3:{source:"primary",lightness:"raised3"},layer4:{source:"primary",lightness:"raised4"},actionBackground:{source:"primary",lightness:"raised3"},actionText:{source:"primary",lightness:"ink"},border:{source:"primary",lightness:"border"},shadow:{source:"primary",lightness:"shadow"},text:{source:"primary",lightness:"text"},typeOverline:{source:"primary",lightness:"text"},typeLabel:{source:"primary",lightness:"text"},typeLead:{source:"primary",lightness:"text"},typeCaption:{source:"primary",lightness:"text"},dangerText:{source:"danger",lightness:"text"},headings:{source:"primary",lightness:"muted"},headingsHover:{source:"primary",lightness:"text"},link:{source:"complementary",lightness:"accent"},linkHover:{source:"complementary",lightness:"text"},linkHoverBg:{source:"triadic-left",lightness:"raised2"},inputCaret:{source:"triadic-right",lightness:"ink"},inputSelection:{source:"complementary",lightness:"text"},inputSelectionBg:{source:"triadic-left",lightness:"raised2"}},J=.4;function ve(){const e={};for(const n of Q)e[n]={min:0,max:J};return e}const Ge={seedColor:"oklch(0.7 0.125 216)",fontBody:"",fontHeadings:"",fontBrand:"",fontMonospace:"",fontWeightBody:"normal",fontWeightHeadings:"bold",fontWeightBrand:"bold",fontWeightMonospace:"normal",stylesheets:[],rootFontSize:16,typeRatio:1.25,spaceRatio:1.618,borderRadius:.2,viewportMin:320,viewportMax:1200,angles:{analogous:30,complementary:180,splitComplementary:30,triadic:120},semanticHues:{danger:27,success:150,warning:90,info:244},variantChroma:{},intents:{},tones:{},chroma:ve(),semanticMappings:{...Oe},anchors:{},roles:{},contexts:{},dataPalette:{...ye},dataRamps:{}},Ce={...Ge,chroma:ve(),semanticMappings:{...Oe},anchors:{},roles:{},contexts:{},tones:{},dataPalette:{...ye},dataRamps:{},lightness:{...Bt}},Ve={...Ge,chroma:ve(),semanticMappings:{...Oe},anchors:{},roles:{},contexts:{},tones:{},dataPalette:{...ye},dataRamps:{},lightness:{...Et}},le=new WeakMap;le.set(Ce,new Set),le.set(Ve,new Set);function _t(e,n){return ut.has(e)?void 0:n}function nn(e){return`--esp-color-${e.replace(/([A-Z])/g,"-$1").replace(/(\d)/g,"-$1").toLowerCase()}`}function Lt(e){try{const n=Je(e),t=JSON.parse(n,_t);return typeof t!="object"||t===null||Array.isArray(t)?null:t}catch{return null}}const Re="\xEF\xBB\xBF";function It(e){if(/^[\u0000-\u007f]*$/.test(e))return btoa(e);const n=new TextEncoder().encode(e);let t=Re;for(const o of n)t+=String.fromCharCode(o);return btoa(t)}function Je(e){const n=atob(e);if(!n.startsWith(Re))return n;const t=Uint8Array.from(n.slice(Re.length),o=>o.charCodeAt(0));return new TextDecoder("utf-8",{fatal:!0}).decode(t)}function Te(e){return It(JSON.stringify(e))}function Ht(e){const n={};for(const t of ee){const o=e[t];typeof o=="string"&&(n[t]=o.startsWith("anchor:")?o:re(o))}return n}function re(e){if(dt(e))return e;const n=W(e);return n?ce(n):e}function Ye(e,n){const t={};for(const[o,r]of Object.entries(e))A(o)&&(t[o]=r);if(!n)return t;for(const[o,r]of Object.entries(n)){if(r===void 0||!A(o))continue;const c=t[o];if(c!==void 0&&typeof r=="object"&&r!==null){const f={};if(typeof c=="string")f.color=c;else for(const[m,d]of Object.entries(c))A(m)&&(f[m]=d);for(const[m,d]of Object.entries(r))d!==void 0&&A(m)&&(f[m]=d);t[o]=f;continue}t[o]=r}return t}function we(e){const n={};if(!H(e))return n;for(const[t,o]of Object.entries(e))A(t)&&j.test(t)&&!te(t)&&typeof o=="number"&&Number.isFinite(o)&&o>=0&&o<=1&&(n[t]=o);return n}function Nt(e,n){return{...we(e),...we(n)}}function Pt(e,n){const t={...e};if(!H(n))return t;for(const o of U){const r=n[o];typeof r=="number"&&Number.isFinite(r)&&r>=0&&r<=1&&(t[o]=r)}return t}function qe(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;const n=e,t={};for(const o of se){const r=n[o];r!==void 0&&(t[o]=r)}if(typeof n.lightness=="object"&&n.lightness!==null&&!Array.isArray(n.lightness)){const o={};for(const r of U){const c=n.lightness[r];c!==void 0&&(o[r]=c)}t.lightness=o}if(n.tones!==void 0){const o=we(n.tones);Object.keys(o).length>0&&(t.tones=o)}if(typeof n.semanticMappings=="object"&&n.semanticMappings!==null&&!Array.isArray(n.semanticMappings)){const o={};for(const[r,c]of Object.entries(n.semanticMappings)){if(!A(r)||!Q.includes(r)||typeof c!="object"||c===null||Array.isArray(c))continue;const{source:f,lightness:m}=c;typeof f=="string"&&(typeof m!="string"||!te(m)&&Ae(m)===null||(o[r]={source:f,lightness:m}))}Object.keys(o).length>0&&(t.semanticMappings=o)}return t}function Dt(e,n){const t={};for(const[o,r]of Object.entries(e)){if(!A(o)||!j.test(o))continue;const c=qe(r);c&&(t[o]=c)}if(!n)return t;for(const[o,r]of Object.entries(n)){if(!A(o)||!j.test(o)||r===void 0)continue;const c=qe(r);if(!c)continue;const f=t[o];t[o]={...f??{},...c,...f?.lightness||c.lightness?{lightness:{...f?.lightness,...c.lightness}}:{},...f?.tones||c.tones?{tones:{...f?.tones,...c.tones}}:{},...f?.semanticMappings||c.semanticMappings?{semanticMappings:{...f?.semanticMappings,...c.semanticMappings}}:{}}}return t}function H(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function F(e){const n={};for(const[t,o]of Object.entries(e))o!==void 0&&A(t)&&(n[t]=o);return n}function Xe(e){if(!H(e))return e;const n=F(e);return H(e.lightness)&&(n.lightness=F(e.lightness)),H(e.tones)&&(n.tones=F(e.tones)),n}function Ft(e,n){const t={};for(const[o,r]of Object.entries(e))A(o)&&(t[o]=Xe(r));if(!n)return t;for(const[o,r]of Object.entries(n)){if(!A(o)||r===void 0)continue;const c=t[o];if(!H(c)||!H(r)){t[o]=Xe(r);continue}const f={...F(c),...F(r)};H(c.lightness)&&H(r.lightness)?f.lightness={...F(c.lightness),...F(r.lightness)}:H(r.lightness)&&(f.lightness=F(r.lightness)),H(c.tones)&&H(r.tones)?f.tones={...F(c.tones),...F(r.tones)}:H(r.tones)&&(f.tones=F(r.tones)),H(c.semanticMappings)&&H(r.semanticMappings)?f.semanticMappings={...F(c.semanticMappings),...F(r.semanticMappings)}:H(r.semanticMappings)&&(f.semanticMappings=F(r.semanticMappings)),t[o]=f}return t}function Wt(e){const n={};for(const[t,o]of Object.entries(e)){if(!A(t))continue;if(typeof o=="string"){n[t]=re(o);continue}const r={};for(const[c,f]of Object.entries(o))A(c)&&(r[c]=typeof f=="string"?re(f):f);n[t]=r}return n}function ie(e){return typeof e!="string"?"":e.startsWith("anchor:")?e:re(e)}function jt(e){const n={};for(const t of fe)n[t]=ie(e[t]);return n}function Ze(e,n){const t={};for(const[o,r]of Object.entries(e))A(o)&&j.test(o)&&r&&typeof r=="object"&&!Array.isArray(r)&&(t[o]={...r});if(!n)return t;for(const[o,r]of Object.entries(n)){if(!A(o)||!j.test(o)||!r||typeof r!="object"||Array.isArray(r))continue;const c=t[o],f=c!==void 0&&r.type!==void 0&&r.type!==c.type;t[o]={...f?{}:c??{},...r}}return t}function Kt(e){const n={};for(const[t,o]of Object.entries(e))if(A(t)){if(o.type==="sequential"&&typeof o.source=="string"){n[t]={...o,source:ie(o.source)};continue}if(o.type==="diverging"&&typeof o.start=="string"&&typeof o.end=="string"){n[t]={...o,start:ie(o.start),end:ie(o.end),...typeof o.neutral=="string"?{neutral:ie(o.neutral)}:{}};continue}n[t]={...o}}return n}function Qe(e){const n=W(e.seedColor);if(!n)return;const t=ge(n,e);return(o,r)=>{const c=ue(o,t,e.anchors)??t.primary,f=e.chroma.actionBackground,m=ae(c,e.lightness[r],f.min,f.max),d={l:m.l>.5?0:1,c:0,h:0};return Math.abs(ft(d,m))}}function Ut(e,n,t){if(n?.background)return n.background;const o=q(e.canvas,"color");if(o!==void 0){const r=Se.canvas.color?.find(([c])=>c==="background");if(r)return{source:o,lightness:r[1]}}return t.background}function et(e,n){const t=W(e.seedColor);if(!t||!n)return;const o=ge(t,e),r=ue(n.source,o,e.anchors)??o.primary,c=e.chroma.background,f=ae(r,Me(e.lightness,e.tones,n.lightness),c.min,c.max);return(m,d)=>{const R=ue(m,o,e.anchors)??o.primary,k=e.chroma.actionBackground,y=ae(R,e.lightness[d],k.min,k.max);return lt(y,f)}}function zt(e){const n=le.get(e);if(n)return new Set(n);const t=new Set,o=Ke(e.roles,e.lightness,e.semanticMappings,{tones:e.tones,actionSurfaceContrast:Qe(e),actionSurfaceSeparation:et(e,e.semanticMappings.background)});for(const[r,c]of Object.entries(o)){const f=e.semanticMappings[r];(f.source!==c.source||f.lightness!==c.lightness)&&t.add(r)}return t}function pe(e,n){const t={...e.roles,...n.roles},o=Dt(e.contexts,n.contexts),r=Pt(e.lightness,n.lightness),c=Nt(e.tones,n.tones),f=Wt(Ye(e.anchors,n.anchors)),m=typeof n.seedColor=="string"?re(n.seedColor):e.seedColor,d=n.semanticMappings,R={...e.angles,...n.angles},k={...e.semanticHues,...n.semanticHues},y={...e.variantChroma,...n.variantChroma},M=Ht({...e.intents,...n.intents}),_=n.chroma,K=ne(e.chroma,_),N=jt(ne(e.dataPalette,n.dataPalette)),s=Kt(Ze(e.dataRamps,n.dataRamps)),i=zt(e),a=new Set(i),u={};for(const x of i)u[x]=e.semanticMappings[x];for(const[x,T]of Object.entries(d??{}))T!==void 0&&(a.add(x),u[x]=T);const p=ne(e.semanticMappings,d),h={angles:R,anchors:f,chroma:K,intents:M,lightness:r,tones:c,seedColor:m,semanticHues:k,variantChroma:y},l=Qe(h),g=et(h,Ut(t,u,p)),$=Ke(t,r,p,{explicitMappings:u,tones:c,actionSurfaceContrast:l,actionSurfaceSeparation:g}),b={};for(const[x,T]of Object.entries($))a.has(x)||(b[x]=T);const C={};for(const x of Tt)!a.has(x)&&b[x]===void 0&&(C[x]={source:p.text.source,lightness:p.text.lightness},_?.text!==void 0&&_[x]===void 0&&(K[x]={...K.text}));const O=ne(ne(ne(e.semanticMappings,C),b),d),S={...e,seedColor:m,fontBody:n.fontBody??e.fontBody,fontHeadings:n.fontHeadings??e.fontHeadings,fontBrand:n.fontBrand??e.fontBrand,fontMonospace:n.fontMonospace??e.fontMonospace,fontWeightBody:String(n.fontWeightBody??e.fontWeightBody),fontWeightHeadings:String(n.fontWeightHeadings??e.fontWeightHeadings),fontWeightBrand:String(n.fontWeightBrand??e.fontWeightBrand),fontWeightMonospace:String(n.fontWeightMonospace??e.fontWeightMonospace),stylesheets:n.stylesheets??[...e.stylesheets],rootFontSize:n.rootFontSize??e.rootFontSize,typeRatio:n.typeRatio??e.typeRatio,spaceRatio:n.spaceRatio??e.spaceRatio,borderRadius:n.borderRadius??e.borderRadius,viewportMin:n.viewportMin??e.viewportMin,viewportMax:n.viewportMax??e.viewportMax,angles:R,semanticHues:k,variantChroma:y,intents:M,lightness:r,tones:c,chroma:K,semanticMappings:O,anchors:f,roles:t,contexts:o,dataPalette:N,dataRamps:s};return n.pageBackgroundImage!==void 0&&(S.pageBackgroundImage=n.pageBackgroundImage),n.pageBackgroundImageOpacity!==void 0&&(S.pageBackgroundImageOpacity=n.pageBackgroundImageOpacity),n.boxBackgroundImage!==void 0&&(S.boxBackgroundImage=n.boxBackgroundImage),n.boxBackgroundImageOpacity!==void 0&&(S.boxBackgroundImageOpacity=n.boxBackgroundImageOpacity),n.vellumOpacity!==void 0&&(S.vellumOpacity=n.vellumOpacity),n.vellumBackgroundImage!==void 0&&(S.vellumBackgroundImage=n.vellumBackgroundImage),n.vellumBackgroundImageOpacity!==void 0&&(S.vellumBackgroundImageOpacity=n.vellumBackgroundImageOpacity),le.set(S,new Set(a)),S}const tt=new WeakMap;function nt(e,n){const t=e.contexts&&Object.prototype.hasOwnProperty.call(e.contexts,n)?e.contexts[n]:void 0;if(!t)return null;let o=tt.get(e);const r=o?.get(n);if(r)return r;const c={};for(const m of se){const d=t[m];d!==void 0&&(c[m]=d)}const f=pe(e,{lightness:t.lightness,tones:t.tones,roles:c,semanticMappings:t.semanticMappings});return o||(o=new Map,tt.set(e,o)),o.set(n,f),f}function ne(e,n){if(!n)return{...e};const t={...e};for(const o of Object.keys(n)){const r=n[o];r!==void 0&&(t[o]=r)}return t}function Be(e){let n;try{n=Je(e)}catch{return{error:"Failed to decode Base64 string."}}let t;try{t=JSON.parse(n)}catch{return{error:"Decoded string is not valid JSON."}}return typeof t!="object"||t===null||Array.isArray(t)?{error:"Theme must be a JSON object."}:{value:t}}function on(e){const n=Be(e);return n.error!==void 0?{valid:!1,errors:[n.error],warnings:[]}:Ee(n.value)}function Ee(e,n=Ce){const t=[],o=[];"seedColor"in e&&(typeof e.seedColor!="string"?t.push("seedColor must be a string."):W(e.seedColor)||t.push(`seedColor is not a valid CSS color: "${e.seedColor}". Accepted forms: ${oe}.`));for(const s of["fontBody","fontHeadings","fontBrand","fontMonospace"])s in e&&typeof e[s]!="string"&&t.push(`${s} must be a string.`);const r=["normal","bold","lighter","bolder"],c=["inherit","initial","unset","revert","revert-layer"];for(const s of["fontWeightBody","fontWeightHeadings","fontWeightBrand","fontWeightMonospace"])if(s in e){const i=e[s];if(typeof i=="number")(i<1||i>1e3)&&t.push(`${s} numeric value must be 1\u20131000 (got ${i}).`);else if(typeof i=="string"){const a=r.includes(i)||c.includes(i);if(/^\d+$/.test(i)){const p=Number(i);(p<1||p>1e3)&&t.push(`${s} numeric value must be 1\u20131000 (got "${i}").`)}else a||o.push(`${s} = "${i}" is not a standard font-weight value.`)}else t.push(`${s} must be a string or number.`)}"stylesheets"in e&&(Array.isArray(e.stylesheets)?e.stylesheets.some(s=>typeof s!="string")&&t.push("Every entry in stylesheets must be a string."):t.push("stylesheets must be an array of strings."));const f=[["rootFontSize",1,100],["borderRadius",0,10],["viewportMin",100,1e4],["viewportMax",200,1e4]];for(const[s,i,a]of f)if(s in e)if(typeof e[s]!="number"||!isFinite(e[s]))t.push(`${s} must be a finite number.`);else{const u=e[s];i!==void 0&&u<i&&t.push(`${s} must be \u2265 ${i} (got ${u}).`),a!==void 0&&u>a&&o.push(`${s} = ${u} is unusually high (max ${a}).`)}for(const s of["typeRatio","spaceRatio"])if(s in e)if(typeof e[s]!="number"||!isFinite(e[s]))t.push(`${s} must be a finite number.`);else{const i=e[s];i<=1&&t.push(`${s} must be > 1 (got ${i}).`);const a=s==="typeRatio"?1.3:2;i>a&&o.push(`${s} = ${i} is very high (max ${a}); scales may be extreme.`)}if("viewportMin"in e&&"viewportMax"in e){const s=e.viewportMin,i=e.viewportMax;typeof s=="number"&&typeof i=="number"&&s>=i&&t.push(`viewportMin (${s}) must be less than viewportMax (${i}).`)}if("angles"in e)if(typeof e.angles!="object"||e.angles===null)t.push("angles must be an object.");else{const s=e.angles;for(const i of["analogous","complementary","splitComplementary","triadic"])if(i in s)if(typeof s[i]!="number"||!isFinite(s[i]))t.push(`angles.${i} must be a finite number.`);else{const a=s[i];(a<0||a>360)&&o.push(`angles.${i} = ${a} is outside 0\u2013360.`)}}if("semanticHues"in e)if(typeof e.semanticHues!="object"||e.semanticHues===null)t.push("semanticHues must be an object.");else{const s=e.semanticHues;for(const i of["danger","success","warning","info"])if(i in s)if(typeof s[i]!="number"||!isFinite(s[i]))t.push(`semanticHues.${i} must be a finite number.`);else{const a=s[i];(a<0||a>360)&&o.push(`semanticHues.${i} = ${a} is outside 0\u2013360.`)}}if("variantChroma"in e)if(typeof e.variantChroma!="object"||e.variantChroma===null)t.push("variantChroma must be an object.");else{const s=e.variantChroma;for(const i of wt)if(i in s)if(typeof s[i]!="number"||!isFinite(s[i]))t.push(`variantChroma["${i}"] must be a finite number.`);else{const a=s[i];a<0&&t.push(`variantChroma["${i}"] must be \u2265 0 (got ${a}).`),a>J&&o.push(`variantChroma["${i}"] = ${a} exceeds ${J}.`)}}function m(s,i){if(typeof s!="object"||s===null||Array.isArray(s)){t.push(`${i} must be an object.`);return}const a=s;for(const u of Object.keys(a))(!A(u)||!te(u))&&t.push(`${i}.${u} is not a built-in lightness stop; expected one of: ${U.join(", ")}.`);for(const u of U)if(u in a)if(typeof a[u]!="number"||!isFinite(a[u]))t.push(`${i}.${u} must be a finite number.`);else{const p=a[u];(p<0||p>1)&&t.push(`${i}.${u} must be 0\u20131 (got ${p}).`)}}"lightness"in e&&m(e.lightness,"lightness");function d(s,i){const a=new Set;if(!H(s))return t.push(`${i} must be an object.`),a;for(const[u,p]of Object.entries(s)){if(!A(u)||!j.test(u)){t.push(`${i}.${u} must be a lowercase slug matching ${j.source}.`);continue}if(te(u)){t.push(`${i}.${u} conflicts with the built-in "${u}" lightness stop; choose a distinct custom tone name.`);continue}a.add(u),typeof p!="number"||!Number.isFinite(p)?t.push(`${i}.${u} must be a finite number.`):(p<0||p>1)&&t.push(`${i}.${u} must be 0\u20131 (got ${p}).`)}return a}const R=new Set(Object.keys(n.tones??{}));if("tones"in e)for(const s of d(e.tones,"tones"))R.add(s);if("chroma"in e)if(typeof e.chroma!="object"||e.chroma===null)t.push("chroma must be an object.");else{const s=e.chroma,i=Rt(e,n);for(const a of Q)if(a in s){const u=s[a];if(typeof u!="object"||u===null){t.push(`chroma.${a} must be { min, max }.`);continue}const p=u;for(const l of i.get(a)??[])l.chroma!==void 0&&typeof p.min=="number"&&typeof p.max=="number"&&(l.chroma<p.min-1e-9||l.chroma>p.max+1e-9)&&o.push(`chroma.${a} clamps the anchor-sourced token ${a} ("${l.source}", effective in ${l.where}) and moves it off its declared swatch. Remove the band if that is unintended \u2014 anchors otherwise keep their own chroma.`);const h=u;(typeof h.min!="number"||h.min<0)&&t.push(`chroma.${a}.min must be \u2265 0.`),(typeof h.max!="number"||h.max<0||h.max>J)&&t.push(`chroma.${a}.max must be 0\u2013${J}.`),typeof h.min=="number"&&typeof h.max=="number"&&h.min>h.max&&t.push(`chroma.${a}.min (${h.min}) must be \u2264 max (${h.max}).`)}}if("anchors"in e)if(typeof e.anchors!="object"||e.anchors===null||Array.isArray(e.anchors))t.push("anchors must be an object of named colors.");else for(const[s,i]of Object.entries(e.anchors))if(A(s)?j.test(s)||t.push(`anchors: "${s}" is not a valid anchor name; use a lowercase slug (letters, digits, hyphens).`):t.push(`anchors: "${s}" is a reserved JavaScript property name and cannot be an anchor name; rename it.`),ke.includes(s)&&t.push(`anchors: "${s}" collides with a reserved color source name.`),typeof i=="string")W(i)||t.push(`anchors.${s} is not a valid CSS color: "${i}". Accepted forms: ${oe}.`);else if(typeof i=="object"&&i!==null&&!Array.isArray(i)){const a=i;typeof a.color!="string"&&t.push(`anchors.${s} must declare its base "color".`);for(const[u,p]of Object.entries(a))A(u)?u!=="color"&&!j.test(u)&&t.push(`anchors.${s}: "${u}" is not a valid slot name; use a lowercase slug.`):t.push(`anchors.${s}: "${u}" is a reserved JavaScript property name and cannot be a slot name; rename it.`),typeof p!="string"?t.push(`anchors.${s}.${u} must be a CSS color string.`):W(p)||t.push(`anchors.${s}.${u} is not a valid CSS color: "${p}". Accepted forms: ${oe}.`)}else t.push(`anchors.${s} must be a color string or a { color, \u2026slots } object.`);const k=new Map;if("anchors"in e&&typeof e.anchors=="object"&&e.anchors!==null&&!Array.isArray(e.anchors))for(const[s,i]of Object.entries(e.anchors))k.set(s,i);function y(s,i){const a=xe(s);if(!a){t.push(`${i} "${s}" is not valid anchor syntax; use anchor:<name> or anchor:<name>.<slot>.`);return}if(!k.has(a.name)){const u=[...k.keys()];t.push(`${i} references undeclared anchor "${a.name}". Declared anchors: ${u.length>0?u.join(", "):"(none)"}.`);return}if(a.slot!==void 0){const u=k.get(a.name);if(typeof(typeof u=="object"&&u!==null&&!Array.isArray(u)?u[a.slot]:void 0)!="string"){const h=typeof u=="object"&&u!==null?Object.keys(u).filter(l=>l!=="color"):[];t.push(`${i} references unknown slot "${a.slot}" on anchor "${a.name}". Declared slots: ${h.length>0?h.join(", "):"(none)"}.`)}}}function M(s,i){if(s.startsWith("anchor:")){y(s,i);return}ke.includes(s)||t.push(`${i} must be one of: ${ke.join(", ")}, or a declared anchor as anchor:<name> / anchor:<name>.<slot>.`)}if("intents"in e)if(typeof e.intents!="object"||e.intents===null||Array.isArray(e.intents))t.push("intents must be an object.");else for(const[s,i]of Object.entries(e.intents)){const a=`intents.${s}`;if(!ee.includes(s)){t.push(`${a} is not a status family; expected one of: ${ee.join(", ")}.`);continue}if(typeof i!="string"){t.push(`${a} must be a string (a CSS color or an anchor: reference).`);continue}if(i.startsWith("anchor:")){y(i,a);continue}W(i)||t.push(`${a} "${i}" is not a supported color form or anchor: reference. Accepted forms: ${oe}, or anchor:<name> / anchor:<name>.<slot>. An override retunes a family with a declared color; it never points one family at another \u2014 retune, never reassign.`)}function _(s,i){if(s.startsWith("anchor:")){y(s,i);return}W(s)||t.push(`${i} is not a valid CSS color: "${s}". Accepted forms: ${oe}, or a declared anchor as anchor:<name> / anchor:<name>.<slot>.`)}if("dataPalette"in e)if(typeof e.dataPalette!="object"||e.dataPalette===null||Array.isArray(e.dataPalette))t.push("dataPalette must be an object with series1\u2013series8 color values.");else{const s=e.dataPalette;for(const[i,a]of Object.entries(s)){if(!fe.includes(i)){t.push(`dataPalette.${i} is not a series slot; expected one of: ${fe.join(", ")}.`);continue}typeof a!="string"?t.push(`dataPalette.${i} must be a CSS color or anchor reference string.`):_(a,`dataPalette.${i}`)}}if("dataRamps"in e)if(typeof e.dataRamps!="object"||e.dataRamps===null||Array.isArray(e.dataRamps))t.push("dataRamps must be an object of named ramp declarations.");else for(const[s,i]of Object.entries(e.dataRamps)){if(A(s)?j.test(s)||t.push(`dataRamps: "${s}" is not a valid ramp name; use a lowercase slug (letters, digits, hyphens).`):t.push(`dataRamps: "${s}" is a reserved JavaScript property name and cannot be a ramp name; rename it.`),typeof i!="object"||i===null||Array.isArray(i)){t.push(`dataRamps.${s} must be a sequential or diverging ramp object.`);continue}const a=i,u=a.steps??$t;if((typeof u!="number"||!Number.isInteger(u)||u<$e||u>be)&&t.push(`dataRamps.${s}.steps must be an integer from ${$e} through ${be}.`),a.type==="sequential"){typeof a.source!="string"?t.push(`dataRamps.${s}.source must be a CSS color or anchor reference string.`):_(a.source,`dataRamps.${s}.source`);const p=a.lightnessStart??.95,h=a.lightnessEnd??.25;for(const[l,g]of[["lightnessStart",p],["lightnessEnd",h]])(typeof g!="number"||!Number.isFinite(g)||g<0||g>1)&&t.push(`dataRamps.${s}.${l} must be a finite number from 0 through 1.`);typeof p=="number"&&typeof h=="number"&&p<=h?t.push(`dataRamps.${s}.lightnessStart must be greater than lightnessEnd.`):typeof p=="number"&&Number.isFinite(p)&&typeof h=="number"&&Number.isFinite(h)&&typeof u=="number"&&Number.isInteger(u)&&u>=$e&&u<=be&&(p-h)/(u-1)<=Fe&&t.push(`dataRamps.${s} lightness bounds must leave more than ${Fe} lightness between serialized stops.`);continue}if(a.type==="diverging"){typeof u=="number"&&Number.isInteger(u)&&u%2===0&&t.push(`dataRamps.${s}.steps must be odd so the neutral is the exact midpoint.`);for(const p of["start","end"])typeof a[p]!="string"?t.push(`dataRamps.${s}.${p} must be a CSS color or anchor reference string.`):_(a[p],`dataRamps.${s}.${p}`);"neutral"in a&&(typeof a.neutral!="string"?t.push(`dataRamps.${s}.neutral must be a CSS color or anchor reference string.`):_(a.neutral,`dataRamps.${s}.neutral`));continue}t.push(`dataRamps.${s}.type must be "sequential" or "diverging".`)}if("dataPalette"in e&&t.length===0){const s=pe(Ce,e),i={};let a=!0;for(const u of fe){const p=xt(s.dataPalette[u],s.anchors);if(!p){a=!1;break}i[u]=p}if(a)for(const u of bt(i))o.push(`dataPalette.${u.series[0]} and dataPalette.${u.series[1]} are only ${u.distance.toFixed(4)} apart under ${u.simulation} simulation (minimum ${u.threshold.toFixed(4)}); also distinguish the series with labels, shapes, or patterns.`)}function K(s,i){if(typeof s!="object"||s===null||Array.isArray(s)){t.push(`${i} must be an object.`);return}const a=s;for(const[u,p]of Object.entries(a)){const h=`${i}.${u}`;if(!se.includes(u)){t.push(`${h} is not a role; expected one of: ${se.join(", ")}. (The status family is reserved and is not a role.)`);continue}const l=u,g=Se[l];if(typeof p=="string"){M(p,h);continue}if(typeof p!="object"||p===null||Array.isArray(p)){t.push(`${h} must be a color source or a { color, \u2026slots } object.`);continue}const $=p;typeof $.color!="string"&&t.push(`${h} must declare its base "color".`);for(const[b,C]of Object.entries($)){if(!Object.prototype.hasOwnProperty.call(g,b)||!A(b)){const O=Object.keys(g).filter(S=>S!=="color");t.push(`${h}.${b} is not a slot of the ${l} role. Declared slots: ${O.length>0?O.join(", "):"(none)"}.`);continue}typeof C!="string"?t.push(`${h}.${b} must be a color source string.`):M(C,`${h}.${b}`)}}}if("roles"in e&&K(e.roles,"roles"),"contexts"in e)if(typeof e.contexts!="object"||e.contexts===null||Array.isArray(e.contexts))t.push("contexts must be an object.");else for(const[s,i]of Object.entries(e.contexts)){if(!A(s)||!j.test(s)){t.push(`contexts.${s} must be a lowercase slug matching ${j.source}.`);continue}if(typeof i!="object"||i===null||Array.isArray(i)){K(i,`contexts.${s}`);continue}const a=i,u=Object.fromEntries(Object.entries(a).filter(([h])=>h!=="lightness"&&h!=="tones"&&h!=="semanticMappings"));K(u,`contexts.${s}`),"lightness"in a&&m(a.lightness,`contexts.${s}.lightness`);const p=new Set(R);if("tones"in a)for(const h of d(a.tones,`contexts.${s}.tones`))p.add(h);"semanticMappings"in a&&N(a.semanticMappings,`contexts.${s}.semanticMappings`,p)}function N(s,i,a){if(typeof s!="object"||s===null||Array.isArray(s)){t.push(`${i} must be an object.`);return}const u=s;for(const p of Object.keys(u))Q.includes(p)||t.push(`${i}.${p} is not a semantic token; expected one of: ${Q.join(", ")}.`);for(const p of Q)if(p in u){const h=u[p];if(typeof h!="object"||h===null){t.push(`${i}.${p} must be { source, lightness }.`);continue}const l=h;if(typeof l.source!="string"?t.push(`${i}.${p}.source must be a string.`):M(l.source,`${i}.${p}.source`),typeof l.lightness!="string")t.push(`${i}.${p}.lightness must be one of: ${U.join(", ")}, or a declared tone:<name>.`);else if(!te(l.lightness)){const g=Ae(l.lightness);if(g===null)t.push(`${i}.${p}.lightness must be one of: ${U.join(", ")}, or a declared tone:<name>.`);else if(!a.has(g)){const $=[...a].sort();t.push(`${i}.${p}.lightness references undeclared tone "${g}". Declared tones: ${$.length>0?$.join(", "):"(none)"}.`)}}}}"semanticMappings"in e&&N(e.semanticMappings,"semanticMappings",R);for(const s of["pageBackgroundImage","boxBackgroundImage","vellumBackgroundImage"])s in e&&typeof e[s]!="string"&&t.push(`${s} must be a string.`);for(const s of["pageBackgroundImageOpacity","boxBackgroundImageOpacity","vellumOpacity","vellumBackgroundImageOpacity"])if(s in e)if(typeof e[s]!="number"||!isFinite(e[s]))t.push(`${s} must be a finite number.`);else{const i=e[s];(i<0||i>1)&&t.push(`${s} must be 0\u20131 (got ${i}).`)}return o.push(...Gt(e,n)),{valid:t.length===0,errors:t,warnings:o}}const E=.045;function Gt(e,n){try{return Vt(e,n)}catch{return[]}}function Vt(e,n){const t=pe(n,e),o=rt(t),r=[...o],c=new Set(o);for(const f of Object.keys(t.contexts)){const m=nt(t,f);if(m)for(const d of rt(m))c.has(d)||r.push(`contexts.${f}: ${d}`)}return r}function Jt(e){const n=mt(he(e)),t=ht(n);return pt({r:de(t.r),g:de(t.g),b:de(t.b)})}function X(e,n){return Math.hypot(e.L-n.L,e.a-n.a,e.b-n.b)}function Y(e){return Jt(W(ce(he(e))))}function ot(e){return(Math.floor(e*1e4)/1e4).toFixed(4)}const Z=2,st=1e-4;function rt(e){const n=W(e.seedColor);if(!n)return[];const t=ge(n,e),o=e.semanticMappings.actionBackground,r=e.chroma.actionBackground,c=Me(e.lightness,e.tones,o.lightness),f=(l,g,$,b)=>he(ae(l,g,$,b)),m=ee.map(l=>({name:l,base:t[l]})),d=[];if(ee.includes(o.source))d.push(`actionBackground is sourced from the "${o.source}" status family: ordinary and ${o.source}-intent action surfaces render identically (their labels may still differ). Remap the action to a non-status source \u2014 retuning "${o.source}" via intents moves both colors together.`);else{const l=ue(o.source,t,e.anchors);l&&m.push({name:"action",base:l})}const R=De(m.map(({name:l,base:g})=>[l,ce(f(g,c,r.min,r.max))]),E,["normal"]);if(R.length===0)return d;const k=new Map,y=(l,g)=>{const $=`${l}:${g}`;let b=k.get($);if(!b){b=[];for(let C=0;C<360;C+=Z)b.push(Y({l,c:g,h:C}));k.set($,b)}return b},M=E-.002,_=(l,g,$,b)=>{const C=Math.hypot(l.a,l.b),O=Math.atan2(l.b,l.a)*180/Math.PI+180;let S=0;for(const x of $===b?[b]:[$,b]){if(x+C<E-1e-4)continue;let T=O,B=X(Y({l:g,c:x,h:O}),l);if(B>=E)return B;const D=y(g,x);for(let w=0;w<D.length;w+=1){const v=X(D[w],l);if(v>=E)return v;v>B&&(B=v,T=w*Z)}let P=T;for(let w=T-Z;w<=T+Z;w+=.1){const v=X(Y({l:g,c:x,h:w}),l);if(v>=E)return v;v>B&&(B=v,P=w)}if(B>=M)for(let w=P-.1;w<=P+.1;w+=.001){const v=X(Y({l:g,c:x,h:w}),l);if(v>=E)return v;v>B&&(B=v)}B>S&&(S=B)}return S},K=(l,g,$)=>{const b=g===$?[$]:[g,$];let C=0;for(const O of b)for(const S of b){if(O+S<E-1e-4)continue;const x=y(l,O),T=y(l,S);let B=0,D=0,P=-1;for(let I=0;I<x.length;I+=1)for(let z=0;z<T.length;z+=1){const G=X(x[I],T[z]);if(G>=E)return G;G>P&&(P=G,B=I*Z,D=z*Z)}const w=(I,z,G,_e)=>{const Le=[];for(let V=z-G;V<=z+G;V+=_e)Le.push({h:V,lab:Y({l,c:S,h:V})});let me=-1,Ie=I,He=z;for(let V=I-G;V<=I+G;V+=_e){const ct=Y({l,c:O,h:V});for(const Ne of Le){const Pe=X(ct,Ne.lab);Pe>me&&(me=Pe,Ie=V,He=Ne.h)}}return{distance:me,hueA:Ie,hueB:He}},v=w(B,D,Z,.1);if(v.distance>=E)return v.distance;let L=Math.max(P,v.distance);if(L>=M){const I=w(v.hueA,v.hueB,.1,.001);if(I.distance>L&&(L=I.distance),L>=E)return L}L>C&&(C=L)}return C},N=l=>ee.includes(l),s=(l,g,$,b,C)=>{const O=Y(f(l.base,$,b,C)),S=Y(f(g.base,$,b,C));if(X(O,S)>=E)return{outcome:"direct"};const x=N(l.name)&&N(g.name),T=[l,g].filter(P=>N(P.name)),B=x?"one of them":`"${N(l.name)?l.name:g.name}"`;let D;for(const P of T){const w=_(P===l?S:O,$,b,C);if(w>=E)return{outcome:"retune",target:B};w>=E-st&&(D=D??B)}if(x){const P=K($,b,C);if(P>=E)return{outcome:"retune",target:"both"};P>=E-st&&(D=D??"both")}return D!==void 0?{outcome:"near",target:D}:{outcome:"no"}},i=l=>l.outcome==="direct"?4:l.outcome==="retune"?l.target==="both"?2:3:l.outcome==="near"?1:0,a=l=>[...l].sort().join("|"),u=new Set(De(m.map(({name:l,base:g})=>[l,ce(f(g,c,0,J))]),E,["normal"]).map(l=>a(l.pair))),p=new Map(m.map(l=>[l.name,l])),h=new Map;for(const{pair:l,distance:g}of R){const $=p.get(l[0]),b=p.get(l[1]),C=N($.name)&&N(b.name),O=s($,b,c,r.min,r.max);if(O.outcome==="direct"||O.outcome==="retune"){const L=O.target??(C?"one of them":`"${N($.name)?$.name:b.name}"`);u.has(a(l))?d.push(`Status colors "${l[0]}" and "${l[1]}" are hard to distinguish under normal vision (distance ${ot(g)} < ${E}); status meanings must stay distinguishable \u2014 retune ${L} via intents.`):d.push(`Status colors "${l[0]}" and "${l[1]}" are hard to distinguish as rendered (distance ${ot(g)} < ${E}); the actionBackground chroma band ({ min: ${r.min}, max: ${r.max} }) pushes them together \u2014 widen the band, or retune ${L} via intents.`);continue}const S=s($,b,c,0,J);let x={outcome:"no"};for(let L=1;L<=19&&x.outcome!=="direct";L+=1){const I=L*.05;if(Math.abs(I-c)<.001)continue;const z=s($,b,I,r.min,r.max);i(z)>i(x)&&(x=z)}let T={outcome:"no"};const B=S.outcome==="direct"||S.outcome==="retune",D=x.outcome==="direct"||x.outcome==="retune";if(!B&&!D)for(let L=1;L<=19&&T.outcome!=="direct";L+=1){const I=s($,b,L*.05,0,J);i(I)>i(T)&&(T=I)}const P=Yt(S,x,T),w=`${O.outcome}|${P}`;let v=h.get(w);v||(v=[],h.set(w,v)),v.push(l)}for(const[l,g]of h){const $=l.indexOf("|"),b=l.slice(0,$),C=l.slice($+1),O=g.map(([T,B])=>`"${T}"/"${B}"`),S=O.length>1?`${O.slice(0,-1).join(", ")} and ${O[O.length-1]}`:O[0],x=b==="near"?`Status colors ${S} may not separate by retuning as rendered: at the action stop's lightness (${c}) with the actionBackground chroma band ({ min: ${r.min}, max: ${r.max} }), the closest reachable retune found comes within the audit's search margin of the ${E} threshold`:`Status colors ${S} cannot be separated as rendered: at the action stop's lightness (${c}) with the actionBackground chroma band ({ min: ${r.min}, max: ${r.max} }), no intent retuning can pull ${g.length>1?"these pairs":"the pair"} ${E} apart`;d.push(x+C)}return d}function Yt(e,n,t){if(e.outcome==="direct"&&n.outcome==="direct")return" \u2014 widen the chroma band or move the action lightness stop.";if(e.outcome==="direct")return n.outcome==="no"?"; no other action lightness stop can help while the band holds \u2014 widen the chroma band.":" \u2014 widen the chroma band.";if(n.outcome==="direct")return e.outcome==="no"?"; no wider chroma band can help at this lightness \u2014 move the action lightness stop.":" \u2014 move the action lightness stop.";if(e.outcome==="retune"&&n.outcome==="retune")return` \u2014 widen the chroma band or move the action lightness stop, then retune ${e.target} via intents.`;if(e.outcome==="retune")return n.outcome==="no"?`; no other action lightness stop can help while the band holds \u2014 widen the chroma band, then retune ${e.target} via intents.`:` \u2014 widen the chroma band, then retune ${e.target} via intents.`;if(n.outcome==="retune")return e.outcome==="no"?`; no wider chroma band can help at this lightness \u2014 move the action lightness stop, then retune ${n.target} via intents.`:` \u2014 move the action lightness stop, then retune ${n.target} via intents.`;const o=e.outcome==="no"&&n.outcome==="no"?"; neither a wider chroma band alone nor another stop alone can help":"; neither a wider chroma band alone nor another stop alone provably helps";return t.outcome==="direct"?`${o} \u2014 change the band and the stop together.`:t.outcome==="retune"?`${o} \u2014 change the band and the stop together, then retune ${t.target} via intents.`:"; no explored band or stop change provably separates this pair."}function sn(e,n){const t=Be(e),o=Be(n),r=t.error!==void 0?{valid:!1,errors:[t.error],warnings:[]}:Ee(t.value),c=o.error!==void 0?{valid:!1,errors:[o.error],warnings:[]}:Ee(o.value,Ve),f=[...r.errors.map(d=>`light: ${d}`),...c.errors.map(d=>`dark: ${d}`)],m=[...r.warnings.map(d=>`light: ${d}`),...c.warnings.map(d=>`dark: ${d}`)];if(r.valid&&c.valid){const d=new Set(Object.keys(t.value?.contexts??{})),R=new Set(Object.keys(o.value?.contexts??{})),k=[...R].filter(M=>!d.has(M)).sort(),y=[...d].filter(M=>!R.has(M)).sort();(k.length>0||y.length>0)&&f.push(`contexts must declare the same names in both schemes. Missing from light: ${k.join(", ")||"(none)"}. Missing from dark: ${y.join(", ")||"(none)"}.`)}return{valid:f.length===0,errors:f,warnings:m}}const qt=["anchors","angles","contexts","dataPalette","dataRamps","intents","roles","chroma","lightness","tones","semanticHues","semanticMappings","variantChroma"];function Xt(e,n){const t={};for(const[o,r]of Object.entries(e))A(o)&&(t[o]=r);for(const[o,r]of Object.entries(n))r!==void 0&&A(o)&&(t[o]=r);for(const o of qt){const r=e[o],c=n[o];if(r&&typeof r=="object"&&!Array.isArray(r)&&c&&typeof c=="object"&&!Array.isArray(c)){if(o==="anchors"){t[o]=Ye(r,c);continue}if(o==="contexts"){t[o]=Ft(r,c);continue}if(o==="dataRamps"){t[o]=Ze(r,c);continue}const f={};for(const[m,d]of Object.entries(r))A(m)&&(f[m]=d);for(const[m,d]of Object.entries(c))d!==void 0&&A(m)&&(f[m]=d);t[o]=f}}return t}function rn(...e){let n={};for(const t of e){if(!t)continue;const o=Lt(t);o&&(n=Xt(n,o))}return Te(n)}function it(e){return e.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r")}const at={borderRadius:.1,fontBody:'"Noto Sans", sans-serif',fontWeightBody:"normal",fontHeadings:'"Inika", serif',fontWeightHeadings:"normal",fontBrand:'"Inika", serif',fontWeightBrand:"normal",fontMonospace:'"JetBrains Mono", monospace',fontWeightMonospace:"normal",seedColor:"oklch(.07 0.15 216)"};function an(e){return Te({...at,pageBackgroundImage:`url("${it(e)}")`,pageBackgroundImageOpacity:.5})}function cn(e){return Te({...at,pageBackgroundImage:`url("${it(e)}")`,pageBackgroundImageOpacity:.55})}export{j as ANCHOR_SLUG_PATTERN,ke as COLOR_SOURCES,Et as DEFAULT_DARK_LIGHTNESS,Ve as DEFAULT_DARK_THEME,Bt as DEFAULT_LIGHT_LIGHTNESS,Ce as DEFAULT_LIGHT_THEME,Oe as DEFAULT_SEMANTIC_MAPPINGS,U as LIGHTNESS_KEYS,qt as NESTED_THEME_KEYS,se as ROLE_NAMES,At as ROLE_PAIRED_INK,Se as ROLE_TOKEN_PLAN,Q as SEMANTIC_COLOR_NAMES,ee as STATUS_COLOR_SOURCES,Ue as TOKEN_PAIRINGS,wt as VARIANT_COLOR_SOURCES,cn as buildTaprootDarkTheme,an as buildTaprootLightTheme,Ke as compileRoles,Te as encodeTheme,zt as explicitMappingTokens,te as isLightnessKey,rn as layerThemes,Xt as mergePartials,pe as mergeTheme,xe as parseAnchorSource,Lt as parseTheme,We as resolveAnchorColor,nt as resolveContextTheme,xt as resolveDataColorSource,Me as resolveLightnessReference,nn as semanticToCSS,Ae as toneReferenceName,on as validateTheme,sn as validateThemePair};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taprootio/espalier",
3
- "version": "3.7.0",
3
+ "version": "3.8.0",
4
4
  "packageManager": "bun@1.3.12",
5
5
  "description": "Espalier — a themeable, accessible, framework-agnostic, enterprise-grade design system built on web standards and love.",
6
6
  "customElements": "custom-elements.json",