Components

All components live in packages/ui/src/components/ and are consumed via the workspace alias @repo/ui/components/[name]. Each follows the CVA + Radix UI + Tailwind pattern and accepts a className prop for overrides.

The component library uses Tailwind CSS v4. Class names reference design tokens via @theme inline. All color classes like bg-brand, text-foreground-muted, and border-border-control resolve to the token values defined in @repo/ui/theme.css.

Atom components

Button

Import: @repo/ui/components/button

The primary action component. Variants map to semantic intent. All variants share the same size scale and focus ring.

VariantUse case
default / primaryPrimary CTAs, form submits, strong positive actions
secondaryLess prominent actions, paired with a primary button
outlineTertiary actions, cancel buttons
ghostIcon buttons, contextual actions in dense UI
destructiveDelete, remove, irreversible actions
warningActions with side effects, non-destructive but caution-warranting
linkIn-text navigation, lowest priority actions

Size scale

Three sizes: sm (28px), md (32px, default), lg (40px). Each has a matching icon-only variant.

SizeHeightUsage
sm28px (h-7)Filter bars, toggle pills, inline controls. Used in filter rows and toolbars (type filters, sort modes, sprint toggles).
md (default)32px (h-8)Action bar buttons, form actions, dialogs. The standard size for page-level actions like "New Task", "View PRD", split buttons.
lg40px (h-10)Hero CTAs, landing page buttons.
icon32px (h-8 w-8)Square icon-only button at md size.
icon-sm28px (h-7 w-7)Small square icon button. Used for inline edit/save icons on naked inputs and compact toolbar controls.
icon-md32px (h-8 w-8)Medium square icon button. Used for action bar icon menus (e.g. MoreVertical).
import { Button } from '@repo/ui/components/button'

<Button>Save changes</Button>              {/* md (default) */}
<Button variant="outline">Cancel</Button>
<Button variant="destructive">Delete project</Button>
<Button size="sm" variant="outline">Filter</Button>
<Button size="lg">Get Started</Button>
<Button size="icon-sm" variant="outline"><Pencil /></Button>
<Button size="icon-md" variant="ghost"><MoreVertical /></Button>

Badge

Import: @repo/ui/components/badge

Status indicators, count chips, and classification tags. Renders as an inline-flex pill. All variants inherit the base size and border-radius — only color changes.

VariantColorUse case
defaultBrand (solid)Primary label, selected state
secondarySurface-300 backgroundNeutral metadata, normal priority
outlineBorder onlySubtle tags, assignee chips
brandBrand-200 background, brand-600 textProject names, brand-associated labels
successBrand-200 background (alias of brand)Completed state, positive outcomes
warningWarning-200 background, warning-600 textHigh priority, caution states
destructiveDestructive-200 background, destructive-600 textUrgent priority, error states, deletion
ghostSurface-200 background, lighter textLow priority, inactive or deprioritized labels
import { Badge } from '@repo/ui/components/badge'

<Badge>Default</Badge>
<Badge variant="secondary">Normal</Badge>
<Badge variant="warning">High</Badge>
<Badge variant="destructive">Urgent</Badge>
<Badge variant="ghost">Low</Badge>
<Badge variant="brand">Project name</Badge>
<Badge variant="outline">Rick</Badge>

For truncating text inside a badge (e.g. long project names), wrap the content in a span with truncate and constrain the badge with max-w-full:

<Badge variant="brand" className="max-w-full">
  <span className="truncate">{projectName}</span>
</Badge>

Input

Import: @repo/ui/components/input

Standard text input with brand-green focus ring. Uses border-border-control at rest and ring-2 ring-brand/40 border-brand on focus.

Naked Input

Import: @repo/ui/components/naked-input

A borderless inline-edit input that auto-sizes to its content width. Used for in-place editing of titles, names, and other text that should look like static text until clicked.

PropTypeDescription
valuestringCurrent input value
onChange(value: string) => voidCalled on every keystroke
onSave(value: string) => voidCalled on blur, Enter, or Tab
onCancel() => voidCalled on Escape
inputClassNamestringApplied to both the input and the hidden sizer span for font matching

Behavior:

  • Auto-focuses on mount with cursor at end of text (not selecting all)
  • Width auto-sizes to content via a hidden sizer span
  • Transparent background, no border — blends with surrounding text
  • Pair with a Button size="icon-xs" for a save/cancel icon
import { NakedInput } from '@repo/ui/components/naked-input'

// Editing state
const [editing, setEditing] = useState(false)
const [draft, setDraft] = useState(project.name)

// In JSX — toggle between static text and naked input
{editing ? (
  <NakedInput
    value={draft}
    onChange={setDraft}
    onSave={handleSave}
    onCancel={() => setEditing(false)}
    inputClassName="text-xl font-medium text-foreground"
  />
) : (
  <button onClick={() => setEditing(true)}>
    {project.name}
    <Button size="icon-sm" variant="outline">
      <Pencil />
    </Button>
  </button>
)}

Textarea

Import: @repo/ui/components/textarea

Same focus and border treatment as Input. Set rows for height.

Select

Import: @repo/ui/components/select

Radix Select with matching input styling. Exports: Select, SelectTrigger, SelectContent, SelectItem, SelectValue, SelectGroup, SelectSeparator.

Checkbox

Import: @repo/ui/components/checkbox

Radix Checkbox. Checked state uses bg-brand. Pair with a Label component using Radix's htmlFor / id pattern.

Switch

Import: @repo/ui/components/switch

Radix Switch. On state: bg-brand. Off: bg-surface-400.

Radio Group

Import: @repo/ui/components/radio-group

Exports: RadioGroup, RadioGroupItem.

Label

Import: @repo/ui/components/label

Radix Label. Automatically handles peer-disabled opacity. Always pair with form controls using matching id /htmlFor.

Card

Import: @repo/ui/components/card

Surface container using bg-surface-100 border-border. Exports: Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter.

Alert

Import: @repo/ui/components/alert

Callout boxes. Variants: default, info, success, warning, destructive. All use the corresponding -200 background with -600 text.

Skeleton

Import: @repo/ui/components/skeleton

Animated pulse loading placeholder. Use in place of content while data loads.

Progress

Import: @repo/ui/components/progress

Radix Progress with brand fill. Accepts a value prop (0–100).

Avatar

Import: @repo/ui/components/avatar

Radix Avatar with bg-brand-200 text-brand-600 fallback initials. Exports: Avatar, AvatarImage, AvatarFallback.

Dialog

Import: @repo/ui/components/dialog

Radix Dialog with bg-surface-100 background. Exports: Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, DialogClose, DialogTrigger.

Popover

Import: @repo/ui/components/popover

Radix Popover. bg-surface-100 border-border with slide-in animations. Exports: Popover, PopoverTrigger, PopoverContent, PopoverAnchor.

Tooltip

Import: @repo/ui/components/tooltip

Radix Tooltip. Dark bg-surface-400 background for contrast against light and dark surfaces. Wrap app root with TooltipProvider. Exports: Tooltip, TooltipTrigger, TooltipContent, TooltipProvider.

Tabs

Import: @repo/ui/components/tabs

Radix Tabs. Active tab: bg-background text-foregroundelevated on a bg-surface-200 pill background. Exports: Tabs, TabsList, TabsTrigger, TabsContent.

Accordion

Import: @repo/ui/components/accordion

Radix Accordion with CSS-variable-driven height animation (animate-accordion-down / animate-accordion-up). Requires the keyframes defined in admin's globals.css. Exports: Accordion, AccordionItem, AccordionTrigger, AccordionContent.

Table

Import: @repo/ui/components/table

Native HTML table with design token classes. Hover rows use bg-surface-200/50. Exports: Table, TableHeader, TableBody, TableRow, TableHead, TableCell, TableFooter, TableCaption.

Scroll Area

Import: @repo/ui/components/scroll-area

Radix ScrollArea. Styled scrollbar thumb using bg-border-strong. Exports: ScrollArea, ScrollBar.

Toggle

Import: @repo/ui/components/toggle

Radix Toggle. On state: bg-surface-300 text-foreground. Variants: default (transparent), outline(bordered).

Fragment patterns

Fragment components are higher-order patterns composed from atoms. They are built in each app rather than shared via @repo/ui since they are layout-specific. Common patterns:

  • Page header — Breadcrumb + h1 + description + action button(s)
  • Form item layout — Label + Input/Select + description + error message
  • Confirmation modal — Dialog + destructive Button + cancel
  • Data table — Table + sort headers + pagination
  • Metric card — Card + large value + label + trend indicator
  • Empty state — Icon + heading + description + CTA Button

Chart components

The chart library lives in @repo/ui alongside the atom components. It wraps recharts with consistent token-based styling and composable slot components. See the dedicated documentation pages for full API reference and usage examples.

  • Charts ChartCard, BarChartCard, LineChartCard, LogsBarChart, ChartTooltip
  • Metric Cards MetricCard, MetricCardValue, MetricCardDifferential, MetricCardSparkline