Design Language · v1.0

Fernwell UI System

Warm, rounded, and legible — built to feel approachable to a first-time consumer trial and sturdy enough for the team running their business through it every day.

Color

01

Marigold carries the energy — used sparingly, only for the action you most want taken. Plum grounds the interface and does the heavy lifting in navigation and headers. Ink and Cloud are doing 90% of the actual work on any given screen.

Ink
#1F2430
Cloud
#F5F6F1
Marigold
#FFC145
Plum
#4C3A73
Meadow
#3FA672
Coral
#E85B4F

Color — dark theme

01b

Not a straight inversion. Marigold stays exactly the same hex in both themes — it's the one constant that says "this is Fernwell" regardless of mode. Everything else shifts: neutrals move from warm-cream to near-black (never pure black, which flattens elevation), and the tinted backgrounds behind status colors switch from solid pastel fills to low-opacity washes, since a light pastel chip on a dark page reads as a UI bug, not a status. Toggle the button above to see it live.

Ink (bg)
#14161C
Surface
#1D2029
Cloud (text)
#F2F1EA
Marigold
#FFC145 · unchanged
Plum
#6B54A3
Meadow
#55D194
Coral
#FF8478
Sky
#6BB3E8
Why not pure black
#14161C keeps a trace of Plum's undertone rather than going neutral gray or true black — it's what makes the dark theme still feel like the same brand and not a generic "dark mode" reskin.
Why Marigold is fixed
It's light in both themes, so instead of changing it, we fixed the text that sits on it: buttons and avatars using Marigold as a background always use Ink-Fixed (#1F2430) for text, never the theme's flipping text color.
Why status colors brighten
Meadow, Coral, and Sky are all lifted 1–2 steps lighter in dark mode. The same hex that meets contrast on Cloud (#F5F6F1) falls short against near-black — darker backgrounds need lighter foreground colors to hold the same readability.

Typography

02

Plus Jakarta Sans for anything that should feel like a voice — headlines, empty states, onboarding. Inter for anything the user reads quickly and often — tables, forms, nav. Plex Mono for numbers, IDs, and anywhere precision matters more than warmth.

Display / 44Get paid on time
Display / 28Invoices sent this week
Body / 16Set a due date, add your terms, and we'll remind clients before it's late.
Body / 14Secondary text and helper copy sits here, always Ink-Soft, never pure gray.
Mono / 13INV-04821 · $1,240.00

Shape

03

One rounding system, four scales. The bigger the surface, the softer the corner — this is the single rule that ties every component together.

sm · 10px · inputs
md · 16px · buttons
lg · 24px · cards
xl · 32px · modals
full · avatars, pills

Buttons

04

Primary is Marigold and appears once per view. Secondary (Plum) is for the next-most-important action. Ghost for anything tertiary. Only one Primary button visible at a time — if two things feel equally important, neither is Primary.

Progress & loading

04b

Determinate bars for anything with a known length (uploads, multi-step forms). Indeterminate bars for anything with an unknown duration. Spinners live inline — in a button, next to a label, or centered in a panel — never as a full-page takeover unless the whole screen is genuinely blocked.

Uploading receipt.pdf64%
Import complete100%
Syncing with your bank
sm · inline text
md · cards, rows
lg · panel / page
in a button

Form fields

05

A required field gets a small coral asterisk after the label — coral is otherwise reserved for errors, but this specific mark is universally recognized and won't be misread in context. It always ships with hidden text for screen readers, since an asterisk alone isn't reliably announced. Disabled fields mute to the Cloud-dim surface and drop the border contrast, signalling "not available" rather than "empty."

We'll send the invoice here.
Enter an amount greater than $0.
Placeholder text — check contrast in dark mode.
Assigned automatically — can't be edited.

Status tags

06

A slight, consistent tilt on tags only — the one place the system allows itself to feel hand-placed. Everything else stays on-grid.

PaidPendingOverdueDraft

Alerts

07
Invoice sent to Bright Path Co.
They'll get a reminder automatically if it's not paid within 7 days.
That card was declined
Check the card details or try a different payment method.

Selection controls

05b

Native checkboxes and radios are visually hidden but stay in the DOM and fully keyboard/screen-reader operable — the styled box is a sibling element driven purely by CSS, not a replacement control. Marigold marks the "on" state everywhere for consistency.

Checkbox
Radio
Toggle

Form example

05c

All of the above, assembled into a real form — including a textarea and a typeahead autocomplete field. Try typing "s" or "m" into the City field.

New client

Used to localize invoice dates and currency.
Billing frequency

Assembled example

08

The tokens above, working together on a real screen.

Outstanding balance
$8,420
↓ 12% vs last month
✉️

No invoices sent yet

Create your first invoice and Fernwell will handle the reminders.

Motion

09
Buttons & controls
120ms ease, 1px lift on hover. Never bounce — playful lives in shape and color, not in springy easing.
Cards & modals entering
180ms, fade + 8px rise. No scale-up — it reads as cheap on data-dense screens.
Success states
A single checkmark draw-in (~300ms) is the one place a slightly more expressive animation is earned — invoice sent, payment received.
Reduced motion
All transforms drop to opacity-only fades when prefers-reduced-motion is set.

Voice

10
Buttons
Name the action, not the mechanism: "Send invoice," not "Submit."
Errors
Say what happened and what to do next. Never apologize, never blame the user: "That card was declined," not "Oops! Something went wrong."
Empty states
An invitation, not a description: "No invoices sent yet" + a direct next action, not "You have no data."
Confirmations
Match the verb on the button: "Send invoice" → "Invoice sent."

Fernwell UI System v1.0 — tokens defined as CSS custom properties at the top of this file. Copy them directly into your codebase.