Design Principles
These principles guide how we build interfaces at Frequency. They apply to dashboards, tools, and product UI — not marketing pages.
Core Principles
1. Clarity Over Complexity
Every element should serve a purpose. If removing something doesn’t change the meaning, remove it. Whitespace is a design tool — use it to separate ideas and let content breathe.
2. Consistency Builds Trust
Use the same patterns, spacing, and components everywhere. Consistency reduces cognitive load and creates familiarity. If a card has 16px padding on one page, it has 16px padding on every page.
3. Accessible by Default
Design for everyone from the start. WCAG 2.1 AA is the floor, not the ceiling. Every interactive element needs visible focus states. Every color combination needs sufficient contrast. Accessibility is not a feature — it’s a quality bar.
4. Every Choice Must Be a Choice
For every decision, you should be able to explain why. Why this layout? Why this color? Why this spacing? If your answer is “it’s common” or “it works” — you haven’t chosen, you’ve defaulted. Defaults are invisible. Invisible choices compound into generic output.
5. Data Informs Design
Design hypotheses should be testable. Use real user behavior, not assumptions, to guide decisions. A number on screen is not design — the question is: what does this number mean to the person looking at it, and what will they do next?
Surface & Depth
Professional interfaces separate themselves from amateur ones in how they handle surfaces. Study Vercel, Linear, Supabase — their elevation changes are so subtle you almost can’t see them, but you feel the hierarchy.
Surface Elevation
Surfaces stack. A dropdown sits above a card which sits above the page. Build a numbered system:
| Level | Role | Example |
|---|---|---|
| 0 | Base canvas | App background |
| 1 | Cards, panels | Same visual plane as base |
| 2 | Floating UI | Dropdowns, popovers |
| 3 | Stacked overlays | Nested dropdowns |
| 4 | Highest elevation | Modals, command palettes |
In dark mode: higher elevation = slightly lighter. In light mode: higher elevation = slightly lighter or uses shadow.
The difference between levels should be whisper-quiet — a few percentage points of lightness, not dramatic jumps.
Depth Strategy
Choose one approach and commit across the product:
| Strategy | Feel | When to use |
|---|---|---|
| Borders only | Clean, technical, dense | Data-heavy tools, developer interfaces |
| Subtle shadows | Soft, approachable | General product UI |
| Layered shadows | Premium, dimensional | Cards that need physical presence |
Don’t mix strategies. A product with shadow cards and bordered tables feels inconsistent.
/* Borders-only */
border: 0.5px solid rgba(0, 0, 0, 0.08);
/* Subtle shadow */
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
/* Layered shadow */
box-shadow:
0 0 0 0.5px rgba(0, 0, 0, 0.05),
0 1px 2px rgba(0, 0, 0, 0.04),
0 2px 4px rgba(0, 0, 0, 0.03),
0 4px 8px rgba(0, 0, 0, 0.02);Borders
Borders should define regions without demanding attention. They should disappear when you’re not looking for them, but be findable when you need to understand structure.
- If borders are the first thing you notice, they’re too strong
- If you can’t tell where regions begin and end, they’re too subtle
- Use low opacity (0.05–0.12 alpha in dark mode, slightly higher in light)
Spacing System
Use an 8px base unit. Every spacing value is a multiple. Random values signal no system.
| Token | Value | Usage |
|---|---|---|
xs | 4px | Icon gaps, tight element pairs |
sm | 8px | Related inline elements |
md | 16px | Default component padding |
lg | 24px | Section spacing |
xl | 32px | Major sections |
2xl | 48px | Page-level spacing |
Padding Rules
Keep padding symmetrical. If top is 16px, sides and bottom are 16px too — unless there’s a clear structural reason.
/* Good */
padding: 16px;
padding: 12px 16px; /* Only when horizontal needs more room */
/* Bad — random values signal no system */
padding: 24px 16px 12px 16px;Typography Hierarchy
Type is not a container — it IS the design. The weight of a headline, the personality of a label, the texture of a paragraph shape how the product feels before anyone reads a word.
Four Levels of Text
Don’t just have “text” and “gray text.” Use all four consistently:
| Level | Color | Usage |
|---|---|---|
| Primary | #383838 | Default text, highest contrast |
| Secondary | #808080 | Supporting text, descriptions |
| Tertiary | gray[4] | Metadata, timestamps |
| Muted | #CCCCCC | Disabled, placeholder |
Type Pairing
| Role | Font | Weight | Tracking |
|---|---|---|---|
| Headlines | Montserrat | 600–700 | -1px to -2px |
| Body | Source Sans 3 | 400 | Normal |
| Labels/UI | Source Sans 3 | 600 | Normal |
| Data/Code | Monospace | 400 | Normal |
Headlines need weight and tight tracking to feel authoritative.
Body needs comfortable weight for sustained reading.
Data needs monospace with font-variant-numeric: tabular-nums for column alignment.
Color Usage
Gray builds structure. Color communicates. Unmotivated color is noise.
Color Primitives
Every color in the interface should trace back to these primitives:
| Primitive | Purpose | Frequency Token |
|---|---|---|
| Foreground | Text hierarchy (primary, secondary, muted) | #383838, #808080, #CCCCCC |
| Background | Surface elevation (base, elevated, overlay) | #FFFFFF, gray[1], #1A1A1A |
| Border | Separation (default, subtle, strong) | Low-opacity neutrals |
| Brand | Primary accent, identity | blue[5] #169BDE |
| Semantic | Status communication | red[5], yellow[5], green[5] |
Don’t invent new colors. Map everything to these primitives. No random hex values — everything traces to the system.
One Accent, Used with Intention
One accent color used purposefully beats five colors used without thought. Frequency Blue (#169BDE) is the primary accent. Violet (#7E57C2) is secondary. Use semantic colors only for their intended meaning.
Interaction States
Every interactive element needs states. Missing states feel broken.
Controls
| State | Visual Treatment |
|---|---|
| Default | Base appearance |
| Hover | Subtle background shift or border change |
| Active/Pressed | Slightly darker than hover |
| Focus | Visible ring (2px offset, brand color) |
| Disabled | Reduced opacity (0.5), no pointer events |
Data States
| State | Treatment |
|---|---|
| Loading | Skeleton placeholders, not spinners |
| Empty | Helpful message + clear action |
| Error | Red semantic color + recovery path |
Form Controls
Native <select> and <input type="date"> cannot be styled consistently. Build custom components using Mantine’s primitives instead. Every input needs: label above (not placeholder-as-label), visible error state, and helpful error messages.
Animation
Keep it fast and functional. Animation should guide attention, not perform.
| Type | Duration | Easing |
|---|---|---|
| Micro (hover, focus) | ~150ms | ease-out |
| Transitions (modals, panels) | 200–250ms | ease-out |
| Content entry | 300–500ms | ease-out |
- No spring or bounce effects in product UI — they feel playful, not professional
- Always respect
prefers-reduced-motion - If you can’t explain what the animation communicates, remove it
Dark Mode
Dark interfaces have different needs. Same hierarchy, different execution.
| Concern | Light Mode | Dark Mode |
|---|---|---|
| Elevation | Shadows or lighter surfaces | Lighter surfaces (shadows less visible) |
| Borders | Low opacity | Lean more on borders for definition |
| Semantic colors | Full saturation | Slightly desaturated |
| Text contrast | Dark on light | Light on dark, reduce pure white |
Z-Index Scale
| Layer | Value | Usage |
|---|---|---|
| Base | 0 | Default content |
| Dropdown | 10 | Menus, selects |
| Sticky | 20 | Sticky headers |
| Modal | 30 | Modal overlays |
| Popover | 40 | Tooltips, popovers |
| Toast | 50 | Notifications |
Responsive Design
Design mobile-first, then enhance for larger screens:
| Breakpoint | Width | Target |
|---|---|---|
sm | 640px | Large phones |
md | 768px | Tablets |
lg | 1024px | Laptops |
xl | 1280px | Desktops |
2xl | 1536px | Large screens |
The Craft Checklist
Before shipping any interface, run these checks:
- The squint test: Blur your eyes at the interface. Can you still perceive hierarchy? Is anything jumping out harshly? Craft whispers.
- The swap test: If you swapped the typeface or layout for a generic alternative, would anyone notice? Where it wouldn’t matter is where you defaulted.
- The state test: Does every interactive element have hover, focus, active, and disabled states? Does data have loading, empty, and error states?
- The token test: Read your CSS variables out loud. Do they trace back to the system? No random hex values?
- The consistency test: Is spacing, padding, border radius, and depth strategy uniform across the entire interface?