# Lumen
> Lumen is a React component kit from Columen Labs: an app frame, data-driven pages and views, widgets, charts, drawers and pickers, all on one theme. You hand it data and a view config; it draws the screen.
Lumen is built on shadcn/ui (Radix UI, Base UI, Tailwind CSS). Install: `bun add @columenlabs/lumen-ui @columenlabs/lumen-ui-types` (one exact version for both), then import `@columenlabs/lumen-ui/theme.css` after Tailwind.
- Docs: https://lumen-ui.columenlabs.com/docs/
- Component manifest (JSON, props and snippets): https://lumen-ui.columenlabs.com/docs/manifests/components.json
- Install guide: https://lumen-ui.columenlabs.com/docs/?path=/docs/install--docs
- Prefer LumenSelector for every picker, LumenDrawer for side panels, LumenTabs for tabs and LumenConfirm for destructive confirms over the raw ui/* parts.
## Components
### IconButton
Every icon button: the icon alone, its name in a tooltip and the accessible name. `busy` spins it, `pressed` fills it, `to` and `href` make it a link.
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/frame-iconbutton--docs
```tsx
import { IconButton } from '@columenlabs/lumen-ui';
```
Props:
- `icon: ComponentType<{ className?: string | undefined; }>` — A lucide icon, or any icon component that takes `className` (a view's own icon).
- `label: string` — The action's name: the tooltip and the accessible name.
- `asChild?: boolean`
- `busy?: boolean` — Spins the icon while the action runs.
- `href?: string` — Opens this outside address in a new tab instead of running `onClick`.
- `pressed?: boolean` — A toggle that is on (pinned, followed): its icon fills in the app colour.
- `size?: enum` = icon-sm — `icon-sm` (headers, toolbars) by default; `icon-xs` in table rows and chips, `icon` in forms.
- `to?: string` — Opens this route instead of running `onClick`.
- `variant?: "default" | "secondary" | "destructive" | "outline" | "ghost" | "link" | "destructive-confirm" | null` = ghost
Example:
```tsx
const Plain = () => ;
```
### LumenShell
The app frame: sidebar (icons-only collapse, a drawer on phone), page header, status bar, Settings and the profile menu. An app passes its brand, nav and routed page; its own parts go in the slots `brand`, `headerActions`, `panel`, `wrap`, `shared`, `profile` and `statusEnd`.
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/frame-lumenshell--docs
```tsx
import { LumenLayout, LumenShell, NavGroup, NoteLine, Tile, TileRow } from '@columenlabs/lumen-ui';
```
Props:
- `app: string` — The app's id: `data-app` on , which picks its accent.
- `appLabel: string` — The app's name, heading its own Settings sections.
- `brand: ReactNode` — The sidebar's top: the app's name, or a switcher between a product's apps.
- `children: ReactNode` — The routed page (usually ``).
- `nav: ReactNode` — The app's sidebar content: `NavGroup`s and `SidebarSection`s.
- `finder?: LumenFinderConfig` — How ⌘K Find reads in this app: its chips and how the nav's pages group.
- `headerActions?: ReactNode` = null — The product's buttons on every page header, after Refresh (the product's Tasks and the assistant).
- `health?: ReactNode` — The services the app checks, as one `StatusHealth` chip at the status bar's end.
- `hideAmounts?: boolean` = false — Shows Hide amounts (eye) in the header: masks the fields and values the app marks `amount`.
- `onLogs?: (() => void)` — Opens the app's Logs page; shows Logs in the profile menu.
- `onRefresh?: (() => Promise)` — Reloads the page's data and drops every cached read (TanStack Query `invalidateQueries`); shows Refresh in the header, which then mounts the page again.
- `panel?: ReactNode` — A panel docked right of the page (the product's assistant).
- `profile?: ReactNode` — Settings ▸ General ▸ Profile's content; no Profile section without it.
- `releases?: string` — Turns on What's new: the app's releases API (`GET ?limit=&before=` answering `{releases}`). The status bar shows the newest version, which opens each release's Key takeaways.
- `settings?: SettingsSectionDef[]` — The app's own Settings sections, listed after General and the product's.
- `shared?: SettingsSectionDef[]` — Sections every app of the product shows, listed after General under `sharedLabel`.
- `sharedLabel?: string`
- `signOutUrl?: string` — Where Sign out goes; no Sign out without it.
- `status?: ReactNode` — App-wide status bar items (health, freshness); pages add their own through `Page`.
- `statusEnd?: ReactNode` — Status bar items after `health` and What's new, before the time zone.
- `user?: ShellUser | null` = null — Who is signed in (the profile button); null while it loads.
- `wrap?: ((frame: ReactNode) => ReactNode)` = (frame) => frame — Wraps the frame in the product's providers, inside the shell so they can call `useShell`.
Example:
```tsx
const Shell = () => Sample app}
nav={()}
user={{ email: 'ada@example.com', name: 'Ada Lovelace' }}>(
),
},
]}
/>);
```
### LumenTabs
A page’s tab row: every tab has an icon, a name and an optional count, and actions for the open tab sit at the row’s end.
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/frame-lumentabs--docs
```tsx
import { Badge } from '@columenlabs/lumen-ui/ui/badge';
import { LumenTabs } from '@columenlabs/lumen-ui';
```
Props:
- `onChange: (value: V) => void`
- `tabs: LumenTab[]`
- `value: string | undefined`
- `actions?: ReactNode` — What only the open tab uses, at the row's right end.
Example:
```tsx
const Tabs = () => {
const [, update] = useArgs();
return (
12,
},
{ value: 'reports', label: 'Reports', icon: BarChart3Icon },
{ value: 'setup', label: 'Setup', icon: SettingsIcon },
]}
onChange={(value) => update({ value })} />
);
};
```
### DetailsTable
A small fixed table for a drawer or card: a header, rows of cells and an optional total row. `FactsTable` lists label and value pairs.
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/pages-and-views-detailstable--docs
```tsx
import { DetailsTable, FactsTable } from '@columenlabs/lumen-ui';
```
Props:
- `head: { label: string; right?: boolean | undefined; grow?: boolean | undefined; }[]` — `grow` marks the column that takes the spare width; the last column does by default.
- `rows: { key: string; cells: ReactNode[]; }[]`
- `foot?: ReactNode[]` — An optional total row, one cell per column.
Example:
```tsx
const Details = () => ;
```
### LumenLayout
A page of cards from one saved view: its filter, a grid of widgets and its table. Give it a domain (the fields), a view config and the rows; it filters, groups, totals and lays out on the client. With `onConfigChange` the page offers Edit layout and + Widget.
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/pages-and-views-lumenlayout--docs
```tsx
import { LumenLayout, SampleShell } from '@columenlabs/lumen-ui';
```
Props:
- `config: CustomViewConfig`
- `crumbs: Crumb[]`
- `domain: ViewDomain`
- `load: LumenLoad` — The rows' loading state (`useLumenLoad` over the queries the rows come from): every view and widget shows its ↻, skeleton, error and load time from it, so it is required.
- `rows: T[]`
- `actions?: ReactNode`
- `cards?: LumenLayoutCard[]` — The page's own cards, after the saved view's. One with a `frame` joins the saved layout as a `page` card: moved and sized like the rest, last until it is placed.
- `columnPicker?: boolean` — False leaves Field out of the header's + Add column (a table whose columns are fixed, a Tune's tries); otherwise every editable view offers the Columns picker there.
- `editable?: boolean` — Whether the page's layout can be edited (every page is a saved view, with this flag set in code for now). False locks it: no Edit, no + Widget, no column changes, even with `onConfigChange` given. Left out: editable where `onConfigChange` is.
- `editor?: ViewEditorSection` — An app's own section in the Edit dialog (an explorer view's columns).
- `fit?: boolean` — Cards take the page's width and wide ones scroll inside themselves (coverage pages).
- `groupActions?: ((key: string, rows: T[]) => ReactNode)`
- `hideFilter?: boolean` — No filter box (a short list, e.g. a playbook's variants).
- `intro?: ReactNode` — Above the cards: tabs or a lead the app shows with this page.
- `lead?: LumenLayoutCard[]` — The page's own cards before the saved view's (a chart over the list it draws from).
- `meta?: ReactNode` — A short status line beside the breadcrumb ("Updated 18:22", a warnings badge).
- `onConfigChange?: ((config: CustomViewConfig) => void)` — Saves an edited view; without it the page has no Edit button.
- `onDelete?: (() => void)`
- `onDuplicate?: (() => void)` — Duplicate and Delete, offered in the Edit dialog's More section.
- `onOpen?: ((row: T) => void)` — A row click opens it (a page of its own) instead of the details drawer.
- `onReset?: (() => void)` — A built-in page the person changed (`useAppView`): Edit layout offers Reset to default.
- `onShownRows?: ((rows: T[]) => void)` — Told the rows the view shows after its filters, every page, whenever they change, so an app loads live data for those rows only.
- `onSummaryRows?: ((rows: T[]) => void)` — Told every row the page's Summaries draw, so an app loads live data for those too : a Summary not linked to the table covers rows the table hides.
- `onTokens?: ((tokens: FilterToken[]) => void)` — Told the filter's tokens whenever they change, so an app can load rows for what they pick (the Symbols view's `in` filter).
- `rowPick?: string` — The `rowLink` value tapped when the page opens, so linked cards start narrowed to one row (a playbook's Results open on its Default variant).
- `rowTitle?: ((row: T) => string)` — The details drawer title for a row.
- `save?: enum` — How the person's chips, sort and grouping save (rule C, by surface): `document` (a saved view, the default) holds them for the visit and shows Changed with Save view and Revert; `sheet` (a workspace tab) saves every change at once through `onConfigChange`, like a cell in a sheet.
- `selection?: { keys: Set; onChange: (keys: Set) => void; }`
- `state?: ReactNode` — Shown in place of the table instead of the rows, e.g. an empty state ("No accounts yet").
- `status?: ReactNode`
- `tile?: ((key: string, rows: T[]) => ReactNode)` — Draws one of the domain's tiles over the view's rows (not the table's filter); the view's `tiles` pick which, in order.
- `viewId?: string` — The saved view's id, shown in the Edit dialog to copy for an MCP call.
- `viewTools?: ReactNode` — Buttons in the view card's header ("+ Add time").
Example:
```tsx
const TableOnly = () => ;
```
### LumenPager
The compact pager every table uses: the count opens the page sizes, the chevrons turn the page.
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/pages-and-views-lumenpager--docs
```tsx
import { LumenPager } from '@columenlabs/lumen-ui';
```
Props:
- `onPage: (page: number) => void`
- `onSize: (size: number) => void`
- `page: number`
- `size: number`
- `total: number`
- `noun?: string` = rows
- `sizes?: readonly number[]` = [25, 50, 100, 250] as const — Page sizes offered; a server-paged source takes its own.
Example:
```tsx
const Pager = () => {
const [, update] = useArgs();
return (
update({ page })}
onSize={(size) => update({ size, page: 0 })} />
);
};
```
### LumenTokenizedFilter
The filter box: type to search, or pick `field:value` chips grouped by field with counts. Number fields take one condition (`> 100`, `a..b`). `useLumenFilter` holds the state and returns the matching rows.
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/pages-and-views-lumentokenizedfilter--docs
```tsx
import { LumenTokenizedFilter } from '@columenlabs/lumen-ui';
```
Props:
- `filter: { fields: FilterField[]; rows: T[]; indexed: { row: T; values: Record; nums: Record; }[]; tokens: FilterToken[]; setTokens: (next: FilterToken[]) => void; text: string; setText: Dispatch>; results: T[]; hasText: boolean; }`
- `changed?: boolean` — Other unsaved changes the save icon also saves (the page's link colours).
- `className?: string`
- `label?: string` = Filter — The input's accessible name.
- `link?: FilterLink | null` — Its card's link colour; a coloured card shares every chip. `null`: it never shares (a widget's own fixed filter).
- `onSave?: ((tokens: FilterToken[]) => void)` — Saves the box's filters as the view's own.
- `placeholder?: string` = Filter…
- `saved?: FilterToken[]` — The view's saved filters; the save button shows when the box differs from them.
Example:
```tsx
const Filter = () => ;
```
### LumenView
One view as a card: a table of the rows a page hands it, with sort, totals, grouping and a pivot. LumenLayout puts it on a page with its filter; use it alone for a second table.
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/pages-and-views-lumenview--docs
```tsx
import { LumenView } from '@columenlabs/lumen-ui';
```
Props:
- `config: CustomViewConfig`
- `domain: ViewDomain` — The view's domain, its formula columns included (`withViewFields`).
- `rows: T[]` — The rows the view shows: its page's filter already applied.
- `cardId?: string` = view — The card's id on its page (its fold is kept per id); a page's second table passes its own.
- `controls?: CardControls` — Its place in a LumenLayout: the layout's edit state and the card's ⋯ menu.
- `filter?: ReactNode` — The view's own filter and Group in its header (the filter is the table's, not the page's), and how many tokens it holds.
- `filterCount?: number`
- `groupActions?: ((key: string, rows: T[]) => ReactNode)` — Buttons at the end of a group row's label (pin a group), given its key and rows.
- `groupBy?: ViewField | null` = null — The grouping the page picked (its Group by switch); null: flat rows.
- `inLayout?: boolean` = false — In a LumenLayout's scroller: the table scrolls with the page, its header pinned.
- `link?: CardLink` — Its link colour on the layout.
- `load?: LumenLoad` — The rows' loading state (`useLumenLoad`): the view shows its skeleton, progress bar, ↻, a failure with Retry and, with Observability details on, its load time.
- `note?: ReactNode` — A short subtitle after the title (a repo path).
- `onAddColumn?: AddColumn` — The + at the end of the header (Edits in place): Field (the Columns picker, at the +), Formula, Your data and Ask the assistant; each left out is not offered.
- `onColumns?: ((keys: string[]) => void)` — The Columns picker in the header: turns any of the domain's fields on or off and hands back the view's new columns. Left out: no picker.
- `onDetails?: ((row: T) => void)` — A row click: opens the details drawer, or the row's own page.
- `onSort?: ((sort: { key: string; desc: boolean; } | null) => void)` — Told when a header click changes the sort.
- `pivot?: boolean` = false — Shows the view's pivot instead of its rows.
- `selection?: { keys: Set; onChange: (keys: Set) => void; }` — Row checkboxes (strategy lab): the checked row keys and their setter; the header box checks every shown row.
- `shown?: Partial>` — Its controls from its card (Edit widget ▸ Controls); missing keys take the Table defaults.
- `sort?: { key: string; desc: boolean; } | null` = z
.object({ key: fieldKey, desc: z.boolean().default(false) })
.nullable()
.default(null) — The sort shown, when the page holds it (rule C); left out: the view's saved sort.
- `state?: ReactNode` — Shown in place of the table instead of the rows, e.g. an empty state ("No accounts yet").
- `strip?: ReactNode` — A line under the filter: the card's Link offer.
- `subgroup?: string | null` — What each row opens, by the domain's sub-group key (the page's Group button); null: none, left out: the view's saved pick.
- `tapped?: ((row: T) => boolean)` — Rows picked by a tap (a linked table's row filter): a muted fill, no border.
- `title?: string` = config.name — The card's title; left out: the view's name.
- `tools?: ReactNode` — Buttons in the card's header before the pager ("+ Add time").
- `total?: number` = rows.length — Every row before the filter, for "12 of 40 rows"; left out: `rows`.
Example:
```tsx
const Table = () => ;
```
### Tile
One headline number with its label and up to a few lines under it. `TileRow` lays tiles in a row that wraps on phones.
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/pages-and-views-tile--docs
```tsx
import { Tile, TileRow } from '@columenlabs/lumen-ui';
```
Props:
- `label: ReactNode`
- `value: ReactNode`
- `caption?: ReactNode`
- `className?: string`
- `lines?: ReactNode[]` — Secondary lines under the value, one each (a Summary card's).
- `valueClassName?: string`
Example:
```tsx
const One = () => ;
```
### LumenConfirm
Asks before an action that cannot be undone. Wrap the button that opens it, or control `open` yourself. Destructive by default (a red action); `destructive={false}` for a plain confirm.
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/pickers-and-panels-lumenconfirm--docs
```tsx
import { Button } from '@columenlabs/lumen-ui/ui/button';
import { LumenConfirm } from '@columenlabs/lumen-ui';
```
Props:
- `onConfirm: () => void`
- `title: ReactNode` — The question, e.g. `Delete view Options?`.
- `action?: ReactNode` = Delete — The confirm button's label.
- `cancel?: string` = Cancel — The button that backs out (Keep, Keep running).
- `children?: ReactNode` — The button that opens the dialog; leave it out and control `open` instead.
- `description?: ReactNode` = This cannot be undone.
- `destructive?: boolean` = true — False for a confirm that destroys nothing (a new version): its action is not red.
- `onOpenChange?: ((open: boolean) => void)`
- `open?: boolean`
- `typeToConfirm?: string` — A word the person must type before the action unlocks, for a step that loses a lot (`DELETE`).
Example:
```tsx
const Destructive = () => ();
```
### LumenDrawer
The one pullout drawer: a right-side panel on desktop whose width drags (320px to 60%), a bottom sheet on phone. Its body holds `DrawerSection`s; its footer always shows (Close when you pass no actions).
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/pickers-and-panels-lumendrawer--docs
```tsx
import { Button } from '@columenlabs/lumen-ui/ui/button';
import { DrawerSection, LumenDrawer } from '@columenlabs/lumen-ui';
```
Props:
- `children: ReactNode` — Stacked DrawerSections; the drawer pads and scrolls them.
- `onClose: () => void`
- `open: boolean`
- `title: ReactNode`
- `defaultSize?: Size` = 32% — Width on desktop: starts at `defaultSize`, drags between `minSize` and `maxSize`.
- `description?: ReactNode` — A muted line under the title ("Active · invited Oct 16"); read aloud only when left out.
- `footer?: ReactNode` — The footer's actions, right-aligned (a destructive action, Cancel and Save); Close when left out.
- `footerStart?: ReactNode` — Left side of the footer: a count or status line ("12 releases").
- `maxSize?: Size` = 60%
- `minSize?: Size` = 320px
Example:
```tsx
const Drawer = () => {
const [, update] = useArgs();
return (
<>
update({ open: false })}
footer={}>
The first store, on the river front.
1,701 orders this month.
>
);
};
```
### LumenForm
A form from a list of fields: text, secret, number, date, textarea, select and multi. Each field checks itself; Save lists what needs fixing and takes you to it instead of saving.
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/pickers-and-panels-lumenform--docs
```tsx
import { LumenForm } from '@columenlabs/lumen-ui';
```
Props:
- `fields: readonly LumenFormField[]`
- `onSave: (values: LumenFormValues) => void` — Runs only when every check passes. A secret left empty is left out.
- `busy?: boolean` = false
- `footer?: ReactNode` — Beside Save, on the left (a Test button, a note).
- `initial?: LumenFormValues`
- `onCancel?: (() => void)`
- `saveLabel?: string` = Save
Example:
```tsx
const Form = () => String(v).startsWith('https://') || 'Must start with https://',
},
{ id: 'key', label: 'API key', kind: 'secret', saved: '••••3f9a' },
{
id: 'region',
label: 'Region',
kind: 'select',
options: [
{ value: 'north', label: 'North' },
{ value: 'centre', label: 'Centre' },
{ value: 'south', label: 'South' },
],
},
{ id: 'notes', label: 'Notes', kind: 'textarea' },
]} />;
```
### LumenSelector
Every picker in one part: one value or many, groups, search past 8 options, and an Add row that turns typed text into a new value. On a phone the list opens as a bottom sheet.
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/pickers-and-panels-lumenselector--docs
```tsx
import { LumenSelector } from '@columenlabs/lumen-ui';
```
Props:
- `onChange: ((value: V) => void) | ((value: V[]) => void)`
- `value: string | readonly V[] | null | undefined`
- `align?: enum`
- `className?: string`
- `create?: ((query: string) => V | null)` — Turns typed text into a new value (a ticker, "ema 13", a new sector); null refuses it.
- `createLabel?: ((query: string) => string)` — The create row's text; `Add "…"` by default.
- `disabled?: boolean`
- `empty?: string` — Shown when nothing matches.
- `groups?: readonly LumenOptionGroup[]`
- `header?: ReactNode` — Above the list (a scope switch such as "for SPY / All").
- `icon?: Icon` — The icon trigger's icon, or a leading icon in a field or chip.
- `id?: string`
- `label?: string` — Accessible name; also the phone sheet's title.
- `max?: number` — At most this many picked; the rest of the list greys out.
- `multiple?: boolean`
- `onHighlight?: ((value: V) => void)` — A row was highlighted (hover or arrow keys): a caller reads it ahead (the assistant's Chats).
- `onOpenChange?: ((open: boolean) => void)`
- `open?: boolean`
- `options?: readonly LumenOption[]` — The options; or `groups` to fix the group order.
- `placeholder?: string`
- `search?: boolean | "auto"` — `auto`: a search box past 8 options.
- `size?: enum`
- `trigger?: enum` — `field` by default.
Example:
```tsx
const Single = () => {
const [, update] = useArgs();
return (
update({ value })} />
);
};
```
### RenameField
A name renamed in place: Enter or leaving the field saves, Escape undoes, and empty or ↺ goes back to the source’s own name.
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/pickers-and-panels-renamefield--docs
```tsx
import { RenameField } from '@columenlabs/lumen-ui';
```
Props:
- `onSave: (name: string | null) => void` — null resets to `original`.
- `original: string` — The source's own name (the broker's, the bank's), what Reset goes back to.
- `originLabel: string` — Where `original` comes from, for the tooltip: "At the broker", "At the bank".
- `value: string` — The name shown now: the person's own, or `original`.
Example:
```tsx
const Rename = args => {
const [, update] = useArgs();
return (
update({ value: name ?? args.original })} />
);
};
```
### ResetButton
Puts a setting back to its default: a ↺ that shows only while something differs, its tooltip naming what it goes back to.
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/pickers-and-panels-resetbutton--docs
```tsx
import { ResetButton } from '@columenlabs/lumen-ui';
```
Props:
- `changed: boolean` — Whether anything differs from the default; nothing shows when it doesn't.
- `label: string` — The tooltip and accessible name: "Reset to 15 – 45", "Reset all".
- `onReset: () => void`
- `size?: enum` = icon-xs
Example:
```tsx
const Reset = () => ;
```
### SaveButton
Save that says why it cannot save: with issues, a chip before it counts them and lists each; tapping Save takes you to the first one. Never a bare disabled button.
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/pickers-and-panels-savebutton--docs
```tsx
import { SaveButton } from '@columenlabs/lumen-ui';
```
Props:
- `issues: FormIssue[]`
- `busy?: boolean` = false — Saving now: Save waits.
- `className?: string`
- `disabled?: boolean` = false — Nothing to save (no change): Save is greyed and needs no reason.
- `icon?: ComponentType<{ className?: string; }> | null`
- `label?: string` = Save
- `onSave?: (() => void)`
- `type?: enum` = button — `submit` inside a form: the form's own submit runs only when there are no issues.
Example:
```tsx
const Ready = () => ;
```
### GrowthChart
Growth of $1 for a set of lines on a log, linear or fit scale, with optional bands, markers and this year’s start. `format` changes the axis from `1.23×` to any unit.
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/charts-growthchart--docs
```tsx
import { GrowthChart, paletteAt } from '@columenlabs/lumen-ui';
```
Props:
- `className: string`
- `lines: Series[]`
- `scale: enum`
- `bands?: ChartBand[]` = []
- `format?: ((v: number) => string)` = (v: number) =>
`${v < 0 ? MINUS : ''}${Math.abs(v).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}×`
- `markers?: ChartMarker[]` = []
- `sinceYears?: number`
- `ytd?: boolean` = true
Example:
```tsx
const Growth = () => ;
```
### LumenEChart
Any Apache ECharts option, themed: `useChartTokens` gives the theme’s colours and fonts and redraws on a theme change; `axisStyle` and `baseOption` style axes the kit’s way.
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/charts-lumenechart--docs
```tsx
import { LumenEChart } from '@columenlabs/lumen-ui';
```
Props:
- `label: string` — What the chart shows, for screen readers.
- `option: ECBasicOption`
- `className?: string`
- `height?: number` = 260
- `on?: EChartHandlers` — Event handlers by ECharts event name ('click', 'axisareaselected').
Example:
```tsx
const Bars = () => ;
```
### LumenGeoMap
A world map shaded by one number per country (ISO alpha-2 codes); countries left out draw empty.
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/charts-lumengeomap--docs
```tsx
import { LumenGeoMap } from '@columenlabs/lumen-ui';
```
Props:
- `label: string` — What the map shows, for screen readers.
- `values: Record` — The number for each country, by ISO alpha-2 code; countries left out draw empty.
- `height?: number` = 260
- `unit?: string` — What the number counts ("visits"), shown in the tooltip after it.
Example:
```tsx
const World = () => ;
```
### PriceChart
A price chart on TradingView Lightweight Charts™: candles, OHLC bars, line, area or Heikin-Ashi, with volume, overlays on the price and a pane per oscillator. Indicators are worked out in the browser from the bars (`computeIndicators`). Made-up bars here.
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/charts-pricechart--docs
```tsx
import { PriceChart, resolveIndicator, computeIndicators } from '@columenlabs/lumen-ui';
```
Props:
- `bars: ChartBar[]`
- `chosen: ChartIndicator[]` — Overlays drawn on the price and oscillators in their own panes.
- `type: enum`
- `values: IndicatorValues`
- `baseline?: number | null` = null — Draws this level as a dashed line (the prior close).
- `className?: string`
- `navigator?: boolean` = false — The timeline under the chart: drag its window to move, its edges to zoom.
- `onOutside?: ((span: TimeSpan) => void)` — The timeline asked for a span the chart's bars do not cover (intraday bars, older days).
- `onRange?: ((span: TimeSpan, byPerson: boolean) => void)` — The shown span (first and last bar), and whether the person moved it (drag, wheel, timeline).
- `range?: TimeSpan` — The span to show; a new object moves the chart there, panning and zooming move it after.
- `timeline?: ChartBar[]` — The navigator's bars when they differ from the chart's: the symbol's whole history, coarser, with the shown span cut out of it. Left out: the chart's own bars.
- `timeZone?: string` = America/Los_Angeles — The person's time zone, for intraday bars (Unix seconds).
- `visible?: number` — Bars shown at first, counted from the latest; left out: all of them.
- `zoom?: boolean` = false — Wheel and pinch zoom (off where the page itself scrolls under the chart).
Example:
```tsx
const Candles = () => resolveIndicator(id) ?? [])}
values={computeIndicators(bars, chosen)}
className="h-96 w-full"
navigator />;
```
### LumenCodeEditor
An editable code box (CodeMirror) for SQL, JavaScript or JSON, loaded only when it first shows.
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/code-lumencodeeditor--docs
```tsx
import { LumenCodeEditor } from '@columenlabs/lumen-ui';
```
Props:
- `onChange: (value: string) => void`
- `value: string`
- `className?: string`
- `lang?: enum`
- `readOnly?: boolean`
- `wrap?: boolean` — Wraps long lines instead of scrolling sideways (off by default).
Example:
```tsx
const Editor = () => {
const [, update] = useArgs();
return (
update({ value })} />
);
};
```
### LumenCode
Read-only code, highlighted by Shiki in the theme’s light and dark colours, with a copy button in its header.
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/code-lumencode--docs
```tsx
import { LumenCode } from '@columenlabs/lumen-ui';
```
Props:
- `code: string`
- `className?: string`
- `header?: boolean` = true — The label and copy icon over the code (chat); off where the page has its own (a file view).
- `lang?: string` — The fence tag or file extension; none draws plain text.
- `lineNumbers?: boolean` = false — Numbers each line (files); off for chat snippets.
- `wrap?: boolean` = false — Wraps long lines instead of scrolling sideways.
Example:
```tsx
const Code = () => ;
}`} />;
```
### ColorDot
A small dot in any CSS colour (a theme token reads in both themes); `ColorChip` puts one before a label.
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/feedback-colordot--docs
```tsx
import { ColorChip, ColorDot } from '@columenlabs/lumen-ui';
```
Props:
- `color: string`
- `className?: string`
Example:
```tsx
const Dot = () => ;
```
### LumenBoundary
Keeps a crash inside its card: the card says it hit an error, with Try again and Reload, and the page around it keeps working. Every grid card and widget body sits in one.
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/feedback-lumenboundary--docs
```tsx
import { LumenBoundary } from '@columenlabs/lumen-ui';
```
Props:
- `where: string` — Logged with the error: the card's id, or the panel's name ("assistant").
- `what?: enum` = card
Example:
```tsx
const CaughtError = () => ;
```
### NoteLine
One muted line over a card or page: an icon, what it says, and an optional button.
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/feedback-noteline--docs
```tsx
import { Button } from '@columenlabs/lumen-ui/ui/button';
import { NoteLine } from '@columenlabs/lumen-ui';
```
Props:
- `icon: LucideIcon`
- `title: ReactNode` — The point, in a few words.
- `action?: ReactNode` — A button the note carries.
- `note?: ReactNode` — The detail, one line.
Example:
```tsx
const Note = () => Connect
)} />;
```
### Badge
shadcn/ui's Badge with Lumen's `success`, `warning` and `info` tones added.
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/base-parts-badge--docs
```tsx
import { Badge } from '@columenlabs/lumen-ui/ui/badge';
```
Props:
- `asChild?: boolean` = false
- `variant?: "default" | "secondary" | "destructive" | "outline" | "ghost" | "link" | "success" | "warning" | "info" | null` = default
Example:
```tsx
const Default = () => Live;
```
### Button
shadcn/ui's Button in Lumen's theme. `destructive` stays muted until hovered and always opens a confirm; its final button is `destructive-confirm`.
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/base-parts-button--docs
```tsx
import { Button } from '@columenlabs/lumen-ui/ui/button';
```
Props:
- `asChild?: boolean` = false
- `size?: "default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null` = default
- `variant?: "default" | "secondary" | "destructive" | "outline" | "ghost" | "link" | "destructive-confirm" | null` = default
Example:
```tsx
const Default = () => ;
```
### Gallery
The common base parts in one place, in the light or dark theme. Each is the shadcn/ui component of the same name; its own docs list every prop.
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/base-parts-gallery--docs
```tsx
import { Alert, AlertDescription, AlertTitle } from '@columenlabs/lumen-ui/ui/alert';
import { Badge } from '@columenlabs/lumen-ui/ui/badge';
import { Button } from '@columenlabs/lumen-ui/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@columenlabs/lumen-ui/ui/card';
import { Checkbox } from '@columenlabs/lumen-ui/ui/checkbox';
import { Input } from '@columenlabs/lumen-ui/ui/input';
import { Kbd, KbdGroup } from '@columenlabs/lumen-ui/ui/kbd';
import { Label } from '@columenlabs/lumen-ui/ui/label';
import { Progress } from '@columenlabs/lumen-ui/ui/progress';
import { Separator } from '@columenlabs/lumen-ui/ui/separator';
import { Skeleton } from '@columenlabs/lumen-ui/ui/skeleton';
import { Slider } from '@columenlabs/lumen-ui/ui/slider';
import { Spinner } from '@columenlabs/lumen-ui/ui/spinner';
import { Switch } from '@columenlabs/lumen-ui/ui/switch';
import { Textarea } from '@columenlabs/lumen-ui/ui/textarea';
```
Example:
```tsx
const Gallery = () => (
Buttons and badgesVariants share one height per size.LiveLateNewDraftFieldsInputs, switches and sliders.
Progress and loading
⌘KBuilt on shadcn/ui
These parts come from shadcn/ui, on Radix and Base UI, styled with Tailwind.
);
```
## Reference
Parts that need an app behind them; their pages list each prop.
### Assistant and workspaces
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/reference-assistant-and-workspaces--docs
- LumenAssistantPanel: The docked chat panel: messages, tool steps, cards and the composer.
- LumenAssistantButton: The header button that opens the panel.
- LumenAssistantChats: The list of past chats, grouped by day.
- WorkspacesSection: The sidebar list of the app's workspaces.
- WorkspacePage: A workspace: tabs of cards over frames or domains.
### Frame parts
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/reference-frame-parts--docs
- Page: A page's frame: header and body, with the page's padding.
- PageHeader: The page header: breadcrumbs, title and actions.
- SidebarSection: A labelled group of sidebar items.
- LumenAppSwitcher: Switches between a suite's apps, each with its access level.
- TasksButton: The header's background tasks button with its popover (a sheet on phone).
- WhatsNew: The status bar's latest release and its drawer of key takeaways.
- StatusHealth: Status bar health of the app's sources.
- LumenFinderButton: Opens Find (⌘K) over the app's pages and records.
### Market
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/reference-market--docs
- LumenChart: The chart dialog: up to 8 symbols, % or price, indicators.
- MarketWidget: Live market widget: index lines, tiles and today's movers.
- IndicatorPicker: Pick and set up chart indicators, or type a formula.
- OptionChainTable: An option chain by expiration: calls and puts around the strike.
- SymbolSheetProvider: Opens the symbol sheet from any symbol cell.
### Settings
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/reference-settings--docs
- LumenSettings: The Settings page: a list of sections, each a row with its title, one-line description and body.
- SchedulesSection: Schedules: each job with its switch, frequency, window and time zone, as the API describes them.
- NotificationsSettings: Notification targets (email, push, webhooks) and the grid of which events go to each.
- McpSettings: The app's MCP server addresses and the steps to connect each client.
- IntegrationRow: One integration row: its key fields, Test and the last result.
- TimeZoneSelect: A time zone picker.
### Widgets and pages
Docs: https://lumen-ui.columenlabs.com/docs/?path=/docs/reference-widgets-and-pages--docs
- LumenWidget: The frame every card sits in: header, link square, size and collapse.
- LumenViewCard: A second table on a page, with its own filter.
- LumenCard: One number and its lines.
- LumenPageState: Loading, empty and error states for a page.
- LumenTabDrawer: Manage tabs: rename, reorder and remove.
- IconPicker: Pick an icon from the kit's set.
- OverviewPage: A home page of stages, each a list of rows.
- LumenCoverageMap: A calendar heatmap of what data exists by day.