Add curated semantic theme system with live preview #3
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Add a curated built-in theme system for macOS, with shared semantic roles that can later map to iOS without forcing identical geometry or materials. The goal is a small set of intentionally art-directed themes—not a color-soup palette editor—and a live, reversible preview in Appearance settings.
The reader redesign (top padding below the physical-top fade, stronger subject contrast, distinct sender/recipient/header, subject, and body regions, expandable full headers) is a separate concern. This issue must provide reader-region roles, but must not block that work or choose its final palettes.
Current seams to build on
App/Sources/Support/AppearanceSettings.swiftis the@MainActor @Observableowner of persisted appearance choices. It currently ownsAppearanceMode(system,light,dark), window opacity, the blur/Liquid Glass treatment choice, message-list row density, and theAccentSource.AccentSourceis the current canonical system/custom accent bridge. SwiftUI receives it through the environment and.tint; AppKit message-list cells resolve the sameNSColor. Existing semantic warning/error/content colors are intentionally outside that one-accent interface.App/Sources/Shell/WindowBackdrop.swiftalready centralizes the blur/glass/opaque decision inWindowBackdropPlan. Reduce Transparency and fullscreen force an opaque fallback; opacity and the treatment choice are persisted separately.App/Sources/Shell/MainWindowController.swiftinjects appearance into the main shell, whileApp/Sources/Account/SettingsWindow.swiftis the existing Appearance settings surface. Any new theme selection should remain observable across both windows without requiring a reopen.App/Sources/MessageWeb/MessageWebView.swiftwraps sanitized HTML and injects reader CSS. Its current boundary intentionally leaves author colors in the email HTML alone and blocks network loads except approved internal/data-image paths.App/Sources/Viewer/MessageViewer.swiftowns native reader chrome around the web content.Use these seams rather than adding per-view defaults or a second accent source. Keep platform rendering at the edge: the semantic theme value should be shared, while SwiftUI, AppKit, and WebKit adapters choose their platform-appropriate representation.
Proposed architecture
ThemeID,ThemeVariant,ThemeTokens, and a resolver) besideAppearanceSettings. A token must describe a role, not a component-specific arbitrary color.AppearanceSettingspersists only a stable preset ID plus the existing appearance mode/variant choice and schema version; it does not persist an unbounded bag of RGB values.systemthrough the effective macOS color scheme. A user-selected light or dark mode must select the corresponding variant of the chosen preset. Unknown IDs, malformed persisted values, and future schema versions must fall back safely to the default preset without crashing or silently creating a new palette.AccentSource.WindowBackdropPlanremains the enforcement point for Reduce Transparency, fullscreen, and opaque fallback. Native materials are optional implementation details, not a requirement of a preset.Semantic token families
The initial role inventory should cover at least:
readerSubjectTint,readerHeaderTint, andreaderBodyTintroles. Also provide reader subject/header/body text hierarchy and separators so the native reader chrome can make those regions visually distinct without blur, glass, or transparency. These are semantic roles only; final palette choices belong to the theme design and remain independent of the reader layout redesign.The resolver should be able to return a contrast-adjusted set for macOS Increased Contrast and a fully opaque set when Reduce Transparency is enabled. It must never use dimming or translucency to make primary text fail contrast.
Presets
Ship several named families with an intentional visual thesis and hand-authored light/dark pairs. For example:
These names and exact palettes are design decisions to validate, not permission to generate arbitrary combinations. Each preset needs written rationale, tested contrast, and a small preview that demonstrates the shell, sidebar, list, and reader-region roles. Do not add a general-purpose color editor in this work.
Appearance settings behavior
AppearanceSettingskeys (mode, backdrop/treatment, opacity, message-list lines, and accent) and missing/invalid values. Theme IDs are versioned so a removed preset maps to a documented fallback rather than changing appearance unpredictably.Reader and HTML isolation
Theme tokens may style Mailternal-owned reader chrome (subject, sender/recipient headers, full-header disclosure, separators, and the subject/header/body region surfaces) and the host fallback canvas. They must not rewrite the author-controlled email design. Scope any WebKit CSS variables/classes to the reader host, keep sanitized HTML in its content boundary, and preserve the current network-blocking and remote-image policy. A malicious message must not be able to inject a theme token, enable a material, load a remote stylesheet, or alter native chrome. Author colors, tables, links, and inline styles must remain isolated from shell/sidebar/list tokens; test both intentionally light and intentionally dark email fixtures.
iOS direction
Define the semantic role names and information hierarchy as the shared contract, not macOS view types, dimensions, or material names. A future iOS renderer should map window/sidebar/list/reader roles to phone-appropriate navigation, sheets, and full-width reader regions. It should preserve the same subject/header/body information principles while using a phone-specific layout and platform-native accessibility behavior. This issue does not implement iOS or require macOS and iOS screenshots to match pixel-for-pixel.
Non-goals
QA and visual-regression plan
Acceptance criteria
References
App/Sources/Support/AppearanceSettings.swift,App/Sources/Shell/WindowBackdrop.swift,App/Sources/Shell/MainWindowController.swift,App/Sources/Account/SettingsWindow.swift,App/Sources/MessageWeb/MessageWebView.swift, andApp/Sources/Viewer/MessageViewer.swift.NSVisualEffectView: https://developer.apple.com/documentation/appkit/nsvisualeffectviewWKContentRuleList: https://developer.apple.com/documentation/webkit/wkcontentrulelist