12 KiB
Sojorn Visual Design System
Design Philosophy
Sojorn's visual system creates a warm, welcoming, friends-first experience through intentional design choices that prioritize human connection.
Core Principles
-
Warm, Not Overwhelming
- Warm neutrals (beige/paper tones) instead of cold grays
- Soft shadows, never harsh
- Muted semantic colors that inform without alarming
-
Modern, Not Trendy
- Timeless color palette
- Classic typography hierarchy
- Subtle animations and transitions
-
Connection-Focused
- Generous line height (1.6-1.65 for body text)
- Optimized for reading and engagement
- Clear hierarchy without relying on color
-
Intentionally Smooth
- Animation durations: 300-400ms
- Ease curves that feel natural
- No jarring transitions
Color Palette
Background System
background = #F8F7F4 // Warm off-white (like paper)
surface = #FFFFFD // Barely warm white
surfaceElevated = #FFFFFF // Pure white for cards
surfaceVariant = #F0EFEB // Subtle warm gray (inputs)
Usage:
background: Main app backgroundsurface: App bars, bottom navigationsurfaceElevated: Cards, dialogs, elevated contentsurfaceVariant: Input fields, disabled states
Border System
borderSubtle = #E8E6E1 // Barely visible dividers
border = #D8D6D1 // Default borders
borderStrong = #C8C6C1 // Emphasized borders
Visual Hierarchy:
- Use
borderSubtlefor dividers between list items - Use
borderfor cards, inputs, default separators - Use
borderStrongfor focused/active states (rare)
Text Hierarchy
textPrimary = #1C1B1A // Near-black with warmth
textSecondary = #6B6A68 // Medium warm gray
textTertiary = #9C9B99 // Light warm gray
textDisabled = #BDBBB8 // Very light gray
textOnAccent = #FFFFFD // For buttons/accents
Usage:
textPrimary: Headlines, body text, primary contenttextSecondary: Metadata, labels, secondary infotextTertiary: Placeholders, timestamps, tertiary infotextDisabled: Disabled button text, inactive statestextOnAccent: White text on colored backgrounds
Accent Colors
accent = #5D6B7A // Muted slate (primary)
accentLight = #8A95A1 // Lighter slate
accentDark = #3F4A56 // Darker slate
accentSubtle = #E8EAED // Barely visible accent
Usage:
accent: Primary buttons, links, active statesaccentLight: Hover states, dark mode primaryaccentDark: Pressed states (rare)accentSubtle: Background for subtle accent areas
Interaction Colors
appreciate = #7A8B6F // Muted sage green (likes)
save = #6F7F92 // Muted blue-gray (saves)
share = #8B7A6F // Muted warm gray (shares)
Semantic Colors
success = #7A8B6F // Soft sage (not bright green)
warning = #B89F7D // Soft amber (not orange alarm)
error = #B07F7F // Soft terracotta (not red alarm)
info = #7A8B9F // Soft blue
Why Muted? Bright red errors create anxiety. Soft terracotta communicates the same information with less stress.
Trust Tier Colors
tierNew = #9C9B99 // Light gray
tierTrusted = #7A8B6F // Sage green
tierEstablished = #5D6B7A // Slate blue
Typography
Font Stack
Primary: 'SF Pro Text' → system-ui → sans-serif
Monospace: 'SF Mono' → 'Courier New' → monospace
Why System Fonts?
- Free, pre-installed, optimized for each platform
- SF Pro Text is warm and highly readable
- No network requests, instant loading
Type Scale
Display (Rare - Only for Large Headings)
displayLarge: 32px / 600 / 1.2 line-height / -0.8 tracking
displayMedium: 28px / 600 / 1.25 line-height / -0.6 tracking
Headlines (Section Titles, Screen Titles)
headlineLarge: 24px / 600 / 1.3 line-height / -0.4 tracking
headlineMedium: 20px / 600 / 1.3 line-height / -0.3 tracking
headlineSmall: 17px / 600 / 1.35 line-height / -0.2 tracking
Body (Reading-Optimized)
bodyLarge: 17px / 400 / 1.65 line-height ← GENEROUS for readability
bodyMedium: 15px / 400 / 1.6 line-height
bodySmall: 13px / 400 / 1.5 line-height
Why 1.65 line-height? Research shows 1.5-1.6 is optimal for readability. We use 1.65 to reinforce comfortable spacing.
Labels (UI Elements, Buttons, Metadata)
labelLarge: 15px / 500 / 1.4 line-height / 0.1 tracking
labelMedium: 13px / 500 / 1.35 line-height / 0.1 tracking
labelSmall: 11px / 500 / 1.3 line-height / 0.3 tracking
Typography Guidelines
DO:
- Use
bodyLargefor post content - Use
headlineMediumfor screen titles - Use
labelMediumfor metadata (timestamps, handles) - Use
monofor technical data (@handles, IDs)
DON'T:
- Mix font weights excessively (400 for body, 500 for labels, 600 for headings)
- Use font sizes outside the scale
- Override line-height without good reason
Spacing System
Based on 4px grid:
spacing2xs = 2px // Rare, internal component spacing
spacingXs = 4px // Tight spacing
spacingSm = 8px // Small gaps
spacingMd = 16px // Default spacing
spacingLg = 24px // Large gaps (card padding)
spacingXl = 32px // Extra large (section gaps)
spacing2xl = 48px // Huge gaps
spacing3xl = 64px // Screen-level spacing
spacing4xl = 96px // Rare, dramatic spacing
Semantic Spacing
spacingCardPadding = 24px // Internal card padding
spacingScreenPadding = 16px // Screen edge padding
spacingSectionGap = 32px // Between major sections
Spacing Guidelines
DO:
- Use
spacingLg(24px) for card padding - Use
spacingMd(16px) for screen padding - Use
spacingXl(32px) between sections
DON'T:
- Use arbitrary spacing values (stick to the scale)
- Create cramped UIs (when in doubt, use more space)
Border Radius
radiusXs = 4px // Chips, badges
radiusSm = 8px // Small buttons
radiusMd = 12px // Inputs, buttons
radiusLg = 16px // Cards, dialogs
radiusXl = 24px // Large containers
radiusFull = 9999px // Pills, circular elements
Consistency:
- Cards:
radiusLg(16px) - Buttons:
radiusMd(12px) - Inputs:
radiusMd(12px) - Trust badges:
radiusXs(4px)
Elevation & Shadows
All shadows use warm black (#1C1B1A) at very low opacity:
shadowSm: 4% opacity, 4px blur, 1px offset
shadowMd: 6% opacity, 8px blur, 2px offset
shadowLg: 8% opacity, 16px blur, 4px offset
Usage:
shadowSm: Default for cardsshadowMd: Elevated cards, modalsshadowLg: Floating action button, dialogs
Why So Subtle? Heavy shadows create visual noise. Soft shadows suggest elevation without aggression.
Animation
Durations
durationFast: 200ms // Hovers, subtle transitions
durationMedium: 300ms // Default
durationSlow: 400ms // Modal entrance, page transitions
Curves
curveDefault: easeInOutCubic // Most animations
curveEnter: easeOut // Elements appearing
curveExit: easeIn // Elements leaving
Why Slow? Fast animations feel rushed. 300-400ms feels intentional and smooth.
Custom Widgets
SojornButton
Replaces ElevatedButton, OutlinedButton, TextButton
Variants:
primary: Filled accent buttonsecondary: Outlined buttontertiary: Text-only buttondestructive: Filled error button
Sizes:
small: 40px heightmedium: 48px heightlarge: 56px height
Example:
SojornButton(
label: 'Sign In',
onPressed: _handleSignIn,
variant: SojornButtonVariant.primary,
size: SojornButtonSize.large,
isFullWidth: true,
isLoading: _isLoading,
)
SojornInput
Replaces TextField with consistent styling
Example:
SojornInput(
label: 'Email',
hint: 'your@email.com',
controller: _emailController,
prefixIcon: Icons.email_outlined,
keyboardType: TextInputType.emailAddress,
)
SojornTextArea
For long-form content (posts, comments)
Example:
SojornTextArea(
label: 'Write your post',
hint: 'Share something thoughtful...',
controller: _postController,
maxLength: 500,
showCharacterCount: true,
)
SojornCard
Replaces Card with consistent elevation and borders
Example:
SojornCard(
onTap: () => navigateToPost(),
child: Column(
children: [
Text('Card title'),
Text('Card content'),
],
),
)
SojornDialog
Replaces showDialog with friendly styling
Static Methods:
// Confirmation
SojornDialog.showConfirmation(
context: context,
title: 'Delete post?',
message: 'This cannot be undone.',
isDestructive: true,
)
// Info
SojornDialog.showInfo(
context: context,
title: 'Account created',
message: 'Welcome to Sojorn!',
)
// Error
SojornDialog.showError(
context: context,
title: 'Connection failed',
message: 'Please check your internet.',
)
SojornSnackbar
Replaces ScaffoldMessenger.showSnackBar
Static Methods:
SojornSnackbar.show(context: context, message: 'Post saved')
SojornSnackbar.showSuccess(context: context, message: 'Post published')
SojornSnackbar.showError(context: context, message: 'Failed to load')
SojornSnackbar.showWarning(context: context, message: 'Slow connection')
Visual Friendliness Enforcement
How the System Enforces Friendliness
-
No Hardcoded Colors
- All colors come from
AppTheme - Impossible to use bright red/blue by accident
- All colors come from
-
No Arbitrary Spacing
- All spacing uses the 4px grid constants
- Creates visual rhythm
-
Generous Defaults
- Card padding: 24px (not 16px)
- Line height: 1.65 (not 1.4)
- Animation: 300ms (not 150ms)
-
Soft Shadows
- Maximum 8% opacity
- Warm black, never pure black
-
Warm Tint
- All grays have warm undertones
- Avoids clinical/sterile feel
-
Limited Font Weights
- 400 (regular), 500 (medium), 600 (semibold)
- No bold (700) or black (900)
Before/After Comparison
Before (Material Default)
- Cold grays (#FAFAFA, #F5F5F5)
- Bright blue accent (#2196F3)
- Harsh shadows (24% opacity)
- Tight spacing (8px padding)
- Fast animations (100-150ms)
- Narrow line-height (1.4)
After (Sojorn System)
- Warm neutrals (#F8F7F4, #F0EFEB)
- Muted slate accent (#5D6B7A)
- Soft shadows (4-8% opacity)
- Generous spacing (24px padding)
- Slow animations (300-400ms)
- Reading line-height (1.65)
Result: Feels like reading a book, not using an app.
Testing Checklist
- All screens use
AppThemeconstants (no hardcoded colors) - All spacing uses the 4px grid
- All buttons use
SojornButtonvariants - All inputs use
SojornInputorSojornTextArea - All cards use
SojornCard - All dialogs use
SojornDialog - All snackbars use
SojornSnackbar - Text hierarchy follows the type scale
- Shadows use the predefined shadow helpers
- Border radius uses the radius constants
Dark Mode (Optional)
Dark theme uses the same warm philosophy:
darkBackground = #1C1B1A // Warm near-black
darkSurface = #252422 // Warm dark gray
darkSurfaceElevated = #2E2C2A // Lighter warm gray
darkTextPrimary = #ECEBE8 // Warm off-white
Key Difference:
- No pure black (#000000)
- No pure white (#FFFFFF)
- Reduced contrast for less eye strain
Resources
- Type Scale Calculator
- Color Palette Generator
- Material Design 3 Guidelines
- iOS Human Interface Guidelines
Last Updated: 2026-01-06 Maintained By: Sojorn Design Team