feat(ios): switch accent to desktop amber-gold theme (was indigo)

Match the desktop/web theme (public/style.css): accent #E3A64A gold (dark) /
#C9892F (light) replacing the indigo, +onAccent #1A1305 dark ink for gold-fill
buttons, +accentSoft. Status colors realigned to the web warm palette
(#46D07F/#F5B14C/#FF6B6B). Terminal canvas fixed to the desktop warm-dark
(#100F0D bg / #ECE9E3 fg) with gold caret/selection. DSButtonStyle primary now
uses onAccent (dark) instead of white for contrast on gold. All via the single
accent token + DS palette — no scattered edits.

Verified: iPhone 16 290 + iPad Pro 11 290 tests green (DesignSystemTests assert
the exact gold + web-status RGB in both schemes).
This commit is contained in:
Yaojia Wang
2026-07-06 07:25:32 +02:00
parent c44725618c
commit d01a69eb72
4 changed files with 56 additions and 31 deletions

View File

@@ -150,7 +150,10 @@ struct DSButtonStyle: ButtonStyle {
private var foreground: Color {
switch kind {
case .primary, .destructive: return .white
// Gold accent fill needs DARK ink for contrast (mirrors web
// --on-accent), NOT white. Red destructive fill keeps white.
case .primary: return DS.Palette.onAccent
case .destructive: return .white
case .secondary: return DS.Palette.accent
}
}

View File

@@ -6,11 +6,11 @@ import UIKit
/// The single source of truth for every visual constant in the App layer.
/// Screens/components must reference these tokens never inline colors,
/// spacings, radii, opacities, durations or haptics. Direction: ""
/// (refined native, Apple HIG), dark-mode-first, indigo/violet accent
/// (refined native, Apple HIG), dark-mode-first, amber-gold accent (desktop-matched)
/// continuing the web selection color.
///
/// Vocabulary (all under `DS.`):
/// - `Palette` adaptive `accent` (indigo) · semantic `status*` colors
/// - `Palette` adaptive `accent` (amber gold, matches desktop) · semantic `status*` colors
/// (color is NEVER the only status signal pair with a symbol,
/// see `StatusStyle`) · `surface`/`card`/`hairline` surfaces ·
/// `textPrimary`/`textSecondary`/`textTertiary`.
@@ -33,9 +33,13 @@ enum DS {
/// status colors use the direction's exact hex so light/dark stay on-brand.
enum Palette {
// Accent (indigo/violet web selection color #7C8CFF)
// Adaptive: dark #7C8CFF, light #4F5BD5. Used sparingly primary
// actions, selection, active state only.
// Accent (amber gold matches the desktop/web theme)
// The desktop web/Electron UI uses --accent #E3A64A ("amber gold",
// public/style.css:14) on a warm near-neutral dark surface. We match it.
// Adaptive: dark = #E3A64A (gold), light = #C9892F (deeper gold =
// --accent-2) for adequate contrast on a light background. Used
// sparingly primary actions, selection, active state only. Gold needs
// DARK text on top use `onAccent`, never white/textPrimary.
/// The one accent token. Inject once at the root via `.tint(DS.Palette.accent)`.
static let accent = Color(uiColor: accentUIColor())
@@ -46,21 +50,30 @@ enum DS {
static func accentUIColor() -> UIColor {
UIColor { trait in
trait.userInterfaceStyle == .dark
? UIColor(red: 0x7C / 255.0, green: 0x8C / 255.0, blue: 0xFF / 255.0, alpha: 1)
: UIColor(red: 0x4F / 255.0, green: 0x5B / 255.0, blue: 0xD5 / 255.0, alpha: 1)
? UIColor(red: 0xE3 / 255.0, green: 0xA6 / 255.0, blue: 0x4A / 255.0, alpha: 1) // #E3A64A --accent
: UIColor(red: 0xC9 / 255.0, green: 0x89 / 255.0, blue: 0x2F / 255.0, alpha: 1) // #C9892F --accent-2
}
}
// Semantic status colors (exact hex from the direction)
// These are the ONLY status colors. `StatusStyle` pairs each with a
// distinct SF Symbol so status is never conveyed by color alone.
/// Text/icon color to place ON an accent-filled surface (gold needs dark
/// ink for contrast mirrors web --on-accent #1A1305).
static let onAccent = rgb(26, 19, 5)
/// Faint accent wash (selection/soft highlight) web --accent-soft.
static let accentSoft = Color(red: 0xE3 / 255.0, green: 0xA6 / 255.0, blue: 0x4A / 255.0, opacity: 0.15)
/// working #34C759 (green).
static let statusWorking = rgb(52, 199, 89)
/// waiting / needs-me #FF9F0A (amber).
static let statusWaiting = rgb(255, 159, 10)
/// stuck #FF453A (red).
static let statusStuck = rgb(255, 69, 58)
// Semantic status colors (match the desktop/web status palette)
// These are the ONLY status colors. `StatusStyle` pairs each with a
// distinct SF Symbol so status is never conveyed by color alone. Hex
// mirrors the web's warm status set (public/style.css:18-20) so iOS and
// desktop read the same. `waiting` amber #F5B14C stays distinct from the
// gold accent #E3A64A (brighter/less brown), and is only ever a small
// badge fill, never a large accent surface.
/// working #46D07F (web --green).
static let statusWorking = rgb(70, 208, 127)
/// waiting / needs-me #F5B14C (web --amber).
static let statusWaiting = rgb(245, 177, 76)
/// stuck #FF6B6B (web --red).
static let statusStuck = rgb(255, 107, 107)
/// idle quiet secondary gray (distinguished from `unknown` by shape).
static let statusIdle = Color.secondary
/// unknown / no signal yet gray.
@@ -93,6 +106,14 @@ enum DS {
/// Tertiary label color (de-emphasized detail).
static let textTertiary = Color(uiColor: .tertiaryLabel)
// Terminal canvas (fixed warm-dark, matches the desktop terminal)
// A terminal reads as dark regardless of app appearance (like the
// desktop). Values mirror the web chrome: --bg #100F0D / --text #ECE9E3.
/// Terminal background warm near-black #100F0D (web --bg).
static let terminalBackground = rgb(16, 15, 13)
/// Terminal foreground warm off-white #ECE9E3 (web --text).
static let terminalForeground = rgb(236, 233, 227)
/// Build an opaque sRGB color from 0255 components.
private static func rgb(_ r: Double, _ g: Double, _ b: Double) -> Color {
Color(.sRGB, red: r / 255, green: g / 255, blue: b / 255, opacity: 1)