# Design System

The visual language of the Pelagia Purchase Management System — an internal PO tool for a maritime company. Everything here is lifted from the live codebase (`app/globals.css` + `components/ui/*`), so screens built from these tokens drop straight in.

Framework

Next.js 15 · App Router

Styling

Tailwind CSS v4

Primitives

Radix UI · CVA

Charts · Icons

recharts · lucide

Tip: click any `code snippet` to copy it.

## Color

Five ramps. Primary = actions, links, active nav. Success = approved / paid. Warning = needs action (edits, vendor ID, partial). Danger = rejected / destructive. Neutral = all structure, text and borders.

## Typography

Inter for UI, JetBrains Mono for codes & numbers (PO numbers, accounting codes).

Page title

`text-2xl font-semibold`

Section heading

`text-lg font-semibold`

Card title

`text-base font-semibold`

Body / table text — the workhorse size for the whole app.

`text-sm`

Muted description & helper text

`text-sm text-neutral-500`

Eyebrow / label

`text-xs uppercase tracking-wider`

PMS/HNR1/9000/2024-25

`font-mono`

## Radius · Shadow · Spacing

Radius

`rounded-md`
`rounded-lg`
`rounded-full`

lg for cards/buttons/inputs, md for nav items, full for badges & avatars.

Elevation

`border`
`shadow-sm`
`shadow-lg`

Flat by default — a 1px border-neutral-200 separates surfaces. shadow-sm on cards, shadow-lg only for popovers/dialogs.

Spacing rhythm

`gap-2 · 8px`
`gap-3 · 12px (rows)`
`p-5 · 20px (cards)`
`p-6 / gap-6 · 24px`

Page padding p-6; cards p-5/p-6; controls gap gap-2/3.

## Icons

lucide-react, stroke 2, sized `h-4 w-4` inline / `h-5 w-5` standalone. They inherit text color (`currentColor`).

## App shell

Fixed w-60 sidebar (white, `border-r`) + h-16 top bar + scrollable `main` on `bg-neutral-50` with p-6. Nav links: `rounded-md px-3 py-2 text-sm font-medium`; active = `bg-primary-50 text-primary-700`; section eyebrow = `text-xs uppercase tracking-wider text-neutral-400`.

PPMS
Dashboard

## Page header

Title + muted subtitle on the left, primary action on the right.

# Purchase Orders

Create, track and approve orders across the fleet.

    <div class="flex items-start justify-between gap-4">
      <div>
        <h1 class="text-2xl font-semibold text-neutral-900">Title</h1>
        <p class="mt-1 text-sm text-neutral-500">Subtitle</p>
      </div>
      <Button>New PO</Button>
    </div>copied ✓

## Buttons

From `components/ui/button.tsx` (CVA). Base: `inline-flex items-center gap-2 rounded-lg text-sm font-medium` + focus ring.

    <Button variant="default|secondary|success|warning|destructive|ghost|link" size="sm|md|lg|icon">copied ✓

## Badges

From `components/ui/badge.tsx`. Base: `rounded-full px-2.5 py-0.5 text-xs font-medium`.

default secondary success warning danger outline

## PO status badges

The 13 lifecycle states map to badge variants in `lib/utils.ts` (`PO_STATUS_LABELS` / `PO_STATUS_VARIANTS`). Reuse this mapping — don't invent new status colors.

## Cards & KPI stats

Card = `rounded-lg border border-neutral-200 bg-white shadow-sm`. KPI stat cards drop the shadow and use p-4/5.

### Vendor details

GST-verified supplier on file.

Acme Marine Supplies Pvt Ltd

GSTIN 27ABCDE1234F1Z5

Open POs

24

▲ 8% vs last month

Awaiting approval

7

across 3 vessels

Spend (FY)

₹2.4 Cr

FY 2025–26

Overdue

2

needs attention

## Forms

Inputs from `components/ui/input.tsx`: `h-10 rounded-lg border-neutral-300`, focus `border-primary-500 ring-2 ring-primary-500/20`.

PO title
Cost centre
Date required
Notes
Disabled

Inline validation error sits under the field in `text-danger-700`.

## Tabs & segmented controls

Toolbar filters use a pill segmented control; page sections use an underline tab bar.

## Tables

Header row `bg-neutral-50` with `text-xs uppercase tracking-wider text-neutral-400`; body rows `divide-y divide-neutral-100`, hover `hover:bg-neutral-50`; numbers `tabular-nums text-right`.

PO Number | Vessel | Status | Amount
--- | --- | --- | ---
PMS/HNR1/9001/2025-26 | MV Pelagia Star | Approved | ₹4,82,000
PMS/OCD2/9002/2025-26 | MV Ocean Dawn | Edits Requested | ₹1,15,500
PMS/CRT3/9003/2025-26 | MV Coral Trident | Closed | ₹92,300

## Alerts & dialog

Inline callouts use the semantic -50 tint + -700 text + matching border.

ℹ InfoImported POs are created directly in the Closed state.
✓ SuccessPurchase order approved and sent for payment.
⚠ WarningThis vendor is unverified — verify before assigning a vendor code.
✕ ErrorPayment date cannot be in the future.

Dialog

### Delete accounting code?

This removes 5110 · Fuel & Lubricants. This action can't be undone.

## Charts

recharts inside a standard card. Grid `strokeDasharray="3 3"` in `#f0f0f0`; axis ticks `fontSize 11, #737373`; bars `radius`, lines `strokeWidth 2`. Y-axis money formatted via `formatCompactINR`.

Spend by cost centre

Monthly trend

Multi-series palette

## Formatting conventions

Currency | Indian locale, INR — `formatCurrency()` → ₹4,82,000
Compact money | `formatCompactINR()` → ₹2 Cr · ₹49 L · ₹75 K (lakh/crore scale)
Dates | `formatDate()` → Jun 22, 2026 · financial year is Apr–Mar (`2025-26`)
PO numbers | Mono font, `COMPANY/VESSEL/ID/FY` → PMS/HNR1/9000/2024-25
Accounting codes | 6-digit mono codes; graphs label headings/sub-headings by name, leaves by code

## Do & don't

Do

- • Separate surfaces with a 1px `border-neutral-200`, not heavy shadows.
- • Reuse `PO_STATUS_VARIANTS` for any PO status pill.
- • Keep one primary action per view; everything else secondary/ghost.
- • Right-align and `tabular-nums` all money columns.
- • Use semantic color only for meaning (success=paid, warning=action, danger=stop).

Don't

- • Don't introduce new hues — stay within the five ramps.
- • Don't use primary blue for decoration; it signals an action.
- • Don't mix radii — `rounded-lg` for boxes, `rounded-full` for pills.
- • Don't crowd charts — one chart per card, generous whitespace.
- • Don't hardcode ₹ formatting — use the shared helpers.