5.0 KiB
WrNexus batteries-included: design & roadmap
Goal: let users start faster with a built-in UI library, theming, i18n, and validation. Built foundation-first so each subsystem lands solid and tested.
Order (one subsystem per turn):
- Theme system ✅ done — design tokens, SSR + client, no-flash, overridable.
- WrNexus UI (
@wrnexus/ui) ✅ done — 17 components + layout primitives as.wrn, consuming theme tokens, overridable 4 ways; auto-discovered +wrnexus eject. - Layout system ✅ done (folded into turn 2) — layout primitives (Container,
Stack, HStack, Grid, Divider, Spacer) + page layouts (
app/layout.wrn+<slot>). - Validation ✅ done — one schema for both form (client) and API (server).
- i18n ✅ done — translations for pages and API responses.
4. i18n (done)
@wrnexus/i18n: locales inapp/locales/<lang>.json(nested/flat keys,{param}interpolation). Per request the lang resolves from thewrn-langcookie → Accept-Language → config default (wrnexus.config.tsi18n.default).- Server:
ctx.lang+ctx.t(key, params)on every request (pages + API). - Views:
{t:key}text sugar compiles to a<span data-t="key">marker;t:<attr>="key"translates an attribute.translateHtmlresolves both on the final HTML (after components + layout), and<html lang>is set (no flash). - Switch:
[data-wrn-lang-set="es"](or<select data-wrn-lang>) → the tiny/__wrnexus/i18n.jssets the cookie + reloads; injected only when present. - Fallback chain: current lang → default lang → the key itself.
All four subsystems + the layout system are complete.
3. Validation (done)
@wrnexus/validation: fluentvbuilder (v.object({ email: v.string().email(), password: v.string().min(8) }))..parse(data)runs server-side (coerce + errors);.describe()emits a JSON descriptor. SharedcheckField/applyRuleback both.- Define once in
app/schemas/<name>.ts. API:import login from "../schemas/login"; parseBody(login, ctx.req)→ 400{errors}or{value}. Form:<form data-schema="login">with[data-error="field"]spans. - The framework bakes all descriptors into
/__wrnexus/schemas.js(window.__wrnSchemas) and ships an eval-free/__wrnexus/validate.jsthat validatesform[data-schema]on submit/blur — injected only when a page hasdata-schema.
2 + 5. WrNexus UI + layout (done)
- Distribution:
@wrnexus/uiships.wrncomponents undercomponents/; the framework auto-discovers them (routercomponentDirs), app/components shadow by name, andwrnexus eject <name>copies one into the app to own it. - Codegen: components now bake prop-driven text/attributes server-side
(
__wrnHtml/__wrnAttr) so static components ship zero JS; state-referencing text stays a reactive mustache. Reserved-word props (class) get__p_refs; attribute{expr}enablesvariant/size/classcomposition. - Slots:
renderComponentsis nesting-aware and fills<slot>with mount children — this also powers page layouts (app/layout.wrnwraps every page). - Styles: one themed stylesheet
/__wrnexus/ui.css(all.wrn-*classes usevar(--wrn-*)), linked theme → ui → app so app CSS overrides win.
1. Theme system (this turn)
Tokens. Kebab-case token keys become --wrn-<key> CSS custom properties.
Framework ships default light + dark token sets (colors, radius, etc.); user
config deep-merges over them and may add new themes.
Config (wrnexus.config.ts):
theme: {
default: "dark",
themes: {
light: { "color-primary": "#2563eb", "radius": "8px", ... },
dark: { "color-primary": "#6c8cff", ... },
},
}
Generated CSS (/__wrnexus/theme.css), linked first in <head> so global.css
and component styles can read/override it:
:root{ /* default theme tokens */ }
[data-theme="light"]{ --wrn-color-primary:#2563eb; color-scheme:light; ... }
[data-theme="dark"]{ --wrn-color-primary:#6c8cff; color-scheme:dark; ... }
SSR (no flash). Server reads the wrn-theme cookie (falls back to config
default) and renders <html data-theme="…">, so the correct theme paints on the
first byte. The theme name is validated against configured names.
Client (/__wrnexus/theme.js, injected only when a page has a toggle):
window.wrnTheme.{get,set,toggle} — sets data-theme on <html>, persists the
wrn-theme cookie, and binds [data-wrn-theme-toggle] /
[data-wrn-theme-set="name"] elements so a .wrn component can switch themes
without writing JS.
Override. Users change tokens in config, or redefine any --wrn-* variable
in their own CSS (loaded after theme.css → wins). WrNexus UI components consume
var(--wrn-*), so overriding a token restyles every component at once.
Integration points: @wrnexus/styles (theme module), @wrnexus/ssr
(renderDocument html attrs), dev runtime/assets, prod server, wrnexus build
(emits dist/theme.css + dist/theme.js, hashed into the asset version).