Skip to main content

ranui

Use when building UI with the ranui web-component library (elements like `<r-button>`, `<r-modal>`, `<r-select>`, `<r-input>`, `<r-message>`) or its fluent DOM builder (`ranui/builder` — Div/View/Span/signal/createEffect/EventManager). Covers common import examples, conventions, accessibility, and where the authoritative API and export references live.

Source facts

Repository
chaxus/ran
Last source activity
October 17, 2026 at 06:37
Detected SKILL.md language
English
Stars
270
Forks
23

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
ranui
description
Use when building UI with the ranui web-component library (elements like `<r-button>`, `<r-modal>`, `<r-select>`, `<r-input>`, `<r-message>`) or its fluent DOM builder (`ranui/builder` — Div/View/Span/signal/createEffect/EventManager). Covers common import examples, conventions, accessibility, and where the authoritative API and export references live.
# ranui — web component library Framework-agnostic Web Components (Custom Elements + Shadow DOM). Works in React, Vue, Svelte, Solid, or plain JS. Each element is `<r-*>` and is used like native HTML. Styling crosses the shadow boundary via **CSS variables**, **`::part()`**, or the per-instance **`sheet`** attribute (escape hatch). ## Authoritative reference (read these for exact APIs — don't guess) When `ranui` is installed in a project, its docs ship inside the package: - **`node_modules/ranui/docs/COMPONENTS.md`** — generated per-element API: every element's attributes, typed properties, events (with `detail` shape), slots, and `::part()` names. This is the source of truth for props/events. Regenerated in the source repo via `npm run doc:api`. - **`node_modules/ranui/docs/DESIGN.md`** — design system rules (color states, spacing, motion, accessibility §7, pre-ship checklist). - **`node_modules/ranui/docs/INTERFACE_GUIDE.md`** — short decision-to-owner index. - **`node_modules/ranui/docs/INTERACTION.md`** — observable input, focus, keyboard, drafts, feedback, progress and recovery; read for behavior changes. - **`node_modules/ranui/docs/WORKFLOW_PATTERNS.md`** — optional batch/queue, settings, account/data and revision recipes. Load only for a relevant task; these are host responsibilities, not additional library APIs. Visual craft remains in DESIGN. - **`node_modules/ranui/docs/style-tokens-public.md`** — the CSS variable (theming token) catalog. If a generated contract omits a detail, check the element's declaration through the installed package's `exports` → `types` target before inspecting repository source. **`node_modules/ranui/docs/CODING.md`** and **`node_modules/ranui/CLAUDE.md`** also ship with the package: engineering rules and the detailed implementation reference (architecture, builder, LESS conventions, testing). Source copies live in `packages/ranui`. ## Import map Start with the host's existing controls, framework and theme authority; a small fix does not require a new component or state layer. Read only applicable owner sections, then stop when the supported contract, patch and evidence are clear. Foundation declarations ship in the installed package’s `dist/ranui.css`; component overrides are in the generated token catalog. Minimal bootstrap (once in the host entry): ```ts import 'ranui/style'; // foundation tokens and shared styles import 'ranui/button'; import 'ranui/input'; ``` Add `import 'ranui/fonts'` only when the host wants bundled Geist fonts. If the host opts into ranui's persisted light/dark/system mode, use `import { initTheme } from 'ranui/theme'` and call `initTheme()` once. Preserve an existing host theme authority; do not add a switch or another persistence owner merely to use the components. Per-component, tree-shakeable side-effect imports register the custom element: ```ts import 'ranui/button'; // then use <r-button> import 'ranui/modal'; import 'ranui/select'; import 'ranui/input'; import 'ranui/message'; // also exposes an imperative message API import 'ranui/checkbox'; ``` Common element subpaths (examples, not a complete inventory): `button icon image input message skeleton tab tabpane radar modal select progress player popover dropdown content loading colorpicker math checkbox card router route link`. For all supported subpaths, read `exports` in the installed `node_modules/ranui/package.json`; use COMPONENTS.md for element contracts. The fluent DOM builder + reactivity (no custom-element registration): ```ts import { Div, View, Span, Label, ButtonBuilder, signal, createEffect, EventManager, Style } from 'ranui/builder'; ``` Consumer SSR rendering helpers are public exports: ```ts import { HTMLElementSSR, RanElement, h, renderToString } from 'ranui/ssr'; ``` Streaming rendering uses `ranui/ssr-stream`; types use `ranui/typings`. `defineSSR` is an internal registration helper at `@/utils/ssr-registry` in the ranui source checkout. It is not exported from `ranui/ssr` or a consumer registration subpath. ## Elements at a glance - **Forms**: `r-input`, `r-checkbox`, `r-select` (+ `r-option`, `r-dropdown-item` as options). The three field elements are **form-associated custom elements** (`attachInternals()` + `ElementInternals.setFormValue()`) — their values are collected by `new FormData(form)` when they're real descendants of a native `<form>`, same as any native field — no wrapper component required. Use `serializeForm(form)` (from `ranui`) to turn a submit into a plain object. `r-button`'s `type` chooses visual appearance, not native form submission. Use a native `<button type="submit">` when submit semantics are needed, or explicitly call `form.requestSubmit()` from the host action; do not assume `type="submit"` on `r-button` submits. `r-colorpicker` is not form-associated: read its `value` / `change` event (`detail.value`) and explicitly include it in the host's submitted data when needed. - **Feedback/overlay**: `r-message` (toasts), `r-modal`, `r-popover`, `r-loading`, `r-skeleton`, `r-progress`. - **Layout/content**: `r-card`, `r-section`, `r-tab`/`r-tabs`, `r-icon`, `r-img`, `r-link`, `r-math`, `r-player`, `r-radar`. - **Routing**: `r-router`, `r-route`, `r-link` (history/hash SPA routing). - **Streaming transcript**: `r-conversation` — renders an append-only event log. Register each kind of content as a view (`match`/`start`/`update` fold state, `mount`/`patch` render it) **before the first `push`**; it throws afterwards. Bottom-follow is on by default and does not fight the reader; `pinnedchange` drives a "jump to latest" button. Use `<r-markdown>` as the prose row — its `mode="streaming"` already closes half-streamed markdown. Projection and cadence: `ranuts/conversation`. - **Tool calls**: `r-tool-card` — set `call` / `result` / `status` from a **declared intent** (`generic` / `terminal` / `diff`), never markup. Views must be pure functions of the call's arguments, because they are recomputed on replay; an unrecognised card degrades to `generic` instead of throwing. - **Chain of thought**: `r-reasoning` — expands while `streaming` is set, collapses when it clears, and **stops following once the reader opens or closes it themselves** (setting `open` from script counts). Feed `content` from `ranuts/stream`'s `reasoning-delta` blocks. - **Attachments**: `r-attachments` — holds the files staged with a message, previews them, validates, and owns its object URLs (revoked on detach/clear/disconnect). It does not collect files: call `add(files)` from paste, drop or a picker. Removal is `detach(id)`, because `remove()` already means "take yourself out of the document". Rejections are reported, never silent. - **Dictation**: `r-voice-button` — a microphone button over `ranuts`' `createSpeechRecognizer`. It reports the **whole capture so far** on each `voiceresult` (interim results are revised), so remember the text already in the box at `voicestart` and concatenate once. It never sends, hides itself where the API is absent, and only `denied`/`failed` are worth surfacing — `noSpeech`/`aborted` are routine. ## Builder + reactivity (from `ranui/builder`) - `Div()/View('r-x')/Span()/...` — chainable: `.class()`, `.attr()`, `.part()`, `.role()`, `.text()`, `.children(...)`, `.on(event, handler)`, `.build()`. - `signal(initial)` → `[getter, setter]`; `createEffect(fn)` re-runs `fn` when the signals it reads change (returns a disposer). - `EventManager` — `.on(target, type, handler)` with `.abort()` to remove all listeners at once (use in `disconnectedCallback`). ## Usage example Declarative — use elements like native HTML (after importing the subpath): ```html <r-select value="a"> <r-option value="a">Apple</r-option> <r-option value="b">Banana</r-option> </r-select> <r-input label="Email" name="email"></r-input> ``` Imperative — create, configure, and wire events (event `detail` shapes are in COMPONENTS.md, e.g. `r-select change → { value, label }`): ```ts import 'ranui/select'; import message from 'ranui/message'; const sel = document.createElement('r-select'); sel.setAttribute('value', 'a'); sel.addEventListener('change', (e) => console.log((e as CustomEvent).detail.value)); document.body.append(sel); message.success('Saved'); // imperative toast API: info | success | warning | error | toast ``` Builder + reactivity — compose DOM with signals (great for framework-free views): ```ts import { Div, ButtonBuilder, signal, createEffect } from 'ranui/builder'; const [count, setCount] = signal(0); const label = Div().build(); createEffect(() => { label.textContent = `Count: ${count()}`; }); // re-runs on change const btn = ButtonBuilder() .text('inc') .on('click', () => setCount(count() + 1)) .build(); document.body.append(label, btn); ``` ## Accessibility (already built in — rely on it, don't re-implement) - `r-message` toasts announce via an `aria-live` region (errors = assertive `alert`). - `r-checkbox`/`r-input`/`r-select` are form-associated and labelled; checkbox is keyboard-toggleable (Space/Enter) with `role=checkbox`/`aria-checked`. - `r-tabs` follows the WAI-ARIA tabs pattern (roles + roving tabindex + arrow keys). - `r-colorpicker` sliders are `role=slider` with arrow-key adjustment; the swatch opens via Enter/Space. - All components honour `prefers-reduced-motion`. For custom UI you build, follow DESIGN.md §7 (keyboard nav, visible focus, ARIA). ## Conventions & gotchas - In the ranui source checkout, component authors register with the internal `defineSSR('r-name', Component)` helper (keeps SSR working). Consumers register shipped elements through their public side-effect imports; the internal helper is not an installed-package API. - Shadow DOM isolates styles: external `<label for>` / `aria-labelledby` do NOT cross into a component's shadow root — use the component's own `label` attribute/slot. - Component CSS uses CSS variables with dark-safe fallbacks; theme via tokens, not by reaching into shadow internals. - Prefer subpath imports (`ranui/button`), never `ranui/dist/...` or internal `@/...`.
View on GitHub