# @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="<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.

## 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 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

| 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 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.

| 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 declared built-in component names.                                          |
| `uiComponentPath`  | `(name: string) => string` | Absolute 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:

```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.

### Component styles and motion

Components own their BEM styles in local `style {}` blocks and do not require a
Tailwind scan. `ui.css` supplies only global tokens, resets, and shared
primitives. Applications can still use Tailwind independently in their own
source files.

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
<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 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.
