@arctura/theme 0.0.1

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 ADDED
@@ -0,0 +1 @@
1
+ # theme
package/dist/cli.cjs ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ require("./parser-DyQY7ASD.cjs").c();
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { c as e } from "./parser-DRqhbS0c.js";
3
+ //#region src/cli.ts
4
+ e();
5
+ //#endregion
@@ -0,0 +1,2 @@
1
+ export { useTheme } from './useTheme';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * ThemeMode represents the possible theme modes for the application.
3
+ *
4
+ * - 'light' | 'Light': Light mode
5
+ * - 'dark' | 'Dark' : Dark mode
6
+ * - 'system'| 'System': Follows the system preference
7
+ */
8
+ type ThemeMode = 'light' | 'dark' | 'system' | 'System' | 'Light' | 'Dark';
9
+ /**
10
+ * List of supported theme modes for the application.
11
+ *
12
+ * Used for theme selection and validation.
13
+ * Only lowercase values are included for internal logic.
14
+ */
15
+ declare const THEME_MODES: ThemeMode[];
16
+ /**
17
+ * Options for theme mode selection UI.
18
+ *
19
+ * Each option contains a value (used internally) and a label (for display).
20
+ * Used in theme switchers and select components.
21
+ */
22
+ declare const THEME_OPTIONS: {
23
+ value: ThemeMode;
24
+ label: ThemeMode;
25
+ }[];
26
+ declare function useTheme(): {
27
+ mode: ThemeMode;
28
+ setMode: (next: ThemeMode) => void;
29
+ };
30
+ export { THEME_MODES, THEME_OPTIONS, useTheme };
31
+ export type { ThemeMode };
32
+ //# sourceMappingURL=useTheme.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTheme.d.ts","sourceRoot":"","sources":["../../src/hooks/useTheme.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,KAAK,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;AAE3E;;;;;GAKG;AACH,QAAA,MAAM,WAAW,EAAE,SAAS,EAAgC,CAAC;AAE7D;;;;;GAKG;AACH,QAAA,MAAM,aAAa,EAAE;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,SAAS,CAAA;CAAE,EAI1D,CAAC;AAuBF,iBAAS,QAAQ;;oBAmBQ,SAAS;EAMjC;AAED,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC;AAChD,YAAY,EAAE,SAAS,EAAE,CAAC"}
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("react");var t=`theme-mode`;function n(){return typeof window>`u`?`system`:localStorage.getItem(t)||`system`}function r(e){return e===`system`?window.matchMedia(`(prefers-color-scheme: dark)`).matches?`dark`:`light`:e}function i(e){document.documentElement.setAttribute(`data-theme`,r(e))}function a(e){return window.addEventListener(`storage`,e),()=>window.removeEventListener(`storage`,e)}function o(){let r=(0,e.useSyncExternalStore)(a,()=>n(),()=>`system`);return(0,e.useLayoutEffect)(()=>{if(i(r),r!==`system`)return;let e=window.matchMedia(`(prefers-color-scheme: dark)`),t=()=>i(`system`);return e.addEventListener(`change`,t),()=>e.removeEventListener(`change`,t)},[r]),{mode:r,setMode:e=>{localStorage.setItem(t,e),window.dispatchEvent(new StorageEvent(`storage`,{key:t}))}}}exports.useTheme=o;
@@ -0,0 +1,2 @@
1
+ export { useTheme } from './hooks';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,30 @@
1
+ import { useLayoutEffect as e, useSyncExternalStore as t } from "react";
2
+ //#region src/hooks/useTheme.ts
3
+ var n = "theme-mode";
4
+ function r() {
5
+ return typeof window > "u" ? "system" : localStorage.getItem(n) || "system";
6
+ }
7
+ function i(e) {
8
+ return e === "system" ? window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" : e;
9
+ }
10
+ function a(e) {
11
+ document.documentElement.setAttribute("data-theme", i(e));
12
+ }
13
+ function o(e) {
14
+ return window.addEventListener("storage", e), () => window.removeEventListener("storage", e);
15
+ }
16
+ function s() {
17
+ let i = t(o, () => r(), () => "system");
18
+ return e(() => {
19
+ if (a(i), i !== "system") return;
20
+ let e = window.matchMedia("(prefers-color-scheme: dark)"), t = () => a("system");
21
+ return e.addEventListener("change", t), () => e.removeEventListener("change", t);
22
+ }, [i]), {
23
+ mode: i,
24
+ setMode: (e) => {
25
+ localStorage.setItem(n, e), window.dispatchEvent(new StorageEvent("storage", { key: n }));
26
+ }
27
+ };
28
+ }
29
+ //#endregion
30
+ export { s as useTheme };
@@ -0,0 +1,155 @@
1
+ import e from "postcss";
2
+ import { colord as t } from "colord";
3
+ import n from "postcss-js";
4
+ import { mkdir as r, writeFile as i } from "node:fs/promises";
5
+ import { dirname as a, resolve as o } from "node:path";
6
+ import { pathToFileURL as s } from "node:url";
7
+ //#region src/errors/cli.ts
8
+ var c = class extends Error {
9
+ argument;
10
+ value;
11
+ constructor(e, t, n) {
12
+ super(`Invalid value: ${t}, provived to the argument: ${e}`, { cause: n }), this.argument = e, this.value = t;
13
+ }
14
+ }, l = class extends Error {
15
+ colorRef;
16
+ constructor(e, t) {
17
+ super(`Invalid color reference: ${e}`, { cause: t }), this.name = "ColorTokenParsingError", this.colorRef = e;
18
+ }
19
+ }, u = "./theme.css", d = "./tokens.json", f = new Set(["-o", "--output"]), p = new Set(["-t", "--tokens"]);
20
+ function m(e, t, n) {
21
+ let r = e.findIndex((e) => t.has(e));
22
+ if (r === -1) return;
23
+ let i = e[r + 1];
24
+ if (!i || i.startsWith("-")) {
25
+ let t = e[r];
26
+ throw new c(t, i, Error(`Missing ${n}`));
27
+ }
28
+ return i;
29
+ }
30
+ function h(e) {
31
+ return m(e, p, "tokens file path. Use -t <tokens-file> or --tokens <tokens-file>") ?? d;
32
+ }
33
+ function g(e) {
34
+ return m(e, f, "output file path. Use -o <output-file> or --output <output-file>") ?? u;
35
+ }
36
+ async function _(e) {
37
+ return (await import(s(o(e)).href, { with: { type: "json" } })).default;
38
+ }
39
+ function v(e) {
40
+ return e.replace(/^([ \t]*[^{}\r\n]+:[^\r\n;{}]+)\r?\n[ \t]*;[ \t]*$/gmu, "$1;").replace(/[ \t]*;?[ \t]*(?:\r?\n[ \t]*)+\}$/gu, ";\n}");
41
+ }
42
+ function y(e) {
43
+ let t = e.split(".");
44
+ if (t.length > 3 || t.length < 2 || t[0] !== "colors") throw new l(e, /* @__PURE__ */ Error("Color reference must be in the format \"colors.{family}.{scale}\" | \"colors.{name}\""));
45
+ let [n, r, i] = t;
46
+ return `var(--${n.replace("s", "")}-${r}${i ? `-${i}` : ""})`;
47
+ }
48
+ function b(n, r, i) {
49
+ let a = e.rule({ selector: ":root" });
50
+ Object.entries(r).forEach(([n, r]) => {
51
+ if (typeof r == "object") Object.entries(r).forEach(([r, i]) => {
52
+ let { r: o, g: s, b: c } = t(i).toRgb();
53
+ a.append(e.decl({
54
+ prop: `--color-${n}-${r}`,
55
+ value: `${o} ${s} ${c}`
56
+ }));
57
+ });
58
+ else if (typeof r == "string") {
59
+ let { r: i, g: o, b: s } = t(r).toRgb();
60
+ a.append(e.decl({
61
+ prop: `--color-${n}`,
62
+ value: `${i} ${o} ${s}`
63
+ }));
64
+ }
65
+ }), a.append(e.decl({
66
+ prop: "--space-unit",
67
+ value: i["space-unit"]
68
+ })), n.append(a);
69
+ }
70
+ function x(t, n) {
71
+ if (!n || !n.fontSize) return;
72
+ let r = e.rule({ selector: ":root" });
73
+ Object.entries(n.fontSize).forEach(([t, n]) => {
74
+ r.append(e.decl({
75
+ prop: `--font-size-${t}`,
76
+ value: `${n}`
77
+ }));
78
+ }), t.append(r);
79
+ }
80
+ function S(e) {
81
+ return e === "background" ? "--background-color" : e === "text" ? "--text-color" : e === "border" ? "--border-color" : e === "outline" ? "--outline-color" : e === "caret" ? "--caret-color" : e === "ring" ? "--ring-color" : `--color-${e}`;
82
+ }
83
+ function C(t, n, r, i, a, o) {
84
+ let s = e.atRule({
85
+ name: "theme",
86
+ params: "inline"
87
+ });
88
+ Object.entries(n).forEach(([t, n]) => {
89
+ let r = S(t);
90
+ Object.entries(n).forEach(([n]) => {
91
+ s.append(e.decl({
92
+ prop: `${r}-${n}`,
93
+ value: `var(--${t}-${n})`
94
+ }));
95
+ });
96
+ });
97
+ let c = Number(r["space-unit"].replace(/px$/, ""));
98
+ s.append(e.decl({
99
+ prop: "--spacing",
100
+ value: r["space-unit"]
101
+ })), Object.entries(i).forEach(([t, n]) => {
102
+ s.append(e.decl({
103
+ prop: `--border-width-${t}`,
104
+ value: n
105
+ }));
106
+ }), Object.entries(a).forEach(([t, n]) => {
107
+ s.append(e.decl({
108
+ prop: `--breakpoint-${t}`,
109
+ value: n
110
+ }));
111
+ }), Object.entries(r).forEach(([t, n]) => {
112
+ t !== "space-unit" && s.append(e.decl({
113
+ prop: `--spacing-${t}`,
114
+ value: `${c * n}px`
115
+ }));
116
+ }), o?.fontSize && Object.entries(o.fontSize).forEach(([t]) => {
117
+ s.append(e.decl({
118
+ prop: `--font-size-${t}`,
119
+ value: `var(--font-size-${t})`
120
+ }));
121
+ }), t.append(s);
122
+ }
123
+ function w(t, n, r) {
124
+ let i = e.rule({ selector: n });
125
+ Object.entries(r).forEach(([t, n]) => {
126
+ Object.entries(n).forEach(([n, r]) => {
127
+ try {
128
+ i.append(e.decl({
129
+ prop: `--${t}-${n}`,
130
+ value: `rgb(${y(r)})`
131
+ }));
132
+ } catch (e) {
133
+ (e instanceof l || e instanceof Error) && console.error(e.message), console.error("Unknown error while parsing semantic color:", n);
134
+ }
135
+ });
136
+ }), t.append(i);
137
+ }
138
+ function T(t) {
139
+ let n = e.root();
140
+ return b(n, t.colors, t.theme.spacing), x(n, t.theme?.typography), w(n, ":root", t.theme.colors.palette.light), C(n, t.theme.colors.palette.light, t.theme.spacing, t.theme.borderWidth, t.theme.breakpoints, t.theme.typography), w(n, "[data-theme=\"light\"]", t.theme.colors.palette.light), w(n, "[data-theme=\"dark\"]", t.theme.colors.palette.dark), n.toString();
141
+ }
142
+ async function E(t, o = u) {
143
+ let s = e.parse(t), c = n.objectify(s), l = await e().process(c, {
144
+ parser: n,
145
+ from: void 0
146
+ }), d = `${e.parse(l.css).nodes.map((e) => v(e.toString())).join("\n\n")}\n`;
147
+ await r(a(o), { recursive: !0 }), await i(o, d, "utf8"), console.log(`✅ Successfully wrote theme CSS to ${o}`);
148
+ }
149
+ async function D() {
150
+ let e = process.argv.slice(2), t = h(e), n = g(e), r = T(await _(t));
151
+ console.log("🎨 Generating CSS Theme..."), console.log(`💾 Writing theme CSS to ${n}...`), await E(r, n);
152
+ }
153
+ process.argv[1] && import.meta.url === s(process.argv[1]).href && D();
154
+ //#endregion
155
+ export { g as a, D as c, T as i, y as n, h as o, v as r, _ as s, E as t };
@@ -0,0 +1,4 @@
1
+ var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("postcss");c=s(c,1);let l=require("colord"),u=require("postcss-js");u=s(u,1);let d=require("node:fs/promises"),f=require("node:path"),p=require("node:url");var m=class extends Error{argument;value;constructor(e,t,n){super(`Invalid value: ${t}, provived to the argument: ${e}`,{cause:n}),this.argument=e,this.value=t}},h=class extends Error{colorRef;constructor(e,t){super(`Invalid color reference: ${e}`,{cause:t}),this.name=`ColorTokenParsingError`,this.colorRef=e}},g=`./theme.css`,_=`./tokens.json`,v=new Set([`-o`,`--output`]),y=new Set([`-t`,`--tokens`]);function b(e,t,n){let r=e.findIndex(e=>t.has(e));if(r===-1)return;let i=e[r+1];if(!i||i.startsWith(`-`)){let t=e[r];throw new m(t,i,Error(`Missing ${n}`))}return i}function x(e){return b(e,y,`tokens file path. Use -t <tokens-file> or --tokens <tokens-file>`)??_}function S(e){return b(e,v,`output file path. Use -o <output-file> or --output <output-file>`)??g}async function C(e){return(await import((0,p.pathToFileURL)((0,f.resolve)(e)).href,{with:{type:`json`}})).default}function w(e){return e.replace(/^([ \t]*[^{}\r\n]+:[^\r\n;{}]+)\r?\n[ \t]*;[ \t]*$/gmu,`$1;`).replace(/[ \t]*;?[ \t]*(?:\r?\n[ \t]*)+\}$/gu,`;
2
+ }`)}function T(e){let t=e.split(`.`);if(t.length>3||t.length<2||t[0]!==`colors`)throw new h(e,Error(`Color reference must be in the format "colors.{family}.{scale}" | "colors.{name}"`));let[n,r,i]=t;return`var(--${n.replace(`s`,``)}-${r}${i?`-${i}`:``})`}function E(e,t,n){let r=c.default.rule({selector:`:root`});Object.entries(t).forEach(([e,t])=>{if(typeof t==`object`)Object.entries(t).forEach(([t,n])=>{let{r:i,g:a,b:o}=(0,l.colord)(n).toRgb();r.append(c.default.decl({prop:`--color-${e}-${t}`,value:`${i} ${a} ${o}`}))});else if(typeof t==`string`){let{r:n,g:i,b:a}=(0,l.colord)(t).toRgb();r.append(c.default.decl({prop:`--color-${e}`,value:`${n} ${i} ${a}`}))}}),r.append(c.default.decl({prop:`--space-unit`,value:n[`space-unit`]})),e.append(r)}function D(e,t){if(!t||!t.fontSize)return;let n=c.default.rule({selector:`:root`});Object.entries(t.fontSize).forEach(([e,t])=>{n.append(c.default.decl({prop:`--font-size-${e}`,value:`${t}`}))}),e.append(n)}function O(e){return e===`background`?`--background-color`:e===`text`?`--text-color`:e===`border`?`--border-color`:e===`outline`?`--outline-color`:e===`caret`?`--caret-color`:e===`ring`?`--ring-color`:`--color-${e}`}function k(e,t,n,r,i,a){let o=c.default.atRule({name:`theme`,params:`inline`});Object.entries(t).forEach(([e,t])=>{let n=O(e);Object.entries(t).forEach(([t])=>{o.append(c.default.decl({prop:`${n}-${t}`,value:`var(--${e}-${t})`}))})});let s=Number(n[`space-unit`].replace(/px$/,``));o.append(c.default.decl({prop:`--spacing`,value:n[`space-unit`]})),Object.entries(r).forEach(([e,t])=>{o.append(c.default.decl({prop:`--border-width-${e}`,value:t}))}),Object.entries(i).forEach(([e,t])=>{o.append(c.default.decl({prop:`--breakpoint-${e}`,value:t}))}),Object.entries(n).forEach(([e,t])=>{e!==`space-unit`&&o.append(c.default.decl({prop:`--spacing-${e}`,value:`${s*t}px`}))}),a?.fontSize&&Object.entries(a.fontSize).forEach(([e])=>{o.append(c.default.decl({prop:`--font-size-${e}`,value:`var(--font-size-${e})`}))}),e.append(o)}function A(e,t,n){let r=c.default.rule({selector:t});Object.entries(n).forEach(([e,t])=>{Object.entries(t).forEach(([t,n])=>{try{r.append(c.default.decl({prop:`--${e}-${t}`,value:`rgb(${T(n)})`}))}catch(e){(e instanceof h||e instanceof Error)&&console.error(e.message),console.error(`Unknown error while parsing semantic color:`,t)}})}),e.append(r)}function j(e){let t=c.default.root();return E(t,e.colors,e.theme.spacing),D(t,e.theme?.typography),A(t,`:root`,e.theme.colors.palette.light),k(t,e.theme.colors.palette.light,e.theme.spacing,e.theme.borderWidth,e.theme.breakpoints,e.theme.typography),A(t,`[data-theme="light"]`,e.theme.colors.palette.light),A(t,`[data-theme="dark"]`,e.theme.colors.palette.dark),t.toString()}async function M(e,t=g){let n=c.default.parse(e),r=u.default.objectify(n),i=await(0,c.default)().process(r,{parser:u.default,from:void 0}),a=`${c.default.parse(i.css).nodes.map(e=>w(e.toString())).join(`
3
+
4
+ `)}\n`;await(0,d.mkdir)((0,f.dirname)(t),{recursive:!0}),await(0,d.writeFile)(t,a,`utf8`),console.log(`✅ Successfully wrote theme CSS to ${t}`)}async function N(){let e=process.argv.slice(2),t=x(e),n=S(e),r=j(await C(t));console.log(`🎨 Generating CSS Theme...`),console.log(`💾 Writing theme CSS to ${n}...`),await M(r,n)}process.argv[1]&&{}.url===(0,p.pathToFileURL)(process.argv[1]).href&&N(),Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return S}}),Object.defineProperty(exports,"c",{enumerable:!0,get:function(){return N}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return j}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return T}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return x}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return w}}),Object.defineProperty(exports,"s",{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return M}});
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./parser-DyQY7ASD.cjs");exports.buildCssFile=e.t,exports.colorRefToCssVar=e.n,exports.formatGeneratedCss=e.r,exports.generateTheme=e.i,exports.getOutputFilePathFromArgs=e.a,exports.getTokensFilePathFromArgs=e.o,exports.loadTokensFile=e.s,exports.runParserCli=e.c;
@@ -0,0 +1,243 @@
1
+ /**
2
+ * Resolves the design tokens file path from CLI arguments.
3
+ *
4
+ * @param args - CLI arguments passed after the parser script path.
5
+ * @returns The path provided by `-t` or `--tokens`, or the default `./tokens.json`.
6
+ * @throws {Error} If the tokens flag is present without a value.
7
+ */
8
+ declare function getTokensFilePathFromArgs(args: string[]): string;
9
+ /**
10
+ * Resolves the generated CSS output file path from CLI arguments.
11
+ *
12
+ * @param args - CLI arguments passed after the parser script path.
13
+ * @returns The path provided by `-o` or `--output`, or the default `./theme.css`.
14
+ * @throws {Error} If the output flag is present without a value.
15
+ */
16
+ declare function getOutputFilePathFromArgs(args: string[]): string;
17
+ /**
18
+ * Dynamically imports a JSON design tokens file from the provided filesystem path.
19
+ *
20
+ * @param tokensFilePath - Path to the tokens JSON file, resolved relative to the current working directory when relative.
21
+ * @returns The parsed design tokens object.
22
+ */
23
+ declare function loadTokensFile(tokensFilePath: string): Promise<Tokens>;
24
+ /**
25
+ * Normalizes PostCSS output where generated declarations can leave semicolons
26
+ * on their own line or omit the final semicolon before a block closes.
27
+ *
28
+ * @param css - CSS text generated by PostCSS.
29
+ * @returns CSS text with normalized declaration semicolons.
30
+ */
31
+ declare function formatGeneratedCss(css: string): string;
32
+ /**
33
+ * @type
34
+ * Mode: Represents the semantic color mapping for a specific theme mode (light or dark).
35
+ * Each key is a semantic color name (e.g. "primary", "secondary") and the value is a reference string to a base color token (e.g. "colors.sky.500").
36
+ * @property background - A nested object mapping semantic background color names to their token references.
37
+ * @property text - A nested object mapping semantic text color names to their token references.
38
+ * @property [key: string] - Allows for additional semantic color categories (e.g. "border", "accent") to be included in the future.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * const lightMode: Mode = {
43
+ * background: {
44
+ * primary: "colors.azure.900",
45
+ * secondary: "colors.ocean.600",
46
+ * },
47
+ * text: {
48
+ * primary: "colors.black",
49
+ * secondary: "colors.ocean.500",
50
+ * }
51
+ * }
52
+ * ```
53
+ */
54
+ type Mode = {
55
+ background: Record<string, string>;
56
+ text: Record<string, string>;
57
+ [key: string]: Record<string, string>;
58
+ };
59
+ /**
60
+ * Palette: Contains both light and dark Mode mappings for semantic colors.
61
+ * Example: { light: Mode, dark: Mode }
62
+ */
63
+ type Palette = {
64
+ light: Mode;
65
+ dark: Mode;
66
+ };
67
+ /**
68
+ * ColorScale: Maps numeric scale steps (e.g. "100", "500") to hex color values.
69
+ * Example: { "100": "#f0f0f0", "500": "#0077ff" }
70
+ */
71
+ type ColorScale = Record<string, string> | string;
72
+ /**
73
+ * BaseColors: Maps color families (e.g. "sky", "mist") to their ColorScale.
74
+ * Example: { sky: ColorScale, mist: ColorScale }
75
+ */
76
+ type BaseColors = Record<string, ColorScale>;
77
+ /**
78
+ * @type
79
+ * SpacingScale: Defines the spacing scale for the theme.
80
+ * @property space-unit - The base spacing unit (e.g. "8px") that other spacing tokens are multiples of.
81
+ * @property [key: string] - Additional spacing tokens (e.g. "xs", "sm", "md", "lg", "xl") that map to numeric multiples of the base space unit.
82
+ *
83
+ * Example:
84
+ * {
85
+ * 'space-unit': '8px',
86
+ * 'xs': 0.5,
87
+ * 'sm': 1,
88
+ * 'md': 2,
89
+ * 'lg': 4,
90
+ * 'xl': 8
91
+ * }
92
+ */
93
+ type SpacingScale = {
94
+ 'space-unit': string;
95
+ [key: string]: string | number;
96
+ };
97
+ interface Typography {
98
+ fontSize: Record<string, string>;
99
+ [key: string]: string | Record<string, string>;
100
+ }
101
+ /**
102
+ * BorderWidthScale: Defines the border width scale for the theme.
103
+ *
104
+ * Maps border width token names (e.g., 'thin', 'medium', 'thick') to their CSS values (e.g., '1px', '2px').
105
+ *
106
+ * Example:
107
+ * {
108
+ * 'thin': '1px',
109
+ * 'medium': '2px',
110
+ * 'thick': '4px'
111
+ * }
112
+ */
113
+ type BorderWidthScale = Record<string, string>;
114
+ /**
115
+ * BreakpointScale: Defines the viewport min-width scale used by Tailwind v4
116
+ * responsive variants.
117
+ *
118
+ * Tailwind v4 consumes these through `--breakpoint-*` variables in `@theme`.
119
+ */
120
+ type BreakpointScale = Record<string, string>;
121
+ /**
122
+ * @interface
123
+ * Theme: Contains the semantic palette for both light and dark modes.
124
+ * @property colors - The semantic color palette for the theme, mapping to a Palette of light and dark modes.
125
+ * @property spacing - The spacing scale for the theme, mapping to a SpacingScale object.
126
+ * @property borderWidth - The border width scale for the theme, mapping to a BorderWidthScale object.
127
+ * @property [key: string] - Allows for additional theme properties (e.g. typography) to be included in the future.
128
+ *
129
+ * @example
130
+ * ```ts
131
+ * const theme: Theme = {
132
+ * colors: {
133
+ * palette: {
134
+ * light: { primary: "colors.sky.500", accent: "colors.mist.500" },
135
+ * dark: { primary: "colors.sky.100", accent: "colors.mist.100" }
136
+ * }
137
+ * },
138
+ * spacing: {
139
+ * 'space-unit': '8px',
140
+ * 'xs': 0.5,
141
+ * 'sm': 1,
142
+ * 'md': 2,
143
+ * 'lg': 4,
144
+ * 'xl': 8
145
+ * }
146
+ * }
147
+ * ```
148
+ */
149
+ interface Theme {
150
+ colors: {
151
+ palette: Palette;
152
+ };
153
+ spacing: SpacingScale;
154
+ typography: Typography;
155
+ borderWidth: BorderWidthScale;
156
+ breakpoints: BreakpointScale;
157
+ [key: string]: string | {
158
+ palette: Palette;
159
+ } | SpacingScale | BreakpointScale | BorderWidthScale | Record<string, Record<string, string>> | Typography;
160
+ }
161
+ /**
162
+ * @interface Tokens
163
+ * Tokens: Root object for all design tokens, including base color scales and theme palettes.
164
+ * @property colors - The base color scales, mapping color families to their scale steps and hex values.
165
+ * @property theme - The semantic color palette for the theme.
166
+ * @property [key: string] - Allows for additional token categories (e.g. typography) to be included in the future.
167
+ *
168
+ * @example
169
+ * ```ts
170
+ * const tokens: Tokens = {
171
+ * colors: {
172
+ * sky: { "100": "#f0f0f0", "500": "#0077ff" },
173
+ * mist: { "100": "#e0e0e0", "500": "#00ccaa" }
174
+ * },
175
+ * theme: {
176
+ * colors: {
177
+ * palette: {
178
+ * light: { primary: "colors.sky.500", accent: "colors.mist.500" },
179
+ * dark: { primary: "colors.sky.100", accent: "colors.mist.100" }
180
+ * }
181
+ * },
182
+ * spacing: {
183
+ * 'space-unit': '8px'
184
+ * 'xs': 0.5,
185
+ * 'sm': 1,
186
+ * 'md': 2,
187
+ * 'lg': 4,
188
+ * 'xl': 8
189
+ * }
190
+ * }
191
+ * }
192
+ * ```
193
+ */
194
+ interface Tokens {
195
+ colors: BaseColors;
196
+ theme: Theme;
197
+ [key: string]: string | Record<string, string> | BaseColors | Theme | SpacingScale;
198
+ }
199
+ /**
200
+ * @function
201
+ * colorRefToCssVar - Converts a color reference string (e.g. "colors.sky.500") to a CSS variable reference (e.g. "var(--sky-500)").
202
+ * Throws an error if the reference is not in the expected format.
203
+ * @param colorRef - The color reference string from the palette (e.g. "colors.sky.500").
204
+ * @returns The CSS variable reference string (e.g. "var(--sky-500)").
205
+ * @throws {Error} If the color reference is not in the format "colors.{family}.{scale}".
206
+ *
207
+ * @example
208
+ * ```ts
209
+ * colorRefToCssVar("colors.sky.500") // returns "var(--sky-500)"
210
+ * ```
211
+ */
212
+ declare function colorRefToCssVar(colorRef: string): string;
213
+ /**
214
+ * Generates the complete CSS string for base color variables and semantic theme variables.
215
+ *
216
+ * Per Tailwind v4 docs, when using @custom-variant dark with a data-attribute selector,
217
+ * the @media (prefers-color-scheme: dark) @theme inline block conflicts with that approach.
218
+ * Instead, explicit [data-theme] selector rules are used so both dark: utilities and
219
+ * CSS variable-based colors respond to the same data-theme attribute.
220
+ *
221
+ * System preference is handled in JS via matchMedia.
222
+ *
223
+ * @param tokens - The full design tokens object containing base colors and theme palettes.
224
+ * @returns The generated CSS string.
225
+ */
226
+ declare function generateTheme(tokens: Tokens): string;
227
+ /**
228
+ * Formats generated CSS and writes it to disk.
229
+ *
230
+ * @param source - Raw generated CSS source to process.
231
+ * @param outputFilePath - Destination stylesheet path. Defaults to `./theme.css`.
232
+ * @returns A promise that resolves after the output directory exists and the file is written.
233
+ */
234
+ declare function buildCssFile(source: string, outputFilePath?: string): Promise<void>;
235
+ /**
236
+ * Runs the CLI flow for generating the theme stylesheet from a tokens file.
237
+ *
238
+ * @returns A promise that resolves when the stylesheet has been generated and written.
239
+ */
240
+ declare function runParserCli(): Promise<void>;
241
+ export { buildCssFile, colorRefToCssVar, formatGeneratedCss, generateTheme, getOutputFilePathFromArgs, getTokensFilePathFromArgs, loadTokensFile, runParserCli, };
242
+ export type { Tokens };
243
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AA2CA;;;;;;GAMG;AACH,iBAAS,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAQzD;AAED;;;;;;GAMG;AACH,iBAAS,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAQzD;AAED;;;;;GAKG;AACH,iBAAe,cAAc,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMrE;AAED;;;;;;GAMG;AACH,iBAAS,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI/C;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,KAAK,IAAI,GAAG;IACV,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC,CAAC;AAEF;;;GAGG;AACH,KAAK,OAAO,GAAG;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAE,CAAC;AAE3C;;;GAGG;AACH,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC;AAElD;;;GAGG;AACH,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAE7C;;;;;;;;;;;;;;;GAeG;AACH,KAAK,YAAY,GAAG;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;CAChC,CAAC;AAEF,UAAU,UAAU;IAClB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChD;AAED;;;;;;;;;;;GAWG;AACH,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE/C;;;;;GAKG;AACH,KAAK,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,UAAU,KAAK;IACb,MAAM,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IAC7B,OAAO,EAAE,YAAY,CAAC;IACtB,UAAU,EAAE,UAAU,CAAC;IACvB,WAAW,EAAE,gBAAgB,CAAC;IAC9B,WAAW,EAAE,eAAe,CAAC;IAC7B,CAAC,GAAG,EAAE,MAAM,GACR,MAAM,GACN;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,GACpB,YAAY,GACZ,eAAe,GACf,gBAAgB,GAChB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GACtC,UAAU,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,UAAU,MAAM;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,KAAK,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,UAAU,GAAG,KAAK,GAAG,YAAY,CAAC;CACpF;AAED;;;;;;;;;;;;GAYG;AACH,iBAAS,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAWlD;AAsQD;;;;;;;;;;;;GAYG;AACH,iBAAS,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAqB7C;AAED;;;;;;GAMG;AACH,iBAAe,YAAY,CACzB,MAAM,EAAE,MAAM,EACd,cAAc,SAA0B,GACvC,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED;;;;GAIG;AACH,iBAAe,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAU3C;AAMD,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,EAClB,aAAa,EACb,yBAAyB,EACzB,yBAAyB,EACzB,cAAc,EACd,YAAY,GACb,CAAC;AACF,YAAY,EAAE,MAAM,EAAE,CAAC"}
package/dist/parser.js ADDED
@@ -0,0 +1,2 @@
1
+ import { a as e, c as t, i as n, n as r, o as i, r as a, s as o, t as s } from "./parser-DRqhbS0c.js";
2
+ export { s as buildCssFile, r as colorRefToCssVar, a as formatGeneratedCss, n as generateTheme, e as getOutputFilePathFromArgs, i as getTokensFilePathFromArgs, o as loadTokensFile, t as runParserCli };
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@arctura/theme",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "packageManager": "yarn@4.11.0",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "repository": {
10
+ "url": "https://github.com/marcomg-byte/arctura"
11
+ },
12
+ "bin": {
13
+ "arctura-theme": "dist/cli.js"
14
+ },
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "import": "./dist/index.js",
19
+ "require": "./dist/index.cjs"
20
+ },
21
+ "./parser": {
22
+ "types": "./dist/parser.d.ts",
23
+ "import": "./dist/parser.js",
24
+ "require": "./dist/parser.cjs"
25
+ }
26
+ },
27
+ "files": [
28
+ "dist/cli.*",
29
+ "dist/hooks/*.d.ts",
30
+ "dist/hooks/*.d.ts.map",
31
+ "dist/index.*",
32
+ "dist/parser-*",
33
+ "dist/parser.*"
34
+ ],
35
+ "sideEffects": false,
36
+ "scripts": {
37
+ "build": "vite build && tsc -p tsconfig.json && rimraf dist/errors",
38
+ "clean": "rimraf dist theme.css",
39
+ "lint": "eslint .",
40
+ "lint:fix": "eslint . --fix",
41
+ "test": "vitest run",
42
+ "test:cov": "vitest run --coverage",
43
+ "test:debug": "vitest --inspect-brk --no-file-parallelism",
44
+ "test:ui": "vitest --ui",
45
+ "test:watch": "vitest watch",
46
+ "theme:parse": "tsx src/cli.ts",
47
+ "typecheck": "tsc -p tsconfig.eslint.json"
48
+ },
49
+ "dependencies": {
50
+ "colord": "^2.9.3",
51
+ "postcss": "^8.5.6",
52
+ "postcss-js": "^5.1.0",
53
+ "react": "^19.2.7",
54
+ "react-dom": "^19.2.7"
55
+ },
56
+ "devDependencies": {
57
+ "@testing-library/dom": "^10.4.1",
58
+ "@testing-library/react": "^16.3.2",
59
+ "@types/node": "^25.9.3",
60
+ "@types/react": "^19.2.14",
61
+ "@types/react-dom": "^19",
62
+ "@vitest/coverage-v8": "4.1.8",
63
+ "eslint": "^9.39.1",
64
+ "jsdom": "^29.1.1",
65
+ "rimraf": "^6.1.3",
66
+ "tsx": "^4.22.4",
67
+ "typescript": "^6.0.3",
68
+ "vite": "^8.0.12",
69
+ "vitest": "4.1.8"
70
+ }
71
+ }