alouette
Version:
A modern, customizable design system built on top of NativeWind v5 with configurable defaults
87 lines (75 loc) • 3.02 kB
text/typescript
/* eslint-disable import-x/extensions */
// Build-script driver: turn palette params into the two files an app imports.
// Node-only — call it from a script, never from app code.
import { mkdirSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import type { GenerateThemeResult } from "./generateTheme.ts";
import { generateTheme } from "./generateTheme.ts";
import type { AccentName, PaletteSpec } from "./paletteSpecs.ts";
const generatedHeader = "/* Generated by alouette writeTheme. DO NOT EDIT. */";
// Emitted the way a formatter would leave it — unquoted theme keys (hence the
// camelcase disable in the file header) and trailing commas — so a generated
// file never churns the first time the app runs prettier/oxfmt over it.
const serializeThemeVariables = (
themeVariables: GenerateThemeResult["themeVariables"],
): string =>
`{\n${Object.entries(themeVariables)
.map(([theme, variables]) => {
const entries = Object.entries(variables)
.map(([name, value]) => ` "${name}": "${value}",`)
.join("\n");
return ` ${theme}: {\n${entries}\n },`;
})
.join("\n")}\n}`;
export interface WriteThemeParams {
/** Directory the two files are written to, created if missing. */
outDir: string;
/**
* Per-accent params merged over the alouette defaults, so only the accents
* the app re-colors need to be listed. Omit to reproduce the default palette.
*/
overrides?: Partial<Record<AccentName, PaletteSpec>>;
cssFileName?: string;
themeVariablesFileName?: string;
}
export interface WriteThemeResult {
cssPath: string;
themeVariablesPath: string;
}
/**
* Generate an app's palette and write both halves of it to disk: the palette
* CSS to import after `alouette/core.css`, and the `themeVariables` module to
* pass to `<AlouetteProvider themeVariables={...}>`. Writing both from one call
* is what keeps the className tokens and the JS token reads on the same colors.
*/
export const writeTheme = ({
outDir,
overrides,
cssFileName = "palette.css",
themeVariablesFileName = "themeVariables.ts",
}: WriteThemeParams): WriteThemeResult => {
const { css, themeVariables } = generateTheme(overrides);
mkdirSync(outDir, { recursive: true });
const cssPath = join(outDir, cssFileName);
writeFileSync(
cssPath,
`${generatedHeader}
/* App palette. Import after "alouette/core.css", instead of
"alouette/global.css" — which carries alouette's default palette. */
${css}`,
);
const themeVariablesPath = join(outDir, themeVariablesFileName);
writeFileSync(
themeVariablesPath,
`${generatedHeader}
/* eslint-disable camelcase */
import type { ThemeVariablesMap } from "alouette";
/**
* Resolved CSS-variable maps for every theme, paired with the generated palette
* CSS. Pass to \`<AlouetteProvider themeVariables={...}>\`.
*/
export const themeVariables: ThemeVariablesMap = ${serializeThemeVariables(themeVariables)};
`,
);
return { cssPath, themeVariablesPath };
};