# 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 1. **Warm, Not Overwhelming** - Warm neutrals (beige/paper tones) instead of cold grays - Soft shadows, never harsh - Muted semantic colors that inform without alarming 2. **Modern, Not Trendy** - Timeless color palette - Classic typography hierarchy - Subtle animations and transitions 3. **Connection-Focused** - Generous line height (1.6-1.65 for body text) - Optimized for reading and engagement - Clear hierarchy without relying on color 4. **Intentionally Smooth** - Animation durations: 300-400ms - Ease curves that feel natural - No jarring transitions --- ## Color Palette ### Background System ```dart 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 background - `surface`: App bars, bottom navigation - `surfaceElevated`: Cards, dialogs, elevated content - `surfaceVariant`: Input fields, disabled states ### Border System ```dart borderSubtle = #E8E6E1 // Barely visible dividers border = #D8D6D1 // Default borders borderStrong = #C8C6C1 // Emphasized borders ``` **Visual Hierarchy:** - Use `borderSubtle` for dividers between list items - Use `border` for cards, inputs, default separators - Use `borderStrong` for focused/active states (rare) ### Text Hierarchy ```dart 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 content - `textSecondary`: Metadata, labels, secondary info - `textTertiary`: Placeholders, timestamps, tertiary info - `textDisabled`: Disabled button text, inactive states - `textOnAccent`: White text on colored backgrounds ### Accent Colors ```dart accent = #5D6B7A // Muted slate (primary) accentLight = #8A95A1 // Lighter slate accentDark = #3F4A56 // Darker slate accentSubtle = #E8EAED // Barely visible accent ``` **Usage:** - `accent`: Primary buttons, links, active states - `accentLight`: Hover states, dark mode primary - `accentDark`: Pressed states (rare) - `accentSubtle`: Background for subtle accent areas ### Interaction Colors ```dart appreciate = #7A8B6F // Muted sage green (likes) save = #6F7F92 // Muted blue-gray (saves) share = #8B7A6F // Muted warm gray (shares) ``` ### Semantic Colors ```dart 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 ```dart tierNew = #9C9B99 // Light gray tierTrusted = #7A8B6F // Sage green tierEstablished = #5D6B7A // Slate blue ``` --- ## Typography ### Font Stack ```dart 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) ```dart 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) ```dart 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) ```dart 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) ```dart 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 `bodyLarge` for post content - Use `headlineMedium` for screen titles - Use `labelMedium` for metadata (timestamps, handles) - Use `mono` for 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**: ```dart 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 ```dart 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 ```dart 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: ```dart 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 cards - `shadowMd`: Elevated cards, modals - `shadowLg`: Floating action button, dialogs **Why So Subtle?** Heavy shadows create visual noise. Soft shadows suggest elevation without aggression. --- ## Animation ### Durations ```dart durationFast: 200ms // Hovers, subtle transitions durationMedium: 300ms // Default durationSlow: 400ms // Modal entrance, page transitions ``` ### Curves ```dart 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 button - `secondary`: Outlined button - `tertiary`: Text-only button - `destructive`: Filled error button **Sizes:** - `small`: 40px height - `medium`: 48px height - `large`: 56px height **Example:** ```dart SojornButton( label: 'Sign In', onPressed: _handleSignIn, variant: SojornButtonVariant.primary, size: SojornButtonSize.large, isFullWidth: true, isLoading: _isLoading, ) ``` ### SojornInput Replaces `TextField` with consistent styling **Example:** ```dart SojornInput( label: 'Email', hint: 'your@email.com', controller: _emailController, prefixIcon: Icons.email_outlined, keyboardType: TextInputType.emailAddress, ) ``` ### SojornTextArea For long-form content (posts, comments) **Example:** ```dart SojornTextArea( label: 'Write your post', hint: 'Share something thoughtful...', controller: _postController, maxLength: 500, showCharacterCount: true, ) ``` ### SojornCard Replaces `Card` with consistent elevation and borders **Example:** ```dart SojornCard( onTap: () => navigateToPost(), child: Column( children: [ Text('Card title'), Text('Card content'), ], ), ) ``` ### SojornDialog Replaces `showDialog` with friendly styling **Static Methods:** ```dart // 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:** ```dart 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 1. **No Hardcoded Colors** - All colors come from `AppTheme` - Impossible to use bright red/blue by accident 2. **No Arbitrary Spacing** - All spacing uses the 4px grid constants - Creates visual rhythm 3. **Generous Defaults** - Card padding: 24px (not 16px) - Line height: 1.65 (not 1.4) - Animation: 300ms (not 150ms) 4. **Soft Shadows** - Maximum 8% opacity - Warm black, never pure black 5. **Warm Tint** - All grays have warm undertones - Avoids clinical/sterile feel 6. **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 `AppTheme` constants (no hardcoded colors) - [ ] All spacing uses the 4px grid - [ ] All buttons use `SojornButton` variants - [ ] All inputs use `SojornInput` or `SojornTextArea` - [ ] 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: ```dart 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](https://type-scale.com/) - [Color Palette Generator](https://coolors.co/) - [Material Design 3 Guidelines](https://m3.material.io/) - [iOS Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/) --- **Last Updated:** 2026-01-06 **Maintained By:** Sojorn Design Team