Developer at a dark-themed workstation with code on screens
← Blog·Design

Designing Knotos: Dark-First with SwiftUI and a Custom Design System

V
Van Thuong Dao·May 3, 2026·5 min read

Most iOS apps treat dark mode as an afterthought — invert some colours, call it done. Knotos was designed dark-first: deep navy backgrounds, indigo accents, and amber as the single warm highlight colour. Light mode adapts from there, not the other way around.

The colour palette

Two primary colours drive the entire system:

  • Indigo #292E8C — primary action colour, bubble backgrounds, active states. Deep enough to feel premium, saturated enough to read clearly on dark backgrounds.
  • Amber #FFB833 — online presence, unread badges, call accept, CTAs. The single warm note in a cool palette — it draws the eye exactly where it should.

Everything else is neutral: near-black backgrounds (#141729 and#1C1E2E), white with opacity for secondary text, andsystemGray6 for input fields.

BrandTheme — a single source of truth

Rather than scattering colour literals across views, Knotos centralises design tokens in a BrandTheme struct:

struct BrandTheme {
  static let appName   = "Knotos"
  static let primary   = Color(red: 0.161, green: 0.180, blue: 0.549)  // indigo
  static let accent    = Color(red: 1.0,   green: 0.722, blue: 0.200)  // amber

  static func adaptiveBackground(_ scheme: ColorScheme) -> Color {
    scheme == .dark
      ? Color(red: 0.078, green: 0.082, blue: 0.114)   // #141729
      : Color(red: 0.973, green: 0.969, blue: 0.957)   // cream
  }
}

Any design change — a brand refresh, an A/B test — happens in one file. Views never hardcode hex values.

The KnotLogo — pure SwiftUI

The logo is drawn in code, not loaded as an image asset. Two overlapping circles using ZStack, Circle(), andtrim() modifiers. This means it scales perfectly to any size, renders crisply on all display densities, and animates trivially.

Message bubbles and typography

Sent messages use a translucent amber background — Color.amber.opacity(0.15)with an amber border — rather than a solid fill. This keeps the bubbles light and readable without overwhelming the conversation. Received messages use solid indigo.

Font sizes follow a strict scale: 17pt for message body (matching iOS default), 13pt for metadata, 15pt for navigation labels. Every text style uses.system(size:weight:design:) with .rounded for headings and .default for body — no custom fonts to bundle.

Input fields

Fields use systemGray6 as the background — slightly lighter than the screen background — with a 1.5pt indigo stroke that appears only when the field is focused. This gives clear focus feedback without the aggressive coloured border that most apps use.

The design system pays off most on the small details: password fields with eye-toggle buttons, animated typing indicators, pinned message banners — all built from the same tokens with no design debt.

Adaptive vs. dark-only

Despite being dark-first, Knotos fully supports light mode. TheadaptiveBackground helper switches between near-black and cream. Accent colours stay the same in both modes — indigo and amber have enough contrast on both backgrounds to work without variants.