@wrnexus/ui
Themeable server-rendered UI components and CSS.
Install the package
After WorkRoot approves private registry access, install the release-aligned package:
bun add @wrnexus/ui@0.2.73Request preview access. Never put registry tokens in source control.
First-party Wire UI component library — a set of themeable .wrn components plus a single tokenized stylesheet.
Part of the WRNexusJS framework — an SSR-first, Bun-native full-stack web framework.
Overview
@wrnexus/ui ships a library of server-rendered .wrn components (layout, form controls, and feedback UI) together with one themeable stylesheet, ui.css. The components are auto-discovered by the framework router — you don't import them in code. Once the package's component directory is on the router's scan path, you mount any component in a page with data-component="<name>". Every visual is driven by var(--wire-*) theme tokens, so components restyle instantly when the theme changes. The tiny JS surface (src/index.ts) exists only so the toolchain (CLI build + dev server) can locate the component directory and stylesheet.
The complete PDF-aligned catalog currently contains 891 components. The generated COMPONENTS.md and component-reference.json files document every mount name, prop, inferred type, default/required status, slot, event, category, and source file directly from the packaged .wrn source.
bun add @wrnexus/ui
Private package — the machine must be authenticated to the wrnexus npm org
(a read token in ~/.npmrc). Requires Bun (Node is not supported).
In practice you rarely install this directly: @wrnexus/cli and @wrnexus/dev-server already depend on it and wire it into the router for you (see [Auto-discovery](#auto-discovery)).
Components
Components live as .wrn files under packages/ui/components/. The mount name is the lowercase file basename (e.g. button.wrn → data-component="button"). Each accepts a class prop (appended to its root element) and most render their body from either a named prop or the default slot.
Layout
| Name | Purpose | Key props |
|---|---|---|
container | Max-width centered content wrapper | class |
stack | Vertical column with gap | gap (0–8) |
hstack | Horizontal row with gap | gap (0–8) |
grid | CSS grid container | see source |
divider | Horizontal rule | class |
spacer | Flexible/empty spacing element | see source |
Core / feedback
| Name | Purpose | Key props | |||||
|---|---|---|---|---|---|---|---|
button | Button | label, variant (default\ | primary\ | danger\ | ghost), size (sm\ | md\ | lg), type |
input | Text input | see source | |||||
textarea | Multi-line input | see source | |||||
checkbox | Checkbox | see source | |||||
badge | Small status badge | label, variant | |||||
alert | Callout box | variant (info\ | success\ | danger\ | warning), title, message | ||
card | Padded, bordered surface | class | |||||
avatar | User avatar | see source | |||||
spinner | Loading indicator | see source | |||||
disclosure | Expandable details/summary | see source | |||||
theme-toggle | Theme switch button (binds data-wire-theme-toggle) | label |
Additional controls & data display
Also shipped: select, radio, switch, progress, tag, skeleton, tooltip, table, FAQAccordion, AnnouncementBar, and BackToTop.
The PDF-defined minimum release and essential build-first set also includes typed typography, form primitives, loading actions, combobox and multi-select, time/date-time and recurring schedule controls, confirmation dialogs, data tables, filters, desktop/mobile navigation, mega menus, marketing/product/legal page shells, product and metric cards, FAQ composition, pricing comparison, SDK tabs, legal navigation, and cookie preferences.
Seo and StructuredData remain framework/page concerns rather than body components: use the native page seo { ... } block and document-head APIs so metadata is emitted in <head> instead of invalid component markup.
The authoritative, always-current list is uiComponentNames() (below), which reads the component directory at runtime.
For the full catalog, see [COMPONENTS.md](./COMPONENTS.md). The machine-readable equivalent is exported as @wrnexus/ui/component-reference.json.
API
The JS module (@wrnexus/ui) exposes four helpers used by the build tooling to locate the component assets. There is no component code to import — the components are .wrn files rendered server-side.
| Export | Signature | Returns |
|---|---|---|
uiComponentsDir | () => string | Absolute path to the .wrn component directory (feed to buildRouter's componentDirs). |
uiCssPath | () => string | Absolute path to ui.css. |
uiCss | () => string | The ui.css file contents (all .wire-* classes, themed via tokens). |
uiComponentNames | () => string[] | Sorted list of built-in component names (e.g. for wrnexus eject listing). |
./ui.css asset export
package.json also exposes the raw stylesheet as a subpath asset:
"exports": {
".": "./src/index.ts",
"./ui.css": "./ui.css"
}
The framework serves this stylesheet once at /__wrnexus/ui.css, so pages get all component styles from a single request.
Tailwind and motion
Components use static Tailwind utility classes alongside the shared .wire-* layer. If the package is consumed by a separate Tailwind build, include its component sources so every utility is generated:
@import "tailwindcss";
@source "../node_modules/@wrnexus/ui/components/*.wrn";
The shared stylesheet gives all component boundaries consistent, GPU-friendly entry and interaction motion. Override --wire-motion-fast, --wire-motion-base, --wire-motion-slow, --wire-ease-standard, or --wire-ease-emphasized to tune it. Hover lift is limited to precise pointing devices and prefers-reduced-motion is honored automatically.
Using the selected theme in application UI
The active theme and palette are not limited to @wrnexus/ui components. The framework exposes the resolved values as semantic CSS custom properties, so pages and custom .wrn components can use the same contract:
.account-card {
background: var(--wire-color-surface);
color: var(--wire-color-text);
border: 1px solid var(--wire-color-border);
}
.account-card__action {
background: var(--wire-color-primary);
color: var(--wire-color-primary-contrast);
}
Stable no-spacing helper classes are also available: wire-bg-page, wire-bg-surface, wire-bg-surface-2, wire-bg-primary, wire-bg-secondary, wire-text, wire-text-muted, wire-text-primary, wire-text-success, wire-text-warning, wire-text-danger, and wire-border.
Tailwind-authored custom markup can continue using the palette families already used by packaged components. indigo-* and violet-* resolve to primary, blue-* to info, emerald-*/green-* to success, amber-* to warning, and red-*/rose-* to danger. These aliases live at :root, so they work outside a [data-component] boundary too.
Usage
Auto-discovery
The router scans extra componentDirs (in addition to the app's own app/components) and keys components by name. Library dirs are scanned first and app/components last, so an app component of the same name shadows the library's. The CLI build (@wrnexus/cli) and dev server (@wrnexus/dev-server) both wire the UI directory in for you:
import { buildRouter } from "@wrnexus/router";
import { uiComponentsDir } from "@wrnexus/ui";
const router = buildRouter(appDir, { componentDirs: [uiComponentsDir()] });
Mounting components in a page
Once discovered, mount any component by name via data-component. Quoted attributes (other than data-component) become string props:
<div data-component="card">
<div data-component="badge" label="New"></div>
<button data-component="button" label="Save" variant="primary" size="lg"></button>
<div data-component="alert" variant="success" title="Done" message="Saved."></div>
</div>
Overrides
Ways to customize the components, in increasing order of power:
1. Theme tokens — override CSS custom properties such as --wire-color-primary, --wire-color-surface, --wire-radius-sm, etc. Every component style resolves through var(--wire-*), so changing a token restyles everything instantly (including across theme switches). 2. App CSS — redefine a .wire-* class in your own stylesheet, which is loaded after ui.css and therefore wins. 3. class prop — pass a class prop to a component; it is appended to the component's root element, letting you add per-instance classes without touching the base styles. 4. wrnexus eject <name> — copy the component's .wrn source into your app/components, where (because app components shadow library ones) you fully own and can edit it. Use uiComponentNames() for the list of ejectable names.
Requirements / Notes
- Bun-only — the package uses standard fs/path/url APIs but is published and
- Peer packages: components are discovered and rendered by
- Depends on [
@wrnexus/core](../core) (dependencies). theme-togglerelies on the framework's theme runtime, which binds the
consumed within the Bun-native WRNexusJS toolchain (Node is not supported).
[@wrnexus/router](../router) (via componentDirs) and served by [@wrnexus/dev-server](../dev-server) / built by [@wrnexus/cli](../cli).
data-wire-theme-toggle attribute — no per-component JS is required.
Complete TypeScript API
Generated from the exact installed package declarations.
/**
* @wrnexus/ui — the Wire UI component library.
*
* Components are `.wrn` files under `components/`, auto-discovered by the
* framework (the router scans this directory in addition to the app's own
* `app/components`). Mount them in any page with `data-component="<name>"`.
* Their styles live in a single themeable stylesheet, `ui.css`, served once at
* `/__wrnexus/ui.css` — every class uses `var(--wire-*)` theme tokens.
*
* Override, in increasing order of power:
* 1. theme tokens (change `--wire-color-primary`, etc.)
* 2. redefine a `.wire-*` class in your own CSS (loaded after ui.css)
* 3. pass a `class` prop (appended to the component root)
* 4. `wrnexus eject <name>` to copy the component into `app/components` and own it
*/
/** Absolute path to the directory of Wire UI component `.wrn` files. */
declare function uiComponentsDir(): string;
/** Absolute path to the Wire UI stylesheet. */
declare function uiCssPath(): string;
/** The Wire UI stylesheet contents (all `.wire-*` classes, themed via tokens). */
declare function uiCss(): string;
/** Names of the built-in components (e.g. for `wrnexus eject` listing). */
declare function uiComponentNames(): string[];
export { uiComponentNames, uiComponentsDir, uiCss, uiCssPath };
Examples
Copy-ready examples from the installed package documentation.
Auto-discovery
import { buildRouter } from "@wrnexus/router";
import { uiComponentsDir } from "@wrnexus/ui";
const router = buildRouter(appDir, { componentDirs: [uiComponentsDir()] });Mounting components in a page
<div data-component="card">
<div data-component="badge" label="New"></div>
<button data-component="button" label="Save" variant="primary" size="lg"></button>
<div data-component="alert" variant="success" title="Done" message="Saved."></div>
</div>Explore the component library
The component reference now has a dedicated catalog with searchable cards and one complete page per component.