@sanity/themer 0.1.0 → 0.2.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.
@@ -0,0 +1,375 @@
1
+ import { t as isColor } from "./mix-BfV6_Ke4.js";
2
+ import { COLOR_TINTS, black, blue, color, gray, white } from "@sanity/color";
3
+ import { buildTheme } from "@sanity/ui/theme";
4
+ /** @internal */
5
+ function hexToHsl(hex) {
6
+ let [r, g, b] = parseHex(hex), max = Math.max(r, g, b), min = Math.min(r, g, b), delta = max - min, l = (max + min) / 2;
7
+ if (delta === 0) return {
8
+ h: 0,
9
+ s: 0,
10
+ l
11
+ };
12
+ let s = delta / (1 - Math.abs(2 * l - 1)), h;
13
+ return h = max === r ? (g - b) / delta % 6 : max === g ? (b - r) / delta + 2 : (r - g) / delta + 4, {
14
+ h: normalizeHue(h * 60),
15
+ s,
16
+ l
17
+ };
18
+ }
19
+ /** @internal */
20
+ function hslToHex(hsl) {
21
+ let h = normalizeHue(hsl.h), s = clamp01(hsl.s), l = clamp01(hsl.l), c = (1 - Math.abs(2 * l - 1)) * s, x = c * (1 - Math.abs(h / 60 % 2 - 1)), m = l - c / 2, r = 0, g = 0, b = 0;
22
+ return h < 60 ? (r = c, g = x) : h < 120 ? (r = x, g = c) : h < 180 ? (g = c, b = x) : h < 240 ? (g = x, b = c) : h < 300 ? (r = x, b = c) : (r = c, b = x), `#${channelToHex(r + m)}${channelToHex(g + m)}${channelToHex(b + m)}`;
23
+ }
24
+ /**
25
+ * Rotates `from` towards `to` along the shortest arc of the hue circle.
26
+ *
27
+ * @internal
28
+ */
29
+ function mixHue(from, to, amount) {
30
+ return normalizeHue(from + (((to - from) % 360 + 540) % 360 - 180) * amount);
31
+ }
32
+ /**
33
+ * The WCAG relative luminance of a hex color, between 0 (black) and 1 (white).
34
+ *
35
+ * @internal
36
+ */
37
+ function relativeLuminance(hex) {
38
+ let [r, g, b] = parseHex(hex);
39
+ return .2126 * linearChannel(r) + .7152 * linearChannel(g) + .0722 * linearChannel(b);
40
+ }
41
+ function linearChannel(value) {
42
+ return value <= .04045 ? value / 12.92 : ((value + .055) / 1.055) ** 2.4;
43
+ }
44
+ /** Parses `#abc` and `#aabbcc` hex colors into channel fractions of 1 */
45
+ function parseHex(hex) {
46
+ let expanded = hex.length === 4 ? `${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}` : hex.slice(1);
47
+ return [
48
+ parseInt(expanded.slice(0, 2), 16) / 255,
49
+ parseInt(expanded.slice(2, 4), 16) / 255,
50
+ parseInt(expanded.slice(4, 6), 16) / 255
51
+ ];
52
+ }
53
+ function channelToHex(value) {
54
+ return Math.round(clamp01(value) * 255).toString(16).padStart(2, "0");
55
+ }
56
+ function normalizeHue(hue) {
57
+ return (hue % 360 + 360) % 360;
58
+ }
59
+ function clamp01(value) {
60
+ return Math.min(1, Math.max(0, value));
61
+ }
62
+ /** The stock accent color — `blue/500` of `@sanity/color` @internal */
63
+ const DEFAULT_ACCENT = blue[500].hex, DEFAULT_TEXT = gray[500].hex, DEFAULT_BACKGROUND_DARK = black.hex, DEFAULT_BACKGROUND_LIGHT = white.hex;
64
+ /** @internal */
65
+ function resolveThemeOptions(options) {
66
+ let accent = normalizeColor(options.accent, "accent");
67
+ return {
68
+ accent,
69
+ background: {
70
+ dark: options.background?.dark === void 0 ? DEFAULT_BACKGROUND_DARK : normalizeColor(options.background.dark, "background.dark"),
71
+ light: options.background?.light === void 0 ? DEFAULT_BACKGROUND_LIGHT : normalizeColor(options.background.light, "background.light")
72
+ },
73
+ contrast: normalizeContrast(options.contrast),
74
+ text: options.text === void 0 ? deriveTextColor(accent) : normalizeColor(options.text, "text")
75
+ };
76
+ }
77
+ /**
78
+ * Derives the text color from an accent color, keeping the relationship the
79
+ * stock palette has between `gray/500` and `blue/500`: the text color takes
80
+ * the accent's hue (offset the way the stock gray is offset from the stock
81
+ * blue), carries about an eighth of its saturation, and keeps the stock text
82
+ * lightness. Deriving from the stock accent returns the stock text color.
83
+ *
84
+ * @internal
85
+ */
86
+ function deriveTextColor(accent) {
87
+ let accentHsl = hexToHsl(normalizeColor(accent, "accent")), defaultAccent = hexToHsl(DEFAULT_ACCENT), defaultText = hexToHsl(DEFAULT_TEXT);
88
+ return hslToHex({
89
+ h: defaultText.h + (accentHsl.h - defaultAccent.h),
90
+ s: Math.min(1, defaultText.s * (accentHsl.s / defaultAccent.s)),
91
+ l: defaultText.l
92
+ });
93
+ }
94
+ function normalizeColor(value, name) {
95
+ if (typeof value != "string" || !isColor(value)) throw TypeError(`Invalid \`${name}\` color: ${JSON.stringify(value)} — expected a hex color like #556bfc`);
96
+ let lower = value.toLowerCase();
97
+ return lower.length === 4 ? `#${lower[1]}${lower[1]}${lower[2]}${lower[2]}${lower[3]}${lower[3]}` : lower;
98
+ }
99
+ function normalizeContrast(value) {
100
+ if (value === void 0) return 85;
101
+ if (typeof value != "number" || !Number.isFinite(value)) throw TypeError(`Invalid \`contrast\`: ${JSON.stringify(value)} — expected a number between 15 and 100`);
102
+ return Math.min(100, Math.max(15, value));
103
+ }
104
+ /**
105
+ * The lightness band the accent and text colors are clamped into — anchors
106
+ * outside it would squeeze most of their scale's tints into an
107
+ * indistinguishable sliver, messing with the rest of the scale.
108
+ */
109
+ const BACKGROUND_LIGHTNESS_STEP = .005;
110
+ /**
111
+ * Generates a Sanity UI color palette from the theme options: the accent
112
+ * color replaces the `blue` scale, the text color the `gray` scale, and the
113
+ * backgrounds replace `black` and `white`. All other hues keep their stock
114
+ * `@sanity/color` values. Called with the stock colors, it returns the stock
115
+ * palette byte-for-byte.
116
+ *
117
+ * ```ts
118
+ * import {buildPalette} from '@sanity/themer'
119
+ *
120
+ * const palette = buildPalette({accent: '#556bfc'})
121
+ * ```
122
+ *
123
+ * @public
124
+ */
125
+ function buildPalette(options) {
126
+ let resolved = resolveThemeOptions(options), accentAnchor = clampAnchorLightness(hexToHsl(resolved.accent)), grayAnchor = applyContrast(clampAnchorLightness(hexToHsl(resolved.text)), accentAnchor, resolved.contrast), blueTints = remapTints(color.blue, hexToHsl(DEFAULT_ACCENT), accentAnchor), grayTints = remapTints(color.gray, hexToHsl(DEFAULT_TEXT), grayAnchor), anchorLuminances = [relativeLuminance(hslToHex(grayAnchor)), relativeLuminance(hslToHex(accentAnchor))];
127
+ return {
128
+ black: darkenBackground(resolved.background.dark, {
129
+ anchorLuminances,
130
+ tints: [grayTints[400], blueTints[400]]
131
+ }),
132
+ white: lightenBackground(resolved.background.light, anchorLuminances),
133
+ gray: grayTints,
134
+ blue: blueTints,
135
+ purple: tintHexes(color.purple),
136
+ magenta: tintHexes(color.magenta),
137
+ red: tintHexes(color.red),
138
+ orange: tintHexes(color.orange),
139
+ yellow: tintHexes(color.yellow),
140
+ green: tintHexes(color.green),
141
+ cyan: tintHexes(color.cyan)
142
+ };
143
+ }
144
+ /**
145
+ * Mixes the accent color into the text color according to the contrast: 85
146
+ * applies the text color as-is, 100 removes its tint entirely, and lower
147
+ * values rotate and saturate it towards the accent.
148
+ */
149
+ function applyContrast(text, accent, contrast) {
150
+ if (contrast === 85) return text;
151
+ if (contrast > 85) {
152
+ let amount = (contrast - 85) / 15;
153
+ return {
154
+ h: text.h,
155
+ s: text.s * (1 - amount),
156
+ l: text.l
157
+ };
158
+ }
159
+ let amount = (85 - contrast) / 70;
160
+ return {
161
+ h: mixHue(text.h, accent.h, amount),
162
+ s: text.s + (accent.s - text.s) * amount,
163
+ l: text.l
164
+ };
165
+ }
166
+ /**
167
+ * Rebuilds a stock tint scale around a new anchor color, where `from` is the
168
+ * scale's stock 500 tint: hues rotate along, saturation scales
169
+ * proportionally, and lightness is remapped piecewise so the anchor takes the
170
+ * new lightness while the scale keeps spanning light to dark. When the anchor
171
+ * matches the stock one, every tint comes back unchanged.
172
+ */
173
+ function remapTints(tints, from, to) {
174
+ let rotation = to.h - from.h, result = {};
175
+ for (let tint of COLOR_TINTS) {
176
+ let value = hexToHsl(tints[tint].hex);
177
+ result[tint] = hslToHex({
178
+ h: value.h + rotation,
179
+ s: remapSaturation(value.s, from.s, to.s),
180
+ l: remapLightness(value.l, from.l, to.l)
181
+ });
182
+ }
183
+ return result;
184
+ }
185
+ function remapSaturation(value, from, to) {
186
+ return from === to ? value : Math.min(1, to / from * value);
187
+ }
188
+ function remapLightness(value, from, to) {
189
+ return from === to ? value : value <= from ? to / from * value : to + (value - from) * (1 - to) / (1 - from);
190
+ }
191
+ function clampAnchorLightness(anchor) {
192
+ let l = Math.min(.75, Math.max(.25, anchor.l));
193
+ return l === anchor.l ? anchor : {
194
+ ...anchor,
195
+ l
196
+ };
197
+ }
198
+ /**
199
+ * Enforces the dark background rules: it can never be lighter than the text
200
+ * or accent colors, and it must reach enough contrast with the 400 tints of
201
+ * their scales — it is made darker until it does (or bottoms out at black).
202
+ */
203
+ function darkenBackground(hex, rules) {
204
+ let maximumLuminance = Math.min(...rules.anchorLuminances), tintLuminances = rules.tints.map((tint) => relativeLuminance(tint)), isDarkEnough = (background) => {
205
+ let luminance = relativeLuminance(background);
206
+ return luminance < maximumLuminance && tintLuminances.every((tintLuminance) => (tintLuminance + .05) / (luminance + .05) >= 4.5);
207
+ }, { h, s, l } = hexToHsl(hex), lightness = l, current = hex;
208
+ for (; !isDarkEnough(current) && lightness > 0;) lightness = Math.max(0, lightness - BACKGROUND_LIGHTNESS_STEP), current = hslToHex({
209
+ h,
210
+ s,
211
+ l: lightness
212
+ });
213
+ return current;
214
+ }
215
+ /**
216
+ * Enforces the light background rule: it can never be darker than the text
217
+ * or accent colors — it is made lighter until it is (or tops out at white).
218
+ */
219
+ function lightenBackground(hex, anchorLuminances) {
220
+ let minimumLuminance = Math.max(...anchorLuminances), { h, s, l } = hexToHsl(hex), lightness = l, current = hex;
221
+ for (; relativeLuminance(current) <= minimumLuminance && lightness < 1;) lightness = Math.min(1, lightness + BACKGROUND_LIGHTNESS_STEP), current = hslToHex({
222
+ h,
223
+ s,
224
+ l: lightness
225
+ });
226
+ return current;
227
+ }
228
+ function tintHexes(tints) {
229
+ let result = {};
230
+ for (let tint of COLOR_TINTS) result[tint] = tints[tint].hex;
231
+ return result;
232
+ }
233
+ /**
234
+ * Generates a Studio theme from a handful of colors, by replacing the color
235
+ * palette that the `buildTheme` from `@sanity/ui/theme` otherwise fills with
236
+ * `@sanity/color`: the accent color replaces the `blue` scale (primary
237
+ * buttons, focus rings, links), the optional text color the `gray` scale
238
+ * (text, icons, borders), and the optional backgrounds replace `black` and
239
+ * `white`. The result is the same type of theme that `@sanity/ui/theme`
240
+ * builds, ready for the `theme` property of a Studio config.
241
+ *
242
+ * ```ts
243
+ * import {buildTheme} from '@sanity/themer'
244
+ * import {defineConfig} from 'sanity'
245
+ *
246
+ * export const theme = buildTheme({
247
+ * accent: '#556bfc',
248
+ * text: '#727892', // optional — derived from `accent` when omitted
249
+ * background: {dark: '#0d0e12', light: '#ffffff'}, // optional
250
+ * contrast: 85, // optional, 15–100
251
+ * })
252
+ *
253
+ * export default defineConfig({
254
+ * theme,
255
+ * // ...rest of the config
256
+ * })
257
+ * ```
258
+ *
259
+ * Called with the stock colors — like the example — it returns the exact
260
+ * same colors as `buildTheme()` from `@sanity/ui/theme` with no options.
261
+ *
262
+ * @public
263
+ */
264
+ function buildTheme$1(options) {
265
+ return buildTheme({ palette: buildPalette(options) });
266
+ }
267
+ /**
268
+ * Preset themes for `buildTheme`, carried over from the hosted Themer service
269
+ * (themer.sanity.build) presets: each one maps the legacy preset's primary
270
+ * hue to `accent`, its default hue to `text` and its lightest/darkest colors
271
+ * to the backgrounds.
272
+ *
273
+ * ```ts
274
+ * import {buildTheme, presets} from '@sanity/themer'
275
+ *
276
+ * const verdant = presets.find((preset) => preset.slug === 'verdant')
277
+ * const theme = buildTheme(verdant.options)
278
+ * ```
279
+ *
280
+ * @public
281
+ */
282
+ const presets = [
283
+ {
284
+ slug: "studio",
285
+ title: "Studio",
286
+ options: { accent: DEFAULT_ACCENT }
287
+ },
288
+ {
289
+ slug: "dew",
290
+ title: "Dew",
291
+ options: {
292
+ accent: "#d1a308",
293
+ text: "#5e63b4",
294
+ background: {
295
+ dark: "#0d0d15",
296
+ light: "#fcfcfd"
297
+ }
298
+ }
299
+ },
300
+ {
301
+ slug: "pink-synth",
302
+ title: "Pink Synth",
303
+ options: {
304
+ accent: "#ec4899",
305
+ text: "#8b6584",
306
+ background: {
307
+ dark: "#171721",
308
+ light: "#f7f2f5"
309
+ }
310
+ }
311
+ },
312
+ {
313
+ slug: "pixel-art",
314
+ title: "Pixel Art",
315
+ options: {
316
+ accent: "#f10784",
317
+ text: "#57619c",
318
+ background: {
319
+ dark: "#0d0e15",
320
+ light: "#fcfcfd"
321
+ }
322
+ }
323
+ },
324
+ {
325
+ slug: "retro-colonial",
326
+ title: "Retro Colonial",
327
+ options: {
328
+ accent: "#fa7a78",
329
+ text: "#8bb9b5",
330
+ background: {
331
+ dark: "#0d1515",
332
+ light: "#fcfdfd"
333
+ }
334
+ }
335
+ },
336
+ {
337
+ slug: "rosabel",
338
+ title: "Rosabel",
339
+ options: {
340
+ accent: "#ed2555",
341
+ text: "#9d8966",
342
+ background: {
343
+ dark: "#15120d",
344
+ light: "#fdfdfc"
345
+ }
346
+ }
347
+ },
348
+ {
349
+ slug: "stereofidelic",
350
+ title: "Stereofidelic",
351
+ options: {
352
+ accent: "#f13009",
353
+ text: "#678e9a",
354
+ background: {
355
+ dark: "#0e1315",
356
+ light: "#fcfdfd"
357
+ }
358
+ }
359
+ },
360
+ {
361
+ slug: "verdant",
362
+ title: "Verdant",
363
+ options: {
364
+ accent: "#1cb485",
365
+ text: "#5c9199",
366
+ background: {
367
+ dark: "#0d1415",
368
+ light: "#fcfdfd"
369
+ }
370
+ }
371
+ }
372
+ ];
373
+ export { DEFAULT_BACKGROUND_DARK as a, resolveThemeOptions as c, DEFAULT_ACCENT as i, buildTheme$1 as n, DEFAULT_BACKGROUND_LIGHT as o, buildPalette as r, deriveTextColor as s, presets as t };
374
+
375
+ //# sourceMappingURL=presets-DWzYcsLg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presets-DWzYcsLg.js","names":["Hsl","h","s","l","hexToHsl","hex","r","g","b","parseHex","max","Math","min","delta","abs","normalizeHue","hslToHex","hsl","clamp01","c","x","m","channelToHex","mixHue","from","to","amount","relativeLuminance","linearChannel","value","expanded","length","slice","parseInt","round","toString","padStart","hue","black","blue","gray","white","isColor","hexToHsl","hslToHex","BuildThemeOptions","accent","background","dark","light","contrast","text","ResolvedThemeOptions","DEFAULT_ACCENT","hex","DEFAULT_TEXT","DEFAULT_BACKGROUND_DARK","DEFAULT_BACKGROUND_LIGHT","DEFAULT_CONTRAST","MINIMUM_CONTRAST","MAXIMUM_CONTRAST","resolveThemeOptions","options","normalizeColor","undefined","normalizeContrast","deriveTextColor","accentHsl","defaultAccent","defaultText","h","s","Math","min","l","value","name","TypeError","JSON","stringify","lower","toLowerCase","length","Number","isFinite","max","color","COLOR_TINTS","ColorHueKey","ColorTintKey","ColorTints","hexToHsl","Hsl","hslToHex","mixHue","relativeLuminance","BuildThemeOptions","DEFAULT_ACCENT","DEFAULT_CONTRAST","DEFAULT_TEXT","MAXIMUM_CONTRAST","MINIMUM_CONTRAST","resolveThemeOptions","MINIMUM_ANCHOR_LIGHTNESS","MAXIMUM_ANCHOR_LIGHTNESS","MINIMUM_DARK_BACKGROUND_CONTRAST","BACKGROUND_LIGHTNESS_STEP","GeneratedColorPalette","black","white","Record","buildPalette","options","resolved","accentAnchor","clampAnchorLightness","accent","textAnchor","text","grayAnchor","applyContrast","contrast","blueTints","remapTints","blue","grayTints","gray","anchorLuminances","darkenBackground","background","dark","tints","lightenBackground","light","purple","tintHexes","magenta","red","orange","yellow","green","cyan","amount","h","s","l","from","to","rotation","result","Partial","tint","value","hex","remapSaturation","remapLightness","Math","min","anchor","max","rules","maximumLuminance","tintLuminances","map","isDarkEnough","luminance","every","tintLuminance","lightness","current","minimumLuminance","buildTheme","buildUITheme","RootTheme","buildPalette","BuildThemeOptions","options","palette","BuildThemeOptions","DEFAULT_ACCENT","ThemePreset","slug","title","options","presets","accent","text","background","dark","light"],"sources":["../src/theme/hsl.ts","../src/theme/options.ts","../src/theme/buildPalette.ts","../src/theme/buildTheme.ts","../src/theme/presets.ts"],"sourcesContent":["/**\n * Color math for the palette generator, kept at full float precision: unlike\n * the HSL helpers in `@sanity/ui/theme`, nothing is rounded between the two\n * conversions, so `hslToHex(hexToHsl(hex))` returns `hex` unchanged for every\n * color. `buildTheme` relies on that to reproduce the stock `@sanity/color`\n * palette byte-for-byte when it is called with the stock anchor colors.\n */\n\n/**\n * A color in HSL space: `h` in degrees within `[0, 360)`, `s` and `l` as\n * fractions within `[0, 1]`.\n *\n * @internal\n */\nexport interface Hsl {\n h: number\n s: number\n l: number\n}\n\n/** @internal */\nexport function hexToHsl(hex: string): Hsl {\n const [r, g, b] = parseHex(hex)\n\n const max = Math.max(r, g, b)\n const min = Math.min(r, g, b)\n const delta = max - min\n\n const l = (max + min) / 2\n\n if (delta === 0) {\n return {h: 0, s: 0, l}\n }\n\n const s = delta / (1 - Math.abs(2 * l - 1))\n\n let h: number\n\n if (max === r) {\n h = ((g - b) / delta) % 6\n } else if (max === g) {\n h = (b - r) / delta + 2\n } else {\n h = (r - g) / delta + 4\n }\n\n return {h: normalizeHue(h * 60), s, l}\n}\n\n/** @internal */\nexport function hslToHex(hsl: Hsl): string {\n const h = normalizeHue(hsl.h)\n const s = clamp01(hsl.s)\n const l = clamp01(hsl.l)\n\n const c = (1 - Math.abs(2 * l - 1)) * s\n const x = c * (1 - Math.abs(((h / 60) % 2) - 1))\n const m = l - c / 2\n\n let r = 0\n let g = 0\n let b = 0\n\n if (h < 60) {\n r = c\n g = x\n } else if (h < 120) {\n r = x\n g = c\n } else if (h < 180) {\n g = c\n b = x\n } else if (h < 240) {\n g = x\n b = c\n } else if (h < 300) {\n r = x\n b = c\n } else {\n r = c\n b = x\n }\n\n return `#${channelToHex(r + m)}${channelToHex(g + m)}${channelToHex(b + m)}`\n}\n\n/**\n * Rotates `from` towards `to` along the shortest arc of the hue circle.\n *\n * @internal\n */\nexport function mixHue(from: number, to: number, amount: number): number {\n const delta = ((((to - from) % 360) + 540) % 360) - 180\n\n return normalizeHue(from + delta * amount)\n}\n\n/**\n * The WCAG relative luminance of a hex color, between 0 (black) and 1 (white).\n *\n * @internal\n */\nexport function relativeLuminance(hex: string): number {\n const [r, g, b] = parseHex(hex)\n\n return 0.2126 * linearChannel(r) + 0.7152 * linearChannel(g) + 0.0722 * linearChannel(b)\n}\n\nfunction linearChannel(value: number): number {\n return value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4\n}\n\n/** Parses `#abc` and `#aabbcc` hex colors into channel fractions of 1 */\nfunction parseHex(hex: string): [number, number, number] {\n const expanded =\n hex.length === 4 ? `${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}` : hex.slice(1)\n\n return [\n parseInt(expanded.slice(0, 2), 16) / 255,\n parseInt(expanded.slice(2, 4), 16) / 255,\n parseInt(expanded.slice(4, 6), 16) / 255,\n ]\n}\n\nfunction channelToHex(value: number): string {\n return Math.round(clamp01(value) * 255)\n .toString(16)\n .padStart(2, '0')\n}\n\nfunction normalizeHue(hue: number): number {\n return ((hue % 360) + 360) % 360\n}\n\nfunction clamp01(value: number): number {\n return Math.min(1, Math.max(0, value))\n}\n","import {black, blue, gray, white} from '@sanity/color'\n\nimport {isColor} from '../lib/mix'\nimport {hexToHsl, hslToHex} from './hsl'\n\n/**\n * Options for {@link buildTheme} and {@link buildPalette}.\n *\n * Every color is a hex color (`#rgb` or `#rrggbb`). Passing the stock values\n * for every option — or just `accent: '#556bfc'` on its own — reproduces the\n * `@sanity/color` palette exactly, so the generated theme matches\n * `buildTheme()` from `@sanity/ui/theme` with no options.\n *\n * @public\n */\nexport interface BuildThemeOptions {\n /**\n * The accent color — the brand color of the theme. Replaces the `blue`\n * scale of the palette, which Sanity UI uses for primary buttons, focus\n * rings and links.\n *\n * The stock accent is `#556bfc` (`blue/500` of `@sanity/color`).\n */\n accent: string\n\n /**\n * The backgrounds of the two color schemes. `dark` replaces `black` in the\n * palette and `light` replaces `white` — every other color of a scheme is\n * blended onto them.\n *\n * Both are kept far enough from the text and accent colors to stay usable:\n * `dark` is made darker until it has enough contrast with them (it can\n * never be lighter than either), and `light` can never be darker than\n * either.\n *\n * The stock backgrounds are `#0d0e12` (dark) and `#ffffff` (light).\n */\n background?: {\n /** The dark scheme background — defaults to `#0d0e12` */\n dark?: string\n /** The light scheme background — defaults to `#ffffff` */\n light?: string\n }\n\n /**\n * How strongly text and borders separate from the accent color, between 15\n * and 100. The default is 85, which uses the text color as-is. Above 85 the\n * text scale loses its tint until it is fully neutral at 100 — a high\n * contrast scheme with no mixing of the text and accent colors. Below 85\n * more and more of the accent color mixes into the text scale, giving text\n * and borders more color, until the scale fully adopts the accent hue and\n * saturation at 15.\n */\n contrast?: number\n\n /**\n * The text color. Replaces the `gray` scale of the palette, which Sanity UI\n * uses for text, icons, borders and neutral surfaces. When omitted, it is\n * derived from the accent color: a mostly desaturated version of it, the\n * way the stock `gray` scale carries a hint of the stock blue.\n *\n * The stock text color is `#727892` (`gray/500` of `@sanity/color`).\n */\n text?: string\n}\n\n/**\n * {@link BuildThemeOptions} with every option filled in: colors normalized to\n * lowercase `#rrggbb`, the text color derived from the accent when it was\n * omitted, and the contrast clamped into its valid range.\n *\n * @internal\n */\nexport interface ResolvedThemeOptions {\n accent: string\n background: {dark: string; light: string}\n contrast: number\n text: string\n}\n\n/** The stock accent color — `blue/500` of `@sanity/color` @internal */\nexport const DEFAULT_ACCENT: string = blue[500].hex\n\n/** The stock text color — `gray/500` of `@sanity/color` @internal */\nexport const DEFAULT_TEXT: string = gray[500].hex\n\n/** The stock dark scheme background — `black` of `@sanity/color` @internal */\nexport const DEFAULT_BACKGROUND_DARK: string = black.hex\n\n/** The stock light scheme background — `white` of `@sanity/color` @internal */\nexport const DEFAULT_BACKGROUND_LIGHT: string = white.hex\n\n/** The contrast that applies the text color as-is @internal */\nexport const DEFAULT_CONTRAST = 85\n\n/** @internal */\nexport const MINIMUM_CONTRAST = 15\n\n/** @internal */\nexport const MAXIMUM_CONTRAST = 100\n\n/** @internal */\nexport function resolveThemeOptions(options: BuildThemeOptions): ResolvedThemeOptions {\n const accent = normalizeColor(options.accent, 'accent')\n\n return {\n accent,\n background: {\n dark:\n options.background?.dark === undefined\n ? DEFAULT_BACKGROUND_DARK\n : normalizeColor(options.background.dark, 'background.dark'),\n light:\n options.background?.light === undefined\n ? DEFAULT_BACKGROUND_LIGHT\n : normalizeColor(options.background.light, 'background.light'),\n },\n contrast: normalizeContrast(options.contrast),\n text:\n options.text === undefined ? deriveTextColor(accent) : normalizeColor(options.text, 'text'),\n }\n}\n\n/**\n * Derives the text color from an accent color, keeping the relationship the\n * stock palette has between `gray/500` and `blue/500`: the text color takes\n * the accent's hue (offset the way the stock gray is offset from the stock\n * blue), carries about an eighth of its saturation, and keeps the stock text\n * lightness. Deriving from the stock accent returns the stock text color.\n *\n * @internal\n */\nexport function deriveTextColor(accent: string): string {\n const accentHsl = hexToHsl(normalizeColor(accent, 'accent'))\n const defaultAccent = hexToHsl(DEFAULT_ACCENT)\n const defaultText = hexToHsl(DEFAULT_TEXT)\n\n return hslToHex({\n h: defaultText.h + (accentHsl.h - defaultAccent.h),\n s: Math.min(1, defaultText.s * (accentHsl.s / defaultAccent.s)),\n l: defaultText.l,\n })\n}\n\nfunction normalizeColor(value: string, name: string): string {\n if (typeof value !== 'string' || !isColor(value)) {\n throw new TypeError(\n `Invalid \\`${name}\\` color: ${JSON.stringify(value)} — expected a hex color like #556bfc`,\n )\n }\n\n const lower = value.toLowerCase()\n\n return lower.length === 4\n ? `#${lower[1]}${lower[1]}${lower[2]}${lower[2]}${lower[3]}${lower[3]}`\n : lower\n}\n\nfunction normalizeContrast(value: number | undefined): number {\n if (value === undefined) {\n return DEFAULT_CONTRAST\n }\n\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n throw new TypeError(\n `Invalid \\`contrast\\`: ${JSON.stringify(value)} — expected a number between ${MINIMUM_CONTRAST} and ${MAXIMUM_CONTRAST}`,\n )\n }\n\n return Math.min(MAXIMUM_CONTRAST, Math.max(MINIMUM_CONTRAST, value))\n}\n","import {color, COLOR_TINTS, ColorHueKey, ColorTintKey, ColorTints} from '@sanity/color'\n\nimport {hexToHsl, Hsl, hslToHex, mixHue, relativeLuminance} from './hsl'\nimport {\n BuildThemeOptions,\n DEFAULT_ACCENT,\n DEFAULT_CONTRAST,\n DEFAULT_TEXT,\n MAXIMUM_CONTRAST,\n MINIMUM_CONTRAST,\n resolveThemeOptions,\n} from './options'\n\n/**\n * The lightness band the accent and text colors are clamped into — anchors\n * outside it would squeeze most of their scale's tints into an\n * indistinguishable sliver, messing with the rest of the scale.\n */\nconst MINIMUM_ANCHOR_LIGHTNESS = 0.25\nconst MAXIMUM_ANCHOR_LIGHTNESS = 0.75\n\n/**\n * The contrast ratio the dark background must reach against the 400 tints of\n * the text and accent scales — the tints Sanity UI paints muted text and\n * solid buttons with on dark backgrounds. The stock palette sits at ~6.8.\n */\nconst MINIMUM_DARK_BACKGROUND_CONTRAST = 4.5\n\n/** How much lightness each step of a background adjustment removes or adds */\nconst BACKGROUND_LIGHTNESS_STEP = 0.005\n\n/**\n * The color palette generated by {@link buildPalette}: the `palette` config\n * of `buildTheme` from `@sanity/ui/theme`, with every color as a hex string.\n *\n * @public\n */\nexport type GeneratedColorPalette = {black: string; white: string} & Record<\n ColorHueKey,\n Record<ColorTintKey, string>\n>\n\n/**\n * Generates a Sanity UI color palette from the theme options: the accent\n * color replaces the `blue` scale, the text color the `gray` scale, and the\n * backgrounds replace `black` and `white`. All other hues keep their stock\n * `@sanity/color` values. Called with the stock colors, it returns the stock\n * palette byte-for-byte.\n *\n * ```ts\n * import {buildPalette} from '@sanity/themer'\n *\n * const palette = buildPalette({accent: '#556bfc'})\n * ```\n *\n * @public\n */\nexport function buildPalette(options: BuildThemeOptions): GeneratedColorPalette {\n const resolved = resolveThemeOptions(options)\n\n const accentAnchor = clampAnchorLightness(hexToHsl(resolved.accent))\n const textAnchor = clampAnchorLightness(hexToHsl(resolved.text))\n const grayAnchor = applyContrast(textAnchor, accentAnchor, resolved.contrast)\n\n const blueTints = remapTints(color.blue, hexToHsl(DEFAULT_ACCENT), accentAnchor)\n const grayTints = remapTints(color.gray, hexToHsl(DEFAULT_TEXT), grayAnchor)\n\n const anchorLuminances = [\n relativeLuminance(hslToHex(grayAnchor)),\n relativeLuminance(hslToHex(accentAnchor)),\n ]\n\n return {\n black: darkenBackground(resolved.background.dark, {\n anchorLuminances,\n tints: [grayTints['400'], blueTints['400']],\n }),\n white: lightenBackground(resolved.background.light, anchorLuminances),\n gray: grayTints,\n blue: blueTints,\n purple: tintHexes(color.purple),\n magenta: tintHexes(color.magenta),\n red: tintHexes(color.red),\n orange: tintHexes(color.orange),\n yellow: tintHexes(color.yellow),\n green: tintHexes(color.green),\n cyan: tintHexes(color.cyan),\n }\n}\n\n/**\n * Mixes the accent color into the text color according to the contrast: 85\n * applies the text color as-is, 100 removes its tint entirely, and lower\n * values rotate and saturate it towards the accent.\n */\nfunction applyContrast(text: Hsl, accent: Hsl, contrast: number): Hsl {\n if (contrast === DEFAULT_CONTRAST) {\n return text\n }\n\n if (contrast > DEFAULT_CONTRAST) {\n const amount = (contrast - DEFAULT_CONTRAST) / (MAXIMUM_CONTRAST - DEFAULT_CONTRAST)\n\n return {h: text.h, s: text.s * (1 - amount), l: text.l}\n }\n\n const amount = (DEFAULT_CONTRAST - contrast) / (DEFAULT_CONTRAST - MINIMUM_CONTRAST)\n\n return {\n h: mixHue(text.h, accent.h, amount),\n s: text.s + (accent.s - text.s) * amount,\n l: text.l,\n }\n}\n\n/**\n * Rebuilds a stock tint scale around a new anchor color, where `from` is the\n * scale's stock 500 tint: hues rotate along, saturation scales\n * proportionally, and lightness is remapped piecewise so the anchor takes the\n * new lightness while the scale keeps spanning light to dark. When the anchor\n * matches the stock one, every tint comes back unchanged.\n */\nfunction remapTints(tints: ColorTints, from: Hsl, to: Hsl): Record<ColorTintKey, string> {\n const rotation = to.h - from.h\n const result: Partial<Record<ColorTintKey, string>> = {}\n\n for (const tint of COLOR_TINTS) {\n const value = hexToHsl(tints[tint].hex)\n\n result[tint] = hslToHex({\n h: value.h + rotation,\n s: remapSaturation(value.s, from.s, to.s),\n l: remapLightness(value.l, from.l, to.l),\n })\n }\n\n // oxlint-disable-next-line no-unsafe-type-assertion -- the loop assigns every tint key\n return result as Record<ColorTintKey, string>\n}\n\nfunction remapSaturation(value: number, from: number, to: number): number {\n if (from === to) {\n return value\n }\n\n return Math.min(1, value * (to / from))\n}\n\nfunction remapLightness(value: number, from: number, to: number): number {\n if (from === to) {\n return value\n }\n\n if (value <= from) {\n return value * (to / from)\n }\n\n return to + ((value - from) * (1 - to)) / (1 - from)\n}\n\nfunction clampAnchorLightness(anchor: Hsl): Hsl {\n const l = Math.min(MAXIMUM_ANCHOR_LIGHTNESS, Math.max(MINIMUM_ANCHOR_LIGHTNESS, anchor.l))\n\n return l === anchor.l ? anchor : {...anchor, l}\n}\n\n/**\n * Enforces the dark background rules: it can never be lighter than the text\n * or accent colors, and it must reach enough contrast with the 400 tints of\n * their scales — it is made darker until it does (or bottoms out at black).\n */\nfunction darkenBackground(\n hex: string,\n rules: {anchorLuminances: number[]; tints: string[]},\n): string {\n const maximumLuminance = Math.min(...rules.anchorLuminances)\n const tintLuminances = rules.tints.map((tint) => relativeLuminance(tint))\n\n const isDarkEnough = (background: string): boolean => {\n const luminance = relativeLuminance(background)\n\n return (\n luminance < maximumLuminance &&\n tintLuminances.every(\n (tintLuminance) =>\n (tintLuminance + 0.05) / (luminance + 0.05) >= MINIMUM_DARK_BACKGROUND_CONTRAST,\n )\n )\n }\n\n const {h, s, l} = hexToHsl(hex)\n let lightness = l\n let current = hex\n\n while (!isDarkEnough(current) && lightness > 0) {\n lightness = Math.max(0, lightness - BACKGROUND_LIGHTNESS_STEP)\n current = hslToHex({h, s, l: lightness})\n }\n\n return current\n}\n\n/**\n * Enforces the light background rule: it can never be darker than the text\n * or accent colors — it is made lighter until it is (or tops out at white).\n */\nfunction lightenBackground(hex: string, anchorLuminances: number[]): string {\n const minimumLuminance = Math.max(...anchorLuminances)\n\n const {h, s, l} = hexToHsl(hex)\n let lightness = l\n let current = hex\n\n while (relativeLuminance(current) <= minimumLuminance && lightness < 1) {\n lightness = Math.min(1, lightness + BACKGROUND_LIGHTNESS_STEP)\n current = hslToHex({h, s, l: lightness})\n }\n\n return current\n}\n\nfunction tintHexes(tints: ColorTints): Record<ColorTintKey, string> {\n const result: Partial<Record<ColorTintKey, string>> = {}\n\n for (const tint of COLOR_TINTS) {\n result[tint] = tints[tint].hex\n }\n\n // oxlint-disable-next-line no-unsafe-type-assertion -- the loop assigns every tint key\n return result as Record<ColorTintKey, string>\n}\n","import {buildTheme as buildUITheme, RootTheme} from '@sanity/ui/theme'\n\nimport {buildPalette} from './buildPalette'\nimport {BuildThemeOptions} from './options'\n\n/**\n * Generates a Studio theme from a handful of colors, by replacing the color\n * palette that the `buildTheme` from `@sanity/ui/theme` otherwise fills with\n * `@sanity/color`: the accent color replaces the `blue` scale (primary\n * buttons, focus rings, links), the optional text color the `gray` scale\n * (text, icons, borders), and the optional backgrounds replace `black` and\n * `white`. The result is the same type of theme that `@sanity/ui/theme`\n * builds, ready for the `theme` property of a Studio config.\n *\n * ```ts\n * import {buildTheme} from '@sanity/themer'\n * import {defineConfig} from 'sanity'\n *\n * export const theme = buildTheme({\n * accent: '#556bfc',\n * text: '#727892', // optional — derived from `accent` when omitted\n * background: {dark: '#0d0e12', light: '#ffffff'}, // optional\n * contrast: 85, // optional, 15–100\n * })\n *\n * export default defineConfig({\n * theme,\n * // ...rest of the config\n * })\n * ```\n *\n * Called with the stock colors — like the example — it returns the exact\n * same colors as `buildTheme()` from `@sanity/ui/theme` with no options.\n *\n * @public\n */\nexport function buildTheme(options: BuildThemeOptions): RootTheme {\n return buildUITheme({palette: buildPalette(options)})\n}\n","import {BuildThemeOptions, DEFAULT_ACCENT} from './options'\n\n/**\n * A preset theme: a named set of {@link BuildThemeOptions} ready to pass to\n * `buildTheme`.\n *\n * @public\n */\nexport interface ThemePreset {\n slug: string\n title: string\n /** The preset's theme options, ready to pass to `buildTheme` */\n options: BuildThemeOptions\n}\n\n/**\n * Preset themes for `buildTheme`, carried over from the hosted Themer service\n * (themer.sanity.build) presets: each one maps the legacy preset's primary\n * hue to `accent`, its default hue to `text` and its lightest/darkest colors\n * to the backgrounds.\n *\n * ```ts\n * import {buildTheme, presets} from '@sanity/themer'\n *\n * const verdant = presets.find((preset) => preset.slug === 'verdant')\n * const theme = buildTheme(verdant.options)\n * ```\n *\n * @public\n */\nexport const presets: ThemePreset[] = [\n {\n slug: 'studio',\n title: 'Studio',\n options: {accent: DEFAULT_ACCENT},\n },\n {\n slug: 'dew',\n title: 'Dew',\n options: {\n accent: '#d1a308',\n text: '#5e63b4',\n background: {dark: '#0d0d15', light: '#fcfcfd'},\n },\n },\n {\n slug: 'pink-synth',\n title: 'Pink Synth',\n options: {\n accent: '#ec4899',\n text: '#8b6584',\n background: {dark: '#171721', light: '#f7f2f5'},\n },\n },\n {\n slug: 'pixel-art',\n title: 'Pixel Art',\n options: {\n accent: '#f10784',\n text: '#57619c',\n background: {dark: '#0d0e15', light: '#fcfcfd'},\n },\n },\n {\n slug: 'retro-colonial',\n title: 'Retro Colonial',\n options: {\n accent: '#fa7a78',\n text: '#8bb9b5',\n background: {dark: '#0d1515', light: '#fcfdfd'},\n },\n },\n {\n slug: 'rosabel',\n title: 'Rosabel',\n options: {\n accent: '#ed2555',\n text: '#9d8966',\n background: {dark: '#15120d', light: '#fdfdfc'},\n },\n },\n {\n slug: 'stereofidelic',\n title: 'Stereofidelic',\n options: {\n accent: '#f13009',\n text: '#678e9a',\n background: {dark: '#0e1315', light: '#fcfdfd'},\n },\n },\n {\n slug: 'verdant',\n title: 'Verdant',\n options: {\n accent: '#1cb485',\n text: '#5c9199',\n background: {dark: '#0d1415', light: '#fcfdfd'},\n },\n },\n]\n"],"mappings":"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"}
package/dist/tool.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as PartialHues } from "./types-DfPLuLv0.js";
1
+ import { t as BuildThemeOptions } from "./options-BaqJJgRs.js";
2
2
  /**
3
3
  * Options for the {@link themerTool} plugin.
4
4
  *
@@ -9,26 +9,26 @@ import { a as PartialHues } from "./types-DfPLuLv0.js";
9
9
  */
10
10
  interface ThemerToolOptions {
11
11
  /**
12
- * The hues that the Studio's configured theme was generated from — the
13
- * themer starts editing from these, so pass the same object that the
14
- * `theme` in the Studio config uses:
12
+ * The `buildTheme` options that the Studio's configured theme was generated
13
+ * from — the themer starts editing from these, so pass the same object that
14
+ * the `theme` in the Studio config uses:
15
15
  *
16
16
  * ```ts
17
- * const hues = parseHuesFromUrl('https://themer.sanity.build/api/hues?preset=verdant')
17
+ * const config: BuildThemeOptions = {accent: '#1cb485'}
18
18
  *
19
19
  * export default defineConfig({
20
- * theme: createTheme(hues),
21
- * plugins: [themerTool({hues})],
20
+ * theme: buildTheme(config),
21
+ * plugins: [themerTool({config})],
22
22
  * })
23
23
  * ```
24
24
  */
25
- hues?: PartialHues;
25
+ config?: BuildThemeOptions;
26
26
  }
27
27
  /**
28
- * A Studio plugin that adds a themer sidebar for the legacy Themer themes:
29
- * a navbar toggle opens the sidebar next to the active tool, where presets,
30
- * per-hue editors and pasted themer.sanity.build URLs preview a legacy
31
- * `createTheme` theme live on the whole Studio while you browse around.
28
+ * A Studio plugin that adds a themer sidebar for `buildTheme` themes: a
29
+ * navbar toggle opens the sidebar next to the active tool, where presets, the
30
+ * accent/text/background pickers and the contrast slider preview a
31
+ * `buildTheme` theme live on the whole Studio while you browse around.
32
32
  * Toggle between light and dark mode with the regular appearance menu — the
33
33
  * preview follows it.
34
34
  *
@@ -1 +1 @@
1
- {"version":3,"file":"tool.d.ts","names":[],"sources":["../src/tool/plugin.tsx"],"mappings":";;;;;;;;;UAgBiB;;;;;;;;;;;;;;;EAef,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;cA0BI,6BAAU,cAAA"}
1
+ {"version":3,"file":"tool.d.ts","names":[],"sources":["../src/tool/plugin.tsx"],"mappings":";;;;;;;;;UAeiB;;;;;;;;;;;;;;;EAef,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;cA0BE,6BAAU,cAAA"}