tempest-react-sdk 0.24.0 → 0.25.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/README.md +11 -3
- package/dist/charts/AreaChart.cjs +1 -1
- package/dist/charts/AreaChart.cjs.map +1 -1
- package/dist/charts/AreaChart.js +7 -7
- package/dist/charts/AreaChart.js.map +1 -1
- package/dist/charts/BarChart.cjs +1 -1
- package/dist/charts/BarChart.cjs.map +1 -1
- package/dist/charts/BarChart.js +7 -7
- package/dist/charts/BarChart.js.map +1 -1
- package/dist/charts/LineChart.cjs +1 -1
- package/dist/charts/LineChart.cjs.map +1 -1
- package/dist/charts/LineChart.js +6 -6
- package/dist/charts/LineChart.js.map +1 -1
- package/dist/charts/PieChart.cjs +1 -1
- package/dist/charts/PieChart.cjs.map +1 -1
- package/dist/charts/PieChart.js +6 -6
- package/dist/charts/PieChart.js.map +1 -1
- package/dist/charts/RadarChart.cjs +1 -1
- package/dist/charts/RadarChart.cjs.map +1 -1
- package/dist/charts/RadarChart.js +6 -6
- package/dist/charts/RadarChart.js.map +1 -1
- package/dist/charts/palette.cjs +1 -1
- package/dist/charts/palette.cjs.map +1 -1
- package/dist/charts/palette.js +19 -2
- package/dist/charts/palette.js.map +1 -1
- package/dist/charts/use-chart-colors.cjs +2 -0
- package/dist/charts/use-chart-colors.cjs.map +1 -0
- package/dist/charts/use-chart-colors.js +29 -0
- package/dist/charts/use-chart-colors.js.map +1 -0
- package/dist/charts.cjs +1 -1
- package/dist/charts.d.ts +59 -10
- package/dist/charts.js +8 -7
- package/dist/components/AvatarGroup/AvatarGroup.cjs +2 -0
- package/dist/components/AvatarGroup/AvatarGroup.cjs.map +1 -0
- package/dist/components/AvatarGroup/AvatarGroup.js +39 -0
- package/dist/components/AvatarGroup/AvatarGroup.js.map +1 -0
- package/dist/components/AvatarGroup/AvatarGroup.module.cjs +2 -0
- package/dist/components/AvatarGroup/AvatarGroup.module.cjs.map +1 -0
- package/dist/components/AvatarGroup/AvatarGroup.module.js +16 -0
- package/dist/components/AvatarGroup/AvatarGroup.module.js.map +1 -0
- package/dist/components/Lightbox/Lightbox.cjs +2 -0
- package/dist/components/Lightbox/Lightbox.cjs.map +1 -0
- package/dist/components/Lightbox/Lightbox.js +148 -0
- package/dist/components/Lightbox/Lightbox.js.map +1 -0
- package/dist/components/Lightbox/Lightbox.module.cjs +2 -0
- package/dist/components/Lightbox/Lightbox.module.cjs.map +1 -0
- package/dist/components/Lightbox/Lightbox.module.js +21 -0
- package/dist/components/Lightbox/Lightbox.module.js.map +1 -0
- package/dist/components/SignaturePad/SignaturePad.cjs +2 -0
- package/dist/components/SignaturePad/SignaturePad.cjs.map +1 -0
- package/dist/components/SignaturePad/SignaturePad.js +115 -0
- package/dist/components/SignaturePad/SignaturePad.js.map +1 -0
- package/dist/components/SignaturePad/SignaturePad.module.cjs +2 -0
- package/dist/components/SignaturePad/SignaturePad.module.cjs.map +1 -0
- package/dist/components/SignaturePad/SignaturePad.module.js +11 -0
- package/dist/components/SignaturePad/SignaturePad.module.js.map +1 -0
- package/dist/components/Stepper/Stepper.cjs +1 -1
- package/dist/components/Stepper/Stepper.cjs.map +1 -1
- package/dist/components/Stepper/Stepper.js +43 -19
- package/dist/components/Stepper/Stepper.js.map +1 -1
- package/dist/components/Stepper/Stepper.module.cjs +1 -1
- package/dist/components/Stepper/Stepper.module.cjs.map +1 -1
- package/dist/components/Stepper/Stepper.module.js +6 -3
- package/dist/components/Stepper/Stepper.module.js.map +1 -1
- package/dist/components/TreeView/TreeView.cjs +2 -0
- package/dist/components/TreeView/TreeView.cjs.map +1 -0
- package/dist/components/TreeView/TreeView.js +124 -0
- package/dist/components/TreeView/TreeView.js.map +1 -0
- package/dist/components/TreeView/TreeView.module.cjs +2 -0
- package/dist/components/TreeView/TreeView.module.cjs.map +1 -0
- package/dist/components/TreeView/TreeView.module.js +17 -0
- package/dist/components/TreeView/TreeView.module.js.map +1 -0
- package/dist/components/Wizard/Wizard.cjs +2 -0
- package/dist/components/Wizard/Wizard.cjs.map +1 -0
- package/dist/components/Wizard/Wizard.js +111 -0
- package/dist/components/Wizard/Wizard.js.map +1 -0
- package/dist/components/Wizard/Wizard.module.cjs +2 -0
- package/dist/components/Wizard/Wizard.module.cjs.map +1 -0
- package/dist/components/Wizard/Wizard.module.js +10 -0
- package/dist/components/Wizard/Wizard.module.js.map +1 -0
- package/dist/offline/create-offline-sync.cjs +1 -1
- package/dist/offline/create-offline-sync.cjs.map +1 -1
- package/dist/offline/create-offline-sync.js +8 -2
- package/dist/offline/create-offline-sync.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/tempest-react-sdk.cjs +1 -1
- package/dist/tempest-react-sdk.d.ts +605 -2
- package/dist/tempest-react-sdk.js +255 -246
- package/dist/theme/apply-theme.cjs +2 -0
- package/dist/theme/apply-theme.cjs.map +1 -0
- package/dist/theme/apply-theme.js +24 -0
- package/dist/theme/apply-theme.js.map +1 -0
- package/dist/theme/color.cjs +2 -0
- package/dist/theme/color.cjs.map +1 -0
- package/dist/theme/color.js +130 -0
- package/dist/theme/color.js.map +1 -0
- package/dist/theme/create-theme.cjs +5 -0
- package/dist/theme/create-theme.cjs.map +1 -0
- package/dist/theme/create-theme.js +135 -0
- package/dist/theme/create-theme.js.map +1 -0
- package/dist/theme/theme-presets.cjs +2 -0
- package/dist/theme/theme-presets.cjs.map +1 -0
- package/dist/theme/theme-presets.js +82 -0
- package/dist/theme/theme-presets.js.map +1 -0
- package/dist/utilities.css +432 -0
- package/package.json +4 -3
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=`tempest-theme`;function t(e){return e?typeof Document<`u`&&e instanceof Document?e.head:e:typeof document>`u`?null:document.head}function n(n,r={}){let{id:i=e,target:a}=r,o=t(a);if(!o||typeof document>`u`)return()=>{};let s=typeof n==`string`?n:n.css,c=Array.from(o.childNodes).find(e=>e instanceof HTMLStyleElement&&e.id===i);return c||(c=document.createElement(`style`),c.id=i,o.appendChild(c)),c.textContent=s,()=>{c?.remove()}}function r(e,t){if(typeof window>`u`||typeof document>`u`||t===null)return``;let n=t??document.documentElement;if(!n)return``;let r=e.startsWith(`--`)?e:`--${e}`;return window.getComputedStyle(n).getPropertyValue(r).trim()}exports.THEME_STYLE_ID=e,exports.applyTheme=n,exports.readThemeToken=r;
|
|
2
|
+
//# sourceMappingURL=apply-theme.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply-theme.cjs","names":[],"sources":["../../src/theme/apply-theme.ts"],"sourcesContent":["/**\n * Runtime installer for a {@link GeneratedTheme}.\n *\n * A generated theme is just CSS text, so it needs a `<style>` element to take\n * effect. This owns that element by id, which keeps re-applying idempotent — a\n * theme picker can call `applyTheme` on every click without stacking dead\n * stylesheets in `<head>`.\n */\nimport type { GeneratedTheme } from \"./create-theme\";\n\n/** Options for {@link applyTheme}. */\nexport interface ApplyThemeOptions {\n /**\n * `id` of the managed `<style>` element. Reusing the same id replaces the\n * previous theme; a distinct id lets two themes coexist (e.g. a scoped\n * preview under its own selector). Default `\"tempest-theme\"`.\n */\n id?: string;\n /**\n * Node the `<style>` is appended to. Default `document.head`. Pass a shadow\n * root to theme a single web component.\n */\n target?: Document | ShadowRoot | HTMLElement;\n}\n\n/** The default `<style>` element id owned by {@link applyTheme}. */\nexport const THEME_STYLE_ID = \"tempest-theme\";\n\nfunction resolveContainer(target: ApplyThemeOptions[\"target\"]): ParentNode | null {\n if (!target) {\n return typeof document === \"undefined\" ? null : document.head;\n }\n if (typeof Document !== \"undefined\" && target instanceof Document) {\n return target.head;\n }\n return target;\n}\n\n/**\n * Install a generated theme (or raw CSS) into the document.\n *\n * Safe to call outside a browser: with no `document` it is a no-op returning a\n * no-op disposer, so app bootstrap code does not need a `typeof window` guard.\n *\n * @param theme - A {@link GeneratedTheme} from `createTheme`, or CSS text.\n * @param options - Style element id and mount target.\n * @returns A function that removes the injected `<style>` element.\n *\n * @example\n * ```ts\n * import { applyTheme, createTheme, themePresets } from \"tempest-react-sdk\";\n *\n * const dispose = applyTheme(createTheme(themePresets.violet));\n * // later: dispose();\n * ```\n */\nexport function applyTheme(\n theme: GeneratedTheme | string,\n options: ApplyThemeOptions = {},\n): () => void {\n const { id = THEME_STYLE_ID, target } = options;\n const container = resolveContainer(target);\n if (!container || typeof document === \"undefined\") {\n return () => {};\n }\n\n const css = typeof theme === \"string\" ? theme : theme.css;\n\n let style = Array.from(container.childNodes).find(\n (node): node is HTMLStyleElement => node instanceof HTMLStyleElement && node.id === id,\n );\n\n if (!style) {\n style = document.createElement(\"style\");\n style.id = id;\n container.appendChild(style);\n }\n\n style.textContent = css;\n\n return () => {\n style?.remove();\n };\n}\n\n/**\n * Read a token's computed value from an element (default: `<html>`).\n *\n * Useful to bridge CSS tokens into JS that cannot take a `var()` — canvas\n * drawing, chart libraries that set SVG attributes, `<meta name=\"theme-color\">`.\n *\n * @param name - Token name, with or without the leading `--`.\n * @param element - Element to resolve against. Omit for `document.documentElement`;\n * an explicit `null` (a ref that has not attached yet) reads nothing rather than\n * silently falling back to the root, whose value may be a different theme.\n * @returns The trimmed value, or `\"\"` when unset or outside a browser.\n */\nexport function readThemeToken(name: string, element?: Element | null): string {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return \"\";\n if (element === null) return \"\";\n const target = element ?? document.documentElement;\n if (!target) return \"\";\n const property = name.startsWith(\"--\") ? name : `--${name}`;\n return window.getComputedStyle(target).getPropertyValue(property).trim();\n}\n"],"mappings":"AA0BA,IAAa,EAAiB,gBAE9B,SAAS,EAAiB,EAAwD,CAO9E,OANK,EAGD,OAAO,SAAa,KAAe,aAAkB,SAC9C,EAAO,KAEX,EALI,OAAO,SAAa,IAAc,KAAO,SAAS,IAMjE,CAoBA,SAAgB,EACZ,EACA,EAA6B,CAAC,EACpB,CACV,GAAM,CAAE,KAAK,EAAgB,UAAW,EAClC,EAAY,EAAiB,CAAM,EACzC,GAAI,CAAC,GAAa,OAAO,SAAa,IAClC,UAAa,CAAC,EAGlB,IAAM,EAAM,OAAO,GAAU,SAAW,EAAQ,EAAM,IAElD,EAAQ,MAAM,KAAK,EAAU,UAAU,CAAC,CAAC,KACxC,GAAmC,aAAgB,kBAAoB,EAAK,KAAO,CACxF,EAUA,OARK,IACD,EAAQ,SAAS,cAAc,OAAO,EACtC,EAAM,GAAK,EACX,EAAU,YAAY,CAAK,GAG/B,EAAM,YAAc,MAEP,CACT,GAAO,OAAO,CAClB,CACJ,CAcA,SAAgB,EAAe,EAAc,EAAkC,CAE3E,GADI,OAAO,OAAW,KAAe,OAAO,SAAa,KACrD,IAAY,KAAM,MAAO,GAC7B,IAAM,EAAS,GAAW,SAAS,gBACnC,GAAI,CAAC,EAAQ,MAAO,GACpB,IAAM,EAAW,EAAK,WAAW,IAAI,EAAI,EAAO,KAAK,IACrD,OAAO,OAAO,iBAAiB,CAAM,CAAC,CAAC,iBAAiB,CAAQ,CAAC,CAAC,KAAK,CAC3E"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region src/theme/apply-theme.ts
|
|
2
|
+
var e = "tempest-theme";
|
|
3
|
+
function t(e) {
|
|
4
|
+
return e ? typeof Document < "u" && e instanceof Document ? e.head : e : typeof document > "u" ? null : document.head;
|
|
5
|
+
}
|
|
6
|
+
function n(n, r = {}) {
|
|
7
|
+
let { id: i = e, target: a } = r, o = t(a);
|
|
8
|
+
if (!o || typeof document > "u") return () => {};
|
|
9
|
+
let s = typeof n == "string" ? n : n.css, c = Array.from(o.childNodes).find((e) => e instanceof HTMLStyleElement && e.id === i);
|
|
10
|
+
return c || (c = document.createElement("style"), c.id = i, o.appendChild(c)), c.textContent = s, () => {
|
|
11
|
+
c?.remove();
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function r(e, t) {
|
|
15
|
+
if (typeof window > "u" || typeof document > "u" || t === null) return "";
|
|
16
|
+
let n = t ?? document.documentElement;
|
|
17
|
+
if (!n) return "";
|
|
18
|
+
let r = e.startsWith("--") ? e : `--${e}`;
|
|
19
|
+
return window.getComputedStyle(n).getPropertyValue(r).trim();
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
export { e as THEME_STYLE_ID, n as applyTheme, r as readThemeToken };
|
|
23
|
+
|
|
24
|
+
//# sourceMappingURL=apply-theme.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply-theme.js","names":[],"sources":["../../src/theme/apply-theme.ts"],"sourcesContent":["/**\n * Runtime installer for a {@link GeneratedTheme}.\n *\n * A generated theme is just CSS text, so it needs a `<style>` element to take\n * effect. This owns that element by id, which keeps re-applying idempotent — a\n * theme picker can call `applyTheme` on every click without stacking dead\n * stylesheets in `<head>`.\n */\nimport type { GeneratedTheme } from \"./create-theme\";\n\n/** Options for {@link applyTheme}. */\nexport interface ApplyThemeOptions {\n /**\n * `id` of the managed `<style>` element. Reusing the same id replaces the\n * previous theme; a distinct id lets two themes coexist (e.g. a scoped\n * preview under its own selector). Default `\"tempest-theme\"`.\n */\n id?: string;\n /**\n * Node the `<style>` is appended to. Default `document.head`. Pass a shadow\n * root to theme a single web component.\n */\n target?: Document | ShadowRoot | HTMLElement;\n}\n\n/** The default `<style>` element id owned by {@link applyTheme}. */\nexport const THEME_STYLE_ID = \"tempest-theme\";\n\nfunction resolveContainer(target: ApplyThemeOptions[\"target\"]): ParentNode | null {\n if (!target) {\n return typeof document === \"undefined\" ? null : document.head;\n }\n if (typeof Document !== \"undefined\" && target instanceof Document) {\n return target.head;\n }\n return target;\n}\n\n/**\n * Install a generated theme (or raw CSS) into the document.\n *\n * Safe to call outside a browser: with no `document` it is a no-op returning a\n * no-op disposer, so app bootstrap code does not need a `typeof window` guard.\n *\n * @param theme - A {@link GeneratedTheme} from `createTheme`, or CSS text.\n * @param options - Style element id and mount target.\n * @returns A function that removes the injected `<style>` element.\n *\n * @example\n * ```ts\n * import { applyTheme, createTheme, themePresets } from \"tempest-react-sdk\";\n *\n * const dispose = applyTheme(createTheme(themePresets.violet));\n * // later: dispose();\n * ```\n */\nexport function applyTheme(\n theme: GeneratedTheme | string,\n options: ApplyThemeOptions = {},\n): () => void {\n const { id = THEME_STYLE_ID, target } = options;\n const container = resolveContainer(target);\n if (!container || typeof document === \"undefined\") {\n return () => {};\n }\n\n const css = typeof theme === \"string\" ? theme : theme.css;\n\n let style = Array.from(container.childNodes).find(\n (node): node is HTMLStyleElement => node instanceof HTMLStyleElement && node.id === id,\n );\n\n if (!style) {\n style = document.createElement(\"style\");\n style.id = id;\n container.appendChild(style);\n }\n\n style.textContent = css;\n\n return () => {\n style?.remove();\n };\n}\n\n/**\n * Read a token's computed value from an element (default: `<html>`).\n *\n * Useful to bridge CSS tokens into JS that cannot take a `var()` — canvas\n * drawing, chart libraries that set SVG attributes, `<meta name=\"theme-color\">`.\n *\n * @param name - Token name, with or without the leading `--`.\n * @param element - Element to resolve against. Omit for `document.documentElement`;\n * an explicit `null` (a ref that has not attached yet) reads nothing rather than\n * silently falling back to the root, whose value may be a different theme.\n * @returns The trimmed value, or `\"\"` when unset or outside a browser.\n */\nexport function readThemeToken(name: string, element?: Element | null): string {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return \"\";\n if (element === null) return \"\";\n const target = element ?? document.documentElement;\n if (!target) return \"\";\n const property = name.startsWith(\"--\") ? name : `--${name}`;\n return window.getComputedStyle(target).getPropertyValue(property).trim();\n}\n"],"mappings":";AA0BA,IAAa,IAAiB;AAE9B,SAAS,EAAiB,GAAwD;CAO9E,OANK,IAGD,OAAO,WAAa,OAAe,aAAkB,WAC9C,EAAO,OAEX,IALI,OAAO,WAAa,MAAc,OAAO,SAAS;AAMjE;AAoBA,SAAgB,EACZ,GACA,IAA6B,CAAC,GACpB;CACV,IAAM,EAAE,QAAK,GAAgB,cAAW,GAClC,IAAY,EAAiB,CAAM;CACzC,IAAI,CAAC,KAAa,OAAO,WAAa,KAClC,aAAa,CAAC;CAGlB,IAAM,IAAM,OAAO,KAAU,WAAW,IAAQ,EAAM,KAElD,IAAQ,MAAM,KAAK,EAAU,UAAU,CAAC,CAAC,MACxC,MAAmC,aAAgB,oBAAoB,EAAK,OAAO,CACxF;CAUA,OARK,MACD,IAAQ,SAAS,cAAc,OAAO,GACtC,EAAM,KAAK,GACX,EAAU,YAAY,CAAK,IAG/B,EAAM,cAAc,SAEP;EACT,GAAO,OAAO;CAClB;AACJ;AAcA,SAAgB,EAAe,GAAc,GAAkC;CAE3E,IADI,OAAO,SAAW,OAAe,OAAO,WAAa,OACrD,MAAY,MAAM,OAAO;CAC7B,IAAM,IAAS,KAAW,SAAS;CACnC,IAAI,CAAC,GAAQ,OAAO;CACpB,IAAM,IAAW,EAAK,WAAW,IAAI,IAAI,IAAO,KAAK;CACrD,OAAO,OAAO,iBAAiB,CAAM,CAAC,CAAC,iBAAiB,CAAQ,CAAC,CAAC,KAAK;AAC3E"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=[50,100,200,300,400,500,600,700,800,900],t={50:.972,100:.938,200:.878,300:.798,400:.712,500:.624,600:.542,700:.458,800:.378,900:.292},n={50:.238,100:.288,200:.362,300:.452,400:.544,500:.632,600:.712,700:.788,800:.862,900:.928},r={50:.18,100:.34,200:.6,300:.82,400:.95,500:1,600:.98,700:.88,800:.74,900:.58};function i(e,t,n){return Math.min(n,Math.max(t,e))}function a(e){let t=e.trim().replace(/^#/,``),n=t.length===3||t.length===4?t.slice(0,3).split(``).map(e=>e+e).join(``):t.slice(0,6);if(!/^[0-9a-fA-F]{6}$/.test(n))throw Error(`Invalid hex color: "${e}"`);return`#${n.toLowerCase()}`}function o(e){let t=a(e).slice(1);return{r:parseInt(t.slice(0,2),16)/255,g:parseInt(t.slice(2,4),16)/255,b:parseInt(t.slice(4,6),16)/255}}function s(e,t,n){let r=e=>Math.round(i(e,0,1)*255).toString(16).padStart(2,`0`);return`#${r(e)}${r(t)}${r(n)}`}function c(e){return e<=.04045?e/12.92:((e+.055)/1.055)**2.4}function l(e){return e<=.0031308?e*12.92:1.055*e**(1/2.4)-.055}function u(e){let{r:t,g:n,b:r}=o(e),i=c(t),a=c(n),s=c(r),l=Math.cbrt(.4122214708*i+.5363325363*a+.0514459929*s),u=Math.cbrt(.2119034982*i+.6806995451*a+.1073969566*s),d=Math.cbrt(.0883024619*i+.2817188376*a+.6299787005*s),f=.2104542553*l+.793617785*u-.0040720468*d,p=1.9779984951*l-2.428592205*u+.4505937099*d,m=.0259040371*l+.7827717662*u-.808675766*d,h=Math.sqrt(p*p+m*m),g=h<1e-6?0:Math.atan2(m,p)*180/Math.PI;return{l:f,c:h,h:g<0?g+360:g}}function d({l:e,c:t,h:n}){let r=n*Math.PI/180,i=Math.cos(r)*t,a=Math.sin(r)*t,o=(e+.3963377774*i+.2158037573*a)**3,s=(e-.1055613458*i-.0638541728*a)**3,c=(e-.0894841775*i-1.291485548*a)**3;return{r:4.0767416621*o-3.3077115913*s+.2309699292*c,g:-1.2684380046*o+2.6097574011*s-.3413193965*c,b:-.0041960863*o-.7034186147*s+1.707614701*c}}function f({r:e,g:t,b:n}){return e>=-1e-4&&e<=1.0001&&t>=-1e-4&&t<=1.0001&&n>=-1e-4&&n<=1.0001}function p(e){let t=Math.max(0,e.c),n=d({...e,c:t});for(let r=0;r<24&&!f(n);r+=1)t*=.9,n=d({...e,c:t});return s(l(n.r),l(n.g),l(n.b))}function m(i,a=`light`){let o=u(i),s=a===`dark`?n:t,c={};for(let t of e)c[t]=p({l:s[t],c:o.c*r[t],h:o.h});return c}function h(e){let{r:t,g:n,b:r}=o(e);return .2126*c(t)+.7152*c(n)+.0722*c(r)}function g(e,t){let n=h(e),r=h(t),i=Math.max(n,r),a=Math.min(n,r);return(i+.05)/(a+.05)}function _(e,t=`#ffffff`,n=`#101828`){return g(e,t)>=g(e,n)?t:n}function v(e,t){let{r:n,g:r,b:a}=o(e),s=e=>Math.round(i(e,0,1)*255);return`rgb(${s(n)} ${s(r)} ${s(a)} / ${i(t,0,1)})`}exports.contrastRatio=g,exports.createColorScale=m,exports.hexToOklch=u,exports.hexToRgb=o,exports.hexToRgbaString=v,exports.oklchToHex=p,exports.readableForeground=_,exports.relativeLuminance=h,exports.rgbToHex=s;
|
|
2
|
+
//# sourceMappingURL=color.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color.cjs","names":[],"sources":["../../src/theme/color.ts"],"sourcesContent":["/**\n * Color math behind {@link createTheme} — OKLab/OKLCH conversions, tint scale\n * generation and WCAG contrast picking.\n *\n * Everything here is pure and dependency-free: the SDK generates brand ramps at\n * runtime in the browser, so a color library would be a disproportionate cost\n * for ~150 lines of well-specified math. OKLCH is used instead of HSL because\n * HSL lightness is not perceptual — an HSL ramp of a yellow and of a blue at the\n * same `L` read as wildly different brightness, which is exactly what breaks a\n * generated palette.\n */\n\n/** A color in the OKLCH space: perceptual lightness, chroma and hue. */\nexport interface Oklch {\n /** Perceptual lightness, `0` (black) to `1` (white). */\n l: number;\n /** Chroma (colorfulness). `0` is gray; sRGB rarely exceeds `~0.37`. */\n c: number;\n /** Hue angle in degrees, `0`–`360`. */\n h: number;\n}\n\n/** The ten steps of a Tempest tint scale, lightest to darkest. */\nexport type ScaleStep = 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;\n\n/** A generated tint scale, keyed by step. */\nexport type ColorScale = Record<ScaleStep, string>;\n\nconst SCALE_STEPS: ScaleStep[] = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900];\n\n/**\n * Target perceptual lightness per step for a light-theme ramp.\n *\n * Tuned against the hand-written scales in `colors.css` so a generated theme\n * sits in the same visual range as the built-in one: `500` is the action color,\n * `50`/`100` are usable as soft backgrounds, `700`+ carries text on white.\n */\nconst LIGHT_LIGHTNESS: Record<ScaleStep, number> = {\n 50: 0.972,\n 100: 0.938,\n 200: 0.878,\n 300: 0.798,\n 400: 0.712,\n 500: 0.624,\n 600: 0.542,\n 700: 0.458,\n 800: 0.378,\n 900: 0.292,\n};\n\n/**\n * Target lightness for a dark-theme ramp — the ramp is **inverted**: `50` is the\n * darkest tint (a surface) and `900` the lightest (text-grade), matching the\n * `[data-tempest-theme=\"dark\"]` block in `colors.css`.\n */\nconst DARK_LIGHTNESS: Record<ScaleStep, number> = {\n 50: 0.238,\n 100: 0.288,\n 200: 0.362,\n 300: 0.452,\n 400: 0.544,\n 500: 0.632,\n 600: 0.712,\n 700: 0.788,\n 800: 0.862,\n 900: 0.928,\n};\n\n/**\n * Chroma multiplier per step, relative to the input color's chroma.\n *\n * Peaks around `500`–`600` and falls off at both ends: near-white and near-black\n * tints hold very little chroma before they look muddy or leave the sRGB gamut.\n */\nconst CHROMA_CURVE: Record<ScaleStep, number> = {\n 50: 0.18,\n 100: 0.34,\n 200: 0.6,\n 300: 0.82,\n 400: 0.95,\n 500: 1,\n 600: 0.98,\n 700: 0.88,\n 800: 0.74,\n 900: 0.58,\n};\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value));\n}\n\n/** Expand `#abc` to `#aabbcc` and normalize to a lowercase 6-digit hex. */\nfunction normalizeHex(hex: string): string {\n const raw = hex.trim().replace(/^#/, \"\");\n const expanded =\n raw.length === 3 || raw.length === 4\n ? raw\n .slice(0, 3)\n .split(\"\")\n .map((char) => char + char)\n .join(\"\")\n : raw.slice(0, 6);\n if (!/^[0-9a-fA-F]{6}$/.test(expanded)) {\n throw new Error(`Invalid hex color: \"${hex}\"`);\n }\n return `#${expanded.toLowerCase()}`;\n}\n\n/** Parse a hex color into sRGB channels in the `0`–`1` range. */\nexport function hexToRgb(hex: string): { r: number; g: number; b: number } {\n const normalized = normalizeHex(hex).slice(1);\n return {\n r: parseInt(normalized.slice(0, 2), 16) / 255,\n g: parseInt(normalized.slice(2, 4), 16) / 255,\n b: parseInt(normalized.slice(4, 6), 16) / 255,\n };\n}\n\n/** Serialize sRGB channels (`0`–`1`, clamped) back to a `#rrggbb` string. */\nexport function rgbToHex(r: number, g: number, b: number): string {\n const channel = (value: number): string =>\n Math.round(clamp(value, 0, 1) * 255)\n .toString(16)\n .padStart(2, \"0\");\n return `#${channel(r)}${channel(g)}${channel(b)}`;\n}\n\nfunction srgbToLinear(channel: number): number {\n return channel <= 0.04045 ? channel / 12.92 : Math.pow((channel + 0.055) / 1.055, 2.4);\n}\n\nfunction linearToSrgb(channel: number): number {\n return channel <= 0.0031308 ? channel * 12.92 : 1.055 * Math.pow(channel, 1 / 2.4) - 0.055;\n}\n\n/** Convert a hex color to OKLCH. */\nexport function hexToOklch(hex: string): Oklch {\n const { r, g, b } = hexToRgb(hex);\n const lr = srgbToLinear(r);\n const lg = srgbToLinear(g);\n const lb = srgbToLinear(b);\n\n const long = Math.cbrt(0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb);\n const medium = Math.cbrt(0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb);\n const short = Math.cbrt(0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb);\n\n const l = 0.2104542553 * long + 0.793617785 * medium - 0.0040720468 * short;\n const a = 1.9779984951 * long - 2.428592205 * medium + 0.4505937099 * short;\n const bAxis = 0.0259040371 * long + 0.7827717662 * medium - 0.808675766 * short;\n\n const c = Math.sqrt(a * a + bAxis * bAxis);\n const hue = c < 1e-6 ? 0 : (Math.atan2(bAxis, a) * 180) / Math.PI;\n\n return { l, c, h: hue < 0 ? hue + 360 : hue };\n}\n\n/** Convert OKLCH to linear-light sRGB, without gamut clamping. */\nfunction oklchToLinearRgb({ l, c, h }: Oklch): { r: number; g: number; b: number } {\n const hRad = (h * Math.PI) / 180;\n const a = Math.cos(hRad) * c;\n const bAxis = Math.sin(hRad) * c;\n\n const long = (l + 0.3963377774 * a + 0.2158037573 * bAxis) ** 3;\n const medium = (l - 0.1055613458 * a - 0.0638541728 * bAxis) ** 3;\n const short = (l - 0.0894841775 * a - 1.291485548 * bAxis) ** 3;\n\n return {\n r: 4.0767416621 * long - 3.3077115913 * medium + 0.2309699292 * short,\n g: -1.2684380046 * long + 2.6097574011 * medium - 0.3413193965 * short,\n b: -0.0041960863 * long - 0.7034186147 * medium + 1.707614701 * short,\n };\n}\n\nfunction inGamut({ r, g, b }: { r: number; g: number; b: number }): boolean {\n const epsilon = 1e-4;\n return (\n r >= -epsilon &&\n r <= 1 + epsilon &&\n g >= -epsilon &&\n g <= 1 + epsilon &&\n b >= -epsilon &&\n b <= 1 + epsilon\n );\n}\n\n/**\n * Convert OKLCH to a hex color, reducing chroma until the result fits sRGB.\n *\n * Lightness and hue are preserved: desaturating is far less noticeable than\n * shifting either of them, and a naive channel clamp would do both.\n */\nexport function oklchToHex(color: Oklch): string {\n let chroma = Math.max(0, color.c);\n let rgb = oklchToLinearRgb({ ...color, c: chroma });\n\n for (let i = 0; i < 24 && !inGamut(rgb); i += 1) {\n chroma *= 0.9;\n rgb = oklchToLinearRgb({ ...color, c: chroma });\n }\n\n return rgbToHex(linearToSrgb(rgb.r), linearToSrgb(rgb.g), linearToSrgb(rgb.b));\n}\n\n/**\n * Build a ten-step tint scale from a single brand color.\n *\n * The input color's hue is kept throughout and its chroma sets the intensity of\n * the whole ramp, so a muted brand color yields a muted scale instead of being\n * \"corrected\" into something the brand never approved.\n *\n * @param hex - Any hex color (`#abc` or `#aabbcc`), the intended `500` step.\n * @param mode - `\"light\"` for a light→dark ramp, `\"dark\"` for the inverted ramp\n * used under `[data-tempest-theme=\"dark\"]`.\n */\nexport function createColorScale(hex: string, mode: \"light\" | \"dark\" = \"light\"): ColorScale {\n const base = hexToOklch(hex);\n const lightness = mode === \"dark\" ? DARK_LIGHTNESS : LIGHT_LIGHTNESS;\n const scale = {} as ColorScale;\n\n for (const step of SCALE_STEPS) {\n scale[step] = oklchToHex({\n l: lightness[step],\n c: base.c * CHROMA_CURVE[step],\n h: base.h,\n });\n }\n\n return scale;\n}\n\n/** WCAG 2.x relative luminance of a hex color. */\nexport function relativeLuminance(hex: string): number {\n const { r, g, b } = hexToRgb(hex);\n return 0.2126 * srgbToLinear(r) + 0.7152 * srgbToLinear(g) + 0.0722 * srgbToLinear(b);\n}\n\n/** WCAG 2.x contrast ratio between two hex colors, from `1` to `21`. */\nexport function contrastRatio(a: string, b: string): number {\n const la = relativeLuminance(a);\n const lb = relativeLuminance(b);\n const lighter = Math.max(la, lb);\n const darker = Math.min(la, lb);\n return (lighter + 0.05) / (darker + 0.05);\n}\n\n/**\n * Pick the readable foreground for a background, by contrast ratio.\n *\n * Used for `--tempest-primary-foreground`: a generated brand color can land\n * anywhere on the lightness axis, and hardcoding white would silently produce\n * unreadable buttons for light brands (yellow, lime, cyan).\n */\nexport function readableForeground(\n background: string,\n light = \"#ffffff\",\n dark = \"#101828\",\n): string {\n return contrastRatio(background, light) >= contrastRatio(background, dark) ? light : dark;\n}\n\n/** Format a hex color as `rgb(r g b / alpha)`, for focus rings and overlays. */\nexport function hexToRgbaString(hex: string, alpha: number): string {\n const { r, g, b } = hexToRgb(hex);\n const channel = (value: number): number => Math.round(clamp(value, 0, 1) * 255);\n return `rgb(${channel(r)} ${channel(g)} ${channel(b)} / ${clamp(alpha, 0, 1)})`;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
//#region src/theme/color.ts
|
|
2
|
+
var e = [
|
|
3
|
+
50,
|
|
4
|
+
100,
|
|
5
|
+
200,
|
|
6
|
+
300,
|
|
7
|
+
400,
|
|
8
|
+
500,
|
|
9
|
+
600,
|
|
10
|
+
700,
|
|
11
|
+
800,
|
|
12
|
+
900
|
|
13
|
+
], t = {
|
|
14
|
+
50: .972,
|
|
15
|
+
100: .938,
|
|
16
|
+
200: .878,
|
|
17
|
+
300: .798,
|
|
18
|
+
400: .712,
|
|
19
|
+
500: .624,
|
|
20
|
+
600: .542,
|
|
21
|
+
700: .458,
|
|
22
|
+
800: .378,
|
|
23
|
+
900: .292
|
|
24
|
+
}, n = {
|
|
25
|
+
50: .238,
|
|
26
|
+
100: .288,
|
|
27
|
+
200: .362,
|
|
28
|
+
300: .452,
|
|
29
|
+
400: .544,
|
|
30
|
+
500: .632,
|
|
31
|
+
600: .712,
|
|
32
|
+
700: .788,
|
|
33
|
+
800: .862,
|
|
34
|
+
900: .928
|
|
35
|
+
}, r = {
|
|
36
|
+
50: .18,
|
|
37
|
+
100: .34,
|
|
38
|
+
200: .6,
|
|
39
|
+
300: .82,
|
|
40
|
+
400: .95,
|
|
41
|
+
500: 1,
|
|
42
|
+
600: .98,
|
|
43
|
+
700: .88,
|
|
44
|
+
800: .74,
|
|
45
|
+
900: .58
|
|
46
|
+
};
|
|
47
|
+
function i(e, t, n) {
|
|
48
|
+
return Math.min(n, Math.max(t, e));
|
|
49
|
+
}
|
|
50
|
+
function a(e) {
|
|
51
|
+
let t = e.trim().replace(/^#/, ""), n = t.length === 3 || t.length === 4 ? t.slice(0, 3).split("").map((e) => e + e).join("") : t.slice(0, 6);
|
|
52
|
+
if (!/^[0-9a-fA-F]{6}$/.test(n)) throw Error(`Invalid hex color: "${e}"`);
|
|
53
|
+
return `#${n.toLowerCase()}`;
|
|
54
|
+
}
|
|
55
|
+
function o(e) {
|
|
56
|
+
let t = a(e).slice(1);
|
|
57
|
+
return {
|
|
58
|
+
r: parseInt(t.slice(0, 2), 16) / 255,
|
|
59
|
+
g: parseInt(t.slice(2, 4), 16) / 255,
|
|
60
|
+
b: parseInt(t.slice(4, 6), 16) / 255
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function s(e, t, n) {
|
|
64
|
+
let r = (e) => Math.round(i(e, 0, 1) * 255).toString(16).padStart(2, "0");
|
|
65
|
+
return `#${r(e)}${r(t)}${r(n)}`;
|
|
66
|
+
}
|
|
67
|
+
function c(e) {
|
|
68
|
+
return e <= .04045 ? e / 12.92 : ((e + .055) / 1.055) ** 2.4;
|
|
69
|
+
}
|
|
70
|
+
function l(e) {
|
|
71
|
+
return e <= .0031308 ? e * 12.92 : 1.055 * e ** (1 / 2.4) - .055;
|
|
72
|
+
}
|
|
73
|
+
function u(e) {
|
|
74
|
+
let { r: t, g: n, b: r } = o(e), i = c(t), a = c(n), s = c(r), l = Math.cbrt(.4122214708 * i + .5363325363 * a + .0514459929 * s), u = Math.cbrt(.2119034982 * i + .6806995451 * a + .1073969566 * s), d = Math.cbrt(.0883024619 * i + .2817188376 * a + .6299787005 * s), f = .2104542553 * l + .793617785 * u - .0040720468 * d, p = 1.9779984951 * l - 2.428592205 * u + .4505937099 * d, m = .0259040371 * l + .7827717662 * u - .808675766 * d, h = Math.sqrt(p * p + m * m), g = h < 1e-6 ? 0 : Math.atan2(m, p) * 180 / Math.PI;
|
|
75
|
+
return {
|
|
76
|
+
l: f,
|
|
77
|
+
c: h,
|
|
78
|
+
h: g < 0 ? g + 360 : g
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function d({ l: e, c: t, h: n }) {
|
|
82
|
+
let r = n * Math.PI / 180, i = Math.cos(r) * t, a = Math.sin(r) * t, o = (e + .3963377774 * i + .2158037573 * a) ** 3, s = (e - .1055613458 * i - .0638541728 * a) ** 3, c = (e - .0894841775 * i - 1.291485548 * a) ** 3;
|
|
83
|
+
return {
|
|
84
|
+
r: 4.0767416621 * o - 3.3077115913 * s + .2309699292 * c,
|
|
85
|
+
g: -1.2684380046 * o + 2.6097574011 * s - .3413193965 * c,
|
|
86
|
+
b: -.0041960863 * o - .7034186147 * s + 1.707614701 * c
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function f({ r: e, g: t, b: n }) {
|
|
90
|
+
return e >= -1e-4 && e <= 1.0001 && t >= -1e-4 && t <= 1.0001 && n >= -1e-4 && n <= 1.0001;
|
|
91
|
+
}
|
|
92
|
+
function p(e) {
|
|
93
|
+
let t = Math.max(0, e.c), n = d({
|
|
94
|
+
...e,
|
|
95
|
+
c: t
|
|
96
|
+
});
|
|
97
|
+
for (let r = 0; r < 24 && !f(n); r += 1) t *= .9, n = d({
|
|
98
|
+
...e,
|
|
99
|
+
c: t
|
|
100
|
+
});
|
|
101
|
+
return s(l(n.r), l(n.g), l(n.b));
|
|
102
|
+
}
|
|
103
|
+
function m(i, a = "light") {
|
|
104
|
+
let o = u(i), s = a === "dark" ? n : t, c = {};
|
|
105
|
+
for (let t of e) c[t] = p({
|
|
106
|
+
l: s[t],
|
|
107
|
+
c: o.c * r[t],
|
|
108
|
+
h: o.h
|
|
109
|
+
});
|
|
110
|
+
return c;
|
|
111
|
+
}
|
|
112
|
+
function h(e) {
|
|
113
|
+
let { r: t, g: n, b: r } = o(e);
|
|
114
|
+
return .2126 * c(t) + .7152 * c(n) + .0722 * c(r);
|
|
115
|
+
}
|
|
116
|
+
function g(e, t) {
|
|
117
|
+
let n = h(e), r = h(t), i = Math.max(n, r), a = Math.min(n, r);
|
|
118
|
+
return (i + .05) / (a + .05);
|
|
119
|
+
}
|
|
120
|
+
function _(e, t = "#ffffff", n = "#101828") {
|
|
121
|
+
return g(e, t) >= g(e, n) ? t : n;
|
|
122
|
+
}
|
|
123
|
+
function v(e, t) {
|
|
124
|
+
let { r: n, g: r, b: a } = o(e), s = (e) => Math.round(i(e, 0, 1) * 255);
|
|
125
|
+
return `rgb(${s(n)} ${s(r)} ${s(a)} / ${i(t, 0, 1)})`;
|
|
126
|
+
}
|
|
127
|
+
//#endregion
|
|
128
|
+
export { g as contrastRatio, m as createColorScale, u as hexToOklch, o as hexToRgb, v as hexToRgbaString, p as oklchToHex, _ as readableForeground, h as relativeLuminance, s as rgbToHex };
|
|
129
|
+
|
|
130
|
+
//# sourceMappingURL=color.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color.js","names":[],"sources":["../../src/theme/color.ts"],"sourcesContent":["/**\n * Color math behind {@link createTheme} — OKLab/OKLCH conversions, tint scale\n * generation and WCAG contrast picking.\n *\n * Everything here is pure and dependency-free: the SDK generates brand ramps at\n * runtime in the browser, so a color library would be a disproportionate cost\n * for ~150 lines of well-specified math. OKLCH is used instead of HSL because\n * HSL lightness is not perceptual — an HSL ramp of a yellow and of a blue at the\n * same `L` read as wildly different brightness, which is exactly what breaks a\n * generated palette.\n */\n\n/** A color in the OKLCH space: perceptual lightness, chroma and hue. */\nexport interface Oklch {\n /** Perceptual lightness, `0` (black) to `1` (white). */\n l: number;\n /** Chroma (colorfulness). `0` is gray; sRGB rarely exceeds `~0.37`. */\n c: number;\n /** Hue angle in degrees, `0`–`360`. */\n h: number;\n}\n\n/** The ten steps of a Tempest tint scale, lightest to darkest. */\nexport type ScaleStep = 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;\n\n/** A generated tint scale, keyed by step. */\nexport type ColorScale = Record<ScaleStep, string>;\n\nconst SCALE_STEPS: ScaleStep[] = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900];\n\n/**\n * Target perceptual lightness per step for a light-theme ramp.\n *\n * Tuned against the hand-written scales in `colors.css` so a generated theme\n * sits in the same visual range as the built-in one: `500` is the action color,\n * `50`/`100` are usable as soft backgrounds, `700`+ carries text on white.\n */\nconst LIGHT_LIGHTNESS: Record<ScaleStep, number> = {\n 50: 0.972,\n 100: 0.938,\n 200: 0.878,\n 300: 0.798,\n 400: 0.712,\n 500: 0.624,\n 600: 0.542,\n 700: 0.458,\n 800: 0.378,\n 900: 0.292,\n};\n\n/**\n * Target lightness for a dark-theme ramp — the ramp is **inverted**: `50` is the\n * darkest tint (a surface) and `900` the lightest (text-grade), matching the\n * `[data-tempest-theme=\"dark\"]` block in `colors.css`.\n */\nconst DARK_LIGHTNESS: Record<ScaleStep, number> = {\n 50: 0.238,\n 100: 0.288,\n 200: 0.362,\n 300: 0.452,\n 400: 0.544,\n 500: 0.632,\n 600: 0.712,\n 700: 0.788,\n 800: 0.862,\n 900: 0.928,\n};\n\n/**\n * Chroma multiplier per step, relative to the input color's chroma.\n *\n * Peaks around `500`–`600` and falls off at both ends: near-white and near-black\n * tints hold very little chroma before they look muddy or leave the sRGB gamut.\n */\nconst CHROMA_CURVE: Record<ScaleStep, number> = {\n 50: 0.18,\n 100: 0.34,\n 200: 0.6,\n 300: 0.82,\n 400: 0.95,\n 500: 1,\n 600: 0.98,\n 700: 0.88,\n 800: 0.74,\n 900: 0.58,\n};\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value));\n}\n\n/** Expand `#abc` to `#aabbcc` and normalize to a lowercase 6-digit hex. */\nfunction normalizeHex(hex: string): string {\n const raw = hex.trim().replace(/^#/, \"\");\n const expanded =\n raw.length === 3 || raw.length === 4\n ? raw\n .slice(0, 3)\n .split(\"\")\n .map((char) => char + char)\n .join(\"\")\n : raw.slice(0, 6);\n if (!/^[0-9a-fA-F]{6}$/.test(expanded)) {\n throw new Error(`Invalid hex color: \"${hex}\"`);\n }\n return `#${expanded.toLowerCase()}`;\n}\n\n/** Parse a hex color into sRGB channels in the `0`–`1` range. */\nexport function hexToRgb(hex: string): { r: number; g: number; b: number } {\n const normalized = normalizeHex(hex).slice(1);\n return {\n r: parseInt(normalized.slice(0, 2), 16) / 255,\n g: parseInt(normalized.slice(2, 4), 16) / 255,\n b: parseInt(normalized.slice(4, 6), 16) / 255,\n };\n}\n\n/** Serialize sRGB channels (`0`–`1`, clamped) back to a `#rrggbb` string. */\nexport function rgbToHex(r: number, g: number, b: number): string {\n const channel = (value: number): string =>\n Math.round(clamp(value, 0, 1) * 255)\n .toString(16)\n .padStart(2, \"0\");\n return `#${channel(r)}${channel(g)}${channel(b)}`;\n}\n\nfunction srgbToLinear(channel: number): number {\n return channel <= 0.04045 ? channel / 12.92 : Math.pow((channel + 0.055) / 1.055, 2.4);\n}\n\nfunction linearToSrgb(channel: number): number {\n return channel <= 0.0031308 ? channel * 12.92 : 1.055 * Math.pow(channel, 1 / 2.4) - 0.055;\n}\n\n/** Convert a hex color to OKLCH. */\nexport function hexToOklch(hex: string): Oklch {\n const { r, g, b } = hexToRgb(hex);\n const lr = srgbToLinear(r);\n const lg = srgbToLinear(g);\n const lb = srgbToLinear(b);\n\n const long = Math.cbrt(0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb);\n const medium = Math.cbrt(0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb);\n const short = Math.cbrt(0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb);\n\n const l = 0.2104542553 * long + 0.793617785 * medium - 0.0040720468 * short;\n const a = 1.9779984951 * long - 2.428592205 * medium + 0.4505937099 * short;\n const bAxis = 0.0259040371 * long + 0.7827717662 * medium - 0.808675766 * short;\n\n const c = Math.sqrt(a * a + bAxis * bAxis);\n const hue = c < 1e-6 ? 0 : (Math.atan2(bAxis, a) * 180) / Math.PI;\n\n return { l, c, h: hue < 0 ? hue + 360 : hue };\n}\n\n/** Convert OKLCH to linear-light sRGB, without gamut clamping. */\nfunction oklchToLinearRgb({ l, c, h }: Oklch): { r: number; g: number; b: number } {\n const hRad = (h * Math.PI) / 180;\n const a = Math.cos(hRad) * c;\n const bAxis = Math.sin(hRad) * c;\n\n const long = (l + 0.3963377774 * a + 0.2158037573 * bAxis) ** 3;\n const medium = (l - 0.1055613458 * a - 0.0638541728 * bAxis) ** 3;\n const short = (l - 0.0894841775 * a - 1.291485548 * bAxis) ** 3;\n\n return {\n r: 4.0767416621 * long - 3.3077115913 * medium + 0.2309699292 * short,\n g: -1.2684380046 * long + 2.6097574011 * medium - 0.3413193965 * short,\n b: -0.0041960863 * long - 0.7034186147 * medium + 1.707614701 * short,\n };\n}\n\nfunction inGamut({ r, g, b }: { r: number; g: number; b: number }): boolean {\n const epsilon = 1e-4;\n return (\n r >= -epsilon &&\n r <= 1 + epsilon &&\n g >= -epsilon &&\n g <= 1 + epsilon &&\n b >= -epsilon &&\n b <= 1 + epsilon\n );\n}\n\n/**\n * Convert OKLCH to a hex color, reducing chroma until the result fits sRGB.\n *\n * Lightness and hue are preserved: desaturating is far less noticeable than\n * shifting either of them, and a naive channel clamp would do both.\n */\nexport function oklchToHex(color: Oklch): string {\n let chroma = Math.max(0, color.c);\n let rgb = oklchToLinearRgb({ ...color, c: chroma });\n\n for (let i = 0; i < 24 && !inGamut(rgb); i += 1) {\n chroma *= 0.9;\n rgb = oklchToLinearRgb({ ...color, c: chroma });\n }\n\n return rgbToHex(linearToSrgb(rgb.r), linearToSrgb(rgb.g), linearToSrgb(rgb.b));\n}\n\n/**\n * Build a ten-step tint scale from a single brand color.\n *\n * The input color's hue is kept throughout and its chroma sets the intensity of\n * the whole ramp, so a muted brand color yields a muted scale instead of being\n * \"corrected\" into something the brand never approved.\n *\n * @param hex - Any hex color (`#abc` or `#aabbcc`), the intended `500` step.\n * @param mode - `\"light\"` for a light→dark ramp, `\"dark\"` for the inverted ramp\n * used under `[data-tempest-theme=\"dark\"]`.\n */\nexport function createColorScale(hex: string, mode: \"light\" | \"dark\" = \"light\"): ColorScale {\n const base = hexToOklch(hex);\n const lightness = mode === \"dark\" ? DARK_LIGHTNESS : LIGHT_LIGHTNESS;\n const scale = {} as ColorScale;\n\n for (const step of SCALE_STEPS) {\n scale[step] = oklchToHex({\n l: lightness[step],\n c: base.c * CHROMA_CURVE[step],\n h: base.h,\n });\n }\n\n return scale;\n}\n\n/** WCAG 2.x relative luminance of a hex color. */\nexport function relativeLuminance(hex: string): number {\n const { r, g, b } = hexToRgb(hex);\n return 0.2126 * srgbToLinear(r) + 0.7152 * srgbToLinear(g) + 0.0722 * srgbToLinear(b);\n}\n\n/** WCAG 2.x contrast ratio between two hex colors, from `1` to `21`. */\nexport function contrastRatio(a: string, b: string): number {\n const la = relativeLuminance(a);\n const lb = relativeLuminance(b);\n const lighter = Math.max(la, lb);\n const darker = Math.min(la, lb);\n return (lighter + 0.05) / (darker + 0.05);\n}\n\n/**\n * Pick the readable foreground for a background, by contrast ratio.\n *\n * Used for `--tempest-primary-foreground`: a generated brand color can land\n * anywhere on the lightness axis, and hardcoding white would silently produce\n * unreadable buttons for light brands (yellow, lime, cyan).\n */\nexport function readableForeground(\n background: string,\n light = \"#ffffff\",\n dark = \"#101828\",\n): string {\n return contrastRatio(background, light) >= contrastRatio(background, dark) ? light : dark;\n}\n\n/** Format a hex color as `rgb(r g b / alpha)`, for focus rings and overlays. */\nexport function hexToRgbaString(hex: string, alpha: number): string {\n const { r, g, b } = hexToRgb(hex);\n const channel = (value: number): number => Math.round(clamp(value, 0, 1) * 255);\n return `rgb(${channel(r)} ${channel(g)} ${channel(b)} / ${clamp(alpha, 0, 1)})`;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
const e=require("./color.cjs");var t={none:{xs:`0`,sm:`0`,md:`0`,lg:`0`,xl:`0`,"2xl":`0`},sm:{xs:`1px`,sm:`2px`,md:`4px`,lg:`6px`,xl:`8px`,"2xl":`12px`},md:{xs:`2px`,sm:`4px`,md:`8px`,lg:`12px`,xl:`16px`,"2xl":`24px`},lg:{xs:`4px`,sm:`6px`,md:`12px`,lg:`16px`,xl:`22px`,"2xl":`32px`},xl:{xs:`6px`,sm:`10px`,md:`16px`,lg:`24px`,xl:`32px`,"2xl":`44px`},full:{xs:`4px`,sm:`8px`,md:`9999px`,lg:`9999px`,xl:`9999px`,"2xl":`9999px`}},n=[50,100,200,300,400,500,600,700,800,900];function r(e,t,r){for(let i of n)e[`--tempest-${t}-${i}`]=r[i]}var i=4.5;function a(t,n,r){for(let a of r)if(e.contrastRatio(t[n],t[a])>=i)return a;return r[r.length-1]}function o(t,n,r,i){t[`--tempest-primary`]=`var(--tempest-primary-500)`,r===`light`?(t[`--tempest-primary-hover`]=`var(--tempest-primary-600)`,t[`--tempest-primary-active`]=`var(--tempest-primary-700)`,t[`--tempest-primary-soft`]=`var(--tempest-primary-50)`,t[`--tempest-primary-soft-hover`]=`var(--tempest-primary-100)`,t[`--tempest-primary-on-soft`]=`var(--tempest-primary-${a(n,50,[600,700,800,900])})`):(t[`--tempest-primary-hover`]=`var(--tempest-primary-400)`,t[`--tempest-primary-active`]=`var(--tempest-primary-300)`,t[`--tempest-primary-soft`]=`var(--tempest-primary-100)`,t[`--tempest-primary-soft-hover`]=`var(--tempest-primary-200)`,t[`--tempest-primary-on-soft`]=`var(--tempest-primary-${a(n,100,[700,800,900])})`);let o=e.readableForeground(n[500]);t[`--tempest-primary-foreground`]=o,t[`--tempest-text-on-primary`]=o,t[`--tempest-focus-ring-color`]=e.hexToRgbaString(n[500],i)}function s(e,t,n){n===`light`?(e[`--tempest-bg`]=`#ffffff`,e[`--tempest-surface`]=`var(--tempest-gray-50)`,e[`--tempest-surface-2`]=`var(--tempest-gray-100)`,e[`--tempest-surface-3`]=`var(--tempest-gray-200)`,e[`--tempest-border`]=`var(--tempest-gray-200)`,e[`--tempest-border-strong`]=`var(--tempest-gray-300)`,e[`--tempest-text`]=`var(--tempest-gray-900)`,e[`--tempest-text-muted`]=`var(--tempest-gray-600)`,e[`--tempest-text-subtle`]=`var(--tempest-gray-500)`):(e[`--tempest-bg`]=t[50],e[`--tempest-surface`]=`var(--tempest-gray-100)`,e[`--tempest-surface-2`]=`var(--tempest-gray-200)`,e[`--tempest-surface-3`]=`var(--tempest-gray-300)`,e[`--tempest-border`]=`var(--tempest-gray-300)`,e[`--tempest-border-strong`]=`var(--tempest-gray-400)`,e[`--tempest-text`]=`var(--tempest-gray-900)`,e[`--tempest-text-muted`]=`var(--tempest-gray-700)`,e[`--tempest-text-subtle`]=`var(--tempest-gray-600)`)}function c(e,t,n,r){r===`light`?(e[`--tempest-${t}`]=n[700],e[`--tempest-${t}-fg`]=n[800],e[`--tempest-${t}-bg`]=n[50],e[`--tempest-${t}-border`]=n[200],e[`--tempest-${t}-solid`]=n[600]):(e[`--tempest-${t}`]=n[700],e[`--tempest-${t}-fg`]=n[700],e[`--tempest-${t}-bg`]=n[50],e[`--tempest-${t}-border`]=n[200],e[`--tempest-${t}-solid`]=n[500])}function l(e,t){let n=Object.entries(t);return n.length===0?``:`${e} {\n${n.map(([e,t])=>` ${e}: ${t};`).join(`
|
|
2
|
+
`)}\n}`}function u(n={}){let{primary:i,gray:a,chart:u,radius:d,focusRingAlpha:f=.35,selector:p=`:root`,darkSelector:m=`[data-tempest-theme="dark"]`}=n,h={},g={};if(i){let t=e.createColorScale(i,`light`),n=e.createColorScale(i,`dark`);r(h,`primary`,t),r(g,`primary`,n),o(h,t,`light`,f),o(g,n,`dark`,f)}if(a){let t=e.createColorScale(a,`light`),n=e.createColorScale(a,`dark`);r(h,`gray`,t),r(g,`gray`,n),s(h,t,`light`),s(g,n,`dark`)}for(let t of[`success`,`warning`,`danger`,`info`]){let r=n[t];r&&(c(h,t,e.createColorScale(r,`light`),`light`),c(g,t,e.createColorScale(r,`dark`),`dark`))}if(u?.length&&u.forEach((e,t)=>{h[`--tempest-chart-${t+1}`]=e}),d){let e=typeof d==`string`?t[d]:d;for(let[t,n]of Object.entries(e))h[`--tempest-radius-${t}`]=n}return{light:h,dark:g,css:[l(p,h),l(m,g)].filter(Boolean).join(`
|
|
3
|
+
|
|
4
|
+
`)}}function d(t){if(!t.primary)return null;let n=e.createColorScale(t.primary,`light`);return e.contrastRatio(n[500],e.readableForeground(n[500]))}exports.createTheme=u,exports.themeContrast=d;
|
|
5
|
+
//# sourceMappingURL=create-theme.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-theme.cjs","names":[],"sources":["../../src/theme/create-theme.ts"],"sourcesContent":["/**\n * Theme factory: turns a handful of brand colors into the full set of\n * `--tempest-*` token overrides, for both light and dark.\n *\n * Rebranding used to mean hand-writing ~167 custom properties (and getting the\n * dark ramp inversion right by hand). `createTheme({ primary: \"#7c3aed\" })`\n * emits the same thing, derived in OKLCH so the ramp stays perceptually even.\n */\nimport {\n contrastRatio,\n createColorScale,\n hexToRgbaString,\n readableForeground,\n type ColorScale,\n type ScaleStep,\n} from \"./color\";\n\n/** Radius presets, applied to the whole `--tempest-radius-*` family at once. */\nexport type ThemeRadius = \"none\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"full\";\n\n/** Status token families that {@link createTheme} can regenerate. */\nexport type ThemeStatus = \"success\" | \"warning\" | \"danger\" | \"info\";\n\n/** Input for {@link createTheme}. Every field is optional — omitted families keep the built-in tokens. */\nexport interface CreateThemeOptions {\n /** Brand color, used as the `500` step of the primary scale. */\n primary?: string;\n /** Neutral color, used as the `500` step of the gray scale (surfaces, borders, text). */\n gray?: string;\n /** Success color (`--tempest-success*`). */\n success?: string;\n /** Warning color (`--tempest-warning*`). */\n warning?: string;\n /** Danger color (`--tempest-danger*`). */\n danger?: string;\n /** Info color (`--tempest-info*`). */\n info?: string;\n /**\n * Categorical series colors for `tempest-react-sdk/charts`, in cycle order.\n * Written to `--tempest-chart-1` … `--tempest-chart-N`, so charts follow the\n * theme instead of a hardcoded palette.\n */\n chart?: string[];\n /** Corner radius scale. A preset name, or explicit per-step values. */\n radius?: ThemeRadius | Partial<Record<\"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\", string>>;\n /** Alpha of `--tempest-focus-ring-color`, derived from the brand color. Default `0.35`. */\n focusRingAlpha?: number;\n /** Selector the light tokens are written under. Default `\":root\"`. */\n selector?: string;\n /** Selector the dark tokens are written under. Default `'[data-tempest-theme=\"dark\"]'`. */\n darkSelector?: string;\n}\n\n/** A generated theme: token maps per color scheme, plus the CSS text that carries them. */\nexport interface GeneratedTheme {\n /** Light-scheme custom properties, without the leading `--`-less names (keys include `--`). */\n light: Record<string, string>;\n /** Dark-scheme custom properties. */\n dark: Record<string, string>;\n /** Both blocks rendered as CSS, ready for {@link applyTheme} or a stylesheet. */\n css: string;\n}\n\nconst RADIUS_PRESETS: Record<ThemeRadius, Record<string, string>> = {\n none: { xs: \"0\", sm: \"0\", md: \"0\", lg: \"0\", xl: \"0\", \"2xl\": \"0\" },\n sm: { xs: \"1px\", sm: \"2px\", md: \"4px\", lg: \"6px\", xl: \"8px\", \"2xl\": \"12px\" },\n md: { xs: \"2px\", sm: \"4px\", md: \"8px\", lg: \"12px\", xl: \"16px\", \"2xl\": \"24px\" },\n lg: { xs: \"4px\", sm: \"6px\", md: \"12px\", lg: \"16px\", xl: \"22px\", \"2xl\": \"32px\" },\n xl: { xs: \"6px\", sm: \"10px\", md: \"16px\", lg: \"24px\", xl: \"32px\", \"2xl\": \"44px\" },\n full: { xs: \"4px\", sm: \"8px\", md: \"9999px\", lg: \"9999px\", xl: \"9999px\", \"2xl\": \"9999px\" },\n};\n\nconst SCALE_STEPS: ScaleStep[] = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900];\n\nfunction writeScale(tokens: Record<string, string>, name: string, scale: ColorScale): void {\n for (const step of SCALE_STEPS) {\n tokens[`--tempest-${name}-${step}`] = scale[step];\n }\n}\n\n/** Minimum contrast for body text, WCAG 2.x AA. */\nconst AA_TEXT_CONTRAST = 4.5;\n\n/**\n * Choose the ramp step for text sitting on the soft tint.\n *\n * A fixed step does not survive an arbitrary brand color: the built-in blue only\n * reaches 4.37:1 at `500` over its own `50` tint, and a generated emerald lands\n * at 4.41:1 at `600` — both fail AA for body text by a hair. So the step is\n * picked by measuring, walking away from the tint until it clears AA, and\n * falling back to the most extreme candidate when even that cannot (a very light\n * or very desaturated brand).\n *\n * @param scale - The generated ramp.\n * @param softStep - Step used as `--tempest-primary-soft`.\n * @param candidates - Steps to try, in order of preference.\n * @returns The first candidate clearing {@link AA_TEXT_CONTRAST}, else the last.\n */\nfunction pickOnSoftStep(\n scale: ColorScale,\n softStep: ScaleStep,\n candidates: ScaleStep[],\n): ScaleStep {\n for (const step of candidates) {\n if (contrastRatio(scale[softStep], scale[step]) >= AA_TEXT_CONTRAST) return step;\n }\n return candidates[candidates.length - 1];\n}\n\n/**\n * Emit the primary aliases for one scheme.\n *\n * The dark scheme walks the ramp the other way (hover is *lighter*, the soft\n * tint is a dark shade, and readable text on that tint is a light shade) — the\n * same inversion the built-in `colors.css` dark block does by hand.\n */\nfunction writePrimaryAliases(\n tokens: Record<string, string>,\n scale: ColorScale,\n scheme: \"light\" | \"dark\",\n focusRingAlpha: number,\n): void {\n tokens[\"--tempest-primary\"] = \"var(--tempest-primary-500)\";\n if (scheme === \"light\") {\n tokens[\"--tempest-primary-hover\"] = \"var(--tempest-primary-600)\";\n tokens[\"--tempest-primary-active\"] = \"var(--tempest-primary-700)\";\n tokens[\"--tempest-primary-soft\"] = \"var(--tempest-primary-50)\";\n tokens[\"--tempest-primary-soft-hover\"] = \"var(--tempest-primary-100)\";\n tokens[\"--tempest-primary-on-soft\"] =\n `var(--tempest-primary-${pickOnSoftStep(scale, 50, [600, 700, 800, 900])})`;\n } else {\n tokens[\"--tempest-primary-hover\"] = \"var(--tempest-primary-400)\";\n tokens[\"--tempest-primary-active\"] = \"var(--tempest-primary-300)\";\n tokens[\"--tempest-primary-soft\"] = \"var(--tempest-primary-100)\";\n tokens[\"--tempest-primary-soft-hover\"] = \"var(--tempest-primary-200)\";\n tokens[\"--tempest-primary-on-soft\"] =\n `var(--tempest-primary-${pickOnSoftStep(scale, 100, [700, 800, 900])})`;\n }\n\n const foreground = readableForeground(scale[500]);\n tokens[\"--tempest-primary-foreground\"] = foreground;\n tokens[\"--tempest-text-on-primary\"] = foreground;\n tokens[\"--tempest-focus-ring-color\"] = hexToRgbaString(scale[500], focusRingAlpha);\n}\n\n/**\n * Emit the neutral surface/border/text aliases from a generated gray scale.\n *\n * `--tempest-bg` is pushed past the ramp on purpose: pure white in light mode and\n * a shade darker than `gray-50` in dark mode, so a raised surface still reads as\n * raised against the page.\n */\nfunction writeNeutralAliases(\n tokens: Record<string, string>,\n scale: ColorScale,\n scheme: \"light\" | \"dark\",\n): void {\n if (scheme === \"light\") {\n tokens[\"--tempest-bg\"] = \"#ffffff\";\n tokens[\"--tempest-surface\"] = \"var(--tempest-gray-50)\";\n tokens[\"--tempest-surface-2\"] = \"var(--tempest-gray-100)\";\n tokens[\"--tempest-surface-3\"] = \"var(--tempest-gray-200)\";\n tokens[\"--tempest-border\"] = \"var(--tempest-gray-200)\";\n tokens[\"--tempest-border-strong\"] = \"var(--tempest-gray-300)\";\n tokens[\"--tempest-text\"] = \"var(--tempest-gray-900)\";\n tokens[\"--tempest-text-muted\"] = \"var(--tempest-gray-600)\";\n tokens[\"--tempest-text-subtle\"] = \"var(--tempest-gray-500)\";\n } else {\n tokens[\"--tempest-bg\"] = scale[50];\n tokens[\"--tempest-surface\"] = \"var(--tempest-gray-100)\";\n tokens[\"--tempest-surface-2\"] = \"var(--tempest-gray-200)\";\n tokens[\"--tempest-surface-3\"] = \"var(--tempest-gray-300)\";\n tokens[\"--tempest-border\"] = \"var(--tempest-gray-300)\";\n tokens[\"--tempest-border-strong\"] = \"var(--tempest-gray-400)\";\n tokens[\"--tempest-text\"] = \"var(--tempest-gray-900)\";\n tokens[\"--tempest-text-muted\"] = \"var(--tempest-gray-700)\";\n tokens[\"--tempest-text-subtle\"] = \"var(--tempest-gray-600)\";\n }\n}\n\n/**\n * Emit one status family (`--tempest-danger`, `-fg`, `-bg`, `-border`, `-solid`).\n *\n * `-fg` is the text shade over `-bg`, so it has to cross the ramp in opposite\n * directions per scheme; `-solid` stays the saturated fill used by badges.\n */\nfunction writeStatus(\n tokens: Record<string, string>,\n name: ThemeStatus,\n scale: ColorScale,\n scheme: \"light\" | \"dark\",\n): void {\n if (scheme === \"light\") {\n tokens[`--tempest-${name}`] = scale[700];\n tokens[`--tempest-${name}-fg`] = scale[800];\n tokens[`--tempest-${name}-bg`] = scale[50];\n tokens[`--tempest-${name}-border`] = scale[200];\n tokens[`--tempest-${name}-solid`] = scale[600];\n } else {\n tokens[`--tempest-${name}`] = scale[700];\n tokens[`--tempest-${name}-fg`] = scale[700];\n tokens[`--tempest-${name}-bg`] = scale[50];\n tokens[`--tempest-${name}-border`] = scale[200];\n tokens[`--tempest-${name}-solid`] = scale[500];\n }\n}\n\nfunction renderBlock(selector: string, tokens: Record<string, string>): string {\n const entries = Object.entries(tokens);\n if (entries.length === 0) return \"\";\n const body = entries.map(([name, value]) => ` ${name}: ${value};`).join(\"\\n\");\n return `${selector} {\\n${body}\\n}`;\n}\n\n/**\n * Generate `--tempest-*` overrides from a small brand description.\n *\n * Only the families you pass are generated; everything else falls through to the\n * SDK's own tokens, so a theme stays a patch and not a fork of `colors.css`.\n *\n * @example\n * ```ts\n * import { applyTheme, createTheme } from \"tempest-react-sdk\";\n *\n * const theme = createTheme({\n * primary: \"#7c3aed\",\n * radius: \"lg\",\n * chart: [\"#7c3aed\", \"#0ea5e9\", \"#22c55e\", \"#f59e0b\"],\n * });\n *\n * applyTheme(theme);\n * ```\n *\n * @param options - Brand colors plus optional radius / chart / focus-ring tuning.\n * @returns The light and dark token maps and the CSS text that carries them.\n */\nexport function createTheme(options: CreateThemeOptions = {}): GeneratedTheme {\n const {\n primary,\n gray,\n chart,\n radius,\n focusRingAlpha = 0.35,\n selector = \":root\",\n darkSelector = '[data-tempest-theme=\"dark\"]',\n } = options;\n\n const light: Record<string, string> = {};\n const dark: Record<string, string> = {};\n\n if (primary) {\n const lightScale = createColorScale(primary, \"light\");\n const darkScale = createColorScale(primary, \"dark\");\n writeScale(light, \"primary\", lightScale);\n writeScale(dark, \"primary\", darkScale);\n writePrimaryAliases(light, lightScale, \"light\", focusRingAlpha);\n writePrimaryAliases(dark, darkScale, \"dark\", focusRingAlpha);\n }\n\n if (gray) {\n const lightScale = createColorScale(gray, \"light\");\n const darkScale = createColorScale(gray, \"dark\");\n writeScale(light, \"gray\", lightScale);\n writeScale(dark, \"gray\", darkScale);\n writeNeutralAliases(light, lightScale, \"light\");\n writeNeutralAliases(dark, darkScale, \"dark\");\n }\n\n for (const status of [\"success\", \"warning\", \"danger\", \"info\"] as const) {\n const value = options[status];\n if (!value) continue;\n writeStatus(light, status, createColorScale(value, \"light\"), \"light\");\n writeStatus(dark, status, createColorScale(value, \"dark\"), \"dark\");\n }\n\n if (chart?.length) {\n chart.forEach((color, index) => {\n light[`--tempest-chart-${index + 1}`] = color;\n });\n }\n\n if (radius) {\n const steps = typeof radius === \"string\" ? RADIUS_PRESETS[radius] : radius;\n for (const [step, value] of Object.entries(steps)) {\n light[`--tempest-radius-${step}`] = value;\n }\n }\n\n const css = [renderBlock(selector, light), renderBlock(darkSelector, dark)]\n .filter(Boolean)\n .join(\"\\n\\n\");\n\n return { light, dark, css };\n}\n\n/**\n * Contrast ratio of `--tempest-primary-foreground` over the brand color.\n *\n * Exposed so an app (or a test) can assert its own brand clears WCAG AA (4.5) for\n * body text or AA-large (3.0) for button labels, instead of trusting the pick.\n *\n * @param options - The same input given to {@link createTheme}.\n * @returns The ratio, or `null` when no `primary` was provided.\n */\nexport function themeContrast(options: CreateThemeOptions): number | null {\n if (!options.primary) return null;\n const scale = createColorScale(options.primary, \"light\");\n return contrastRatio(scale[500], readableForeground(scale[500]));\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { contrastRatio as e, createColorScale as t, hexToRgbaString as n, readableForeground as r } from "./color.js";
|
|
2
|
+
//#region src/theme/create-theme.ts
|
|
3
|
+
var i = {
|
|
4
|
+
none: {
|
|
5
|
+
xs: "0",
|
|
6
|
+
sm: "0",
|
|
7
|
+
md: "0",
|
|
8
|
+
lg: "0",
|
|
9
|
+
xl: "0",
|
|
10
|
+
"2xl": "0"
|
|
11
|
+
},
|
|
12
|
+
sm: {
|
|
13
|
+
xs: "1px",
|
|
14
|
+
sm: "2px",
|
|
15
|
+
md: "4px",
|
|
16
|
+
lg: "6px",
|
|
17
|
+
xl: "8px",
|
|
18
|
+
"2xl": "12px"
|
|
19
|
+
},
|
|
20
|
+
md: {
|
|
21
|
+
xs: "2px",
|
|
22
|
+
sm: "4px",
|
|
23
|
+
md: "8px",
|
|
24
|
+
lg: "12px",
|
|
25
|
+
xl: "16px",
|
|
26
|
+
"2xl": "24px"
|
|
27
|
+
},
|
|
28
|
+
lg: {
|
|
29
|
+
xs: "4px",
|
|
30
|
+
sm: "6px",
|
|
31
|
+
md: "12px",
|
|
32
|
+
lg: "16px",
|
|
33
|
+
xl: "22px",
|
|
34
|
+
"2xl": "32px"
|
|
35
|
+
},
|
|
36
|
+
xl: {
|
|
37
|
+
xs: "6px",
|
|
38
|
+
sm: "10px",
|
|
39
|
+
md: "16px",
|
|
40
|
+
lg: "24px",
|
|
41
|
+
xl: "32px",
|
|
42
|
+
"2xl": "44px"
|
|
43
|
+
},
|
|
44
|
+
full: {
|
|
45
|
+
xs: "4px",
|
|
46
|
+
sm: "8px",
|
|
47
|
+
md: "9999px",
|
|
48
|
+
lg: "9999px",
|
|
49
|
+
xl: "9999px",
|
|
50
|
+
"2xl": "9999px"
|
|
51
|
+
}
|
|
52
|
+
}, a = [
|
|
53
|
+
50,
|
|
54
|
+
100,
|
|
55
|
+
200,
|
|
56
|
+
300,
|
|
57
|
+
400,
|
|
58
|
+
500,
|
|
59
|
+
600,
|
|
60
|
+
700,
|
|
61
|
+
800,
|
|
62
|
+
900
|
|
63
|
+
];
|
|
64
|
+
function o(e, t, n) {
|
|
65
|
+
for (let r of a) e[`--tempest-${t}-${r}`] = n[r];
|
|
66
|
+
}
|
|
67
|
+
var s = 4.5;
|
|
68
|
+
function c(t, n, r) {
|
|
69
|
+
for (let i of r) if (e(t[n], t[i]) >= s) return i;
|
|
70
|
+
return r[r.length - 1];
|
|
71
|
+
}
|
|
72
|
+
function l(e, t, i, a) {
|
|
73
|
+
e["--tempest-primary"] = "var(--tempest-primary-500)", i === "light" ? (e["--tempest-primary-hover"] = "var(--tempest-primary-600)", e["--tempest-primary-active"] = "var(--tempest-primary-700)", e["--tempest-primary-soft"] = "var(--tempest-primary-50)", e["--tempest-primary-soft-hover"] = "var(--tempest-primary-100)", e["--tempest-primary-on-soft"] = `var(--tempest-primary-${c(t, 50, [
|
|
74
|
+
600,
|
|
75
|
+
700,
|
|
76
|
+
800,
|
|
77
|
+
900
|
|
78
|
+
])})`) : (e["--tempest-primary-hover"] = "var(--tempest-primary-400)", e["--tempest-primary-active"] = "var(--tempest-primary-300)", e["--tempest-primary-soft"] = "var(--tempest-primary-100)", e["--tempest-primary-soft-hover"] = "var(--tempest-primary-200)", e["--tempest-primary-on-soft"] = `var(--tempest-primary-${c(t, 100, [
|
|
79
|
+
700,
|
|
80
|
+
800,
|
|
81
|
+
900
|
|
82
|
+
])})`);
|
|
83
|
+
let o = r(t[500]);
|
|
84
|
+
e["--tempest-primary-foreground"] = o, e["--tempest-text-on-primary"] = o, e["--tempest-focus-ring-color"] = n(t[500], a);
|
|
85
|
+
}
|
|
86
|
+
function u(e, t, n) {
|
|
87
|
+
n === "light" ? (e["--tempest-bg"] = "#ffffff", e["--tempest-surface"] = "var(--tempest-gray-50)", e["--tempest-surface-2"] = "var(--tempest-gray-100)", e["--tempest-surface-3"] = "var(--tempest-gray-200)", e["--tempest-border"] = "var(--tempest-gray-200)", e["--tempest-border-strong"] = "var(--tempest-gray-300)", e["--tempest-text"] = "var(--tempest-gray-900)", e["--tempest-text-muted"] = "var(--tempest-gray-600)", e["--tempest-text-subtle"] = "var(--tempest-gray-500)") : (e["--tempest-bg"] = t[50], e["--tempest-surface"] = "var(--tempest-gray-100)", e["--tempest-surface-2"] = "var(--tempest-gray-200)", e["--tempest-surface-3"] = "var(--tempest-gray-300)", e["--tempest-border"] = "var(--tempest-gray-300)", e["--tempest-border-strong"] = "var(--tempest-gray-400)", e["--tempest-text"] = "var(--tempest-gray-900)", e["--tempest-text-muted"] = "var(--tempest-gray-700)", e["--tempest-text-subtle"] = "var(--tempest-gray-600)");
|
|
88
|
+
}
|
|
89
|
+
function d(e, t, n, r) {
|
|
90
|
+
r === "light" ? (e[`--tempest-${t}`] = n[700], e[`--tempest-${t}-fg`] = n[800], e[`--tempest-${t}-bg`] = n[50], e[`--tempest-${t}-border`] = n[200], e[`--tempest-${t}-solid`] = n[600]) : (e[`--tempest-${t}`] = n[700], e[`--tempest-${t}-fg`] = n[700], e[`--tempest-${t}-bg`] = n[50], e[`--tempest-${t}-border`] = n[200], e[`--tempest-${t}-solid`] = n[500]);
|
|
91
|
+
}
|
|
92
|
+
function f(e, t) {
|
|
93
|
+
let n = Object.entries(t);
|
|
94
|
+
return n.length === 0 ? "" : `${e} {\n${n.map(([e, t]) => ` ${e}: ${t};`).join("\n")}\n}`;
|
|
95
|
+
}
|
|
96
|
+
function p(e = {}) {
|
|
97
|
+
let { primary: n, gray: r, chart: a, radius: s, focusRingAlpha: c = .35, selector: p = ":root", darkSelector: m = "[data-tempest-theme=\"dark\"]" } = e, h = {}, g = {};
|
|
98
|
+
if (n) {
|
|
99
|
+
let e = t(n, "light"), r = t(n, "dark");
|
|
100
|
+
o(h, "primary", e), o(g, "primary", r), l(h, e, "light", c), l(g, r, "dark", c);
|
|
101
|
+
}
|
|
102
|
+
if (r) {
|
|
103
|
+
let e = t(r, "light"), n = t(r, "dark");
|
|
104
|
+
o(h, "gray", e), o(g, "gray", n), u(h, e, "light"), u(g, n, "dark");
|
|
105
|
+
}
|
|
106
|
+
for (let n of [
|
|
107
|
+
"success",
|
|
108
|
+
"warning",
|
|
109
|
+
"danger",
|
|
110
|
+
"info"
|
|
111
|
+
]) {
|
|
112
|
+
let r = e[n];
|
|
113
|
+
r && (d(h, n, t(r, "light"), "light"), d(g, n, t(r, "dark"), "dark"));
|
|
114
|
+
}
|
|
115
|
+
if (a?.length && a.forEach((e, t) => {
|
|
116
|
+
h[`--tempest-chart-${t + 1}`] = e;
|
|
117
|
+
}), s) {
|
|
118
|
+
let e = typeof s == "string" ? i[s] : s;
|
|
119
|
+
for (let [t, n] of Object.entries(e)) h[`--tempest-radius-${t}`] = n;
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
light: h,
|
|
123
|
+
dark: g,
|
|
124
|
+
css: [f(p, h), f(m, g)].filter(Boolean).join("\n\n")
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function m(n) {
|
|
128
|
+
if (!n.primary) return null;
|
|
129
|
+
let i = t(n.primary, "light");
|
|
130
|
+
return e(i[500], r(i[500]));
|
|
131
|
+
}
|
|
132
|
+
//#endregion
|
|
133
|
+
export { p as createTheme, m as themeContrast };
|
|
134
|
+
|
|
135
|
+
//# sourceMappingURL=create-theme.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-theme.js","names":[],"sources":["../../src/theme/create-theme.ts"],"sourcesContent":["/**\n * Theme factory: turns a handful of brand colors into the full set of\n * `--tempest-*` token overrides, for both light and dark.\n *\n * Rebranding used to mean hand-writing ~167 custom properties (and getting the\n * dark ramp inversion right by hand). `createTheme({ primary: \"#7c3aed\" })`\n * emits the same thing, derived in OKLCH so the ramp stays perceptually even.\n */\nimport {\n contrastRatio,\n createColorScale,\n hexToRgbaString,\n readableForeground,\n type ColorScale,\n type ScaleStep,\n} from \"./color\";\n\n/** Radius presets, applied to the whole `--tempest-radius-*` family at once. */\nexport type ThemeRadius = \"none\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"full\";\n\n/** Status token families that {@link createTheme} can regenerate. */\nexport type ThemeStatus = \"success\" | \"warning\" | \"danger\" | \"info\";\n\n/** Input for {@link createTheme}. Every field is optional — omitted families keep the built-in tokens. */\nexport interface CreateThemeOptions {\n /** Brand color, used as the `500` step of the primary scale. */\n primary?: string;\n /** Neutral color, used as the `500` step of the gray scale (surfaces, borders, text). */\n gray?: string;\n /** Success color (`--tempest-success*`). */\n success?: string;\n /** Warning color (`--tempest-warning*`). */\n warning?: string;\n /** Danger color (`--tempest-danger*`). */\n danger?: string;\n /** Info color (`--tempest-info*`). */\n info?: string;\n /**\n * Categorical series colors for `tempest-react-sdk/charts`, in cycle order.\n * Written to `--tempest-chart-1` … `--tempest-chart-N`, so charts follow the\n * theme instead of a hardcoded palette.\n */\n chart?: string[];\n /** Corner radius scale. A preset name, or explicit per-step values. */\n radius?: ThemeRadius | Partial<Record<\"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\", string>>;\n /** Alpha of `--tempest-focus-ring-color`, derived from the brand color. Default `0.35`. */\n focusRingAlpha?: number;\n /** Selector the light tokens are written under. Default `\":root\"`. */\n selector?: string;\n /** Selector the dark tokens are written under. Default `'[data-tempest-theme=\"dark\"]'`. */\n darkSelector?: string;\n}\n\n/** A generated theme: token maps per color scheme, plus the CSS text that carries them. */\nexport interface GeneratedTheme {\n /** Light-scheme custom properties, without the leading `--`-less names (keys include `--`). */\n light: Record<string, string>;\n /** Dark-scheme custom properties. */\n dark: Record<string, string>;\n /** Both blocks rendered as CSS, ready for {@link applyTheme} or a stylesheet. */\n css: string;\n}\n\nconst RADIUS_PRESETS: Record<ThemeRadius, Record<string, string>> = {\n none: { xs: \"0\", sm: \"0\", md: \"0\", lg: \"0\", xl: \"0\", \"2xl\": \"0\" },\n sm: { xs: \"1px\", sm: \"2px\", md: \"4px\", lg: \"6px\", xl: \"8px\", \"2xl\": \"12px\" },\n md: { xs: \"2px\", sm: \"4px\", md: \"8px\", lg: \"12px\", xl: \"16px\", \"2xl\": \"24px\" },\n lg: { xs: \"4px\", sm: \"6px\", md: \"12px\", lg: \"16px\", xl: \"22px\", \"2xl\": \"32px\" },\n xl: { xs: \"6px\", sm: \"10px\", md: \"16px\", lg: \"24px\", xl: \"32px\", \"2xl\": \"44px\" },\n full: { xs: \"4px\", sm: \"8px\", md: \"9999px\", lg: \"9999px\", xl: \"9999px\", \"2xl\": \"9999px\" },\n};\n\nconst SCALE_STEPS: ScaleStep[] = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900];\n\nfunction writeScale(tokens: Record<string, string>, name: string, scale: ColorScale): void {\n for (const step of SCALE_STEPS) {\n tokens[`--tempest-${name}-${step}`] = scale[step];\n }\n}\n\n/** Minimum contrast for body text, WCAG 2.x AA. */\nconst AA_TEXT_CONTRAST = 4.5;\n\n/**\n * Choose the ramp step for text sitting on the soft tint.\n *\n * A fixed step does not survive an arbitrary brand color: the built-in blue only\n * reaches 4.37:1 at `500` over its own `50` tint, and a generated emerald lands\n * at 4.41:1 at `600` — both fail AA for body text by a hair. So the step is\n * picked by measuring, walking away from the tint until it clears AA, and\n * falling back to the most extreme candidate when even that cannot (a very light\n * or very desaturated brand).\n *\n * @param scale - The generated ramp.\n * @param softStep - Step used as `--tempest-primary-soft`.\n * @param candidates - Steps to try, in order of preference.\n * @returns The first candidate clearing {@link AA_TEXT_CONTRAST}, else the last.\n */\nfunction pickOnSoftStep(\n scale: ColorScale,\n softStep: ScaleStep,\n candidates: ScaleStep[],\n): ScaleStep {\n for (const step of candidates) {\n if (contrastRatio(scale[softStep], scale[step]) >= AA_TEXT_CONTRAST) return step;\n }\n return candidates[candidates.length - 1];\n}\n\n/**\n * Emit the primary aliases for one scheme.\n *\n * The dark scheme walks the ramp the other way (hover is *lighter*, the soft\n * tint is a dark shade, and readable text on that tint is a light shade) — the\n * same inversion the built-in `colors.css` dark block does by hand.\n */\nfunction writePrimaryAliases(\n tokens: Record<string, string>,\n scale: ColorScale,\n scheme: \"light\" | \"dark\",\n focusRingAlpha: number,\n): void {\n tokens[\"--tempest-primary\"] = \"var(--tempest-primary-500)\";\n if (scheme === \"light\") {\n tokens[\"--tempest-primary-hover\"] = \"var(--tempest-primary-600)\";\n tokens[\"--tempest-primary-active\"] = \"var(--tempest-primary-700)\";\n tokens[\"--tempest-primary-soft\"] = \"var(--tempest-primary-50)\";\n tokens[\"--tempest-primary-soft-hover\"] = \"var(--tempest-primary-100)\";\n tokens[\"--tempest-primary-on-soft\"] =\n `var(--tempest-primary-${pickOnSoftStep(scale, 50, [600, 700, 800, 900])})`;\n } else {\n tokens[\"--tempest-primary-hover\"] = \"var(--tempest-primary-400)\";\n tokens[\"--tempest-primary-active\"] = \"var(--tempest-primary-300)\";\n tokens[\"--tempest-primary-soft\"] = \"var(--tempest-primary-100)\";\n tokens[\"--tempest-primary-soft-hover\"] = \"var(--tempest-primary-200)\";\n tokens[\"--tempest-primary-on-soft\"] =\n `var(--tempest-primary-${pickOnSoftStep(scale, 100, [700, 800, 900])})`;\n }\n\n const foreground = readableForeground(scale[500]);\n tokens[\"--tempest-primary-foreground\"] = foreground;\n tokens[\"--tempest-text-on-primary\"] = foreground;\n tokens[\"--tempest-focus-ring-color\"] = hexToRgbaString(scale[500], focusRingAlpha);\n}\n\n/**\n * Emit the neutral surface/border/text aliases from a generated gray scale.\n *\n * `--tempest-bg` is pushed past the ramp on purpose: pure white in light mode and\n * a shade darker than `gray-50` in dark mode, so a raised surface still reads as\n * raised against the page.\n */\nfunction writeNeutralAliases(\n tokens: Record<string, string>,\n scale: ColorScale,\n scheme: \"light\" | \"dark\",\n): void {\n if (scheme === \"light\") {\n tokens[\"--tempest-bg\"] = \"#ffffff\";\n tokens[\"--tempest-surface\"] = \"var(--tempest-gray-50)\";\n tokens[\"--tempest-surface-2\"] = \"var(--tempest-gray-100)\";\n tokens[\"--tempest-surface-3\"] = \"var(--tempest-gray-200)\";\n tokens[\"--tempest-border\"] = \"var(--tempest-gray-200)\";\n tokens[\"--tempest-border-strong\"] = \"var(--tempest-gray-300)\";\n tokens[\"--tempest-text\"] = \"var(--tempest-gray-900)\";\n tokens[\"--tempest-text-muted\"] = \"var(--tempest-gray-600)\";\n tokens[\"--tempest-text-subtle\"] = \"var(--tempest-gray-500)\";\n } else {\n tokens[\"--tempest-bg\"] = scale[50];\n tokens[\"--tempest-surface\"] = \"var(--tempest-gray-100)\";\n tokens[\"--tempest-surface-2\"] = \"var(--tempest-gray-200)\";\n tokens[\"--tempest-surface-3\"] = \"var(--tempest-gray-300)\";\n tokens[\"--tempest-border\"] = \"var(--tempest-gray-300)\";\n tokens[\"--tempest-border-strong\"] = \"var(--tempest-gray-400)\";\n tokens[\"--tempest-text\"] = \"var(--tempest-gray-900)\";\n tokens[\"--tempest-text-muted\"] = \"var(--tempest-gray-700)\";\n tokens[\"--tempest-text-subtle\"] = \"var(--tempest-gray-600)\";\n }\n}\n\n/**\n * Emit one status family (`--tempest-danger`, `-fg`, `-bg`, `-border`, `-solid`).\n *\n * `-fg` is the text shade over `-bg`, so it has to cross the ramp in opposite\n * directions per scheme; `-solid` stays the saturated fill used by badges.\n */\nfunction writeStatus(\n tokens: Record<string, string>,\n name: ThemeStatus,\n scale: ColorScale,\n scheme: \"light\" | \"dark\",\n): void {\n if (scheme === \"light\") {\n tokens[`--tempest-${name}`] = scale[700];\n tokens[`--tempest-${name}-fg`] = scale[800];\n tokens[`--tempest-${name}-bg`] = scale[50];\n tokens[`--tempest-${name}-border`] = scale[200];\n tokens[`--tempest-${name}-solid`] = scale[600];\n } else {\n tokens[`--tempest-${name}`] = scale[700];\n tokens[`--tempest-${name}-fg`] = scale[700];\n tokens[`--tempest-${name}-bg`] = scale[50];\n tokens[`--tempest-${name}-border`] = scale[200];\n tokens[`--tempest-${name}-solid`] = scale[500];\n }\n}\n\nfunction renderBlock(selector: string, tokens: Record<string, string>): string {\n const entries = Object.entries(tokens);\n if (entries.length === 0) return \"\";\n const body = entries.map(([name, value]) => ` ${name}: ${value};`).join(\"\\n\");\n return `${selector} {\\n${body}\\n}`;\n}\n\n/**\n * Generate `--tempest-*` overrides from a small brand description.\n *\n * Only the families you pass are generated; everything else falls through to the\n * SDK's own tokens, so a theme stays a patch and not a fork of `colors.css`.\n *\n * @example\n * ```ts\n * import { applyTheme, createTheme } from \"tempest-react-sdk\";\n *\n * const theme = createTheme({\n * primary: \"#7c3aed\",\n * radius: \"lg\",\n * chart: [\"#7c3aed\", \"#0ea5e9\", \"#22c55e\", \"#f59e0b\"],\n * });\n *\n * applyTheme(theme);\n * ```\n *\n * @param options - Brand colors plus optional radius / chart / focus-ring tuning.\n * @returns The light and dark token maps and the CSS text that carries them.\n */\nexport function createTheme(options: CreateThemeOptions = {}): GeneratedTheme {\n const {\n primary,\n gray,\n chart,\n radius,\n focusRingAlpha = 0.35,\n selector = \":root\",\n darkSelector = '[data-tempest-theme=\"dark\"]',\n } = options;\n\n const light: Record<string, string> = {};\n const dark: Record<string, string> = {};\n\n if (primary) {\n const lightScale = createColorScale(primary, \"light\");\n const darkScale = createColorScale(primary, \"dark\");\n writeScale(light, \"primary\", lightScale);\n writeScale(dark, \"primary\", darkScale);\n writePrimaryAliases(light, lightScale, \"light\", focusRingAlpha);\n writePrimaryAliases(dark, darkScale, \"dark\", focusRingAlpha);\n }\n\n if (gray) {\n const lightScale = createColorScale(gray, \"light\");\n const darkScale = createColorScale(gray, \"dark\");\n writeScale(light, \"gray\", lightScale);\n writeScale(dark, \"gray\", darkScale);\n writeNeutralAliases(light, lightScale, \"light\");\n writeNeutralAliases(dark, darkScale, \"dark\");\n }\n\n for (const status of [\"success\", \"warning\", \"danger\", \"info\"] as const) {\n const value = options[status];\n if (!value) continue;\n writeStatus(light, status, createColorScale(value, \"light\"), \"light\");\n writeStatus(dark, status, createColorScale(value, \"dark\"), \"dark\");\n }\n\n if (chart?.length) {\n chart.forEach((color, index) => {\n light[`--tempest-chart-${index + 1}`] = color;\n });\n }\n\n if (radius) {\n const steps = typeof radius === \"string\" ? RADIUS_PRESETS[radius] : radius;\n for (const [step, value] of Object.entries(steps)) {\n light[`--tempest-radius-${step}`] = value;\n }\n }\n\n const css = [renderBlock(selector, light), renderBlock(darkSelector, dark)]\n .filter(Boolean)\n .join(\"\\n\\n\");\n\n return { light, dark, css };\n}\n\n/**\n * Contrast ratio of `--tempest-primary-foreground` over the brand color.\n *\n * Exposed so an app (or a test) can assert its own brand clears WCAG AA (4.5) for\n * body text or AA-large (3.0) for button labels, instead of trusting the pick.\n *\n * @param options - The same input given to {@link createTheme}.\n * @returns The ratio, or `null` when no `primary` was provided.\n */\nexport function themeContrast(options: CreateThemeOptions): number | null {\n if (!options.primary) return null;\n const scale = createColorScale(options.primary, \"light\");\n return contrastRatio(scale[500], readableForeground(scale[500]));\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e={tempest:{primary:`#0066ff`,gray:`#667085`,chart:[`#2563eb`,`#16a34a`,`#f59e0b`,`#7c3aed`,`#ec4899`,`#06b6d4`]},violet:{primary:`#7c3aed`,gray:`#6b7280`,chart:[`#7c3aed`,`#0ea5e9`,`#22c55e`,`#f59e0b`,`#ec4899`,`#14b8a6`]},emerald:{primary:`#059669`,gray:`#64748b`,chart:[`#059669`,`#0ea5e9`,`#f59e0b`,`#8b5cf6`,`#ef4444`,`#14b8a6`]},rose:{primary:`#e11d48`,gray:`#71717a`,chart:[`#e11d48`,`#7c3aed`,`#0ea5e9`,`#16a34a`,`#f59e0b`,`#06b6d4`]},slate:{primary:`#475569`,gray:`#64748b`,chart:[`#475569`,`#0ea5e9`,`#16a34a`,`#f59e0b`,`#a855f7`,`#06b6d4`]},amber:{primary:`#d97706`,gray:`#78716c`,chart:[`#d97706`,`#0284c7`,`#16a34a`,`#7c3aed`,`#dc2626`,`#0891b2`]}};function t(t){return e[t]}exports.getThemePreset=t,exports.themePresets=e;
|
|
2
|
+
//# sourceMappingURL=theme-presets.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme-presets.cjs","names":[],"sources":["../../src/theme/theme-presets.ts"],"sourcesContent":["/**\n * Ready-made brand descriptions for {@link createTheme}.\n *\n * Each preset is a plain {@link CreateThemeOptions} object — data, not CSS — so\n * an app can spread one and override a single field (`{ ...themePresets.violet,\n * radius: \"full\" }`) instead of forking a stylesheet. The chart arrays keep the\n * `/charts` module in the same family as the brand, which is the whole point of\n * `--tempest-chart-*`.\n */\nimport type { CreateThemeOptions } from \"./create-theme\";\n\n/** Name of a bundled preset. */\nexport type ThemePresetName = \"tempest\" | \"violet\" | \"emerald\" | \"rose\" | \"slate\" | \"amber\";\n\n/**\n * The bundled presets.\n *\n * `tempest` restates the SDK's own defaults — useful as a starting point to\n * tweak, and as the \"reset to default\" entry in a theme picker.\n */\nexport const themePresets: Record<ThemePresetName, CreateThemeOptions> = {\n tempest: {\n primary: \"#0066ff\",\n gray: \"#667085\",\n chart: [\"#2563eb\", \"#16a34a\", \"#f59e0b\", \"#7c3aed\", \"#ec4899\", \"#06b6d4\"],\n },\n violet: {\n primary: \"#7c3aed\",\n gray: \"#6b7280\",\n chart: [\"#7c3aed\", \"#0ea5e9\", \"#22c55e\", \"#f59e0b\", \"#ec4899\", \"#14b8a6\"],\n },\n emerald: {\n primary: \"#059669\",\n gray: \"#64748b\",\n chart: [\"#059669\", \"#0ea5e9\", \"#f59e0b\", \"#8b5cf6\", \"#ef4444\", \"#14b8a6\"],\n },\n rose: {\n primary: \"#e11d48\",\n gray: \"#71717a\",\n chart: [\"#e11d48\", \"#7c3aed\", \"#0ea5e9\", \"#16a34a\", \"#f59e0b\", \"#06b6d4\"],\n },\n slate: {\n primary: \"#475569\",\n gray: \"#64748b\",\n chart: [\"#475569\", \"#0ea5e9\", \"#16a34a\", \"#f59e0b\", \"#a855f7\", \"#06b6d4\"],\n },\n amber: {\n primary: \"#d97706\",\n gray: \"#78716c\",\n chart: [\"#d97706\", \"#0284c7\", \"#16a34a\", \"#7c3aed\", \"#dc2626\", \"#0891b2\"],\n },\n};\n\n/**\n * Look up a preset by name.\n *\n * @param name - Preset name.\n * @returns The preset options, or `undefined` when the name is unknown (so a\n * value coming from `localStorage` or a query string cannot crash boot).\n */\nexport function getThemePreset(name: string): CreateThemeOptions | undefined {\n return themePresets[name as ThemePresetName];\n}\n"],"mappings":"AAoBA,IAAa,EAA4D,CACrE,QAAS,CACL,QAAS,UACT,KAAM,UACN,MAAO,CAAC,UAAW,UAAW,UAAW,UAAW,UAAW,SAAS,CAC5E,EACA,OAAQ,CACJ,QAAS,UACT,KAAM,UACN,MAAO,CAAC,UAAW,UAAW,UAAW,UAAW,UAAW,SAAS,CAC5E,EACA,QAAS,CACL,QAAS,UACT,KAAM,UACN,MAAO,CAAC,UAAW,UAAW,UAAW,UAAW,UAAW,SAAS,CAC5E,EACA,KAAM,CACF,QAAS,UACT,KAAM,UACN,MAAO,CAAC,UAAW,UAAW,UAAW,UAAW,UAAW,SAAS,CAC5E,EACA,MAAO,CACH,QAAS,UACT,KAAM,UACN,MAAO,CAAC,UAAW,UAAW,UAAW,UAAW,UAAW,SAAS,CAC5E,EACA,MAAO,CACH,QAAS,UACT,KAAM,UACN,MAAO,CAAC,UAAW,UAAW,UAAW,UAAW,UAAW,SAAS,CAC5E,CACJ,EASA,SAAgB,EAAe,EAA8C,CACzE,OAAO,EAAa,EACxB"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
//#region src/theme/theme-presets.ts
|
|
2
|
+
var e = {
|
|
3
|
+
tempest: {
|
|
4
|
+
primary: "#0066ff",
|
|
5
|
+
gray: "#667085",
|
|
6
|
+
chart: [
|
|
7
|
+
"#2563eb",
|
|
8
|
+
"#16a34a",
|
|
9
|
+
"#f59e0b",
|
|
10
|
+
"#7c3aed",
|
|
11
|
+
"#ec4899",
|
|
12
|
+
"#06b6d4"
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
violet: {
|
|
16
|
+
primary: "#7c3aed",
|
|
17
|
+
gray: "#6b7280",
|
|
18
|
+
chart: [
|
|
19
|
+
"#7c3aed",
|
|
20
|
+
"#0ea5e9",
|
|
21
|
+
"#22c55e",
|
|
22
|
+
"#f59e0b",
|
|
23
|
+
"#ec4899",
|
|
24
|
+
"#14b8a6"
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
emerald: {
|
|
28
|
+
primary: "#059669",
|
|
29
|
+
gray: "#64748b",
|
|
30
|
+
chart: [
|
|
31
|
+
"#059669",
|
|
32
|
+
"#0ea5e9",
|
|
33
|
+
"#f59e0b",
|
|
34
|
+
"#8b5cf6",
|
|
35
|
+
"#ef4444",
|
|
36
|
+
"#14b8a6"
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
rose: {
|
|
40
|
+
primary: "#e11d48",
|
|
41
|
+
gray: "#71717a",
|
|
42
|
+
chart: [
|
|
43
|
+
"#e11d48",
|
|
44
|
+
"#7c3aed",
|
|
45
|
+
"#0ea5e9",
|
|
46
|
+
"#16a34a",
|
|
47
|
+
"#f59e0b",
|
|
48
|
+
"#06b6d4"
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
slate: {
|
|
52
|
+
primary: "#475569",
|
|
53
|
+
gray: "#64748b",
|
|
54
|
+
chart: [
|
|
55
|
+
"#475569",
|
|
56
|
+
"#0ea5e9",
|
|
57
|
+
"#16a34a",
|
|
58
|
+
"#f59e0b",
|
|
59
|
+
"#a855f7",
|
|
60
|
+
"#06b6d4"
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
amber: {
|
|
64
|
+
primary: "#d97706",
|
|
65
|
+
gray: "#78716c",
|
|
66
|
+
chart: [
|
|
67
|
+
"#d97706",
|
|
68
|
+
"#0284c7",
|
|
69
|
+
"#16a34a",
|
|
70
|
+
"#7c3aed",
|
|
71
|
+
"#dc2626",
|
|
72
|
+
"#0891b2"
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
function t(t) {
|
|
77
|
+
return e[t];
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
export { t as getThemePreset, e as themePresets };
|
|
81
|
+
|
|
82
|
+
//# sourceMappingURL=theme-presets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme-presets.js","names":[],"sources":["../../src/theme/theme-presets.ts"],"sourcesContent":["/**\n * Ready-made brand descriptions for {@link createTheme}.\n *\n * Each preset is a plain {@link CreateThemeOptions} object — data, not CSS — so\n * an app can spread one and override a single field (`{ ...themePresets.violet,\n * radius: \"full\" }`) instead of forking a stylesheet. The chart arrays keep the\n * `/charts` module in the same family as the brand, which is the whole point of\n * `--tempest-chart-*`.\n */\nimport type { CreateThemeOptions } from \"./create-theme\";\n\n/** Name of a bundled preset. */\nexport type ThemePresetName = \"tempest\" | \"violet\" | \"emerald\" | \"rose\" | \"slate\" | \"amber\";\n\n/**\n * The bundled presets.\n *\n * `tempest` restates the SDK's own defaults — useful as a starting point to\n * tweak, and as the \"reset to default\" entry in a theme picker.\n */\nexport const themePresets: Record<ThemePresetName, CreateThemeOptions> = {\n tempest: {\n primary: \"#0066ff\",\n gray: \"#667085\",\n chart: [\"#2563eb\", \"#16a34a\", \"#f59e0b\", \"#7c3aed\", \"#ec4899\", \"#06b6d4\"],\n },\n violet: {\n primary: \"#7c3aed\",\n gray: \"#6b7280\",\n chart: [\"#7c3aed\", \"#0ea5e9\", \"#22c55e\", \"#f59e0b\", \"#ec4899\", \"#14b8a6\"],\n },\n emerald: {\n primary: \"#059669\",\n gray: \"#64748b\",\n chart: [\"#059669\", \"#0ea5e9\", \"#f59e0b\", \"#8b5cf6\", \"#ef4444\", \"#14b8a6\"],\n },\n rose: {\n primary: \"#e11d48\",\n gray: \"#71717a\",\n chart: [\"#e11d48\", \"#7c3aed\", \"#0ea5e9\", \"#16a34a\", \"#f59e0b\", \"#06b6d4\"],\n },\n slate: {\n primary: \"#475569\",\n gray: \"#64748b\",\n chart: [\"#475569\", \"#0ea5e9\", \"#16a34a\", \"#f59e0b\", \"#a855f7\", \"#06b6d4\"],\n },\n amber: {\n primary: \"#d97706\",\n gray: \"#78716c\",\n chart: [\"#d97706\", \"#0284c7\", \"#16a34a\", \"#7c3aed\", \"#dc2626\", \"#0891b2\"],\n },\n};\n\n/**\n * Look up a preset by name.\n *\n * @param name - Preset name.\n * @returns The preset options, or `undefined` when the name is unknown (so a\n * value coming from `localStorage` or a query string cannot crash boot).\n */\nexport function getThemePreset(name: string): CreateThemeOptions | undefined {\n return themePresets[name as ThemePresetName];\n}\n"],"mappings":";AAoBA,IAAa,IAA4D;CACrE,SAAS;EACL,SAAS;EACT,MAAM;EACN,OAAO;GAAC;GAAW;GAAW;GAAW;GAAW;GAAW;EAAS;CAC5E;CACA,QAAQ;EACJ,SAAS;EACT,MAAM;EACN,OAAO;GAAC;GAAW;GAAW;GAAW;GAAW;GAAW;EAAS;CAC5E;CACA,SAAS;EACL,SAAS;EACT,MAAM;EACN,OAAO;GAAC;GAAW;GAAW;GAAW;GAAW;GAAW;EAAS;CAC5E;CACA,MAAM;EACF,SAAS;EACT,MAAM;EACN,OAAO;GAAC;GAAW;GAAW;GAAW;GAAW;GAAW;EAAS;CAC5E;CACA,OAAO;EACH,SAAS;EACT,MAAM;EACN,OAAO;GAAC;GAAW;GAAW;GAAW;GAAW;GAAW;EAAS;CAC5E;CACA,OAAO;EACH,SAAS;EACT,MAAM;EACN,OAAO;GAAC;GAAW;GAAW;GAAW;GAAW;GAAW;EAAS;CAC5E;AACJ;AASA,SAAgB,EAAe,GAA8C;CACzE,OAAO,EAAa;AACxB"}
|