Files
Sportstime/docs/plans/2026-01-16-brutalist-app-wide-design.md
Trey t dc43bf0d53 docs: add brutalist app-wide design spec
Design for extending Brutalist style from home-screen-only to app-wide:
- StyleProvider protocol for shape/typography language
- Adaptive view routers for each major screen
- Brutalist variants for TripDetail, MyTrips, Schedule, Settings
- Colors still controlled by user's selected Theme

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 09:04:32 -06:00

5.3 KiB

Brutalist App-Wide Design

Overview

Extend the Brutalist design style from home-screen-only to work app-wide. Brutalist is a shape/typography language that layers on top of the user's chosen color theme (Teal, Orbit, Clutch, etc.).

Scope: Classic + Brutalist styles only. Other 22 styles fall back to Classic for non-home screens.

Design Language

What Brutalist Controls (Shape Language)

Property Classic Brutalist
Typography System default Monospaced
Text case Mixed case UPPERCASE
Letter tracking 0 2-4
Corner radius 8-16px 0px
Borders None 1-2px solid
Shadows Soft None or harsh
Gradients Yes No (flat colors)
Dividers Subtle lines Perforated ticket-stub
Labels Plain text Bracketed [ SECTION ]

What Theme Still Controls (Color Palette)

  • Theme.warmOrange → accent color
  • Theme.routeGold → secondary accent
  • Theme.textPrimary/Secondary/Muted → text colors
  • Theme.cardBackground → surfaces
  • Theme.darkBackground1/lightBackground1 → flat backgrounds (Brutalist override)

Architecture

Pattern: Adaptive View Routers

Each major screen has an "Adaptive" wrapper that routes to style-specific variants:

struct AdaptiveTripDetailView: View {
    let trip: Trip

    var body: some View {
        switch DesignStyleManager.shared.currentStyle {
        case .brutalist:
            TripDetailView_Brutalist(trip: trip)
        default:
            TripDetailView_Classic(trip: trip)
        }
    }
}

File Structure

SportsTime/Core/Design/
├── StyleProvider.swift                    (protocol + implementations)

SportsTime/Features/Trip/Views/
├── AdaptiveTripDetailView.swift           (router)
└── Variants/
    ├── Classic/TripDetailView_Classic.swift
    └── Brutalist/TripDetailView_Brutalist.swift

SportsTime/Features/MyTrips/Views/
├── AdaptiveMyTripsView.swift              (router)
└── Variants/
    ├── Classic/MyTripsView_Classic.swift
    └── Brutalist/MyTripsView_Brutalist.swift

SportsTime/Features/Schedule/Views/
├── AdaptiveScheduleView.swift             (router)
└── Variants/
    ├── Classic/ScheduleView_Classic.swift
    └── Brutalist/ScheduleView_Brutalist.swift

SportsTime/Features/Settings/Views/
├── AdaptiveSettingsView.swift             (router)
└── Variants/
    ├── Classic/SettingsView_Classic.swift
    └── Brutalist/SettingsView_Brutalist.swift

StyleProvider Protocol

Lightweight protocol for shared component styling within variants:

protocol StyleProvider {
    // Shape
    var cornerRadius: CGFloat { get }
    var borderWidth: CGFloat { get }

    // Typography
    var fontDesign: Font.Design { get }
    var usesUppercase: Bool { get }
    var headerTracking: CGFloat { get }

    // Effects
    var usesGradientBackgrounds: Bool { get }
    var usesSoftShadows: Bool { get }

    // Helpers
    func flatBackground(_ colorScheme: ColorScheme) -> Color
}

struct ClassicStyle: StyleProvider {
    let cornerRadius: CGFloat = 12
    let borderWidth: CGFloat = 0
    let fontDesign: Font.Design = .default
    let usesUppercase = false
    let headerTracking: CGFloat = 0
    let usesGradientBackgrounds = true
    let usesSoftShadows = true
}

struct BrutalistStyle: StyleProvider {
    let cornerRadius: CGFloat = 0
    let borderWidth: CGFloat = 1
    let fontDesign: Font.Design = .monospaced
    let usesUppercase = true
    let headerTracking: CGFloat = 2
    let usesGradientBackgrounds = false
    let usesSoftShadows = false
}

Screen Designs

TripDetailView_Brutalist

Header:

  • Date range: Monospaced, uppercase, wide tracking
  • Route: DETROIT → MILWAUKEE → SALT LAKE (arrows, no dots)
  • Sport badges: Bordered rectangles, monospace text

Stats row:

  • Bordered boxes: [ 7 DAYS ] [ 3 CITIES ] [ 4 GAMES ]
  • Monospaced numbers, uppercase labels

Score card:

  • Sharp rectangle, 2px border
  • Score grade uses Theme.warmOrange
  • No glow effects

Itinerary:

  • Day headers: DAY 1 large monospace
  • Perforated dividers between days
  • Game rows: Bordered boxes, monospace teams
  • Travel: [ TRAVEL ] DETROIT → MILWAUKEE • 327 MI • 5H 27M
  • Timeline: Solid line, square dots

MyTripsView_Brutalist

  • Section headers: [ PLANNED ] [ COMPLETED ]
  • Trip rows: Bordered, monospace city names
  • Status badges: Sharp rectangles

ScheduleView_Brutalist

  • Date picker: Sharp buttons, monospace dates
  • Game list: Bordered rows, monospace matchups
  • Filters: Bracketed labels [ NBA ] [ NHL ]

SettingsView_Brutalist

  • Section headers: [ APPEARANCE ] [ ACCOUNT ]
  • Row items: Bordered, monospace labels
  • Toggles: Sharp rectangles

Implementation Order

  1. StyleProvider.swift - Protocol + Classic/Brutalist implementations
  2. TripDetailView_Brutalist.swift + AdaptiveTripDetailView.swift
  3. MyTripsView_Brutalist.swift + AdaptiveMyTripsView.swift
  4. ScheduleView_Brutalist.swift + AdaptiveScheduleView.swift
  5. SettingsView_Brutalist.swift + AdaptiveSettingsView.swift

Files to Modify

  • UIDesignStyle.swift - Add styleProvider computed property
  • All callers of TripDetailView, MyTripsView, etc. - Switch to Adaptive versions