# Sojorn Calm UX Guide ## Design Philosophy Sojorn enforces calm through **intentional friction, respectful boundaries, and visual restraint**. Every interaction should feel like settling in, not being rushed. --- ## Part 1: Reading & Feed Experience ### Design Intent - Reading feels like settling into a book, not scrolling a timeline - No visual shouting or metric obsession - No urgency cues or FOMO mechanics ### Feed Layout Decisions #### 1. **Comfortable Max Width (680px)** **Why:** - Optimal line length for reading is 50-75 characters - Wide text blocks strain eye tracking - Creates "settling in" feeling vs infinite scroll anxiety **Implementation:** ```dart Container( constraints: const BoxConstraints(maxWidth: 680), // ... ) ``` #### 2. **Generous Vertical Spacing (24px between posts)** **Why:** - Tight spacing = rushed feeling - Space = permission to pause - Each post gets breathing room **Visual Effect:** - Posts feel like chapters in a book - No "wall of content" anxiety #### 3. **No Aggressive Dividers** **Why:** - Heavy dividers create visual noise - Soft shadows and spacing create natural separation - Avoids the "list item" feeling **Implementation:** ```dart boxShadow: AppTheme.shadowSm // Only 4% opacity ``` ### Post Card Design Decisions #### 1. **Body Text is THE HERO** **Why:** - You came here to read, not to scan metrics - Large, comfortable type (17px at 1.7 line-height) - Primary visual weight goes to content **Implementation:** ```dart Text( post.body, style: AppTheme.bodyLarge.copyWith( height: 1.7, // Extra generous for reading letterSpacing: 0.1, // Slight tracking ), ) ``` **Contrast with Twitter/X:** - Twitter: 15px text, 1.4 line-height, competing with images/metrics - Sojorn: 17px text, 1.7 line-height, nothing competes #### 2. **Author Identity: Clear but Not Dominant** **Why:** - You need to know who's speaking - But not at the expense of the message - Small avatar (36px vs typical 48px) - De-emphasized text color (textSecondary, not textPrimary) **Visual Hierarchy:** ``` 1. Post body (textPrimary, 17px, bold visual weight) 2. Actions (tertiary, 16px icons) 3. Author (textSecondary, 13px) 4. Metadata (textTertiary, 11px) ``` #### 3. **Metrics De-emphasized** **Why:** - Like counts don't mean quality - Save counts are personal, not performance - Comment counts ≠ value (and don't boost reach!) **Design Choices:** - Icons are small (18px, not 24px) - Text is labelSmall (11px) - Color is textTertiary (very light gray) - No visual weight **What's Hidden:** - View counts (never shown) - Ratio metrics (likes/comments) - Trending indicators #### 4. **Trust Tier Badge: Subtle Signal** **Why:** - Trust matters for context - But shouldn't dominate - Tiny badge (8px font, 12% opacity background) **vs Other Platforms:** - Twitter verification: 20px, bright blue, dominant - Sojorn trust tier: 8px, muted color, barely visible ### Interaction Behavior #### 1. **Gentle Press States** **Why:** - Aggressive hover/press = visual aggression - Subtle border change (borderSubtle → borderStrong) - Shadow removal (not addition) **Implementation:** ```dart border: Border.all( color: _isPressed ? AppTheme.borderStrong : AppTheme.borderSubtle, width: 0.5, ), boxShadow: _isPressed ? null : AppTheme.shadowSm, ``` **Effect:** - Card "settles in" when pressed - No bounce, no scale, no aggressive feedback #### 2. **No Metric Celebration** **Why:** - No confetti when you hit 10 likes - No "trending" badges - No "your post is doing well!" notifications **Philosophy:** - You wrote something calm → reward is internal - External validation ≠ quality ### Reading Enhancements #### 1. **Optimal Typography** - 17px body text (larger than most platforms) - 1.7 line-height (research shows 1.5-1.6 is ideal, we go further) - 0.1 letter-spacing (slight tracking for comfort) - System fonts (SF Pro Text, Roboto) for familiarity #### 2. **Interaction Affordances** - Appreciate/Save always visible but quiet - No hidden menus (everything upfront) - Tap target size: 44px minimum (accessibility) --- ## Part 2: Writing & Commenting Experience ### Composer Design Intent - Writing pauses before publishing (no tweet-and-regret) - Friction feels supportive, not punitive - Tone guidance is optional and respectful ### Composer UX Decisions #### 1. **Large, Calm Text Area** **Why:** - Small inputs = rushed thoughts - Large area = room to think - 500 character limit shown gently (not alarming) **Design:** ```dart SojornTextArea( minLines: 5, // Not 1-2 like Twitter maxLines: 15, maxLength: 500, style: AppTheme.bodyLarge, // Same size as reading ) ``` #### 2. **Character Limit: Gentle, Not Alarming** **What We Don't Do:** - ❌ Turn red at 480/500 - ❌ Show "You're over the limit!" in red text - ❌ Disable publish button aggressively **What We Do:** - ✅ Show "487 / 500" in textTertiary - ✅ Turn accent color at 490 - ✅ Fade publish button (not disable) at 501+ **Copy:** ``` Good: "487 / 500" Bad: "ONLY 13 CHARACTERS LEFT!" ``` #### 3. **Category Selection: Clear and Required** **Why:** - Categories are structural boundaries - Must be intentional (no "general" default) - Clear labels, obvious UI ### Tone Nudge UI #### 1. **When Triggered** **Scenario:** Post gets CIS < 0.85 **What We Don't Do:** - ❌ Red warning banner - ❌ "This violates community guidelines" - ❌ Block publishing immediately - ❌ Shame the user **What We Do:** - ✅ Neutral language - ✅ Soft amber background (not red) - ✅ Suggestion, not demand - ✅ Allow dismiss **Copy:** ```markdown ## Sharp Edges Detected This post has language that may feel sharp to readers. **Suggested rewrite:** "I respectfully disagree with this approach." **Original:** "This is stupid and wrong." [ Publish Anyway ] [ Edit ] ``` **Tone:** - No "You violated..." - No "This is not allowed" - Just "This may feel sharp" #### 2. **Allow Dismiss Without Penalty** **Why:** - You're an adult - Tone detection isn't perfect - Trust users to make decisions **But:** - Persistent low CIS → harmony score impact - 3+ rejected posts → temporary slow-down - Trust tier may adjust **Philosophy:** - Friction, not force - Consequences, not punishment ### Comment UI #### 1. **Mutual-Follow Only** **Design:** - Comment box only appears if mutual follow - Otherwise: "Follow each other to comment" - No shame, just structure #### 2. **Compact, Conversational Layout** **Why:** - Comments are dialogue, not performance - Small avatars (28px) - Lighter visual weight than posts #### 3. **Downvotes: De-emphasized** **Why:** - Downvotes useful for spam/quality - But not a weapon **Design:** - No downvote count shown - Icon is tertiary gray (not red) - No "controversial" indicators ### Empty States #### 1. **No Pressure to Post** **Bad Copy:** ``` "Your feed is empty! Start following people!" "Nothing to see here. Get active!" ``` **Good Copy (Sojorn Voice):** ``` "Nothing here yet" "Posts you appreciate will appear here" "Your feed is quiet right now" ``` **Tone:** - Welcoming, not urgent - Calm, not demanding - Permission to lurk --- ## Part 3: Navigation & Information Architecture ### Design Intent - Users always know where they are - No hidden mechanics or dark patterns - No surprise destinations ### Bottom Navigation #### 1. **Clear, Limited Tabs** **What We Have:** - **Following** (chronological from follows) - **Sojorn** (algorithmic FYP) - **Profile** (your stats and posts) **What We Don't Have:** - ❌ "Discover" (too vague) - ❌ "Notifications" (reduces checking anxiety) - ❌ "Messages" (not yet implemented) **Why 3 Tabs:** - Cognitive load: 3-5 is ideal - Each tab has one job - No confusion #### 2. **No Surprise Destinations** **Rule:** - Tab icon = where you land - No "Following but actually Explore" - No "Profile but actually Settings" ### Profile Hierarchy #### 1. **Posts First** **Why:** - You came to see what they wrote - Not their bio or follower count **Layout:** ``` 1. Posts (primary view) 2. Bio (secondary, collapsed) 3. Stats (tertiary, small) 4. Controls (obvious but not dominant) ``` #### 2. **Follow/Unfollow: Obvious** **Design:** - Always visible in header - Clear label ("Follow" / "Following") - No hidden in "..." menu ### Settings Organization #### 1. **Grouped by Concern** ``` 📖 Reading & Filters - Category preferences - Content filters - Feed preferences 🔒 Privacy & Blocking - Blocked users - Profile visibility - Data sharing 👤 Account & Data - Email/password - Export data - Delete account ``` **Why This Order:** - Most common (reading) first - Safety (blocking) never buried - Destructive (delete) last #### 2. **No Buried Safety Controls** **Rule:** - Block button on every profile - Privacy settings in top-level menu - Export data always accessible ### Discoverability #### 1. **Explore Tab Clearly Separate** **Why:** - "Explore" ≠ "Following" - No accidental algorithm exposure - Opt-in discovery #### 2. **Categories Never Auto-Enable** **Rule:** - All categories off by default (except general) - Explicit opt-in required - No "We think you'd like..." suggestions --- ## Part 4: Blocking & Filtering UX ### Design Intent - Blocking is self-care, not confrontation - Filtering is private and encouraged - No shame, no drama ### Block Affordance #### 1. **Always Visible** **Where:** - On every profile (header menu) - On every post (overflow menu) - In comment threads **Design:** ``` Icon: shield_outline (not block_circle) Label: "Block @username" Color: textSecondary (not error red) ``` **Why Shield Icon:** - Block = protection, not punishment - Shield = self-care - Less aggressive than ⛔ #### 2. **One-Tap Confirmation** **Flow:** ``` Tap "Block" → Dialog: "Block @username?" "You won't see their posts or comments. They won't be notified." [ Cancel ] [ Block ] ``` **No:** - ❌ "Are you SURE?" - ❌ "This is permanent" - ❌ Multiple confirmations **Copy Tone:** - Neutral, not dramatic - Reassuring, not scary #### 3. **No Explanation Required** **Why:** - You don't owe anyone an explanation - Block is personal boundary - No "Report" pressure **But:** - Separate "Report" option exists - Report ≠ block (different flows) ### Filter Controls #### 1. **Category Toggles** **Design:** ``` [ ] Quiet Reflections [ ] Gratitude [x] General Discussion [ ] Deep Questions ``` **Each Shows:** - Name - Description (one sentence) - Post count (optional) #### 2. **Keyword/Topic Filters (Optional)** **Future Feature:** ``` Hide posts containing: - "election" - "crypto" - "diet" ``` **Design:** - Off by default - No suggestions - No "trending" pressure #### 3. **Preview What's Hidden (Optional)** **Design:** ``` [ ] Show me what I'm filtering ``` **When Enabled:** - Filtered posts appear grayed out - "Hidden by your filters" label - Can tap to reveal **Default:** OFF (out of sight, out of mind) ### Feedback Copy #### 1. **After Blocking** ``` "You won't see posts from @username anymore." "They won't be notified." ``` **Not:** ``` ❌ "User blocked successfully!" ❌ "You'll never see them again!" ``` #### 2. **After Filtering** ``` "Quiet Reflections hidden from your feeds." ``` **Not:** ``` ❌ "Category disabled!" ❌ "You won't miss anything important" ``` ### Export/Import Block List #### 1. **Clear Warnings** ``` ## Export Block List This downloads a JSON file with usernames you've blocked. ⚠️ This file contains your personal blocking decisions. ⚠️ Sharing this may reveal your social boundaries. ⚠️ Sojorn does not endorse public block list sharing. [ Cancel ] [ Download JSON ] ``` #### 2. **No Recommendations** **What We Don't Do:** - ❌ "Import from popular block lists" - ❌ "People like you also block..." - ❌ "Suggested blocks based on your follows" **Why:** - Block lists = personal boundaries - No crowd-sourcing judgment - No guilt by association --- ## Part 5: Transparency & Explanation ### Design Intent - Calm confidence through clarity - No mystery, no jargon - Trust through honesty ### "How Reach Works" Page #### Content Structure ```markdown # How Sojorn Ranking Works Posts in your Sojorn feed are ranked by **calm velocity**—a measure of genuine appreciation over time. ## What Boosts Posts - ❤️ Appreciations (likes) - 🔖 Saves (strong signal) - ⏱️ Time spent reading (dwell time) - 🎯 High Content Integrity Score (CIS) ## What Slows Posts - ⏰ Age (older posts fade naturally) - 👎 Downvotes (quality filter) - 🚩 Low CIS (tone issues) ## What Doesn't Matter - 💬 Comment count (dialogue ≠ quality) - 👥 Author's follower count - 📊 Retweets/shares (we don't have those) ## Why This Way Calm velocity rewards thoughtful content, not viral outrage. Posts earn reach through genuine appreciation, not reaction-baiting. ``` #### Design Choices - **Plain language** (no "algorithm" jargon) - **Bullet points** (scannable) - **Emojis** (visual anchors, but not excessive) - **Honesty** (explicitly state what doesn't matter) ### "Rules & Tone" Page #### Content Structure ```markdown # Community Tone Guidelines Sojorn welcomes all ideas, but requires calm expression. ## Focus: Tone, Not Ideology We don't police what you think. We ask how you express it. ### ✅ Allowed - "I disagree with that approach." - "This feels uncomfortable to me." - "I see it differently." ### ⛔ Not Allowed - "This is stupid." - "You're an idiot." - "What the hell were you thinking?" ## Why Tone Matters Sharp language creates defensiveness. Calm language creates dialogue. ## How We Detect Tone - Pattern-based analysis (not perfect) - Content Integrity Score (CIS) - Human review for edge cases ## What Happens - CIS < 0.85: Gentle nudge to rephrase - CIS < 0.70: Post blocked - Persistent low CIS: Harmony score impact ``` #### Tone Choices - **No shame** ("not allowed" not "violations") - **Examples** (show, don't just tell) - **Honesty** (admit imperfection) ### Contextual Help #### 1. **"Why am I seeing this?"** **Trigger:** Tap "..." on any post **Copy:** ``` ## Why This Post This appeared in your Sojorn feed because: - High calm velocity (287 appreciates, 45 saves) - Category: General Discussion (you're subscribed) - Posted 2 hours ago ``` #### 2. **"Why can't I comment?"** **Scenario:** Non-mutual follow **Copy:** ``` ## Commenting You can comment when you both follow each other. This protects against drive-by harassment and ensures dialogue, not performance. ``` **Tone:** - No "You're not allowed" - Just "This is how it works" --- ## Part 6: Accessibility & Inclusivity ### Text Accessibility #### 1. **Scalable Font Sizes** **Implementation:** ```dart Text( post.body, style: Theme.of(context).textTheme.bodyLarge, // Respects user's OS text size settings ) ``` **Why:** - Low vision users need large text - Flutter automatically scales with OS settings - No hardcoded font sizes #### 2. **High Readability Contrast** **Ratios:** - textPrimary on background: 12.5:1 (AAA) - textSecondary on background: 7.2:1 (AA) - textTertiary on background: 4.8:1 (AA large text) **Why:** - WCAG AAA for body text - Still feels calm (not harsh black on white) ### Interaction Accessibility #### 1. **Keyboard Navigation (Web)** **Requirements:** - Tab through all interactive elements - Enter/Space activates buttons - Escape closes modals - Arrow keys in lists **Implementation:** ```dart Focus( onKey: (node, event) { // Handle keyboard events }, child: Widget(), ) ``` #### 2. **Screen Reader Labels** **Example:** ```dart IconButton( icon: Icon(Icons.favorite_border), tooltip: 'Appreciate this post', semanticsLabel: 'Appreciate post from ${post.author.displayName}', ) ``` **Why:** - Icon alone = meaningless to screen readers - Context matters #### 3. **Focus States** **Design:** ```dart focusColor: AppTheme.accent.withValues(alpha: 0.1), // Soft highlight, not harsh outline ``` ### Motion Accessibility #### 1. **Respect Reduced Motion** **Check:** ```dart final reducedMotion = MediaQuery.of(context).accessibleNavigation; final duration = reducedMotion ? Duration.zero : AppTheme.durationMedium; ``` **Where Applied:** - Fade transitions - Sheet slides - Loading spinners #### 2. **No Required Animations** **Rule:** - All info accessible without animation - Skeleton loaders have static alt - Progress shown via text too ### Cognitive Load #### 1. **Avoid Dense UI** **Guidelines:** - Maximum 3 actions per card - One primary action per screen - Generous spacing (24px, not 8px) #### 2. **Avoid Urgency** **No:** - ❌ Red badges - ❌ Pulsing animations - ❌ "New!" labels **Why:** - Reduces anxiety - Allows focus - Respects attention --- ## Part 7: Final Polish ### Microinteractions #### 1. **Subtle Haptics (Mobile)** **When:** - Appreciate/Save actions (light impact) - Publish post (medium impact) - Error state (notification feedback) **Implementation:** ```dart HapticFeedback.lightImpact(); // Not heavyImpact ``` **Why:** - Confirms action - But doesn't startle #### 2. **Sound Cues (Optional, OFF by default)** **Future:** - Soft "bloom" on appreciate - Gentle "save" sound - Muted error tone **Default:** OFF **Why:** Audio = intrusive ### Loading States #### 1. **Skeletons, Not Spinners** **Design:** ```dart ShimmerSkeleton( child: PostCardSkeleton(), ) ``` **Why:** - Shows structure - Feels faster - Less anxiety than spinner #### 2. **Calm Copy** **Good:** ``` "Loading..." "One moment" ``` **Bad:** ``` ❌ "Hang tight!" ❌ "Almost there!" ❌ "This won't take long!" ``` **Tone:** - Neutral, not cheerful - Honest, not performative ### Error Handling #### 1. **Gentle Language** **Good:** ``` "Couldn't load posts" "Connection issue" ``` **Bad:** ``` ❌ "ERROR: Network failure" ❌ "Oops! Something went wrong!" ❌ "Fatal exception occurred" ``` #### 2. **Clear Recovery** **Pattern:** ``` [Error message] [What happened] [Action button] ``` **Example:** ``` Couldn't load posts Check your internet connection. [ Try Again ] ``` #### 3. **No Blame** **Don't:** - ❌ "You're offline" - ❌ "Invalid input" **Do:** - ✅ "No connection" - ✅ "Hmm, that didn't work" ### Performance #### 1. **Optimize List Rendering** ```dart ListView.builder( itemBuilder: (context, index) { return RepaintBoundary( child: PostCard(post: posts[index]), ); }, ) ``` **Why:** - RepaintBoundary = isolate repaints - Smooth 60fps scroll #### 2. **Cache Feeds Responsibly** - Cache for 5 minutes - Invalidate on pull-to-refresh - Respect memory limits #### 3. **No Jank on Scroll** **Techniques:** - Lazy load images - Debounce pagination - Avoid setState in scroll listener --- ## Summary: What Makes Sojorn Calm ### Visual Calm - Warm neutrals, not cold grays - Soft shadows (4-8% opacity) - Generous spacing (24px, not 8px) - Muted colors (no bright red/blue) ### Interaction Calm - Slow animations (300-400ms) - Gentle press states (no bounce) - Subtle haptics (light, not heavy) - No urgency cues ### Content Calm - Body text is hero (17px, 1.7 line-height) - Metrics de-emphasized (11px, tertiary) - Author identity clear but quiet - No performance pressure ### Structural Calm - Mutual-follow commenting - Category opt-in - Block without drama - Tone guidance without shame ### Cognitive Calm - No mystery (transparency pages) - No dark patterns (honest UI) - No jargon (plain language) - No surprises (predictable navigation) --- **Result:** An app that feels like settling into a good book, not scrolling a frantic timeline. **Enforcement:** Design system + custom widgets make it impossible to violate calm principles. **Philosophy:** Calm is not a feature. Calm is structural.