# @wrnexus/ui > First-party Wire UI component library — a set of themeable `.wrn` components plus a single tokenized stylesheet. Part of the **WrNexus** 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=""`. 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. ## Installation ```bash 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 `` 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: ```json "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: ```css @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: ```css .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: ```ts 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: ```html
``` ## 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 `** — 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 WrNexus 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.