@taprootio/espalier 3.7.0 → 3.9.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 +30 -0
- package/custom-elements.json +7995 -7877
- package/dist/button/action-state-lightness.js +1 -1
- package/dist/button/esp-button.d.ts +34 -0
- package/dist/button/esp-button.js +40 -50
- package/dist/index.d.ts +1 -1
- package/dist/root/helpers/compute-semantic-properties.js +1 -1
- package/dist/shared/theme-fit-report.d.ts +5 -5
- package/dist/shared/theme.d.ts +51 -19
- package/dist/shared/theme.js +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
const T=.
|
|
1
|
+
const T=.88;export{T as BUTTON_INTERACTION_LIGHTNESS_FACTOR};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type TemplateResult } from "lit";
|
|
2
2
|
import { EspalierElementBase } from "../shared/esp-element-base.js";
|
|
3
|
+
export type ButtonIconPosition = "left" | "right";
|
|
3
4
|
/**
|
|
4
5
|
* Used to initiate a user action or link to another page.
|
|
5
6
|
*
|
|
@@ -29,6 +30,27 @@ import { EspalierElementBase } from "../shared/esp-element-base.js";
|
|
|
29
30
|
* </div>
|
|
30
31
|
* ```
|
|
31
32
|
*
|
|
33
|
+
* Use `icon-position="left"` for a leading icon. The default `"right"`
|
|
34
|
+
* position keeps the icon trailing; both positions follow writing direction
|
|
35
|
+
* and therefore mirror in RTL.
|
|
36
|
+
*
|
|
37
|
+
* ```html
|
|
38
|
+
* <div style="display: flex; gap: var(--esp-size-padding);">
|
|
39
|
+
* <esp-button
|
|
40
|
+
* collapsed
|
|
41
|
+
* label="Save"
|
|
42
|
+
* icon="save"
|
|
43
|
+
* icon-position="left">
|
|
44
|
+
* </esp-button>
|
|
45
|
+
* <esp-button
|
|
46
|
+
* collapsed
|
|
47
|
+
* label="Next"
|
|
48
|
+
* icon="arrow-big-right"
|
|
49
|
+
* icon-position="right">
|
|
50
|
+
* </esp-button>
|
|
51
|
+
* </div>
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
32
54
|
* @customElement esp-button
|
|
33
55
|
* @slot - Optional custom SVG icon. Slotted icons override the
|
|
34
56
|
* generated SVG from the `icon` attribute.
|
|
@@ -180,6 +202,18 @@ export declare class EspalierButton extends EspalierElementBase {
|
|
|
180
202
|
* @type {string}
|
|
181
203
|
*/
|
|
182
204
|
icon: string;
|
|
205
|
+
/**
|
|
206
|
+
* Places the optional icon on the logical leading (`"left"`) or trailing
|
|
207
|
+
* (`"right"`) side of the label. Logical placement mirrors in RTL.
|
|
208
|
+
*
|
|
209
|
+
* ```html
|
|
210
|
+
* <esp-button label="Save" icon="save" icon-position="left"></esp-button>
|
|
211
|
+
* <esp-button label="Next" icon="arrow-big-right" icon-position="right"></esp-button>
|
|
212
|
+
* ```
|
|
213
|
+
*
|
|
214
|
+
* @type {"left" | "right"}
|
|
215
|
+
*/
|
|
216
|
+
iconPosition: ButtonIconPosition;
|
|
183
217
|
/**
|
|
184
218
|
* The label of the button.
|
|
185
219
|
*
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var n=function(h,t,
|
|
1
|
+
var n=function(h,t,r,o){var a=arguments.length,i=a<3?t:o===null?o=Object.getOwnPropertyDescriptor(t,r):o,d;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")i=Reflect.decorate(h,t,r,o);else for(var c=h.length-1;c>=0;c--)(d=h[c])&&(i=(a<3?d(i):a>3?d(t,r,i):d(t,r))||i);return a>3&&i&&Object.defineProperty(t,r,i),i};import{css as f,html as p,nothing as l}from"lit";import{customElement as g,eventOptions as m,property as s,state as v}from"lit/decorators.js";import{classMap as u}from"lit/directives/class-map.js";import{EspalierElementBase as b}from"../shared/esp-element-base.js";import{ref as y}from"lit/directives/ref.js";import{getIconHrefForHost as w}from"../shared/intent-values.js";import{SlottedIconController as x}from"../shared/slotted-icon-controller.js";import{renderSpriteIcon as k}from"../shared/svgs/render-sprite-icon.js";import{BUTTON_INTERACTION_LIGHTNESS_FACTOR as T}from"./action-state-lightness.js";const S=p`<svg
|
|
2
2
|
xmlns="http://www.w3.org/2000/svg"
|
|
3
3
|
viewBox="0 0 24 24"
|
|
4
4
|
fill="none"
|
|
@@ -10,40 +10,40 @@ var n=function(h,t,i,e){var a=arguments.length,r=a<3?t:e===null?e=Object.getOwnP
|
|
|
10
10
|
>
|
|
11
11
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
12
12
|
<path d="M4.05 11a8 8 0 1 1 .5 4m-.5 5v-5h5" />
|
|
13
|
-
</svg>`;let
|
|
13
|
+
</svg>`;let e=class extends b{constructor(){super(...arguments),this.iconSlot=new x(this),this.innerTabindex=null,this.neutralizingTabindex=!1,this.buttonType="button",this.collapsed=!1,this.disabled=!1,this.href="",this.iconOnly=!1,this.incognito=!1,this.icon="",this.iconPosition="right",this.label="",this.loading=!1,this.target="",this.rel="",this.formNoValidate=!1,this.buildIcon=t=>this.loading?p` <span>${S}</span> `:p`
|
|
14
14
|
<span>
|
|
15
|
-
<slot ${
|
|
16
|
-
${!this.iconSlot.hasSlottedIcon(":scope > *")&&t?
|
|
15
|
+
<slot ${y(this.iconSlot.slotRef)} @slotchange=${this.iconSlot.handleSlotChange}></slot>
|
|
16
|
+
${!this.iconSlot.hasSlottedIcon(":scope > *")&&t?k(t):l}
|
|
17
17
|
</span>
|
|
18
|
-
`}static get observedAttributes(){return[...super.observedAttributes,"tabindex","aria-pressed"]}attributeChangedCallback(t,
|
|
19
|
-
<div part="wrapper" class=${u(
|
|
18
|
+
`}static get observedAttributes(){return[...super.observedAttributes,"tabindex","aria-pressed"]}attributeChangedCallback(t,r,o){if(super.attributeChangedCallback(t,r,o),t==="aria-pressed"){this.requestUpdate();return}if(t!=="tabindex"||this.neutralizingTabindex)return;const a=o===null?null:Number(o),i=a===null||Number.isNaN(a)?null:a;i!==null&&i>=0?(this.innerTabindex=i,this.writeHostTabindex(null)):i===null&&o!==null?(this.innerTabindex=null,this.writeHostTabindex(null)):this.innerTabindex=i}writeHostTabindex(t){this.getAttribute("tabindex")!==t&&(this.neutralizingTabindex=!0,t===null?this.removeAttribute("tabindex"):this.setAttribute("tabindex",t),this.neutralizingTabindex=!1)}removeAttribute(t){super.removeAttribute(t),t==="tabindex"&&!this.neutralizingTabindex&&(this.innerTabindex=null)}focus(t){this.focusShadowElementAfterUpdate("button, a",t)}handleClick(t){if(this.dispatchEvent(new CustomEvent("clicked",{detail:{},bubbles:!0,composed:!0})),this.buttonType==="submit"){const r=this.closest("form");if(r){const o=document.createElement("button");o.type="submit",o.hidden=!0,this.formNoValidate&&(o.formNoValidate=!0),r.appendChild(o),r.requestSubmit(o),o.remove()}}else this.buttonType==="reset"&&this.closest("form")?.reset()}render(){const t=w(this.icon,this),r=this.loading||this.iconSlot.hasSlottedIcon(":scope > *")||t.length>0,{collapsed:o,iconOnly:a}=this,i=a?this.getAttribute("aria-label")||this.label||l:l,d=this.getAttribute("aria-pressed")||l,c={"esp-field":!0,"icon-only":a,incognito:this.incognito,"is-collapsed":o,"has-icon":r,"icon-left":r&&!a&&this.iconPosition==="left"};return this.href.length?p`
|
|
19
|
+
<div part="wrapper" class=${u(c)}>
|
|
20
20
|
<a
|
|
21
21
|
?disabled=${this.disabled}
|
|
22
22
|
href=${this.href}
|
|
23
23
|
target=${this.target}
|
|
24
24
|
rel=${this.rel||l}
|
|
25
25
|
tabindex=${this.innerTabindex??l}
|
|
26
|
-
aria-label=${
|
|
26
|
+
aria-label=${i}
|
|
27
27
|
>
|
|
28
28
|
<span>${this.label??""}</span>
|
|
29
29
|
${this.buildIcon(t)}
|
|
30
30
|
</a>
|
|
31
31
|
</div>
|
|
32
32
|
`:p`
|
|
33
|
-
<div part="wrapper" class=${u(
|
|
33
|
+
<div part="wrapper" class=${u(c)}>
|
|
34
34
|
<button
|
|
35
35
|
?disabled=${this.disabled||this.loading}
|
|
36
36
|
@click=${this.handleClick}
|
|
37
37
|
type=${this.buttonType}
|
|
38
38
|
tabindex=${this.innerTabindex??l}
|
|
39
|
-
aria-label=${
|
|
40
|
-
aria-pressed=${
|
|
39
|
+
aria-label=${i}
|
|
40
|
+
aria-pressed=${d}
|
|
41
41
|
>
|
|
42
42
|
<span>${this.label??""}</span>
|
|
43
43
|
${this.buildIcon(t)}
|
|
44
44
|
</button>
|
|
45
45
|
</div>
|
|
46
|
-
`}};
|
|
46
|
+
`}};e.styles=[...b.styles,f`
|
|
47
47
|
:host {
|
|
48
48
|
--_esp-button-radius: var(--esp-size-border-radius);
|
|
49
49
|
}
|
|
@@ -81,15 +81,8 @@ var n=function(h,t,i,e){var a=arguments.length,r=a<3?t:e===null?e=Object.getOwnP
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
> span:nth-child(2) {
|
|
84
|
-
background-color: oklch(
|
|
85
|
-
from var(--esp-color-action-background)
|
|
86
|
-
calc(l * ${g}) c h
|
|
87
|
-
);
|
|
88
84
|
display: none;
|
|
89
85
|
place-content: center;
|
|
90
|
-
border-left: 1px dotted var(--esp-color-border);
|
|
91
|
-
border-bottom-right-radius: max(0px, calc(var(--_esp-button-radius) - 0.1rem));
|
|
92
|
-
border-top-right-radius: max(0px, calc(var(--_esp-button-radius) - 0.1rem));
|
|
93
86
|
padding: var(--esp-button-padding, var(--esp-size-padding));
|
|
94
87
|
|
|
95
88
|
.generated-icon,
|
|
@@ -101,6 +94,17 @@ var n=function(h,t,i,e){var a=arguments.length,r=a<3?t:e===null?e=Object.getOwnP
|
|
|
101
94
|
}
|
|
102
95
|
}
|
|
103
96
|
|
|
97
|
+
&.icon-left {
|
|
98
|
+
button,
|
|
99
|
+
a {
|
|
100
|
+
grid-template-columns: min-content auto;
|
|
101
|
+
|
|
102
|
+
> span:nth-child(2) {
|
|
103
|
+
order: -1;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
104
108
|
&.has-icon {
|
|
105
109
|
button,
|
|
106
110
|
a {
|
|
@@ -123,7 +127,6 @@ var n=function(h,t,i,e){var a=arguments.length,r=a<3?t:e===null?e=Object.getOwnP
|
|
|
123
127
|
}
|
|
124
128
|
|
|
125
129
|
> span:nth-child(2) {
|
|
126
|
-
background-color: var(--esp-color-action-background);
|
|
127
130
|
border: none;
|
|
128
131
|
border-radius: var(--_esp-button-radius);
|
|
129
132
|
}
|
|
@@ -136,24 +139,12 @@ var n=function(h,t,i,e){var a=arguments.length,r=a<3?t:e===null?e=Object.getOwnP
|
|
|
136
139
|
&:hover,
|
|
137
140
|
&:focus-within {
|
|
138
141
|
background-color: oklch(
|
|
139
|
-
from var(--esp-color-action-background) calc(l * ${
|
|
142
|
+
from var(--esp-color-action-background) calc(l * ${T})
|
|
140
143
|
c h
|
|
141
144
|
);
|
|
142
|
-
|
|
143
|
-
button,
|
|
144
|
-
a {
|
|
145
|
-
> span:nth-child(2) {
|
|
146
|
-
background-color: oklch(
|
|
147
|
-
from var(--esp-color-action-background)
|
|
148
|
-
calc(l * ${S}) c h
|
|
149
|
-
);
|
|
150
|
-
border-color: oklch(from var(--esp-color-border) calc(l * 1.2) c h);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
145
|
}
|
|
154
146
|
|
|
155
147
|
&.incognito {
|
|
156
|
-
grid-template-columns: min-content auto;
|
|
157
148
|
background-color: transparent;
|
|
158
149
|
border-color: transparent;
|
|
159
150
|
color: var(--esp-color-text);
|
|
@@ -162,17 +153,27 @@ var n=function(h,t,i,e){var a=arguments.length,r=a<3?t:e===null?e=Object.getOwnP
|
|
|
162
153
|
button,
|
|
163
154
|
a {
|
|
164
155
|
> span:nth-child(1) {
|
|
165
|
-
|
|
166
|
-
justify-content: left;
|
|
156
|
+
justify-content: start;
|
|
167
157
|
padding: var(--esp-button-incognito-padding, var(--esp-size-tiny)) 0;
|
|
168
|
-
padding-
|
|
158
|
+
padding-inline-end: calc(
|
|
159
|
+
2 * var(--esp-button-incognito-padding, var(--esp-size-tiny))
|
|
160
|
+
);
|
|
169
161
|
}
|
|
170
162
|
|
|
171
163
|
> span:nth-child(2) {
|
|
172
|
-
grid-row: 1;
|
|
173
|
-
grid-column: 1;
|
|
174
164
|
background-color: transparent;
|
|
175
|
-
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
&.icon-left {
|
|
169
|
+
button,
|
|
170
|
+
a {
|
|
171
|
+
> span:nth-child(1) {
|
|
172
|
+
padding-inline-start: calc(
|
|
173
|
+
2 * var(--esp-button-incognito-padding, var(--esp-size-tiny))
|
|
174
|
+
);
|
|
175
|
+
padding-inline-end: 0;
|
|
176
|
+
}
|
|
176
177
|
}
|
|
177
178
|
}
|
|
178
179
|
|
|
@@ -193,17 +194,6 @@ var n=function(h,t,i,e){var a=arguments.length,r=a<3?t:e===null?e=Object.getOwnP
|
|
|
193
194
|
&:has(button:disabled):hover,
|
|
194
195
|
&:has(button:disabled):focus-within {
|
|
195
196
|
background-color: var(--esp-color-action-background);
|
|
196
|
-
|
|
197
|
-
button,
|
|
198
|
-
a {
|
|
199
|
-
> span:nth-child(2) {
|
|
200
|
-
background-color: oklch(
|
|
201
|
-
from var(--esp-color-action-background)
|
|
202
|
-
calc(l * ${g}) c h
|
|
203
|
-
);
|
|
204
|
-
border-color: var(--esp-color-border);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
197
|
}
|
|
208
198
|
|
|
209
199
|
svg.rotate {
|
|
@@ -223,4 +213,4 @@ var n=function(h,t,i,e){var a=arguments.length,r=a<3?t:e===null?e=Object.getOwnP
|
|
|
223
213
|
transform: rotate(360deg);
|
|
224
214
|
}
|
|
225
215
|
}
|
|
226
|
-
`],n([
|
|
216
|
+
`],n([v()],e.prototype,"innerTabindex",void 0),n([s({attribute:"button-type",type:String})],e.prototype,"buttonType",void 0),n([s({type:Boolean})],e.prototype,"collapsed",void 0),n([s({type:Boolean,reflect:!0})],e.prototype,"disabled",void 0),n([s({type:String})],e.prototype,"href",void 0),n([s({attribute:"icon-only",type:Boolean})],e.prototype,"iconOnly",void 0),n([s({type:Boolean,reflect:!0})],e.prototype,"incognito",void 0),n([s({type:String})],e.prototype,"icon",void 0),n([s({attribute:"icon-position",type:String})],e.prototype,"iconPosition",void 0),n([s({type:String})],e.prototype,"label",void 0),n([s({type:Boolean,reflect:!0})],e.prototype,"loading",void 0),n([s({type:String})],e.prototype,"target",void 0),n([s({type:String})],e.prototype,"rel",void 0),n([s({attribute:"formnovalidate",type:Boolean})],e.prototype,"formNoValidate",void 0),n([m({capture:!1,passive:!0})],e.prototype,"handleClick",null),e=n([g("esp-button")],e);export{e as EspalierButton};
|
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
|
|
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
|
|
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
|
|
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
|
|
92
|
-
lightness:
|
|
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. */
|
package/dist/shared/theme.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
114
|
-
* applies unchanged. Anchors are absolute sources rather
|
|
115
|
-
* rotations — a brand color is a color, not a relationship
|
|
116
|
-
* seed — so they hold steady while contexts rebind roles around
|
|
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
|
-
/**
|
|
171
|
-
lightness:
|
|
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
|
|
233
|
-
* Root-level
|
|
234
|
-
*
|
|
235
|
-
*
|
|
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
|
|
679
|
-
*
|
|
680
|
-
* `semanticMappings` layer on top as explicit token pins. Returns `null`
|
|
681
|
-
*
|
|
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
|
*
|