UI & Design Standards
Aesthetics are everything. Visual design, polish, and attention to detail are critical. Every pixel matters, every interaction should feel intentional, and every screen should look like it was designed by someone who cares.
This philosophy guides every UI decision at Frequency. These standards ensure visual consistency, accessibility, and maintainability across all our applications.
The Component Hierarchy
When building UI, always follow this priority order:
@frequencyads/components
Check the component catalog first. If Hero, AudioPlayer, CodeBlock, PrincipleCard, DosDonts, or any other shared component exists, use it. These components are already themed, accessible, and tested.
Mantine v8 Primitives
Paper, Card, SimpleGrid, Table, Group, Stack, Text, Title, Button, ActionIcon, Tabs, Accordion — Mantine v8 has 100+ components. Use them for everything else. Never raw HTML with Tailwind.
Custom Components
Only when neither the component library nor Mantine has what you need. Use CSS Modules for styling. Mark with a // TODO: Extract to @frequencyads/components comment if it could be reusable.
Styling Rules
What to Use
| Approach | When | Example |
|---|---|---|
| Mantine style props | One-off spacing, colors, sizing | <Text c="dimmed" mb="lg"> |
| CSS Modules | Component-specific styles | classes.header |
| Theme tokens | Colors, spacing, typography | var(--mantine-color-blue-6) |
What to Avoid
| Approach | Why Not |
|---|---|
Inline style objects | Hard to maintain, no hover/responsive support |
| Tailwind classes | Installed but reserved for explicit human requests only |
| Hardcoded colors | Breaks theming and dark mode |
| CSS-in-JS runtime | Performance overhead, hydration issues |
Color Rules
This is critical — hardcoded colors break dark mode and theming.
// ❌ NEVER hardcode colors
<Box bg="white">
<Text color="#000000">
<Card style={{ backgroundColor: '#f5f5f5' }}>
// ✅ ALWAYS use theme-aware values
<Box bg="var(--mantine-color-body)">
<Text c="dimmed">
<Card bg="var(--mantine-color-default)">
// ✅ Use Mantine color tokens
<Text c="blue.6">
<Badge color="green">
<ThemeIcon color="violet.4">Available Brand Colors
| Token | Hex | Usage |
|---|---|---|
blue.6 | #169BDE | Primary actions, links |
violet.6 | #7E57C2 | Secondary accents |
red.6 | #E63459 | Errors, destructive actions |
green.6 | #5AB267 | Success, confirmation |
yellow.6 | #E79E26 | Warnings, attention |
cyan.6 | #15C5DE | Information, highlights |
See @frequencyads/brand for the full color palette and theme configuration.
Typography
Font Families
| Use | Font | Weight Range |
|---|---|---|
| Headings | Montserrat | 400–700 |
| Body text | Source Sans 3 | 400–600 |
| Code | JetBrains Mono / system mono | 400 |
Typography Scale
| Element | Size | Weight | Letter Spacing |
|---|---|---|---|
| Display | 60px | 700 | -2px |
| H1 | 48px | 700 | -2px |
| H2 | 36px | 700 | -1px |
| H3 | 24px | 600 | -0.5px |
| Body | 16px | 400 | normal |
| Small | 14px | 400 | normal |
| Label | 12px | 600 | 0.3em (uppercase) |
Loading Fonts
// In layout.tsx — fonts loaded via Google Fonts URL
import { googleFontsUrl } from '@frequencyads/brand/typography';
// In <head>
<link rel="stylesheet" href={googleFontsUrl} />Theme Setup
Every app must wrap its content in a themed provider:
import { MantineProvider } from '@mantine/core';
import { frequencyTheme } from '@frequencyads/brand/mantine';
function App({ children }) {
return (
<MantineProvider theme={frequencyTheme} defaultColorScheme="auto">
{children}
</MantineProvider>
);
}Dark Mode
All Frequency apps should support dark mode by default:
- Never assume light mode — use theme-aware colors
- Test both themes — every component should look intentional in both modes
- Use
defaultColorScheme="auto"— respects user’s system preference - Dark backgrounds follow this palette:
#000000(body),#121212(sections),#181818(cards),#242424(elevated surfaces)
Accessibility (WCAG 2.1 AA)
This is the floor, not the ceiling:
- Color contrast — minimum 4.5:1 for normal text, 3:1 for large text
- Focus indicators — every interactive element must have a visible focus state
- Keyboard navigation — all functionality accessible via keyboard
- Screen readers — meaningful alt text, ARIA labels where needed
- Reduced motion — respect
prefers-reduced-motionfor animations
Icons
// ✅ Use Tabler icons — lightweight outline SVGs
import { IconSettings, IconUser } from '@tabler/icons-react';
<IconSettings size={20} stroke={1.5} />
// ❌ Never use emojis in UI (unless explicitly requested)
// ❌ Never use filled/heavy icon stylesInteraction Patterns
Hover Effects
- Subtle
translateY(-4px)lift for cards - Border color lightens from
rgba(255,255,255,0.05)torgba(255,255,255,0.15) - Icon scales up slightly (
scale(1.1))
Transitions
- Default:
200ms easefor color changes - Cards:
300ms easefor transforms - Scroll animations:
700ms ease-outvia FadeInSection
Spacing
- Section padding:
120pxvertical - Card padding:
24px(mobile) to32px(desktop) - Grid gaps:
16px(tight),24px(default),32px(spacious)
Anti-Patterns
| Don’t | Do Instead |
|---|---|
| Build a custom modal | Use @mantine/core Modal |
| Create a custom dropdown | Use @mantine/core Select or Menu |
Style with className="text-blue-500" | Use c="blue.5" prop |
Add !important to component CSS | Fix specificity or use CSS Modules |
Use <div> for layout | Use Mantine’s Stack, Group, SimpleGrid, Flex |
| Create a custom tooltip | Use @mantine/core Tooltip |