OTFotf

Overview

The @otfdashkit/ui component surface — primitives, composites, blocks, and advanced.

@otfdashkit/ui is the web side of the OTF design system. It bundles ~85 components in four buckets, all built on Radix UI + Tailwind and styled via the shared @otfdashkit/tokens CSS variables.

Buckets

BucketWhat's thereWhen to reach for it
Primitives50 unstyled-but-themed building blocks (Button, Input, Dialog, Tabs, …)You're composing your own UI
Composite16 opinionated pieces (Persona, Stat, Banner, DataGrid, …)You want a finished pattern, not a Lego brick
Blocks~25 product-shape blocks — sidebars, modals, dashboard layoutsYou're standing up a new app screen and want a working starting point
Advanced7 power-user pieces — Kanban, Gantt, Rich Editor, Command Bar, Filters, Bulk ActionsYou're building an internal-tool-grade interface

Importing

Every export sits at the package root:

import {
  Button,
  Input,
  Dialog, DialogContent, DialogTitle,
  Stat,
  Persona,
  CommandBar,
} from '@otfdashkit/ui'

There's no submodule split (e.g. @otfdashkit/ui/blocks) — the bundler tree-shakes unused exports just fine, and a single import surface is what AI tools handle best when they write code against your kit.

Theming

Every component reads its colors from the tokens published by @otfdashkit/tokens. To switch themes at runtime, swap the class on <html>:

document.documentElement.className = 'theme-warm dark'

See Tokens / Themes for the four shipped themes (theme-slate, theme-warm, theme-cosmic, theme-terminal) and how to author your own.

Charts

Chart, BarChart, LineChart, AreaChart, BarList, Sparkline, Heatmap, and ActivityHeatmap all live in @otfdashkit/ui and consume the same chart tokens. Always pass theme tokens for colors:

<BarChart
  data={data}
  dataKey={['issues', 'completed']}
  colors={['hsl(var(--chart-1))', 'hsl(var(--chart-3))']}
  stacked
/>

When passing data to recharts-backed charts, deep-clone first — recharts mutates the array, and Storybook's frozen args throw:

<BarChart data={data.map((d) => ({ ...d }))} ... />

Toasts

Toasts ship inside @otfdashkit/ui/styles. To render them, mount <Toaster /> once at the root of your app and call toast(...) imperatively from anywhere:

import { Toaster, toast } from '@otfdashkit/ui'

export default function RootLayout({ children }) {
  return (
    <>
      {children}
      <Toaster />
    </>
  )
}

A11y + linting

@otfdashkit/eslint-plugin-otf-design ships rules that catch:

  • Hardcoded hex / RGB outside lib/theme.ts
  • Inline style={{ background: '...' }}
  • Missing data-slot attributes on custom components

Add it to your ESLint config and your kit will inherit the same conventions the OTF kits themselves are linted against.

What's NOT in the box

Some pieces are deliberately not shipped:

  • Charts library. We re-export recharts wrappers, not a custom charting engine.
  • Form library. Form, FormField are thin Radix primitives; bring your own resolver (zod / valibot / yup) via @hookform/resolvers.
  • Date library. Calendar is a wrapper around react-day-picker; format dates with whatever you already use.
  • State management. The blocks accept controlled props; pick TanStack Query / Zustand / Redux yourself.

This keeps the bundle small and the API surface honest.

On this page