Skip to content

UI Components

The @simplemodule/ui package provides a comprehensive set of React components built on Radix UI primitives with Tailwind CSS styling. It follows the shadcn/ui approach -- components live in your workspace as source code, not as an opaque npm dependency.

Package Overview

The package is located at packages/SimpleModule.UI/ and uses two export paths:

ts
// Import components
import { Button, Card, Dialog } from '@simplemodule/ui';

// Import utilities
import { cn } from '@simplemodule/ui/lib/utils';

The cn Utility

The cn function combines clsx and tailwind-merge to safely merge Tailwind classes:

ts
import { cn } from '@simplemodule/ui/lib/utils';

<div className={cn('p-4 bg-surface', isActive && 'bg-primary-subtle', className)} />

This avoids class conflicts (e.g., p-4 and p-2 don't both end up in the DOM -- tailwind-merge resolves them).

Available Components

Layout

ComponentDescription
ContainerCentered max-width container with variant sizes
GridCSS grid wrapper with configurable columns
StackFlexbox stack (vertical/horizontal) with gap control
PageShellFull page wrapper with title, description, and breadcrumbs
PageHeaderPage header with title and actions slot
SectionSemantic section wrapper
SeparatorVisual divider (horizontal or vertical)
AspectRatioMaintains a fixed aspect ratio for content
ScrollAreaCustom scrollbar container
ResizableResizable panel layout (ResizablePanel, ResizableHandle, ResizablePanelGroup)

Data Display

ComponentDescription
CardSurface container (Card, CardHeader, CardContent, CardFooter, CardTitle, CardDescription)
TableStyled table (Table, TableHeader, TableBody, TableRow, TableHead, TableCell)
DataGridFeature-rich data table with sorting, filtering, and pagination
DataGridPageFull-page data grid with built-in search and actions
BadgeInline status indicator with variants
AvatarUser avatar with image and fallback
HoverCardCard shown on hover
TooltipContextual tooltip
CalendarDate calendar display
ChartRecharts wrapper with theming (ChartContainer, ChartTooltip, ChartLegend)
SkeletonLoading placeholder
SpinnerLoading spinner with size variants
ProgressProgress bar
StatDashboard metric tile -- value, unit, label, trend, and optional interactive/lift
EmptyStateStandard empty-state block with icon, title, description, and action slots
FilterBarToolbar for list pages with search, controls, and actions slots
ComponentDescription
BreadcrumbBreadcrumb navigation (Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator)
TabsTab navigation (Tabs, TabsList, TabsTrigger, TabsContent)
SidebarApplication sidebar (Sidebar, SidebarHeader, SidebarContent, SidebarMenu, SidebarMenuItem, etc.)
DropdownMenuDropdown menu (DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, etc.)
CommandCommand palette / search (Command, CommandInput, CommandList, CommandItem, etc.)
PopoverFloating content panel

Forms

ComponentDescription
ButtonButton with variants (primary, secondary, ghost, danger, outline) and sizes (sm, default, lg, icon)
InputText input with variants
SearchInputSearch field with a leading icon and optional shortcut hint
NumberInputNumeric input with stepper buttons; clamps to min/max
KbdInline keyboard key cap (e.g. ⌘K)
TextareaMulti-line text input
CheckboxCheckbox input
RadioGroupRadio button group (RadioGroup, RadioGroupItem)
SelectDropdown select (Select, SelectTrigger, SelectValue, SelectContent, SelectItem)
SwitchToggle switch
SliderRange slider
LabelForm label
FieldForm field wrapper with label, description, and error (Field, FieldGroup, FieldDescription, FieldError)
DatePickerDate picker combining calendar and popover
ToggleToggle button
ToggleGroupGroup of toggle buttons

Feedback

ComponentDescription
AlertAlert message with variants (Alert, AlertTitle, AlertDescription)
DialogModal dialog (Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter)
SheetSlide-out panel (Sheet, SheetTrigger, SheetContent, SheetHeader, SheetTitle, SheetDescription)
ToastToast notification (Toast, ToastProvider, ToastViewport, ToastTitle, ToastDescription, ToastAction)
AccordionCollapsible content sections (Accordion, AccordionItem, AccordionTrigger, AccordionContent)
CollapsibleSingle collapsible section

Usage Examples

Page with Cards

tsx
import { Card, CardContent, PageShell } from '@simplemodule/ui';
import type { Customer } from '../types';

export default function Browse({ customers }: { customers: Customer[] }) {
  return (
    <PageShell title="Customers" description="Browse the customer list.">
      <div className="space-y-3">
        {customers.map((c) => (
          <Card key={c.id}>
            <CardContent className="flex justify-between items-center">
              <span className="font-medium">{c.name}</span>
              <span className="text-text-muted">{c.email}</span>
            </CardContent>
          </Card>
        ))}
      </div>
    </PageShell>
  );
}

Form with Field Components

tsx
import { Button, Field, FieldError, FieldGroup, Input, Label } from '@simplemodule/ui';

function CreateForm() {
  return (
    <form>
      <FieldGroup>
        <Field>
          <Label>Customer Name</Label>
          <Input name="name" placeholder="Enter customer name" />
          <FieldError>Name is required</FieldError>
        </Field>
        <Button type="submit">Create Customer</Button>
      </FieldGroup>
    </form>
  );
}

Button Variants

tsx
import { Button } from '@simplemodule/ui';

<Button>Save</Button>                          {/* variant="primary" by default */}
<Button variant="secondary">Cancel</Button>
<Button variant="ghost">Dismiss</Button>
<Button variant="danger">Delete</Button>
<Button variant="outline">Learn more</Button>

<Button size="sm">Compact</Button>
<Button size="lg">Hero CTA</Button>

asChild forwards the variant styles to a child element (useful with <Link>):

tsx
import { Link } from '@inertiajs/react';

<Button asChild>
  <Link href="/customers/new">New customer</Link>
</Button>

Data Grid Page

tsx
import { DataGridPage } from '@simplemodule/ui';

export default function Manage({ customers }) {
  return (
    <DataGridPage
      title="Customers"
      description="Manage your customer list."
      columns={columns}
      data={customers}
      // Shown automatically when data is empty:
      emptyTitle="No customers yet"
      emptyDescription="Create your first customer to get started."
    />
  );
}

DataGridPage renders an EmptyState internally when data is empty; emptyTitle, emptyDescription, emptyIcon, and emptyAction customize it.

Dashboard Stats

tsx
import { Stat } from '@simplemodule/ui';

<div className="grid gap-4 sm:grid-cols-3">
  <Stat label="Active users" value={1284} trend="up" change="+12% vs last week" />
  <Stat label="Avg. latency" value={98} unit="ms" trend="down" />
  <Stat label="Error rate" value="0.4%" trend="flat" onClick={openDetails} />
</div>

When onClick is provided, Stat renders as a keyboard-accessible <button> and turns on the interactive affordance.

tsx
import { FilterBar, SearchInput, Button } from '@simplemodule/ui';

<FilterBar
  search={<SearchInput placeholder="Search customers" shortcut="⌘K" />}
  controls={<Button variant="secondary" size="sm">Filters</Button>}
  actions={<Button>New customer</Button>}
/>

Empty State

tsx
import { EmptyState, Button } from '@simplemodule/ui';

<EmptyState
  title="No results"
  description="Try adjusting your search or filters."
  action={<Button variant="secondary">Clear search</Button>}
/>

Dependencies

The UI package relies on these key dependencies:

  • Radix UI -- Accessible, unstyled primitives (dialog, dropdown, select, tabs, etc.)
  • class-variance-authority (CVA) -- Type-safe component variants
  • clsx + tailwind-merge -- Class name composition
  • cmdk -- Command palette
  • react-day-picker -- Calendar and date picker
  • recharts -- Charting library

React and React-DOM are peer dependencies -- they are provided by the host application, not bundled.

Next Steps

Released under the MIT License.