# Twentyfirst UI: complete docs > Version 0.6.0. Examples import from `@twentyfirstmedia/halo`; for Pro import the same names from `@twentyfirstmedia/pro`. Patterns (full screens) are linked at the end; their code is in each linked Markdown page. ## Introduction > Halo is one of the two Twentyfirst UI kits: React components, tokens and complete screens with the same API as Pro. Twentyfirst UI is two React UI kits with one component API. - **Halo** is soft, rounded and light-first: big white cards on a warm canvas, five pastel tints that carry meaning and ink as the only accent. - **Pro** is sharp, dense and dark-first: monochrome cells on a visible hairline grid, square corners, Geist, and colour only for state. Both kits have exactly the same components, props, markup, classes and tokens. Only tokens and CSS differ, so an app can load both and switch at runtime with `data-kit`, without changing markup or losing state. ### What is in the box - 236 components on 119 pages: foundations, navigation, forms, data, charts, overlays, AI (chat, agents, approvals), commerce, admin, real estate and media. - 37 patterns: complete screens built only from kit parts. - Tokens for colour, type, space, radius, shadow and motion in light and dark, as CSS variables and as a Tailwind v4 preset. - A CLI that sets up an app and copies components when you want to own them, and an MCP server so coding agents know every component. ### Principles - **Mobile first.** Every component works from a 320px screen up: shells collapse, tables scroll, dialogs become sheets and inputs never zoom. - **Accessible.** Keyboard paths, focus rings and ARIA follow the WAI-ARIA practices; axe (WCAG 2.1 AA) runs on every example of both kits, light and dark. - **Tokens only.** No colour literals in component CSS. - **Private.** Fonts are self-hosted, nothing phones home and components make no requests of their own. - **English, formatted with Intl.** Built-in text is plain English and overridable through props or `UIProvider messages`; numbers, money, percentages and dates go through `formatNumber`, `formatMoney`, `formatPercent` and `formatDate`, in en-US or the `UIProvider locale` ([Text and locale](https://ui.twentyfirst.media/halo/text/)). ### Quick start ```sh npx @twentyfirstmedia/ui init --kit halo ``` ```tsx import "@twentyfirstmedia/halo/styles.css"; import { UIProvider, Button } from "@twentyfirstmedia/halo"; export function App() { return ( ); } ``` Next: [Installation](https://ui.twentyfirst.media/halo/installation/), [CLI](https://ui.twentyfirst.media/halo/cli/), [Theming and tokens](https://ui.twentyfirst.media/halo/theming/), [AI agents and MCP](https://ui.twentyfirst.media/halo/ai/) and [all components](https://ui.twentyfirst.media/halo/components/). ## Installation > Install one kit from npm, import its styles once and wrap the app in UIProvider. React 18.3 or 19; works with Next.js, Vite and any React setup. ### 1. The kit The packages are on npm under the MIT license. Install a kit with the CLI (it also sets up styles, the provider and the Next.js layout) or by hand: ```sh npx @twentyfirstmedia/ui init --kit halo # or pnpm add @twentyfirstmedia/halo ``` ### 2. Styles and provider Import `styles.css` once. It brings the self-hosted fonts, the tokens (light, dark, density) and every component style; no `data-kit` attribute is needed. ```tsx import "@twentyfirstmedia/halo/styles.css"; import { UIProvider, Button } from "@twentyfirstmedia/halo"; export function App() { return ( ); } ``` ### Tailwind v4 The kit ships a preset that maps every token to Tailwind and clears the default palette, so utilities can only use kit values. ```css @import "tailwindcss"; @import "@twentyfirstmedia/halo/tailwind.css"; ``` ### Both kits in one app Import `@twentyfirstmedia/ui-core/themes.css` instead (both kits, scoped under `[data-kit="…"]`) and switch with `` or the attribute on ``. Markup, props and state stay as they are. ```tsx import "@twentyfirstmedia/ui-core/themes.css"; import { UIProvider } from "@twentyfirstmedia/halo"; {children}; ``` ### Fonts for share images Tools that cannot read woff2 or variable fonts, such as `next/og` (Satori) or PDF generators, get static WOFF files under `fonts/og/`: Halo ships Plus Jakarta Sans (400–800) and IBM Plex Mono (500, 600), Pro ships Geist (400–700) and Geist Mono (500, 600), latin subset. ```tsx // app/opengraph-image.tsx import { readFile } from "node:fs/promises"; import { createRequire } from "node:module"; const require = createRequire(import.meta.url); const font = await readFile( require.resolve("@twentyfirstmedia/halo/fonts/og/plus-jakarta-sans-latin-700-normal.woff"), ); // new ImageResponse(
…
, { fonts: [{ name: "Plus Jakarta Sans", data: font, weight: 700 }] }) ``` ### Coding agents Give Claude Code, Cursor or VS Code the kit's docs with the MCP server, see [AI agents and MCP](https://ui.twentyfirst.media/halo/ai/): ```sh npx @twentyfirstmedia/ui mcp init ``` ## CLI > npx @twentyfirstmedia/ui sets up a kit, copies components into your app when you want to change them and runs the MCP server for coding agents. ### init Installs the kit, imports its styles (and the Tailwind preset when the app uses Tailwind), creates `components/ui-root.tsx` with the `UIProvider`, wraps the Next.js layout and sets `data-kit` on ``. Running it again changes nothing. ```sh npx @twentyfirstmedia/ui init --kit halo npx @twentyfirstmedia/ui init --kit halo --both # load both kits and switch at runtime ``` ### add Copies a component, with the private helpers it needs, into `components/ui`. Everything public stays an import from `@twentyfirstmedia/ui-core`, so the copy keeps working in both kits. ```sh npx @twentyfirstmedia/ui add ApprovalCard DataTable ``` ### list and diff `list` shows every component by group and marks the ones you copied. `diff` compares your copy with the current package source, so updates are easy to merge by hand. ```sh npx @twentyfirstmedia/ui list npx @twentyfirstmedia/ui diff ApprovalCard ``` ### mcp `mcp` runs the MCP server on stdio; `mcp init` writes the config for Claude Code, Cursor and VS Code and adds the kit rules to `AGENTS.md`. See [AI agents and MCP](https://ui.twentyfirst.media/halo/ai/). ```sh npx @twentyfirstmedia/ui mcp init --kit halo npx @twentyfirstmedia/ui mcp init --client cursor --no-rules ``` ## Theming and tokens > Theme both kits like shadcn/ui: the same variables (primary, secondary, muted, accent, destructive, border, ring, radius, charts, sidebar) restyle every component, light and dark. Both kits use shadcn/ui's theme variables. A theme is any CSS that sets them, so a theme from ui.shadcn.com/themes, tweakcn or your own design system works as it is, and every component follows it: buttons, fields, menus, tables, charts, the Sidebar, in Halo and in Pro, light and dark. ```css @import "@twentyfirstmedia/halo/styles.css"; :root { --primary: oklch(0.546 0.245 262.881); --primary-foreground: oklch(0.985 0 0); --ring: oklch(0.546 0.245 262.881); --radius: 0.75rem; } .dark, [data-theme="dark"] { --primary: oklch(0.707 0.165 254.624); --primary-foreground: oklch(0.145 0 0); } ``` The kits define their variables with zero specificity, so your theme wins in any order. Dark mode follows `data-theme="dark"` or the `.dark` class on `` (next-themes works as it is), and `data-theme="system"` follows the operating system. ### The variables | Variable | Used for | | --- | --- | | `--background`, `--foreground` | Page background and default text | | `--card`, `--card-foreground` | Cards, panels, fields, the workspace sheet | | `--popover`, `--popover-foreground` | Menus, popovers, select lists, command palette, hover cards, dialogs | | `--primary`, `--primary-foreground` | Primary buttons, checked boxes and radios, switches, selected chips and days, progress, current marks | | `--secondary`, `--secondary-foreground` | Soft buttons and icon buttons | | `--muted`, `--muted-foreground` | Quiet fills (tracks, search fields, code) and secondary text | | `--accent`, `--accent-foreground` | The highlight under the pointer or keyboard: menu items, rows, options, nav items | | `--destructive`, `--destructive-foreground` | Danger buttons, error text and icons | | `--border`, `--input`, `--ring` | Hairlines, field and checkbox borders, focus rings | | `--radius` | The base corner radius; every corner derives from it | | `--chart-1` … `--chart-5` | Chart series (lines, dots, legends) | | `--sidebar`, `--sidebar-foreground`, `--sidebar-primary`, `--sidebar-primary-foreground`, `--sidebar-accent`, `--sidebar-accent-foreground`, `--sidebar-border`, `--sidebar-ring` | The Sidebar | The kits' own names (`--canvas`, `--surface`, `--ink`, `--line`, Pro's `--bg-000` … `--fg-400` scale) point at these, and the in-between shades are mixed from them, so you only set the variables above. Outside the theme on purpose: the five tints and status colours (they carry meaning), Halo's dark app frame, paper (e-mail and invoice previews) and media overlays. Halo draws chart bars in soft fills of the series colours (`--chart-sky` … `--chart-blush`); the theme builder and `twentyfirst-ui theme` set them for you when the charts change. ### Presets, CLI and builder Six brand presets keep each kit's neutrals and change the brand colour: blue, violet, emerald, orange, rose and teal. ```css @import "@twentyfirstmedia/halo/styles.css"; @import "@twentyfirstmedia/halo/themes/violet.css"; /* both kits loaded: @twentyfirstmedia/ui-core/themes/violet.css */ ``` The CLI writes a theme into your app and imports it: a preset, a brand colour (light, optionally dark) or a shadcn theme file. ```sh npx @twentyfirstmedia/ui theme violet npx @twentyfirstmedia/ui theme "#2563eb,#60a5fa" npx @twentyfirstmedia/ui theme ./shadcn-theme.css ``` The [theme builder](https://ui.twentyfirst.media/halo/themes/) shows a theme live on real components and gives you the CSS. ### Themes at runtime For a brand per customer or a colour the user picks, `UIProvider` takes the same variables. One set applies in both modes; `{ light, dark }` picks by the current theme. ```tsx import { UIProvider, brandTheme } from "@twentyfirstmedia/halo"; {children}; {children}; ``` `themeCss(theme, { kit })` writes the CSS for a theme (for a server-rendered `