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

  view {
    <div class="docs-shell">
      <a class="skip-link" href="#main">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="/language">Language</a><a href="/architecture">Architecture</a></nav>
        <div class="topbar-actions"><a class="preview-pill" href="/access">Private preview · v0.2.31</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="/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="page package-page">
      <aside class="sidebar"><a href="/packages">← All packages</a><span class="category">Frontend</span><h2>@wrnexus/styles</h2><p>CSS pipeline, themes, fonts, profiles, and application config.</p><span class="status status-beta">Private preview · 0.2.31</span><nav><a href="#access">Access</a><a href="#guide">Guide</a><a href="#api">Complete API</a></nav></aside>
      <article id="main" class="documentation"><section class="doc-intro"><span class="eyebrow">Frontend · Preview</span><h1>@wrnexus/styles</h1><p>CSS pipeline, themes, fonts, profiles, and application config.</p><section id="access" class="access-callout"><h2>Private registry access required</h2><p>This package is not available from the public npm registry. After WorkRoot approves access and supplies private registry instructions, install the release-aligned package:</p><pre><code>bun add @wrnexus/styles@0.2.31</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" class="prose"><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;
  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 token keys (both <code>light</code> and <code>dark</code>): <code>color-scheme</code>, <code>color-bg</code>, <code>color-surface</code>, <code>color-surface-2</code>, <code>color-text</code>, <code>color-muted</code>, <code>color-border</code>, <code>color-primary</code>, <code>color-primary-hover</code>, <code>color-primary-contrast</code>, <code>color-danger</code>, <code>color-success</code>, <code>color-warning</code>, <code>radius</code>, <code>radius-sm</code>, <code>font-sans</code>, <code>shadow-1</code>.</p>
<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;
    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="prose api"><h2>Complete TypeScript API</h2><p>This declaration comes from the exact installed package and lists its exported functions, classes, interfaces, and types.</p><pre data-language="typescript"><code>import &#123; SeoConfig, SecurityConfig &#125; from '@wrnexus/core';
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;;
interface ThemeConfig &#123;
    /** 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;;
/** 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;
interface AppConfig &#123;
    /** 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;;
/** 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, DEFAULT_THEMES, type FontConfig, type FontDisplay, type GoogleFont, type LocalFontFace, type MobileConfig, type Mode, type PwaConfig, type ResolvedTheme, type StyleProcessContext, type StylesConfig, type Mode as StylesMode, THEME_COOKIE, THEME_CSS_HREF, THEME_JS_HREF, type ThemeConfig, type ThemeTokens, bundleCss, findStyleEntry, fontCspSources, headToString, loadAppConfig, loadEnv, loadRawConfig, renderFontHead, renderProductionFontHead, renderStyles, renderThemeCss, renderThemeRuntime, resolveProfile, resolveThemeConfig, resolveThemeName &#125;;
</code></pre></section><section id="examples" class="prose examples"><h2>Examples</h2><p>Examples are taken from this package's installed documentation and must be evaluated with its requirements and stability notes.</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;
    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>WRNexusJS 0.2.31 · Private Developer Preview · Bun-native · Documentation generated from installed package APIs.</footer>
    </div>
  }
}
