release: WRNexusJS 0.2.57
This commit is contained in:
@@ -19,9 +19,17 @@ export { loadAppConfig, loadRawConfig, headToString, resolveProfile, loadEnv } f
|
||||
export { findStyleEntry, bundleCss } from "./styles.ts";
|
||||
export type { FontConfig, GoogleFont, LocalFontFace, FontDisplay } from "./fonts.ts";
|
||||
export { renderFontHead, renderProductionFontHead, fontCspSources } from "./fonts.ts";
|
||||
export type { ThemeConfig, ThemeTokens, ResolvedTheme } from "./theme.ts";
|
||||
export type {
|
||||
ThemeConfig,
|
||||
ThemeTokens,
|
||||
ResolvedTheme,
|
||||
ThemePaletteName,
|
||||
CustomThemePalette,
|
||||
} from "./theme.ts";
|
||||
export {
|
||||
DEFAULT_THEMES,
|
||||
THEME_PALETTES,
|
||||
THEME_PALETTE_NAMES,
|
||||
THEME_COOKIE,
|
||||
THEME_CSS_HREF,
|
||||
THEME_JS_HREF,
|
||||
|
||||
@@ -13,7 +13,36 @@
|
||||
|
||||
export type ThemeTokens = Record<string, string>;
|
||||
|
||||
export const THEME_PALETTE_NAMES = [
|
||||
"blue",
|
||||
"indigo",
|
||||
"violet",
|
||||
"emerald",
|
||||
"cyan",
|
||||
"rose",
|
||||
"amber",
|
||||
"slate",
|
||||
] as const;
|
||||
|
||||
export type ThemePaletteName = (typeof THEME_PALETTE_NAMES)[number];
|
||||
|
||||
/** Required semantic colors for a custom application palette. */
|
||||
export interface CustomThemePalette {
|
||||
primary: string;
|
||||
primaryHover: string;
|
||||
primaryContrast: string;
|
||||
secondary: string;
|
||||
secondaryHover: string;
|
||||
secondaryContrast: string;
|
||||
info: string;
|
||||
success: string;
|
||||
warning: string;
|
||||
danger: string;
|
||||
}
|
||||
|
||||
export interface ThemeConfig {
|
||||
/** Built-in palette name, or a complete custom semantic color palette. */
|
||||
palette?: ThemePaletteName | CustomThemePalette;
|
||||
/** Name of the theme used when no `wire-theme` cookie is present. */
|
||||
default?: string;
|
||||
/** Named token maps. Deep-merged over the framework's built-in light/dark. */
|
||||
@@ -31,6 +60,142 @@ export const THEME_COOKIE = "wire-theme";
|
||||
export const THEME_CSS_HREF = "/__wrnexus/theme.css";
|
||||
export const THEME_JS_HREF = "/__wrnexus/theme.js";
|
||||
|
||||
export const THEME_PALETTES: Record<ThemePaletteName, CustomThemePalette> = {
|
||||
blue: {
|
||||
primary: "#2563eb",
|
||||
primaryHover: "#1d4ed8",
|
||||
primaryContrast: "#ffffff",
|
||||
secondary: "#475569",
|
||||
secondaryHover: "#334155",
|
||||
secondaryContrast: "#ffffff",
|
||||
info: "#0284c7",
|
||||
success: "#16a34a",
|
||||
warning: "#d97706",
|
||||
danger: "#dc2626",
|
||||
},
|
||||
indigo: {
|
||||
primary: "#4f46e5",
|
||||
primaryHover: "#4338ca",
|
||||
primaryContrast: "#ffffff",
|
||||
secondary: "#7c3aed",
|
||||
secondaryHover: "#6d28d9",
|
||||
secondaryContrast: "#ffffff",
|
||||
info: "#0284c7",
|
||||
success: "#16a34a",
|
||||
warning: "#d97706",
|
||||
danger: "#dc2626",
|
||||
},
|
||||
violet: {
|
||||
primary: "#7c3aed",
|
||||
primaryHover: "#6d28d9",
|
||||
primaryContrast: "#ffffff",
|
||||
secondary: "#db2777",
|
||||
secondaryHover: "#be185d",
|
||||
secondaryContrast: "#ffffff",
|
||||
info: "#2563eb",
|
||||
success: "#059669",
|
||||
warning: "#d97706",
|
||||
danger: "#dc2626",
|
||||
},
|
||||
emerald: {
|
||||
primary: "#059669",
|
||||
primaryHover: "#047857",
|
||||
primaryContrast: "#ffffff",
|
||||
secondary: "#0f766e",
|
||||
secondaryHover: "#115e59",
|
||||
secondaryContrast: "#ffffff",
|
||||
info: "#0284c7",
|
||||
success: "#16a34a",
|
||||
warning: "#d97706",
|
||||
danger: "#dc2626",
|
||||
},
|
||||
cyan: {
|
||||
primary: "#0891b2",
|
||||
primaryHover: "#0e7490",
|
||||
primaryContrast: "#ffffff",
|
||||
secondary: "#2563eb",
|
||||
secondaryHover: "#1d4ed8",
|
||||
secondaryContrast: "#ffffff",
|
||||
info: "#0284c7",
|
||||
success: "#16a34a",
|
||||
warning: "#d97706",
|
||||
danger: "#dc2626",
|
||||
},
|
||||
rose: {
|
||||
primary: "#e11d48",
|
||||
primaryHover: "#be123c",
|
||||
primaryContrast: "#ffffff",
|
||||
secondary: "#9333ea",
|
||||
secondaryHover: "#7e22ce",
|
||||
secondaryContrast: "#ffffff",
|
||||
info: "#2563eb",
|
||||
success: "#059669",
|
||||
warning: "#d97706",
|
||||
danger: "#dc2626",
|
||||
},
|
||||
amber: {
|
||||
primary: "#d97706",
|
||||
primaryHover: "#b45309",
|
||||
primaryContrast: "#ffffff",
|
||||
secondary: "#92400e",
|
||||
secondaryHover: "#78350f",
|
||||
secondaryContrast: "#ffffff",
|
||||
info: "#0284c7",
|
||||
success: "#15803d",
|
||||
warning: "#d97706",
|
||||
danger: "#dc2626",
|
||||
},
|
||||
slate: {
|
||||
primary: "#475569",
|
||||
primaryHover: "#334155",
|
||||
primaryContrast: "#ffffff",
|
||||
secondary: "#0f172a",
|
||||
secondaryHover: "#020617",
|
||||
secondaryContrast: "#ffffff",
|
||||
info: "#0369a1",
|
||||
success: "#15803d",
|
||||
warning: "#b45309",
|
||||
danger: "#b91c1c",
|
||||
},
|
||||
};
|
||||
|
||||
function paletteTokens(palette: CustomThemePalette): ThemeTokens {
|
||||
return {
|
||||
"color-primary": palette.primary,
|
||||
"color-primary-hover": palette.primaryHover,
|
||||
"color-primary-contrast": palette.primaryContrast,
|
||||
"color-secondary": palette.secondary,
|
||||
"color-secondary-hover": palette.secondaryHover,
|
||||
"color-secondary-contrast": palette.secondaryContrast,
|
||||
"color-info": palette.info,
|
||||
"color-success": palette.success,
|
||||
"color-warning": palette.warning,
|
||||
"color-danger": palette.danger,
|
||||
"color-error": palette.danger,
|
||||
};
|
||||
}
|
||||
|
||||
function resolvePalette(value?: ThemeConfig["palette"]): CustomThemePalette {
|
||||
if (!value) return THEME_PALETTES.blue;
|
||||
if (typeof value === "string") {
|
||||
const palette = THEME_PALETTES[value];
|
||||
if (!palette) {
|
||||
throw new Error(
|
||||
`Unknown theme palette "${value}". Choose one of: ${THEME_PALETTE_NAMES.join(", ")}.`,
|
||||
);
|
||||
}
|
||||
return palette;
|
||||
}
|
||||
const missing = Object.keys(THEME_PALETTES.blue).filter((key) => {
|
||||
const color = value[key as keyof CustomThemePalette];
|
||||
return typeof color !== "string" || !color.trim();
|
||||
});
|
||||
if (missing.length) {
|
||||
throw new Error(`Custom theme palette is missing required colors: ${missing.join(", ")}.`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/** Built-in themes so components have tokens out of the box. */
|
||||
export const DEFAULT_THEMES: Record<string, ThemeTokens> = {
|
||||
light: {
|
||||
@@ -44,9 +209,14 @@ export const DEFAULT_THEMES: Record<string, ThemeTokens> = {
|
||||
"color-primary": "#2563eb",
|
||||
"color-primary-hover": "#1d4ed8",
|
||||
"color-primary-contrast": "#ffffff",
|
||||
"color-secondary": "#475569",
|
||||
"color-secondary-hover": "#334155",
|
||||
"color-secondary-contrast": "#ffffff",
|
||||
"color-info": "#0284c7",
|
||||
"color-danger": "#dc2626",
|
||||
"color-success": "#16a34a",
|
||||
"color-warning": "#d97706",
|
||||
"color-error": "#dc2626",
|
||||
radius: "8px",
|
||||
"radius-sm": "5px",
|
||||
"font-sans": "system-ui, -apple-system, Segoe UI, Roboto, sans-serif",
|
||||
@@ -63,9 +233,14 @@ export const DEFAULT_THEMES: Record<string, ThemeTokens> = {
|
||||
"color-primary": "#6c8cff",
|
||||
"color-primary-hover": "#8aa2ff",
|
||||
"color-primary-contrast": "#0b1020",
|
||||
"color-secondary": "#a78bfa",
|
||||
"color-secondary-hover": "#c4b5fd",
|
||||
"color-secondary-contrast": "#0b1020",
|
||||
"color-info": "#38bdf8",
|
||||
"color-danger": "#f87171",
|
||||
"color-success": "#4ade80",
|
||||
"color-warning": "#fbbf24",
|
||||
"color-error": "#f87171",
|
||||
radius: "10px",
|
||||
"radius-sm": "6px",
|
||||
"font-sans": "system-ui, -apple-system, Segoe UI, Roboto, sans-serif",
|
||||
@@ -75,13 +250,18 @@ export const DEFAULT_THEMES: Record<string, ThemeTokens> = {
|
||||
|
||||
/** Merge the user's theme config over the built-in defaults. */
|
||||
export function resolveThemeConfig(config?: ThemeConfig): ResolvedTheme {
|
||||
const semanticTokens = paletteTokens(resolvePalette(config?.palette));
|
||||
const themes: Record<string, ThemeTokens> = {};
|
||||
const names = new Set<string>([
|
||||
...Object.keys(DEFAULT_THEMES),
|
||||
...Object.keys(config?.themes ?? {}),
|
||||
]);
|
||||
for (const name of names) {
|
||||
themes[name] = { ...(DEFAULT_THEMES[name] ?? {}), ...(config?.themes?.[name] ?? {}) };
|
||||
themes[name] = {
|
||||
...(DEFAULT_THEMES[name] ?? DEFAULT_THEMES.light),
|
||||
...semanticTokens,
|
||||
...(config?.themes?.[name] ?? {}),
|
||||
};
|
||||
}
|
||||
const list = Object.keys(themes);
|
||||
const preferred = config?.default && themes[config.default] ? config.default : undefined;
|
||||
|
||||
Reference in New Issue
Block a user