Warm stone neutrals, a Harvest-green brand with a gold accent, Geist typography and a token-driven component kit. Light and dark mode from a single source of truth. Toggle the theme in the header.
The Harvest-green scale: warm stone neutrals, forest green brand, gold secondary accent.
Geist, Sakani's typeface, now self-hosted through next/font.
The responsive baseline. Every size is a clamp() that interpolates between a mobile value (at 360px) and a desktop value (at 1024px). The mobile anchors were compressed by ~3px so headings, body and chips read at phone-friendly sizes; desktop values are unchanged. Drag the slider to see the scale breathe.
Every heading tier sits roughly 3px smaller on mobile than the original static sizes (18px → 15px titles, 16px → 13px card headings, and so on). The phones-first pass applied across the app. Desktop values at 1024px+ match the original design exactly, so wide screens are unaffected.
For small UI text, prefer the two dedicated tiers instead of guessing: text-caption (10→13px) for helper text and metadata, and text-chips (10→11px) for chips, pills, tags and badges. Tighter than body copy, still legible on phones.
Real <h1>–<h4> elements pick up these sizes automatically from the @layer base rule. Any explicit size utility (text-xl, text-h1) always wins. Layer base never overrides it.
| Utility | Use for | Mobile → desktop |
|---|---|---|
| text-display | Hero / landing headline | 19px → 34px |
| text-h1 | Page titles | 17px → 30px |
| text-h2 | Section headings | 15px → 24px |
| text-h3 | Card / group headings | 14px → 20px |
| text-h4 | Small sub-headings | 13px → 16px |
| text-body | Default body copy | 13px → 16px |
| text-caption | Helper text / metadata | 10px → 13px |
| text-chips | Chips, pills & tags | 10px → 11px |
| text-kicker | Uppercase eyebrow labels | 10px → 10px |
Companion to the type scale. Every gap and padding clamp()s between a mobile value (at 360px) and a desktop value (at 1024px). The bars below render each token at its exact computed size for the current slider position.
The tokens are mapped as --spacing-fluid-* on purpose: a bare --spacing-md would collide with Tailwind's built-in max-w-* container scale and silently break every max-width in the app. Each token generates the full spacing family: p-fluid-lg, gap-fluid-lg, mt-fluid-lg, px-fluid-2xs, all sharing the same clamp.
Pair the two tightest spacing steps with the chip type tier for small UI text: sp-2xs (3→7px) for vertical padding and sp-xs (5→9px) for horizontal padding, with text-chips (10→11px) for the label. A typical pill is inline-flex items-center gap-1 rounded-full px-fluid-xs py-fluid-2xs text-chips font-semibold. It scales down on phones and breathes on desktop without re-tuning.
| Token | Utility (p- / gap- / m-…) | Use for | Mobile → desktop |
|---|---|---|---|
| --sp-2xs | p-fluid-2xs | Tight gaps & chips | 3px → 7px |
| --sp-xs | p-fluid-xs | Compact paddings | 5px → 9px |
| --sp-sm | p-fluid-sm | Small gaps | 7px → 11px |
| --sp-md | p-fluid-md | Card padding | 10px → 18px |
| --sp-lg | p-fluid-lg | Section gaps | 14px → 26px |
| --sp-xl | p-fluid-xl | Hero padding | 17px → 34px |
| --sp-fluid | p-fluid | Page gutters | 10px → 24px |
Clamp-based border-radius. The same 360→1024px band as type and spacing. Mobile anchors match the original static values, so phones are untouched; corners breathe a little larger on wide screens. Every rounded-* utility in the app now shares these clamps.
The --radius-* tokens in :root are clamps, and the standard rounded-* utilities are generated from them, so buttons, inputs and cards inherit the fluid scale with no per-component changes. rounded-fluid-* is the explicit opt-in form. rounded-full stays a perfect pill at every width.
As with the type and spacing scales, the bars above show the intended linear interpolation across the band. The live clamps resolve to their desktop anchor once the preferred size passes the max, which is how every clamp() in this system behaves.
| Utility | Use for | Mobile → desktop |
|---|---|---|
| rounded-fluid-xs | Tight chips & tags | 3px → 6px |
| rounded-fluid-sm | Small buttons | 5px → 8px |
| rounded-fluid-md | Inputs & controls | 7px → 10px |
| rounded-fluid-lg | Cards & panels | 10px → 16px |
| rounded-fluid-xl | Large surfaces | 13px → 20px |
| rounded-fluid-2xl | Modals & hero cards | 20px → 32px |
| rounded-fluid-3xl | Showcase & feature cards | 23px → 36px |
Softer, more lifted shadows at larger viewports. Blur and spread grow across the same 360→1024px band, while phones keep today's crisp, flat depth. The demo cards render the exact interpolated shadow for the current slider position.
Small screens read depth best with tight, flat shadows. The mobile anchors match the original static values. On wide screens, larger blurs and offsets read as soft elevation instead of hard outlines. The standard shadow-* utilities use the same clamps, so cards, modals and toasts soften automatically at desktop widths. shadow-fluid-* is the explicit opt-in form.
The demo cards interpolate the blur/offset linearly across the band, like the type and spacing sections. Real clamps cap at their desktop anchors, so nothing ever exceeds the values shown here.
Borderless pills for statuses: subtle and solid variants.
The shared order stepper. One generic component (ui/OrderStepper) renders any module's journey from its status metadata; the presets below are what pages import — Exhibition and Ram use Approved, Food uses Posted. Pending marks the current step in brand, completed steps in success, and Cancelled collapses to the danger chip.
This order was cancelled.
import { ExhibitionOrderStepper, RamOrderStepper, FoodOrderStepper } from '@/components/ui/OrderStepper'
{/* Pages just pass the order status — the preset binds the module's steps */}
<ExhibitionOrderStepper status={order.status} />
<RamOrderStepper status={order.status} />
<FoodOrderStepper status={order.status} />
{/* The generic accepts any meta + steps (kit escape hatch) */}
<OrderStepper status={status} meta={RAM_STATUS_META} steps={APPROVE_STEPS} />The shared season closing-date chip (ui/CycleCountdown). One component renders the identical 'closes in N days' treatment on the My Coop module cards, the rep access cards and the admin dashboard module switcher — the exact close date lives in the hover tooltip. prefix adds the cycle name; variant="bare" drops the pill so it can sit inside tabs and other pills.
import CycleCountdown from '@/components/ui/CycleCountdown'
{/* My Coop module cards — Food passes its own tooltip noun */}
<CycleCountdown endsAt={foodCycle.ends_at} noun="Cycle" />
<CycleCountdown endsAt={ramCycle.ends_at} />
<CycleCountdown endsAt={exhibitionCycle.ends_at} />
{/* Rep access cards — cycle name shown before the countdown */}
<CycleCountdown endsAt={cycle.ends_at} prefix={cycle.name} />
{/* Admin dashboard switcher — transparent, inherits the tab pill */}
<CycleCountdown endsAt={activeCycle.ends_at} variant="bare" />The KPI building blocks for the admin dashboard.
Theme-aware bar and line charts built on the --chart-* tokens. They flip automatically between light and dark. Stacked bars take { label, [seriesKey]: value } rows; the line chart takes { label, value } rows. Hover for tooltips; empty states fall back to a muted panel.
Stacked series, animated grow-in, hover tooltip with totals.
Smooth area line, hover crosshair and value tooltip.
--chart-1…5 flip between light and dark; series colors can be any semantic token.
Pass empty to render a muted panel instead of a broken axis.
Surface cards with header, description and actions.
Orders consolidated by delivery branch.
Hover raises the border and shadow, like Sakani's interactive surface.
Accent-subtle background for emphasis cards.
Used for featured stats, alerts and highlighted panels without breaking the token system.
Inputs, selects and textareas: brand focus ring in both themes.
Brand underline for the active tab.
Subtle borders, hover rows, status badges.
| Order | Member | Branch | Amount | Status |
|---|---|---|---|---|
| ORD-2481 | Adeyemi O. | Lagos HQ | ₦84,500 | Pending |
| ORD-2482 | Okafor C. | Abuja | ₦212,000 | Posted |
| ORD-2483 | Bello M. | Ibadan | ₦46,750 | Delivered |
| ORD-2484 | Eze N. | Port Harcourt | ₦129,300 | Cancelled |
There are no orders waiting for approval in this cycle. New member orders will appear here.