The Coop Sakani design system
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.
Brand palette
The Harvest-green scale: warm stone neutrals, forest green brand, gold secondary accent.
Typography
Geist, Sakani's typeface, now self-hosted through next/font.
Fluid type scale
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.
Section heading
Section heading
| 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 |
Fluid spacing scale
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 |
Fluid radii scale
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 |
Fluid shadows scale
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.
Badges
Borderless pills for statuses: subtle and solid variants.
Order progress
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.
- Pending
- Approved
- Delivered
- Pending
- Approved
- Delivered
- Pending
- Approved
- Delivered
This order was cancelled.
- Pending
- Approved
- Delivered
- Pending
- Approved
- Delivered
- Pending
- Posted
- Delivered
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} />Cycle countdown
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" />Stat cards
The KPI building blocks for the admin dashboard.
Charts
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.
Bar chart: orders by branch
Stacked series, animated grow-in, hover tooltip with totals.
Line chart: cycle value trend
Smooth area line, hover crosshair and value tooltip.
Chart tokens
--chart-1…5 flip between light and dark; series colors can be any semantic token.
Empty states
Pass empty to render a muted panel instead of a broken axis.
Cards
Surface cards with header, description and actions.
Branch summary
Orders consolidated by delivery branch.
Interactive card
Hover raises the border and shadow, like Sakani's interactive surface.
Featured surface
Accent-subtle background for emphasis cards.
Used for featured stats, alerts and highlighted panels without breaking the token system.
Form controls
Inputs, selects and textareas: brand focus ring in both themes.
Tabs
Brand underline for the active tab.
Data table
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 |
Modal · Empty state · Skeleton · Toasts
No pending orders
There are no orders waiting for approval in this cycle. New member orders will appear here.
