sojorn/sojorn_docs/design/DESIGN_SYSTEM.md

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

  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

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

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

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

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

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 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:

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 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

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 button
  • secondary: Outlined button
  • tertiary: Text-only button
  • destructive: Filled error button

Sizes:

  • small: 40px height
  • medium: 48px height
  • large: 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

  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:

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


Last Updated: 2026-01-06 Maintained By: Sojorn Design Team