Design System
Color
Primitive shades across 8 color families aliased into semantic Content, Background, and Border tokens.
Primitives — Color Families
Blue
Navy
Purple
Lilac
Black
Green
Red
Yellow
White
Semantics — Content
Semantics — Background
Semantics — Border
Typography
Three type scales — Headings, Text, and Caption — each with Desktop and Mobile responsive values shown side-by-side.
Headings
Aa
72px / 80px · -0.01em
Aa
56px / 64px · -0.01em
Aa
64px / 72px · -0.01em
Aa
48px / 56px · -0.01em
Aa
56px / 64px · -0.01em
Aa
40px / 48px · -0.01em
Aa
48px / 56px · -0.01em
Aa
32px / 40px · -0.01em
Aa
40px / 48px · -0.01em
Aa
28px / 36px · -0.01em
Aa
32px / 40px · -0.01em
Aa
24px / 32px · -0.01em
Aa
24px / 32px · -0.01em
Aa
20px / 28px · -0.01em
Aa
20px / 28px · -0.01em
Aa
16px / 24px · -0.01em
Aa
16px / 24px · -0.01em
Aa
12px / 18px · -0.01em
Aa
12px / 18px · -0.01em
Aa
12px / 18px · -0.01em
Text — Regular
Aa
20px / 32px · -0.01em
Aa
20px / 32px · -0.01em
Aa
16px / 24px · -0.01em
Aa
16px / 24px · -0.01em
Aa
14px / 20px · -0.01em
Aa
14px / 20px · -0.01em
Text — Bold
Aa
20px / 32px · -0.01em
Aa
20px / 32px · -0.01em
Aa
16px / 24px · -0.01em
Aa
16px / 24px · -0.01em
Aa
14px / 20px · -0.01em
Aa
14px / 20px · -0.01em
Caption
Aa
20px / 32px · 0em
Aa
20px / 32px · 0em
Aa
16px / 24px · 0em
Aa
16px / 24px · 0em
Aa
14px / 20px · 0em
Aa
14px / 20px · 0em
Aa
12px / 18px · 0em
Aa
12px / 18px · 0em
Layout & Spacing
8-point grid spacing scale (2XS → 10XL) paired with four responsive breakpoints.
Breakpoints
1440px
XL
1024px
L
768px
M
393px
S
Spacing Scale
Borders
Six corner radius values (XS 2 px → Pill 999 px) and three border widths.
Border Radius
circle
50%
pill
999px
l
12px
m
8px
s
4px
xs
2px
none
0px
Border Width
m
2px
s
1.5px
xs
1px
Icons
Three icon families served from /public/icons. Each preview is 48 × 48 px.
System Icons
38 iconsFormat
8 icons · sub-family of System IconsMenu Icons
78 iconsCategories Icons
21 iconsButtons
Buttons trigger an action such as submitting a form or showing/hiding an interface component. Use the appropriate variant to communicate hierarchy and importance.
Variants
Primary
Highest emphasisThe single most important action in a view or section. Use for final, flow-completing, or irreversible actions: Save, Confirm, Submit, Publish.
Secondary
Medium emphasisSupporting actions that pair with a primary action — alternatives, reversals, or non-destructive secondary paths: Cancel, Back, Export.
Tertiary
Low emphasisOptional or inline actions that do not compete with the main call-to-action — navigation hints, expandable sections, or supplemental links.
Tertiary Mono
Lowest emphasisSupplemental actions on neutral or non-brand surfaces where brand colour would feel out of place — dismissals, ancillary controls.
Interactive Playground
Props
variant"primary" | "secondary" | "tertiary" | "tertiaryMono""primary"size"L" | "S""L"labelstring"Button"loadingbooleanfalsedisabledbooleanfalseleadingIconReactNodeundefinedtrailingIconReactNodeundefinedCode Snippet
// Primary — main CTA
<Button variant="primary" label="Save changes" onClick={handleSave} />
// Loading state
<Button variant="primary" label="Saving…" loading />
// With leading icon
<Button variant="primary" label="Add item"
leadingIcon={<PlusIcon size={24} />} />Usage
Per-section limits
- Use at most one
primarybutton per section or container. - Pair
primarywithsecondaryortertiaryfor the dismiss action; never pair twoprimarybuttons. tertiaryandtertiaryMonomay appear multiple times, but group them logically and avoid visual clutter.
Do's and Don'ts
✓ Do
- Use primary for the single action that completes or advances the user's current goal.
- Write labels as verb + noun pairs: "Save changes", "Add item", "Export PDF".
- Show the loading state for async operations so users receive immediate feedback.
- Size icons consistently relative to button size: 20 px for L, 16 px for S.
✕ Don't
- Place more than one primary button in the same section or container.
- Use vague labels such as "Click here", "OK", or "Submit" without action-specific context.
- Use icons as the sole visible content — always pair icons with a visible label.
- Disable a button without providing a clear path to resolve the blocking condition.
Input Fields
Input fields let users enter and edit single-line text. A floating label appears inside the field whenever it is focused or has a value.
Design Tokens
Interactive Playground
Props
labelstringundefinedvaluestringundefineddefaultValuestringundefinedonChange(e: ChangeEvent) => voidundefinedplaceholderstringundefinedhintTextstringundefinedmaxLengthnumberundefinederrorMessagestringundefineddisabledbooleanfalseidstringautoCode Snippet
// Basic — label + placeholder + hint
<InputField
label="Email address"
placeholder="you@example.com"
hintText="We'll never share your email."
value={value} onChange={setValue}
/>
// Error state
<InputField
label="Password"
type="password"
errorMessage="Must be at least 8 characters."
/>Usage Rules
✓ Do
- Always provide a label — the floating label ensures the field purpose is visible even after the user starts typing.
- Use hintText for format hints (e.g. 'DD/MM/YYYY') or constraints visible before interaction.
- Use errorMessage for specific, actionable validation feedback.
- Pair maxLength with a counter so users know how much space remains.
✕ Don't
- Rely on placeholder alone as a label — it disappears on focus and fails accessibility.
- Use generic error messages like 'Invalid input' — be precise about what went wrong.
- Use this component for multi-line text — use a Textarea instead.
- Disable a field without providing context about why it is unavailable.
Chips
Chips are compact, non-interactive labels used to surface metadata, categories, or attributes alongside content. Use them to communicate what something is — a skill, a tag, a category — not to trigger an action.
Design Tokens
Interactive Playground
Props
labelstring—size"L" | "S""S"Code Snippet
// Default (S)
<Chip label="AI" />
// Large — hero section
<Chip label="Product Designer" size="L" />
// Inline group
<div style={{ display: "flex", gap: "8px", flexWrap: "wrap" }}>
<Chip label="UX" />
<Chip label="UI Design" />
</div>Usage Rules
✓ Do
- Use chips to label what something is — a skill, a tag, a category.
- Keep labels short (one to three words). Chips truncate poorly; prefer wrapping the group.
- Use size="L" in hero sections and size="S" in cards and compact layouts.
✕ Don't
- Use chips for interactive actions (filters, toggles, dismissal) — they have no interactivity.
- Mix L and S sizes in the same chip group.
- Use chips as navigation or as a substitute for buttons.
Containers
Container is a composable box primitive used as the foundation for cards, callouts, and content sections. It exposes two visual variants and a single size axis — padding — so surfaces compose consistently across the app. Corners are always square.
Variants
Two surface treatments. Use bordered for outlined callouts with no fill, and filled for quiet in-flow blocks.
Sizes
Padding · s · m · l
Interactive Playground
Props
childrenReactNode—variant"bordered" | "filled""bordered"padding"s" | "m" | "l""m"Code Snippet
// Bordered callout (default)
<Container>
content
</Container>
// Filled in-flow block, large padding
<Container variant="filled" padding="l" >
content
</Container>Usage Rules
✓ Do
- Use Container as the foundation for cards, callouts, stat tiles, and content boxes — anything that needs a consistent surface treatment.
- Pick variant by role: bordered for outlined callouts, filled for quiet in-flow blocks.
- Compose by nesting content directly; Container only owns background, border, and padding.
✕ Don't
- Override background or border via className or inline styles — pick a different variant instead.
- Use Container for interactive elements. It has no hover/focus/pressed states. Wrap a Button or link inside if interaction is needed.
- Mix padding scales within a single card group. Pick one padding value per group of sibling containers.
Page Layouts
Placeholder — content coming soon.
Principles
Two energy registers
Reactive UI and entrances use calm curves (ease-out, ease-in-out, Material standard). The loader uses high-energy curves (expo-out, expo-in, strong ease-in-out). The two never cross.
Speed scales with intent
Reactive UI (hover, focus, color change) is 100–200ms — fast enough to feel instant. Layout transitions are 200–350ms. Decorative entrances are 500ms. The loader’s phases run 1000–1200ms because they’re the showpiece, not infrastructure.
Page Loader
Three-phase choreography on first visit, followed by the nav sliding in. The nav reuses the loader's expo-out curve at the curtain's duration so the two moments visually link.
Element Entrance
Cards, hero text, list items, sidebar children. One pattern across the codebase.
500ms, ease-out
Stagger. When several elements enter together, each starts 50–100ms after the previous. Mobile menu uses 100ms (few items, more breathing room); sidebar children use 50ms (more items, snappier). Pick the step by item count.
Active Indicator
The sidebar's active-line marker tracks the current section as you scroll or click. Springs between positions instead of fading or sliding — physics gives it a reactive feel that property-based easing can't.
spring { stiffness: 350, damping: 30 }
Click between the items above to see the line spring between positions.