@wrnexus/ui
Themeable server-rendered UI components and CSS.
Private registry access required
This package is not available from the public npm registry. After WorkRoot approves access and supplies private registry instructions, install the release-aligned package:
bun add @wrnexus/ui@0.2.28Request 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.
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, and table.
The authoritative, always-current list is uiComponentNames() (below), which reads the component directory at runtime.
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.
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
This declaration comes from the exact installed package and lists its exported functions, classes, interfaces, and types.
/**
* @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
Examples are taken from this package's installed documentation and must be evaluated with its requirements and stability notes.
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>