CBN Coop
CBN CoopSeasonal Sales
Home
C
Coop Sakani
Design System · v1
Back to homeRebrand preview

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.

50
#effaf3
100
#dbf2e4
200
#bce2cc
300
#93cdad
400
#69b48d
500
#479d73
600
#30825c
700
#1d6746
800
#134e34
900
#0d3926
950
#052315
Canvas
semantic token
Surface
semantic token
Subtle
semantic token
Inverse
semantic token
FG default
semantic token
FG muted
semantic token
Brand
semantic token
Accent
semantic token
Success
semantic token
Warning
semantic token
Danger
semantic token
Info
semantic token

Typography

Geist, Sakani's typeface, now self-hosted through next/font.

Display · 30px / 600
₦4,820,500.00
Heading · 16px / 600
Pending orders this cycle
Body · 14px / 400
Members can browse items, place orders and track delivery through their branch representative.
Caption · 12px / 500
Updated 2 minutes ago · Cycle 14 · Lagos HQ

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.

768px
320360 mobile min1024 desktop max1440
text-display
Hero / landing headline
28.22px19→34
S
text-h1
Page titles
24.99px17→30
S
text-h2
Section headings
20.53px15→24
S
text-h3
Card / group headings
17.69px14→20
S
text-h4
Small sub-headings
14.84px13→16
S
text-body
Default body copy
14.84px13→16
S
text-caption
Helper text / metadata
11.84px10→13
S
text-chips
Chips, pills & tags
10.61px10→11
S
text-kicker
Uppercase eyebrow labels
10px10→10
S
Mobile compression & the small sizes

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.

Layer behavior

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.

No utility: base default

Section heading

Explicit text-sm wins

Section heading

UtilityUse forMobile → desktop
text-displayHero / landing headline19px → 34px
text-h1Page titles17px → 30px
text-h2Section headings15px → 24px
text-h3Card / group headings14px → 20px
text-h4Small sub-headings13px → 16px
text-bodyDefault body copy13px → 16px
text-captionHelper text / metadata10px → 13px
text-chipsChips, pills & tags10px → 11px
text-kickerUppercase eyebrow labels10px → 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.

768px
320360 mobile min1024 desktop max1440
p-fluid-2xs
Tight gaps & chips
5.46px3→7
p-fluid-xs
Compact paddings
7.46px5→9
p-fluid-sm
Small gaps
9.46px7→11
p-fluid-md
Card padding
14.92px10→18
p-fluid-lg
Section gaps
21.37px14→26
p-fluid-xl
Hero padding
27.45px17→34
p-fluid
Page gutters
18.6px10→24
Why the fluid- prefix

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.

Chips, pills & tags: the small-size pairing

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.

TokenUtility (p- / gap- / m-…)Use forMobile → desktop
--sp-2xsp-fluid-2xsTight gaps & chips3px → 7px
--sp-xsp-fluid-xsCompact paddings5px → 9px
--sp-smp-fluid-smSmall gaps7px → 11px
--sp-mdp-fluid-mdCard padding10px → 18px
--sp-lgp-fluid-lgSection gaps14px → 26px
--sp-xlp-fluid-xlHero padding17px → 34px
--sp-fluidp-fluidPage gutters10px → 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.

768px
320360 mobile min1024 desktop max1440
rounded-fluid-xs
Tight chips & tags
4.84px3→6
rounded-fluid-sm
Small buttons
6.84px5→8
rounded-fluid-md
Inputs & controls
8.84px7→10
rounded-fluid-lg
Cards & panels
13.69px10→16
rounded-fluid-xl
Large surfaces
17.3px13→20
rounded-fluid-2xl
Modals & hero cards
27.37px20→32
rounded-fluid-3xl
Showcase & feature cards
30.99px23→36
How the scale reaches the app

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.

UtilityUse forMobile → desktop
rounded-fluid-xsTight chips & tags3px → 6px
rounded-fluid-smSmall buttons5px → 8px
rounded-fluid-mdInputs & controls7px → 10px
rounded-fluid-lgCards & panels10px → 16px
rounded-fluid-xlLarge surfaces13px → 20px
rounded-fluid-2xlModals & hero cards20px → 32px
rounded-fluid-3xlShowcase & feature cards23px → 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.

768px
320360 mobile min1024 desktop max1440
shadow-fluid-xs
blur 2px
Subtle inset separation
shadow-fluid-sm
blur 4.84px
Hover & small cards
shadow-fluid-md
blur 12.92px
Default cards
shadow-fluid-lg
blur 23.37px
Popovers & dropdowns
shadow-fluid-xl
blur 33.83px
Floating actions
shadow-fluid-2xl
blur 62.75px
Modals & dialogs
Why shadows grow with the viewport

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.

Buttons

8px radius, weight 500, chromatic focus ring, 220ms motion.

Export buttons

The app-wide Excel/PDF export pair — accent pill + download icon for Excel, danger pill + file icon for PDF. One component, one import, used by every admin and rep page. Busy shows a spinner with an optional busyText; disabled dims to 50% opacity.

Idle
Busy (busyText)
Disabled
Canonical usage
import ExportButton, { ExportButtons } from '@/components/ui/ExportButton'

{/* Single button */}
<ExportButton format="excel" onClick={exportExcel} disabled={!orders.length} />
<ExportButton format="pdf" onClick={exportPDF} busy={exportingPdf} busyText="Preparing…" />

{/* The pair */}
<ExportButtons onExcel={exportExcel} onPdf={exportPDF} excelDisabled pdfBusy />

Badges

Borderless pills for statuses: subtle and solid variants.

NeutralBrandDeliveredPendingPostedCancelledSolid accentSolid successSolid danger

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
  1. Pending
  2. Approved
  3. Delivered
Approved
  1. Pending
  2. Approved
  3. Delivered
Delivered
  1. Pending
  2. Approved
  3. Delivered
Cancelled

This order was cancelled.

Module presets
Exhibition · Pending → Approved → Delivered
  1. Pending
  2. Approved
  3. Delivered
Ram · Pending → Approved → Delivered
  1. Pending
  2. Approved
  3. Delivered
Food · Pending → Posted → Delivered
  1. Pending
  2. Posted
  3. Delivered
Canonical usage
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.

Days out
closes in 12 days
With cycle name (prefix)
Food Cycle 2026 · closes in 12 days
Closes tomorrow
closes tomorrow
Closes today
closes today
Bare variant (inside a pill)
closes in 12 dayscloses in 12 days
Canonical usage
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.

Total orders
₦4.8M
+12.5%
Pending
342
+3.1%
Delivered
2,180
-1.2%
Low stock
7
2 alerts
Loading
Loading
Loading
Loading

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.

Lagos HQ
Abuja
Ibadan
PH
PendingPostedDelivered

Line chart: cycle value trend

Smooth area line, hover crosshair and value tooltip.

MonTueWedThuFriSatSun

Chart tokens

--chart-1…5 flip between light and dark; series colors can be any semantic token.

chart-1chart-2chart-3chart-4chart-5

Empty states

Pass empty to render a muted panel instead of a broken axis.

No data to chart yet

Cards

Surface cards with header, description and actions.

Branch summary

Orders consolidated by delivery branch.

Lagos HQ1,204
Abuja876
Ibadan542
This cycle

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.

Showing the pending tab content area.

Data table

Subtle borders, hover rows, status badges.

OrderMemberBranchAmountStatus
ORD-2481Adeyemi O.Lagos HQ₦84,500Pending
ORD-2482Okafor C.Abuja₦212,000Posted
ORD-2483Bello M.Ibadan₦46,750Delivered
ORD-2484Eze N.Port Harcourt₦129,300Cancelled

Modal · Empty state · Skeleton · Toasts

No pending orders

There are no orders waiting for approval in this cycle. New member orders will appear here.

Coop Sakani v1 · light/dark tokens · Geist · Lucide · tokens in globals.css