WineBox Design System
A living reference for the visual and UX language of WineBox. Everything here uses the real style.css — if something looks wrong on this page, it's wrong in the app.
Colour
Brand palette and semantic aliases. Always use semantic aliases (--primary-color, --border-color) in component CSS — keep brand names reserved for the palette itself.
Brand palette
Semantic aliases
Gradients
Typography
Headings use Playfair Display (serif). Body and UI use DM Sans with a system fallback stack. Serif never appears in buttons, labels, or inputs.
Heading 1 · Playfair Display
Heading 2 · 1.75rem
Heading 3 · section label
Body paragraph · 1rem · DM Sans. A cellar is a curated collection — the copy should read like it was written by someone who cares about wine, not someone documenting a database.
Secondary text · 0.875rem · muted. Used for helper copy, timestamps, and supporting details.
Spacing
Based on a 0.25rem (4px) unit. Do not invent one-off values.
Border radius
Two tokens cover nearly every case: --radius for inputs & buttons, --radius-lg for cards & modals.
Elevation
Only two levels: default and hovered/raised. Plus a focus ring on inputs.
Buttons
Every button has the .btn base class plus one variant. Labels start with a verb and use sentence case.
Variants
Sizes
States
Forms
Every input has a visible label. Focus state is burgundy border + soft glow — never suppress it.
Image upload
Cards
White fill, 12px corners, soft shadow. Use .stat-card for numeric tiles — always pair a .stat-value with a .stat-label so no number appears naked.
Modals
Background scrim dims the page. Three widths: small (≤400px), default (≤800px), large (≤900px). Buttons stack full-width inside small modals.
Status colours
There is no dedicated alert component yet. When you need to indicate status, use these semantic tokens.
Alerts
Inline status messages. Paired with a semantic colour. Use for non-blocking feedback inside a page flow. Title is optional; body is always required.
Toasts
Transient feedback floating outside the page flow. Use after a background action finishes — not for form-level errors (those are alerts, §2.6). Errors are sticky by default; success/warning/info auto-dismiss after 4 seconds.
Trigger a toast
API
WineBox.toast.success("Bottle added");
WineBox.toast.error("Couldn't import - three rows missing a wine name.");
WineBox.toast.warning("Vintage looks unusual", { title: "Double-check" });
WineBox.toast.info("Tip - drag a photo onto the scan area");
// Full form
WineBox.toast.show("Custom message", {
variant: "success", // success | warning | error | info
title: "Optional",
duration: 6000, // ms; 0 = sticky (errors default to sticky)
dismissible: true // show X button (default true)
});
Rules
- Errors are sticky by default — never auto-dismiss something the user must act on.
- One short sentence. If you need more, link to a page or open a modal instead.
- Don't toast confirmation for things the user already sees in the UI.
- Don't stack more than three at once. If you would, you're using toasts for the wrong thing.
Empty states
Every list or dashboard panel has one. Title is a short noun phrase; description explains what belongs here; up to two actions fill the gap.
Badges
Small pill labels for categorical status. One badge per item — more and the list becomes noisy. Never use a badge as the only signal for a number.
Variants
In context
Icons
All UI icons share a 24-unit grid, stroked paths in currentColor, and one of five fixed sizes. Apply .icon plus an optional size modifier to any inline SVG.
Size scale
Inline with text
Stroke-only, current colour
Authoring rules
- Always
viewBox="0 0 24 24"· alwaysfill="none"with stroked paths. - Don't set inline
width,height, orstroke-width— the size class does it. - Decorative icon next to text →
aria-hidden="true". - Icon-only button →
aria-labelon the button.
Voice & tone
The reader is a wine enthusiast, not a developer. Use wine vocabulary, not system vocabulary.
Words to use vs. avoid
- cellar · bottle · case · label
- vintage · varietal · tasting note
- "Record a wine"
- "You have 42 bottles"
- "14.5% ABV"
- record · entry · document · batch
- row · field · object · endpoint
- "Submit form"
- "42 records found"
- "14.5" (naked number)
Button label rules
- Start with a verb:
Save changes,Record wine,Import cellar. - Never just
OK,Submit, orClick here. - Destructive actions are specific:
Delete bottle, notDelete.
Full guidelines
See DESIGN-SYSTEM.md §3 in the brand kit for the complete voice & tone reference, including empty states and accessibility baseline.