890 lines
53 KiB
Plaintext
890 lines
53 KiB
Plaintext
page wrnexusstyles {
|
|
seo {
|
|
title = "@wrnexus/styles"
|
|
description = "CSS pipeline, themes, fonts, profiles, and application config."
|
|
}
|
|
|
|
view {
|
|
<div class="docs-shell">
|
|
<a href="#main" class="skip-link">Skip to content</a>
|
|
<header class="topbar">
|
|
<a class="brand" href="/"><span>W</span> WRNexusJS</a>
|
|
<nav aria-label="Primary"><a href="/getting-started">Get started</a><a href="/packages">Packages</a><a href="https://component.wrnexusjs.dev/">Components</a><a href="/language">Language</a><a href="/architecture">Architecture</a></nav>
|
|
<div class="topbar-actions"><a class="preview-pill" href="/access">Private preview · v0.8.0</a><button data-wire-theme-toggle class="theme-button" aria-label="Toggle color theme" title="Toggle color theme">◐</button></div>
|
|
</header>
|
|
<div class="mobile-doc-nav"><details><summary>Browse documentation</summary><nav><a href="/getting-started">Get started</a><a href="/packages">Packages</a><a href="https://component.wrnexusjs.dev/">Components</a><a href="/language">Language</a><a href="/architecture">Architecture</a><a href="/tutorial">Tutorial</a><a href="/guides/project-structure">Guides</a><a href="/examples">Examples</a><a href="/search">Search</a></nav></details></div>
|
|
<main class="portal-main docs-layout">
|
|
<article id="main" class="documentation prose standalone package-document"><nav class="breadcrumbs" aria-label="Breadcrumb"><a href="/">Home</a><span>/</span><a href="/packages">Packages</a><span>/</span><span aria-current="page">@wrnexus/styles</span></nav><section class="doc-intro"><span class="eyebrow">Frontend · Package reference</span><h1>@wrnexus/styles</h1><p>CSS pipeline, themes, fonts, profiles, and application config.</p><div class="doc-meta"><span>v0.8.0</span><span>Private registry</span><span>Frontend</span></div><section id="access" class="access-callout"><h2>Install the package</h2><p>After WorkRoot approves private registry access, install the release-aligned package:</p><pre><code>bun add @wrnexus/styles@0.8.0</code><button type="button" class="copy-button" aria-label="Copy installation command">Copy</button></pre><p><a href="/access">Request preview access</a>. Never put registry tokens in source control.</p></section></section><section id="guide"><h3 id="reusable-layers-and-presets">Reusable layers and presets</h3>
|
|
<p>Compose local or package foundations in order; later layers override earlier ones and the application has final base-config precedence:</p>
|
|
<pre data-language="ts"><code>export default defineConfig({
|
|
extends: ["@workroot/wrnexus-enterprise", "./layers/company"],
|
|
profiles: { production: { port: 8080 } },
|
|
});</code></pre>
|
|
<p>A directory layer exports <code>wrnexus.layer.ts</code> (JavaScript/MJS are supported). A package can provide that conventional file or declare <code>wrnexus.layer</code> in its <code>package.json</code>. Layers may extend other layers and carry the complete app configuration, including plugins that contribute layouts, components, routes, middleware, and migrations. <code>plugins</code> and <code>head</code> compose; other arrays intentionally replace earlier values. Cycles and missing/invalid entries fail with stable <code>WRN-CONFIG-LAYER-*</code> diagnostics. <code>wrnexus config --explain</code> lists every resolved layer source.</p>
|
|
<blockquote>Global CSS bundling, the <code>--wire-*</code> design-token theme system, and the <code>wrnexus.config.ts</code> app-config loader for WRNexusJS apps.</blockquote>
|
|
<p>Part of the <strong>WRNexusJS</strong> framework — an SSR-first, Bun-native full-stack web framework.</p>
|
|
<h3 id="overview">Overview</h3>
|
|
<p>This package owns three server-side concerns that shape every page a WRNexusJS app renders:</p>
|
|
<p>1. <strong>Global stylesheet pipeline</strong> — finds <code>app/styles/global.css</code> (or aggregates <code>app/styles/*.css</code>), bundles it with Bun's CSS bundler (which resolves <code>@import</code>, including from <code>node_modules</code>), and produces one stylesheet that is <code><link></code>ed into every page's <code><head></code>. Because it is a plain global sheet, it styles server-rendered markup and hydrated client islands identically. A custom <code>process</code> hook lets you swap in Tailwind / PostCSS / Sass. 2. <strong>Theme system</strong> — design tokens exposed as CSS custom properties (<code>--wire-<key></code>), with built-in <code>light</code>/<code>dark</code> sets, deep-merged user overrides, an SSR <code><html data-theme></code> render (no flash), and a tiny client runtime to toggle/persist the choice. 3. <strong>App config</strong> — loads <code>wrnexus.config.ts</code> (the <code>AppConfig</code> type), applies named profile overrides, and loads the <code>.env</code> cascade.</p>
|
|
<p>It runs server-side / at build time. Reach for it when configuring an app, defining themes, or customising how global CSS is produced.</p>
|
|
<pre data-language="bash"><code>bun add @wrnexus/styles</code></pre>
|
|
<blockquote>Private package — the machine must be authenticated to the <code>wrnexus</code> npm org</blockquote>
|
|
<blockquote>(a read token in <code>~/.npmrc</code>). Requires <strong>Bun</strong> (Node is not supported).</blockquote>
|
|
<h3 id="api">API</h3>
|
|
<p>Everything is exported from the package root (<code>@wrnexus/styles</code>).</p>
|
|
<h4 id="config-loading">Config loading</h4>
|
|
<div class="table-wrap"><table>
|
|
<thead><tr><th>Export</th><th>Signature</th><th>Purpose</th></tr></thead>
|
|
<tbody><tr><td><code>loadAppConfig</code></td><td><code>(appRoot: string, profile?: string) => Promise<AppConfig></code></td><td>Load <code>wrnexus.config.*</code> with the active profile deep-merged in (<code>profiles</code> stripped from the result).</td></tr><tr><td><code>loadRawConfig</code></td><td><code>(appRoot: string) => Promise<AppConfig></code></td><td>Load the raw config with the <code>profiles</code> map intact; returns <code>{}</code> if no config file exists.</td></tr><tr><td><code>resolveProfile</code></td><td><code>(options?: { explicit?; mode? }) => string</code></td><td>Resolve the active profile: explicit arg > <code>WRNEXUS_PROFILE</code> env var > mode-based default (<code>production</code> in prod, else <code>development</code>).</td></tr><tr><td><code>loadEnv</code></td><td><code>(appRoot: string, profile: string) => Record<string, string></code></td><td>Load the <code>.env</code> cascade for a profile into <code>process.env</code> without clobbering real env vars. Returns what it loaded.</td></tr><tr><td><code>headToString</code></td><td>`(head?: string \</td><td>string[]) => string`</td><td>Flatten <code>AppConfig.head</code> into a single HTML string.</td></tr></tbody></table></div>
|
|
<p>Config file names probed, in order: <code>wrnexus.config.ts</code>, <code>wrnexus.config.js</code>, <code>wrnexus.config.mjs</code>.</p>
|
|
<p><code>.env</code> cascade precedence (low → high): <code>.env</code> < <code>.env.<profile></code> < <code>.env.local</code> < <code>.env.<profile>.local</code>. Variables already present in the real environment always win.</p>
|
|
<h4 id="appconfig"><code>AppConfig</code></h4>
|
|
<p>The type of the object your <code>wrnexus.config.ts</code> default-exports. Every field is optional.</p>
|
|
<div class="table-wrap"><table>
|
|
<thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead>
|
|
<tbody><tr><td><code>head</code></td><td>`string \</td><td>string[]`</td><td>Raw HTML appended to every page's <code><head></code> (e.g. CDN stylesheet/script links).</td></tr><tr><td><code>seo</code></td><td><code>SeoConfig</code></td><td>Global SEO defaults, merged with each page's exported <code>meta</code>. (from <code>@wrnexus/core</code>)</td></tr><tr><td><code>security</code></td><td><code>SecurityConfig</code></td><td>Framework security headers and optional CORS policy. (from <code>@wrnexus/core</code>)</td></tr><tr><td><code>styles</code></td><td><code>StylesConfig</code></td><td>Global stylesheet pipeline config (see below).</td></tr><tr><td><code>theme</code></td><td><code>ThemeConfig</code></td><td>Design-token themes, deep-merged over the built-in light/dark.</td></tr><tr><td><code>i18n</code></td><td><code>{ default?: string; locales?: string[] }</code></td><td>Default language + supported locales (strings live in <code>app/locales/*.json</code>).</td></tr><tr><td><code>db</code></td><td>`{ driver: "sqlite" \</td><td>"postgres" \</td><td>"mysql" \</td><td>"mongo"; url: string }`</td><td>Default database connection; reached with <code>getDb()</code>.</td></tr><tr><td><code>databases</code></td><td><code>Record<string, { driver; url }></code></td><td>Additional named databases, reached with <code>getDb("<name>")</code>; each has its own <code>app/db/<name>/</code> migrations/queries.</td></tr><tr><td><code>realtime</code></td><td><code>{ scale?: boolean; redisUrl?: string }</code></td><td>When <code>scale</code> is true (or <code>redisUrl</code> is set), room broadcasts bridge over Redis pub/sub so they reach clients on every app process.</td></tr><tr><td><code>port</code></td><td><code>number</code></td><td>Default server port.</td></tr><tr><td><code>profiles</code></td><td><code>Record<string, Partial<Omit<AppConfig, "profiles">>></code></td><td>Named profiles (dev, prod, uat, test, …). The active profile's overrides are deep-merged over the base config. Selected via <code>--profile=<name></code> or <code>WRNEXUS_PROFILE</code>.</td></tr></tbody></table></div>
|
|
<h4 id="styles-pipeline">Styles pipeline</h4>
|
|
<div class="table-wrap"><table>
|
|
<thead><tr><th>Export</th><th>Signature</th><th>Purpose</th></tr></thead>
|
|
<tbody><tr><td><code>findStyleEntry</code></td><td>`(appDir, appRoot, override?) => string \</td><td>null`</td><td>Resolve the CSS entry: <code>override</code> (relative to <code>appRoot</code>) → <code>app/styles/global.css</code> → an aggregate of all <code>app/styles/*.css</code> (written to <code>app/.wrnexus/styles-entry.css</code>). <code>null</code> if the app has no styles.</td></tr><tr><td><code>bundleCss</code></td><td><code>(entryPath: string, mode: Mode) => Promise<string></code></td><td>Bundle an entry with <code>Bun.build</code> (CSS bundler). Resolves <code>@import</code> (local + node_modules), handles nesting, minifies when <code>mode === "production"</code>.</td></tr><tr><td><code>renderStyles</code></td><td><code>(ctx: StyleProcessContext, styles?: StylesConfig) => Promise<string></code></td><td>Produce final CSS: runs <code>styles.process(ctx)</code> if provided, else <code>bundleCss</code>. Returns <code>""</code> when <code>ctx.entryPath</code> is null.</td></tr></tbody></table></div>
|
|
<p><code>StylesConfig</code>:</p>
|
|
<pre data-language="ts"><code>interface StylesConfig {
|
|
/** CSS entry path relative to the app root. Default: app/styles/global.css */
|
|
entry?: string;
|
|
/** Custom processor — return the final CSS string (Tailwind/PostCSS/Sass). */
|
|
process?: (ctx: StyleProcessContext) => string | Promise<string>;
|
|
}
|
|
|
|
interface StyleProcessContext {
|
|
entryPath: string | null; // resolved absolute CSS entry, or null
|
|
appDir: string;
|
|
appRoot: string;
|
|
mode: Mode; // "development" | "production"
|
|
}</code></pre>
|
|
<h4 id="theme-system">Theme system</h4>
|
|
<div class="table-wrap"><table>
|
|
<thead><tr><th>Export</th><th>Type / Signature</th><th>Purpose</th></tr></thead>
|
|
<tbody><tr><td><code>DEFAULT_THEMES</code></td><td><code>Record<string, ThemeTokens></code></td><td>Built-in <code>light</code> and <code>dark</code> token maps.</td></tr><tr><td><code>THEME_COOKIE</code></td><td><code>"wire-theme"</code></td><td>Cookie the resolved theme is read from / persisted to.</td></tr><tr><td><code>THEME_CSS_HREF</code></td><td><code>"/__wrnexus/theme.css"</code></td><td>URL the generated theme stylesheet is served at.</td></tr><tr><td><code>THEME_JS_HREF</code></td><td><code>"/__wrnexus/theme.js"</code></td><td>URL the client theme runtime is served at.</td></tr><tr><td><code>resolveThemeConfig</code></td><td><code>(config?: ThemeConfig) => ResolvedTheme</code></td><td>Deep-merge the user's <code>theme</code> config over the defaults; pick the default theme (config's <code>default</code> if valid, else <code>dark</code>, else the first).</td></tr><tr><td><code>resolveThemeName</code></td><td>`(cookieValue: string \</td><td>undefined, theme: ResolvedTheme) => string`</td><td>Pick a valid theme name from a cookie, falling back to <code>theme.default</code>.</td></tr><tr><td><code>renderThemeCss</code></td><td><code>(theme: ResolvedTheme) => string</code></td><td>Generate the theme stylesheet: a <code>:root{…}</code> default plus one <code>[data-theme="<name>"]{…}</code> block per theme.</td></tr><tr><td><code>renderThemeRuntime</code></td><td><code>(theme: ResolvedTheme) => string</code></td><td>Generate the client runtime (see below).</td></tr></tbody></table></div>
|
|
<p>Tokens are emitted as <code>--wire-<key></code> custom properties, <strong>except</strong> the reserved key <code>color-scheme</code>, which is emitted as the native <code>color-scheme</code> CSS property so form controls and scrollbars match the theme.</p>
|
|
<p><code>ThemeConfig</code> / <code>ThemeTokens</code> / <code>ResolvedTheme</code>:</p>
|
|
<pre data-language="ts"><code>type ThemeTokens = Record<string, string>;
|
|
|
|
interface ThemeConfig {
|
|
palette?: ThemePaletteName | CustomThemePalette;
|
|
default?: string; // theme used when no cookie is present
|
|
themes?: Record<string, ThemeTokens>; // deep-merged over built-in light/dark
|
|
}
|
|
|
|
interface ResolvedTheme {
|
|
default: string;
|
|
names: string[];
|
|
themes: Record<string, ThemeTokens>;
|
|
}</code></pre>
|
|
<p>Built-in palettes are <code>blue</code>, <code>indigo</code>, <code>violet</code>, <code>emerald</code>, <code>cyan</code>, <code>rose</code>, <code>amber</code>, and <code>slate</code>. Each supplies primary, secondary, info, success, warning, danger/error, hover, and contrast colors to every light/dark theme. Built-in token keys also include surfaces, text, borders, radii, fonts, and shadows.</p>
|
|
<p>A custom palette is intentionally complete, so components never fall back to an unrelated blue status or action color:</p>
|
|
<pre data-language="ts"><code>theme: {
|
|
palette: {
|
|
primary: "#7c3aed",
|
|
primaryHover: "#6d28d9",
|
|
primaryContrast: "#ffffff",
|
|
secondary: "#db2777",
|
|
secondaryHover: "#be185d",
|
|
secondaryContrast: "#ffffff",
|
|
info: "#2563eb",
|
|
success: "#059669",
|
|
warning: "#d97706",
|
|
danger: "#dc2626",
|
|
},
|
|
}</code></pre>
|
|
<p>The client runtime (<code>renderThemeRuntime</code>) exposes <code>window.wireTheme</code> with <code>{ get, set, toggle, bind, themes }</code>, wires up any <code>[data-wire-theme-toggle]</code> and <code>[data-wire-theme-set]</code> elements on load, and persists the choice to the <code>wire-theme</code> cookie (<code>max-age</code> 1 year, <code>samesite=lax</code>). <code>toggle()</code> cycles through the configured theme names in order.</p>
|
|
<h3 id="usage">Usage</h3>
|
|
<h4 id="wrnexus-config-ts"><code>wrnexus.config.ts</code></h4>
|
|
<pre data-language="ts"><code>import type { AppConfig } from "@wrnexus/styles";
|
|
|
|
export default {
|
|
head: [
|
|
'<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5/dist/css/bootstrap.min.css">',
|
|
],
|
|
port: 3000,
|
|
db: { driver: "sqlite", url: "app.db" },
|
|
theme: {
|
|
palette: "violet",
|
|
default: "dark",
|
|
themes: {
|
|
light: { "color-primary": "#7c3aed" }, // override one token; rest inherited
|
|
brand: {
|
|
// add a whole new theme
|
|
"color-scheme": "dark",
|
|
"color-bg": "#0a0a0a",
|
|
"color-primary": "#22d3ee",
|
|
},
|
|
},
|
|
},
|
|
styles: {
|
|
entry: "app/styles/main.css",
|
|
},
|
|
profiles: {
|
|
production: {
|
|
db: { driver: "postgres", url: process.env.DATABASE_URL! },
|
|
},
|
|
},
|
|
} satisfies AppConfig;</code></pre>
|
|
<h4 id="loading-config-producing-css">Loading config + producing CSS</h4>
|
|
<pre data-language="ts"><code>import {
|
|
loadAppConfig,
|
|
resolveProfile,
|
|
loadEnv,
|
|
findStyleEntry,
|
|
renderStyles,
|
|
} from "@wrnexus/styles";
|
|
|
|
const appRoot = process.cwd();
|
|
const mode = "production" as const;
|
|
|
|
const profile = resolveProfile({ mode });
|
|
loadEnv(appRoot, profile);
|
|
|
|
const config = await loadAppConfig(appRoot, profile);
|
|
|
|
const appDir = `${appRoot}/app`;
|
|
const entryPath = findStyleEntry(appDir, appRoot, config.styles?.entry);
|
|
const css = await renderStyles({ entryPath, appDir, appRoot, mode }, config.styles);</code></pre>
|
|
<h4 id="rendering-the-theme">Rendering the theme</h4>
|
|
<pre data-language="ts"><code>import {
|
|
resolveThemeConfig,
|
|
resolveThemeName,
|
|
renderThemeCss,
|
|
renderThemeRuntime,
|
|
THEME_COOKIE,
|
|
} from "@wrnexus/styles";
|
|
|
|
const theme = resolveThemeConfig(config.theme);
|
|
|
|
// Server: pick the active theme from the request cookie (no flash).
|
|
const active = resolveThemeName(cookies[THEME_COOKIE], theme);
|
|
// → render <html data-theme={active}>
|
|
|
|
const themeCss = renderThemeCss(theme); // served at THEME_CSS_HREF
|
|
const themeJs = renderThemeRuntime(theme); // served at THEME_JS_HREF</code></pre>
|
|
<p>In templates, consume tokens via the custom properties:</p>
|
|
<pre data-language="css"><code>.card {
|
|
background: var(--wire-color-surface);
|
|
color: var(--wire-color-text);
|
|
border: 1px solid var(--wire-color-border);
|
|
border-radius: var(--wire-radius);
|
|
box-shadow: var(--wire-shadow-1);
|
|
}</code></pre>
|
|
<pre data-language="html"><code><button data-wire-theme-toggle>Toggle theme</button>
|
|
<button data-wire-theme-set="brand">Brand theme</button></code></pre>
|
|
<h3 id="requirements-notes">Requirements / Notes</h3>
|
|
<ul>
|
|
<li><strong>Bun-only.</strong> <code>bundleCss</code> uses <code>Bun.build</code>'s CSS bundler for <code>@import</code> resolution, nesting, and minification. Node is not supported.</li>
|
|
<li>Config and env loading use <code>node:fs</code> / <code>node:path</code> / <code>node:url</code> and read from <code>process.env</code>.</li>
|
|
<li>Peer package: <code>@wrnexus/core</code> supplies the <code>SeoConfig</code> and <code>SecurityConfig</code> types referenced by <code>AppConfig</code>.</li>
|
|
<li>The bundled global stylesheet, the theme stylesheet (<code>THEME_CSS_HREF</code>), and the theme runtime (<code>THEME_JS_HREF</code>) are wired into pages by the framework's server; this package only produces their contents.</li>
|
|
</ul></section><section id="api" class="api"><h2>Complete TypeScript API</h2><p>Generated from the exact installed package declarations.</p><pre data-language="typescript"><code>import { PerformanceBudgets, SeoConfig, SecurityConfig } from '@wrnexus/core';
|
|
import { PluginInput, PluginPermission } from '@wrnexus/plugin';
|
|
import { StorageConfig } from '@wrnexus/uploader';
|
|
|
|
/**
|
|
* Theme system - design tokens that work SSR and client-side.
|
|
*
|
|
* Tokens are plain CSS custom properties (`--wire-<key>`) so they cascade and
|
|
* can be overridden by user CSS. Each theme is a flat token map; the framework
|
|
* ships default `light`/`dark` sets and the user's config deep-merges over them.
|
|
*
|
|
* The server renders both `<html data-theme="...">` and
|
|
* `<html data-accent="...">` from cookies, so the correct theme and accent are
|
|
* present before the first paint. The reserved token key `color-scheme` is
|
|
* emitted as the native CSS property instead of a custom property.
|
|
*/
|
|
type ThemeSemanticColor = "primary" | "secondary" | "info" | "success" | "warning" | "danger" | "error";
|
|
type ThemeToken = "color-scheme" | "color-bg" | "color-background" | "color-foreground" | "color-surface" | "color-surface-2" | "color-surface-raised" | "color-surface-muted" | "color-text" | "color-text-muted" | "color-text-subtle" | "color-muted" | "color-border" | "color-border-strong" | "color-code-background" | "color-code-surface" | "color-code-text" | "color-code-muted" | "color-code-border" | `color-${ThemeSemanticColor}` | `color-${ThemeSemanticColor}-${"hover" | "active" | "contrast" | "soft" | "muted" | "text"}` | `color-on-${"primary" | "secondary"}` | "radius" | "radius-sm" | "shadow-1" | "shadow-sm" | "shadow-md" | "shadow-lg" | "space-section" | "space-section-sm" | "container-max" | "font-sans";
|
|
/** Known tokens get autocomplete while applications may add namespaced custom tokens. */
|
|
type ThemeTokens = Partial<Record<ThemeToken, string>> & Record<string, string>;
|
|
declare function defineThemeTokens<T extends ThemeTokens>(tokens: T): T;
|
|
declare function themeVar(token: ThemeToken, fallback?: string): string;
|
|
declare const THEME_PALETTE_NAMES: readonly ["blue", "indigo", "violet", "emerald", "cyan", "rose", "amber", "slate"];
|
|
type ThemePaletteName = (typeof THEME_PALETTE_NAMES)[number];
|
|
/** Required semantic colors for a custom application palette. */
|
|
interface CustomThemePalette {
|
|
primary: string;
|
|
primaryHover: string;
|
|
primaryContrast: string;
|
|
secondary: string;
|
|
secondaryHover: string;
|
|
secondaryContrast: string;
|
|
info: string;
|
|
success: string;
|
|
warning: string;
|
|
danger: string;
|
|
}
|
|
interface ThemeAccentConfig {
|
|
/**
|
|
* Accent used when no `wire-accent` cookie is present.
|
|
*
|
|
* - Omitted: use the named `palette`, or `blue` when no palette is configured.
|
|
* - `false`: keep the configured base palette until the user explicitly picks an accent.
|
|
*/
|
|
default?: ThemePaletteName | false;
|
|
/** Runtime-selectable accent names. Defaults to every built-in THEME_PALETTE. */
|
|
options?: ThemePaletteName[];
|
|
}
|
|
interface ThemeConfig {
|
|
/** Built-in palette name, or a complete custom semantic color palette. */
|
|
palette?: ThemePaletteName | CustomThemePalette;
|
|
/** Runtime accent/palette switcher configuration. */
|
|
accent?: ThemeAccentConfig;
|
|
/** 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. */
|
|
themes?: Record<string, ThemeTokens>;
|
|
}
|
|
interface ResolvedTheme {
|
|
default: string;
|
|
names: string[];
|
|
themes: Record<string, ThemeTokens>;
|
|
defaultAccent?: ThemePaletteName;
|
|
accentNames: ThemePaletteName[];
|
|
}
|
|
/** Cookies used by the SSR renderer and client runtime. */
|
|
declare const THEME_COOKIE = "wire-theme";
|
|
declare const ACCENT_COOKIE = "wire-accent";
|
|
declare const THEME_CSS_HREF = "/__wrnexus/theme.css";
|
|
declare const THEME_JS_HREF = "/__wrnexus/theme.js";
|
|
/**
|
|
* Single source of truth for both configured palettes and runtime accents.
|
|
* Do not create a second hard-coded ACCENTS map in the browser runtime.
|
|
*/
|
|
declare const THEME_PALETTES: Record<ThemePaletteName, CustomThemePalette>;
|
|
/** Built-in themes so components have tokens out of the box. */
|
|
declare const DEFAULT_THEMES: Record<string, ThemeTokens>;
|
|
/** Merge the user's theme config over the built-in defaults. */
|
|
declare function resolveThemeConfig(config?: ThemeConfig): ResolvedTheme;
|
|
/** Pick a valid theme name from a cookie value, falling back to the default. */
|
|
declare function resolveThemeName(cookieValue: string | undefined, theme: ResolvedTheme): string;
|
|
/** Pick a valid accent name from a cookie value, falling back to the configured default. */
|
|
declare function resolveAccentName(cookieValue: string | undefined, theme: ResolvedTheme): ThemePaletteName | undefined;
|
|
/**
|
|
* Generate the theme stylesheet.
|
|
*
|
|
* Theme selectors are emitted first. Accent selectors are emitted afterwards,
|
|
* so a selected accent consistently overrides every semantic palette token,
|
|
* including soft/muted/text variants, before the first paint.
|
|
*/
|
|
declare function renderThemeCss(theme: ResolvedTheme): string;
|
|
/**
|
|
* Generate the client theme runtime. It exposes `window.wireTheme` and
|
|
* `window.wireAccent`, and binds theme/accent controls.
|
|
*
|
|
* The runtime changes only data attributes and cookies. It never writes inline
|
|
* CSS variables and never uses localStorage, so CSS and SSR remain the single
|
|
* source of truth.
|
|
*/
|
|
declare function renderThemeRuntime(theme: ResolvedTheme): string;
|
|
|
|
/**
|
|
* Font configuration.
|
|
*
|
|
* Declare fonts in `wrnexus.config.ts` under `fonts` and the framework emits
|
|
* optimized `<head>` markup for you:
|
|
* - Google Fonts: `preconnect` hints + a single subsetted stylesheet request
|
|
* (only the weights you list) with `font-display`. The CSP is auto-extended
|
|
* so the fonts load under the default security policy (see loadAppConfig).
|
|
* - Self-hosted fonts: generated `@font-face` rules + optional `<link rel=preload>`
|
|
* for above-the-fold text (the fastest, no-third-party option).
|
|
* - Family stacks: `sans`/`mono`/`serif` become `--wrn-font-*` CSS variables,
|
|
* and `sans` is applied to `body`.
|
|
*/
|
|
type FontDisplay = "auto" | "block" | "swap" | "fallback" | "optional";
|
|
interface GoogleFont {
|
|
/** Family name as it appears on fonts.google.com, e.g. "Inter". */
|
|
family: string;
|
|
/** Weights to load — ONLY these are fetched. Default: [400]. */
|
|
weights?: (number | string)[];
|
|
/** Also load italic styles for each weight. */
|
|
italic?: boolean;
|
|
/** Per-font `font-display` override (else the config default). */
|
|
display?: FontDisplay;
|
|
}
|
|
interface LocalFontFace {
|
|
/** `font-family` name this face defines. */
|
|
family: string;
|
|
/** URL to the font file, typically served from `public/` (e.g. "/fonts/inter.woff2"). */
|
|
src: string;
|
|
/** e.g. 400, "700", or "100 900" for a variable font. Default: 400. */
|
|
weight?: number | string;
|
|
style?: "normal" | "italic";
|
|
/** CSS `src` format; inferred from the file extension when omitted. */
|
|
format?: string;
|
|
display?: FontDisplay;
|
|
/** Emit `<link rel="preload" as="font">` — use for the primary above-the-fold face. */
|
|
preload?: boolean;
|
|
/** Optional `unicode-range` subset. */
|
|
unicodeRange?: string;
|
|
}
|
|
interface FontConfig {
|
|
/** Google Fonts, loaded with preconnect + weight subsetting + `font-display`. */
|
|
google?: GoogleFont[];
|
|
/** Self-hosted `@font-face` definitions (files served from `public/`). */
|
|
local?: LocalFontFace[];
|
|
/** Default `font-display` for faces that don't set their own. Default: "swap". */
|
|
display?: FontDisplay;
|
|
/** Body / default family stack → `--wrn-font-sans` + `body { font-family }`. */
|
|
sans?: string;
|
|
/** Monospace family stack → `--wrn-font-mono`. */
|
|
mono?: string;
|
|
/** Serif family stack → `--wrn-font-serif`. */
|
|
serif?: string;
|
|
}
|
|
/**
|
|
* Render all `<head>` markup for a font config. Returns "" when nothing is
|
|
* configured. The output is trusted, framework-controlled HTML.
|
|
*/
|
|
declare function renderFontHead(fonts?: FontConfig): string;
|
|
/**
|
|
* Production variant that inlines the small Google Fonts stylesheet at build
|
|
* time. This removes a render-blocking CSS round trip while retaining the same
|
|
* font files, `font-display`, CSP sources, and offline-safe fallback markup.
|
|
*/
|
|
declare function renderProductionFontHead(fonts?: FontConfig, fetcher?: (input: string, init?: RequestInit) => Promise<Response>): Promise<string>;
|
|
/**
|
|
* CSP source hosts required by the configured fonts, so the policy can be
|
|
* auto-extended (Google Fonts need their CSS + static hosts allow-listed).
|
|
*/
|
|
declare function fontCspSources(fonts?: FontConfig): {
|
|
style: string[];
|
|
font: string[];
|
|
};
|
|
|
|
declare const CURRENT_COMPATIBILITY_DATE = "2026-08-02";
|
|
declare const CURRENT_FRAMEWORK_BEHAVIOUR = 1;
|
|
interface CompatibilityPolicy {
|
|
compatibilityDate?: string;
|
|
frameworkBehaviour?: number;
|
|
}
|
|
interface CompatibilityReport {
|
|
configuredDate?: string;
|
|
effectiveDate: string;
|
|
currentDate: string;
|
|
configuredBehaviour?: number;
|
|
effectiveBehaviour: number;
|
|
currentBehaviour: number;
|
|
needsUpgrade: boolean;
|
|
future: boolean;
|
|
messages: string[];
|
|
}
|
|
declare function isCompatibilityDate(value: string): boolean;
|
|
declare function resolveCompatibility(policy: CompatibilityPolicy): CompatibilityReport;
|
|
|
|
/**
|
|
* App configuration loader (`wrnexus.config.ts`).
|
|
*
|
|
* The config is optional. It lets an app inject arbitrary `<head>` HTML (ideal
|
|
* for CDN-delivered CSS frameworks like Bootstrap or the Tailwind Play CDN) and
|
|
* customise the global stylesheet pipeline (entry file or a custom processor for
|
|
* Tailwind / PostCSS / Sass).
|
|
*/
|
|
|
|
type Mode = "development" | "production";
|
|
interface StyleProcessContext {
|
|
/** Resolved absolute path to the CSS entry, or null if there is none. */
|
|
entryPath: string | null;
|
|
/** Original application entry when a package-aware wrapper was generated. */
|
|
originalEntryPath?: string | null;
|
|
/** Package component/style directories that processors should scan. */
|
|
sources?: string[];
|
|
/** Package-owned CSS entries automatically imported into the application bundle. */
|
|
entries?: string[];
|
|
appDir: string;
|
|
appRoot: string;
|
|
mode: Mode;
|
|
}
|
|
interface StylesConfig {
|
|
/** Path to the CSS entry, relative to the app root. Default: app/styles/global.css */
|
|
entry?: string;
|
|
/**
|
|
* Optional custom processor. Return the final CSS string. Use this to run
|
|
* Tailwind, PostCSS, Sass, etc. When omitted, the built-in Bun CSS bundler is
|
|
* used (which already resolves `@import`, including from node_modules).
|
|
*/
|
|
process?: (ctx: StyleProcessContext) => string | Promise<string>;
|
|
/** Automatically append package scan sources to custom processor input. Default true. */
|
|
includePackageSources?: boolean;
|
|
/** Production defaults to throw; development defaults to best-effort fallback. */
|
|
failureMode?: "throw" | "fallback";
|
|
}
|
|
interface MobileConfig {
|
|
enabled?: boolean;
|
|
/** Mobile renderer. `webview` uses Capacitor; `native` scaffolds an Expo/React Native app. */
|
|
mode?: "webview" | "native";
|
|
appId?: string;
|
|
appName?: string;
|
|
serverUrl?: string;
|
|
userAgent?: string;
|
|
layout?: string;
|
|
backgroundColor?: string;
|
|
icon?: string;
|
|
errorTitle?: string;
|
|
errorMessage?: string;
|
|
/** Base URL used by a fully native client for WRNexusJS API and realtime requests. */
|
|
apiUrl?: string;
|
|
/** URL scheme used for native deep links (defaults to a slug of appName). */
|
|
scheme?: string;
|
|
/** Advanced Expo app config fields merged into generated app.config.ts. */
|
|
expo?: Record<string, unknown>;
|
|
/** Advanced CapacitorConfig fields merged into generated capacitor.config.ts. */
|
|
capacitor?: Record<string, unknown>;
|
|
}
|
|
interface PwaScreenshot {
|
|
src: string;
|
|
sizes: string;
|
|
type?: string;
|
|
formFactor?: "wide" | "narrow";
|
|
label?: string;
|
|
}
|
|
interface PwaShortcut {
|
|
name: string;
|
|
shortName?: string;
|
|
description?: string;
|
|
url: string;
|
|
icons?: Array<{
|
|
src: string;
|
|
sizes: string;
|
|
type?: string;
|
|
purpose?: string;
|
|
}>;
|
|
}
|
|
interface PwaConfig {
|
|
enabled?: boolean;
|
|
id?: string;
|
|
name?: string;
|
|
shortName?: string;
|
|
description?: string;
|
|
startUrl?: string;
|
|
scope?: string;
|
|
lang?: string;
|
|
display?: "standalone" | "fullscreen" | "minimal-ui" | "browser";
|
|
orientation?: "any" | "natural" | "landscape" | "landscape-primary" | "landscape-secondary" | "portrait" | "portrait-primary" | "portrait-secondary";
|
|
themeColor?: string;
|
|
backgroundColor?: string;
|
|
icons?: Array<{
|
|
src: string;
|
|
sizes: string;
|
|
type?: string;
|
|
purpose?: string;
|
|
}>;
|
|
categories?: string[];
|
|
screenshots?: PwaScreenshot[];
|
|
shortcuts?: PwaShortcut[];
|
|
/** Disable service-worker registration while keeping the web manifest. */
|
|
serviceWorker?: boolean;
|
|
/** Navigation shown when both the network and requested page cache are unavailable. */
|
|
offlineUrl?: string;
|
|
/** Additional same-origin URLs precached during service-worker installation. */
|
|
cacheUrls?: string[];
|
|
/** Service-worker cache key. Change it to invalidate existing PWA caches. */
|
|
cacheName?: string;
|
|
/** Ordered URL rules for runtime caching. Patterns are regular-expression source strings. */
|
|
runtimeCaching?: Array<{
|
|
pattern: string;
|
|
strategy: "network-first" | "cache-first" | "stale-while-revalidate";
|
|
cacheName?: string;
|
|
methods?: string[];
|
|
}>;
|
|
/** Background Sync tag used by the offline mutation queue. */
|
|
backgroundSyncTag?: string;
|
|
}
|
|
type DevToolbarPosition = "bottom-center" | "bottom-left" | "bottom-right";
|
|
interface DevToolbarConfig {
|
|
enabled?: boolean;
|
|
position?: DevToolbarPosition;
|
|
defaultOpen?: boolean;
|
|
keyboardShortcut?: string;
|
|
scanOnNavigation?: boolean;
|
|
scanOnHmr?: boolean;
|
|
openEditor?: boolean;
|
|
editor?: string;
|
|
rules?: Partial<Record<string, boolean>>;
|
|
severity?: Partial<Record<string, "error" | "warning" | "info" | "suggestion">>;
|
|
ignoredRules?: string[];
|
|
ignoredPaths?: string[];
|
|
slowRequestMs?: number;
|
|
largeImageBytes?: number;
|
|
veryLargeImageBytes?: number;
|
|
}
|
|
interface ExperimentalConfig {
|
|
serverComponents?: boolean;
|
|
streaming?: boolean;
|
|
partialHydration?: boolean;
|
|
typedRpc?: boolean;
|
|
pluginTransforms?: boolean;
|
|
[feature: string]: boolean | undefined;
|
|
}
|
|
interface PerformanceConfig {
|
|
budgets?: PerformanceBudgets;
|
|
/** `warn` reports budget violations; `error` fails production builds. */
|
|
enforcement?: "off" | "warn" | "error";
|
|
analyze?: boolean;
|
|
}
|
|
interface ObservabilityConfig {
|
|
enabled?: boolean;
|
|
serviceName?: string;
|
|
serverTiming?: boolean;
|
|
sampleRate?: number;
|
|
exporter?: "console" | "otlp" | "none";
|
|
endpoint?: string;
|
|
/** Collect privacy-preserving Core Web Vitals from real browsers. */
|
|
webVitals?: boolean;
|
|
/** Same-origin endpoint receiving Web Vitals. */
|
|
webVitalsEndpoint?: string;
|
|
}
|
|
interface TenancyConfig {
|
|
mode?: "subdomain" | "domain" | "path" | "custom";
|
|
required?: boolean;
|
|
rootDomains?: string[];
|
|
pathPrefix?: string;
|
|
}
|
|
interface BuildConfig {
|
|
cache?: boolean;
|
|
cacheDir?: string;
|
|
sourceMaps?: boolean;
|
|
report?: boolean;
|
|
adapter?: "bun" | "node" | "static" | "serverless" | "edge" | string;
|
|
}
|
|
interface NavigationConfig {
|
|
/**
|
|
* `auto` (default) omits navigation JavaScript from fully static pages and
|
|
* progressively enhances routes that already need browser behavior.
|
|
* `client` always enhances same-origin links with in-place page swaps.
|
|
* `document` keeps normal browser navigation so every route performs a fresh
|
|
* server-rendered document request.
|
|
*/
|
|
mode?: "auto" | "client" | "document";
|
|
}
|
|
interface ImportsConfig {
|
|
mode?: "legacy" | "compatible" | "explicit";
|
|
autoImport?: boolean;
|
|
aliases?: Record<string, string>;
|
|
}
|
|
interface TypesConfig {
|
|
strict?: boolean;
|
|
noImplicitAny?: boolean;
|
|
strictNullChecks?: boolean;
|
|
checkTemplates?: boolean;
|
|
checkComponentProps?: boolean;
|
|
generateDeclarations?: boolean;
|
|
globalTypes?: string;
|
|
}
|
|
interface FunctionsConfig {
|
|
legacyDefaultRuntime?: "current" | "client" | "server" | "shared";
|
|
}
|
|
interface StoresConfig {
|
|
strictMutations?: boolean;
|
|
persistence?: boolean;
|
|
}
|
|
interface CompatibilityConfig {
|
|
legacyEmit?: boolean;
|
|
legacyEventProps?: boolean;
|
|
legacyComponentDiscovery?: boolean;
|
|
stringLayouts?: boolean;
|
|
}
|
|
interface AppConfig extends CompatibilityPolicy {
|
|
/** Ordered reusable configuration layers; the application always has final precedence. */
|
|
extends?: string | string[];
|
|
/** Compiler/dev/build plugins, resolved in deterministic pre/normal/post order. */
|
|
plugins?: PluginInput;
|
|
/** Optional least-privilege enforcement for automatically discovered packages. */
|
|
pluginPermissions?: {
|
|
enforce?: boolean;
|
|
grants?: Record<string, PluginPermission[]>;
|
|
};
|
|
/** WRN v0.6 explicit import and compatibility resolution. */
|
|
imports?: ImportsConfig;
|
|
/** TypeScript-backed .wrn type checking and declaration generation. */
|
|
types?: TypesConfig;
|
|
/** Legacy function runtime behavior for existing applications. */
|
|
functions?: FunctionsConfig;
|
|
/** Typed global/page store behavior. */
|
|
stores?: StoresConfig;
|
|
/** Temporary v0.5 syntax compatibility switches. */
|
|
compatibility?: CompatibilityConfig;
|
|
/** Opt-in APIs that are not yet covered by stable compatibility guarantees. */
|
|
experimental?: ExperimentalConfig;
|
|
/** Route and asset budgets plus build analyzer behavior. */
|
|
performance?: PerformanceConfig;
|
|
/** Request tracing, Server-Timing, and exporter configuration. */
|
|
observability?: ObservabilityConfig;
|
|
/** First-class tenant resolution defaults. */
|
|
tenancy?: TenancyConfig;
|
|
/** Build cache, source map, report, and deployment adapter settings. */
|
|
build?: BuildConfig;
|
|
/** Page navigation strategy. Defaults to progressive client navigation. */
|
|
navigation?: NavigationConfig;
|
|
/** Development-only page diagnostics toolbar. Enabled by default in development. */
|
|
devToolbar?: boolean | DevToolbarConfig;
|
|
/** Raw HTML appended to every page's `<head>` (e.g. CDN stylesheet links). */
|
|
head?: string | string[];
|
|
/** Global SEO defaults merged with every page's exported `meta`. */
|
|
seo?: SeoConfig;
|
|
/** Framework security headers and optional CORS policy. */
|
|
security?: SecurityConfig;
|
|
styles?: StylesConfig;
|
|
/** Capacitor/native shell defaults and mobile-only page rendering. */
|
|
mobile?: MobileConfig;
|
|
/** Progressive Web App metadata. Enabled by default unless set to false. */
|
|
pwa?: PwaConfig | false;
|
|
/**
|
|
* Fonts. Declare Google Fonts (subsetted + preconnect + `font-display`) and/or
|
|
* self-hosted `@font-face` (with preload), and set `sans`/`mono`/`serif` family
|
|
* stacks. Google Fonts auto-extend the CSP so they load under the default policy.
|
|
*/
|
|
fonts?: FontConfig;
|
|
/** Design-token themes (deep-merged over the built-in light/dark). */
|
|
theme?: ThemeConfig;
|
|
/** i18n: default language + supported locales (strings live in app/locales/*.json). */
|
|
i18n?: {
|
|
default?: string;
|
|
locales?: string[];
|
|
labels?: Record<string, string>;
|
|
fallbacks?: Record<string, string[]>;
|
|
direction?: Record<string, "ltr" | "rtl">;
|
|
cookie?: {
|
|
name?: string;
|
|
maxAge?: number;
|
|
path?: string;
|
|
sameSite?: "Strict" | "Lax" | "None";
|
|
secure?: boolean;
|
|
};
|
|
strict?: boolean;
|
|
};
|
|
/** Default database connection (driver + url); reached with `getDb()`. */
|
|
db?: {
|
|
driver: "sqlite" | "postgres" | "mysql" | "mongo";
|
|
url: string;
|
|
};
|
|
/**
|
|
* File-upload storage. Declare named stores (local dir or S3-compatible),
|
|
* upload with `handleUpload`/`upload` from `@wrnexus/uploader`, and serve
|
|
* files back. Each store is `access: "public" | "private"`.
|
|
*/
|
|
storage?: StorageConfig;
|
|
/**
|
|
* Additional named databases, reached with `getDb("<name>")`. Each has its own
|
|
* migrations/queries under `app/db/<name>/`. Connect to as many as you like and
|
|
* read/write to any of them per request.
|
|
*
|
|
* databases: { analytics: { driver: "postgres", url: "…" } }
|
|
*/
|
|
databases?: Record<string, {
|
|
driver: "sqlite" | "postgres" | "mysql" | "mongo";
|
|
url: string;
|
|
}>;
|
|
/**
|
|
* Realtime scaling. When `scale` is true (or `redisUrl` is set), room
|
|
* broadcasts are bridged over Redis pub/sub so they reach clients on **every**
|
|
* app process/instance — realtime that works with multiple running apps.
|
|
*/
|
|
realtime?: {
|
|
scale?: boolean;
|
|
redisUrl?: string;
|
|
};
|
|
/** Default server port. */
|
|
port?: number;
|
|
/**
|
|
* Named config profiles (dev, prod, uat, test, …). When a profile is active
|
|
* its overrides are DEEP-MERGED over the base config. Select with
|
|
* `--profile=<name>` or the `WRNEXUS_PROFILE` env var.
|
|
*/
|
|
profiles?: Record<string, Partial<Omit<AppConfig, "profiles">>>;
|
|
}
|
|
/**
|
|
* Resolve the active profile name: explicit argument > `WRNEXUS_PROFILE` env var
|
|
* > a mode-based default ("production" in prod, else "development").
|
|
*/
|
|
declare function resolveProfile(options?: {
|
|
explicit?: string;
|
|
mode?: Mode;
|
|
}): string;
|
|
interface ResolvedConfigLayers {
|
|
config: AppConfig;
|
|
sources: string[];
|
|
}
|
|
declare function resolveConfigLayers(appRoot: string, application: AppConfig): Promise<ResolvedConfigLayers>;
|
|
/** Load the raw `wrnexus.config.*` (with the `profiles` map intact), or `{}`. */
|
|
declare function loadRawConfig(appRoot: string): Promise<AppConfig>;
|
|
/** Load `wrnexus.config.*`, applying the active profile's overrides. */
|
|
declare function loadAppConfig(appRoot: string, profile?: string): Promise<AppConfig>;
|
|
/**
|
|
* Load the `.env` cascade for a profile into `process.env`, WITHOUT clobbering
|
|
* variables already set in the real environment (which always win). Order, low
|
|
* → high precedence: `.env` < `.env.<profile>` < `.env.local` < `.env.<profile>.local`.
|
|
* Returns the variables it loaded.
|
|
*/
|
|
declare function loadEnv(appRoot: string, profile: string): Record<string, string>;
|
|
interface ConfigIssue {
|
|
path: string;
|
|
severity: "error" | "warning";
|
|
message: string;
|
|
}
|
|
declare function defineConfig(config: AppConfig): AppConfig;
|
|
declare function validateAppConfig(config: AppConfig): ConfigIssue[];
|
|
interface ExplainedConfig {
|
|
profile: string;
|
|
config: AppConfig;
|
|
issues: ConfigIssue[];
|
|
sources: string[];
|
|
}
|
|
declare function explainAppConfig(appRoot: string, profile?: string): Promise<ExplainedConfig>;
|
|
/** Flatten a head config into a single HTML string. */
|
|
declare function headToString(head?: string | string[]): string;
|
|
|
|
/**
|
|
* Global stylesheet pipeline.
|
|
*
|
|
* Convention: `app/styles/global.css` is the entry. If it is absent but other
|
|
* `app/styles/*.css` files exist, they are aggregated into one entry. The entry
|
|
* is bundled by Bun's CSS bundler, which resolves `@import` — including from
|
|
* node_modules — so any npm CSS framework (Bootstrap, etc.) works by importing
|
|
* it. A custom `process` hook can replace the bundler for Tailwind/PostCSS/Sass.
|
|
*/
|
|
|
|
/**
|
|
* Resolve the CSS entry for an app.
|
|
* - `override` (from config.styles.entry) is resolved relative to `appRoot`.
|
|
* - otherwise prefer `app/styles/global.css`.
|
|
* - otherwise aggregate all `app/styles/*.css` into a generated entry.
|
|
* Returns null when the app has no styles.
|
|
*/
|
|
declare function findStyleEntry(appDir: string, appRoot: string, override?: string): string | null;
|
|
/**
|
|
* Bundle a CSS entry into a single stylesheet string using Bun's CSS bundler.
|
|
* Resolves `@import` (local and node_modules), handles nesting, minifies in prod.
|
|
*/
|
|
declare function bundleCss(entryPath: string, mode: Mode): Promise<string>;
|
|
|
|
interface CssTokenAudit {
|
|
declared: string[];
|
|
used: string[];
|
|
missing: string[];
|
|
unused: string[];
|
|
}
|
|
/** Audit framework design-token declarations and var() references. */
|
|
declare function auditWireTokens(css: string): CssTokenAudit;
|
|
interface StyleSource {
|
|
path: string;
|
|
reason?: string;
|
|
}
|
|
/** Normalize/dedupe Tailwind scan sources without allowing line injection. */
|
|
declare function normalizeStyleSources(values: readonly (string | StyleSource)[]): StyleSource[];
|
|
declare function tailwindSourceDirectives(values: readonly (string | StyleSource)[]): string;
|
|
interface ContrastResult {
|
|
ratio: number;
|
|
level: "fail" | "aa-large" | "aa" | "aaa";
|
|
}
|
|
declare function contrast(foreground: string, background: string): ContrastResult | null;
|
|
interface CssPerformanceAuditIssue {
|
|
code: string;
|
|
severity: "error" | "warning" | "info";
|
|
message: string;
|
|
line?: number;
|
|
}
|
|
/** Detect CSS patterns that commonly increase style, paint, or compositing cost. */
|
|
declare function auditCssPerformance(source: string): CssPerformanceAuditIssue[];
|
|
|
|
/**
|
|
* @wrnexus/styles — global stylesheet pipeline + app config.
|
|
*
|
|
* Works for SSR and CSR: the bundled stylesheet is `<link>`ed into every page's
|
|
* `<head>`, so it styles server-rendered markup and hydrated client islands
|
|
* alike. Use any CSS framework via `@import` in global.css (npm) or via a CDN
|
|
* link in `wrnexus.config.ts`'s `head` field.
|
|
*/
|
|
|
|
/**
|
|
* Produce the final CSS for an entry: run the config's custom processor if one
|
|
* is provided (Tailwind/PostCSS/Sass), otherwise use the built-in Bun bundler.
|
|
*
|
|
* Development can fall back to best-effort CSS. Production throws by default so
|
|
* a deployment cannot silently ship unprocessed Tailwind/PostCSS directives.
|
|
*/
|
|
declare function renderStyles(ctx: StyleProcessContext, styles?: StylesConfig): Promise<string>;
|
|
|
|
export { ACCENT_COOKIE, type AppConfig, type BuildConfig, CURRENT_COMPATIBILITY_DATE, CURRENT_FRAMEWORK_BEHAVIOUR, type CompatibilityPolicy, type CompatibilityReport, type ConfigIssue, type ContrastResult, type CssPerformanceAuditIssue, type CssTokenAudit, type CustomThemePalette, DEFAULT_THEMES, type DevToolbarConfig, type ExperimentalConfig, type ExplainedConfig, type FontConfig, type FontDisplay, type GoogleFont, type LocalFontFace, type MobileConfig, type Mode, type NavigationConfig, type ObservabilityConfig, type PerformanceConfig, type PwaConfig, type ResolvedConfigLayers, type ResolvedTheme, type StyleProcessContext, type StyleSource, type StylesConfig, type Mode as StylesMode, THEME_COOKIE, THEME_CSS_HREF, THEME_JS_HREF, THEME_PALETTES, THEME_PALETTE_NAMES, type TenancyConfig, type ThemeAccentConfig, type ThemeConfig, type ThemePaletteName, type ThemeSemanticColor, type ThemeToken, type ThemeTokens, auditCssPerformance, auditWireTokens, bundleCss, contrast, defineConfig, defineThemeTokens, explainAppConfig, findStyleEntry, fontCspSources, headToString, isCompatibilityDate, loadAppConfig, loadEnv, loadRawConfig, normalizeStyleSources, renderFontHead, renderProductionFontHead, renderStyles, renderThemeCss, renderThemeRuntime, resolveAccentName, resolveCompatibility, resolveConfigLayers, resolveProfile, resolveThemeConfig, resolveThemeName, tailwindSourceDirectives, themeVar, validateAppConfig };
|
|
</code></pre></section><section id="examples" class="examples"><h2>Examples</h2><p>Copy-ready examples from the installed package documentation.</p><div class="example-grid"><article class="example-card"><h3>wrnexus.config.ts</h3><pre data-language="ts"><code>import type { AppConfig } from "@wrnexus/styles";
|
|
|
|
export default {
|
|
head: [
|
|
'<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5/dist/css/bootstrap.min.css">',
|
|
],
|
|
port: 3000,
|
|
db: { driver: "sqlite", url: "app.db" },
|
|
theme: {
|
|
palette: "violet",
|
|
default: "dark",
|
|
themes: {
|
|
light: { "color-primary": "#7c3aed" }, // override one token; rest inherited
|
|
brand: {
|
|
// add a whole new theme
|
|
"color-scheme": "dark",
|
|
"color-bg": "#0a0a0a",
|
|
"color-primary": "#22d3ee",
|
|
},
|
|
},
|
|
},
|
|
styles: {
|
|
entry: "app/styles/main.css",
|
|
},
|
|
profiles: {
|
|
production: {
|
|
db: { driver: "postgres", url: process.env.DATABASE_URL! },
|
|
},
|
|
},
|
|
} satisfies AppConfig;</code></pre></article><article class="example-card"><h3>Loading config + producing CSS</h3><pre data-language="ts"><code>import {
|
|
loadAppConfig,
|
|
resolveProfile,
|
|
loadEnv,
|
|
findStyleEntry,
|
|
renderStyles,
|
|
} from "@wrnexus/styles";
|
|
|
|
const appRoot = process.cwd();
|
|
const mode = "production" as const;
|
|
|
|
const profile = resolveProfile({ mode });
|
|
loadEnv(appRoot, profile);
|
|
|
|
const config = await loadAppConfig(appRoot, profile);
|
|
|
|
const appDir = `${appRoot}/app`;
|
|
const entryPath = findStyleEntry(appDir, appRoot, config.styles?.entry);
|
|
const css = await renderStyles({ entryPath, appDir, appRoot, mode }, config.styles);</code></pre></article><article class="example-card"><h3>Rendering the theme</h3><pre data-language="ts"><code>import {
|
|
resolveThemeConfig,
|
|
resolveThemeName,
|
|
renderThemeCss,
|
|
renderThemeRuntime,
|
|
THEME_COOKIE,
|
|
} from "@wrnexus/styles";
|
|
|
|
const theme = resolveThemeConfig(config.theme);
|
|
|
|
// Server: pick the active theme from the request cookie (no flash).
|
|
const active = resolveThemeName(cookies[THEME_COOKIE], theme);
|
|
// → render <html data-theme={active}>
|
|
|
|
const themeCss = renderThemeCss(theme); // served at THEME_CSS_HREF
|
|
const themeJs = renderThemeRuntime(theme); // served at THEME_JS_HREF</code></pre></article><article class="example-card"><h3>Rendering the theme</h3><pre data-language="css"><code>.card {
|
|
background: var(--wire-color-surface);
|
|
color: var(--wire-color-text);
|
|
border: 1px solid var(--wire-color-border);
|
|
border-radius: var(--wire-radius);
|
|
box-shadow: var(--wire-shadow-1);
|
|
}</code></pre></article><article class="example-card"><h3>Rendering the theme</h3><pre data-language="html"><code><button data-wire-theme-toggle>Toggle theme</button>
|
|
<button data-wire-theme-set="brand">Brand theme</button></code></pre></article></div></section></article>
|
|
<aside class="on-this-page"><h2>On this page</h2><nav><a class="toc-level-2" href="#guide">Guide</a><a class="toc-level-3" href="#reusable-layers-and-presets">Reusable layers and presets</a><a class="toc-level-3" href="#overview">Overview</a><a class="toc-level-3" href="#api">API</a><a class="toc-level-4" href="#config-loading">Config loading</a><a class="toc-level-4" href="#appconfig">AppConfig</a><a class="toc-level-4" href="#styles-pipeline">Styles pipeline</a><a class="toc-level-4" href="#theme-system">Theme system</a><a class="toc-level-3" href="#usage">Usage</a><a class="toc-level-4" href="#wrnexus-config-ts">wrnexus.config.ts</a><a class="toc-level-4" href="#loading-config-producing-css">Loading config + producing CSS</a><a class="toc-level-4" href="#rendering-the-theme">Rendering the theme</a><a class="toc-level-3" href="#requirements-notes">Requirements / Notes</a><a class="toc-level-2" href="#api">Complete API</a><a class="toc-level-2" href="#examples">Examples</a></nav></aside>
|
|
</main>
|
|
<footer><div class="footer-brand"><span class="footer-mark" aria-hidden="true">W</span><p><strong>WRNexusJS 0.8.0</strong><span>Complete API documentation generated from installed package declarations.</span></p></div><nav aria-label="Footer"><a href="/packages">All packages</a><a href="/getting-started">Get started</a><a href="/security">Security</a><a href="/support">Support</a><a href="/llms.txt">AI guide</a></nav><p class="footer-meta">Private Developer Preview · Bun-native</p></footer>
|
|
</div>
|
|
}
|
|
}
|