Skip to Content
UI Components

UI Components (@numa/ui)

Shared UI component library built with shadcn/ui, Radix UI, Tailwind CSS v4, and class-variance-authority (cva).

Stack

LibraryRole
Tailwind CSS v4Utility-first styling (CSS-first)
shadcn/uiUI primitives (Radix-based)
Radix UIAccessible headless components
cvaVariant-based component styling
lucide-reactIcon library
vaulDrawer component
react-day-pickerCalendar/date picker
sonnerToast notifications
tw-animate-cssAnimations

Package structure

packages/ui/ ├── globals.css # Tailwind v4 theme, design tokens, CSS variables ├── primitives/ # shadcn base components │ ├── button.tsx │ ├── calendar.tsx │ ├── dialog.tsx │ ├── drawer.tsx │ ├── popover.tsx │ ├── select.tsx │ └── sheet.tsx ├── shared/ │ ├── ui/ # G-prefixed shared components │ │ ├── typography/ # GText │ │ ├── button/ # GButton │ │ ├── input/ # GInput, GPassword, GEmail, GSelect, etc. │ │ ├── icon/ # GIcon │ │ ├── table/ # GTable │ │ ├── modal/ # Modal components │ │ ├── form/ # GForm (declarative form builder) │ │ ├── sonner.tsx # Toast re-export │ │ └── otp/ # OTP input │ ├── layout/ # Layout shells │ │ ├── energy/ # GLayoutEnergy, GEnergyPage │ │ ├── auth/ # Auth layout │ │ ├── dashboard/ # Dashboard layout │ │ └── containers/ # GContainer │ ├── loaders/ # Loading states │ ├── error/ # Error page components │ └── private/ # Permission guard components └── global.d.ts

G-prefixed components

Numa uses a G prefix convention for shared components to distinguish them from shadcn primitives:

ComponentPurpose
GButtonButton with variants, loading state, AppleSpinner
GTextTypography with size/weight variants
GIconTyped icon component using iconSrc from config
GInputText input with label, error, prefix/suffix icons
GPasswordPassword input with visibility toggle
GEmailEmail input with validation
GSelectSelect dropdown
GPhoneNumberPhone number input
GTextareaMulti-line text input
GFormDeclarative form builder (see Forms)
GTableData table component
GContainerPage container wrapper

Importing components

import { GLayoutEnergy } from "@numa/ui/shared/layout"; import GIcon from "@numa/ui/shared/ui/icon"; import { GText } from "@numa/ui/shared/ui/typography"; import { GButton } from "@numa/ui/shared/ui/button"; import { Toaster, toast } from "@numa/ui/shared/ui/sonner";

Layouts

LayoutRoute groupDescription
GLayoutEnergy(energy)/Sidebar + top bar for energy dashboard
GLayoutAuth(auth)/Auth pages shell
GContainerAllMain content container

GEnergyPage

A layout component for energy list screens providing consistent header, search, actions, and query loading/error/empty states:

<GEnergyPage title="Clients" searchPlaceholder="Search clients..." actions={<Button>Add Client</Button>} query={{ isLoading, error }} > {/* Page content */} </GEnergyPage>

Adding shadcn components

Use the gc script:

pnpm gc -- input

This runs npx shadcn@latest add input and moves the result into the nested folder structure (shared/ui/input/index.tsx).

For manual addition:

npx shadcn@latest add button -p components/shared/ui

Then move the file to shared/ui/button/index.tsx.

Sub-path exports

Every import path used by consumers must be explicitly listed in packages/ui/package.json exports:

{ "exports": { "./shared/layout/auth": "./shared/layout/auth/index.tsx", "./shared/ui/button": "./shared/ui/button/index.tsx" } }

If you add a new component, add its export path to package.json.

Theming

See the Theming page for how CSS variables and design tokens work.

Last updated on