page wrnexusui { seo { title = "@wrnexus/ui" description = "Themeable server-rendered UI components and CSS." } view {
W WRNexusJS
Frontend · Package reference

@wrnexus/ui

Themeable server-rendered UI components and CSS.

v0.5.11Private registryFrontend

Install the package

After WorkRoot approves private registry access, install the release-aligned package:

bun add @wrnexus/ui@0.5.11

Request 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 85 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 canonical mount name comes from the component declaration (for example, component Button mounts as data-component="Button"). Component lookup is case-insensitive, so existing lowercase mounts continue to work. Each component accepts a class prop (appended to its root element), and most render their body from either a named prop or the default slot.

Layout

NamePurposeKey props
containerMax-width centered content wrapperclass
stackVertical column with gapgap (0–8)
hstackHorizontal row with gapgap (0–8)
gridCSS grid containersee source
dividerHorizontal ruleclass
spacerFlexible/empty spacing elementsee source

Core / feedback

NamePurposeKey props
buttonButtonlabel, variant (default\primary\danger\ghost), size (sm\md\lg), type
inputText inputsee source
textareaMulti-line inputsee source
checkboxCheckboxsee source
badgeSmall status badgelabel, variant
alertCallout boxvariant (info\success\danger\warning), title, message
cardPadded, bordered surfaceclass
avatarUser avatarsee source
spinnerLoading indicatorsee source
disclosureExpandable details/summarysee source
theme-toggleTheme 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 five 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.

ExportSignatureReturns
uiComponentsDir() => stringAbsolute path to the .wrn component directory (feed to buildRouter's componentDirs).
uiCssPath() => stringAbsolute path to ui.css.
uiCss() => stringThe ui.css file contents (all .wire-* classes, themed via tokens).
uiComponentNames() => string[]Sorted list of declared built-in component names.
uiComponentPath(name: string) => stringAbsolute source path for a declared component name or case-insensitive alias.

./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
  • consumed within the Bun-native WRNexusJS toolchain (Node is not supported).

  • Peer packages: components are discovered and rendered by
  • [@wrnexus/router](../router) (via componentDirs) and served by [@wrnexus/dev-server](../dev-server) / built by [@wrnexus/cli](../cli).

  • Depends on [@wrnexus/core](../core) (dependencies).
  • theme-toggle relies on the framework's theme runtime, which binds the
  • data-wire-theme-toggle attribute — no per-component JS is required.

Complete TypeScript API

Generated from the exact installed package declarations.

interface UiComponentMetadata {
    name: string;
    mount: string;
    category?: string;
    purpose?: string;
    props?: Array<{
        name: string;
        default?: unknown;
    }>;
    events?: string[];
}
interface UiComponentReference {
    count: number;
    components: UiComponentMetadata[];
}
declare function uiComponentReference(): UiComponentReference;
declare function findUiComponent(name: string): UiComponentMetadata | undefined;
declare function auditUiComponents(): Array<{
    component: string;
    issue: string;
}>;

/**
 * @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 declared by the bundled components, independent of filename casing. */
declare function uiComponentNames(): string[];
/** Absolute path to a bundled component by its declared component name. */
declare function uiComponentPath(name: string): string;

export { type UiComponentMetadata, type UiComponentReference, auditUiComponents, findUiComponent, uiComponentNames, uiComponentPath, uiComponentReference, 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

Browse interactive examples and complete component usage in the dedicated WRNexusJS component showcase.

Browse all 90 components →

} }