
# Beamline UI knowledge

Concise answers drawn from Beamline's public documentation and support knowledge, reviewed on **14 October 2026**.
Each answer links to its source. Release-specific notes retain their dates and versions; use the linked installation,
pricing, licence and testing pages for current instructions and terms.

**Installing now:** use the [release archive or editable-source instructions](https://beamline.io/docs/install). Public npm publication
is incomplete on this review date; a direct npm install is not yet a complete supported route. An AI coding agent
is optional. You can try [free Settings](https://beamline.io/components/settings) without an account or key.



- [Getting started and the Settings trial](#getting-started-and-the-settings-trial)
- [Installation](#installation)
- [Data, persistence and service boundaries](#data-persistence-and-service-boundaries)
- [Lists, filters and record selection](#lists-filters-and-record-selection)
- [Custom styles and component contracts](#custom-styles-and-component-contracts)
- [Languages, formatting and direction](#languages-formatting-and-direction)
- [Looks and typography](#looks-and-typography)
- [Layout, controls and accessibility](#layout-controls-and-accessibility)
- [Motion and interface sound](#motion-and-interface-sound)
- [Upgrades and release migrations](#upgrades-and-release-migrations)
- [Licence, purchases and account access](#licence-purchases-and-account-access)
- [Dated checks and known limits](#dated-checks-and-known-limits)

## Getting started and the Settings trial

### Editable source and custom screens

Beamline provides editable React, TypeScript and CSS source for individual components and complete screens.
Install the components you need into `src/ui/beamline`, compose your own layout and edit the files directly.
Complete screens are additional compositions of those components. You can change their layout, behavior and
styling, or build a screen from individual parts. Local compiled packages are an alternative installation mode.
Coding agents are optional, and the running app does not depend on Beamline servers.

Source: [Install](https://beamline.io/docs/install) · [Customise](https://beamline.io/docs/customize) · [Live components](https://beamline.io/components/).

### Beamline 0.2 base modes and React 19 prerequisites

Beamline is a React UI library for developers; a coding agent is optional. Use the documented commands and React imports directly, or let a connected agent select and install the same parts. It supplies UI code, not a hosted application, database, model service, or voice provider.

Beamline supplies interface code, not a backend. Its code and customer style files live in the app repository; after installation, building and running do not need Beamline servers. The app supplies data, persistence, authentication, and handlers. Updates fetch a release only when explicitly requested. Runtime independence is not a security guarantee: the app remains responsible for permissions, secret handling, backend calls, and dependency updates.

When adding Beamline beside existing UI, start with one screen or region inside the documented `UIRoot`. Keep the app's routing, data, authentication, and save handlers. Compiled styles are scoped and need no host Tailwind setup; full-source shares the app's Tailwind build, so read its theme instructions before combining styles.

React 19 is an app prerequisite. React 18 and older are unsupported because components use React 19 APIs, including `ref` as a prop.

Choose one Beamline base:

- **Compiled packages:** install selected verified archives and required Beamline units. Component source is not downloaded, and Tailwind is not required to style Beamline.
- **Full editable source:** copy selected entries and required source into a managed app folder. Components use Tailwind v4, and this mode has no `@beamline/*` package entries.

Do not mix full source with compiled Beamline packages: a second runtime context can split roots from their children. The existing `--shared-base` arrangement is a distinct compatibility mode, not full source: components are local while runtime, foundation, looks, and fonts stay packaged.

Use the tool and release descriptor from the same delivered release. Select a 0.2 aggregate explicitly; do not combine a legacy 0.1 aggregate with split 0.2 units.

The bundled CLI requires Node 20 or newer, subject to the app toolchain's own requirements. Tested Vite 8.3.2 declares `^20.19.0 || >=22.12.0`; tested Next.js declares `>=20.9.0`.

Source: [Start here](https://beamline.io/docs/start-here).

### Beamline system size and app stack

The complete system contains 200+ components and 50+ complete screens. Its reference app uses React 19. Full editable-source installs use Tailwind v4; compiled-package installs do not need Tailwind.

Source: [Start here](https://beamline.io/docs/start-here).

### Beamline UI element discovery and validation

Read `CAPABILITIES.md` or search for the task you need to solve, such as picking a date range, showing an order book, or requesting approval:

```bash
node /path/to/release/agents/skills/beamline-ui/scripts/find.mjs --app /path/to/app <job>
```

Each catalog entry explains what the element is for and what to use instead when it is a poor fit. Before relying on an element, read `docs/LIMITATIONS.md` to check what is demonstration-only, which layouts are verified, which browsers were exercised, and where your own backend must do the work. The UI does not claim that an external action succeeded; your handlers determine what the screens show.

Source: [Start here](https://beamline.io/docs/start-here).

### Beamline release kit: folder contents and optional assets

- `release.json` and `packages/`: the release descriptor and verified npm archives for selected `@beamline/*` packages. The archives contain compiled ES modules and type declarations; the installer selects the required archives.
- `source/ui/`: editable TypeScript and CSS used to build the packages, including components, blocks (complete screens), hooks, helpers, styles, and entry examples such as `demo.tsx`.
- `reference-app/`: a React 19 and Vite application showing the package in use, with navigation, connected screens, data adapters, and a brand change. The complete bundle has five screens; a standalone pack retains the screens for its included blocks. A pack without blocks has no reference app and starts from `examples/`.
- `examples/`: focused apps, including `embed-in-existing-app`, which adds Beamline without touching existing styles, and `source-app`, which provides editable source with Tailwind when the bundle includes its elements.
- `catalog/catalog.json` and `CAPABILITIES.md`: the machine-readable catalog and element menu, including intended uses, imports, props, and keyboard details. The finder uses the same catalog offline.
- `previews/`: a release-matched gallery snapshot when included. `MANIFEST.json` records whether previews are present.
- `docs/`: installation, upgrades, customization, data integration, dependencies, licences, limitations, changelog, conventions, and design language.
- `tools/beamline.mjs`: installs compiled packages or full editable source, including the base, and manages source upgrades while preserving edits.
- `AGENTS.md` and `agents/`: instructions and a small skill for coding agents such as Claude Code and Codex.
- `LICENSE.md`, `THIRD-PARTY-LICENSES.md`, and `NOTICE`: Beamline’s licence and the licences and obligations for third-party packages it can bring in.
- `MANIFEST.json`: records the exact version, source revision, entry list, and a checksum for every file in the bundle.

Source: [Start here](https://beamline.io/docs/start-here).

### Starting the Beamline reference app

The reference app requires Node 20 or newer. From the extracted kit, run:

```bash
cd reference-app
npm install
npm run dev
```

Open the address printed by the development server.

The screens use demonstration data labelled as such on screen and do not call an external service. `reference-app/README.md` explains where each screen’s data enters and where to connect your backend.

Source: [Start here](https://beamline.io/docs/start-here).

### No-account Settings trial with Beamline sample 0.2.0-rc.33

A no-account free Settings trial starts from a running React 19 app on a development branch, with a package manager, browser automation, and a development record the user may change. Allow about 30 minutes of active work; downloads, agent availability, or an unfamiliar backend can take longer. The free sample needs no Beamline account or key and is available under MIT or the Beamline licence; MIT permits commercial use.

Install the pinned free `settings`, `alert`, and `spinner` entries at sample release `0.2.0-rc.33` with Prism and Inter. Then inspect the installed API before building:

```sh
beamline-find --app . --capabilities
beamline-find --app . --id settings alert spinner
```

Use the installed `@beamline/settings` block and real props. Import the generated stylesheet once and use one `UIRoot` with installed Prism. For Next.js App Router, keep form state in a client component and import global CSS in the layout; for Vite, import CSS in the entry. Remove conflicting starter page-centering styles and use the app's real data, not a demo fixture.

If the pinned archive is unavailable, use the current no-account install line on the Settings code page and record the version. Keep CLI and release compatible; do not substitute a paid package or sign-in flow.

Source: [Try it in 30 minutes](https://beamline.io/docs/try-in-30).

### Beamline Settings 30-minute trial: coverage and evidence limits

The described 30-minute trial covers saving and reloading real settings, recovering from a save failure with **Retry**, checking a phone layout, restyling the screen, and adding a new field. A successful trial checks a real integration with the app’s framework and data contract: persistence, save-failure recovery, phone layout, keyboard use, custom styling, and one complete field change. The linked guide provides the prerequisites, installation commands, save/recovery steps and evidence to retain.

A local file API can demonstrate those paths locally; it does not qualify production authentication, hosting, or database behavior. The app continues to own its data and services.

Source: [Try it in 30 minutes](https://beamline.io/docs/try-in-30).

### Beamline Trial Settings: Loading and Persistence

#### Use real records and persistence

- Inspect and reuse the application's existing data layer, record shape, validation, and save operation. Load actual settings and map them to `SettingsValues`, using the record's `name`, `email`, and time zone.
- Await the real save before reporting success. For an API adapter, preserve the existing endpoint and request mapping, check the response status, and throw an `Error` or reject on failure. This lets Settings retain the draft and offer Retry.
- Keep the Settings instance mounted while saving: a failed request must not replace the edited draft with the last saved values. Preserve failed drafts for retry and validate at the actual persistence boundary.
- Verify the stored result after reload, rejection without mutation, and a controlled failure at that boundary. A toast alone proves only the UI response.

#### Demonstration adapters are not the app's persistence layer

- `createBrowserSettingsApi` in `demo/settings-fixtures` is a labelled sample that writes a fictional record to the current origin's `localStorage`, under `beamline-profile-sample-v1` by default. The record survives reload in the same browser and origin; clearing browser storage removes it, and it is not shared with another browser or account.
- The older `createSettingsApi` is an in-memory demonstration for specialized workspace sections; reload resets its changes.
- Replace these demonstration adapters with the application's real handler. The browser-storage adapter is only a sample, not a substitute for the trial app's persistence.

#### Local API fallback when there is no backend

- Create a small local development API with `GET /api/settings` and `PATCH /api/settings`, backed by a JSON file on disk. Use a Next.js route handler or a small Node server behind Vite's `/api` proxy.
- Validate input on the server and write it before returning success. Label the API clearly as local in the report: it is real local storage, not a production service.
- Do not substitute browser `localStorage`, a timer, an always-successful promise, or in-memory data.

#### Initial states and browser checks

- While fetching, show a loading state. If no record exists, show a useful empty state with a way to create one. If fetching fails, show a load-error state with Retry; Retry must load real data.
- Test delayed initial loading, a no-record response, and a failed initial load followed by Retry in the browser. Keep test and failure controls out of the customer screen.

Source: [Try it in 30 minutes](https://beamline.io/docs/try-in-30).

### Matching 0.2 Settings: Profile Fields and Save Behavior

#### Fields and release compatibility

- Configure ordinary Settings fields with `name`, `email`, `timeZone`, and `notifications`.
- In the matching **0.2 release**, omit `workspace` and `slug` to omit Workspace.
- Declare optional inputs through `profileFields`; their values are stored under `values.profile`, and changed keys use the form `profile.<id>`. An optional Job title can use the existing save path. Empty optional values are valid.
- Inspect the installed types before using these newer fields with an older artifact.

#### Save and draft behavior

- `onSave(values, changed)` must resolve only after the destination has persisted the record, and reject if it has not. The block trims name and email before invoking it.
- A general `Error` retains the draft and permits retry. An error with a known `field` key adds an inline error and focuses that field, or offers a return to its section.
- Edits made while the save promise is pending remain a new unsaved draft after the submitted snapshot succeeds.
- The callback currently returns `void`. If the server canonicalizes other fields, coordinate the saved-value contract rather than silently displaying a different record.

#### Host navigation

- When the host already supplies navigation, matching Settings releases use `navigation="tabs"` and `heading={null}`.
- When the host routes sections, use controlled `section` with `navigation="none"` to avoid a second application shell.

Source: [Connect your data](https://beamline.io/docs/data-integration).

### Job title field in trial and Beamline Settings

Add an optional **Job title** field through the Settings block’s `profileFields` in both trial and Beamline Settings:

- Use field ID `jobTitle` and storage path `profile.jobTitle`.
- Wire the field through the UI, request mapping, server validation, storage, and read-back; preserve every existing field.
- On the server, when a value is submitted, require it to be a string of at most 80 characters.

Verify the integration by saving a value, refreshing the page or screen, and checking that the value appears in the form and in the API record. Also submit invalid input directly to the API and verify that it is rejected without changing storage. An extra form input without corresponding backend support is not a complete field integration.

Source: [Try it in 30 minutes](https://beamline.io/docs/try-in-30).

### Beamline Settings trial: verification and reporting checklist

#### Record and report

Use `EVALUATION.md` to record actual commands, browser observations, and screenshot paths. Mark every check **PASS**, **FAIL**, or **BLOCKED**, and report anything not exercised. Fix resolvable problems and rerun the affected checks. If permissions are missing or prerequisites are incompatible, stop and name them rather than claiming a pass.

#### Verification checklist

- **Persistence:** Change **Name**, save, read the stored record through the API, and refresh. Confirm the new value remains.
- **Real save failure and recovery:** Before a write, cause a real backend save to fail using a local/development failure control or by stopping the local API. Do not simulate failure by mocking the browser response. Confirm storage stays unchanged, the edited value remains visible, and an error appears. Recover the API and use **Retry**; confirm it saves that same draft. Restore the development record afterwards.
- **Initial-load states:** In the browser, exercise delayed initial loading, a no-record response, and failed initial loading followed by **Retry**. Verify the visible state and appropriate next action for each case.
- **Phone layout:** At 390 px wide, edit and save without clipped fields, an unreachable save bar, or horizontal page scrolling.
- **Keyboard and accessibility:** Using only the keyboard, reach and edit a field, reach **Save**, and activate it. Check visible focus and that focus remains usable after saving. Use semantic labels.
- **After changes:** Repeat the save, failure/retry, phone, and keyboard checks after restyling and adding the field. Read back the added field. Run the production build.

Capture the final screen at 1512×982 as `evaluation/desktop.png` and at 390×844 as `evaluation/phone.png`. Include framework and package versions; the API and storage actually used; before-and-after style observations; the added-field read-back; elapsed time; and exact rerun steps. A successful build or toast alone does not prove persistence.

Source: [Try it in 30 minutes](https://beamline.io/docs/try-in-30).

### Beamline consumer-app installation checks

In the actual app, render the selected `Button` inside a dark branded root and activate its app handler. Open the selected `Dialog`.

Confirm that:

- The browser console is clear.
- Existing navigation and styles still work.
- Customer alias and brand files are unchanged.
- `Blocks` have a parent with a size.

These are consumer checks; the included gallery is separate evidence.

Source: [Install](https://beamline.io/docs/install).

### Reference-App Demo Adapter Replacement in Beamline

Installed block catalogs and `source/ui/blocks/<id>/meta.json` document each block’s props. Reference-app adapters under `reference-app/src/data/` show where demonstration data can be replaced with an API. Replace the adapter body while preserving its shape, and test the real persistence boundary after reload.

The CRM screen uses the same `DataGrid`; `@beamline/demo-crm-fixtures` generates fictional deals.

Source: [Connect your data](https://beamline.io/docs/data-integration).

## Installation

### Compiled Beamline 0.2 installs for app package managers

Run the release tool from an app directory containing `package.json`; a local HTTP descriptor URL is also accepted.

```sh
node /path/to/release/tools/beamline.mjs install button dialog --release /path/to/release/release.json --preset prism
```

The tool verifies the descriptor graph and selected archives before invoking the app's package manager. An offline bundle contains Beamline archives, but registry dependencies must be available from a registry or package-manager cache.

The app manifest records selected components, looks, fonts, and effects. The app-root `.beamline/` stores package archives under `.beamline/packages/`, lockfile references, and `package-selection.json`. Internal Beamline units resolve through package-manager overrides or resolutions; registry dependencies such as Radix, Motion, and chart dependencies accompany those units rather than being added as Beamline manifest entries.

The tool leaves the app's React and ReactDOM ranges unchanged and stops if they do not allow React 19. It removes dependencies previously added by the tool when they are no longer needed and preserves unrelated manifest content. The app must declare packages it imports itself, including icon libraries and packages used in component prop types. Form requires the app's `react-hook-form` `useForm` at the release-specified `^7.89.0` range so app and component share one copy. pnpm may fail to resolve undeclared imports even when npm hoists them into reach.

The installer follows `package.json#packageManager` or the app lockfile. Packaged CLI invocations include:

```sh
bun x --package https://mcp.beamline.io/cli/latest.tgz beamline install button dialog --release /path/to/release/release.json
yarn dlx --package beamline@https://mcp.beamline.io/cli/latest.tgz beamline install button dialog --release /path/to/release/release.json
yarn add --dev https://mcp.beamline.io/cli/latest.tgz
yarn beamline install button dialog --release /path/to/release/release.json
```

Use the same package manager as the app. The Yarn Berry form gives the archive URL an explicit package name; Yarn Classic installs the CLI locally because it has no `dlx`. `--dry-run` previews manifest and stylesheet changes. `--json` returns the full machine-readable result, while package-manager output goes to standard error.

For pnpm, the tool recognizes the package-manager field, lockfile, or enclosing workspace. It adds version-qualified archive overrides while preserving workspace members, settings, and unrelated overrides; prior workspace configuration bytes are saved under `.beamline/package-manager/`. Commit the app-root `.beamline/`, which contains the manifest and lockfile references, archives, and `package-selection.json`. This path was exercised with pnpm 11.22.0.

In an npm workspace member, npm applies `overrides` only at the workspace root and resolves a relative archive path from each dependent folder. Compiled installs there list every Beamline unit used by the selection in the member's `dependencies`—Beamline archives only, not registry packages or React. Standalone npm apps and pnpm list only chosen packages.

Concurrent Yarn Classic apps should use separate `YARN_CACHE_FOLDER` directories; sharing an empty cache produced tar extraction `EEXIST`/`ENOENT` errors before Beamline ran. With Berry PnP, use `yarn run build` and `yarn run typecheck` so PnP is active.

Compiled installs write `src/styles/beamline/beamline.css` with fonts, foundation, selected looks, applied custom style, and selected components in cascade order. Import it once. Per-component CSS imports each bring foundation again; a component's own `styles.css` can be used alone if the app uses only that component. The tool regenerates the aggregate while it still matches what it last wrote; if edited, it preserves the file and prints imports it would have added. Compiled installs write no other files under `src/` except this aggregate.

Source: [Install](https://beamline.io/docs/install).

### Full-source Beamline setup and shared-base migration

`source` means `add --full-source`. A new source-only app also gets full-source mode with plain `add`; existing installs retain their recorded mode on `add` and `upgrade`.

```sh
node /path/to/release/tools/beamline.mjs source settings data-grid --release /path/to/release/release.json --preset prism --install
```

Use `--app /path/to/app` to target another app and `--to src/ui/another-folder` to choose a different managed subdirectory. By default, files go under `src/ui/beamline`: selected components, blocks, hooks, libraries, and their dependencies; runtime, types, appearance context, and selected look setup; styles, tokens, themes, utilities, and `beamline.css`; selected look references; font CSS, local fonts, and OFL notices; licence notices; and `beamline-ui.lock.json` plus `.beamline/` upgrade state. Commit the lock and `.beamline/` as well as the source.

Internal imports become relative. The tool does not change aliases or bundler configuration, so keep an existing `@/` alias. Keep customer brand files, presets, wrappers, and other customer files outside the managed folder.

Full-source components use Tailwind v4; keep the app's existing integration. For Vite without one, install `tailwindcss` and `@tailwindcss/vite`, then add `tailwindcss()` alongside the React plugin. For Next.js App Router, use `@tailwindcss/postcss` in the app PostCSS configuration. Import Tailwind and the managed `styles/beamline.css` once. The managed entry imports fonts, source theme, foundation, and chosen looks and declares Tailwind's source scan; CSS modules travel with components. Use plain `@import 'tailwindcss'`, not `theme(inline)`. In Next.js App Router, keep stateful forms in a client component and import global CSS in the layout; in Vite, import it in the entry file.

Third-party packages remain ordinary npm dependencies. `--install` installs them only after source is accepted; without it, run `deps --install` later. If package-manager installation fails, accepted source remains recorded and the tool explains how to retry. Source acceptance and package resolution are separate outcomes. The skill installer supports `--source --full-source --entries settings,data-grid --preset prism`; use `--no-install` to prepare source without dependency installation. The hosted MCP `beamline_install` accepts `source: true` and returns the command and local-import guidance. After installation, building and running need no Beamline connection.

Existing `add --shared-base` installs keep components under `src/ui/beamline` while runtime, foundation, looks, and fonts remain `@beamline/*` packages. Locks without a full-source marker retain that arrangement and its imports on upgrade. To migrate to full source:

1. Replace app-owned `@beamline/*` imports with corresponding local paths.
2. Remove Beamline dependencies and overrides from the app manifest.
3. Rerun `add <entries> --full-source` with the existing entry selection at the accepted release.
4. Resolve source merge conflicts and install dependencies.

Do not mix a local runtime with compiled Beamline components. Full-source files, runtime, foundation, selected looks, and fonts can all be local.

Source: [Install](https://beamline.io/docs/install).

### Beamline shadcn registry source installation

In a project already known to shadcn through `components.json`, add a free element directly or register Beamline and add an element by name:

```sh
npx shadcn@latest add https://beamline.io/registry/segmented-control.json
npx shadcn@latest registry add @beamline=https://beamline.io/registry/{name}.json
npx shadcn@latest add @beamline/segmented-control
```

Paid elements require a key created under the account's Keys for shadcn area, stored as `BEAMLINE_KEY` in `.env.local`, and configured as a bearer authorization header in `components.json`.

Registry files land as local source under `src/ui/beamline`, including the free runtime/foundation base; this path installs no `@beamline/*` packages or hosted runtime. Font bytes are embedded in editable local CSS with OFL notices. After Tailwind, import the shared `styles/beamline.css` once, then selected look styles such as `look-prism.css`; the shared base precedes each look and is not repeated between looks. Wrap the app in `UIRoot` using local runtime and look imports.

In registry naming, `@beamline/setup` is the free source base; `@beamline/look-metal` adds Metal, and other looks follow `look-<name>`. Paid looks require a key. Use the local setup function named by the install result where a look requires browser setup.

shadcn overwrites files by replacement and creates no managed Beamline baselines. Existing shadcn apps retain old base packages until imports and dependencies are explicitly migrated.

Source: [Install](https://beamline.io/docs/install).

### Beamline 0.1 bundle installation and imports

Install a 0.1 bundle using its original package instructions:

```sh
npm i /path/to/beamline-ui-0.1.x/package/beamline-ui-0.1.x.tgz
```

Import components through `@beamline/ui/<id>`, import `@beamline/ui/styles.css`, and render components inside `UIRoot` from `@beamline/ui/ui-root`. Do not apply 0.2 setup instructions to 0.1 package contents.

A legacy source lock containing only hashes requires its original baseline bundle and a manual migration. The new source tool refuses to invent missing baselines.

Source: [Install](https://beamline.io/docs/install).

## Data, persistence and service boundaries

### Beamline Component Data and Callback Boundaries

Beamline components accept plain data through props and report interactions through callbacks; they do not fetch data. The application retains control of requests, persistence, authentication, permissions, and network state. The same interface may use a REST API, an existing cache, a server-rendered page, or a live stream; Beamline does not provide those services. Beamline documentation covers integrations for market data in trading, tables, streaming AI text, and complete screens.

Place a small adapter between the API and each component to validate or decode responses and map fields once. Prop names vary: `DataGrid` takes `data` and `columns`, `OrderBook` takes `bids` and `asks`, and `PriceChart` takes `candles`. Inspect the installed declarations or props rather than assuming every component takes `rows`.

A controlled `value` paired with `onValueChange` lets the application own a field; `defaultValue` lets the component own its draft. A changed value does not prove that a record was saved.

Callback contracts differ. `Form.onSubmit` and `Settings.onSave` await promises, so reject the promise when persistence fails. `DataGrid.onCellEdit` returns `void`, so the application must manage pending and error state and update `data` itself. Do not assume every callback waits for an external operation.

Keep numbers raw and format them at the view boundary with the component's formatter. Preserve an unmeasured value as `null` where the prop accepts it; do not turn it into zero with `Number(value)` or `value || 0`. A measured zero remains zero. Numeric displays may show a hatch or missing marker, and grid cells support `nullLabel`. Array props still require arrays: use `[]` with loading or error state, not `null` cast as an array. Market candle timestamps use UTC seconds, not JavaScript milliseconds.

Source: [Connect your data](https://beamline.io/docs/data-integration).

### Authentication and Authorization Boundaries for Beamline Apps

#### Proven and untested integrations

Clerk is the only authentication-provider integration stated as proven in Beamline’s own service: beamline.io uses the kit’s `Auth` block for email-code and Google/GitHub flows. This is evidence about that service only; it does not establish that every Clerk flow or authentication control has been tested. The kit does not include a configured Clerk project, session store, or server authorization layer.

Auth.js, Supabase Auth, Firebase Auth, Auth0, and existing cookie/session APIs are documented as untested integration patterns. The application must implement the installed `Auth` or `SignIn` callbacks using the provider SDK or session endpoints, and validate redirects, session creation, errors, and sign-out. With Clerk, map provider results into the sign-in and sign-up handlers and activate the session before reporting success.

#### Server-side authorization and streaming

A hidden button or an authentication success screen is not authorization. Protect reads, writes, and stream subscriptions on the server, regardless of what the UI displays.

Browser `EventSource` and `WebSocket` constructors do not accept an arbitrary Authorization header. Use the application’s same-origin authenticated endpoint or a server-issued, short-lived connection mechanism. Do not put a long-lived API key in a URL or browser bundle. Provider credentials remain with the application.

Source: [Connect your data](https://beamline.io/docs/data-integration).

### Next.js Data Boundaries for Beamline Deals

#### Public demonstration route

In the example, `app/page.tsx` fetches public demonstration deals from a server-only `BACKEND_ORIGIN`, using `cache: "no-store"` so the data is fetched per request. Configure the origin on the server; never take it from user input. Validate the response with `dealsSchema`, then pass its serializable records to the client-owned `DealsView`, a small client view.

#### Private data

Fetch private records in a Server Component, checking the real session and authorization there before fetching. Pass serializable records to the client view, and do not share a cached response across users.

#### Client and write boundaries

Keep hooks, event handlers, column render functions, `UIRoot`, and interactive controls on the client side. A normal callback cannot be passed from a Server Component to a Client Component. Server Actions can provide another write boundary, but the server must still authorize the operation.

Keep secrets in server-only modules; never put them in a `NEXT_PUBLIC_` variable or a client prop. Give the route loading and error boundaries appropriate to the application.

Source: [Connect your data](https://beamline.io/docs/data-integration).

### Beamline data-integration examples in React apps

#### Required Beamline parts

The examples use these selected compiled packages:

- `@beamline/data-grid`
- `@beamline/table`
- `@beamline/admin-users`
- `@beamline/pagination`
- `@beamline/order-book`
- `@beamline/price-chart`
- `@beamline/form`
- `@beamline/input`
- `@beamline/button`

Install the required parts through the kit installer. An aggregate kit exports the same parts under `@beamline/ui/<id>`.

#### App setup and example dependencies

Import the generated stylesheet once and wrap the app in `UIRoot` with the installed look. The examples also use app-owned libraries:

- Zod 4 for response validation.
- React Hook Form 7 for form state.
- TanStack Query 5 or SWR 2 in the cache example.

#### Files and API routes

Put the example files beside one another, except for the Next.js page, whose path is `app/page.tsx`. Replace example `/api/...` routes with the app’s routes. The payloads are example contracts, not endpoints supplied by Beamline.

Source: [Connect your data](https://beamline.io/docs/data-integration).

### Deals REST Loading, Validation, and Pagination for Beamline Grids

#### Response validation

The example deals endpoint returns `{ data, total }`: `data` is an array of deals, while `total` counts all matching records. Validate JSON with Zod at the adapter boundary before passing it to the grid. Require each deal to have a nonempty string `id`, a string `name`, and `amount` as either a finite number or `null`; require `total` to be a nonnegative integer. The fetcher must check `response.ok` and parse the JSON with that schema. A malformed response or non-success HTTP status is an error and must never reach the grid as a successful result. Use `credentials: "same-origin"`; the fetcher accepts an optional `AbortSignal`.

#### Page requests and cancellation

Keep `page`, `loading`, `result`, and `error` in application state. Request page 1 initially and use `pageSize = 20`. Treat the example endpoint as a path without a query string; send `page` and `pageSize` as query parameters.

Create an `AbortController` for each effect request, pass its signal to the fetcher, and abort that controller in effect cleanup. Ignore results from aborted requests so a slow earlier response cannot overwrite a newer one.

On page changes, clear old page data while fetching unless the UI can identify those rows as previous data. If retaining rows during refresh, label them as previous or stale data and keep the previous page number displayed until the new page arrives; never label page 1 rows as page 2.

#### Result, error, and pagination states

On first load, use `data = []` with `loading` on. A successful empty result has empty data, loading off, and no error. A failed request must report an error and offer a real app-owned retry; do not present failure as a fabricated empty success. A failed refresh may retain previous records only if they are clearly marked stale.

Render pagination only when a result exists and `total > 0`; calculate `pageCount = Math.ceil(total / pageSize)`. Reset or clamp the current page when filters or deletion change the result count.

Source: [Connect your data](https://beamline.io/docs/data-integration).

### React Hook Form Profiles and Grid Edits: Confirmed Saves

#### Confirm persistence before success

A successful HTTP response must mean the server accepted and persisted the operation. Check the response status, decode and validate its contents, and report success only after that confirmation. A toast proves only that the UI displayed a response.

#### React Hook Form profile example

The example uses React Hook Form 7 and Zod 4. Its schema trims `name` and requires it to be nonempty. On submit:

1. Clear prior errors.
2. POST JSON with `credentials: "same-origin"`.
3. Check `response.ok`, decode the response, and validate the returned profile.
4. Reset the form to the server-confirmed profile and only then report success, such as with “Profile saved.”

The example disables editing while the save is in progress. If a save fails or the request rejects, keep the draft available for retry. If editing remains enabled during a save, merge the response against the submitted snapshot so an earlier response cannot erase later typing.

Client validation helps the user but does not replace server-side input validation, authentication, authorization for the record, or the app’s CSRF policy. For structured validation responses, validate the response shape and map only known field errors through `form.setError`; do not trust arbitrary response keys.

A lost response can leave the outcome of a non-idempotent operation unknown. Read the record back before retrying rather than assuming the operation failed.

#### Grid edits and persistence checks

The same confirmation rule applies to grid edits: `DataGrid.onCellEdit` is a `void` notification, not proof that a save occurred. Report success only when the server confirms persistence.

Verify the actual persistence boundary, including after a reload, and test rejection without mutation and a controlled failure.

Source: [Connect your data](https://beamline.io/docs/data-integration).

### Application Cache Ownership for Beamline Views

Reuse the application's existing cache rather than creating a second cache in a component or fetching the same resource again. Include the resource, account or tenant, and query parameters in cache keys; clear private cached data on sign-out.

After a successful mutation, invalidate the relevant TanStack Query key or call SWR's `mutate` for the relevant resource. Keep mutation errors separate from read errors, and replace cached rows only with a confirmed response. The documented examples demonstrate reads only; choose and test the application's invalidation and optimistic-update policy before using a cache for writes.

The documented React Query and SWR alternatives use the same `readDeals` adapter and grid view; choose one. React Query requires a `QueryClientProvider` above the view, and its example creates a client per mounted provider. The documented example dependencies are TanStack Query 5 and SWR 2.

Source: [Connect your data](https://beamline.io/docs/data-integration).

### Beamline Data Views: Loading, Errors, and Stream States

- **First request:** Pass `data={[]}` with `loading` while the request is in flight; do not make the collection appear successfully empty.
- **Successful empty response:** Turn loading off and show a specific empty message.
- **Failure:** Report the error and offer a real retry rather than presenting an empty success.
- **Failed refresh:** If previous records exist, keep them visible and label them as stale or no longer current.
- **Disconnected stream:** Keep the last received data visible and show a connection warning.

Use each installed component's API. `PriceChart` supports `loading`, `error`, and `stale`; `OrderBook` has no general loading or error prop, so show its status beside the component. For screen-level states, the kit also provides `Skeleton`, `EmptyState`, and `Alert`.

Source: [Connect your data](https://beamline.io/docs/data-integration).

### Beamline import mapping: application handler responsibilities

The application must implement the handlers for import assessment, import execution, and retry behavior. The gallery’s import assessment is a labelled demonstration only; it is not a real import service.

Source: [Known limitations](https://beamline.io/docs/limitations).

### Voice adapters in `@beamline/ui/lib/voice`

Voice components accept provider adapters through `@beamline/ui/lib/voice`. The only shipped adapter is `browserSpeechToText()`, which uses the browser’s own speech recognition and requires no key or payment. Recognition quality and privacy depend on the browser vendor: some browsers send audio to their servers, and some do not support speech recognition.

To connect a hosted speech or voice-agent service, the application must supply an adapter. The speech-input and conversation-bar entries describe the integration shape. Microphone switching during a call reaches the service only if its adapter implements `replaceStream`.

The gallery’s voice audio consists of placeholder tones with synthetic timings.

Source: [Known limitations](https://beamline.io/docs/limitations).

### Beamline video player: formats and streaming limits

The player supports video formats that the browser can play natively, including MP4/H.264 and WebM. It does not provide adaptive streaming such as HLS or DASH. Picture-in-picture is available only when the browser offers it.

Source: [Known limitations](https://beamline.io/docs/limitations).

### Streaming Markdown Responses in Beamline Apps

`Response` renders Markdown as text arrives. To smooth bursts, use `useSmoothText` from `@beamline/hook-use-smooth-text`, passing it the received text and a `streaming` flag.

The application owns cancellation, transport errors, and provider billing. Do not silently present an interrupted answer as complete.

Source: [Connect your data](https://beamline.io/docs/data-integration).

### SSE/WebSocket Snapshots for Market Views

Normalize exchange-specific messages in one server-side or app-owned adapter. Treat each full snapshot as containing an increasing integer `sequence`, `bids` and `asks` as `[price, size]` levels, a nullable last price, candles, and deals.

#### Validate and bound snapshots

- Validate a snapshot before replacing displayed data. Prices must be finite and positive; sizes and volumes must be finite and nonnegative. A last price may be null.
- Candle times must be nonnegative integer UTC-second timestamps, unique and strictly increasing, with candles ordered oldest first.
- Ignore snapshots whose `sequence` is not greater than the last accepted sequence. Reject invalid snapshots and retain the last valid data.
- Keep at most the first 50 bid levels and first 50 ask levels, the last 500 candles, and the last 100 deals.

#### Freshness and view status

Represent status distinctly enough to show connecting, live, reconnecting, disconnected, invalid snapshot, and stale states. The example marks data stale when no snapshot has arrived for more than **15 seconds**, checking once per second; adapt that threshold to the feed’s heartbeat and market hours. Retain the last snapshot on invalid data or disconnection.

Pass `stale` to `OrderBook`; show connection and error status beside it because it has no general loading or error prop. Pass `stale` to `PriceChart`, which can show loading while awaiting its first snapshot. Other views need adjacent status and error information.

#### SSE and WebSocket lifecycle

Serve SSE with `Content-Type: text/event-stream`, separate events with a blank line, and flush each event. Disable proxy buffering and compression that holds small chunks; the example uses `Cache-Control: no-cache, no-transform`. Confirm that the browser receives the first event before the connection closes. Default SSE messages use the `message` event and `onmessage`; named events require `addEventListener` for that event name.

Browsers reconnect SSE automatically; in the example, a button initiates WebSocket reconnection. After either reconnection, the server must send a fresh full snapshot. Keep sequence numbers increasing within a connection; if they can reset without a new connection, use a server-owned session epoch.

#### Deltas and rendering performance

For an exchange delta protocol, load a snapshot, apply only correctly sequenced deltas, and request a new snapshot after a gap. Coalesce fast updates before setting React state while preserving the events needed to compute the book. Avoid unbounded appends and sorting a large dataset on every tick. A chart may update its appended or amended final candle in place, but must receive a new array when data changes. Remount the connection when its resource changes so old-symbol data cannot flash under a new symbol.

These samples render market data; they do not execute trades.

Source: [Connect your data](https://beamline.io/docs/data-integration).

### TradingTerminal account and order adapter

The application’s account adapter supplies `balance`, `positions`, `orders`, `fills`, `submitOrder`, `cancelOrder`, and `closePosition`, mapping order requests to the application’s order API. Provide this account to `TradingTerminal` as a `TradingAccount`.

If the adapter exposes a `useExchangeAccount` hook, call it at the top level inside the React component and pass its returned account to the terminal. This hook is application-owned, not a Beamline export.

A confirmed submission returns the declared filled or resting result; a rejection throws an `Error`. If the outcome is unknown, reconcile with the server before allowing a duplicate submission. The terminal does not fill orders. Verify this boundary with a non-production account.

`usePaperAccount` from `@beamline/demo-paper-account` fills paper orders in the browser. It is a simulation, not an exchange adapter.

Source: [Connect your data](https://beamline.io/docs/data-integration).

### TradingTerminal market-feed adapter and data

#### Component and shared types

Import `TradingTerminal` from `@beamline/trading-terminal`. It takes `symbols`, a `useFeed` hook, and a `TradingAccount`. Shared types from `@beamline/lib-market` include `Candle`, `Level`, `Trade`, `MarketFeed`, `FeedRequest`, `UseMarketFeed`, `Ticker`, `Position`, `OrderRequest`, and `TradingAccount`. Inspect the installed declarations for the release in use.

#### Feed lifecycle and prices

The terminal calls `useFeed` once per open market and remounts it when the symbol or interval changes. The hook returns candles, book levels, trades, `last`, `prev`, and `change`.

`MarketFeed` currently requires numeric prices. Load and validate an initial snapshot outside the terminal, then initialize `useFeed` from the adapter’s cached snapshot on its first call. If prices are not yet measured, use that snapshot rather than seeding missing prices with zero.

`useMockFeed` from `@beamline/demo-market-simulator` is a random-walk simulation, not an exchange feed adapter.

Source: [Connect your data](https://beamline.io/docs/data-integration).

### Beamline 0.2 incomplete-data summaries, charts, and totals

In Beamline 0.2, affected summaries no longer silently count missing chart values, targets, source chunk counts, holding prices, costs, or cash as zero.

- Partial pie or bar-list data no longer claims percentages of a whole total.
- Missing candle volume produces a chart gap.
- Wholly unmeasured invoice lines no longer produce zero subtotals, and unset allocation changes no longer produce numeric deltas.

These corrected unmeasured states can change displayed totals when data is incomplete.

Source: [Upgrade](https://beamline.io/docs/upgrade).

## Lists, filters and record selection

### Shared list state and paging — Beamline record views

#### Shared contract and ownership

- The shared types are `ListProps`, `ListSort`, `ListFilters`, and `ListWindowProps`, exported from `ui/lib/list.ts` and compiled as `@beamline/lib-list`. Paging state and controls live in internal helpers; product code must not import those helpers.
- DataGrid, Table record mode, and record-list screens can use the same list state. `manual` defaults to `false`. When it is `true`, the caller owns ordering, filtering, search, and page slicing; supplied rows must already be the requested result, so do not sort or filter that page again. Keep toolbar callbacks, but make them request another result. List controls are optional, so existing native tables and local screens can retain their interactions.
- Table retains native compound children. Its optional `data`, `columns`, and `getRowId` props activate record mode; `className` and its ref still address the native table.

#### State

- `page` / `defaultPage` / `onPageChange` use positive, one-based integers; the default is 1.
- `pageSize` / `defaultPageSize` / `onPageSizeChange` use positive integers; the default is 25. Changing page size resets the page to 1.
- `sort` / `defaultSort` / `onSortChange` use an ordered `{ id: string, desc: boolean }[]`; `[]` means unsorted. In local mode, missing sort values go last in either direction.
- `filters` / `defaultFilters` / `onFiltersChange` use `Record<string, string[]>`. Keys identify fields or documented view presets; an absent key or empty array clears that filter.
- `query` / `defaultQuery` / `onQueryChange` use a string, including the controlled empty string `""` to clear the query.
- Query, filter, and sort actions reset the page to 1.

#### Counts and pagination

- `rowCount` is the number of matching records before paging. In manual mode, pass `null` when the server cannot count them and provide `hasMore`; omission also means unknown count where the contract allows it. A real count of zero means the result is empty. In local mode, derive the count before slicing.
- Supplying a `page`, `pageSize`, or `rowCount` prop enables pagination; `pagination={false}` suppresses it. If the count is unknown, `hasMore={false}` disables Next.
- `Pagination.pageCount` always counts pages, not records. Deprecated `total` is an alias for `rowCount`; an explicitly supplied `rowCount`, including `null`, takes precedence.

Source: [Connect your data](https://beamline.io/docs/data-integration).

### Domain filters and record props — Beamline product views

#### Record props by surface

Shared list state does not replace each surface's domain record props. Use the required props for each view:

- DataGrid and Table use `data`, `columns`, and `getRowId`.
- CRM, Invoices, OrdersAdmin, and AdminUsers use `deals`, `invoices`, `orders`, and `users`, respectively. Blocks retain domain props such as `deals`, `users`, `invoices`, or `tickets` rather than being replaced by Table props.
- AgentRuns uses `runs`; WorkflowBuilder attaches the shared list state to run history as `runsList={list}`.
- SocialScheduler uses `posts`, `view="queue"`, and `queueList={list}`. Its per-post publishing `onRetry(id)` is separate.
- OrderHistory uses `orders`, or in manual `view="fills"` mode uses a `fills` page of `HistoryFillRow` records. Each fill includes its parent `order`, and `rowCount` counts fills.
- Portfolio, MarketScreener, Watchlist, and PositionsTable use `holdings`, `markets`, `items`, and `positions`.
- HelpdeskInbox uses `tickets` plus `agents`/`me`; InboxList uses `items`; SupportConsole uses `conversations` plus `me`.
- AuditLog and ChangelogFeed use `entries`; Timeline uses `events`.
- TeamMembers, Projects, FileManager, and Integrations use `members`, `projects`, `items`, and `apps`.
- KnowledgeBase uses `sources`; ConversationHistory uses `threads`.
- Billing uses `invoices` plus `plan`; ApiKeys uses `keys` or `webhooks` for the active tab.

#### Domain filter semantics

The application adapter must implement each screen's domain meaning for filters:

- CRM: `scope` is `all` or `mine`.
- MarketScreener: `screen`.
- HelpdeskInbox, TeamMembers, and SupportConsole: `view`.
- Invoices: `state`.
- OrdersAdmin: `view` is `all`, `fulfil`, `unpaid`, or `returns`.
- ChangelogFeed: `tags`.
- Projects: `health`.
- Integrations: `category`.
- KnowledgeBase: `attention`.
- ConversationHistory: `bookmarked`.
- AuditLog: `area`, `actor`, `from`, and `to`; `from` and `to` are ISO date strings for the range.

Unknown field IDs do not filter local rows. Folder and reporting-range changes use the screen's domain navigation callbacks. If a screen has multiple record views, change request scope and `rowCount` with the active view.

#### Embedded-view boundaries

The shared state applies only to the record list in an embedded surface: WorkflowBuilder's run list is separate from editor-node selection; SocialScheduler's post queue is separate from calendar layout and per-post publishing retry; and ApiKeys applies it to the active keys or webhooks tab. Charts, navigation choices, editor nodes, and parts of one record are not record-list state.

Source: [Connect your data](https://beamline.io/docs/data-integration).

### Server requests and retries — Beamline record lists

#### Request ownership and adapter

- The application owns fetching, response validation, cancellation, and persistence. In manual mode, toolbar callbacks request a new result rather than transforming the loaded page.
- The example adapter sends `page`, `pageSize`, `query`, JSON-encoded `sort`, and JSON-encoded `filters`. Reset the page to 1 when page size, query, sort, or filters change.
- Validate the response at the boundary before decoding rows. The expected shape is `{ rows, rowCount, hasMore }`: `rows` must be an array, `rowCount` must be `null` or a nonnegative safe integer, and `hasMore` must be boolean. Then decode the rows.
- Keep `decodeRow` stable outside the component or memoize it; otherwise renders can trigger another request. Abort superseded requests.

#### Loading and retry

- `loading`, `loadingMore`, and `error` describe caller-owned request facts, not editable list state; they have no change callbacks. `error` may be a message, React node, or `Error`; an `Error` renders its message.
- Keep the last completed rows while a request is pending or fails, including when a fetching library temporarily supplies `[]`. Show the error and retry without losing selection. A successful empty response clears the old rows.
- `onRetry()` requests the failed page again. A rejected request requires explicit retry; do not start an automatic retry loop.

#### Append loading

- For append mode, omit page props and supply `onEndReached`, `hasMore`, and `loadingMore`; the application appends received rows. Append mode disables inferred page controls.
- Stop requests when `hasMore={false}`. If the `onEndReached` callback rejects, stop further requests until an explicit retry.

Source: [Connect your data](https://beamline.io/docs/data-integration).

### Selection and record opening — Beamline server-backed lists

#### Selection

- In list mode, `selected` is an array of stable string row IDs and has `defaultSelected` and change-callback forms. Selection survives page changes and includes IDs from other server pages. Never derive IDs from row indices or silently discard IDs for records not currently loaded.
- `selectable` exposes selection controls. Custom list screens use the shared selection menu when `selectable` is enabled.
- The application must apply bulk mutations to the selected IDs; a component's local mutation handler cannot update unloaded records.
- For a server-wide bulk action, opt into `allowSelectAllMatching` and handle `onSelectAllMatching({ rowCount, query, filters })`. This is an explicit request and does not create IDs for unloaded rows; the application owns the server-side action.

#### Opening records

Opening one record is separate from list selection. Use the screen's opening action, such as `openUser`, `openTicket`, `openSource`, or `openConversation`, rather than treating an opened record as the list selection.

Source: [Connect your data](https://beamline.io/docs/data-integration).

### Loaded-data windows and metrics — Beamline lists and charts

- Virtualization reduces the number of rendered rows, not the download cost. Bound live-stream and chart windows.
- Metrics calculated from supplied records describe only those loaded records. Pass separate account-wide metrics when full-dataset metrics are needed.
- A Watchlist keeps its chosen local order while market values tick; manual mode preserves server order.

Source: [Connect your data](https://beamline.io/docs/data-integration).

### Legacy props and retry compatibility — Beamline record screens

#### Legacy aliases

- Preserve old spellings and callback payloads. In development, warn once after mounting.
- Provide corresponding default and change forms for these legacy mappings:
  
  - Crm `scope` maps to `filters.scope`.
  - MarketScreener `screen` maps to `filters.screen`.
  - HelpdeskInbox, TeamMembers, and SupportConsole `view` map to `filters.view`.
  - ChangelogFeed `filter` maps to `filters.tags`.
- KnowledgeBase and SupportConsole retain scalar `selected` as an alias for opening a record: `openSource` for KnowledgeBase and `openConversation` for SupportConsole. Array `selected` means list selection. The array callback contract is opted into by `manual`, `selectable`, array-valued `selected` or `defaultSelected`, or the new open-record props.
- AuditLog retains legacy structured `onFiltersChange(AuditFilters)` until `manual`, `filters`, or `defaultFilters` opts into the standard field-map contract.

#### Retry callback meanings

- For AgentRuns, use `onRetryRun(id)` for execution retry. The legacy one-argument `onRetry(id)` remains an execution callback outside manual or load-error mode; standard zero-argument `onRetry()` retries loading the list.
- When both old and new names are supplied, the new names win.

Source: [Connect your data](https://beamline.io/docs/data-integration).

### Beamline rc.33+: filter prop migrations

Use the shared filter props as follows:

- **CRM:** Replace `scope`/`defaultScope`/`onScopeChange` with `filters.scope`/`defaultFilters.scope`/`onFiltersChange`. Filter values are arrays, for example `{ scope: ["mine"] }`.
- **MarketScreener:** Replace `screen`/`defaultScreen`/`onScreenChange` with `filters.screen`/`defaultFilters.screen`/`onFiltersChange`; for example, `{ screen: ["gainers"] }`.
- **HelpdeskInbox, TeamMembers, and SupportConsole:** Replace `view`/`defaultView`/`onViewChange` with `filters.view`/`defaultFilters.view`/`onFiltersChange`. Domain values stay the same.
- **ChangelogFeed tags:** Replace `filter`/`defaultFilter`/`onFilterChange` with `filters.tags`/`defaultFilters.tags`/`onFiltersChange`. Keep the same tag IDs under `tags`.
- **AuditLog:** Replace structured `onFiltersChange(AuditFilters)` with standard `onFiltersChange(ListFilters)`. Set `manual`, `filters`, or `defaultFilters` to use the new callback; old calls retain the `query`/`areas`/`actor`/`range` payloads. Use `AuditLogListProps` for the field-map callback.

Source: [Upgrade](https://beamline.io/docs/upgrade).

### Beamline rc.33+: KnowledgeBase and SupportConsole selection

KnowledgeBase and SupportConsole use open-record props to identify the displayed record separately from list selection.

- **KnowledgeBase:** The legacy scalar `selected`/`defaultSelected`/`onSelectedChange` inputs map to `openSource`/`defaultOpenSource`/`onOpenSourceChange`. Old scalar calls and their string callback remain supported; array selection uses the standard names.
- **SupportConsole:** The legacy scalar `selected`/`defaultSelected`/`onSelectedChange` inputs map to `openConversation`/`defaultOpenConversation`/`onOpenConversationChange`, separating the open detail from bulk selection.

For both components, `manual`, `selectable`, array-valued `selected`/`defaultSelected`, or the new open-record props opt into array callbacks. The old `…Props` interfaces retain scalar compatibility; `…ListProps` describes the standard array form. A controlled empty array means selection is cleared.

Source: [Upgrade](https://beamline.io/docs/upgrade).

### Beamline rc.33+: pagination and end-of-list loading

As of Beamline rc.33, existing calls continue to work. New names win; deprecated inputs warn once after mounting in development and remain silent in production.

List components make no requests themselves. Use the server-list recipe for the associated integration pattern.

- **Pagination:** Replace `total` with `rowCount`. `rowCount` counts records, not pages; `pageCount` keeps its existing unit. Explicit `rowCount={null}` wins over `total`.
- **Billing and AuditLog:** Replace `onLoadOlder`/`hasOlder` with `onEndReached`/`hasMore`. The legacy `onLoadOlder` callback returns arrays that the block appends; with the new `onEndReached` callback, the app appends. Legacy buttons remain explicit; new end-reached callbacks can load when the end becomes visible.
- **ChangelogFeed:** Replace `onLoadMore` with `onEndReached`; `hasMore` keeps its name. Rejected requests require an explicit retry.

Source: [Upgrade](https://beamline.io/docs/upgrade).

### DataGrid height in Beamline 0.2.0-rc.21

In Beamline 0.2.0-rc.21, DataGrid `height` became the maximum height the grid can grow to. With only a few rows, the grid is only as tall as those rows, and content below it moves up.

To preserve a fixed-height box, give the grid’s container that height and pass `height="fill"` to the grid.

Source: [Upgrade](https://beamline.io/docs/upgrade).

### Settings without navigation in Beamline 0.2.0-rc.22

In Beamline 0.2.0-rc.22, `<Settings navigation="none">` no longer paints its own background or centers its column. It uses the page background and starts at the page’s left edge. Give its container the same padding as other page content, matching the surrounding content.

The profile section’s help text changed to neutral wording. Update tests that match the previous sentences.

Source: [Upgrade](https://beamline.io/docs/upgrade).

## Custom styles and component contracts

### Beamline custom styles: parameters and app integration

Start with a built-in look, then make it your own through fonts, public tokens, radii, density and motion. Keep custom style files in your app; you can work directly in React and CSS, with the local style tools, or with a coding agent. A token change reaches the components that read it.

A custom style can set a base look, pins, foundation parameters, and explicit token overrides. Use `style parameter` for foundation parameters such as `radiusScale`, `motionScale`, `displayScale`, and `brand`. A style does not set size tokens such as `--pui-text-*`, spacing, or control heights; those come from scale parameters, keeping one size system across looks. To alter a token, use `style set`; it accepts only tokens declared by the base look.

Use `style modifier <file> motion 1.25` to compose a motion modifier with the app’s speed. Reduced-motion and `none` policies still take precedence. To fix a token while scaling, use `style pin <file> --token --pui-radius-control --reason "Part of our identity"`; `style unpin` removes that constraint.

`displayScale` is the separate Hero size control: it affects only `.text-display` / `--pui-text-display`, the screen’s single lead headline. Its range is 0.75–1.75, applied after type size and contrast. The studio labels the slider **Hero size**, and `displayScale` travels with the style as a `UIRoot` prop.

For direct integration, import the selected base look’s stylesheet and the custom stylesheet. Pass a canonical preset reference to `UIRoot`, for example:

```tsx
<UIRoot preset={{ id: "acme/calm", visualContract: 1, base: "prism" }} radiusScale={1.5} motionScale={2}>
  {children}
</UIRoot>
```

Custom IDs do not go in the deprecated built-in `look` prop. A custom preset passed to `UIRoot` is a pure reference, so import its stylesheet explicitly to make its CSS available. A custom look implementing visual contract 1 supplies complete CSS; external IDs need no runtime registry or edit to the built-in `Look` type. Custom preset fonts must be selected and installed explicitly.

Static-scope CSS includes suggested numeric, focus, and brand parameters. `UIRoot` props also carry density and motion policy to stable portals. An example’s **Use with my agent** instruction and downloaded choice include its custom style, active foundation parameters, and explicit token overrides. Shared preview links reproduce those settings without using the recipient’s saved appearance, but do not replace the recipient’s saved studio library.

Source: [Customise](https://beamline.io/docs/customize).

### App-owned Beamline styles in `src/styles/beamline`

Customization files belong outside the managed component tree. Component-source upgrades leave `src/styles/beamline` alone, and the app-owned directory and customer edits survive source and package upgrades. `style add` stores the custom `.style.json`, `.preset.css`, and `appearance.ts` there by default. Its `beamline.css` imports the selected base look's structural stylesheet, so keep that look in the upgraded package selection.

`style add` accepts style JSON or studio-exported preset CSS; its base look must be installed from the app's accepted release. To change a style, edit its source, run `style validate`, and run `style add`. Reapplication uses the app's accepted release and refuses to overwrite files edited since the last application.

Specify the file containing the intended single `UIRoot` imported from `@beamline/runtime` or `@beamline/ui/ui-root`; named aliases are supported. The default destination is `src/styles/beamline`; use `--styles-to` to change it. The destination holds editable source, complete preset CSS, saved `UIRoot` props in `appearance.ts`, and the app's one Beamline stylesheet. The stylesheet and appearance module import the custom preset.

The command adds one appearance import and props spread while preserving children, event handlers, existing imports, and the client directive. Appearance-owned props (`preset`, `look`, and written brand, scale, motion, density, or tokens) are removed from the root, reported with their previous source, and replaced by the spread at the first removed prop's position. Props after the spread remain and win. A look import used only by removed props is removed too. If the app uses another wrapper, apply generated appearance props there as normal integration work.

Repeating the same command adds nothing twice. Reapplying after editing the original updates only files still matching the previous application and names customer-edited files.

Source: [Customise](https://beamline.io/docs/customize).

### Beamline 0.2 React 19 refs and component styling roots

Use `className`, `style`, or `classNames.root` for a documented visual root; `classNames` targets documented composite parts. Component CSS is in `@layer components`, below normal unlayered app CSS and Tailwind utilities. The cascade depends on layers, importance, and specificity; putting a class last does not guarantee it wins.

A scoped root is a styling boundary, not a guarantee that every third-party component, global reset, portal, or CSS rule will coexist unchanged. Check the actual combined screen and its open menus/dialogs; the [documented install results](https://beamline.io/docs/install#package-managers-checked-11-october-2026) name combinations exercised, and [known limitations](https://beamline.io/docs/limitations) names gaps.

In React 19, `ref` is a prop targeting the documented native element; existing `forwardRef` components remain valid. Button and Table share the ref with their visual root. Input's ref, native attributes, and value target the native input; Select's ref targets its trigger. Input and Select use a field wrapper as visual root. In 0.2, Input `style` targets the wrapper like `className`; use `classNames.input` for the native input. Select uses `classNames.trigger`.

For Input, PasswordField, SearchField, and Textarea, `className` and `style` address the visual field root. These fields previously passed `style` to the native input; move native-only styling to a class and use the documented native-input part, for example `classNames={{ input: 'my-input' }}`. `classNames.frame`, `label`, `description`, and `error` target shared parts; picker class names are defined by props and metadata. Native values, attributes, and refs retain their native-control target.

Textarea and SearchField compose forwarded refs with internal measurement/focus behavior, including callback cleanup. Textarea row bounds use actual line height so `rows` and `maxRows` remain consistent with larger type or input-slot line-height changes. VoicePicker `style` targets its labelled wrapper; use `classNames.control` for the inner control row. Labelled Checkbox and Switch `style` targets the labelled wrapper; use `classNames.control` for the button, while refs still target the native control. Bare Checkbox and Switch retain the button as their styling root.

Chart `style`, `className`, and `ref` address the figure; `classNames` exposes documented frame, tooltip, and slice-list parts where present. Plot measurements exclude customer-added padding. Styling or slotting must not replace measurement refs, keyboard behavior, handlers, or ARIA relationships.

Only triggers and link-able parts accept `asChild` with Radix Slot; other components are not polymorphic. Text components that need polymorphism accept `as`.

Source: [Conventions](https://beamline.io/docs/conventions).

### Beamline font loading for installed and custom families

From rc.20, the next `install` adds selected looks’ typefaces as packages. Selected installs include the chosen look’s font packages in the generated stylesheet. Prism supplies Geist, Geist Mono, and Instrument Serif, and its stylesheet imports them. Text that previously fell back to the system face then uses the look’s typeface; each face loads only where text uses it.

Font package stylesheets such as `@beamline/font-<id>/styles.css` serve fonts from the app itself. These are variable fonts under SIL Open Font License 1.1, and each package includes its license. Browsers download only the faces and character sets a page uses. The full aggregate also exports `@beamline/ui/fonts.css`.

Use `remove --fonts <id>` to keep a font out. To use other faces, set `--pui-font-inter`, `--pui-font-geist`, and `--pui-font-mono-face` to the desired families, and omit the corresponding font imports from the app-owned stylesheet. Custom preset fonts must be selected and installed explicitly.

Source: [Customise](https://beamline.io/docs/customize).

### Font overrides belong on the Beamline root

Apply custom font tokens to the relevant `.pui` Beamline root or the app-owned style preset. Setting them only on document `:root` can lose to the scoped root's own values.

Use `--pui-font-inter`, `--pui-font-geist` and `--pui-font-mono-face` to replace the default families. Omit the corresponding font imports only when supplying those fonts in the app.

Source: [Customise](https://beamline.io/docs/customize).

### Beamline chart apps: SVG look runtimes (0.2 guidance)

Charts no longer initialize every built-in SVG effect on mount. Follow the selected look’s runtime setup printed by the CLI; Glass, Glow, Mono, and Ink currently declare such setup.

- **When an app offers every built-in look:** Call `ensureLookFilters()` from `@beamline/lib-look-filters` once, and retain its cleanup for teardown.
- **When an app includes selected looks rather than all built-ins:** Import only the runtime for each selected look. The named SVG-resource runtime entries are `@beamline/look-glow/runtime`, `@beamline/look-mono/runtime`, and `@beamline/look-ink/runtime`. Initialize the selected look’s `setup` and return its cleanup from an application effect. Setup shares resources within the document; the preset’s root entry remains pure.
- The reference app and gallery own this setup at the application boundary.

Chart entrances and gauge glides follow managed motion and host-local timing tokens. Changing looks does not replay a completed SVG entrance.

Source: [Customise](https://beamline.io/docs/customize).

### Explicit edge-light setup export

Import the named `setup` function from `@beamline/effect-edge-light`, call it once inside an application effect, and return its cleanup. `UIRoot` does not automatically start edge-light or every look initializer.

The optional effect does nothing on touch screens, while the pointer is still, or with reduced motion. Each look sets its colour and reach through `--m-edge-light*` tokens, or turns it off.

Source: [Customise](https://beamline.io/docs/customize).

### Beamline custom elements: look roles and authoring contract

Custom elements can use these look role classes: `m-control`, `m-solid` (set `--m-color`), `m-field`, `m-inset`, `m-toggle`, `m-knob`, `m-tag`, `m-surface`, and `m-overlay`.

Use `look-page` for a page, `look-chrome` for a sidebar (frosted in glass), and `meter` for a meter track (segmented in metal).

A new look implements visual contract 1 as complete CSS. Built-in authored recipes are in `ui/styles/preset-source.css`; `looks.css` is the explicit aggregate. The generator flattens inheritance into each selected stylesheet and validates the declared contract. External IDs require neither a runtime registry nor an edit to the built-in `Look` type.

Source: [Customise](https://beamline.io/docs/customize).

### Beamline `.pui` styling and host Tailwind cascade

All Beamline tokens and base styles must exist only inside a `.pui` root, provided by `<html class="pui">` or `UIRoot`. System-defined names begin with `pui`, including custom properties and keyframes. Components must not write to `:root`, `html`, `body`, or a global class, or define unprefixed custom properties or keyframes. Package tooling confines remaining global rules to roots.

Compiled utilities use `:where(.pui, .pui *)` in the `utilities.pui` sublayer; this scoping adds no specificity. Standard host Tailwind utilities in the direct `utilities` layer therefore win regardless of stylesheet import order: `hidden md:flex` becomes visible at `md`. This ordering applies to normal declarations in that host layer.

Source: [Conventions](https://beamline.io/docs/conventions).

### Beamline portalled UI: managed portal destinations

Use `usePortalContainer()` from `@/runtime/appearance` for portalled UI: dialogs, sheets, popovers, menus, selects, tooltips, hover cards, toasts, and floating toolbars.

Interpret its return value as follows:

- `undefined` means there is no provider and the portal may fall back to `document.body`.
- `null` means a managed scope exists but its host is pending.

When the value is `null`, keep hooks and triggers mounted, but render no portal content. Initially open overlays must mount directly in their already-styled scope host, and appearance updates reuse that destination. The old `UIRoot` component path re-exports this same runtime rather than creating another context.

Source: [Conventions](https://beamline.io/docs/conventions).

### Beamline managed component source: file, naming, and edit conventions

#### Editing managed source

With the source-copy installation (`INSTALL.md`, path B), components can be changed directly. Follow `docs/CONVENTIONS.md`, including the shared props vocabulary, `data-slot` conventions, and token-only styling, to keep edits consistent with the rest of the system.

#### Component and block layout

- A component lives in `ui/components/<id>/` and has an `<id>.tsx` file for named exports and a default export of the main component. It may also have an `<id>.module.css` file, a `demo.tsx` exporting a default `Demo[]` of live variants and states, and a `meta.json` for its catalog entry and specification.
- Composed screens use the same shape under `ui/blocks/<id>/`.

#### Names and imports

- Use kebab-case nouns for IDs, such as `order-book` or `date-range-picker`; use PascalCase for exports, such as `OrderBook`.
- Prefix compound parts with the component name, such as `DialogContent` or `SidebarMenuItem`.
- Give each component one import path, such as `import { Button } from "@/components/button/button"`. Components do not re-export other components’ parts.
- Keep internal helpers in folders whose names start with `_`; products must not import them.
- Use `@/lib/utils` for `cn()`, `@/hooks/use-motion-tokens` for host-local motion values, and `@/hooks/*` for shared hooks.

Source: [Conventions](https://beamline.io/docs/conventions).

### Beamline customer catalog metadata and component specifications

`meta.json` describes a component or block. Include:

- `name`, `title`, `category`, and `kind`
- A one-sentence `description`
- `source`, dependencies, tags, `whenToUse`, and `notFor`
- A `spec` with applicable anatomy, variants, states, keyboard behavior, motion, and props

A source example is `source: { from: "Beamline", license: "MIT", mode: "original" }`. Supported `mode` values are `original`, `wrapper`, and `vendored`.

`spec.variants` can be an object mapping names to values, such as `{ "layout": ["stacked", "side-by-side"] }`, or a list of descriptive lines when a variant needs prose. Tools print both shapes.

#### Width and catalog generation

`minWidth` is optional and measured in pixels. Elements are expected to work at 360 px and are checked at 390 px, so omit `minWidth` unless the element is not intended for a phone panel, such as a pro desktop tool. If used, set it to the width below which the element no longer reads; the gallery canvas stops there rather than at 360 px. The value flows through the gallery light index (`vite.config.ts`, `light-meta`) into `catalog.json`.

Run `node scripts/gen-catalog.mjs` to turn each `meta.json` into `catalog.json` and `CATALOG.md`.

#### Source and provenance

Vendored and wrapped code retains `source.url` and `license`. For third-party code inside an original element, list its name, licence, and URL in `source.credits`; these details flow into `NOTICE`.

Because `meta.json` ships to customers, its `source` should say only what customers may read: Beamline or the third-party package and its licence. Record research lineage—such as observed references, clean-room recreations of paid kits, and merged sources—in the internal `rules/provenance/components.json`. It is not shipped and is read by `scripts/acceptance.mjs`.

#### Categories

The supported categories are `actions`, `forms`, `overlays`, `navigation`, `layout`, `feedback`, `display`, `data`, `charts`, `trading`, `dashboard`, `ai`, `voice`, `effects`, and `blocks`.

Source: [Conventions](https://beamline.io/docs/conventions).

## Languages, formatting and direction

### Beamline UIRoot locale and direction

#### Locale and root scope

Set `UIRoot`’s `locale` to a BCP 47 language tag. If omitted, a nested root inherits from the nearest managed root; the outermost browser root uses `document.documentElement.lang`, then falls back to `en-US`. Server rendering without an explicit locale uses `en-US`; pass the same explicit locale on the server and client.

Locale follows nested roots, dialogs, and other portals. Set the resolved language on the root and its stable portal host. `inherit={false}` starts a fresh scope and resets inherited appearance, locale, and messages. A nested root can localize one workspace without changing its sibling.

#### Direction and updates

Set `dir="rtl"` separately when the layout should be right-to-left. Language selection does not silently change direction; set `dir` explicitly when the application changes direction.

Changing `locale` or `messages` on an existing root updates mounted controls and open overlays. Keep the root’s React key stable to retain drafts, selections, and focus.

Source: [Customise](https://beamline.io/docs/customize).

### Beamline message dictionaries and translation keys

#### Scope and dictionary structure

`UIRoot`’s `messages` prop accepts a typed partial dictionary keyed by `<component-id>.<key>`. Missing overrides fall back to English. Nested roots merge dictionaries by full key; omitted or `undefined` entries inherit, while `inherit={false}` starts a fresh message scope. English is the default: changing `locale` changes formatting, while supplied messages change words.

Each component or block owns English defaults in its `messages.ts` and loads them only with that component. The generated `ui/runtime/message-catalog.ts` contains types only, so importing a Button does not load copy for every block. Screen-specific dictionaries can stay with their screens and be lazy-loaded with them.

Shared field and list copy uses `_shared`; specialized helpers use namespaces such as `_shared-chart` and `_shared-voice-notice`; the free profile wrapper adds `settings-sample`. These keys are part of the same `Messages` type. Shared helpers own a small `<helper>.messages.ts` file; do not put family-specific copy in the general `_shared/messages.ts`.

#### Component authoring and catalogue

New components and blocks must use messages from their first implementation. Call `useMessages(componentId, english)` inside the rendering component for a typed translator; `useLocale()` resolves the nearest root. Keep keys stable and purpose-based, not tied to English wording or line numbers, so copy edits do not change keys. Keep all built-in copy in dictionaries, including visible text, accessibility labels, loading and empty states, errors, validation fallbacks, units, and default option labels and descriptions.

Run `node scripts/extract-messages.mjs` to regenerate the type catalogue and metadata. Each component’s Usage & props → Translate and localise table and `meta.json` list its complete keys, English defaults, and interpolation parameters.

#### Message text and interpolation

Messages are plain text, never HTML. Strings can interpolate named values such as `{name}` or `{count}`; number values use the root locale. Missing overrides fall back to English, and missing variables remain visible as `{name}`.

Plural messages are complete sentences or phrases and require an `other` branch. They may also define `zero`, `one`, `two`, `few`, and `many`, selected by `Intl.PluralRules` using `count`. An exact `"=0"` branch takes precedence. Write each branch as a complete phrase so translators can reorder words and choose the appropriate number of forms.

Source: [Customise](https://beamline.io/docs/customize).

### Beamline component labels and application-owned data

#### Instance-level copy

For one component instance, text props and `labels` override root messages. An explicitly empty label remains empty rather than falling back to the dictionary. Changing labels does not change option values, record IDs, or callback payloads.

New `labels` objects can use keys such as `label`, `description`, `empty`, `loading`, `error`, `retry`, `search`, `clear`, `close`, `previous`, `next`, `confirm`, `cancel`, `save`, and `discard,` plus specific action or field names. Existing spellings remain deprecated aliases; if both an alias and its canonical key are passed, the canonical key wins. Keep existing callback-valued labels callable.

A `labels` array used for chart points or record tags is customer data, not a copy dictionary. For image-comparison captions, the tuple form is a deprecated alias for `{ before, after }`; `false` still hides captions.

#### Application-owned content and values

Customer records, children, explicit labels, supplied options and their display text, custom profile labels, backend error messages, identifiers, source code, and user-authored documents remain application-owned content rather than built-in dictionary copy. Localize built-in error and validation fallbacks, but do not replace application-provided backend errors.

Never translate stored option values, filter IDs, or event names; translate their display labels separately. DataGrid’s `valueLabels` maps stored values to labels without changing filtering IDs.

Source: [Customise](https://beamline.io/docs/customize).

### Beamline locale-aware formatting and integrations

All built-in `Intl` number, date, relative-time, and list formatters use the scoped locale. Use `useFormatters()` from `@beamline/runtime` to format application data with the root’s `number`, `date`, `relative`, and `list` helpers. A supplied instance-level formatting callback takes precedence.

Public pure helpers accept locale and messages as arguments; they do not read mutable global locale state. React consumers, including renderer and canvas adapters, pass their scoped locale. Numeric input parsing follows the locale of the displayed number.

Pass translators into pure text helpers. For third-party editor callbacks, pass the translator through a current ref so changing language does not recreate an editor or restart a microphone stream.

Source: [Customise](https://beamline.io/docs/customize).

### Beamline EmptyStates plurals and cropImage translation

For `EmptyStates`, supply both `noun` and `pluralNoun` when adding `s` does not form the correct plural.

`cropImage` accepts an optional `translator` from `useMessages("image-cropper", english)` for its drawing error. The rendering component already uses the nearest root automatically.

Source: [Customise](https://beamline.io/docs/customize).

### Beamline Settings language selector

`Settings` exposes `labels`, `localeOptions`, `timeZones`, `roles`, `notificationOptions`, `profileFields`, and `currency`. The optional `SettingsValues.locale` enables its language selector.

`localeOptions` supplies suggestions and can include custom display labels. A saved valid language tag outside the list is still accepted and displayed: treat the list as suggestions, not an allowlist. Default option lists are overridable props, and validation must not reject a customer’s locale merely because it is absent from shipped options.

Save the chosen locale through `onSave`, then have the application pass it to `UIRoot` to change the interface language; saving the setting alone does not change the root locale.

Source: [Customise](https://beamline.io/docs/customize).

### Beamline pseudo-localization and verification

#### Pseudo-localized layout review

Use `<UIRoot pseudoLocale>` during layout review. Built-in English becomes accented, approximately 35% longer, and enclosed in `⟦…⟧`. Pseudo-localization preserves interpolation names and interpolated customer values; customer text, explicit translations, and explicit customer labels remain unchanged. It is independent of `locale`, so number and date formatting remain realistic. Review the resting page, narrow screens, and open dialogs.

#### Gallery and locale proof

Pseudo-locale gallery checks scan rendered built-in text and accessibility names and check desktop and 390px geometry. Locale proof includes German and Ukrainian messages and formats, nested scopes, open portals, reactive switching, instance precedence, and RTL.

Run the family-wide check with `node tests/e2e/i18n-families.mjs --url <development-gallery> --out <artifact-dir>`. Start the gallery outside the heavy slot. The finder inspects real examples and accessibility attributes. For every customer-data exemption, record a reason, the actual demo source, and the supplied prop. Keep unresolved findings as failures; do not add blanket route or English-word exemptions. Record native `Intl` output separately.

Record the source state, rerun commands, and actual screenshots or results as evidence.

Source: [Customise](https://beamline.io/docs/customize).

### Beamline RTL direction for portal regions

Use `<UIRoot dir="rtl">` when a region includes portals; its direction reaches both the region and its stable portal host. `Direction` from `@beamline/direction` remains available for a local direction provider.

Components use logical properties, and directional icons flip.

Source: [Customise](https://beamline.io/docs/customize).

## Looks and typography

### All nine looks are included in the Complete System

The **$49 launch price Complete System** includes all 9 current looks: Prism, Soft, Blueprint, Glass, Metal, Glow, Pop, Mono and Ink, each in dark and light. Access through a paid registry key does not mean an included look needs a separate purchase. Possible future separately sold add-on packs are different from these included looks. The free sample includes Prism and Metal.

Source: [Pricing](https://beamline.io/pricing) · [Customise](https://beamline.io/docs/customize).

### Beamline Prism visual treatment

#### Surfaces

Prism is Beamline's default look: cut crystal in a dark room, with the same cut-glass treatment available in a light room. Surfaces are nearly black and still, defined by crisp cut edges rather than filled, milky, or glowing faces. Controls catch fine top-left light that fades along their edges, with a faint cut outline and shaded lower face. Fields are channels with a lit lower lip; cards are slabs with a top-left edge highlight and deep shadow.

#### Light and colour

The main action is white light (ink-black in light mode), unless a brand or accent replaces it. Split colour appears only at points of attention: broken bands along the bottom of a pointed-at main action, a field being written in, the chosen tab, or a card edge as the pointer arrives. These bands are not uniform gradients; their direction or shape responds to pointer, press, or selection movement. Otherwise, colour belongs to data and to two distant page corners. `LightBand` supplies the bands of split light.

#### Type, motion, and charts

Prism uses crisp radii, rigid quick motion, 1.5px icons, tight Geist, Geist Mono labels, and an Instrument Serif italic highlighted word. Its chart palette follows the light-band hues: blue, teal, red, amber, deep blue, coral, and sky.

Source: [Design language](https://beamline.io/docs/design-language).

### Beamline Soft visual treatment

Soft is a clean, modern standard with the brand's hue in the page and every surface. The hue contribution follows the brand's own chroma, so a neutral brand remains neutral.

A soft light falls from the top of the page, and cards catch it along their top edges. Surfaces use hairline rims, short layered shadows, and a faint sheen on solids. Typography pairs Geist with Inter.

Source: [Design language](https://beamline.io/docs/design-language).

### Beamline Glass visual treatment

#### Layers and lens behavior

- Glass models Liquid Glass: light and distortion are visible at the edges, while the centre remains true.
- Floating layers—controls, menus, popovers, and dialogs—bend the background at a thick convex rim. Cards and sidebars are content layers that frost and catch light but do not bend.
- Overlays bend and frost more and cast deeper shadows than controls.
- The lens implementation is in `ui/runtime/presets/glass.ts` and works in Chromium; elsewhere, only the frost is used.

#### Frost and readability

A single `--pui-glass-frost` value controls all parts: 0 is clear, 1 is heavy, and the default is 0.5. Frost is mainly a tint—dark in dark mode and white in light mode—with a little blur, keeping glass text readable over varied backgrounds.

Rims have a bright entering crescent at top left and a fainter leaving crescent at bottom right. Pointer position stands in for phone tilt.

#### Controls and page composition

- Controls are capsules. Press light spreads from the press point; buttons swell toward the finger on a snappy, non-overshooting spring; held slider or switch knobs lift into clear lenses.
- The main action is tinted glass.
- Reduced transparency and increased contrast strengthen the frost and remove the lens. Increased contrast also draws plain strong edges.
- The page uses a faint drafting grid beneath two wide, heavily diffused, low-saturation brand-colour pools. Headings catch light.

Source: [Design language](https://beamline.io/docs/design-language).

### Beamline Metal visual treatment

#### Surface and construction

- Treat the page as one machined plate: black anodised with diamond-cut edges in dark mode and anodised aluminium in light mode.
- Show visible bead-blasted grain and one broad reflection.
- Mill components into the plate rather than placing them on top. Cards are pockets with a shadow under the top wall and light on the bottom wall; the plate grain continues through them.
- Fields and wells are shallow pockets. Actuators—buttons, thumbs, and knobs—stand up as keys with polished chamfers.

#### Visual language

Use machined radii rather than soft pills, anodised solids, lathe-turned knobs, segmented meters, wide Archivo headings, engraved monospace labels, and monospaced figures. Avoid chrome bands, brushed stripes, and floating plates.

Source: [Design language](https://beamline.io/docs/design-language).

### Beamline Glow visual treatment

Glow concentrates light where the eye should land. The page is a calm neutral night, or clean paper in light mode, with hairline surfaces like Soft.

A faint spectrum haze appears across the top of the page, and the highlighted word uses that spectrum. Primary and accent buttons stand on a soft spectrum of light that spreads when pointed at. Cards, fields, charts, sliders, and selected chips remain unlit.

Source: [Design language](https://beamline.io/docs/design-language).

### Beamline Mono visual treatment

Mono is a monochrome printed instrument; only data carries colour.

- Solid marks appear as rows of short segments, soft fills as dot grids, and meters as segments, while lines remain solid.
- Regions are cells framed by dashed rules, and every inner divider is dashed.
- The highlighted word is underlined in dots.
- Actions, selection, links, and focus use ink; brand colour remains only in the data series it leads.

Source: [Design language](https://beamline.io/docs/design-language).

### Beamline grain and glass: surface treatments

- `.grain` is a static noise tile using `--pui-grain-tile`: soft-light at 18% on dark surfaces or multiply at 6% on light surfaces. It can cover a page or be clipped to one surface with `.grain--local` inside `.grain-host`. Grain is never animated or a hit target, and it is removed in print.
- `.glass` is for overlays only and uses static blur from `--pui-blur-glass`. Under `prefers-reduced-transparency`, it becomes an opaque `surface-2` equivalent.

Source: [Design language](https://beamline.io/docs/design-language).

### Beamline scrollbars: shared styling and hiding

`ui/styles/base.css` draws one scrollbar style everywhere from `--pui-scrollbar-size`, `--pui-scrollbar-thumb`, `--pui-scrollbar-thumb-hover`, and `--pui-scrollbar-track`: a thin rounded thumb, no arrow buttons, and no track fill.

Components do not set `scrollbar-color` or `scrollbar-width: thin`, which brings back the OS scrollbar in Chromium. To hide a scroller’s bar, use both `scrollbar-width: none` and `::-webkit-scrollbar { display: none }`. `ScrollArea` draws its overlay bar from the same tokens.

Source: [Design language](https://beamline.io/docs/design-language).

### Beamline interface typography: roles, sizes, and measure

Prefer a type role over choosing a size directly. Role classes in `utilities.css` work with or without Tailwind and bundle size, weight, leading, and tracking; ink color remains controlled by ink tokens.

| Role | Typeface and size | Use |
| --- | --- | --- |
| `.text-display` | Geist, 40px | The screen’s single lead headline; scaled only by Hero size. |
| `.text-title` | Geist, 30px | Page titles. |
| `.text-heading` | Geist, 20px | Sections. |
| `.text-subheading` | Inter, 16px | Cards and dialogs. |
| `.text-body` | Inter, 14px | Interface body text. |
| `.text-body-sm`, `.text-label` | Inter, 13px | Labels use weight 500. |
| `.text-caption` | Inter, 12px | Captions. |
| `.text-overline` | Inter, 11px | Uppercase overlines. |
| `.text-numeric` | Geist Mono, 13px | Tabular figures. |
| `.text-code` | Geist Mono, 12.5px | Code. |

Use Inter for interface text, Geist for display numbers and headings, and Geist Mono for code and aligned figures. Product body text is 13–14px; use 15px only on reading surfaces such as answers and documentation.

The size scale is in rem:

| Token | Size |
| --- | --- |
| `2xs` | `0.6875rem` |
| `xs` | `0.75rem` |
| `sm` | `0.8125rem` |
| `base` | `0.875rem` |
| `lg` | `1rem` |
| `xl` | `1.25rem` |
| `2xl` | `1.5rem` |
| `3xl` | `1.875rem` |
| `4xl` | `2.5rem` |
| `5xl` | `3.5rem` |

Tailwind `text-*` utilities resolve to these same tokens. Size utilities do not provide leading; leading comes from the role or container.

Numbers that can change or appear in columns use tabular figures. The base layer applies them to `[data-numeric]`, tables, and monospace text. Headings and display figures use weight 500 and tracking from −0.01em to −0.02em; chrome does not use bold 700. Weight tokens are regular, medium, and semibold at 400, 500, and 600. Uppercase `.eyebrow` labels open up to +0.06em tracking; monospace caps use +0.04em.

Use `balance` for headings and `pretty` for body text. Never balance prose: it does not understand sentence structure and is capped at six lines. Dense table cells are fragments, so truncate them with an ellipsis and provide the full value in a tooltip.

Aim for a reading measure of 45–78 real characters. `ch` measures the width of “0,” about 1.4 times an average letter in Inter and Geist, so `66ch` renders about 95 characters. `--pui-measure` is `48ch`, approximately 68 characters; remeasure if the typeface changes.

Source: [Design language](https://beamline.io/docs/design-language).

## Layout, controls and accessibility

### Beamline Components: Available-Room Sizing

Components respond to the room they are given—a sidebar, split pane, dashboard tile, or phone—not just the window.

Make the root of a block-level component, such as a card, chart, table, list, form, or panel, a size container (`@container`). Style its parts with container variants such as `@sm:` and `@lg:`. The shared container steps are:

| Variant | Width |
| --- | --- |
| `@3xs:` | `16rem` |
| `@2xs:` | `18rem` |
| `@xs:` | `20rem` |
| `@sm:` | `24rem` |
| `@md:` | `28rem` |
| `@lg:` | `32rem` |
| `@xl:` | `36rem` |
| `@2xl:` | `42rem` |
| `@3xl:` | `48rem` |
| `@4xl:` | `56rem` |

Keep window breakpoints such as `sm:` and `md:` for pages and page frames, not inside components. Do not put inline-size containment on content-sized parts such as buttons, badges, chips, keycaps, or avatars; it can make their width zero. Let the containing row or cell adapt instead: rows fold these parts, and cells truncate them. A container that might itself be placed content-sized in a flex row should protect itself with `min-width: min(16rem, 100%)`.

Source: [Design language](https://beamline.io/docs/design-language).

### Beamline Components: Priority, Folding, and Reveals

Assign each part an importance rank. As room shrinks, fold the lowest-ranked parts first, but never fold the part in focus. Preserve item order; reshuffling breaks the reader’s spatial memory. Depending on the component, reduce detail with a tooltip, an ordered `+N`, a shorter format such as `$1.2M` for `$1,204,310`, fewer ticks, or a narrow mode.

For a row of items that can outgrow its room, use `useFit` from `@/hooks/use-fit`. Mark items with `data-fit-item`, mark the `+N` item with `data-fit-more`, preserve order, and pass items that must remain visible as `keep`.

At responsive stops, change structure without a jump: for example, move a label into the name line, detach a column, or move a legend into a header. Moving parts should retain their path through contours or `useFlip`; between stops, the layout must remain legible. Wider space can reveal comparisons and trends or separate columns that were joined in narrow mode.

Put secondary information that waits for attention inside `.reveal-host` using `.reveal`. It appears on hover, focus within, open, or selected; it takes no space when shown and is always visible on touch. Set `data-reveal="always"` on a root or region to keep every reveal there visible.

Source: [Design language](https://beamline.io/docs/design-language).

### Beamline Components: Width Measurement and Client-Side APIs

When JavaScript must decide a width-dependent layout—such as narrow mode, chart scale, or items per row—use `useContentWidth(ref, fallback)`. When only a flag or count depends on width, use `useWidthAs(ref, (w) => w < 640, false)`. These hooks can also decide chart tick count or how many items fit; read width once per change, not on every frame. They measure in a layout effect so the first frame is already correct instead of jumping after a guessed width.

Empty states and alerts that callers usually centre should read their own width with `useWidthAs` rather than becoming containers.

Access client-only APIs—`ResizeObserver`, canvas, WebGL, and audio—inside effects so server rendering does not crash.

Source: [Design language](https://beamline.io/docs/design-language).

### Beamline Charts: Readability at Narrow Widths

As a chart gets narrower, reduce the number of ticks and shorten tick labels first. Then move labels inside the chart or into a tooltip, put the legend on one line or in the header, and thin the axes. Keep marks their share of the room and keep the chart’s message legible at the smallest size.

Source: [Design language](https://beamline.io/docs/design-language).

### Beamline Tables and Popovers: Narrow Screens and RTL

Components should work at `390px`. Data tables scroll horizontally with the first column pinned rather than collapsing into cards; where declared, secondary columns can fold into the name line. Under `640px`, popovers become bottom sheets when their content is a form or a long list.

Support RTL with logical properties such as `ms-`, `pe-`, and `inset-inline-start`, and flip directional icons.

Source: [Design language](https://beamline.io/docs/design-language).

### Beamline Demos and Height-Filling Components

Components that fill a parent’s height—including charts, lists, and terminals—must work at any supplied height and aspect ratio without cutting off their own controls.

In `demo.tsx`, make the root fill the gallery stage up to a sensible maximum, such as `w-full max-w-[440px]`, rather than using a fixed width; this lets the gallery reduce the available room. A filling example uses `layout: "fill"` to occupy the height it receives.

Source: [Design language](https://beamline.io/docs/design-language).

### Beamline Controls: Accessibility and Precise Data

`sr-only` text is absolutely positioned. If a component contains it in long content, such as a table cell or row, give the component root `relative` so the text is positioned against that component, not an ancestor outside its scroll area.

Use tabular figures for numbers. Preserve price precision—for example, `0.00482`—and never round prices.

Keep controls keyboard reachable, show visible focus, and use Radix or native roles. Use `aria-live="polite"` for values changed by user action, such as a total after filtering. Do not use it for high-frequency feeds such as prices ticking twice a second; those should be readable on demand. Give every icon-only control a label.

Source: [Conventions](https://beamline.io/docs/conventions).

### Beamline keyboard-visible focus-ring requirements

Focus is always visible and never animated. Use `--pui-focus-ring` as an outline at least `2px` wide, with a `2px` offset and `transition: none`.

A look can change the ring’s style—Blueprint’s or Mono’s dashed ring, Pop’s `3px` ring, or Ink’s double rule—but cannot remove it or make it thinner. Custom-styled components still show it on `:focus-visible`.

The ring is solid accent on dark and the brand at lightness 50 on light. When an app sets `data-kbd="on"` on the root after Tab, `--pui-focus-ring-kbd` provides a louder amber ring with a halo.

Source: [Design language](https://beamline.io/docs/design-language).

### Beamline controls and data regions: interaction feedback

- **Press:** Use an immediate scale of `0.96–0.985` depending on width; smaller items press deeper. `.ix` defaults to `--pui-press-scale: 0.955` for small controls.
- **Hit targets:** Targets are at least `24px` (`--pui-hit-min`). `.hit` enlarges the actual box; use `.hit-bleed` to extend a control whose drawn size is intentional. A larger painted pseudo-element does not enlarge the pointer target unless it is positioned inside the control.
- **Hover:** Raise the surface one rung without creating a shadow jump on dense rows.
- **Selection:** Use a filled `accent-tint` plus a `2px` accent edge or check; color alone is insufficient. For moving selections such as segmented thumbs, tab indicators, or radio-card rings, use `useIndicator`, `glideProps`, and `.glide`. The two edges travel separately, stretching toward the new choice before gathering there; motion is critically damped, with no overshoot.
- **Loading and errors:** Loading controls retain their size and focus while the label morphs into a spinner. Data regions use skeletons matching their real shape. Anything taking more than about 2 seconds shows elapsed time. Errors appear inline beside their cause and include the fix; reserve toasts for background results.

Source: [Design language](https://beamline.io/docs/design-language).

### Beamline overlays: shared stacking order

Use one stacking ladder for all overlay components:

| Layer | Level |
| --- | --- |
| Raised | 1 |
| Sticky | 10 |
| Overlay/modal scrim | 50 |
| Modal (dialog, sheet, lightbox) | 51 |
| Popover (menus, selects, comboboxes, popovers, hover cards) | 60 |
| Toast | 70 |
| Tooltip | 80 |

A menu or tooltip opened inside a dialog therefore remains above the dialog. Stacking contained within one component, such as a pinned column or chart crosshair, stays local with small numbers.

Source: [Design language](https://beamline.io/docs/design-language).

## Motion and interface sound

### Beamline control timing and input-response rules

Motion defaults are `90ms` to arrive and `180ms` to leave, using `--pui-ease-default`; arrival and departure are distinct events.

Hover arrives immediately and leaves over `--pui-motion-out`. Press is acknowledged immediately, and control value, focus, and ARIA state change at once.

Frequently used elements still move, but briefly (`--pui-duration-fast`) and without delaying the next key. Menu or list highlights glide under arrow keys; typed characters and live market data do not animate. `.ix` in `utilities.css` provides hover and press behavior for clickable items.

Available curves include `default`, `standard`, `enter`, `exit`, `in-out`, `strong-out`, `drawer`, and a critically damped spring. `--pui-ease-exception` is reserved for a specifically named situational reveal. Bounce and overshoot curves are not defaults.

Source: [Design language](https://beamline.io/docs/design-language).

### Beamline managed reduced-motion behavior

Managed `motion="auto"` follows `prefers-reduced-motion`. `motion="reduced"` removes decorative displacement while retaining restrained fades; `motion="none"` removes decorative transitions, including fades.

With reduced motion, every duration becomes `1ms` (effectively instant) while end events still fire, and stagger becomes zero. Decorative loops such as tickers, marquees, and shimmer stop. `--pui-motion-travel` becomes zero, turning shared rise/scale entrances into fades. Fade-in and fade-out durations follow arrival and departure. Dialogs, popovers, menus, scrims, and Skeleton swaps use these fades.

Meaningful loading/status feedback remains. Live data and progress are not removed and continue updating.

Source: [Design language](https://beamline.io/docs/design-language).

### Beamline rendered motion-token resolution

Rendered motion consumers resolve each CSS duration, easing, spring, and stagger separately with `useMotionTokens(hostRef)`. Use the host ref of the actual control; the optional no-ref form resolves the nearest managed root, while static scopes require a real host ref.

The hook’s `reduced` flag follows managed/static policy and the operating-system preference. Before `ready`, it exposes the pure foundation snapshot. CSS transitions use `--pui-motion-in` and `--pui-motion-out`.

`motionTokens` is the pure, server-safe foundation table for non-DOM use. `useResolvedAppearance` also accepts `times` in seconds and `easings` as computed CSS timing functions; it resolves conditional hosts when attached and clears readiness after removal.

Reduced timing declarations take precedence over inline timing overrides. Reading intervals, deadlines, and data pacing remain the caller’s responsibility.

Source: [Conventions](https://beamline.io/docs/conventions).

### Beamline selection morphs and changing-content patterns

#### Selection surfaces

Beamline treats a selection as a surface that travels, rather than switching off at one location and on at another. This pattern is used for segmented thumbs, tab indicators, radio beads, sidebar and pagination plates, selected calendar days, and selected tree rows.

Contours come from `ui/lib/contour.ts`: four corners follow critically damped springs with uneven response; edges bulge only outward, and a stretched contour can narrow slightly at its waist. Elasticity comes from parts arriving at different times, not overshoot. The control commits immediately; only its decorative surface settles. Reflow places the surface without travel, and at rest it is a plain CSS box without a clip or frame.

Each look supplies material-specific motion through `--m-morph-spread`, `--m-morph-front`, `--m-morph-waist`, `--m-morph-weight`, and `--m-morph-rim`: Soft is viscous, Glass heavier, Pop rubber-like, Ink brush-like, and Metal, Blueprint, and Mono rigid.

#### Layer origins

Layers open from their source. A popper begins at its trigger’s box: the near side opens at once and the far side by about `200ms`. A dialog begins at its opener. They close back into their source.

#### Shared changing-element patterns

1. **Gliding plate:** A plate travels to an active row or cell (`_shared/glide.tsx`), as used for menus, listboxes, selections, and similar targets.
2. **Collection changes:** Collections grow, move, and leave a fading ghost (`hooks/use-flip.ts`), as used for chips, filters, comments, attachments, breadcrumbs, and table rows.
3. **Value changes:** Values roll or reshape, including number-field steps, bar widths, sparklines, and SlotText.
4. **Mode swaps:** A mode changes size in place, such as inline editing or password reveal.

Changes preserve size and path where possible. A bar’s lost portion remains as a draining ghost and its gained portion arrives lit (`_shared/delta-ghost`); an action changing several values can trace to each with `ui/lib/trace.ts`.

Content changes use critically damped `spring.snappy`, `spring.smooth`, or `spring.morph` from `ui/lib/motion-tokens.ts`. Nothing that moves on its own overshoots; only a sheet or slider thumb released by a finger may use `spring.held` with bounce `.14`.

Under reduced motion, these patterns remain as still marks. Live market rows never travel.

Source: [Design language](https://beamline.io/docs/design-language).

### Beamline shared animation and scroll-reveal utilities

Shared utilities include the height-animated `.disclosure-panel` and the JavaScript-free `.overlay-anim` for native popovers and dialogs.

Scroll-position `.sr-fade` and `.sr-wipe` reveals are limited to at most one per screenful, with none in the first screenful; they must never use fade-and-rise.

Enter, exit, slide, accordion, shimmer, pulse, and marquee animations are available with character, word, item, and line stagger tokens.

Source: [Design language](https://beamline.io/docs/design-language).

### Beamline hold behavior for independently moving content

Independently moving content must be holdable by anyone under the stated WCAG 2.2.2 rule. Use the shared `hooks/use-hold.ts` mechanism rather than a private hover/focus pair.

The hold activates while a pointer is over the content or keyboard focus is inside it. On touch, a tap holds it until the next tap; a tap that selects something inside keeps it held until touch elsewhere. If moving content has nothing focusable inside it, it becomes a tab stop while moving.

Source: [Design language](https://beamline.io/docs/design-language).

### Beamline Liquid Gooey Item motion and offscreen controls

Plain mirrored `Items` support scope speed, reduced or no motion, and offscreen settling.

The optional upstream `observe`, `shape`, `move`, `bend`, and `melt` modes do not expose managed motion or visibility controls. When those policies are required, use plain `Items` or ordinary controls; an effect’s appearance alone does not guarantee its work is paused.

Source: [Known limitations](https://beamline.io/docs/limitations).

### Beamline Button artifact behavior and Metal preset

The selected Button artifact retains its Motion label, loading, and press behavior. Under reduced motion, Button label/loader changes use restrained fades.

The old `Button metal` effect was removed. Select the accepted Metal look through the preset; there is no replacement `MetalFx` effect.

Source: [Design language](https://beamline.io/docs/design-language).

### Beamline interface sound, media, and voice

[Sound](https://beamline.io/components/sound) is optional feedback for UI actions such as a selection or a completed operation. `SoundProvider` is off by default; opt in with `enabled` and call `useSound().play(name)` for an interaction. Keep visible feedback and a user-controlled mute choice. Audition repeated actions at the volume and on the devices your product uses; the presence of a sound API is not a claim about listening comfort on every device.

This is separate from an [audio player](https://beamline.io/components/audio-player) for media and from the [voice-agent interface](https://beamline.io/guides/react-voice-agent-ui), which shows microphone, transcript and session state supplied by your adapter. Beamline does not bundle a speech or AI backend. Turning interface feedback off should not be used as your media player’s playback control.

Use one shared provider for interaction sounds such as tap, select, toggle on/off, open/close and tick, instead of separate sound systems in each component.

Source: [Customise](https://beamline.io/docs/customize).

### Testing Beamline motion on real screens

Try the [Motion foundation](https://beamline.io/components/foundations/motion) and a live [expandable card](https://beamline.io/components/expandable-card). Motion policy does not substitute for checking your actual screen, especially after custom CSS or nested roots. [Dated checks and limits](https://beamline.io/tested) distinguish recorded browser results from unverified devices.

Source: [Customise](https://beamline.io/docs/customize).

## Upgrades and release migrations

### Beamline 0.2 source and package upgrades

Read the release's migration notes, keep the app and its recorded Beamline files in version control, and exercise actual screens after changing a release. Compiled-package upgrades replace selected release archives; managed source compares local files with the recorded baseline and target, stopping for overlapping edits. [Custom styles](https://beamline.io/docs/customize#custom-styles-by-hand-or-with-an-agent) live outside the managed component tree and stay with the app. A shadcn overwrite replaces files rather than performing a managed merge.

To change a managed source install's pinned release, run the explicit upgrade command:

```sh
node /path/to/release/tools/beamline.mjs upgrade --release /path/to/new-release/release.json --app /path/to/app
```

The destination defaults to `src/ui/beamline`; repeat a custom `--to` if needed. The tool fetches only declared selected files, verifies hashes, and stages the entire selection as a transaction. `--stage-only` lets you inspect a proposal; `--dry-run` previews without changing the app. Adding or removing roots alone does not change the accepted release. Adding entries unions roots at the accepted release; removing an entry recomputes the dependency closure and retains files still needed by another root or local import.

For each managed file, the merge compares **B** (accepted transformed upstream baseline), **L** (current local bytes or deletion), and **U** (target transformed upstream bytes or deletion):

| State | Outcome |
| --- | --- |
| Local bytes equal target | Accept the target baseline without rewriting the file. |
| Upstream is unchanged | Preserve the local edit or deletion without a sidecar. |
| Local bytes equal accepted baseline | Replace or remove using the staged target. |
| Both sides changed and text merges cleanly | Stage merged bytes and retain U as the new baseline. |
| Both sides changed with overlap, binary conflict, or delete/modify conflict | Leave the whole live selection unchanged and save a pending proposal. |
| New path already exists without ownership | Report a collision, even if bytes match. |
| Removed helper is still imported by a surviving local edit | Retain and report the helper as an orphan. |

Git enables clean three-way text merging. Without Git, files changed on both sides remain pending for explicit resolution. The tool never silently recreates a local deletion against changed upstream bytes; a locally modified file removed upstream remains pending removal. `beamline-ui.lock.json` records `acceptedRelease` separately from `pending`; transactions record per-file accepted and target releases and B/L/U/result hashes. `manifestPath` identifies the accepted release's verified descriptor/graph cache. Baseline blobs, import transforms, transaction state, and backups live in `.beamline/`; hashes without retained baseline bytes are not baselines.

Base-file upgrades use the same merge. Customer edits to base files survive non-overlapping changes; overlapping edits await `resolve` and `apply`, and changed binary fonts require an explicit choice. Keep the lock and `.beamline/` so accepted baselines and recovery state remain available. The state retains accepted baselines, a pending or interrupted transaction, the latest accepted transaction with one release copy in `releases/`, and proposals declined since. Older transactions, release copies, and blobs are removed after each accept, discard, or rollback. `UPGRADE.md` covers merges and recovery.

For compiled installs, install the selected packages from the target descriptor with `install <id…> --upgrade --release <target>`, then rebuild the app. Full-source mode must not be mixed with compiled Beamline packages. The separate shared-base arrangement is a local-component/packaged-base mode; any source/package combination must use compatible canonical runtime and foundation packages, and an existing source selection must be upgraded before installing packages from another release.

From rc.4, the first compiled `install`—for any root, a look, or `--upgrade`—rewrites a selection made with rc.3 or earlier: listed internal Beamline units move from `dependencies` to `overrides`, leaving the chosen packages. Registry packages added by an earlier installer, such as Radix, Motion, and recharts, remain because the tool cannot distinguish them from app-added packages; remove ones the code does not import. React is never changed. Replace per-component `import '@beamline/…/styles.css'` lines with the single printed `beamline.css` import. Keeping both works but imports foundation again.

From rc.5, compiled-install records and archives live in app-root `.beamline/`, not `src/ui/beamline/.beamline/`. The first writing command (`install`, `remove`, or `style add`) moves the records. The next `install` or `deps --install` repoints `package.json`, the lockfile, and pnpm overrides to root archives, then removes old archives and empty folders. If editable source is also present, its source and lock stay where they are. Commit app-root `.beamline/`. pnpm archive overrides include the exact Beamline version; installing another release adds matching mappings without redirecting another workspace version and preserves unrelated overrides and workspace settings. After package-manager failure, retry `deps --install` for accepted source or repeat the compiled `install` command.

Source: [Upgrade](https://beamline.io/docs/upgrade).

### Beamline App: Pending Source-Proposal Resolution

A managed-source upgrade compares app files with the recorded baseline and the new release; overlapping edits stop as pending proposals for resolution.

Inspect the pending transaction, explicitly resolve each conflicting path, then apply:

```bash
node /path/to/release/tools/beamline.mjs status --app /path/to/app
node /path/to/release/tools/beamline.mjs resolve components/button/button.tsx \
  --use local --app /path/to/app
node /path/to/release/tools/beamline.mjs apply --app /path/to/app --install
```

For a path, choose one of these resolutions:

- `--use upstream` accepts the proposed upstream bytes or deletion.
- `--use local` explicitly keeps the current live bytes or deletion while accepting `U` as the next upstream baseline.
- `--use file --file /path/to/reviewed-merge.tsx` supplies a reviewed merge.

Resolving an ownership collision explicitly grants ownership of that path. No live source changes occur until all conflicts are resolved and `apply` succeeds.

#### Inspecting and managing the proposal

Status JSON exposes `baseHash`, `localHash`, `upstreamHash`, and `resultHash`; a `null` hash means deletion. To compare exact content, open the matching `.beamline/blobs/<hash>` files.

Repeating the same pending target leaves its transaction ID and proposal bytes unchanged; a different target cannot overwrite it. `discard` abandons an unapplied proposal while retaining evidence, after which you can request a new target.

If live bytes change after staging, apply is blocked. Resolve that path explicitly, or discard and replan. A conflicting caller holds the selected group, including helper changes. Imported orphan helpers remain marked in the lock until the customer removes the dependency or resolves ownership.

Source: [Upgrade](https://beamline.io/docs/upgrade).

### Beamline App: Interrupted Source-Apply Recovery

Before writing live files, a transaction saves the original local bytes, proposed results, and previous accepted lock, and records each completed file write. If apply fails or is interrupted, the accepted release remains unchanged until all files commit.

Rollback is the default recovery choice; resume finishes the already staged transaction:

```bash
node /path/to/release/tools/beamline.mjs recover --rollback --app /path/to/app
node /path/to/release/tools/beamline.mjs recover --resume --app /path/to/app
```

Both choices inspect all live files first. If a customer changed a file after interruption, recovery refuses to overwrite it. Preserve that edit outside the managed folder, restore the path to its saved original or proposed bytes, and retry the chosen recovery.

#### After the source apply

A completed source apply may still need package installation. `deps --install` retries only dependencies from the accepted source descriptor. The installer moves existing direct Beamline packages to matching target artifacts along with source dependencies.

If the target descriptor lacks an installed package, installation stops and names it; use a descriptor containing the existing selection. npm/pnpm lockfiles describe actual package resolution.

Source: [Upgrade](https://beamline.io/docs/upgrade).

### Beamline 0.1-to-0.2 release and legacy artifact compatibility

Read the target release's migration notes before moving to another minor version. The 0.1.x promise is additive-only: props may be added within 0.1. The coordinated 0.2 migration can change prop, root or slot, CSS, and appearance contracts. Release descriptors identify exact, immutable graph, source, and package artifacts.

For a legacy 0.1 package, keep a new tarball at a distinct path and install it with the app package manager; never replace bytes at an existing version or digest.

For a legacy source lock that contains hashes only, keep the original bundle and compare original, local, and target files manually. Hashes alone are not baselines: the newer tool refuses automatic migration without accepted baseline bytes and does not claim a requested target is already accepted. Original 0.1 archives have their own instructions in `docs/INSTALL.md`.

Source: [Upgrade](https://beamline.io/docs/upgrade).

### Beamline 0.2 prop and appearance migration

The 0.2 migration can change prop and appearance contracts. For 0.2.0-rc.34, deprecated prop names still work. In development they warn once per component/prop after mount; production does not warn. When preferred and deprecated setting names are both present on an instance, the preferred name wins. Scoped defaults retain old design keys such as `variant`, `size`, and `tone`; `appearance` is currently an instance prop for the entries below, so saved appearance settings need not be rewritten. Explicit instance props, including an explicitly supplied legacy prop, override scope defaults.

| Component or part | Migration |
| --- | --- |
| BarChart, ConversationBar, MessageContent, TabsList, TabsNav, TableOfContents | `variant` → `appearance` |
| Rating, Reasoning | `variant` → `mode` |
| Credit | `variant` → `presentation` |
| Glow | `variant` → `effect` |
| ImageGeneration | `variant` → `reveal` |
| InlineEdit | `variant` → `textStyle` |
| LightBand, Waveform | `variant` → `shape` |
| ThemeSwitch | `variant` → `animation` |
| ColorPalette, PieChart, RadarChart, RadialChart | Numeric `size` → `diameter` |
| SheetContent | CSS `size` → `width` |
| DialogContent | Width-preset `size` → `width`, retaining the `sm`/`md`/`lg`/`xl` presets |
| WaffleChart | Numeric `size` → `width` |
| AnnouncementBar, Sidebar | `tone` → `appearance` |
| AppShell | `sidebarTone` → `sidebarAppearance` |
| Item | `variant='plain'`/`'card'` → `variant='ghost'`/`'secondary'` |
| ItemIcon | Named-colour `tone` → `palette`; semantic tones also accept success, warning, danger, and info |
| NotificationItem | `tone='mention'` → `tone='accent'`, retaining the mention icon |
| Gauge | `size='fill'` → `fill` |
| AvatarGroup, AttachmentGlyph | `size='xs'` → `compact` |
| ToggleGroup | `variant='default'`, `size='default'` → `variant='ghost'`, `size='md'` |
| PromptBar | `tall`, `onSend` → `size='lg'`, `onSubmit` |
| TreeView | `expandedIds`, `defaultExpandedIds`, `selectedId`, `defaultSelectedId` → `expanded`, `defaultExpanded`, `selected`, `defaultSelected` |
| Calendar, DatePicker, TimePicker, MentionInput, OtpInput | Value callback `onChange` → `onValueChange` |

For visibility props, positive names take precedence over negative aliases; replace `hideLabel={condition}` with `showLabel={!condition}`, and similarly replace `hideTitle` with `showTitle` and `hideAudioTags` with `showAudioTags`. Defaults remain unchanged. `showLabel` is available on ChipGroup, ColorPicker, DatePicker, Input, MentionInput, MicSelector, MultiSelect, OtpInput, PasswordField, PhoneInput, RadioCards, RadioGroup, Rating, SegmentedControl, Select, ShortcutRecorder, Slider, SpeechInput, TagInput, TimePicker, and VoicePicker. `showTitle` is available on ActivityHeatmap, AreaChart, BarChart, ComposedChart, Gauge, LineChart, PieChart, RadarChart, RadialChart, Ridgeline, SankeyChart, SlopeChart, Streamgraph, Treemap, and WaffleChart. TranscriptViewer uses `showAudioTags`.

Calendar, DatePicker, TimePicker, MentionInput, and OtpInput call both `onChange` and `onValueChange` if both are supplied; choose one during migration. Native Input, PasswordField, Textarea, and SearchField keep React `onChange(event)` and also call `onValueChange(text)`. SearchField updates its own value and calls both observers when a native handler is supplied.

Since rc.15, SegmentedControl, ChipGroup, and RadioCards show their field label by default; `hideLabel` remains available for a view switch or quick filter.

Since rc.19, an unnamed root means Prism: `<UIRoot>` or `.pui.dark` without a named look uses Prism. A root retains a look when it names it, such as `preset={soft}` or `data-look='soft'`. If only Soft is installed, an unnamed root is no longer painted; name Soft or install Prism. Since rc.19, UIRoot does not supply a blue accent when none is requested, so Pop uses its tomato accent; pass `accent='blue'` or a brand to retain blue.

Since rc.20, the next compiled `install` adds look typefaces as packages. Prism supplies Geist, Geist Mono, and Instrument Serif and its stylesheet imports them; each face loads only where used. Use `remove --fonts <id>` to keep a font out.

Source: [Upgrade](https://beamline.io/docs/upgrade).

### Beamline component state, defaults, and Alert dismissal

#### Controlled and uncontrolled state

Additional controlled/uncontrolled pairs are available for these component state props:

| Component | State prop(s) |
| --- | --- |
| Accordion | `open` |
| Calendar | `value`, `view` |
| OtpInput | `value` |
| InlineEdit | `value` |
| ColorPicker | `format` |
| Timeline | `expanded` |
| Item | `selected` |
| ThemeSwitch | `theme` |

Explicit `null` clears supported nullable selections. In TreeView, `onSelectedChange(id)` reports selection; `onSelect(node)` continues to report activation. InlineEdit commits and reports its new value only after `onSave` succeeds; if saving rejects, the draft and error remain visible.

#### Initial values

- Pagination accepts `defaultPage`, which defaults to `1` if omitted.
- OrderBook accepts `defaultGrouping` (the first grouping or `tick`).
- Watchlist accepts `defaultStarred`. Omit both `starred` and `defaultStarred` to keep Watchlist stars hidden.

Existing controlled callers retain their behavior.

#### Alert dismissal state

Alert adds `defaultOpen`, `onOpenChange`, and `dismissible`. Existing `onDismiss`-only use remains event-only; pass `dismissible` or `onOpenChange` to opt into local closing. A controlled `open` remains caller-owned.

Source: [Upgrade](https://beamline.io/docs/upgrade).

### Beamline dashboard, trading, settings, and form state APIs

#### Added screen-level state APIs

- **AnalyticsDashboard** adds controlled range, segment, and sidebar props.
- **ProductAnalytics** adds controlled query and sidebar props.
- **CRM**, **SalesDashboard**, and **StatusDashboard** add controlled sidebar props.
- **OrderTicket** adds `defaultPrice` alongside `price`/`onPriceChange`.
- **TradingTerminal** adds controlled `symbol`/`onSymbolChange`.
- **Settings** adds `draft`/`onDraftChange` over saved values and `onPeriodChange` after a successful plan change.
- **SignIn** adds `email`/`onEmailChange`.
- **SignupForm** adds `values`/`defaultValues`/`onValuesChange` for name, email, and password.

Consent and temporary verification codes remain local to their forms.

Source: [Upgrade](https://beamline.io/docs/upgrade).

### Beamline value callbacks and SignaturePad state notifications

#### Native text-field callbacks

Native Input, PasswordField, Textarea, and SearchField retain React's `onChange(event)` and also call `onValueChange(text)`; native `onChange` is not deprecated. When a native `onChange` handler is supplied, SearchField updates its own value and calls both observers.

#### SignaturePad state and notifications

SignaturePad adds these controlled value, default value, and callback sets:

| State | Controlled prop | Default prop | Callback |
| --- | --- | --- | --- |
| Strokes | `value` | `defaultValue` | `onValueChange` |
| Color | `color` | `defaultColor` | `onColorChange` |
| Width | `width` | `defaultWidth` | `onWidthChange` |
| Typed state | `typed` | `defaultTyped` | `onTypedChange` |

Legacy `onChange` still observes initial and current strokes. The new `onValueChange` fires only for drawing, undo, and clear. If migrating an observer that relied on the initial notification, handle the initial strokes yourself.

Source: [Upgrade](https://beamline.io/docs/upgrade).

### Beamline component ref, class, and platform compatibility

- **RadioCards** accepts a ref to its radiogroup while preserving its measurement ref.
- **StepForm** gives `className` precedence over `classNames.root`.
- **ShortcutRecorder** and **ShortcutCheatsheet** honor `platform`, which **Kbd** also accepts.
- **Orb**'s unused `resizeDebounce` is deprecated; omit it.

Source: [Upgrade](https://beamline.io/docs/upgrade).

### Beamline look defaults and field labels in rc.19–rc.20

In rc.19, an app’s explicitly named look remains unchanged: `<UIRoot preset={soft}>` and a static root with `data-look="soft"` continue to use Soft.

An unnamed root now means Prism: `<UIRoot>` without `preset`, or `<html class="pui dark">` without `data-look`. If the app installed only Soft, the former default, an unnamed root is no longer painted; switches, slider tracks, and field frames become invisible. Name the installed look with `preset={soft}` from `@beamline/look-soft` or `data-look="soft"`, or add Prism with `install --preset prism`. The audit flags the unnamed `UIRoot` case.

`UIRoot` no longer applies a blue accent when none is requested, so Pop’s tomato accent appears. Pass `accent="blue"` or a `brand` to keep blue. Since rc.15, segmented control, chip group, and radio cards show their labels like other fields; use `hideLabel` for a view switch or quick filter. Prism parts also changed their finish, but there were no prop or layout changes.

Source: [Upgrade](https://beamline.io/docs/upgrade).

### BillingToggle and Advertisement replacements in Beamline 0.2.0-rc.32

#### BillingToggle → SegmentedControl

Starting with Beamline 0.2.0-rc.32, `BillingToggle` is no longer exported; its billing-choice use case is supported by two option fields on `SegmentedControl`:

- Move the old `badge` value to `note` and `activeBadge` to `activeNote`. The note changes to the saving text when that option is chosen.
- Rename `classNames.badge` to `classNames.note`.
- The old `size="lg"` look corresponds to `SegmentedControl` `size="md"`.

```tsx
import { useState } from "react";
import { SegmentedControl } from "@beamline/segmented-control";

export function BillingPeriod() {
  const [period, setPeriod] = useState("monthly");
  return (
    <SegmentedControl hideLabel label="Billing period" size="sm" value={period} onValueChange={setPeriod} options={[
      { value: "monthly", label: "Monthly" },
      { value: "yearly", label: "Yearly", note: "−20%", activeNote: "Saving 20%" },
    ]} />
  );
}
```

#### Advertisement replacements

`Advertisement` is no longer part of Beamline. Use `AnnouncementBar` for product news (news inside a product) or a `Card` for a promotion.

#### Availability and existing copied source

Existing copied source continues to build because its `SegmentedControl` and `TextMorph` imports remain. The install tool and MCP no longer offer `billing-toggle` or `advertisement`; requests and the old library pages direct users to replacements.

Source: [Upgrade](https://beamline.io/docs/upgrade).

### Portfolio daily P&L dates and calendar in 0.2.0-rc.34

In Beamline 0.2.0-rc.34, `Portfolio.dailyPnl` accepts `PnlDay[]`. Rename each `day` field to `date` and provide a full `YYYY-MM-DD` date. Use the corresponding date from the account history for labels such as `"Oct 11"`; the component cannot infer the year.

```tsx
// Before
<Portfolio holdings={holdings} dailyPnl={[{ day: "Oct 11", pnl: 125 }]} />

// After: use the corresponding full date from the account history
<Portfolio holdings={holdings} dailyPnl={[{ date: "2026-10-11", pnl: 125 }]} />
```

Use `pnl: null` for an unrecorded result and `pnl: 0` only for a measured zero. The P&L calendar provides month navigation, day details, and week and month totals. Each day may also include `trades` and `wins`. The `classNames.daily` slot remains available.

Source: [Upgrade](https://beamline.io/docs/upgrade).

### Required-field test locators in Beamline 0.2.0-rc.21

Required fields remain required, and there is no visual change to the field. Tests that previously located a field by its old accessible name, such as `"Email required"`, should locate it using the words in its label, such as `"Email"`.

Source: [Upgrade](https://beamline.io/docs/upgrade).

## Licence, purchases and account access

### Beamline licence: personal access and End Product use

#### Individual licence and personal access

- Once you purchase Beamline or redeem Beamline credit, the Seller grants the person whose Beamline account receives the purchase or credit redemption a **worldwide, non-exclusive, non-transferable, perpetual licence**. If an organisation pays, the licence still gives access to only that person.
- Account access, credentials, MCP access, downloads, installs, updates, and the full library are personal and may not be shared. Other people must buy their own licence to use Beamline’s MCP server or library.

#### End Product use

- The licensee may use, copy, and modify Beamline to design, build, and maintain any number of their own or clients’ End Products, including commercial ones. An **End Product** is an application, website, internal tool, or other software whose value to its users is mainly something other than Beamline itself.
- Beamline may be included, modified or unmodified, inside those End Products and distributed or hosted as part of them, as long as Beamline is not offered on its own.
- Teammates, contractors, and the client working on an End Product may use, edit, build, and ship the Beamline code the licensee installed in that End Product’s codebase without their own Beamline account. This permission applies only to that End Product; it does not give collaborators access to the Beamline library or MCP server, or allow them to take Beamline parts from the End Product into other apps.
- The licensee may use coding agents and AI tools on projects containing Beamline. The licensee’s agent may use Beamline’s MCP server on their behalf through the licensee’s account. Collaborators may use their tools on code already in the End Product; this does not give them access to Beamline’s MCP server or library.
- The licensee may not use Beamline, or a substantial part of it, to build a dataset for training or fine-tuning a machine-learning model, or provide it to a service that does. This does not affect the permitted agent use above.

Source: [Full licence](https://beamline.io/docs/licence).

### Team licences and published pricing

The published **$49 launch price** is one payment, plus applicable tax, for one person's account, MCP and library access. Account access cannot be shared.

Collaborators may use, edit, build and ship installed code within the same End Product without a Beamline account. Separate access to the library requires their own licence. A volume discount or negotiated team price is not part of the published offer; contact [hello@beamline.io](mailto:hello@beamline.io) about a specific purchase.

Source: [Pricing](https://beamline.io/pricing) · [Full licence](https://beamline.io/docs/licence).

### Paid source redistribution and the MIT sample

The full licence permits building, hosting and distributing real End Products, with collaboration on installed code inside those products. It prohibits offering the paid Product or a substantial part as a component library, UI kit, design system, template, theme, starter or similar product whose main value is Beamline, including modified copies.

Paid source may not be published on its own where it can be obtained apart from an End Product, including public repositories, registries and file shares. Putting it in an app does not remove the licence conditions. For a proposed public repository or an uncertain use, ask [hello@beamline.io](mailto:hello@beamline.io).

Training or fine-tuning datasets made from the Product or a substantial part are prohibited; ordinary coding-agent use in licensed projects is permitted. Keep required third-party notices. The publicly released MIT sample has separate terms; paid parts do not become MIT.

Source: [Full licence](https://beamline.io/docs/licence).

### Beamline free sample and paid parts — licence scope

`LICENSE.md` contains the full Beamline licence; `beamline.io/terms` and `beamline.io/licence` summarize it.

Files published as Beamline’s free sample may be used under either the MIT licence or the Beamline licence. The sample includes the shared runtime (including UIRoot), the foundation (tokens, base CSS and helpers), and the Prism look.

Under the MIT licence, those files may be copied, modified and shipped as application source. Retain the copyright and permission notice; include it with full-source installs as `licenses/free-sample-MIT.txt`.

Paid Beamline parts remain subject to the Beamline licence, including its use and redistribution restrictions, whether installed as packages or copied as source.

Source: [Full licence](https://beamline.io/docs/licence).

### Third-party material — package and End Product obligations

Third-party runtime packages retain their own licences, and third-party material is licensed by its authors under their terms, not by the Beamline licence. For an End Product, required obligations are listed in each package’s `THIRD-PARTY-LICENSES.md` and in `docs/DEPENDENCIES.md`: package files list the licences, while the dependency documentation identifies obligations that reach the product. Retain required notices and visible attribution, including TradingView attribution on the price chart.

Selected font files have their own SIL Open Font License 1.1, included beside the fonts.

Source: [Full licence](https://beamline.io/docs/licence).

### Beamline Updates and Ownership of Modifications and End Products

#### Updates

A purchase includes new versions of the Complete System for as long as the Seller publishes them. The Seller decides what future versions contain. Separate add-on packs may be sold separately and are not updates. No release schedule, new feature, or support response time is promised. Versions received remain licensed if publication ends.

#### Rights and ownership

The Seller retains all rights in Beamline that the licence does not grant. You own your End Products and your changes, but changes to Beamline code remain subject to the Beamline licence wherever they contain Beamline.

Source: [Full licence](https://beamline.io/docs/licence).

### Beamline licence termination after breach or refund

The licence ends if you materially breach it and fail to remedy the breach within 30 days after written notice. Following this breach-based termination, you must stop using Beamline and delete your copies. You do not have to delete copies inside End Products already delivered to users before the breach; those products may continue to run.

A refund also ends the licence.

Source: [Full licence](https://beamline.io/docs/licence).

### Beamline licence warranty and liability limits

Subject to the terms of sale—including terms about the Product working as described, refunds, and updates—and applicable law, Beamline is provided “as is.”

Beamline’s trading components display data and collect order details; they do not execute trades and are not financial advice. Demonstration data in the Product is invented.

For a business purchase, the Seller is not liable for indirect or consequential losses, and the Seller’s total liability under the licence is limited to the amount paid for it. The licence does not exclude liability for fraud, death or personal injury caused by negligence, or liability that cannot lawfully be excluded or limited. It does not limit rights granted by consumer law in the buyer’s country.

Source: [Full licence](https://beamline.io/docs/licence).

### Beamline licence — governing law and consumer court rights

The Beamline licence is governed by the law of Ukraine, and the courts of Kyiv have jurisdiction. This does not remove protections provided by the law of a consumer’s country of residence. Consumers retain any mandatory rights to bring claims in their home courts.

Source: [Full licence](https://beamline.io/docs/licence).

### Beamline refund policy and request channels

Beamline's voluntary refund window is **30 days from purchase, until the first paid install or paid source opening on beamline.io**. Within those conditions, no reason is required. Separately, if a purchased part does not work as described, Beamline promises to fix it or refund you. Statutory remedies and applicable Paddle buyer terms remain.

Where permitted by law, the published withdrawal terms end the 14-day withdrawal right when supply begins after express consent, acknowledgement and required confirmation; rights for faulty or misdescribed content remain.

For card or PayPal orders, reply to the Paddle receipt, find the order at [Paddle](https://paddle.net), or email [hello@beamline.io](mailto:hello@beamline.io). For crypto, contact Beamline with the order link. A refund ends the paid licence and paid access/updates; the free sample remains usable. Read the full refund policy for conditions, timing and rights. The assistant cannot inspect eligibility or issue a refund.

Source: [Refund policy](https://beamline.io/refunds).

### Payments, tax and order invoices

Beamline supports card and PayPal through Paddle, its reseller and merchant of record for those orders, and cryptocurrency through NOWPayments where offered at checkout. Paddle displays applicable VAT or sales tax and the total before payment.

Use the Paddle receipt or [paddle.net](https://paddle.net) for card/PayPal orders and invoices. For an invoice correction or a payment question, contact Paddle or [hello@beamline.io](mailto:hello@beamline.io). The published instructions do not specify a VAT-number entry workflow or bank-transfer option; use the options actually offered at checkout.

Source: [Pricing](https://beamline.io/pricing) · [Terms](https://beamline.io/terms) · [Contact](https://beamline.io/contact).

### The Beamline partner programme

Open [Referrals in your account](https://beamline.io/account/referrals) and press Get my partner link. Each new customer's first purchase through your link earns you 30% of what they paid, excluding tax, in cash. Rekomi runs the programme: it holds each commission for 30 days in case of a refund, then pays by Stripe, bank transfer or PayPal once $25 is due. The earlier referral credit programme has ended; no credit was earned under it.

The published policy does not offer employer gift vouchers, transferable coupon codes or a gift-code entry flow. Contact [hello@beamline.io](mailto:hello@beamline.io) about an arrangement you cannot identify.

Source: [Partner programme](https://beamline.io/referrals) · [Partner terms](https://beamline.io/partner-terms).

### Account sign-in, support and leaked registry keys

Beamline sign-in uses Google, GitHub or a code sent to email through Clerk. Open [your account](https://beamline.io/account) with the identity used for the purchase. If access is missing or that identity cannot be reached, contact [hello@beamline.io](mailto:hello@beamline.io).

For a leaked shadcn registry key, open the account's Connect section, find Keys for shadcn, select Revoke and confirm. Anything still using that key stops working. Create a replacement, copy it when shown once, and update the affected project's local secret configuration. Keep keys out of chat and source control. The support assistant cannot verify purchase records or change account access.

Source: [Contact](https://beamline.io/contact) · [Your account](https://beamline.io/account).

### Connecting Claude Code or Codex to Beamline MCP

The hosted MCP endpoint is `https://mcp.beamline.io/mcp`. For Claude Code:

```sh
claude mcp add --transport http --scope user beamline https://mcp.beamline.io/mcp
```

Then sign in from Claude Code's `/mcp` interface. For Codex:

```sh
codex mcp add beamline --url https://mcp.beamline.io/mcp
codex mcp login beamline
```

Finish sign-in in the opened browser using the Beamline account's Google, GitHub or email-code flow. Account and library access are personal. The agent-specific connect pages also offer installer-guided setup and current client instructions.

Source: [Claude Code](https://beamline.io/connect/claude) · [Codex](https://beamline.io/connect/codex).

## Dated checks and known limits

### Beamline 0.2 package-manager qualification on 11 October 2026

On 11 October 2026, local candidate `0.2.0-compat2.1` passed 32 fresh-app profiles. The matrix used Node 24.21.0, React/ReactDOM 19.3.0, TypeScript 6.0.3, and Tailwind 4.3.3 for source consumers. It covered Bun 1.4.3, Yarn Classic 1.22.22, and Yarn Berry 4.18.1 with both `node-modules` and PnP, across compiled CLI, full source, generated shadcn 4.21.4 registry, and public sample source. Each manager/linker was tested in fresh Next.js 16.4.0 App Router apps using webpack and Vite 8.3.4 apps.

Every profile installed, passed strict types and a production build, rendered three Settings URLs, saved and reloaded, and exercised Button actions and Dialog focus/Escape in Chromium 153.0.8010.12 on Linux at 1512×982. This is named-component coverage, not proof for every component, browser, or framework option. The Bun/Yarn matrix covers fresh standalone apps, not Bun or Yarn workspaces. A pnpm workspace app and separate consumer UI package were exercised separately with one shared runtime. Registry checks served generated content on loopback and do not qualify live paid-key authorization; public-sample checks used an unchanged older snapshot separate from the candidate.

The [matrix and exact commands](https://beamline.io/docs/install#package-managers-checked-11-october-2026) record the profile boundaries.

A separate local exercise used Node 24.19.0, React/ReactDOM 19.3.0, Vite 8.3.2, TypeScript 5.9.3, npm 11.17.0, and pnpm 11.22.0 in Chromium. Production builds covered the reference app and selected compiled/source consumers; the full 332-export declaration surface was checked with `skipLibCheck: false`. Next.js 16.3.8 App Router with TypeScript 6.0.3 was exercised for server-side pure helpers, Button hydration, Dialog portals/focus, and Table context with explicit CSS. This is named-subset evidence, not proof that every component works in every Next configuration. In that exercise, TypeScript 5.9.3 did not provide Next's required `URLPattern` globals.

Dark/light gallery layout checks used 1512×982 desktop and 390px phone width. The contrast run measured selected token pairs in every look and theme. Current browser journeys and accessibility results are collected on [How Beamline is tested](https://beamline.io/tested); entry-specific constraints appear in the catalog.

Source: [Install: dated package-manager checks](https://beamline.io/docs/install) · [Dated testing evidence](https://beamline.io/tested).

### Next.js 16.4 Turbopack checks and older Beamline sample limits

On 11 October 2026, six additional Next.js 16.4.0 Turbopack source/registry profiles passed production builds, strict types, three routes, and computed CSS Module checks. They used Bun 1.4.3, Yarn Classic 1.22.22, and Yarn Berry 4.18.1 with `node-modules`. The candidate hoists module-composition declarations outside cascade layers while keeping visual declarations in their layer; no custom loader or CSS bypass was used. Turbopack with Yarn PnP remains unchecked.

The pinned older public sample snapshot still uses `next build --webpack` with Tailwind PostCSS. Its unchanged source reproduced Next.js 16.4.0's failure on `composes` inside `@layer` in `toast.module.css` (Next.js issue [\#85200](https://github.com/vercel/next.js/issues/85200)). Keep webpack for that older source; the candidate's successful Turbopack checks do not qualify it retroactively.

Source: [Known limitations](https://beamline.io/docs/limitations).

### Beamline gallery route and interaction tests — Linux, 10–11 October 2026

#### Linux gallery run — 10 October 2026

Playwright 1.63.0 on Linux exercised Firefox 155.0 and WebKit 26.6, starting in dark mode. The tested sizes were desktop 1512×982 and touch phone 390×844. All 220 unique gallery routes rendered at both widths in both engines.

Across 162 interaction scenarios per width, results were:

| Viewport | Firefox 155.0 | WebKit 26.6 |
| --- | --- | --- |
| Desktop, 1512×982 | 162/162 | 162/162 |
| Touch phone, 390×844 | 161/162 | 159/162 |

These results combine complete sweeps with focused replays on the `rc.32` base and three browser fixes; they are not a clean sweep of every later release.

#### Focused local production-build checks — 11 October 2026

Focused checks closed the following findings:

- Foundations phone overflow: 34 checks per engine across both widths.
- WebKit dialog-origin offset.
- Reduced-motion caption finding.

The documented short opacity fade is allowed; moving keyframes are rejected. These were focused local release-candidate checks, not a rerun of the published site. See the [dated results](https://beamline.io/tested).

Source: [Known limitations](https://beamline.io/docs/limitations) · [Dated testing evidence](https://beamline.io/tested).

### Free sample CI journeys — desktop and phone, 14 October 2026

The [14 October public run](https://github.com/dprvda/beamline/actions/runs/38498960153) installed the hosted Settings source into fresh Next.js and Vite applications and passed:

- Fresh Next.js App Router and Vite installs.
- Typecheck and build.
- Three Settings routes.
- Save/reload, dialog focus, overflow, and axe checks.

Chromium, Firefox, and WebKit ran these checks at desktop and touch-phone widths. Safari 26.6.2 on a macOS GitHub runner passed those journeys at 1512×982 for both apps. Safari is required for the routine workflow to pass.

All four jobs passed: 14/14 browser/viewport journeys, 168/168 assertions and zero violations in 56 axe scans of the three Settings pages and open dialog. Artifacts include installed-source hashes, commands and screenshots. The [public proof workflow](https://github.com/dprvda/beamline/tree/main/tests/public-proof) can be rerun without a Beamline account or secret.

Source: [Known limitations](https://beamline.io/docs/limitations) · [Dated testing evidence](https://beamline.io/tested).

### Browser and accessibility evidence scope

The public sample is tested in Chromium, Firefox and WebKit at desktop and phone widths, and Safari 26.6.2 on macOS at desktop width. These results cover the named installed Settings journeys. Physical-iPhone and screen-reader speech testing are outside these runs; the separate iOS Simulator workflow remains unqualified. Automated results are not a whole-product WCAG conformance audit. [The testing page](https://beamline.io/tested) holds the current results and their precise scope.

Source: [Known limitations](https://beamline.io/docs/limitations) · [Dated testing evidence](https://beamline.io/tested).

### Beamline browser chart rendering and Sankey layout limits

#### Chart rendering

Area, line, bar, and composed charts draw their plots in the browser once the chart width is known. Before hydration, a server-rendered page shows the chart frame and title; the plot appears after hydration. Charts are plain SVG, and their only chart dependency is d3-shape, used for curve mathematics.

#### Sankey constraints

Sankey flows must not loop. The layout is clearest with up to about 30 nodes.

Source: [Known limitations](https://beamline.io/docs/limitations).

### Phone-number format and length checks — 48-country scope

Format and length checks cover common phone-number shapes in 48 countries, not every numbering plan. Developers must validate phone numbers on their server.

Source: [Known limitations](https://beamline.io/docs/limitations).

### Beamline demonstration content — demo examples

Figures and names in the bundled component examples are demonstration data. They do not represent customer results. The public gallery may replace bundled sample media with photographs or other preview assets; those previews are separate from the code installed in an app.

Source: [Known limitations](https://beamline.io/docs/limitations).

### Beamline host test DOM: capability fallbacks

Browser capability reads are optional in host test DOMs:

- If media queries are absent, keep the server default.
- If resize observation is absent, keep the initial content.
- If a CSS feature is unsupported, use the existing fallback.

Source: [Conventions](https://beamline.io/docs/conventions).

### Translation, Figma and accessibility documentation limits

Beamline documents localization, app-owned dictionaries, pseudo-localization and RTL. These are localization tools; they do not establish an exhaustive set of bundled translations.

Beamline's delivered design assets are editable React/TypeScript/CSS, shared tokens and custom style files. A ready-made Figma library and formal VPAT/ACR are not included. See [published accessibility checks](https://beamline.io/tested), or contact [hello@beamline.io](mailto:hello@beamline.io) for a specific design-file or procurement requirement.

Source: [Known limitations](https://beamline.io/docs/limitations).

Part of [Beamline UI: polished, editable React components and complete screens for custom applications](https://beamline.io/). [Build your own style from any look; tokens, fonts, radii, motion; not only colours](https://beamline.io/docs/customize).
