page wrnexusstyles {
  seo {
    title = "@wrnexus/styles"
    description = "CSS pipeline, themes, fonts, profiles, and application config."
  }

  view {
    <div class="docs-shell">
      <SkipLink label="Skip to content" href="#main" class="docs-skip-link" />
      <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="/components">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.3.2</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="/components">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.3.2</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.3.2</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"><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>&lt;link&gt;</code>ed into every page's <code>&lt;head&gt;</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-&lt;key&gt;</code>), with built-in <code>light</code>/<code>dark</code> sets, deep-merged user overrides, an SSR <code>&lt;html data-theme&gt;</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) =&gt; Promise&lt;AppConfig&gt;</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) =&gt; Promise&lt;AppConfig&gt;</code></td><td>Load the raw config with the <code>profiles</code> map intact; returns <code>&#123;&#125;</code> if no config file exists.</td></tr><tr><td><code>resolveProfile</code></td><td><code>(options?: &#123; explicit?; mode? &#125;) =&gt; string</code></td><td>Resolve the active profile: explicit arg &gt; <code>WRNEXUS_PROFILE</code> env var &gt; 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) =&gt; Record&lt;string, string&gt;</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[]) =&gt; 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> &lt; <code>.env.&lt;profile&gt;</code> &lt; <code>.env.local</code> &lt; <code>.env.&lt;profile&gt;.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>&lt;head&gt;</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>&#123; default?: string; locales?: string[] &#125;</code></td><td>Default language + supported locales (strings live in <code>app/locales/*.json</code>).</td></tr><tr><td><code>db</code></td><td>`&#123; driver: &quot;sqlite&quot; \</td><td>&quot;postgres&quot; \</td><td>&quot;mysql&quot; \</td><td>&quot;mongo&quot;; url: string &#125;`</td><td>Default database connection; reached with <code>getDb()</code>.</td></tr><tr><td><code>databases</code></td><td><code>Record&lt;string, &#123; driver; url &#125;&gt;</code></td><td>Additional named databases, reached with <code>getDb(&quot;&lt;name&gt;&quot;)</code>; each has its own <code>app/db/&lt;name&gt;/</code> migrations/queries.</td></tr><tr><td><code>realtime</code></td><td><code>&#123; scale?: boolean; redisUrl?: string &#125;</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&lt;string, Partial&lt;Omit&lt;AppConfig, &quot;profiles&quot;&gt;&gt;&gt;</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=&lt;name&gt;</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?) =&gt; 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) =&gt; Promise&lt;string&gt;</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 === &quot;production&quot;</code>.</td></tr><tr><td><code>renderStyles</code></td><td><code>(ctx: StyleProcessContext, styles?: StylesConfig) =&gt; Promise&lt;string&gt;</code></td><td>Produce final CSS: runs <code>styles.process(ctx)</code> if provided, else <code>bundleCss</code>. Returns <code>&quot;&quot;</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 &#123;
  /** 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) =&gt; string | Promise&lt;string&gt;;
&#125;

interface StyleProcessContext &#123;
  entryPath: string | null; // resolved absolute CSS entry, or null
  appDir: string;
  appRoot: string;
  mode: Mode; // &quot;development&quot; | &quot;production&quot;
&#125;</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&lt;string, ThemeTokens&gt;</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>&quot;wire-theme&quot;</code></td><td>Cookie the resolved theme is read from / persisted to.</td></tr><tr><td><code>THEME_CSS_HREF</code></td><td><code>&quot;/__wrnexus/theme.css&quot;</code></td><td>URL the generated theme stylesheet is served at.</td></tr><tr><td><code>THEME_JS_HREF</code></td><td><code>&quot;/__wrnexus/theme.js&quot;</code></td><td>URL the client theme runtime is served at.</td></tr><tr><td><code>resolveThemeConfig</code></td><td><code>(config?: ThemeConfig) =&gt; 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) =&gt; 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) =&gt; string</code></td><td>Generate the theme stylesheet: a <code>:root&#123;…&#125;</code> default plus one <code>[data-theme=&quot;&lt;name&gt;&quot;]&#123;…&#125;</code> block per theme.</td></tr><tr><td><code>renderThemeRuntime</code></td><td><code>(theme: ResolvedTheme) =&gt; string</code></td><td>Generate the client runtime (see below).</td></tr></tbody></table></div>
<p>Tokens are emitted as <code>--wire-&lt;key&gt;</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&lt;string, string&gt;;

interface ThemeConfig &#123;
  palette?: ThemePaletteName | CustomThemePalette;
  default?: string; // theme used when no cookie is present
  themes?: Record&lt;string, ThemeTokens&gt;; // deep-merged over built-in light/dark
&#125;

interface ResolvedTheme &#123;
  default: string;
  names: string[];
  themes: Record&lt;string, ThemeTokens&gt;;
&#125;</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: &#123;
  palette: &#123;
    primary: &quot;#7c3aed&quot;,
    primaryHover: &quot;#6d28d9&quot;,
    primaryContrast: &quot;#ffffff&quot;,
    secondary: &quot;#db2777&quot;,
    secondaryHover: &quot;#be185d&quot;,
    secondaryContrast: &quot;#ffffff&quot;,
    info: &quot;#2563eb&quot;,
    success: &quot;#059669&quot;,
    warning: &quot;#d97706&quot;,
    danger: &quot;#dc2626&quot;,
  &#125;,
&#125;</code></pre>
<p>The client runtime (<code>renderThemeRuntime</code>) exposes <code>window.wireTheme</code> with <code>&#123; get, set, toggle, bind, themes &#125;</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 &#123; AppConfig &#125; from &quot;@wrnexus/styles&quot;;

export default &#123;
  head: [
    '&lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdn.jsdelivr.net/npm/bootstrap@5/dist/css/bootstrap.min.css&quot;&gt;',
  ],
  port: 3000,
  db: &#123; driver: &quot;sqlite&quot;, url: &quot;app.db&quot; &#125;,
  theme: &#123;
    palette: &quot;violet&quot;,
    default: &quot;dark&quot;,
    themes: &#123;
      light: &#123; &quot;color-primary&quot;: &quot;#7c3aed&quot; &#125;, // override one token; rest inherited
      brand: &#123;
        // add a whole new theme
        &quot;color-scheme&quot;: &quot;dark&quot;,
        &quot;color-bg&quot;: &quot;#0a0a0a&quot;,
        &quot;color-primary&quot;: &quot;#22d3ee&quot;,
      &#125;,
    &#125;,
  &#125;,
  styles: &#123;
    entry: &quot;app/styles/main.css&quot;,
  &#125;,
  profiles: &#123;
    production: &#123;
      db: &#123; driver: &quot;postgres&quot;, url: process.env.DATABASE_URL! &#125;,
    &#125;,
  &#125;,
&#125; satisfies AppConfig;</code></pre>
<h4 id="loading-config-producing-css">Loading config + producing CSS</h4>
<pre data-language="ts"><code>import &#123;
  loadAppConfig,
  resolveProfile,
  loadEnv,
  findStyleEntry,
  renderStyles,
&#125; from &quot;@wrnexus/styles&quot;;

const appRoot = process.cwd();
const mode = &quot;production&quot; as const;

const profile = resolveProfile(&#123; mode &#125;);
loadEnv(appRoot, profile);

const config = await loadAppConfig(appRoot, profile);

const appDir = `$&#123;appRoot&#125;/app`;
const entryPath = findStyleEntry(appDir, appRoot, config.styles?.entry);
const css = await renderStyles(&#123; entryPath, appDir, appRoot, mode &#125;, config.styles);</code></pre>
<h4 id="rendering-the-theme">Rendering the theme</h4>
<pre data-language="ts"><code>import &#123;
  resolveThemeConfig,
  resolveThemeName,
  renderThemeCss,
  renderThemeRuntime,
  THEME_COOKIE,
&#125; from &quot;@wrnexus/styles&quot;;

const theme = resolveThemeConfig(config.theme);

// Server: pick the active theme from the request cookie (no flash).
const active = resolveThemeName(cookies[THEME_COOKIE], theme);
// → render &lt;html data-theme=&#123;active&#125;&gt;

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 &#123;
  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);
&#125;</code></pre>
<pre data-language="html"><code>&lt;button data-wire-theme-toggle&gt;Toggle theme&lt;/button&gt;
&lt;button data-wire-theme-set=&quot;brand&quot;&gt;Brand theme&lt;/button&gt;</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 &#123; PerformanceBudgets, SeoConfig, SecurityConfig &#125; from '@wrnexus/core';
import &#123; PluginInput &#125; from '@wrnexus/plugin';
import &#123; StorageConfig &#125; from '@wrnexus/uploader';

/**
 * Theme system — design tokens that work SSR and client-side.
 *
 * Tokens are plain CSS custom properties (`--wire-&lt;key&gt;`) 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 `&lt;html data-theme=&quot;…&quot;&gt;` from the `wire-theme` cookie (no
 * flash), and a tiny client runtime toggles/persists it. The reserved token key
 * `color-scheme` is emitted as the native CSS property (not a variable) so form
 * controls and scrollbars match the theme.
 */
type ThemeTokens = Record&lt;string, string&gt;;
declare const THEME_PALETTE_NAMES: readonly [&quot;blue&quot;, &quot;indigo&quot;, &quot;violet&quot;, &quot;emerald&quot;, &quot;cyan&quot;, &quot;rose&quot;, &quot;amber&quot;, &quot;slate&quot;];
type ThemePaletteName = (typeof THEME_PALETTE_NAMES)[number];
/** Required semantic colors for a custom application palette. */
interface CustomThemePalette &#123;
    primary: string;
    primaryHover: string;
    primaryContrast: string;
    secondary: string;
    secondaryHover: string;
    secondaryContrast: string;
    info: string;
    success: string;
    warning: string;
    danger: string;
&#125;
interface ThemeConfig &#123;
    /** 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. */
    themes?: Record&lt;string, ThemeTokens&gt;;
&#125;
interface ResolvedTheme &#123;
    default: string;
    names: string[];
    themes: Record&lt;string, ThemeTokens&gt;;
&#125;
/** Cookie the resolved theme is read from / persisted to. */
declare const THEME_COOKIE = &quot;wire-theme&quot;;
declare const THEME_CSS_HREF = &quot;/__wrnexus/theme.css&quot;;
declare const THEME_JS_HREF = &quot;/__wrnexus/theme.js&quot;;
declare const THEME_PALETTES: Record&lt;ThemePaletteName, CustomThemePalette&gt;;
/** Built-in themes so components have tokens out of the box. */
declare const DEFAULT_THEMES: Record&lt;string, ThemeTokens&gt;;
/** 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;
/** Generate the theme stylesheet: a `:root` default plus one block per theme. */
declare function renderThemeCss(theme: ResolvedTheme): string;
/**
 * Generate the client theme runtime. It exposes `window.wireTheme` and binds
 * `[data-wire-theme-toggle]` / `[data-wire-theme-set]` elements. The configured
 * theme names are baked in so `toggle()` cycles through them in order.
 */
declare function renderThemeRuntime(theme: ResolvedTheme): string;

/**
 * Font configuration.
 *
 * Declare fonts in `wrnexus.config.ts` under `fonts` and the framework emits
 * optimized `&lt;head&gt;` 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 `&lt;link rel=preload&gt;`
 *     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 = &quot;auto&quot; | &quot;block&quot; | &quot;swap&quot; | &quot;fallback&quot; | &quot;optional&quot;;
interface GoogleFont &#123;
    /** Family name as it appears on fonts.google.com, e.g. &quot;Inter&quot;. */
    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;
&#125;
interface LocalFontFace &#123;
    /** `font-family` name this face defines. */
    family: string;
    /** URL to the font file, typically served from `public/` (e.g. &quot;/fonts/inter.woff2&quot;). */
    src: string;
    /** e.g. 400, &quot;700&quot;, or &quot;100 900&quot; for a variable font. Default: 400. */
    weight?: number | string;
    style?: &quot;normal&quot; | &quot;italic&quot;;
    /** CSS `src` format; inferred from the file extension when omitted. */
    format?: string;
    display?: FontDisplay;
    /** Emit `&lt;link rel=&quot;preload&quot; as=&quot;font&quot;&gt;` — use for the primary above-the-fold face. */
    preload?: boolean;
    /** Optional `unicode-range` subset. */
    unicodeRange?: string;
&#125;
interface FontConfig &#123;
    /** 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: &quot;swap&quot;. */
    display?: FontDisplay;
    /** Body / default family stack → `--wrn-font-sans` + `body &#123; font-family &#125;`. */
    sans?: string;
    /** Monospace family stack → `--wrn-font-mono`. */
    mono?: string;
    /** Serif family stack → `--wrn-font-serif`. */
    serif?: string;
&#125;
/**
 * Render all `&lt;head&gt;` markup for a font config. Returns &quot;&quot; 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) =&gt; Promise&lt;Response&gt;): Promise&lt;string&gt;;
/**
 * 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): &#123;
    style: string[];
    font: string[];
&#125;;

/**
 * App configuration loader (`wrnexus.config.ts`).
 *
 * The config is optional. It lets an app inject arbitrary `&lt;head&gt;` 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 = &quot;development&quot; | &quot;production&quot;;
interface StyleProcessContext &#123;
    /** Resolved absolute path to the CSS entry, or null if there is none. */
    entryPath: string | null;
    appDir: string;
    appRoot: string;
    mode: Mode;
&#125;
interface StylesConfig &#123;
    /** 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) =&gt; string | Promise&lt;string&gt;;
&#125;
interface MobileConfig &#123;
    enabled?: boolean;
    /** Mobile renderer. `webview` uses Capacitor; `native` scaffolds an Expo/React Native app. */
    mode?: &quot;webview&quot; | &quot;native&quot;;
    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&lt;string, unknown&gt;;
    /** Advanced CapacitorConfig fields merged into generated capacitor.config.ts. */
    capacitor?: Record&lt;string, unknown&gt;;
&#125;
interface PwaScreenshot &#123;
    src: string;
    sizes: string;
    type?: string;
    formFactor?: &quot;wide&quot; | &quot;narrow&quot;;
    label?: string;
&#125;
interface PwaShortcut &#123;
    name: string;
    shortName?: string;
    description?: string;
    url: string;
    icons?: Array&lt;&#123;
        src: string;
        sizes: string;
        type?: string;
        purpose?: string;
    &#125;&gt;;
&#125;
interface PwaConfig &#123;
    enabled?: boolean;
    id?: string;
    name?: string;
    shortName?: string;
    description?: string;
    startUrl?: string;
    scope?: string;
    lang?: string;
    display?: &quot;standalone&quot; | &quot;fullscreen&quot; | &quot;minimal-ui&quot; | &quot;browser&quot;;
    orientation?: &quot;any&quot; | &quot;natural&quot; | &quot;landscape&quot; | &quot;landscape-primary&quot; | &quot;landscape-secondary&quot; | &quot;portrait&quot; | &quot;portrait-primary&quot; | &quot;portrait-secondary&quot;;
    themeColor?: string;
    backgroundColor?: string;
    icons?: Array&lt;&#123;
        src: string;
        sizes: string;
        type?: string;
        purpose?: string;
    &#125;&gt;;
    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;
&#125;
type DevToolbarPosition = &quot;bottom-center&quot; | &quot;bottom-left&quot; | &quot;bottom-right&quot;;
interface DevToolbarConfig &#123;
    enabled?: boolean;
    position?: DevToolbarPosition;
    defaultOpen?: boolean;
    keyboardShortcut?: string;
    scanOnNavigation?: boolean;
    scanOnHmr?: boolean;
    openEditor?: boolean;
    editor?: string;
    rules?: Partial&lt;Record&lt;string, boolean&gt;&gt;;
    severity?: Partial&lt;Record&lt;string, &quot;error&quot; | &quot;warning&quot; | &quot;info&quot; | &quot;suggestion&quot;&gt;&gt;;
    ignoredRules?: string[];
    ignoredPaths?: string[];
    slowRequestMs?: number;
    largeImageBytes?: number;
    veryLargeImageBytes?: number;
&#125;
interface ExperimentalConfig &#123;
    serverComponents?: boolean;
    streaming?: boolean;
    partialHydration?: boolean;
    typedRpc?: boolean;
    pluginTransforms?: boolean;
    [feature: string]: boolean | undefined;
&#125;
interface PerformanceConfig &#123;
    budgets?: PerformanceBudgets;
    /** `warn` reports budget violations; `error` fails production builds. */
    enforcement?: &quot;off&quot; | &quot;warn&quot; | &quot;error&quot;;
    analyze?: boolean;
&#125;
interface ObservabilityConfig &#123;
    enabled?: boolean;
    serviceName?: string;
    serverTiming?: boolean;
    sampleRate?: number;
    exporter?: &quot;console&quot; | &quot;otlp&quot; | &quot;none&quot;;
    endpoint?: string;
&#125;
interface TenancyConfig &#123;
    mode?: &quot;subdomain&quot; | &quot;domain&quot; | &quot;path&quot; | &quot;custom&quot;;
    required?: boolean;
    rootDomains?: string[];
    pathPrefix?: string;
&#125;
interface BuildConfig &#123;
    cache?: boolean;
    cacheDir?: string;
    sourceMaps?: boolean;
    report?: boolean;
    adapter?: &quot;bun&quot; | &quot;node&quot; | &quot;static&quot; | &quot;serverless&quot; | &quot;edge&quot; | string;
&#125;
interface AppConfig &#123;
    /** Compiler/dev/build plugins, resolved in deterministic pre/normal/post order. */
    plugins?: PluginInput;
    /** 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;
    /** Development-only page diagnostics toolbar. Enabled by default in development. */
    devToolbar?: boolean | DevToolbarConfig;
    /** Raw HTML appended to every page's `&lt;head&gt;` (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?: &#123;
        default?: string;
        locales?: string[];
    &#125;;
    /** Default database connection (driver + url); reached with `getDb()`. */
    db?: &#123;
        driver: &quot;sqlite&quot; | &quot;postgres&quot; | &quot;mysql&quot; | &quot;mongo&quot;;
        url: string;
    &#125;;
    /**
     * 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: &quot;public&quot; | &quot;private&quot;`.
     */
    storage?: StorageConfig;
    /**
     * Additional named databases, reached with `getDb(&quot;&lt;name&gt;&quot;)`. Each has its own
     * migrations/queries under `app/db/&lt;name&gt;/`. Connect to as many as you like and
     * read/write to any of them per request.
     *
     *   databases: &#123; analytics: &#123; driver: &quot;postgres&quot;, url: &quot;…&quot; &#125; &#125;
     */
    databases?: Record&lt;string, &#123;
        driver: &quot;sqlite&quot; | &quot;postgres&quot; | &quot;mysql&quot; | &quot;mongo&quot;;
        url: string;
    &#125;&gt;;
    /**
     * 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?: &#123;
        scale?: boolean;
        redisUrl?: string;
    &#125;;
    /** 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=&lt;name&gt;` or the `WRNEXUS_PROFILE` env var.
     */
    profiles?: Record&lt;string, Partial&lt;Omit&lt;AppConfig, &quot;profiles&quot;&gt;&gt;&gt;;
&#125;
/**
 * Resolve the active profile name: explicit argument &gt; `WRNEXUS_PROFILE` env var
 * &gt; a mode-based default (&quot;production&quot; in prod, else &quot;development&quot;).
 */
declare function resolveProfile(options?: &#123;
    explicit?: string;
    mode?: Mode;
&#125;): string;
/** Load the raw `wrnexus.config.*` (with the `profiles` map intact), or `&#123;&#125;`. */
declare function loadRawConfig(appRoot: string): Promise&lt;AppConfig&gt;;
/** Load `wrnexus.config.*`, applying the active profile's overrides. */
declare function loadAppConfig(appRoot: string, profile?: string): Promise&lt;AppConfig&gt;;
/**
 * 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` &lt; `.env.&lt;profile&gt;` &lt; `.env.local` &lt; `.env.&lt;profile&gt;.local`.
 * Returns the variables it loaded.
 */
declare function loadEnv(appRoot: string, profile: string): Record&lt;string, string&gt;;
interface ConfigIssue &#123;
    path: string;
    severity: &quot;error&quot; | &quot;warning&quot;;
    message: string;
&#125;
declare function defineConfig(config: AppConfig): AppConfig;
declare function validateAppConfig(config: AppConfig): ConfigIssue[];
interface ExplainedConfig &#123;
    profile: string;
    config: AppConfig;
    issues: ConfigIssue[];
    sources: string[];
&#125;
declare function explainAppConfig(appRoot: string, profile?: string): Promise&lt;ExplainedConfig&gt;;
/** 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&lt;string&gt;;

/**
 * @wrnexus/styles — global stylesheet pipeline + app config.
 *
 * Works for SSR and CSR: the bundled stylesheet is `&lt;link&gt;`ed into every page's
 * `&lt;head&gt;`, 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.
 *
 * If a custom processor throws (e.g. Tailwind can't resolve `tailwindcss`
 * because deps aren't installed), we DON'T crash every request — we log a clear,
 * actionable message and fall back to best-effort CSS so the app keeps serving.
 */
declare function renderStyles(ctx: StyleProcessContext, styles?: StylesConfig): Promise&lt;string&gt;;

export &#123; type AppConfig, type BuildConfig, type ConfigIssue, type CustomThemePalette, DEFAULT_THEMES, type DevToolbarConfig, type ExperimentalConfig, type ExplainedConfig, type FontConfig, type FontDisplay, type GoogleFont, type LocalFontFace, type MobileConfig, type Mode, type ObservabilityConfig, type PerformanceConfig, type PwaConfig, type ResolvedTheme, type StyleProcessContext, type StylesConfig, type Mode as StylesMode, THEME_COOKIE, THEME_CSS_HREF, THEME_JS_HREF, THEME_PALETTES, THEME_PALETTE_NAMES, type TenancyConfig, type ThemeConfig, type ThemePaletteName, type ThemeTokens, bundleCss, defineConfig, explainAppConfig, findStyleEntry, fontCspSources, headToString, loadAppConfig, loadEnv, loadRawConfig, renderFontHead, renderProductionFontHead, renderStyles, renderThemeCss, renderThemeRuntime, resolveProfile, resolveThemeConfig, resolveThemeName, validateAppConfig &#125;;
</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 &#123; AppConfig &#125; from &quot;@wrnexus/styles&quot;;

export default &#123;
  head: [
    '&lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdn.jsdelivr.net/npm/bootstrap@5/dist/css/bootstrap.min.css&quot;&gt;',
  ],
  port: 3000,
  db: &#123; driver: &quot;sqlite&quot;, url: &quot;app.db&quot; &#125;,
  theme: &#123;
    palette: &quot;violet&quot;,
    default: &quot;dark&quot;,
    themes: &#123;
      light: &#123; &quot;color-primary&quot;: &quot;#7c3aed&quot; &#125;, // override one token; rest inherited
      brand: &#123;
        // add a whole new theme
        &quot;color-scheme&quot;: &quot;dark&quot;,
        &quot;color-bg&quot;: &quot;#0a0a0a&quot;,
        &quot;color-primary&quot;: &quot;#22d3ee&quot;,
      &#125;,
    &#125;,
  &#125;,
  styles: &#123;
    entry: &quot;app/styles/main.css&quot;,
  &#125;,
  profiles: &#123;
    production: &#123;
      db: &#123; driver: &quot;postgres&quot;, url: process.env.DATABASE_URL! &#125;,
    &#125;,
  &#125;,
&#125; satisfies AppConfig;</code></pre></article><article class="example-card"><h3>Loading config + producing CSS</h3><pre data-language="ts"><code>import &#123;
  loadAppConfig,
  resolveProfile,
  loadEnv,
  findStyleEntry,
  renderStyles,
&#125; from &quot;@wrnexus/styles&quot;;

const appRoot = process.cwd();
const mode = &quot;production&quot; as const;

const profile = resolveProfile(&#123; mode &#125;);
loadEnv(appRoot, profile);

const config = await loadAppConfig(appRoot, profile);

const appDir = `$&#123;appRoot&#125;/app`;
const entryPath = findStyleEntry(appDir, appRoot, config.styles?.entry);
const css = await renderStyles(&#123; entryPath, appDir, appRoot, mode &#125;, config.styles);</code></pre></article><article class="example-card"><h3>Rendering the theme</h3><pre data-language="ts"><code>import &#123;
  resolveThemeConfig,
  resolveThemeName,
  renderThemeCss,
  renderThemeRuntime,
  THEME_COOKIE,
&#125; from &quot;@wrnexus/styles&quot;;

const theme = resolveThemeConfig(config.theme);

// Server: pick the active theme from the request cookie (no flash).
const active = resolveThemeName(cookies[THEME_COOKIE], theme);
// → render &lt;html data-theme=&#123;active&#125;&gt;

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 &#123;
  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);
&#125;</code></pre></article><article class="example-card"><h3>Rendering the theme</h3><pre data-language="html"><code>&lt;button data-wire-theme-toggle&gt;Toggle theme&lt;/button&gt;
&lt;button data-wire-theme-set=&quot;brand&quot;&gt;Brand theme&lt;/button&gt;</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="#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.3.2</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>
      <BackToTop />
    </div>
  }
}
