Components
Overview

Components Overview

Sunday uses a component-based architecture with Radix UI primitives and Tailwind CSS.

Component Organization

components/
├── ui/                 # Base UI components
│   ├── button.tsx
│   ├── dialog.tsx
│   ├── input.tsx
│   └── ...
├── board/              # Board-specific components
│   ├── table-view.tsx
│   ├── kanban-view.tsx
│   └── ...
├── layout/             # Layout components
│   ├── sidebar.tsx
│   ├── header.tsx
│   └── ...
├── automations/        # Automation panel
├── filters/            # Filter components
├── forms/              # Form components
├── search/             # Search components
└── landing/            # Landing page components

Design System

Colors

Primary colors use Tailwind's color palette:

PurposeClassExample
Primaryblue-600Buttons, links
Successgreen-500Done status
Warningyellow-500Review status
Errorred-500Stuck status
Neutralslate-*Text, borders

Typography

  • Headings: font-semibold or font-bold
  • Body: Default font weight
  • Monospace: Code blocks, IDs

Spacing

Uses Tailwind's spacing scale (4px base):

  • p-2 = 8px
  • p-4 = 16px
  • gap-4 = 16px

Shadows

shadow-sm    - Subtle elevation
shadow       - Cards, modals
shadow-lg    - Dropdowns
shadow-xl    - Popovers

Component Patterns

Composable Components

Components are designed to be composable:

<Dialog>
  <DialogTrigger>
    <Button>Open</Button>
  </DialogTrigger>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Title</DialogTitle>
    </DialogHeader>
    {/* Content */}
  </DialogContent>
</Dialog>

Controlled vs Uncontrolled

Most components support both modes:

// Uncontrolled (internal state)
<Checkbox />
 
// Controlled
<Checkbox checked={checked} onCheckedChange={setChecked} />

Forwarding Refs

Components forward refs for DOM access:

const Button = forwardRef<HTMLButtonElement, ButtonProps>(
  ({ className, ...props }, ref) => (
    <button ref={ref} className={className} {...props} />
  )
)

Radix UI Integration

Base UI components wrap Radix primitives:

import * as DialogPrimitive from '@radix-ui/react-dialog'
 
export const Dialog = DialogPrimitive.Root
export const DialogTrigger = DialogPrimitive.Trigger
 
export const DialogContent = forwardRef(({ className, ...props }, ref) => (
  <DialogPrimitive.Portal>
    <DialogPrimitive.Overlay className="fixed inset-0 bg-black/50" />
    <DialogPrimitive.Content
      ref={ref}
      className={cn("fixed left-1/2 top-1/2 ...", className)}
      {...props}
    />
  </DialogPrimitive.Portal>
))

Styling with Tailwind

Class Variance Authority

Use CVA for component variants:

import { cva } from 'class-variance-authority'
 
const buttonVariants = cva(
  "inline-flex items-center rounded-md font-medium",
  {
    variants: {
      variant: {
        default: "bg-blue-600 text-white hover:bg-blue-700",
        secondary: "bg-slate-100 text-slate-900 hover:bg-slate-200",
        ghost: "hover:bg-slate-100",
      },
      size: {
        sm: "h-8 px-3 text-sm",
        md: "h-10 px-4",
        lg: "h-12 px-6 text-lg",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "md",
    },
  }
)

cn() Utility

Merge Tailwind classes safely:

import { clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'
 
export function cn(...inputs) {
  return twMerge(clsx(inputs))
}
 
// Usage
<div className={cn(
  "base-classes",
  condition && "conditional-class",
  className
)} />