Files
hope-voice-api/design/README.md
T

7.5 KiB
Raw Blame History

Hope shared themes · v1

Identity and scope

Hope is an authenticated voice companion, not a model dashboard. Users may talk in a dim room or review text in daylight; appearance therefore follows the device by default, with explicit light/dark overrides. The restrained Nearlight system uses opaque tinted surfaces, readable ink and one action accent. Opal Pink remains the default, preserving the existing iOS identity rather than the historical web navy palette.

The source of truth is hope-palette.v1.json, schema version 1, token version 1.0.0. Palette IDs are stable persisted/API values: opal (display label Opal Pink), graphite, ocean, sage, lavender. Appearance IDs are system, light, dark; default system. Labels are presentation, never storage keys. Theme settings must not affect authentication, voice, conversation or error state. Consumers should normalize unknown IDs to Opal/system.

The extracted native source is Nearlight.swift, lines 11–43. Opal's exact ground/surface/personaSurface/ink/quiet/rosewood/onRosewood values are preserved. Alert channels are deliberately stored as normalized decimal sRGB [0.675, 0.208, 0.282] and [1.0, 0.604, 0.675]: rounding them to hex would change the existing UIColor values. All other opaque primitives use six-digit sRGB hex. The generator emits round-trip precision channel values, not an approximate color conversion.

Architecture and outputs

  1. Primitive values: explicit sRGB palette values and shared alert primitives.
  2. Semantic roles: ground, surface, personaSurface, ink, quiet, accent, onAccent, alert, onAlert, focus, controlBorder; aliases are documented in the JSON. accent replaces color-specific naming for new code; existing rosewood semantics remain a compatibility alias.
  3. Component aliases: action, input, persona and error roles refer to semantic roles, never raw values.

generate_hope_themes.py uses the standard library only. It validates required roles and contrast before writing, generates deterministic artifacts, and embeds source version/SHA256. Change the source and regenerate; do not hand-edit outputs. A changed schema requires a new schema version; removed/repurposed role or palette IDs require a major token version. Additive roles/palettes use a minor version; compatible corrections use a patch version. Native identity changes require explicit product approval.

Web handoff

Load hope-themes.css after existing base styles, then consume prefixed variables:

<html data-palette="opal" data-appearance="system">
body { background: var(--hope-ground); color: var(--hope-ink); }
button.primary { background: var(--hope-action-background); color: var(--hope-action-foreground); }
input { background: var(--hope-input-background); color: var(--hope-input-foreground); border-color: var(--hope-input-border); }
input::placeholder { color: var(--hope-input-placeholder); opacity: 1; }
:focus-visible { outline: 2px solid var(--hope-focus); outline-offset: 3px; }

Attributes belong on the document root, not arbitrary descendants. Missing/stale palette IDs inherit Opal. Missing/stale/system appearance follows prefers-color-scheme; explicit light/dark overrides the OS. color-scheme matches the effective appearance so native controls match. Theme styles define tokens only; they do not modify layout, attach event listeners, store preferences, or override legacy unprefixed CSS automatically. Consumers own preference loading/persistence and loading the sheet before rendering to avoid a theme flash.

iOS handoff

HopeTheme.generated.swift is staged in this backend repository, not installed into the iOS target. The iOS teammate may copy the unchanged artifact into the app build and map existing Nearlight roles:

HopeTheme.color(.ground, palette: .opal, appearance: .system)
HopeTheme.color(.accent, palette: palette, appearance: appearance) // Nearlight.rosewood
HopeTheme.color(.onAccent, palette: palette, appearance: appearance) // Nearlight.onRosewood

HopePalette and HopeAppearance are raw-string enums for persistence. HopePalette.label supplies display labels; HopeTheme.version identifies the source version. System colors use the native trait-aware UIColor provider; explicit overrides return a fixed color. UIKit/SwiftUI are required. Spacing, typography, radii, motion and native materials remain the responsibility of the existing Nearlight system; this task does not alter them.

Accessibility and state rules

  • Primary/supporting/placeholder/accent/error text is tested at ≥4.5:1 on ground, surface and persona surfaces for every palette and appearance. Action/destructive labels also meet 4.5:1; focus and essential control boundaries meet ≥3:1 against those surfaces.
  • All 230 declared foreground/background checks pass. Lowest normal-text contrast is 4.558:1 (Sage light quiet on selectedWash). Checks use WCAG relative luminance and sRGB transfer functions. Run --contrast for the complete measured report. This verifies declared token pairings, not every rendered UI, arbitrary layering or hardware display.
  • separator is decorative only. Native's ink × 14%, accent × 10% selected wash and alert × 8% error wash are precomposited over surface into opaque shared tokens. On that surface they preserve the original appearance. Do not reuse the baked wash expecting translucency over a different parent; use a reviewed derivation if needed.
  • Essential boundaries use controlBorder, not the low-contrast separator. A focus ring needs thickness and offset, not color alone. In forced-colors mode consumers must retain browser/system focus and control boundaries.
  • Errors are not represented only by palette/color: keep explicit messages, icons or labels, accessibility announcements and recovery actions. alert is shared across palettes, not the theme's accent. Selected controls also need checkmarks/pressed or selected semantics. Never use color alone for listening, speaking, offline, loading, success or destructive state.
  • Disabled controls may use native disabled semantics; do not dim readable supporting text into a disabled color. Consumers retain reduced-motion handling and Dynamic Type/text zoom support.

Offline verification (no NAS, no deployment)

python3 scripts/generate_hope_themes.py
python3 scripts/generate_hope_themes.py --check
python3 scripts/generate_hope_themes.py --check --contrast
python3 -m unittest discover -s tests/unit -p test_hope_themes.py -v
swiftc -frontend -parse generated/HopeTheme.generated.swift

Token tests fix the native identity, ensure shared alert semantics and opaque derivations, reject malformed primitives/missing roles/low contrast, verify deterministic artifact drift, and confirm round-trip Swift channels. The optional Playwright test runs local Chromium against in-memory HTML/CSS with 70 palette/appearance/OS combinations, including missing/stale values and overrides; it does not call an API or server. It skips if the Playwright module is absent; install Playwright and its Chromium browser for full verification. Swift parsing verifies syntax only; native UIKit compilation and final device rendering belong to the iOS integration qualification. Do not run the legacy top-level live-default test suite for token changes.