@vielzeug/codex 26.10.14 → 26.10.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/catalog.json +84 -43
- package/data/llms-full.txt +739 -2
- package/data/llms.txt +2 -1
- package/data/manifest.json +1 -1
- package/data/packages/beacon.json +51 -0
- package/data/packages/refine.json +2 -2
- package/data/refine.json +854 -416
- package/data/search.json +33 -3
- package/package.json +5 -5
- package/skills/vielzeug/SKILL.md +4 -3
package/data/llms.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Vielzeug
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> 43 focused TypeScript packages. Version: 26.10.15
|
|
4
4
|
|
|
5
5
|
Install any package independently: `pnpm add @vielzeug/<name>`
|
|
6
6
|
|
|
@@ -9,6 +9,7 @@ Install any package independently: `pnpm add @vielzeug/<name>`
|
|
|
9
9
|
- [@vielzeug/archive](/archive/): Community content publishing for Cloudflare Workers + D1: a hardened Worker core over per-collection payload schemas
|
|
10
10
|
- [@vielzeug/arsenal](/arsenal/): Tree-shakeable TypeScript utilities with focused category entry points for arrays, async work, caching, objects, strings, math, and guards.
|
|
11
11
|
- [@vielzeug/assay](/assay/): Scoped DOM queries, exact event dispatch, and cancellable async waiting for browser tests.
|
|
12
|
+
- [@vielzeug/beacon](/beacon/): Guided onboarding and contextual product tours for web applications
|
|
12
13
|
- [@vielzeug/clockwork](/clockwork/): Framework-neutral typed state machines with pure transitions, actor-owned runtime work, timers, invokes, and explicit effects.
|
|
13
14
|
- [@vielzeug/codex](/codex/): Local MCP access to Vielzeug documentation and package metadata.
|
|
14
15
|
- [@vielzeug/coins](/coins/): Exact bigint monetary arithmetic with explicit currency definitions, decimal strings, allocation, exchange, formatting, and JSON boundaries.
|
package/data/manifest.json
CHANGED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"apiSource": "// Public root surface of @vielzeug/beacon. Every public export goes through this file.\nexport { createCoachmark } from './coachmark';\nexport { BeaconConfigError, BeaconDisposedError, BeaconError } from './errors';\nexport { resolveTarget } from './target';\nexport { createTour } from './tour';\nexport type {\n BeaconBaseEvent,\n BeaconCoachmarkConfig,\n BeaconCoachmarkEvent,\n BeaconCoachmarkHandle,\n BeaconCurrentStep,\n BeaconPlacement,\n BeaconPresentation,\n BeaconProgress,\n BeaconRect,\n BeaconStatus,\n BeaconStep,\n BeaconStorage,\n BeaconTarget,\n BeaconTargetResolver,\n BeaconTourConfig,\n BeaconTourEvent,\n BeaconTourHandle,\n} from './types';\n",
|
|
3
|
+
"docs": {
|
|
4
|
+
"index": "---\ntitle: 'Beacon: Product tours and coachmarks'\ndescription: \"Guided onboarding and contextual product tours for web applications\"\npackage: beacon\ncategory: ui-interaction\nkeywords: [onboarding, product-tour, coachmark, walkthrough, step-by-step, spotlight, headless]\nrelated: [refine, orbit, ripple, vault]\nexports: [createTour, createCoachmark, resolveTarget]\nenvironments: [browser]\n---\n\n<!-- markdownlint-disable MD025 MD033 MD060 -->\n\n<PackageHero package=\"beacon\" />\n\n## Why Beacon?\n\nProduct tours rot fast: hand-wired step sequences hard-code positioning, persistence, and target waiting into application code, and break the first time the UI moves. Beacon is the headless half of a tour — it owns step order, target waiting, resumable progress, and events — while your markup (or `@vielzeug/refine`'s `ore-tour` components) decides what a step looks like.\n\n```ts\n// Before: step logic tangled with rendering and storage\nlet step = Number(localStorage.getItem('tour-step') ?? 0);\nfunction showStep() {\n const el = document.querySelector(steps[step].selector);\n card.style.top = `${el.getBoundingClientRect().bottom + 8}px`; // breaks on scroll\n card.innerHTML = steps[step].html;\n}\n\n// After: the engine owns order, persistence, and the anchor rect\nimport { createTour } from '@vielzeug/beacon';\n\nconst tour = createTour({ id: 'welcome', steps, storage });\ntour.current.subscribe(() => renderCard(tour.current.value)); // rect is the live anchor\ntour.start();\n```\n\n| Feature | Beacon | Shepherd.js | Intro.js |\n| --- | --- | --- | --- |\n| Bundle size | <PackageInfo package=\"beacon\" type=\"size\" /> | ~50 kB | ~25 kB |\n| External dependencies | <ore-icon name=\"check\" size=\"16\"></ore-icon> (none) | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> |\n| Headless (you own the UI) | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n| Resumable persisted progress | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"triangle-alert\" size=\"16\"></ore-icon> |\n| Versioned re-offers | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n| Coachmarks (single hints) | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"triangle-alert\" size=\"16\"></ore-icon> |\n\n<div class=\"decision-callout\">\n\n**Use Beacon when** tours must survive UI changes: steps anchor to your real DOM, progress persists and resumes, and the card markup is yours (or refine's ready-made `ore-tour`).\n\n**Consider Shepherd or Intro.js when** you want a fully-rendered, opinionated tour widget you never style yourself and can accept their DOM and CSS.\n\n</div>\n\n## Installation\n\n::: code-group\n\n```sh [pnpm]\npnpm add @vielzeug/beacon\n```\n\n```sh [npm]\nnpm install @vielzeug/beacon\n```\n\n```sh [yarn]\nyarn add @vielzeug/beacon\n```\n\n:::\n\n## Quick Start\n\nDefine the tour as data, run it, and render the current step wherever you like. The handle is disposable, so component teardown cleans everything up.\n\n```ts\nimport { createTour, type BeaconTourConfig } from '@vielzeug/beacon';\n\nconst config = {\n id: 'getting-started',\n steps: [\n { id: 'welcome', title: 'Welcome', description: 'A two-minute look around.' },\n { id: 'nav', target: '[data-tour=\"nav\"]', title: 'Navigation', placement: 'bottom-start' },\n ],\n} satisfies BeaconTourConfig;\n\nconst tour = createTour(config);\n\n// Render from the live snapshot: rect is the anchor, null centers the card.\nconst unsubscribe = tour.current.subscribe(() => {\n const { presentation, rect, step } = tour.current.value;\n console.log({ presentation, rect, step }); // drive your card from this\n});\n\ntour.start();\n\n// On teardown:\nunsubscribe();\ntour.dispose();\n```\n\n## Features\n\n<div class=\"features-grid\">\n\n- `createTour()` runs a sequential, resumable walkthrough over your own DOM.\n- `createCoachmark()` shows a single contextual hint once per user per version.\n- `current` is a live `Readable<BeaconCurrentStep>` snapshot: step, index, placement, presentation, and resolved anchor rect, kept fresh on scroll, resize, and layout changes.\n- `target` accepts a selector, `Element`, rect, or lazy resolver, and beacon waits for lazily-mounted targets before showing the step.\n- `storage` persists progress through any `BeaconStorage` bridge (localStorage, IndexedDB via `@vielzeug/vault`, a server API).\n- `version` re-offers a finished tour when the product changes underneath it.\n- `tap()` subscribes to lifecycle events (`step:start`, `tour:complete`, `target-missing`, ...) with `AbortSignal` support.\n- `resolveTarget()` turns any target into a viewport rect compatible with `@vielzeug/orbit` positioners.\n- `ore-tour`'s `bind()` in `@vielzeug/refine` renders a handle and routes its actions with zero glue code.\n\n</div>\n\n## Documentation\n\n<div class=\"doc-links\">\n\n- [Usage Guide](./usage.md)\n- [API Reference](./api.md)\n- [Examples](./examples.md)\n- [Migration Guide](./migration.md)\n\n</div>\n\n## See Also\n\n<div class=\"see-also\">\n\n- [Refine](/refine/): ships `ore-tour` and `ore-spotlight`, the ready-made dialog/popover/bottom-sheet presentation layer for a beacon handle.\n- [Orbit](/orbit/): compute exact popover coordinates from `current.rect` when you build a custom presentation.\n- [Vault](/vault/): durable storage backends that plug into `BeaconStorage` with a six-line bridge.\n- [Ripple](/ripple/): the `Readable` signals every beacon handle exposes come from here.\n\n</div>\n\n<!-- markdownlint-enable MD025 MD033 MD060 -->\n",
|
|
5
|
+
"api": "---\ntitle: 'Beacon: API Reference'\ndescription: Public API of @vielzeug/beacon.\n---\n\n[[toc]]\n\n## API Overview\n\n| Symbol | Purpose | Execution mode | Common gotcha |\n| --- | --- | --- | --- |\n| `createTour(config)` | Runs a sequential, resumable walkthrough over the app's DOM | Sync (storage reads may be async) | `start()` is a no-op when the stored record says the tour is completed |\n| `createCoachmark(config)` | Shows a single contextual hint anchored to one target | Sync (storage reads may be async) | `show()` is a no-op after `dismiss()` until `reset()` or a `version` bump |\n| `resolveTarget(target)` | Resolves any target to a viewport-space rect or `null` | Sync | Call it per position update; a scrolled target's rect changes |\n| `BeaconError` | Base class for all beacon-originated errors | — | Catch this to catch any beacon error |\n| `BeaconConfigError` | Invalid tour/coachmark configuration | Thrown at creation | Empty `steps` or duplicate step ids fail at `createTour()` |\n| `BeaconDisposedError` | Handle used after `dispose()` | Thrown on call | `dispose()` is idempotent; later method calls throw |\n\n## Package Entry Point\n\n| Import | Purpose |\n| --- | --- |\n| `@vielzeug/beacon` | Complete public Beacon API |\n\n---\n\n## Core Functions\n\n### createTour\n\n```ts\nfunction createTour(config: BeaconTourConfig): BeaconTourHandle\n```\n\nReturns a live handle for one guided tour. Throws `BeaconConfigError` when `id` is empty, `steps` is empty, or step ids are duplicated.\n\nParameters: see [`BeaconTourConfig`](#beacontourconfig).\n\n**Returns:** a [`BeaconTourHandle`](#beacontourhandle) that is also `Disposable`.\n\n**Example:**\n\n```ts\nimport { createTour } from '@vielzeug/beacon';\n\nconst tour = createTour({\n id: 'release-tour',\n steps: [{ id: 'intro', title: 'What changed' }],\n autoStart: true,\n});\n\ntour.status.subscribe(() => console.log(tour.status.value));\n```\n\nHandle methods:\n\n| Member | Behavior |\n| --- | --- |\n| `start()` | Begin or resume the tour. No-op when completed; throws after dispose. |\n| `next()` | Advance; past the last step, finishes the tour. |\n| `back()` | Return to the previous step. No-op on the first step. |\n| `goto(step)` | Jump to a step by index or id. Unknown values warn and no-op. |\n| `pause()` | Stop showing, keep progress so `start()` resumes it. |\n| `skip()` | End from a \"Skip\" control. Persisted as dismissed, reported with reason `'skip'`. |\n| `finish()` | Mark finished and stop showing. |\n| `dismiss()` | End by closing (Escape, backdrop). Persisted as dismissed. |\n| `reset()` | Discard persisted progress; the tour is eligible again. |\n| `tap(handler, options?)` | Observe [`BeaconTourEvent`](#beacontourentype)s; returns an unsubscribe, honors `options.signal`. |\n| `dispose()` / `[Symbol.dispose]()` | Release all listeners and observers. Idempotent. |\nHandle properties:\n\n| Member | Type | Notes |\n| --- | --- | --- |\n| `current` | `Readable<BeaconCurrentStep>` | The snapshot to render; `step` is `null` while closed. Beacon re-publishes the rect on scroll, resize, and target layout changes while a step is active. |\n| `status` | `Readable<BeaconStatus>` | Lifecycle, persisted on every change. |\n| `eligible` | `Readable<boolean>` | Whether the tour should be offered to this user. |\n| `steps` | `readonly BeaconStep[]` | The configured steps, in order. |\n| `id` | `string` | The configured id (also the storage key suffix). |\n| `disposed` | `boolean` | Whether `dispose()` has run. |\n| `disposalSignal` | `AbortSignal` | Aborted on dispose; pass it to your own listeners. |\n\n---\n\n### createCoachmark\n\n```ts\nfunction createCoachmark(config: BeaconCoachmarkConfig): BeaconCoachmarkHandle\n```\n\nReturns a handle for one contextual hint. Throws `BeaconConfigError` when `id` is empty.\n\nParameters: see [`BeaconCoachmarkConfig`](#beaconcoachmarkconfig).\n\n**Returns:** a [`BeaconCoachmarkHandle`](#beaconcoachmarkhandle) that is also `Disposable`.\n\n**Example:**\n\n```ts\nimport { createCoachmark } from '@vielzeug/beacon';\n\nconst hint = createCoachmark({\n id: 'share-hint',\n target: '[data-tour=\"share\"]',\n title: 'Share this board',\n});\n\nhint.show(); // waits for the target to mount if it is not on screen yet\n```\n\nHandle members:\n\n| Member | Behavior |\n| --- | --- |\n| `show()` | Show the hint. No-op once dismissed without a newer version. |\n| `hide()` | Hide without recording it as seen; it can show again. |\n| `dismiss()` | Hide and record as seen permanently (until `reset()`/`version`). |\n| `reset()` | Discard the persisted record; the hint is eligible again. |\n| `tap(handler, options?)` | Observe `coachmark:show`, `coachmark:dismiss`, and common events. |\n| `current` | `Readable<BeaconCurrentStep>` — the hint as a single-step snapshot: `step` is the hint while visible, `null` while hidden. The rect stays live on scroll, resize, and target layout changes. |\n| `eligible` | `Readable<boolean>` — never seen, or superseded by a newer version. |\n\n---\n\n### resolveTarget\n\n```ts\nfunction resolveTarget(target: BeaconTarget): BeaconRect | null\n```\n\nResolves a target to a viewport-space rect, or `null` when it is not available yet. Strings resolve through `document.querySelector`; functions are called and their result normalized.\n\n| Parameter | Type | Description |\n| --- | --- | --- |\n| `target` | `BeaconTarget` | Selector, `Element`, rect, or lazy resolver. |\n\n**Returns:** `BeaconRect | null` — shape-compatible with `getBoundingClientRect()` and `@vielzeug/orbit`'s `Rect`.\n\n**Example:**\n\n```ts\nimport { resolveTarget } from '@vielzeug/beacon';\n\nconst rect = resolveTarget('[data-tour=\"share\"]');\n// Anchor an orbit positioner from a virtual reference:\nconst reference = { getBoundingClientRect: () => resolveTarget('[data-tour=\"share\"]') ?? ZERO_RECT };\n```\n\n---\n\n## Types\n\n### BeaconTourConfig\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `id` | `string` | required | Unique identity; used as the storage key. |\n| `steps` | `readonly BeaconStep[]` | required | Every step, in order. At least one. |\n| `autoScroll` | `boolean` | `true` | Scroll each anchored step's target into view when it activates. |\n| `autoStart` | `boolean` | `false` | Start on the next microtask when eligible. |\n| `placement` | `BeaconPlacement` | `'auto'` | Default placement handed to the presentation layer. |\n| `presentation` | `BeaconPresentation` | `'auto'` | Default presentation handed to the presentation layer. |\n| `scrollOffset` | `number \\| (() => number)` | `0` | Pixels kept clear at the viewport top for a sticky header, read at scroll time. |\n| `storage` | `BeaconStorage` | memory-only | Progress persistence. |\n| `targetTimeoutMs` | `number` | `Infinity` | How long to wait for a step's target before skipping that step. `0` publishes immediately with a `null` rect. |\n| `version` | `number` | `1` | Bump to re-show a finished tour. |\n\n### BeaconCoachmarkConfig\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `id` | `string` | required | Unique identity; used as the storage key. |\n| `target` | `BeaconTarget` | required | Where the hint points. |\n| `autoShow` | `boolean` | `true` | Show automatically when not yet seen. |\n| `data` | `unknown` | — | Free-form data for the presentation layer. |\n| `description` | `string` | — | Supporting copy. |\n| `placement` | `BeaconPlacement` | `'auto'` | Preferred side. |\n| `presentation` | `BeaconPresentation` | `'auto'` | Preferred presentation. |\n| `storage` | `BeaconStorage` | memory-only | Seen-record persistence. |\n| `title` | `string` | — | Headline copy. |\n| `version` | `number` | `1` | Bump to re-show a seen coachmark. |\n\n### BeaconStep\n\n```ts\ninterface BeaconStep {\n readonly data?: unknown;\n readonly description?: string;\n readonly id: string;\n readonly placement?: BeaconPlacement;\n readonly presentation?: BeaconPresentation;\n readonly target?: BeaconTarget;\n readonly title?: string;\n}\n```\n\nOne stop in a tour. `id` is stable and persisted — renaming it resets progress for that step. Omit `target` for a centered intro/outro step. `placement`/`presentation` override the tour-level defaults.\n\n### BeaconTarget\n\n```ts\ntype BeaconTarget = BeaconTargetResolver | BeaconRect | Element | string;\n```\n\nWhat a step points at. Selectors and elements anchor to real DOM; rects (or resolvers returning one) anchor to element-less geometry such as a canvas region.\n\n### BeaconTargetResolver\n\n```ts\ntype BeaconTargetResolver = () => Element | BeaconRect | null;\n```\n\nResolves a target lazily; `null` means \"not available yet\".\n\n### BeaconRect\n\n```ts\ninterface BeaconRect {\n readonly height: number;\n readonly width: number;\n readonly x: number;\n readonly y: number;\n}\n```\n\nA viewport-space rectangle in CSS pixels. Matches `@vielzeug/orbit`'s `Rect` shape.\n\n### BeaconPlacement\n\n```ts\ntype BeaconPlacement =\n | 'auto'\n | 'bottom' | 'bottom-end' | 'bottom-start'\n | 'left' | 'left-end' | 'left-start'\n | 'right' | 'right-end' | 'right-start'\n | 'top' | 'top-end' | 'top-start';\n```\n\nPreferred side for an anchored presentation; `'auto'` asks the positioning layer to choose.\n\n### BeaconPresentation\n\n```ts\ntype BeaconPresentation = 'auto' | 'bottom-sheet' | 'modal' | 'popover';\n```\n\nHow a step should be presented. `'auto'` lets the presentation layer pick by available space.\n\n### BeaconStatus\n\n```ts\ntype BeaconStatus = 'active' | 'completed' | 'dismissed' | 'idle' | 'paused';\n```\n\nTour lifecycle, persisted across visits. `'idle'` means never started.\n\n### BeaconProgress\n\n```ts\ninterface BeaconProgress {\n readonly status: BeaconStatus;\n readonly stepIndex: number;\n readonly version: number;\n}\n```\n\nThe persisted record for one tour or coachmark. `stepIndex` is `-1` before the first step. Records are validated on read; invalid records fall back to a fresh tour.\n\n### BeaconStorage\n\n```ts\ninterface BeaconStorage {\n get(key: string): Promise<BeaconProgress | null | undefined> | BeaconProgress | null | undefined;\n remove(key: string): Promise<unknown> | unknown;\n set(key: string, value: BeaconProgress): Promise<unknown> | unknown;\n}\n```\n\nMinimal storage contract; sync or async. Values are JSON. Keys arrive namespaced (`@vielzeug/beacon:<id>`). A `@vielzeug/vault` store plugs in with:\n\n```ts\nconst storage: BeaconStorage = {\n get: (key) => store.get('progress', key),\n remove: (key) => store.delete('progress', key),\n set: (key, value) => store.put('progress', { id: key, ...value }),\n};\n```\n\n### BeaconCurrentStep\n\n```ts\ninterface BeaconCurrentStep {\n readonly placement: BeaconPlacement;\n readonly presentation: BeaconPresentation;\n readonly rect: BeaconRect | null;\n readonly step: BeaconStep | null;\n readonly stepIndex: number;\n readonly totalSteps: number;\n}\n```\n\nThe snapshot a presentation layer renders from. `step` is `null` before start and after the tour ends; `rect` is `null` for centered steps and unresolved targets. While a step is active, Beacon keeps `rect` fresh on scroll, resize, and target layout changes — no app-side refresh call is needed.\n\n### BeaconTourEvent\n\n```ts\ntype BeaconTourEvent =\n | { readonly index: number; readonly step: BeaconStep; readonly type: 'step:start' }\n | { readonly index: number; readonly step: BeaconStep; readonly type: 'step:complete' }\n | { readonly reason: 'finish' | 'skip'; readonly type: 'tour:complete' }\n | { readonly type: 'tour:dismiss' }\n | { readonly status: BeaconStatus; readonly type: 'status-change' }\n | { readonly message: string; readonly type: 'target-missing' }\n | { readonly index: number; readonly message: string; readonly step: BeaconStep; readonly type: 'target-waiting' }\n | { readonly error: unknown; readonly message: string; readonly type: 'warning' }\n | { readonly type: 'dispose' };\n```\n\nNotable moments a tour reports through `tap()`. `target-missing` means a step was skipped because its target did not appear in time; `target-waiting` fires when a step begins waiting for its target to mount; `warning` carries storage or handler failures.\n\n### BeaconCoachmarkEvent\n\n```ts\ntype BeaconCoachmarkEvent =\n | { readonly type: 'coachmark:show' }\n | { readonly type: 'coachmark:dismiss' }\n | { readonly type: 'target-waiting' }\n | { readonly error: unknown; readonly message: string; readonly type: 'warning' }\n | { readonly type: 'dispose' };\n```\n\nNotable moments a coachmark reports through `tap()`.\n\n### BeaconTourHandle / BeaconCoachmarkHandle\n\nThe objects returned by `createTour()` and `createCoachmark()`, documented in [Core Functions](#core-functions). Both expose `disposalSignal`, `disposed`, `tap()`, `dispose()`, and `[Symbol.dispose]()`.\n\n## Errors\n\n| Error | Triggered by | Notes |\n| --- | --- | --- |\n| `BeaconError` | Base for all beacon errors | `name` is the subclass name; `cause` supported via `ErrorOptions`. |\n| `BeaconConfigError` | Empty `id`, empty `steps`, missing/duplicate step ids | Thrown synchronously from `createTour()`/`createCoachmark()`. |\n| `BeaconDisposedError` | Calling handle methods after `dispose()` | `dispose()` itself never throws. |\n",
|
|
6
|
+
"usage": "---\ntitle: 'Beacon: Usage Guide'\ndescription: How to use @vielzeug/beacon.\n---\n\n[[toc]]\n\n## Basic Usage\n\nA tour is a config object plus a handle you drive from your own UI. The handle never renders anything: it publishes a `current` snapshot, and your card (or refine's `ore-tour`) renders it.\n\n```ts\nimport { createTour } from '@vielzeug/beacon';\n\nconst tour = createTour({\n id: 'product-tour',\n steps: [\n { id: 'welcome', title: 'Welcome', description: 'See what changed in this release.' },\n { id: 'nav', target: '[data-tour=\"nav\"]', title: 'Find everything', placement: 'bottom-start' },\n { id: 'share', target: '[data-tour=\"share\"]', title: 'Share the board', placement: 'bottom-end' },\n ],\n autoStart: true,\n});\n\n// Readable<T> from @vielzeug/ripple: the listener takes no arguments;\n// read .value inside it.\nconst unsubscribe = tour.current.subscribe(() => {\n const current = tour.current.value;\n if (!current.step) {\n hideCard();\n return;\n }\n renderCard({\n anchor: current.rect, // viewport rect of the target, null for centered steps\n index: current.stepIndex,\n placement: current.placement,\n presentation: current.presentation,\n title: current.step.title,\n total: current.totalSteps,\n });\n});\n\n// Wire your card's controls back to the handle.\ncardEl.addEventListener('next', () => tour.next());\ncardEl.addEventListener('back', () => tour.back());\ncardEl.addEventListener('skip', () => tour.skip());\n\n// On teardown:\nunsubscribe();\ntour.dispose();\n```\n\n`start()` is a no-op when the stored record says this user already completed the tour, so calling it on every page load is safe.\n\n## Steps and Targets\n\nA step without a `target` centers (intro/outro copy). A step with one anchors, and beacon waits for the target to exist before publishing the step — a tour can walk through lazily-mounted routes without any coordination code.\n\n```ts\nconst tour = createTour({\n id: 'deep-tour',\n steps: [\n // Selector: resolved with document.querySelector on activation.\n { id: 'sidebar', target: '[data-tour=\"sidebar\"]', title: 'Your libraries' },\n // Element: a ref your framework already holds.\n { id: 'editor', target: editorEl, title: 'The editor' },\n // Rect: geometry with no element, such as a canvas region.\n { id: 'chart', target: { height: 120, width: 240, x: 400, y: 300 }, title: 'This trend' },\n // Resolver: computed on every activation, and on scroll/resize while the step is active.\n { id: 'selection', target: () => window.getSelection()?.getRangeAt(0).getBoundingClientRect() ?? null, title: 'Your selection' },\n ],\n // Give up waiting after 4s and skip the step instead of hanging the tour.\n targetTimeoutMs: 4000,\n});\n```\n\nWith the default `targetTimeoutMs: Infinity`, a step whose target never mounts simply waits: the tour resumes the moment the user navigates to it. Set `0` to publish immediately with a `null` rect instead of waiting.\n\n## Persisting Progress\n\nWithout `storage`, progress lives in memory for the handle's lifetime only. Any object matching `BeaconStorage` makes it durable — values are JSON, keys are namespaced under `@vielzeug/beacon:`.\n\n```ts\nimport type { BeaconStorage } from '@vielzeug/beacon';\n\nconst storage: BeaconStorage = {\n get: (key) => JSON.parse(localStorage.getItem(key) ?? 'null'),\n remove: (key) => localStorage.removeItem(key),\n set: (key, value) => localStorage.setItem(key, JSON.stringify(value)),\n};\n\nconst tour = createTour({ id: 'product-tour', steps, storage });\n```\n\nAsync storage works too: `get` may return a promise, and the tour boots as soon as it resolves. A `set` that rejects is reported through `tap()` as a `warning` event, never thrown.\n\nRecords are validated on read, so a hand-edited or stale record falls back to a fresh tour instead of crashing.\n\n### Re-offering a finished tour\n\nBump `version` when the product changes and every user who finished the old tour becomes eligible again — users mid-tour on the old version keep their progress.\n\n```ts\nconst tour = createTour({\n id: 'product-tour',\n steps: newSteps,\n version: 2, // stored records at version 1 no longer block the tour\n});\n```\n\n`reset()` clears the persisted record for the current user, which is what a \"Replay tour\" or \"Reset onboarding\" control should call before `start()`.\n\n## Driving a Refine Presentation\n\n`@vielzeug/refine` ships `ore-tour` (a dialog with step footer, progress, and focus handling) and `ore-spotlight` (a decorative cutout that dims everything but the target). A beacon handle satisfies `ore-tour`'s `TourController` shape, so one call wires both directions:\n\n```ts\nimport type { OreTourElement } from '@vielzeug/refine/tour';\n\nconst card = document.querySelector<OreTourElement>('#tour-card');\n\nconst tour = createTour({ id: 'product-tour', presentation: 'auto', steps });\n\n// bind() renders `current` into the card's props and routes the card's\n// next/back/skip/finish/dismiss actions back into the handle.\nconst unbind = card.bind(tour);\n\ntour.start();\n\n// On teardown:\nunbind();\ntour.dispose();\n```\n\n`presentation: 'auto'` maps to a popover on wide viewports and a bottom sheet on narrow ones; ore-tour makes that switch itself and freezes the choice while the card is open, so a mid-step resize never rebuilds the card. Beacon keeps `current.rect` fresh on scroll, resize, and layout changes, so the anchored card follows the layout with no app-side listeners.\n\nApps that render their own markup read `current` directly instead (see [Basic Usage](#basic-usage)).\n\n## Coachmarks\n\nA coachmark is one hint anchored to one target, shown the first time the target appears and dismissed permanently after. Use it for the \"explain this button once\" moment a tour would be too heavy for — or as the follow-up a tour hands off to.\n\n```ts\nimport { createCoachmark } from '@vielzeug/beacon';\n\nconst share = createCoachmark({\n id: 'share-hint',\n target: '[data-tour=\"share\"]',\n title: 'Share this board',\n description: 'Send a link that opens straight into this view.',\n storage,\n});\n\n// Render from `current` — the same single-step snapshot a tour publishes.\nshare.current.subscribe(() => console.log(share.current.value.step !== null, share.current.value.rect));\n\n// The target may never appear on this page; show() waits for it.\nshare.show();\n\n// \"Got it\" button:\nshare.dismiss(); // records it as seen; never shows again for this version\n```\n\n`hide()` closes the hint without recording it, so it can appear again later; `dismiss()` is permanent until `reset()` or a `version` bump.\n\n## Events and Analytics\n\n`tap()` observes everything the engine considers notable. It returns an unsubscribe function and accepts an `AbortSignal`, so one controller can tear down every subscription.\n\n```ts\nconst controller = new AbortController();\n\ntour.tap((event) => {\n switch (event.type) {\n case 'step:start':\n analytics.track('tour_step', { id: tour.id, step: event.step.id });\n break;\n case 'tour:complete':\n analytics.track('tour_complete', { id: tour.id, reason: event.reason });\n break;\n case 'target-missing':\n // A step's target never mounted: your selector drifted from the DOM.\n console.warn(event.message);\n break;\n case 'warning':\n console.warn(event.message, event.error);\n break;\n }\n}, { signal: controller.signal });\n```\n\n`target-missing` is the event to alert on: it means a step was skipped because its target did not appear within `targetTimeoutMs`, which is exactly how stale tours announce themselves.\n\n## Testing\n\nBeacon runs without a rendering layer, so plain DOM tests are enough. The default in-memory storage keeps each handle's progress isolated, and `jsdom`'s `getBoundingClientRect()` returns zero-sized rects, which is fine for step-order assertions.\n\n```ts\nimport { createTour } from '@vielzeug/beacon';\nimport { expect, test } from 'vitest';\n\ntest('walks steps in order and completes', () => {\n const tour = createTour({\n id: 'test-tour',\n steps: [{ id: 'a' }, { id: 'b' }],\n targetTimeoutMs: 0, // publish immediately even without targets\n });\n\n tour.start();\n expect(tour.current.value.step?.id).toBe('a');\n\n tour.next();\n expect(tour.current.value.step?.id).toBe('b');\n\n tour.next(); // past the last step finishes\n expect(tour.status.value).toBe('completed');\n});\n```\n\nFor persistence assertions, pass a `Map`-backed `BeaconStorage` and a second handle with the same `id` to check it resumes where the first stopped.\n\n## Framework Integration\n\nBeacon is framework-neutral: bridge the `Readable` snapshot with whatever primitive your framework already has.\n\n::: code-group\n\n```tsx [React]\nimport { useEffect, useState } from 'react';\nimport { createTour } from '@vielzeug/beacon';\n\nfunction useTourState(tour: ReturnType<typeof createTour>) {\n const [current, setCurrent] = useState(tour.current.value);\n useEffect(() => tour.current.subscribe(() => setCurrent(tour.current.value)), [tour]);\n return current;\n}\n```\n\n```vue [Vue 3]\n<script setup lang=\"ts\">\nimport { onScopeDispose, shallowRef } from 'vue';\nimport { createTour } from '@vielzeug/beacon';\n\nconst current = shallowRef(tour.current.value);\nconst unsubscribe = tour.current.subscribe(() => (current.value = tour.current.value));\nonScopeDispose(unsubscribe);\n</script>\n```\n\n```svelte [Svelte]\n<script lang=\"ts\">\n import { onDestroy } from 'svelte';\n import { readable } from 'svelte/store';\n\n const current = readable(tour.current.value, (set) => {\n set(tour.current.value);\n return tour.current.subscribe(() => set(tour.current.value));\n });\n onDestroy(() => tour.dispose());\n</script>\n```\n\n:::\n\n## Working with Other Vielzeug Libraries\n\n- **Refine**: `ore-tour` and `ore-spotlight` are the intended presentation layer; see [Driving a Refine Presentation](#driving-a-refine-presentation).\n- **Orbit**: `current.rect` matches orbit's `Rect` shape, so a custom card positions with `computePosition({ getBoundingClientRect: () => current.rect ?? ZERO_RECT }, floatingEl, middleware)`.\n- **Vault**: any vault store becomes a `BeaconStorage` with a six-line bridge (see the `BeaconStorage` docs in [API Reference](./api.md)).\n- **Ripple**: every handle exposes ripple `Readable`s; `createRipple().isBatchingObservable` semantics apply when you subscribe from effects.\n\n## Best Practices\n\n- Give every tour and coachmark a stable `id` — it is the storage key; treat renames as data migrations.\n- Keep tour configs in their own modules as plain data checked with `satisfies BeaconTourConfig`, so steps stay reviewable data, not component code.\n- Prefer `data-tour=\"...\"` attributes as target selectors: they survive class renames and read as intent.\n- Set a finite `targetTimeoutMs` in production and alert on `target-missing` to catch stale selectors.\n- Call `reset()` before `start()` for replay controls; never delete storage keys by hand.\n- Bump `version` when a tour's meaning changes, not when its copy is typo-fixed.\n- Dispose handles on teardown; `tap()` subscriptions with a shared `AbortSignal` clean up in one call.\n",
|
|
7
|
+
"examples": "---\ntitle: 'Beacon: Examples'\ndescription: Practical examples and recipes for @vielzeug/beacon.\n---\n\n## Examples\n\n- [Getting Started](./examples/getting-started.md)\n"
|
|
8
|
+
},
|
|
9
|
+
"examples": [
|
|
10
|
+
{
|
|
11
|
+
"id": "coachmark",
|
|
12
|
+
"code": "import { createCoachmark } from '@vielzeug/beacon'\n\nconst button = document.createElement('button')\nbutton.textContent = 'Share'\nbutton.dataset.tour = 'share'\ndocument.body.appendChild(button)\n\nconst hint = createCoachmark({\n autoShow: false,\n description: 'Send a link that opens straight into this view.',\n id: 'share-hint',\n target: '[data-tour=\"share\"]',\n title: 'Share this board',\n})\n\nhint.tap((event) => console.log('event:', event.type))\n\n// current is the single-step snapshot: step is the hint while visible, null while hidden.\nhint.current.subscribe(() => console.log('visible:', hint.current.value.step !== null))\n\nhint.show()\nconsole.log('rect anchored to button:', JSON.stringify(hint.current.value.rect))\n\n// hide() can come back; dismiss() records it as seen.\nhint.hide()\nhint.show()\nhint.dismiss()\n\n// Once dismissed, show() is a no-op until reset() or a version bump.\nhint.show()\nconsole.log('eligible after dismiss:', hint.eligible.value)\n\nhint.reset()\nconsole.log('eligible after reset:', hint.eligible.value)",
|
|
13
|
+
"name": "Coachmark - Show Once, Dismiss, Reset"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"id": "persist-progress",
|
|
17
|
+
"code": "import { createTour } from '@vielzeug/beacon'\n\n// Any key-value object is a BeaconStorage; keys arrive namespaced.\nconst db = new Map()\nconst storage = {\n get: (key) => (db.has(key) ? JSON.parse(db.get(key)) : null),\n remove: (key) => db.delete(key),\n set: (key, value) => db.set(key, JSON.stringify(value)),\n}\n\nconst steps = [\n { id: 'a', title: 'First' },\n { id: 'b', title: 'Second' },\n { id: 'c', title: 'Third' },\n]\n\nconst first = createTour({ id: 'resume-demo', steps, storage, targetTimeoutMs: 0 })\nfirst.start()\nfirst.next()\nconsole.log('First session stopped at step index:', first.current.value.stepIndex)\nfirst.pause() // keeps progress so the next handle resumes\n\n// A brand-new handle (next page load) resumes from the stored record.\nconst second = createTour({ id: 'resume-demo', steps, storage, targetTimeoutMs: 0 })\nconsole.log('Resumed status:', second.status.value)\nsecond.start()\nconsole.log('Resumed at step:', second.current.value.step.title)\n\nsecond.finish()\nconsole.log('After finish:', second.status.value)\n\n// start() is a no-op for completed tours:\nsecond.start()\nconsole.log('Still:', second.status.value)\n\n// reset() makes it eligible again (what a \"Replay tour\" button calls):\nsecond.reset()\nsecond.start()\nconsole.log('After reset + start, step:', second.current.value.step.title)",
|
|
18
|
+
"name": "Persist Progress - Resume, Finish, Replay"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "tour-basics",
|
|
22
|
+
"code": "import { createTour } from '@vielzeug/beacon'\n\n// A tiny stage so the tour has something real to anchor to.\nconst stage = document.createElement('div')\nstage.innerHTML = '<button data-tour=\"nav\" style=\"margin:24px\">Navigation</button><button data-tour=\"share\" style=\"margin:24px\">Share</button>'\ndocument.body.appendChild(stage)\n\nconst tour = createTour({\n autoScroll: false, // jsdom has no real scrolling\n id: 'repl-tour',\n steps: [\n { id: 'welcome', title: 'Welcome' },\n { id: 'nav', target: '[data-tour=\"nav\"]', title: 'Navigation' },\n { id: 'share', target: '[data-tour=\"share\"]', title: 'Share' },\n ],\n targetTimeoutMs: 0,\n})\n\n// Readable<T>: the listener takes no arguments, read .value inside.\ntour.current.subscribe(() => {\n const current = tour.current.value\n if (!current.step) return\n console.log('Step ' + (current.stepIndex + 1) + ' of ' + current.totalSteps + ': ' + current.step.title)\n console.log(' anchor rect:', current.rect === null ? 'null (centered)' : JSON.stringify(current.rect))\n})\n\ntour.tap((event) => {\n if (event.type === 'tour:complete') console.log('Tour complete, reason:', event.reason)\n})\n\ntour.start()\ntour.next()\ntour.next()\ntour.next() // past the last step finishes the tour\n\nconsole.log('status:', tour.status.value)",
|
|
23
|
+
"name": "Tour Basics - Steps, Rects, Completion"
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"typeSignatures": {
|
|
27
|
+
"createCoachmark": "export { createCoachmark } from './coachmark';",
|
|
28
|
+
"BeaconConfigError": "export { BeaconConfigError, BeaconDisposedError, BeaconError } from './errors';",
|
|
29
|
+
"BeaconDisposedError": "export { BeaconConfigError, BeaconDisposedError, BeaconError } from './errors';",
|
|
30
|
+
"BeaconError": "export { BeaconConfigError, BeaconDisposedError, BeaconError } from './errors';",
|
|
31
|
+
"resolveTarget": "export { resolveTarget } from './target';",
|
|
32
|
+
"createTour": "export { createTour } from './tour';",
|
|
33
|
+
"BeaconBaseEvent": "export type {\n BeaconBaseEvent,\n BeaconCoachmarkConfig,\n BeaconCoachmarkEvent,\n BeaconCoachmarkHandle,\n BeaconCurrentStep,\n BeaconPlacement,\n BeaconPresentation,\n BeaconProgress,\n BeaconRect,\n BeaconStatus,\n BeaconStep,\n BeaconStorage,\n BeaconTarget,\n BeaconTargetResolver,\n BeaconTourConfig,\n BeaconTourEvent,\n BeaconTourHandle,\n} from './types';",
|
|
34
|
+
"BeaconCoachmarkConfig": "export type {\n BeaconBaseEvent,\n BeaconCoachmarkConfig,\n BeaconCoachmarkEvent,\n BeaconCoachmarkHandle,\n BeaconCurrentStep,\n BeaconPlacement,\n BeaconPresentation,\n BeaconProgress,\n BeaconRect,\n BeaconStatus,\n BeaconStep,\n BeaconStorage,\n BeaconTarget,\n BeaconTargetResolver,\n BeaconTourConfig,\n BeaconTourEvent,\n BeaconTourHandle,\n} from './types';",
|
|
35
|
+
"BeaconCoachmarkEvent": "export type {\n BeaconBaseEvent,\n BeaconCoachmarkConfig,\n BeaconCoachmarkEvent,\n BeaconCoachmarkHandle,\n BeaconCurrentStep,\n BeaconPlacement,\n BeaconPresentation,\n BeaconProgress,\n BeaconRect,\n BeaconStatus,\n BeaconStep,\n BeaconStorage,\n BeaconTarget,\n BeaconTargetResolver,\n BeaconTourConfig,\n BeaconTourEvent,\n BeaconTourHandle,\n} from './types';",
|
|
36
|
+
"BeaconCoachmarkHandle": "export type {\n BeaconBaseEvent,\n BeaconCoachmarkConfig,\n BeaconCoachmarkEvent,\n BeaconCoachmarkHandle,\n BeaconCurrentStep,\n BeaconPlacement,\n BeaconPresentation,\n BeaconProgress,\n BeaconRect,\n BeaconStatus,\n BeaconStep,\n BeaconStorage,\n BeaconTarget,\n BeaconTargetResolver,\n BeaconTourConfig,\n BeaconTourEvent,\n BeaconTourHandle,\n} from './types';",
|
|
37
|
+
"BeaconCurrentStep": "export type {\n BeaconBaseEvent,\n BeaconCoachmarkConfig,\n BeaconCoachmarkEvent,\n BeaconCoachmarkHandle,\n BeaconCurrentStep,\n BeaconPlacement,\n BeaconPresentation,\n BeaconProgress,\n BeaconRect,\n BeaconStatus,\n BeaconStep,\n BeaconStorage,\n BeaconTarget,\n BeaconTargetResolver,\n BeaconTourConfig,\n BeaconTourEvent,\n BeaconTourHandle,\n} from './types';",
|
|
38
|
+
"BeaconPlacement": "export type {\n BeaconBaseEvent,\n BeaconCoachmarkConfig,\n BeaconCoachmarkEvent,\n BeaconCoachmarkHandle,\n BeaconCurrentStep,\n BeaconPlacement,\n BeaconPresentation,\n BeaconProgress,\n BeaconRect,\n BeaconStatus,\n BeaconStep,\n BeaconStorage,\n BeaconTarget,\n BeaconTargetResolver,\n BeaconTourConfig,\n BeaconTourEvent,\n BeaconTourHandle,\n} from './types';",
|
|
39
|
+
"BeaconPresentation": "export type {\n BeaconBaseEvent,\n BeaconCoachmarkConfig,\n BeaconCoachmarkEvent,\n BeaconCoachmarkHandle,\n BeaconCurrentStep,\n BeaconPlacement,\n BeaconPresentation,\n BeaconProgress,\n BeaconRect,\n BeaconStatus,\n BeaconStep,\n BeaconStorage,\n BeaconTarget,\n BeaconTargetResolver,\n BeaconTourConfig,\n BeaconTourEvent,\n BeaconTourHandle,\n} from './types';",
|
|
40
|
+
"BeaconProgress": "export type {\n BeaconBaseEvent,\n BeaconCoachmarkConfig,\n BeaconCoachmarkEvent,\n BeaconCoachmarkHandle,\n BeaconCurrentStep,\n BeaconPlacement,\n BeaconPresentation,\n BeaconProgress,\n BeaconRect,\n BeaconStatus,\n BeaconStep,\n BeaconStorage,\n BeaconTarget,\n BeaconTargetResolver,\n BeaconTourConfig,\n BeaconTourEvent,\n BeaconTourHandle,\n} from './types';",
|
|
41
|
+
"BeaconRect": "export type {\n BeaconBaseEvent,\n BeaconCoachmarkConfig,\n BeaconCoachmarkEvent,\n BeaconCoachmarkHandle,\n BeaconCurrentStep,\n BeaconPlacement,\n BeaconPresentation,\n BeaconProgress,\n BeaconRect,\n BeaconStatus,\n BeaconStep,\n BeaconStorage,\n BeaconTarget,\n BeaconTargetResolver,\n BeaconTourConfig,\n BeaconTourEvent,\n BeaconTourHandle,\n} from './types';",
|
|
42
|
+
"BeaconStatus": "export type {\n BeaconBaseEvent,\n BeaconCoachmarkConfig,\n BeaconCoachmarkEvent,\n BeaconCoachmarkHandle,\n BeaconCurrentStep,\n BeaconPlacement,\n BeaconPresentation,\n BeaconProgress,\n BeaconRect,\n BeaconStatus,\n BeaconStep,\n BeaconStorage,\n BeaconTarget,\n BeaconTargetResolver,\n BeaconTourConfig,\n BeaconTourEvent,\n BeaconTourHandle,\n} from './types';",
|
|
43
|
+
"BeaconStep": "export type {\n BeaconBaseEvent,\n BeaconCoachmarkConfig,\n BeaconCoachmarkEvent,\n BeaconCoachmarkHandle,\n BeaconCurrentStep,\n BeaconPlacement,\n BeaconPresentation,\n BeaconProgress,\n BeaconRect,\n BeaconStatus,\n BeaconStep,\n BeaconStorage,\n BeaconTarget,\n BeaconTargetResolver,\n BeaconTourConfig,\n BeaconTourEvent,\n BeaconTourHandle,\n} from './types';",
|
|
44
|
+
"BeaconStorage": "export type {\n BeaconBaseEvent,\n BeaconCoachmarkConfig,\n BeaconCoachmarkEvent,\n BeaconCoachmarkHandle,\n BeaconCurrentStep,\n BeaconPlacement,\n BeaconPresentation,\n BeaconProgress,\n BeaconRect,\n BeaconStatus,\n BeaconStep,\n BeaconStorage,\n BeaconTarget,\n BeaconTargetResolver,\n BeaconTourConfig,\n BeaconTourEvent,\n BeaconTourHandle,\n} from './types';",
|
|
45
|
+
"BeaconTarget": "export type {\n BeaconBaseEvent,\n BeaconCoachmarkConfig,\n BeaconCoachmarkEvent,\n BeaconCoachmarkHandle,\n BeaconCurrentStep,\n BeaconPlacement,\n BeaconPresentation,\n BeaconProgress,\n BeaconRect,\n BeaconStatus,\n BeaconStep,\n BeaconStorage,\n BeaconTarget,\n BeaconTargetResolver,\n BeaconTourConfig,\n BeaconTourEvent,\n BeaconTourHandle,\n} from './types';",
|
|
46
|
+
"BeaconTargetResolver": "export type {\n BeaconBaseEvent,\n BeaconCoachmarkConfig,\n BeaconCoachmarkEvent,\n BeaconCoachmarkHandle,\n BeaconCurrentStep,\n BeaconPlacement,\n BeaconPresentation,\n BeaconProgress,\n BeaconRect,\n BeaconStatus,\n BeaconStep,\n BeaconStorage,\n BeaconTarget,\n BeaconTargetResolver,\n BeaconTourConfig,\n BeaconTourEvent,\n BeaconTourHandle,\n} from './types';",
|
|
47
|
+
"BeaconTourConfig": "export type {\n BeaconBaseEvent,\n BeaconCoachmarkConfig,\n BeaconCoachmarkEvent,\n BeaconCoachmarkHandle,\n BeaconCurrentStep,\n BeaconPlacement,\n BeaconPresentation,\n BeaconProgress,\n BeaconRect,\n BeaconStatus,\n BeaconStep,\n BeaconStorage,\n BeaconTarget,\n BeaconTargetResolver,\n BeaconTourConfig,\n BeaconTourEvent,\n BeaconTourHandle,\n} from './types';",
|
|
48
|
+
"BeaconTourEvent": "export type {\n BeaconBaseEvent,\n BeaconCoachmarkConfig,\n BeaconCoachmarkEvent,\n BeaconCoachmarkHandle,\n BeaconCurrentStep,\n BeaconPlacement,\n BeaconPresentation,\n BeaconProgress,\n BeaconRect,\n BeaconStatus,\n BeaconStep,\n BeaconStorage,\n BeaconTarget,\n BeaconTargetResolver,\n BeaconTourConfig,\n BeaconTourEvent,\n BeaconTourHandle,\n} from './types';",
|
|
49
|
+
"BeaconTourHandle": "export type {\n BeaconBaseEvent,\n BeaconCoachmarkConfig,\n BeaconCoachmarkEvent,\n BeaconCoachmarkHandle,\n BeaconCurrentStep,\n BeaconPlacement,\n BeaconPresentation,\n BeaconProgress,\n BeaconRect,\n BeaconStatus,\n BeaconStep,\n BeaconStorage,\n BeaconTarget,\n BeaconTargetResolver,\n BeaconTourConfig,\n BeaconTourEvent,\n BeaconTourHandle,\n} from './types';"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"apiSource": "/**\n * Refine components register through their explicit component entry points.\n *\n * Keeping the package root free of registration side effects makes dependency\n * ownership and bundle contents obvious to application code.\n */\n\nexport { RefineError } from './errors';\nexport type { IconAttributes, IconDefinition, IconElement } from './icons';\nexport { registerIcons } from './icons';\nexport { eventFieldChecked, eventFieldValue } from './inputs/shared/native-field-event';\n",
|
|
3
3
|
"docs": {
|
|
4
|
-
"index": "---\ntitle: 'Refine: Web component library'\ndescription: Accessible, themeable web components built with Ore for framework and vanilla DOM apps.\npackage: refine\ncategory: ui-components\nkeywords: [web-components, accessible, themeable, ui, components, design-system]\nrelated: [ore, orbit, forge, keymap]\nexports:\n [\n ore-accordion,\n ore-accordion-item,\n ore-alert,\n ore-async,\n ore-avatar,\n ore-avatar-group,\n ore-badge,\n ore-box,\n ore-breadcrumb,\n ore-breadcrumb-item,\n ore-button,\n ore-button-group,\n ore-calendar,\n ore-card,\n ore-carousel,\n ore-carousel-slide,\n ore-chat-message,\n ore-checkbox,\n ore-checkbox-group,\n ore-chip,\n ore-code-window,\n ore-column,\n ore-combobox,\n ore-combobox-option,\n ore-command-palette,\n ore-command-palette-item,\n ore-cookie-banner,\n ore-copy-command,\n ore-counter,\n ore-datagrid,\n ore-date-picker,\n ore-dialog,\n ore-drawer,\n ore-file-input,\n ore-grid,\n ore-grid-item,\n ore-icon,\n ore-input,\n ore-keyboard-key,\n ore-keyboard-shortcut,\n ore-list,\n ore-list-item,\n ore-marquee,\n ore-menu,\n ore-menu-item,\n ore-menu-separator,\n ore-message-composer,\n ore-navbar,\n ore-navbar-item,\n ore-navigation-menu,\n ore-navigation-menu-item,\n ore-navigation-menu-panel,\n ore-number-input,\n ore-otp-input,\n ore-pagination,\n ore-password-strength,\n ore-popover,\n ore-progress,\n ore-qr-code,\n ore-qr-scanner,\n ore-query-bar,\n ore-radio,\n ore-radio-group,\n ore-rating,\n ore-select,\n ore-separator,\n ore-sidebar,\n ore-sidebar-group,\n ore-sidebar-item,\n ore-skeleton,\n ore-slider,\n ore-speech-player,\n ore-speed-reading,\n ore-stats,\n ore-step,\n ore-stepper,\n ore-switch,\n ore-tab-item,\n ore-tab-panel,\n ore-table,\n ore-tabs,\n ore-td,\n ore-text,\n ore-th,\n ore-tr,\n ore-textarea,\n ore-time-picker,\n ore-toast,\n ore-tooltip,\n ore-typing-indicator,\n ]\nenvironments: [browser]\n---\n\n<!-- markdownlint-disable MD025 MD033 MD060 -->\n\n<PackageHero package=\"refine\" />\n\n## Why Refine?\n\nEvery project needs UI primitives. Refine provides accessible web components that work natively anywhere HTML is rendered, no framework required.\n\n```html\n<!-- Before: roll your own button with ARIA -->\n<button class=\"btn btn-primary\" role=\"button\" aria-pressed=\"false\" tabindex=\"0\">\n <span class=\"btn-spinner\" aria-hidden=\"true\"></span>\n Save\n</button>\n\n<!-- After: Refine -->\n<ore-button variant=\"solid\" color=\"primary\" loading>Save</ore-button>\n```\n\n| Feature | Refine | Shoelace | Material Web |\n| ------------------ | ------------------------------------------- | ------------------------------------------ | ------------------------------------------ |\n| Bundle size | <PackageInfo package=\"refine\" type=\"size\" /> | ~145 kB | ~200 kB |\n| Built with | Ore | Lit | Lit |\n| Runtime dependencies | Vielzeug UI primitives; Lucide only for the opt-in full-library registrar | Lit + utilities | Lit + Material foundations |\n| Accessible | WCAG AA | WCAG AA | WCAG AA |\n| Framework agnostic | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> |\n\n<div class=\"decision-callout\">\n\n**Use Refine when** you want accessible web components that match the Vielzeug design system without a heavy framework dependency.\n\n**Consider Shoelace or Material Web** if your team is already standardized on those ecosystems and you need their established component catalogs.\n\n</div>\n\n## Installation\n\n::: code-group\n\n```sh [pnpm]\npnpm add @vielzeug/refine\n```\n\n```sh [npm]\nnpm install @vielzeug/refine\n```\n\n```sh [yarn]\nyarn add @vielzeug/refine\n```\n\n:::\n\n## Quick Start\n\n```ts\n// 1. Import global styles once\nimport '@vielzeug/refine/fouc.css'; // Hide unupgraded custom elements until first paint\nimport '@vielzeug/refine/tokens.css'; // Tokens, animations, cascade layers\n\n// 2. Register only the elements you need\nimport '@vielzeug/refine/button';\nimport '@vielzeug/refine/input';\nimport '@vielzeug/refine/card';\n```\n\n```html\n<ore-button variant=\"solid\" color=\"primary\">Save</ore-button>\n<ore-input label=\"Email\" type=\"email\" required></ore-input>\n<ore-card padding=\"lg\">\n <span slot=\"header\">Account</span>\n <p>Card content goes here.</p>\n</ore-card>\n```\n\n```ts\n```\n\n### CDN / Vanilla HTML\n\nUse the self-contained IIFE bundle to load Refine directly from a CDN in any HTML page: no build step required:\n\n```html\n<!-- 1. Styles -->\n<link rel=\"stylesheet\" href=\"https://unpkg.com/@vielzeug/refine/dist/styles/fouc.css\" />\n<link rel=\"stylesheet\" href=\"https://unpkg.com/@vielzeug/refine/dist/styles/tokens.css\" />\n\n<!-- 2. All components (IIFE: registers global Refine namespace) -->\n<script src=\"https://unpkg.com/@vielzeug/refine/dist/refine.iife.js\"></script>\n```\n\nFor bundler-based projects that still want a CDN URL, use the ESM bundle via an import map:\n\n```html\n<script type=\"importmap\">\n {\n \"imports\": {\n \"@vielzeug/refine\": \"https://esm.sh/@vielzeug/refine\",\n \"@vielzeug/refine/button\": \"https://esm.sh/@vielzeug/refine/button\",\n \"@vielzeug/refine/input\": \"https://esm.sh/@vielzeug/refine/input\"\n }\n }\n</script>\n\n<script type=\"module\">\n import '@vielzeug/refine/button';\n import '@vielzeug/refine/input';\n</script>\n```\n\n### Package Entry Points\n\n| Import | Purpose |\n| ------------------------ | ----------------------------------------- |\n| `@vielzeug/refine/fouc.css` | FOUC suppression for unupgraded custom elements |\n| `@vielzeug/refine/tokens.css` | Global design tokens and cascade layers |\n| `@vielzeug/refine/styles/preflight.css` | Optional browser-default reset (includes FOUC suppression) |\n\nComponent registration happens through side-effect imports such as `@vielzeug/refine/button` and `@vielzeug/refine/dialog`.\n\n### Components\n\n**Content:** `ore-avatar`, `ore-avatar-group`, `ore-breadcrumb`, `ore-card`, `ore-carousel`, `ore-carousel-slide`, `ore-chat-message`, `ore-icon`, `ore-keyboard-key`, `ore-keyboard-shortcut`, `ore-list`, `ore-list-item`, `ore-marquee`, `ore-pagination`, `ore-qr-code`, `ore-separator`, `ore-speed-reading`, `ore-stats`, `ore-step`, `ore-stepper`, `ore-table`, `ore-text`\n\n**Disclosure:** `ore-accordion`, `ore-accordion-item`, `ore-tabs`, `ore-tab-item`, `ore-tab-panel`\n\n**Feedback:** `ore-alert`, `ore-async`, `ore-badge`, `ore-chip`, `ore-cookie-banner`, `ore-password-strength`, `ore-progress`, `ore-skeleton`, `ore-toast`, `ore-typing-indicator`\n\n**Inputs:** `ore-button`, `ore-button-group`, `ore-calendar`, `ore-checkbox`, `ore-checkbox-group`, `ore-column`, `ore-combobox`, `ore-counter`, `ore-datagrid`, `ore-date-picker`, `ore-file-input`, `ore-input`, `ore-message-composer`, `ore-number-input`, `ore-otp-input`, `ore-qr-scanner`, `ore-query-bar`, `ore-radio`, `ore-radio-group`, `ore-rating`, `ore-select`, `ore-slider`, `ore-switch`, `ore-textarea`, `ore-time-picker`\n\n**Layout:** `ore-box`, `ore-grid`, `ore-grid-item`, `ore-navbar`, `ore-sidebar`\n\n**Overlay:** `ore-command-palette`, `ore-command-palette-item`, `ore-dialog`, `ore-drawer`, `ore-menu`, `ore-popover`, `ore-tooltip`\n\n## Component portfolio\n\nRefine keeps one public package and classifies components by composition level rather than stability. All listed components remain supported and tested.\n\n- **Foundations**: buttons, inputs, selection controls, disclosure, dialogs, menus, tooltips, layout primitives, and feedback indicators. Start here when building an application-specific interface.\n- **Composed patterns**: data grids, command palettes, navigation menus, steppers, calendars, date/time pickers, file inputs, and message composers. These package recurring accessibility and interaction behavior that is costly to rebuild correctly.\n- **Presentation patterns**: cards, chat messages, code windows, stats, marquees, avatars, badges, and skeletons. Use these when their product vocabulary matches the application; prefer foundations when it does not.\n\nClassification guides discovery and bundle selection. It is not a reason to remove working components or move them into unstable packages; per-component subpath imports remain the tree-shaking boundary.\n\n## Features\n\n<div class=\"features-grid\">\n\n- **Accessible**: keyboard navigation, ARIA wiring, and focus management across interactive components\n- **Themeable**: global tokens plus component-level CSS custom properties\n- **Framework agnostic**: works anywhere HTML can be rendered\n- **Tree-shakeable**: import only the component entry points you register\n- **Comprehensive surface**: inputs, content, disclosure, feedback, layout, and overlay primitives\n- **Native form integration**: form-associated controls submit values, validate supported required constraints, and reset with their form\n- **Typed framework declarations**: DOM, React, and Vue tags derive from the component property contracts\n\n</div>\n\n### Prerequisites\n\n- Browser runtime with Custom Elements support.\n- Import `@vielzeug/refine/fouc.css` and `@vielzeug/refine/tokens.css` before rendering components.\n- For SSR, render placeholders server-side and hydrate components only on the client.\n\n## Documentation\n\n<div class=\"doc-links\">\n\n- [Usage Guide](./usage.md)\n- [API Reference](./api.md)\n- [Migration Guide](./migration.md)\n\n</div>\n\n## See Also\n\n<div class=\"see-also\">\n\n- [Ore](/ore/): Web component runtime that powers Refine\n- [Orbit](/orbit/): Floating UI positioning used in Refine's overlays\n- [Forge](/forge/): Form state management for use with Refine inputs\n- [Keymap](/keymap/): Keyboard shortcut manager that powers the command palette's global trigger\n\n</div>\n\n<!-- markdownlint-enable MD025 MD033 MD060 -->\n",
|
|
5
|
-
"api": "---\ntitle: 'Refine: API Reference'\ndescription: Published component, stylesheet, framework type, and error entry points for @vielzeug/refine.\n---\n\n[[toc]]\n\n## API Overview\n\n| Symbol | Purpose | Execution mode | Common gotcha |\n| --- | --- | --- | --- |\n| `@vielzeug/refine/<component>` | Register one custom element and export its public types | Sync | Importing a type alone does not register the element |\n| `@vielzeug/refine/tokens.css` | Load required theme tokens, animations, and cascade layers | CSS | Import once before components render |\n| `@vielzeug/refine/fouc.css` | Hide unregistered `ore-*` elements until upgrade | CSS | Load before first paint |\n| `@vielzeug/refine/styles/preflight.css` | Apply the optional browser reset | CSS | The reset affects global elements |\n| `@vielzeug/refine/frameworks/elements` | Register typed DOM tag mappings | Types only | Import for side effects in TypeScript |\n| `@vielzeug/refine/frameworks/react` | Register typed React JSX elements | Types only | Does not provide runtime wrappers |\n| `@vielzeug/refine/frameworks/vue` | Register typed Vue global components | Types only | Does not install a Vue plugin |\n| `@vielzeug/refine/theme` | `createThemeController()`: light/dark/system document theme | Sync | Does not persist the preference or set an accent color |\n| `eventFieldValue()` / `eventFieldChecked()` | Read a form control's value/checked from `event.currentTarget` | Sync | Exported from the package root; guards non-field targets |\n| `RefineError` | Base class for package-defined public errors | Sync | Component configuration warnings do not throw this error |\n\n## Package Entry Point\n\n| Import | Purpose |\n| --- | --- |\n| `@vielzeug/refine` | Export `registerIcons`, icon definition types, `RefineError`, and the field readers without registering components |\n| `@vielzeug/refine/<component>` | Register one component and export its tag constant, props, events, and related types |\n| `@vielzeug/refine/theme` | `createThemeController()` and its theme types |\n| `@vielzeug/refine/tokens.css` | Required global design contract |\n| `@vielzeug/refine/fouc.css` | Optional pre-upgrade visibility rule |\n| `@vielzeug/refine/styles/*` | Focused theme, animation, layer, and preflight stylesheets |\n| `@vielzeug/refine/frameworks/*` | TypeScript augmentations for DOM, React, and Vue |\n\n## Icon Registration and Adapters\n\n`registerIcons(icons: Readonly<Record<string, IconDefinition>>): void` is available from `@vielzeug/refine/icons`, `@vielzeug/refine`, and `@vielzeug/refine/icon`. The common `icons` entry and root remain free of component registration side effects.\n\n`IconDefinition`, `IconElement`, and `IconAttributes` are exported from all three entries. Shared definition and registration modules live in `src/icons/`, alongside adapters and internal SVG helpers/defaults. Definitions contain a numeric `viewBox` tuple, optional presentation attributes, and recursive SVG children; they do not require a provider identity. Invalid registrations throw the root-exported `RefineError` atomically.\n\n| Entry | Export | Purpose |\n| --- | --- | --- |\n| `@vielzeug/refine/icons` | `registerIcons`, `IconDefinition`, `IconElement`, `IconAttributes` | Common library-neutral registration and definition API |\n| `@vielzeug/refine/icons/lucide` | `lucideIcons`, `LucideIconData` | Pure adapter for selected structural Lucide SVG data |\n| `@vielzeug/refine/icons/lucide/all` | Side-effect registration | Explicitly register the full installed Lucide library |\n| `@vielzeug/refine/icons/heroicons` | `heroiconsIcons`, `IconifyIcon` | Convert a caller-named map of individual Heroicons Iconify data |\n| `@vielzeug/refine/icons/tabler` | `tablerIcons`, `IconifyIcon` | Convert a caller-named map of individual Tabler Iconify data |\n| `@vielzeug/refine/icons/mdi` | `mdiIcons`, `IconifyIcon` | Convert a caller-named map of individual MDI Iconify data |\n| `@vielzeug/refine/icons/iconify` | `iconifyIcon`, `iconifyIcons` | Convert individual Iconify data or selected/complete collections |\n| `@vielzeug/refine/icons/iconify` | `IconifyIcon`, `IconifyAlias`, `IconifyCollection`, `IconifyTransform` | Structural data types; no Iconify dependency required |\n\nLucide is an optional peer, not a mandatory dependency. Only `icons/lucide/all` requires an installed `lucide` package; consumers selecting that entry declare it themselves. The structural `icons/lucide` adapter accepts supplied data without importing Lucide.\n\nThe standalone IIFE exposes `registerIcons`, `lucideIcons`, `heroiconsIcons`, `tablerIcons`, `mdiIcons`, `iconifyIcon`, and `iconifyIcons` under `Refine.components.content`. Neither the IIFE nor component entries import Lucide. Iconify conversion drops provider `class` metadata, validates finite numeric offsets and positive finite dimensions before transforms, and retains the strict SVG allowlist. See the [icon reference](./components/icon.md) for presentation overrides, local SVG ID rewriting, supported families, and licenses.\n\nThe three family adapters share this signature:\n\n```ts\nheroiconsIcons(icons: Readonly<Record<string, IconifyIcon>>): Record<string, IconDefinition>;\ntablerIcons(icons: Readonly<Record<string, IconifyIcon>>): Record<string, IconDefinition>;\nmdiIcons(icons: Readonly<Record<string, IconifyIcon>>): Record<string, IconDefinition>;\n```\n\nEach accepts individual Iconify SVG data, preserves input keys without adding a namespace, and returns definitions without registering them. They require `DOMParser`, preserve each icon's geometry/presentation/transforms, and throw `RefineError` for invalid Iconify data. They do not accept React/Vue components, collection JSON, or raw SVG path strings. Use `iconifyIcons(collection, names?)` for collections and alias resolution.\n\n## Styles\n\n```ts\nimport '@vielzeug/refine/fouc.css';\nimport '@vielzeug/refine/tokens.css';\nimport '@vielzeug/refine/styles/preflight.css';\n```\n\n`tokens.css` is required. It defines tokens, animations, and cascade-layer order without resetting global elements. `preflight.css` is optional and includes FOUC suppression.\n\n| Import path | Purpose |\n| --- | --- |\n| `@vielzeug/refine/fouc.css` | FOUC suppression for unupgraded custom elements |\n| `@vielzeug/refine/tokens.css` | Tokens, animation helpers, and cascade layers |\n| `@vielzeug/refine/styles/theme.css` | Theme token declarations |\n| `@vielzeug/refine/styles/animation.css` | Animation helpers |\n| `@vielzeug/refine/styles/layers.css` | Cascade layer declarations |\n| `@vielzeug/refine/styles/preflight.css` | Optional browser reset and FOUC suppression |\n\n## Components\n\nEach component uses one registration and type entry point:\n\n```ts\nimport '@vielzeug/refine/button';\nimport type { OreButtonProps } from '@vielzeug/refine/button';\n```\n\n| Area | Components |\n| --- | --- |\n| Content | `avatar`, `avatar-group`, `breadcrumb`, `card`, `carousel`, `chat-message`, `code-window`, `copy-command`, `icon`, `keyboard-key`, `list`, `list-item`, `marquee`, `pagination`, `separator`, `speech-player`, `speed-reading`, `stats`, `step`, `stepper`, `table`, `text` |\n| Disclosure | `accordion`, `accordion-item`, `tabs`, `tab-item`, `tab-panel` |\n| Feedback | `alert`, `async`, `badge`, `chip`, `cookie-banner`, `password-strength`, `progress`, `skeleton`, `toast`, `typing-indicator` |\n| Inputs | `button`, `button-group`, `calendar`, `checkbox`, `checkbox-group`, `combobox`, `counter`, `datagrid`, `date-picker`, `file-input`, `input`, `message-composer`, `number-input`, `otp-input`, `query-bar`, `radio`, `radio-group`, `rating`, `select`, `slider`, `switch`, `textarea`, `time-picker` |\n| Layout | `box`, `grid`, `grid-item`, `navbar`, `sidebar` |\n| Overlays | `command-palette`, `dialog`, `drawer`, `menu`, `navigation-menu`, `popover`, `tooltip` |\n\nEach component page lists its attributes, JavaScript properties, events, slots, parts, and CSS custom properties.\n\n## Framework Types\n\n```ts\nimport type {} from '@vielzeug/refine/frameworks/elements';\nimport type {} from '@vielzeug/refine/frameworks/react';\nimport type {} from '@vielzeug/refine/frameworks/vue';\n```\n\nThe declarations cover every supported tag and derive component properties from Refine's authoritative `HTMLElementTagNameMap`. They provide types only; component registration still uses component subpaths.\n\n## Events and Form Controls\n\nForm controls expose `.value` or `.checked` and dispatch standard `input` and `change` events. Read the property from `event.currentTarget`.\n\nFinite-choice properties use Ore's `prop.oneOf()` parsing: values outside a component's documented options resolve to\nthe declared default, or `undefined` when no default is declared.\n\nForm-associated controls participate in native form submission, validity checks, and reset. A named control's value\nis available through `new FormData(form)`, and supported `required` constraints affect `form.checkValidity()`,\n`form.reportValidity()`, and submit blocking. The `error` property renders and announces feedback; it does not by\nitself set native constraint validity.\n\nStateful overlays expose controlled `open`, optional `default-open`, and an `open-change` custom event with `{ open, reason }` detail. Component pages define additional detail fields and reasons.\n\n## Utilities\n\n### `createThemeController(options?)`\n\n```ts\nimport { createThemeController } from '@vielzeug/refine/theme';\n\nconst theme = createThemeController(); // starts in 'system' mode\ntheme.setPreference('dark');\ntheme.dispose();\n```\n\nOwns the light/dark/system theme for the document: a reactive `preference` signal, the effective `resolved` mode (`'dark' | 'light'` after resolving `system` against the OS), and the DOM application `styles/theme.css` expects: the `.dark` class plus the `color-scheme` property on the root element, so every `light-dark()` token resolves to the right branch. While in `system` mode the controller tracks the OS `prefers-color-scheme` via sentinel's `createMediaQuery`.\n\nThe controller does **not** persist the preference or expose an accent color: those are consumer concerns (wire `watch(controller.preference, save)` for persistence; set `--color-primary-hue` yourself). Options: `initial` (starting preference, default `'system'`), `root` (element carrying the classes, default `document.documentElement`), `target` (window whose `matchMedia` backs `system`). The handle is disposable: `dispose()`, `disposed`, `disposalSignal`, `[Symbol.dispose]`.\n\n### `eventFieldValue(event)` / `eventFieldChecked(event)`\n\n```ts\nimport { eventFieldChecked, eventFieldValue } from '@vielzeug/refine';\n\ninput.addEventListener('input', (event) => {\n const value = eventFieldValue(event); // string | undefined: never a blind cast\n});\n```\n\nGuarded readers for the standard `input`/`change` event pattern above: they read `value`/`checked` from `event.currentTarget` only when the target actually carries it, so a handler wired to the wrong element yields `undefined`/`false` instead of a crash or a lie. Exported from the package root, which registers no components.\n\n## Types\n\nComponent props follow the `Ore<Component>Props` naming pattern. Event maps follow `Ore<Component>Events`. Import both from the component subpath that owns them.\n\n`RefineElementMap`, `RefineReactIntrinsicElements`, and `RefineVueGlobalComponents` are exported from their respective `frameworks/*` type entry points.\n\n## Errors\n\n### `RefineError`\n\n```ts\nclass RefineError extends Error {\n constructor(message: string, opts?: ErrorOptions);\n}\n```\n\nBase class for public Refine errors. It preserves `cause` through `ErrorOptions`.\n",
|
|
4
|
+
"index": "---\ntitle: 'Refine: Web component library'\ndescription: Accessible, themeable web components built with Ore for framework and vanilla DOM apps.\npackage: refine\ncategory: ui-components\nkeywords: [web-components, accessible, themeable, ui, components, design-system]\nrelated: [ore, orbit, forge, keymap]\nexports:\n [\n ore-accordion,\n ore-accordion-item,\n ore-alert,\n ore-async,\n ore-avatar,\n ore-avatar-group,\n ore-badge,\n ore-box,\n ore-breadcrumb,\n ore-breadcrumb-item,\n ore-button,\n ore-button-group,\n ore-calendar,\n ore-card,\n ore-carousel,\n ore-carousel-slide,\n ore-chat-message,\n ore-checkbox,\n ore-checkbox-group,\n ore-chip,\n ore-code-window,\n ore-column,\n ore-combobox,\n ore-combobox-option,\n ore-command-palette,\n ore-command-palette-item,\n ore-cookie-banner,\n ore-copy-command,\n ore-counter,\n ore-datagrid,\n ore-date-picker,\n ore-dialog,\n ore-drawer,\n ore-file-input,\n ore-grid,\n ore-grid-item,\n ore-icon,\n ore-input,\n ore-keyboard-key,\n ore-keyboard-shortcut,\n ore-list,\n ore-list-item,\n ore-marquee,\n ore-menu,\n ore-menu-item,\n ore-menu-separator,\n ore-message-composer,\n ore-navbar,\n ore-navbar-item,\n ore-navigation-menu,\n ore-navigation-menu-item,\n ore-navigation-menu-panel,\n ore-number-input,\n ore-otp-input,\n ore-pagination,\n ore-password-strength,\n ore-popover,\n ore-progress,\n ore-qr-code,\n ore-qr-scanner,\n ore-query-bar,\n ore-radio,\n ore-radio-group,\n ore-rating,\n ore-select,\n ore-separator,\n ore-sidebar,\n ore-sidebar-group,\n ore-sidebar-item,\n ore-skeleton,\n ore-slider,\n ore-speech-player,\n ore-speed-reading,\n ore-spotlight,\n ore-stats,\n ore-step,\n ore-stepper,\n ore-switch,\n ore-tab-item,\n ore-tab-panel,\n ore-table,\n ore-tabs,\n ore-td,\n ore-text,\n ore-th,\n ore-tr,\n ore-textarea,\n ore-time-picker,\n ore-toast,\n ore-tooltip,\n ore-tour,\n ore-typing-indicator,\n ]\nenvironments: [browser]\n---\n\n<!-- markdownlint-disable MD025 MD033 MD060 -->\n\n<PackageHero package=\"refine\" />\n\n## Why Refine?\n\nEvery project needs UI primitives. Refine provides accessible web components that work natively anywhere HTML is rendered, no framework required.\n\n```html\n<!-- Before: roll your own button with ARIA -->\n<button class=\"btn btn-primary\" role=\"button\" aria-pressed=\"false\" tabindex=\"0\">\n <span class=\"btn-spinner\" aria-hidden=\"true\"></span>\n Save\n</button>\n\n<!-- After: Refine -->\n<ore-button variant=\"solid\" color=\"primary\" loading>Save</ore-button>\n```\n\n| Feature | Refine | Shoelace | Material Web |\n| ------------------ | ------------------------------------------- | ------------------------------------------ | ------------------------------------------ |\n| Bundle size | <PackageInfo package=\"refine\" type=\"size\" /> | ~145 kB | ~200 kB |\n| Built with | Ore | Lit | Lit |\n| Runtime dependencies | Vielzeug UI primitives; Lucide only for the opt-in full-library registrar | Lit + utilities | Lit + Material foundations |\n| Accessible | WCAG AA | WCAG AA | WCAG AA |\n| Framework agnostic | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> |\n\n<div class=\"decision-callout\">\n\n**Use Refine when** you want accessible web components that match the Vielzeug design system without a heavy framework dependency.\n\n**Consider Shoelace or Material Web** if your team is already standardized on those ecosystems and you need their established component catalogs.\n\n</div>\n\n## Installation\n\n::: code-group\n\n```sh [pnpm]\npnpm add @vielzeug/refine\n```\n\n```sh [npm]\nnpm install @vielzeug/refine\n```\n\n```sh [yarn]\nyarn add @vielzeug/refine\n```\n\n:::\n\n## Quick Start\n\n```ts\n// 1. Import global styles once\nimport '@vielzeug/refine/fouc.css'; // Hide unupgraded custom elements until first paint\nimport '@vielzeug/refine/tokens.css'; // Tokens, animations, cascade layers\n\n// 2. Register only the elements you need\nimport '@vielzeug/refine/button';\nimport '@vielzeug/refine/input';\nimport '@vielzeug/refine/card';\n```\n\n```html\n<ore-button variant=\"solid\" color=\"primary\">Save</ore-button>\n<ore-input label=\"Email\" type=\"email\" required></ore-input>\n<ore-card padding=\"lg\">\n <span slot=\"header\">Account</span>\n <p>Card content goes here.</p>\n</ore-card>\n```\n\n```ts\n```\n\n### CDN / Vanilla HTML\n\nUse the self-contained IIFE bundle to load Refine directly from a CDN in any HTML page: no build step required:\n\n```html\n<!-- 1. Styles -->\n<link rel=\"stylesheet\" href=\"https://unpkg.com/@vielzeug/refine/dist/styles/fouc.css\" />\n<link rel=\"stylesheet\" href=\"https://unpkg.com/@vielzeug/refine/dist/styles/tokens.css\" />\n\n<!-- 2. All components (IIFE: registers global Refine namespace) -->\n<script src=\"https://unpkg.com/@vielzeug/refine/dist/refine.iife.js\"></script>\n```\n\nFor bundler-based projects that still want a CDN URL, use the ESM bundle via an import map:\n\n```html\n<script type=\"importmap\">\n {\n \"imports\": {\n \"@vielzeug/refine\": \"https://esm.sh/@vielzeug/refine\",\n \"@vielzeug/refine/button\": \"https://esm.sh/@vielzeug/refine/button\",\n \"@vielzeug/refine/input\": \"https://esm.sh/@vielzeug/refine/input\"\n }\n }\n</script>\n\n<script type=\"module\">\n import '@vielzeug/refine/button';\n import '@vielzeug/refine/input';\n</script>\n```\n\n### Package Entry Points\n\n| Import | Purpose |\n| ------------------------ | ----------------------------------------- |\n| `@vielzeug/refine/fouc.css` | FOUC suppression for unupgraded custom elements |\n| `@vielzeug/refine/tokens.css` | Global design tokens and cascade layers |\n| `@vielzeug/refine/styles/preflight.css` | Optional browser-default reset (includes FOUC suppression) |\n\nComponent registration happens through side-effect imports such as `@vielzeug/refine/button` and `@vielzeug/refine/dialog`.\n\n### Components\n\n**Content:** `ore-avatar`, `ore-avatar-group`, `ore-breadcrumb`, `ore-card`, `ore-carousel`, `ore-carousel-slide`, `ore-chat-message`, `ore-icon`, `ore-keyboard-key`, `ore-keyboard-shortcut`, `ore-list`, `ore-list-item`, `ore-marquee`, `ore-pagination`, `ore-qr-code`, `ore-separator`, `ore-speed-reading`, `ore-stats`, `ore-step`, `ore-stepper`, `ore-table`, `ore-text`\n\n**Disclosure:** `ore-accordion`, `ore-accordion-item`, `ore-tabs`, `ore-tab-item`, `ore-tab-panel`\n\n**Feedback:** `ore-alert`, `ore-async`, `ore-badge`, `ore-chip`, `ore-cookie-banner`, `ore-password-strength`, `ore-progress`, `ore-skeleton`, `ore-toast`, `ore-typing-indicator`\n\n**Inputs:** `ore-button`, `ore-button-group`, `ore-calendar`, `ore-checkbox`, `ore-checkbox-group`, `ore-column`, `ore-combobox`, `ore-counter`, `ore-datagrid`, `ore-date-picker`, `ore-file-input`, `ore-input`, `ore-message-composer`, `ore-number-input`, `ore-otp-input`, `ore-qr-scanner`, `ore-query-bar`, `ore-radio`, `ore-radio-group`, `ore-rating`, `ore-select`, `ore-slider`, `ore-switch`, `ore-textarea`, `ore-time-picker`\n\n**Layout:** `ore-box`, `ore-grid`, `ore-grid-item`, `ore-navbar`, `ore-sidebar`\n\n**Overlay:** `ore-command-palette`, `ore-command-palette-item`, `ore-dialog`, `ore-drawer`, `ore-menu`, `ore-popover`, `ore-spotlight`, `ore-tooltip`, `ore-tour`\n\n## Component portfolio\n\nRefine keeps one public package and classifies components by composition level rather than stability. All listed components remain supported and tested.\n\n- **Foundations**: buttons, inputs, selection controls, disclosure, dialogs, menus, tooltips, layout primitives, and feedback indicators. Start here when building an application-specific interface.\n- **Composed patterns**: data grids, command palettes, navigation menus, steppers, calendars, date/time pickers, file inputs, and message composers. These package recurring accessibility and interaction behavior that is costly to rebuild correctly.\n- **Presentation patterns**: cards, chat messages, code windows, stats, marquees, avatars, badges, and skeletons. Use these when their product vocabulary matches the application; prefer foundations when it does not.\n\nClassification guides discovery and bundle selection. It is not a reason to remove working components or move them into unstable packages; per-component subpath imports remain the tree-shaking boundary.\n\n## Features\n\n<div class=\"features-grid\">\n\n- **Accessible**: keyboard navigation, ARIA wiring, and focus management across interactive components\n- **Themeable**: global tokens plus component-level CSS custom properties\n- **Framework agnostic**: works anywhere HTML can be rendered\n- **Tree-shakeable**: import only the component entry points you register\n- **Comprehensive surface**: inputs, content, disclosure, feedback, layout, and overlay primitives\n- **Native form integration**: form-associated controls submit values, validate supported required constraints, and reset with their form\n- **Typed framework declarations**: DOM, React, and Vue tags derive from the component property contracts\n\n</div>\n\n### Prerequisites\n\n- Browser runtime with Custom Elements support.\n- Import `@vielzeug/refine/fouc.css` and `@vielzeug/refine/tokens.css` before rendering components.\n- For SSR, render placeholders server-side and hydrate components only on the client.\n\n## Documentation\n\n<div class=\"doc-links\">\n\n- [Usage Guide](./usage.md)\n- [API Reference](./api.md)\n- [Migration Guide](./migration.md)\n\n</div>\n\n## See Also\n\n<div class=\"see-also\">\n\n- [Ore](/ore/): Web component runtime that powers Refine\n- [Orbit](/orbit/): Floating UI positioning used in Refine's overlays\n- [Forge](/forge/): Form state management for use with Refine inputs\n- [Keymap](/keymap/): Keyboard shortcut manager that powers the command palette's global trigger\n\n</div>\n\n<!-- markdownlint-enable MD025 MD033 MD060 -->\n",
|
|
5
|
+
"api": "---\ntitle: 'Refine: API Reference'\ndescription: Published component, stylesheet, framework type, and error entry points for @vielzeug/refine.\n---\n\n[[toc]]\n\n## API Overview\n\n| Symbol | Purpose | Execution mode | Common gotcha |\n| --- | --- | --- | --- |\n| `@vielzeug/refine/<component>` | Register one custom element and export its public types | Sync | Importing a type alone does not register the element |\n| `@vielzeug/refine/tokens.css` | Load required theme tokens, animations, and cascade layers | CSS | Import once before components render |\n| `@vielzeug/refine/fouc.css` | Hide unregistered `ore-*` elements until upgrade | CSS | Load before first paint |\n| `@vielzeug/refine/styles/preflight.css` | Apply the optional browser reset | CSS | The reset affects global elements |\n| `@vielzeug/refine/frameworks/elements` | Register typed DOM tag mappings | Types only | Import for side effects in TypeScript |\n| `@vielzeug/refine/frameworks/react` | Register typed React JSX elements | Types only | Does not provide runtime wrappers |\n| `@vielzeug/refine/frameworks/vue` | Register typed Vue global components | Types only | Does not install a Vue plugin |\n| `@vielzeug/refine/theme` | `createThemeController()`: light/dark/system document theme | Sync | Does not persist the preference or set an accent color |\n| `eventFieldValue()` / `eventFieldChecked()` | Read a form control's value/checked from `event.currentTarget` | Sync | Exported from the package root; guards non-field targets |\n| `RefineError` | Base class for package-defined public errors | Sync | Component configuration warnings do not throw this error |\n\n## Package Entry Point\n\n| Import | Purpose |\n| --- | --- |\n| `@vielzeug/refine` | Export `registerIcons`, icon definition types, `RefineError`, and the field readers without registering components |\n| `@vielzeug/refine/<component>` | Register one component and export its tag constant, props, events, and related types |\n| `@vielzeug/refine/theme` | `createThemeController()` and its theme types |\n| `@vielzeug/refine/tokens.css` | Required global design contract |\n| `@vielzeug/refine/fouc.css` | Optional pre-upgrade visibility rule |\n| `@vielzeug/refine/styles/*` | Focused theme, animation, layer, and preflight stylesheets |\n| `@vielzeug/refine/frameworks/*` | TypeScript augmentations for DOM, React, and Vue |\n\n## Icon Registration and Adapters\n\n`registerIcons(icons: Readonly<Record<string, IconDefinition>>): void` is available from `@vielzeug/refine/icons`, `@vielzeug/refine`, and `@vielzeug/refine/icon`. The common `icons` entry and root remain free of component registration side effects.\n\n`IconDefinition`, `IconElement`, and `IconAttributes` are exported from all three entries. Shared definition and registration modules live in `src/icons/`, alongside adapters and internal SVG helpers/defaults. Definitions contain a numeric `viewBox` tuple, optional presentation attributes, and recursive SVG children; they do not require a provider identity. Invalid registrations throw the root-exported `RefineError` atomically.\n\n| Entry | Export | Purpose |\n| --- | --- | --- |\n| `@vielzeug/refine/icons` | `registerIcons`, `IconDefinition`, `IconElement`, `IconAttributes` | Common library-neutral registration and definition API |\n| `@vielzeug/refine/icons/lucide` | `lucideIcons`, `LucideIconData` | Pure adapter for selected structural Lucide SVG data |\n| `@vielzeug/refine/icons/lucide/all` | Side-effect registration | Explicitly register the full installed Lucide library |\n| `@vielzeug/refine/icons/heroicons` | `heroiconsIcons`, `IconifyIcon` | Convert a caller-named map of individual Heroicons Iconify data |\n| `@vielzeug/refine/icons/tabler` | `tablerIcons`, `IconifyIcon` | Convert a caller-named map of individual Tabler Iconify data |\n| `@vielzeug/refine/icons/mdi` | `mdiIcons`, `IconifyIcon` | Convert a caller-named map of individual MDI Iconify data |\n| `@vielzeug/refine/icons/iconify` | `iconifyIcon`, `iconifyIcons` | Convert individual Iconify data or selected/complete collections |\n| `@vielzeug/refine/icons/iconify` | `IconifyIcon`, `IconifyAlias`, `IconifyCollection`, `IconifyTransform` | Structural data types; no Iconify dependency required |\n\nLucide is an optional peer, not a mandatory dependency. Only `icons/lucide/all` requires an installed `lucide` package; consumers selecting that entry declare it themselves. The structural `icons/lucide` adapter accepts supplied data without importing Lucide.\n\nThe standalone IIFE exposes `registerIcons`, `lucideIcons`, `heroiconsIcons`, `tablerIcons`, `mdiIcons`, `iconifyIcon`, and `iconifyIcons` under `Refine.components.content`. Neither the IIFE nor component entries import Lucide. Iconify conversion drops provider `class` metadata, validates finite numeric offsets and positive finite dimensions before transforms, and retains the strict SVG allowlist. See the [icon reference](./components/icon.md) for presentation overrides, local SVG ID rewriting, supported families, and licenses.\n\nThe three family adapters share this signature:\n\n```ts\nheroiconsIcons(icons: Readonly<Record<string, IconifyIcon>>): Record<string, IconDefinition>;\ntablerIcons(icons: Readonly<Record<string, IconifyIcon>>): Record<string, IconDefinition>;\nmdiIcons(icons: Readonly<Record<string, IconifyIcon>>): Record<string, IconDefinition>;\n```\n\nEach accepts individual Iconify SVG data, preserves input keys without adding a namespace, and returns definitions without registering them. They require `DOMParser`, preserve each icon's geometry/presentation/transforms, and throw `RefineError` for invalid Iconify data. They do not accept React/Vue components, collection JSON, or raw SVG path strings. Use `iconifyIcons(collection, names?)` for collections and alias resolution.\n\n## Styles\n\n```ts\nimport '@vielzeug/refine/fouc.css';\nimport '@vielzeug/refine/tokens.css';\nimport '@vielzeug/refine/styles/preflight.css';\n```\n\n`tokens.css` is required. It defines tokens, animations, and cascade-layer order without resetting global elements. `preflight.css` is optional and includes FOUC suppression.\n\n| Import path | Purpose |\n| --- | --- |\n| `@vielzeug/refine/fouc.css` | FOUC suppression for unupgraded custom elements |\n| `@vielzeug/refine/tokens.css` | Tokens, animation helpers, and cascade layers |\n| `@vielzeug/refine/styles/theme.css` | Theme token declarations |\n| `@vielzeug/refine/styles/animation.css` | Animation helpers |\n| `@vielzeug/refine/styles/layers.css` | Cascade layer declarations |\n| `@vielzeug/refine/styles/preflight.css` | Optional browser reset and FOUC suppression |\n\n## Components\n\nEach component uses one registration and type entry point:\n\n```ts\nimport '@vielzeug/refine/button';\nimport type { OreButtonProps } from '@vielzeug/refine/button';\n```\n\n| Area | Components |\n| --- | --- |\n| Content | `avatar`, `avatar-group`, `breadcrumb`, `card`, `carousel`, `chat-message`, `code-window`, `copy-command`, `icon`, `keyboard-key`, `list`, `list-item`, `marquee`, `pagination`, `separator`, `speech-player`, `speed-reading`, `stats`, `step`, `stepper`, `table`, `text` |\n| Disclosure | `accordion`, `accordion-item`, `tabs`, `tab-item`, `tab-panel` |\n| Feedback | `alert`, `async`, `badge`, `chip`, `cookie-banner`, `password-strength`, `progress`, `skeleton`, `toast`, `typing-indicator` |\n| Inputs | `button`, `button-group`, `calendar`, `checkbox`, `checkbox-group`, `combobox`, `counter`, `datagrid`, `date-picker`, `file-input`, `input`, `message-composer`, `number-input`, `otp-input`, `query-bar`, `radio`, `radio-group`, `rating`, `select`, `slider`, `switch`, `textarea`, `time-picker` |\n| Layout | `box`, `grid`, `grid-item`, `navbar`, `sidebar` |\n| Overlays | `command-palette`, `dialog`, `drawer`, `menu`, `navigation-menu`, `popover`, `tooltip` |\n\nEach component page lists its attributes, JavaScript properties, events, slots, parts, and CSS custom properties.\n\nThe [`query-bar`](./components/query-bar.md) entry also exports `QueryState`, `QueryTokenDef`, `QueryQuickFilter`, `QueryBarLabels`, and pure grammar helpers. Vocabulary entries default to single selection; `selection: 'multiple'` allows distinct values for the same key. `anyLabel` customizes the unrestricted single-selection choice, such as “Default order” for sorting. Options accept an optional `icon` (registered `ore-icon` name), displayed beside their labels in the drawer. `tokenValues(state, key)` reads all values, `removeToken(state, token)` removes an exact pair, and `clearTokens(state, key)` clears a group. Its unified field contains applied filters, with a compact secondary row for filter entry, quick toggles, and a `toolbar-end` slot for application content such as a result count. Drawer selection uses the same state and vocabulary as typed suggestions. Pending edits block incidental backdrop/swipe dismissal; explicit Cancel, Close, and Escape discard them. Search-hidden selections remain visible in a summary.\n\n## Framework Types\n\n```ts\nimport type {} from '@vielzeug/refine/frameworks/elements';\nimport type {} from '@vielzeug/refine/frameworks/react';\nimport type {} from '@vielzeug/refine/frameworks/vue';\n```\n\nThe declarations cover every supported tag and derive component properties from Refine's authoritative `HTMLElementTagNameMap`. They provide types only; component registration still uses component subpaths.\n\n## Events and Form Controls\n\nForm controls expose `.value` or `.checked` and dispatch standard `input` and `change` events. Read the property from `event.currentTarget`.\n\nFinite-choice properties use Ore's `prop.oneOf()` parsing: values outside a component's documented options resolve to\nthe declared default, or `undefined` when no default is declared.\n\nForm-associated controls participate in native form submission, validity checks, and reset. A named control's value\nis available through `new FormData(form)`, and supported `required` constraints affect `form.checkValidity()`,\n`form.reportValidity()`, and submit blocking. The `error` property renders and announces feedback; it does not by\nitself set native constraint validity.\n\nStateful overlays expose controlled `open`, optional `default-open`, and an `open-change` custom event with `{ open, reason }` detail. Component pages define additional detail fields and reasons.\n\n## Utilities\n\n### `createThemeController(options?)`\n\n```ts\nimport { createThemeController } from '@vielzeug/refine/theme';\n\nconst theme = createThemeController(); // starts in 'system' mode\ntheme.setPreference('dark');\ntheme.dispose();\n```\n\nOwns the light/dark/system theme for the document: a reactive `preference` signal, the effective `resolved` mode (`'dark' | 'light'` after resolving `system` against the OS), and the DOM application `styles/theme.css` expects: the `.dark` class plus the `color-scheme` property on the root element, so every `light-dark()` token resolves to the right branch. While in `system` mode the controller tracks the OS `prefers-color-scheme` via sentinel's `createMediaQuery`.\n\nThe controller does **not** persist the preference or expose an accent color: those are consumer concerns (wire `watch(controller.preference, save)` for persistence; set `--color-primary-hue` yourself). Options: `initial` (starting preference, default `'system'`), `root` (element carrying the classes, default `document.documentElement`), `target` (window whose `matchMedia` backs `system`). The handle is disposable: `dispose()`, `disposed`, `disposalSignal`, `[Symbol.dispose]`.\n\n### `eventFieldValue(event)` / `eventFieldChecked(event)`\n\n```ts\nimport { eventFieldChecked, eventFieldValue } from '@vielzeug/refine';\n\ninput.addEventListener('input', (event) => {\n const value = eventFieldValue(event); // string | undefined: never a blind cast\n});\n```\n\nGuarded readers for the standard `input`/`change` event pattern above: they read `value`/`checked` from `event.currentTarget` only when the target actually carries it, so a handler wired to the wrong element yields `undefined`/`false` instead of a crash or a lie. Exported from the package root, which registers no components.\n\n## Types\n\nComponent props follow the `Ore<Component>Props` naming pattern. Event maps follow `Ore<Component>Events`. Import both from the component subpath that owns them.\n\n`RefineElementMap`, `RefineReactIntrinsicElements`, and `RefineVueGlobalComponents` are exported from their respective `frameworks/*` type entry points.\n\n## Errors\n\n### `RefineError`\n\n```ts\nclass RefineError extends Error {\n constructor(message: string, opts?: ErrorOptions);\n}\n```\n\nBase class for public Refine errors. It preserves `cause` through `ErrorOptions`.\n",
|
|
6
6
|
"usage": "---\ntitle: 'Refine: Usage Guide'\ndescription: Register Refine components, configure properties, handle events, compose slots, and integrate frameworks.\n---\n\n[[toc]]\n\n## Basic Usage\n\nLoad the global design contract once, then register only the components used by the page.\n\n```ts\nimport '@vielzeug/refine/tokens.css';\nimport '@vielzeug/refine/button';\nimport '@vielzeug/refine/input';\nimport '@vielzeug/refine/dialog';\n\nconst input = document.querySelector('ore-input');\nconst save = document.querySelector('ore-button');\n\nif (!(input instanceof HTMLElement) || !(save instanceof HTMLElement)) {\n throw new Error('Expected Refine controls');\n}\n\nsave.addEventListener('click', () => {\n console.log((input as HTMLElement & { value: string }).value);\n});\n```\n\nAdd the browser reset only when the application owns global element styling:\n\n```ts\nimport '@vielzeug/refine/styles/preflight.css';\n```\n\n## Choose an Icon Library\n\nRefine includes only its internal component icons. Register application icons before mounting them: use `registerIcons` and neutral `IconDefinition` data from `@vielzeug/refine/icons`, the pure `@vielzeug/refine/icons/lucide` adapter for selected Lucide icons, or `@vielzeug/refine/icons/iconify` for local Heroicons, Tabler, and Material Design Icons data. Keep importing the component from `@vielzeug/refine/icon`. Lucide is an optional peer: install `lucide` yourself only when choosing its data or `@vielzeug/refine/icons/lucide/all`; core and structural adapters do not require it. The [icon reference](./components/icon.md) covers imports, licenses, and presentation behavior.\n\nFor selected individual Iconify data, use `heroiconsIcons`, `tablerIcons`, or `mdiIcons` from `@vielzeug/refine/icons/heroicons`, `/tabler`, or `/mdi`. For example, `registerIcons(heroiconsIcons({ Search, Home }))` preserves your app keys. Install the matching `@iconify-icons/<family>` data package yourself; these helpers do not accept framework components. Keep `iconifyIcons` for collection JSON and aliases.\n\n## Set Attributes and Properties\n\nUse attributes for serializable HTML state.\n\n```html\n<ore-button variant=\"outline\" color=\"secondary\" size=\"lg\" disabled>\n Save changes\n</ore-button>\n```\n\nUse JavaScript properties for arrays, objects, callbacks, and controlled state.\n\n```ts\nimport '@vielzeug/refine/datagrid';\nimport type { OreDataGridProps } from '@vielzeug/refine/datagrid';\n\nconst grid = document.querySelector('ore-datagrid') as HTMLElement & OreDataGridProps;\ngrid.columns = [\n { key: 'name', label: 'Name', sortable: true },\n { key: 'role', label: 'Role' },\n];\ngrid.rows = [\n { id: '1', name: 'Ada', role: 'Admin' },\n { id: '2', name: 'Linus', role: 'Editor' },\n];\n```\n\nEnumerated properties such as `variant`, `size`, and `color` accept the values listed on each component's reference\npage. An unsupported value resolves to the property's declared default (or `undefined` when it has no default).\n\nIn an Ore template, `model()` can bind a Refine field's `value` to a Ripple signal and write user input back:\n\n```ts\nimport { signal } from '@vielzeug/ripple';\nimport { html, model } from '@vielzeug/ore';\nimport '@vielzeug/refine/input';\n\nconst email = signal('');\nconst field = html`<ore-input label=\"Email\" type=\"email\" value=${model(email)} />`;\n```\n\n## Handle Events\n\nForm controls dispatch standard `input` and `change` events. Read their current property from `event.currentTarget`.\n\n```ts\nconst input = document.querySelector('ore-input');\ninput?.addEventListener('input', (event) => {\n const target = event.currentTarget as HTMLElement & { value: string };\n console.log(target.value);\n});\n```\n\nCustom events use kebab-case names and typed detail objects.\n\n```ts\nimport type { OreDialogEvents } from '@vielzeug/refine/dialog';\n\nconst dialog = document.querySelector('ore-dialog');\ndialog?.addEventListener('open-change', (event: CustomEvent<OreDialogEvents['open-change']>) => {\n console.log(event.detail.open, event.detail.reason);\n});\n```\n\n## Native Form Validation\n\nRefine's form-associated controls integrate with native form submission, `FormData`, validity checks, and reset.\nUse `required` for a required-value constraint; setting `error` displays and announces feedback but does not, by\nitself, invalidate the form.\n\n```html\n<form id=\"profile-form\">\n <ore-input name=\"email\" type=\"email\" label=\"Email\" required></ore-input>\n <ore-select name=\"role\" label=\"Role\" required>\n <option value=\"\">Choose a role…</option>\n <option value=\"editor\">Editor</option>\n <option value=\"admin\">Admin</option>\n </ore-select>\n <ore-button type=\"submit\">Save</ore-button>\n</form>\n\n<script type=\"module\">\n import '@vielzeug/refine/input';\n import '@vielzeug/refine/select';\n import '@vielzeug/refine/button';\n\n const form = document.querySelector('#profile-form');\n form.addEventListener('submit', (event) => {\n event.preventDefault();\n console.log(new FormData(form).get('email'));\n });\n</script>\n```\n\nThe browser runs native validation before dispatching `submit`. Call `form.reportValidity()` to show validation\nfeedback directly, or `form.checkValidity()` for a boolean check without the browser's validation UI.\n\n## Compose Slots\n\nDefault slots hold primary content. Named slots identify component-owned regions.\n\n```html\n<ore-card>\n <span slot=\"header\">Account</span>\n <p>Profile settings</p>\n <div slot=\"footer\">\n <ore-button variant=\"ghost\">Cancel</ore-button>\n <ore-button>Save</ore-button>\n </div>\n</ore-card>\n```\n\nInput components commonly expose `prefix`, `suffix`, `label`, `helper`, and `error` slots. Each component reference lists its supported slots.\n\n## Load TypeScript Framework Declarations\n\nImport the declaration matching the framework once in an application type entry point.\n\n::: code-group\n\n```ts [DOM]\nimport type {} from '@vielzeug/refine/frameworks/elements';\n```\n\n```ts [React]\nimport type {} from '@vielzeug/refine/frameworks/react';\n```\n\n```ts [Vue]\nimport type {} from '@vielzeug/refine/frameworks/vue';\n```\n\n:::\n\nThese entry points provide typed tags and component properties. They do not register components or install runtime adapters.\n\n## Framework Integration\n\nRefine elements use native DOM properties, events, and slots. Frameworks differ only in how they forward custom-element properties and events.\n\n::: code-group\n\n```tsx [React 19]\nimport '@vielzeug/refine/button';\n\nexport function SaveButton() {\n return <ore-button variant=\"solid\">Save</ore-button>;\n}\n```\n\n```vue [Vue 3]\n<script setup lang=\"ts\">\nimport '@vielzeug/refine/button';\n</script>\n\n<template>\n <ore-button variant=\"solid\">Save</ore-button>\n</template>\n```\n\n```svelte [Svelte]\n<script lang=\"ts\">\n import '@vielzeug/refine/button';\n</script>\n\n<ore-button variant=\"solid\">Save</ore-button>\n```\n\n:::\n\nSee [Framework Integration](./frameworks.md) for custom events, controlled properties, SSR guards, and framework-specific configuration.\n\n## Working with Other Vielzeug Libraries\n\n### Ore\n\nUse Ore when authoring a custom element that composes Refine components.\n\n```ts\nimport '@vielzeug/refine/button';\nimport { define, html } from '@vielzeug/ore';\n\ndefine('save-panel', {\n setup() {\n return html`<ore-button @click=${() => console.log('save')}>Save</ore-button>`;\n },\n});\n```\n\n### Ripple\n\nUse Ripple to drive component properties from application state.\n\n```ts\nimport '@vielzeug/refine/button';\nimport { effect, signal } from '@vielzeug/ripple';\n\nconst loading = signal(false);\nconst button = document.querySelector('ore-button') as HTMLElement & { loading?: boolean };\nconst loadingEffect = effect(() => {\n button.loading = loading.value;\n});\n\nloadingEffect.dispose();\n```\n\n## Best Practices\n\n- **Import** `tokens.css` once before rendering components.\n- **Register** components through explicit subpaths.\n- **Set** structured data and callbacks as JavaScript properties.\n- **Read** form state from the event target instead of custom event payloads.\n- **Provide** accessible labels for icon-only controls.\n- **Use** component reference pages for supported slots, parts, and CSS properties.\n- **Dispose** application-owned Ripple effects and service handles.\n- **Guard** DOM-dependent component registration during SSR.\n- **Working with AI agents?** Install the `vielzeug` skill (`npx -y @vielzeug/codex skills install`) so agents reach for Refine components and tokens instead of custom widgets: see the [Codex usage guide](../codex/usage.md#agent-skill).\n"
|
|
7
7
|
},
|
|
8
8
|
"examples": [],
|