@columenlabs/lumen-ui

中文文档
0.1.219发布于 2026/11/09MITTypeScriptES Module近 30 天下载 5.1万

Lumen: shared React components (frame, views, widgets, charts, drawers).

原始使用文档
文档目录6 节
  1. Install
  2. Set up
  3. What's inside
  4. Rules the kit follows, and apps should too
  5. Notices
  6. Versions

@columenlabs/lumen-ui

Lumen's React components for data apps: the app frame, pages and views over your rows, widgets, charts, drawers and pickers, all on one theme. You give it data and a view config; it draws the screen. Built on shadcn/ui (Radix UI, Base UI, Tailwind CSS 4). Its view model is @columenlabs/lumen-ui-types.

Install

Pin both packages to the same exact version.

bun add @columenlabs/lumen-ui @columenlabs/lumen-ui-types
bun add react react-dom @tanstack/react-query @tanstack/react-router   # peers
bun add -d tailwindcss @tailwindcss/vite tw-animate-css shadcn

Set up

src/index.css:

@import "tailwindcss";
@import "tw-animate-css";
@import "shadcn/tailwind.css";
@import "@columenlabs/lumen-ui/theme.css";
@source "../node_modules/@columenlabs/lumen-ui/dist";

/* your accent, per app (LumenShell sets data-app on <html>) */
:root[data-app="myapp"] { --primary: oklch(0.55 0.15 160); }
/* optional: numbers in the text font instead of mono (code stays mono via `font-code`) */
:root[data-app="myapp"] { --num-font: var(--app-font); }

At start, before anything renders, tell the kit about your app. The kit names no product: your names and data come in here.

import { configureLumen } from '@columenlabs/lumen-ui';
import { registerDomains } from '@columenlabs/lumen-ui-types/registry';

configureLumen({ prefsCookie: 'myapp_prefs' });   // where display preferences live
registerDomains({ /* your joins, sources and domain keys */ });

Wrap your routes in the shell:

import { LumenShell } from '@columenlabs/lumen-ui';

<LumenShell app="myapp" appLabel="My app" brand={<Logo />} nav={nav} user={{ email, name }}>
  <Outlet />
</LumenShell>

Your own parts go in LumenShell's slots: brand, headerActions, panel (a side panel such as an assistant), wrap (providers around the page), shared, profile (the profile menu) and statusEnd (the right end of the status bar).

Use createDeskQueryClient() for the app's TanStack Query client: data stays fresh 5 minutes and cached 30.

What's inside

Frame. LumenShell (sidebar with icons-only collapse and a drawer on phone, header, status bar, Settings, profile menu, ⌘K Find), LumenAppSwitcher and LumenSuiteSwitcher (groups, apps on other sites, a list read from an address), WhatsNew (release notes in a drawer), TasksButton and TasksProvider (background work in the header), NotificationsSettings, McpSettings, sign-in screens (LumenAuthStage, LumenSecondStep, LumenSignInMethods, LumenBackupCodes, LumenBotCheck for Cloudflare Turnstile, authErrorText for errors in plain words), StatusHealth, LumenBoundary (keeps a crash inside its card), LumenPageState (loading, empty and error), lumenToast.

Pages and views. LumenLayout (a page of cards from one view config: filter, tiles, widgets; Edit layout and Edit widget when you pass onConfigChange; a card with header: false is its body alone), LumenGrid (the 6-column grid under it), LumenView and LumenViewCard (a table that filters, groups, pivots and totals the rows you load), LumenTable, LumenTokenizedFilter, LumenWidget and LumenSavedWidget (the frame every card sits in, with its link square and controls), LumenCard (one number and its lines), LumenPager, DetailsTable, LumenTree, LumenCalendar (events by month, week or agenda, in a time zone), LumenBoard (items in columns by stage), useAppView (a page whose layout each person can change and reset).

Charts. LumenChart (one or more symbols' prices, with indicators), LumenEChart, LumenGeoMap, growth and price charts, StackedBars, StackedColumns, TimeLines (lines over time, with an optional right axis), RankedBars (with each bar's share of the total or of the first bar, and an empty state).

Pickers and panels. LumenSelector (every picker), LumenDrawer with DrawerSection (a side panel on desktop, a sheet on phone), LumenTabs, LumenConfirm (destructive confirms), LumenForm, LumenDateInput, LumenChoice, LumenSecret, LumenFileField (drop or pick a file), RenameField, ResetButton, SaveButton with FormIssue (a Save that says why it can't), IconPicker, LumenPlayer (video, audio, or slides over audio, with chapters, a following transcript and resume).

Primitives. The shadcn parts under @columenlabs/lumen-ui/ui/* (ui/button, ui/badge, ui/input, ...) and theme.css (light and dark tokens).

Every export, its props and an example are on the docs and in llms-full.txt.

Rules the kit follows, and apps should too

  • Pickers are LumenSelector, side panels LumenDrawer, tabs LumenTabs, destructive confirms LumenConfirm; reach for the raw ui/* part only when no Lumen part fits.
  • Every card body sits in LumenBoundary, so one crash never blanks a page.
  • Numbers use tabular figures and a true minus −; a gain or loss always shows a sign or arrow.
  • A page's header numbers are metric cards: TileRow of Tile (or TileToggleGroup when they pick what the page shows), with fill so they share the row, each with spark set to its daily history where it has one. Don't build a card of your own for this.
  • Column widths: the first column (a row's name) grows only to --lead-col-max (18rem, theme.css); past it the cell truncates on one line and a plain text value shows in full on hover (a rendered cell brings its own hover card). Cap any other column with the field's maxWidth ('12rem'), or lift the cap with maxWidth: false. In a rendered name cell, put the name in a truncate span so the ellipsis lands on it, not on a chip after it.
  • Rows never wrap. Every row (data, group, sub-group, Total) reads on one line at every width. On a phone each row is a one-line card: its name (ellipsis, full name on hover) and one value on the right, the sorted column, else the first number or date. The other values are in the row's drawer or panel. Pick which value a phone shows by sorting (Sort by) or by marking the others hideOnPhone.
  • Column alignment is the table's, not the cell's. Text left, numbers right, and a number column has one right edge for its header, values, dashes and Total. UnitValue puts the unit in a fixed slot after the number (--unit-slot, 2rem) and its status dot just left of the number. In a LumenView table the first UnitValue drawn in a column marks it (data-units on every cell of that column, through the UnitColumns context and the cell's data-col); theme.css then widens that column's right gutter by the slot and hangs each unit in it. So a plain —, Free or $0.21 an app draws in the same column sits on the digits' edge, and the header sits over the digits. Don't hand-place units, dots or padding to line a column up; if a column is off, draw its numbers through UnitValue. Outside a LumenView table (a hover card, a tile) UnitValue keeps its slot inline.
  • A metered column (use against an allowance) shows one number per cell with UnitValue (right-aligned number, fixed muted unit slot, status dot), never value [pct]. Offer both readings through the field's showAs (Amount / % of allowance, picked per column in Edit view, saved as shows); default amounts for sizes (storage) and shares for capped counts. Put the other reading, the allowance and the cost formula in a hover card. Totals use rollRender: sum amounts with their unit; a share is the rows' total over their allowance, never a sum of percents.
  • A floating bulk-action bar over a table's selection: Apply clears the selection and the bar goes away; the toast's Undo restores the change, not the selection. Cancel keeps the selection.
  • Bars in a column line up: use ShareMeter (fixed track, fixed right-aligned number), never a bar sized by its label.
  • A shared cap split by who used it (an account's allowance by label): ShareMeter segments={[{ value, label }]} names each part's share in the bar's hover; the bar itself keeps one colour, its level's, so colour always says how full it is. Roll such rows up with poolShare(rows, { pool, used, cap }) in a field's rollRender: a pool's share is its rows' use over the cap (never a sum or max of their own %), and a group across several pools reads the fullest one (top).
  • Meter levels are the kit's, not the app's. A usage share reads ok (green) under 75%, watch (amber) from 75%, alert (orange) from 90%, over (red) from 100%; past 100% the ShareMeter stays full and a hatched stub spills past a tick at the cap, up to +50%. A health share (kind="health": uptime, success rate) reads green at 99% and over, amber from 95%, red under 95%. ShareMeter takes its colour from the share unless you pass color; UnitValue share={0.8} tints its number and dot from amber up (green numbers stay plain); progress cards follow the same levels. For any other mark (a dot, a chip, a "nearest cap" cell) call meterLevel(share, levels, kind) and colour it with METER_LEVEL_COLOR or meterValueTone(share). Override per app with configureLumen({ meterLevels: { watch: 0.8 }, healthLevels: { watch: 0.995 } }) (each level can move alone) or per meter with levels. Don't hand-roll share >= 0.75 checks.
  • Pace (time to a cap at the recent rate) is the kit's: burnRate(samples, { per, gauge }) over the window's complete samples (hourly, or daily for 7d), then timeToCap(rate, used, cap, resetInHours). A lone sample above 3× the window's median counts as 3× the median (spikes says how many); a run of hot samples counts in full. Show it with TimeToCap (in 2h 05m, At cap, Resets first; short on a phone), coloured by the meter level of where the meter lands at reset, and pick the window with PaceWindowPicker (6h, 12h, 24h, 7d) in the page header; usePaceWindow() reads the same choice everywhere, kept in the viewer's browser (setDefaultPaceWindow sets the app's default). Where most hours are empty (median 0) a lone nonzero hour counts as 0, and a window with too little use (timeToCap(..., { minTotal }), default 0.1% of the cap) is thin: no time to the cap. Don't hand-roll ETAs or a window toggle.
  • A change between periods (this week against last) is trendChange(now, prev, { minBase }) drawn by TrendValue: ▲ 12% over a big enough earlier period, the plain difference ▲ +3 when it is at or under minBase, ▲ >10× past ten times. A % over a near-empty baseline misleads; never show one.
  • Table rows are one line. A cell shows one value (with a small bar or tag at most); anything more (other metrics, the allowance, a breakdown) goes in a hover tooltip, never in stacked text or sub-rows.
  • Chart colours are theme tokens (var(--chart-1), useChartTokens()), never hex picked for one theme; LumenEChart resolves var(--…) in its option so sparklines read in light and dark.
  • "No label" filters come built in: LumenTokenizedFilter pins a "No " row on any text field where some rows have no value (an empty token); don't add your own toggle for it.
  • Fewer tab rows: one row of tabs at most per card; narrower cuts (a product, a type) are fields in LumenTokenizedFilter, and a link to one opens the tab with that filter chip set.
  • One selected sidebar row: give each nav item its own route, and let an open parent look neutral.
  • What's new is on in every app: pass releases="/api/releases" to LumenShell, served by lumen-ship's listReleases.
  • A Save blocked by its form's checks is a SaveButton that lists each issue, never a bare disabled.

Notices

Charts use TradingView Lightweight Charts™ with its logo off. Please link to https://www.tradingview.com/ somewhere in your app (see NOTICE).

Versions

0.x: any minor may break things until 1.0. Every merge to Lumen's main publishes the next patch of every Lumen package; see the releases.

版本与发布记录

@columenlabs/lumen-ui 共发布 232 个版本,首次发布于 2026/10/23;过去 12 个月发布了 232 个版本。

版本发布日期距今
0.1.219latest2026/11/091 天前
0.1.2182026/11/091 天前
0.1.2172026/11/091 天前
0.1.2162026/11/091 天前
0.1.2152026/11/091 天前
0.1.2142026/11/091 天前

其他发布通道:legacy → 0.1.12。

关于 @columenlabs/lumen-ui 的常见问题

@columenlabs/lumen-ui 是什么?

Lumen: shared React components (frame, views, widgets, charts, drawers).

如何安装 @columenlabs/lumen-ui?

在项目目录运行 npm install @columenlabs/lumen-ui,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:react(^19.0.0)、react-dom(^19.0.0)、@tanstack/react-query(^5.103.2)、@tanstack/react-router(^1.170.39)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@columenlabs/lumen-ui 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@columenlabs/lumen-ui 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@columenlabs/lumen-ui 可以商用吗?

@columenlabs/lumen-ui 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@columenlabs/lumen-ui 的下载量有多少?

根据 npm 官方下载统计,@columenlabs/lumen-ui 在 2026/10/11 – 2026/11/09 这 30 天内被下载 51,271 次,日均约 1,709 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@columenlabs/lumen-ui 还在维护吗?

最新版本 0.1.219 发布于 2026/11/09(1 天前);至今共发布 232 个版本,其中过去 12 个月发布了 232 个;首次发布于 2026/10/23。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。