From 985c812a759b5ec0694a74b145041ba2cd7bd3f7 Mon Sep 17 00:00:00 2001 From: Shane Maynard Date: Sat, 3 Oct 2026 08:36:28 -0400 Subject: [PATCH] feat: add shared versioned Hope palette tokens --- design/README.md | 68 ++ design/hope-palette.v1.json | 62 ++ generated/HopeTheme.generated.swift | 160 ++++ scripts/generate_hope_themes.py | 199 +++++ tests/unit/test_hope_themes.py | 117 +++ web/hope-themes.css | 1057 +++++++++++++++++++++++++++ 6 files changed, 1663 insertions(+) create mode 100644 design/README.md create mode 100644 design/hope-palette.v1.json create mode 100644 generated/HopeTheme.generated.swift create mode 100644 scripts/generate_hope_themes.py create mode 100644 tests/unit/test_hope_themes.py create mode 100644 web/hope-themes.css diff --git a/design/README.md b/design/README.md new file mode 100644 index 0000000..f0169a7 --- /dev/null +++ b/design/README.md @@ -0,0 +1,68 @@ +# 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](./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](../../voice-ios/Sources/App/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](../scripts/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](../web/hope-themes.css) after existing base styles, then consume prefixed variables: + +```html + +``` + +```css +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](../generated/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: + +```swift +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) + +```sh +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](../tests/unit/test_hope_themes.py) 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. diff --git a/design/hope-palette.v1.json b/design/hope-palette.v1.json new file mode 100644 index 0000000..203d573 --- /dev/null +++ b/design/hope-palette.v1.json @@ -0,0 +1,62 @@ +{ + "schemaVersion": 1, + "version": "1.0.0", + "defaultPalette": "opal", + "defaultAppearance": "system", + "primitives": {"white": "#FFFFFF", "alertLight": [0.675, 0.208, 0.282], "alertDark": [1.0, 0.604, 0.675]}, + "roles": { + "ground": "Screen background", + "surface": "Opaque controls, grouped rows and dock", + "personaSurface": "Opaque square persona stage", + "ink": "Primary text", + "quiet": "Supporting text, placeholders and inactive icons; never reduce opacity for body copy", + "accent": "Primary actions and current selection; Nearlight.rosewood compatibility", + "onAccent": "Primary action label", + "alert": "Error text and destructive actions; independent of palette", + "onAlert": "Label on solid destructive action", + "focus": "Keyboard focus ring against surrounding surfaces", + "controlBorder": "Essential control boundary; not the decorative separator" + }, + "sharedRoles": { + "light": {"alert": "alertLight", "onAlert": "white"}, + "dark": {"alert": "alertDark", "onAlert": "$ground"} + }, + "derivedRoles": { + "separator": {"foreground": "ink", "alpha": 0.14, "background": "surface", "purpose": "Decorative separator only; not an essential boundary"}, + "selectedWash": {"foreground": "accent", "alpha": 0.10, "background": "surface", "purpose": "Selected background; pair with selected semantics/checkmark"}, + "errorWash": {"foreground": "alert", "alpha": 0.08, "background": "surface", "purpose": "Error background; pair with icon, explicit message and recovery"} + }, + "components": { + "actionBackground": "accent", "actionForeground": "onAccent", + "inputBackground": "surface", "inputForeground": "ink", "inputPlaceholder": "quiet", + "inputBorder": "controlBorder", "inputFocus": "focus", "personaBackground": "personaSurface", + "errorBackground": "errorWash", "errorForeground": "alert" + }, + "palettes": { + "opal": { + "label": "Opal Pink", + "light": {"ground": "#FFFDFD", "surface": "#FFF7FA", "personaSurface": "#FFF2F7", "ink": "#302B40", "quiet": "#706074", "accent": "#A64270", "onAccent": "#FFFFFF", "focus": "#A64270", "controlBorder": "#706074"}, + "dark": {"ground": "#201820", "surface": "#302330", "personaSurface": "#3B2836", "ink": "#FFF2F7", "quiet": "#CBB6C7", "accent": "#F0A8C8", "onAccent": "#201820", "focus": "#F0A8C8", "controlBorder": "#CBB6C7"} + }, + "graphite": { + "label": "Graphite", + "light": {"ground": "#FDFDFE", "surface": "#F3F4F6", "personaSurface": "#EDEEF2", "ink": "#272A33", "quiet": "#5E626E", "accent": "#505969", "onAccent": "#FFFFFF", "focus": "#505969", "controlBorder": "#5E626E"}, + "dark": {"ground": "#191B20", "surface": "#25282F", "personaSurface": "#30343D", "ink": "#F3F4F7", "quiet": "#BCC1CD", "accent": "#BFC9DC", "onAccent": "#191B20", "focus": "#BFC9DC", "controlBorder": "#BCC1CD"} + }, + "ocean": { + "label": "Ocean", + "light": {"ground": "#FCFEFF", "surface": "#F0F7FB", "personaSurface": "#E7F2F9", "ink": "#233442", "quiet": "#536878", "accent": "#27658C", "onAccent": "#FFFFFF", "focus": "#27658C", "controlBorder": "#536878"}, + "dark": {"ground": "#15212B", "surface": "#20313F", "personaSurface": "#283D4D", "ink": "#EDF7FF", "quiet": "#B3CBDE", "accent": "#97CEEE", "onAccent": "#15212B", "focus": "#97CEEE", "controlBorder": "#B3CBDE"} + }, + "sage": { + "label": "Sage", + "light": {"ground": "#FCFEFC", "surface": "#F0F6F1", "personaSurface": "#E7F0E9", "ink": "#29382D", "quiet": "#596B5D", "accent": "#3C704D", "onAccent": "#FFFFFF", "focus": "#3C704D", "controlBorder": "#596B5D"}, + "dark": {"ground": "#19231C", "surface": "#26342A", "personaSurface": "#304236", "ink": "#F0F8F1", "quiet": "#B9CFBE", "accent": "#A5D4AF", "onAccent": "#19231C", "focus": "#A5D4AF", "controlBorder": "#B9CFBE"} + }, + "lavender": { + "label": "Lavender", + "light": {"ground": "#FEFDFF", "surface": "#F6F2FC", "personaSurface": "#EEE8F8", "ink": "#342D43", "quiet": "#685E7B", "accent": "#70529C", "onAccent": "#FFFFFF", "focus": "#70529C", "controlBorder": "#685E7B"}, + "dark": {"ground": "#201B29", "surface": "#2E273C", "personaSurface": "#3B324D", "ink": "#F7F1FF", "quiet": "#C8BADF", "accent": "#CFB4F4", "onAccent": "#201B29", "focus": "#CFB4F4", "controlBorder": "#C8BADF"} + } + } +} diff --git a/generated/HopeTheme.generated.swift b/generated/HopeTheme.generated.swift new file mode 100644 index 0000000..965ff2f --- /dev/null +++ b/generated/HopeTheme.generated.swift @@ -0,0 +1,160 @@ +// Generated from design/hope-palette.v1.json v1.0.0; SHA256 bb76134be477916d776cda4259f45096fc2dcfcf873918b8703a0b716391a350. +// DO NOT EDIT. python3 scripts/generate_hope_themes.py +import SwiftUI +import UIKit + +struct HopeRGB: Sendable { + let red: Double + let green: Double + let blue: Double + init(_ red: Double, _ green: Double, _ blue: Double) { + self.red = red; self.green = green; self.blue = blue + } + var uiColor: UIColor { UIColor(red: CGFloat(red), green: CGFloat(green), blue: CGFloat(blue), alpha: 1) } +} + +enum HopeAppearance: String, CaseIterable, Sendable { + case system, light, dark +} + +enum HopePalette: String, CaseIterable, Sendable { + case opal + case graphite + case ocean + case sage + case lavender + static let defaultPalette: HopePalette = .opal + var label: String { + switch self { + case .opal: return "Opal Pink" + case .graphite: return "Graphite" + case .ocean: return "Ocean" + case .sage: return "Sage" + case .lavender: return "Lavender" + } + } +} + +enum HopeThemeRole: String, CaseIterable, Sendable { + case ground + case surface + case personaSurface + case ink + case quiet + case accent + case onAccent + case alert + case onAlert + case focus + case controlBorder + case separator + case selectedWash + case errorWash +} + +enum HopeTheme { + static let version = "1.0.0" + static func rgb(_ role: HopeThemeRole, palette: HopePalette = .defaultPalette, dark: Bool) -> HopeRGB { + switch palette { + case .opal: + switch role { + case .ground: return dark ? HopeRGB(0.12549019607843137, 0.09411764705882353, 0.12549019607843137) : HopeRGB(1, 0.9921568627450981, 0.9921568627450981) + case .surface: return dark ? HopeRGB(0.18823529411764706, 0.13725490196078433, 0.18823529411764706) : HopeRGB(1, 0.9686274509803922, 0.9803921568627451) + case .personaSurface: return dark ? HopeRGB(0.23137254901960785, 0.1568627450980392, 0.21176470588235294) : HopeRGB(1, 0.9490196078431372, 0.9686274509803922) + case .ink: return dark ? HopeRGB(1, 0.9490196078431372, 0.9686274509803922) : HopeRGB(0.18823529411764706, 0.16862745098039217, 0.25098039215686274) + case .quiet: return dark ? HopeRGB(0.796078431372549, 0.7137254901960784, 0.7803921568627451) : HopeRGB(0.4392156862745098, 0.3764705882352941, 0.4549019607843137) + case .accent: return dark ? HopeRGB(0.9411764705882353, 0.6588235294117647, 0.7843137254901961) : HopeRGB(0.6509803921568628, 0.25882352941176473, 0.4392156862745098) + case .onAccent: return dark ? HopeRGB(0.12549019607843137, 0.09411764705882353, 0.12549019607843137) : HopeRGB(1, 1, 1) + case .focus: return dark ? HopeRGB(0.9411764705882353, 0.6588235294117647, 0.7843137254901961) : HopeRGB(0.6509803921568628, 0.25882352941176473, 0.4392156862745098) + case .controlBorder: return dark ? HopeRGB(0.796078431372549, 0.7137254901960784, 0.7803921568627451) : HopeRGB(0.4392156862745098, 0.3764705882352941, 0.4549019607843137) + case .alert: return dark ? HopeRGB(1, 0.604, 0.675) : HopeRGB(0.675, 0.208, 0.282) + case .onAlert: return dark ? HopeRGB(0.12549019607843137, 0.09411764705882353, 0.12549019607843137) : HopeRGB(1, 1, 1) + case .separator: return dark ? HopeRGB(0.3018823529411765, 0.2509019607843137, 0.2974901960784314) : HopeRGB(0.8863529411764706, 0.8566274509803922, 0.8782745098039215) + case .selectedWash: return dark ? HopeRGB(0.2635294117647059, 0.1894117647058824, 0.24784313725490195) : HopeRGB(0.9650980392156863, 0.8976470588235295, 0.9262745098039216) + case .errorWash: return dark ? HopeRGB(0.2531764705882353, 0.17459450980392158, 0.2271764705882353) : HopeRGB(0.9740000000000001, 0.9077772549019608, 0.9245207843137255) + } + case .graphite: + switch role { + case .ground: return dark ? HopeRGB(0.09803921568627451, 0.10588235294117647, 0.12549019607843137) : HopeRGB(0.9921568627450981, 0.9921568627450981, 0.996078431372549) + case .surface: return dark ? HopeRGB(0.1450980392156863, 0.1568627450980392, 0.1843137254901961) : HopeRGB(0.9529411764705882, 0.9568627450980393, 0.9647058823529412) + case .personaSurface: return dark ? HopeRGB(0.18823529411764706, 0.20392156862745098, 0.23921568627450981) : HopeRGB(0.9294117647058824, 0.9333333333333333, 0.9490196078431372) + case .ink: return dark ? HopeRGB(0.9529411764705882, 0.9568627450980393, 0.9686274509803922) : HopeRGB(0.15294117647058825, 0.16470588235294117, 0.2) + case .quiet: return dark ? HopeRGB(0.7372549019607844, 0.7568627450980392, 0.803921568627451) : HopeRGB(0.3686274509803922, 0.3843137254901961, 0.43137254901960786) + case .accent: return dark ? HopeRGB(0.7490196078431373, 0.788235294117647, 0.8627450980392157) : HopeRGB(0.3137254901960784, 0.34901960784313724, 0.4117647058823529) + case .onAccent: return dark ? HopeRGB(0.09803921568627451, 0.10588235294117647, 0.12549019607843137) : HopeRGB(1, 1, 1) + case .focus: return dark ? HopeRGB(0.7490196078431373, 0.788235294117647, 0.8627450980392157) : HopeRGB(0.3137254901960784, 0.34901960784313724, 0.4117647058823529) + case .controlBorder: return dark ? HopeRGB(0.7372549019607844, 0.7568627450980392, 0.803921568627451) : HopeRGB(0.3686274509803922, 0.3843137254901961, 0.43137254901960786) + case .alert: return dark ? HopeRGB(1, 0.604, 0.675) : HopeRGB(0.675, 0.208, 0.282) + case .onAlert: return dark ? HopeRGB(0.09803921568627451, 0.10588235294117647, 0.12549019607843137) : HopeRGB(1, 1, 1) + case .separator: return dark ? HopeRGB(0.2581960784313726, 0.2688627450980392, 0.29411764705882354) : HopeRGB(0.8409411764705882, 0.8459607843137256, 0.8576470588235294) + case .selectedWash: return dark ? HopeRGB(0.2054901960784314, 0.22, 0.2521568627450981) : HopeRGB(0.8890196078431373, 0.8960784313725491, 0.9094117647058824) + case .errorWash: return dark ? HopeRGB(0.2134901960784314, 0.19263372549019608, 0.22356862745098044) : HopeRGB(0.9307058823529412, 0.8969537254901961, 0.9100894117647059) + } + case .ocean: + switch role { + case .ground: return dark ? HopeRGB(0.08235294117647059, 0.12941176470588237, 0.16862745098039217) : HopeRGB(0.9882352941176471, 0.996078431372549, 1) + case .surface: return dark ? HopeRGB(0.12549019607843137, 0.19215686274509805, 0.24705882352941178) : HopeRGB(0.9411764705882353, 0.9686274509803922, 0.984313725490196) + case .personaSurface: return dark ? HopeRGB(0.1568627450980392, 0.23921568627450981, 0.30196078431372547) : HopeRGB(0.9058823529411765, 0.9490196078431372, 0.9764705882352941) + case .ink: return dark ? HopeRGB(0.9294117647058824, 0.9686274509803922, 1) : HopeRGB(0.13725490196078433, 0.20392156862745098, 0.25882352941176473) + case .quiet: return dark ? HopeRGB(0.7019607843137254, 0.796078431372549, 0.8705882352941177) : HopeRGB(0.3254901960784314, 0.40784313725490196, 0.47058823529411764) + case .accent: return dark ? HopeRGB(0.592156862745098, 0.807843137254902, 0.9333333333333333) : HopeRGB(0.15294117647058825, 0.396078431372549, 0.5490196078431373) + case .onAccent: return dark ? HopeRGB(0.08235294117647059, 0.12941176470588237, 0.16862745098039217) : HopeRGB(1, 1, 1) + case .focus: return dark ? HopeRGB(0.592156862745098, 0.807843137254902, 0.9333333333333333) : HopeRGB(0.15294117647058825, 0.396078431372549, 0.5490196078431373) + case .controlBorder: return dark ? HopeRGB(0.7019607843137254, 0.796078431372549, 0.8705882352941177) : HopeRGB(0.3254901960784314, 0.40784313725490196, 0.47058823529411764) + case .alert: return dark ? HopeRGB(1, 0.604, 0.675) : HopeRGB(0.675, 0.208, 0.282) + case .onAlert: return dark ? HopeRGB(0.08235294117647059, 0.12941176470588237, 0.16862745098039217) : HopeRGB(1, 1, 1) + case .separator: return dark ? HopeRGB(0.2380392156862745, 0.30086274509803923, 0.35247058823529415) : HopeRGB(0.828627450980392, 0.8615686274509804, 0.8827450980392156) + case .selectedWash: return dark ? HopeRGB(0.17215686274509806, 0.25372549019607843, 0.31568627450980397) : HopeRGB(0.8623529411764705, 0.9113725490196078, 0.9407843137254902) + case .errorWash: return dark ? HopeRGB(0.19545098039215686, 0.22510431372549022, 0.28129411764705886) : HopeRGB(0.9198823529411766, 0.9077772549019608, 0.9281286274509805) + } + case .sage: + switch role { + case .ground: return dark ? HopeRGB(0.09803921568627451, 0.13725490196078433, 0.10980392156862745) : HopeRGB(0.9882352941176471, 0.996078431372549, 0.9882352941176471) + case .surface: return dark ? HopeRGB(0.14901960784313725, 0.20392156862745098, 0.16470588235294117) : HopeRGB(0.9411764705882353, 0.9647058823529412, 0.9450980392156862) + case .personaSurface: return dark ? HopeRGB(0.18823529411764706, 0.25882352941176473, 0.21176470588235294) : HopeRGB(0.9058823529411765, 0.9411764705882353, 0.9137254901960784) + case .ink: return dark ? HopeRGB(0.9411764705882353, 0.9725490196078431, 0.9450980392156862) : HopeRGB(0.1607843137254902, 0.2196078431372549, 0.17647058823529413) + case .quiet: return dark ? HopeRGB(0.7254901960784313, 0.8117647058823529, 0.7450980392156863) : HopeRGB(0.34901960784313724, 0.4196078431372549, 0.36470588235294116) + case .accent: return dark ? HopeRGB(0.6470588235294118, 0.8313725490196079, 0.6862745098039216) : HopeRGB(0.23529411764705882, 0.4392156862745098, 0.30196078431372547) + case .onAccent: return dark ? HopeRGB(0.09803921568627451, 0.13725490196078433, 0.10980392156862745) : HopeRGB(1, 1, 1) + case .focus: return dark ? HopeRGB(0.6470588235294118, 0.8313725490196079, 0.6862745098039216) : HopeRGB(0.23529411764705882, 0.4392156862745098, 0.30196078431372547) + case .controlBorder: return dark ? HopeRGB(0.7254901960784313, 0.8117647058823529, 0.7450980392156863) : HopeRGB(0.34901960784313724, 0.4196078431372549, 0.36470588235294116) + case .alert: return dark ? HopeRGB(1, 0.604, 0.675) : HopeRGB(0.675, 0.208, 0.282) + case .onAlert: return dark ? HopeRGB(0.09803921568627451, 0.13725490196078433, 0.10980392156862745) : HopeRGB(1, 1, 1) + case .separator: return dark ? HopeRGB(0.25992156862745097, 0.3115294117647059, 0.2739607843137255) : HopeRGB(0.8319215686274509, 0.8603921568627451, 0.8374901960784313) + case .selectedWash: return dark ? HopeRGB(0.19882352941176473, 0.26666666666666666, 0.2168627450980392) : HopeRGB(0.8705882352941177, 0.9121568627450981, 0.8807843137254902) + case .errorWash: return dark ? HopeRGB(0.21709803921568627, 0.2359278431372549, 0.2055294117647059) : HopeRGB(0.9198823529411766, 0.9041694117647059, 0.8920501960784314) + } + case .lavender: + switch role { + case .ground: return dark ? HopeRGB(0.12549019607843137, 0.10588235294117647, 0.1607843137254902) : HopeRGB(0.996078431372549, 0.9921568627450981, 1) + case .surface: return dark ? HopeRGB(0.1803921568627451, 0.15294117647058825, 0.23529411764705882) : HopeRGB(0.9647058823529412, 0.9490196078431372, 0.9882352941176471) + case .personaSurface: return dark ? HopeRGB(0.23137254901960785, 0.19607843137254902, 0.30196078431372547) : HopeRGB(0.9333333333333333, 0.9098039215686274, 0.9725490196078431) + case .ink: return dark ? HopeRGB(0.9686274509803922, 0.9450980392156862, 1) : HopeRGB(0.20392156862745098, 0.17647058823529413, 0.2627450980392157) + case .quiet: return dark ? HopeRGB(0.7843137254901961, 0.7294117647058823, 0.8745098039215686) : HopeRGB(0.40784313725490196, 0.3686274509803922, 0.4823529411764706) + case .accent: return dark ? HopeRGB(0.8117647058823529, 0.7058823529411765, 0.9568627450980393) : HopeRGB(0.4392156862745098, 0.3215686274509804, 0.611764705882353) + case .onAccent: return dark ? HopeRGB(0.12549019607843137, 0.10588235294117647, 0.1607843137254902) : HopeRGB(1, 1, 1) + case .focus: return dark ? HopeRGB(0.8117647058823529, 0.7058823529411765, 0.9568627450980393) : HopeRGB(0.4392156862745098, 0.3215686274509804, 0.611764705882353) + case .controlBorder: return dark ? HopeRGB(0.7843137254901961, 0.7294117647058823, 0.8745098039215686) : HopeRGB(0.40784313725490196, 0.3686274509803922, 0.4823529411764706) + case .alert: return dark ? HopeRGB(1, 0.604, 0.675) : HopeRGB(0.675, 0.208, 0.282) + case .onAlert: return dark ? HopeRGB(0.12549019607843137, 0.10588235294117647, 0.1607843137254902) : HopeRGB(1, 1, 1) + case .separator: return dark ? HopeRGB(0.29074509803921567, 0.263843137254902, 0.3423529411764706) : HopeRGB(0.8581960784313726, 0.8408627450980392, 0.8866666666666667) + case .selectedWash: return dark ? HopeRGB(0.24352941176470588, 0.20823529411764707, 0.3074509803921569) : HopeRGB(0.9121568627450981, 0.8862745098039215, 0.9505882352941177) + case .errorWash: return dark ? HopeRGB(0.24596078431372548, 0.18902588235294118, 0.27047058823529413) : HopeRGB(0.941529411764706, 0.8897380392156863, 0.9317364705882354) + } + } + } + + static func color(_ role: HopeThemeRole, palette: HopePalette = .defaultPalette, + appearance: HopeAppearance = .system) -> Color { + let light = rgb(role, palette: palette, dark: false).uiColor + let dark = rgb(role, palette: palette, dark: true).uiColor + switch appearance { + case .light: return Color(uiColor: light) + case .dark: return Color(uiColor: dark) + case .system: + return Color(uiColor: UIColor { traits in + traits.userInterfaceStyle == .dark ? dark : light + }) + } + } +} diff --git a/scripts/generate_hope_themes.py b/scripts/generate_hope_themes.py new file mode 100644 index 0000000..acf5d96 --- /dev/null +++ b/scripts/generate_hope_themes.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python3 +"""Generate deterministic web/Swift theme artifacts; no third-party dependencies.""" +from __future__ import annotations + +import argparse +import hashlib +import json +import re +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +SPEC = ROOT / "design/hope-palette.v1.json" +OUTPUTS = {"css": ROOT / "web/hope-themes.css", "swift": ROOT / "generated/HopeTheme.generated.swift"} + + +def rgb(value): + """Normalized sRGB; decimal channels preserve the native alert exactly.""" + if isinstance(value, str) and re.fullmatch(r"#[0-9A-F]{6}", value): + return tuple(int(value[i:i + 2], 16) / 255 for i in (1, 3, 5)) + if isinstance(value, list) and len(value) == 3 and all( + isinstance(c, (int, float)) and not isinstance(c, bool) and 0 <= c <= 1 for c in value + ): + return tuple(value) + raise ValueError(f"Invalid opaque sRGB value: {value!r}") + + +def resolve(spec, palette, appearance): + colors = {role: rgb(value) for role, value in spec["palettes"][palette][appearance].items()} + for role, reference in spec["sharedRoles"][appearance].items(): + colors[role] = colors[reference[1:]] if reference.startswith("$") else rgb(spec["primitives"][reference]) + for role, derivation in spec["derivedRoles"].items(): + fg, bg, alpha = colors[derivation["foreground"]], colors[derivation["background"]], derivation["alpha"] + colors[role] = tuple(f * alpha + b * (1 - alpha) for f, b in zip(fg, bg)) + return colors + + +def luminance(color): + channels = [c / 12.92 if c <= 0.04045 else ((c + 0.055) / 1.055) ** 2.4 for c in color] + return sum(c * weight for c, weight in zip(channels, (0.2126, 0.7152, 0.0722))) + + +def contrast(foreground, background): + a, b = sorted((luminance(foreground), luminance(background))) + return (b + 0.05) / (a + 0.05) + + +def contrast_checks(spec): + results = [] + for palette in spec["palettes"]: + for appearance in ("light", "dark"): + colors = resolve(spec, palette, appearance) + pairs = [(fg, bg, 4.5) for fg in ("ink", "quiet", "accent", "alert") + for bg in ("ground", "surface", "personaSurface")] + pairs += [("ink", "selectedWash", 4.5), ("quiet", "selectedWash", 4.5), + ("alert", "errorWash", 4.5), ("onAccent", "accent", 4.5), + ("onAlert", "alert", 4.5)] + pairs += [(fg, bg, 3.0) for fg in ("focus", "controlBorder") + for bg in ("ground", "surface", "personaSurface")] + for fg, bg, minimum in pairs: + results.append({"palette": palette, "appearance": appearance, "foreground": fg, + "background": bg, "ratio": contrast(colors[fg], colors[bg]), "minimum": minimum}) + return results + + +def validate(spec): + if spec["schemaVersion"] != 1 or not re.fullmatch(r"\d+\.\d+\.\d+", spec["version"]): + raise ValueError("Unsupported schema or invalid semantic version") + if spec["defaultPalette"] not in spec["palettes"] or spec["defaultAppearance"] != "system": + raise ValueError("Invalid theme defaults") + required = set(spec["roles"]) + for palette, data in spec["palettes"].items(): + if not re.fullmatch(r"[a-z][a-z0-9-]*", palette) or not data["label"]: + raise ValueError("Invalid palette identifier/label") + for appearance in ("light", "dark"): + if set(data[appearance]) | set(spec["sharedRoles"][appearance]) != required: + raise ValueError(f"Incomplete semantic roles: {palette}/{appearance}") + resolve(spec, palette, appearance) + for derivation in spec["derivedRoles"].values(): + if not 0 <= derivation["alpha"] <= 1: + raise ValueError("Invalid derived opacity") + if not set(spec["components"].values()) <= required | set(spec["derivedRoles"]): + raise ValueError("Unknown component role") + failures = [row for row in contrast_checks(spec) if row["ratio"] < row["minimum"]] + if failures: + raise ValueError("Contrast verification failed: " + json.dumps(failures)) + + +def kebab(value): + return re.sub(r"[A-Z]", lambda match: "-" + match[0].lower(), value) + + +def number(value): + return repr(float(value)) if not float(value).is_integer() else str(int(value)) + + +def css_color(color): + return "rgb(" + " ".join(number(c * 255) for c in color) + ")" + + +def css_block(spec, palette, appearance, selector): + lines = [selector + " {", f" color-scheme: {appearance};"] + for role, color in resolve(spec, palette, appearance).items(): + lines.append(f" --hope-primitive-{kebab(role)}: {css_color(color)};") + lines.append(f" --hope-{kebab(role)}: var(--hope-primitive-{kebab(role)});") + # Opal's native API is retained as an alias, not a new meaning for other palettes. + lines += [" --hope-rosewood: var(--hope-accent);", " --hope-on-rosewood: var(--hope-on-accent);"] + for component, role in spec["components"].items(): + lines.append(f" --hope-{kebab(component)}: var(--hope-{kebab(role)});") + return "\n".join(lines + ["}"]) + + +def render_css(spec, digest): + chunks = [f"/* Generated from design/hope-palette.v1.json v{spec['version']}; SHA256 {digest}.\n * DO NOT EDIT. python3 scripts/generate_hope_themes.py\n * Set data-palette and data-appearance on ; missing/unknown values fall back to Opal/system.\n */"] + default = spec["defaultPalette"] + # Root is also the fallback for stale/unknown persisted palette or appearance IDs. + chunks.append(css_block(spec, default, "light", ":root")) + for palette in spec["palettes"]: + chunks.append(css_block(spec, palette, "light", f':root[data-palette="{palette}"]')) + system_blocks = [css_block(spec, default, "dark", ':root:not([data-appearance="light"]):not([data-appearance="dark"])')] + for palette in spec["palettes"]: + system_blocks.append(css_block(spec, palette, "dark", f':root[data-palette="{palette}"]:not([data-appearance="light"]):not([data-appearance="dark"])')) + chunks.append("@media (prefers-color-scheme: dark) {\n" + "\n".join(system_blocks) + "\n}") + for appearance in ("light", "dark"): + chunks.append(css_block(spec, default, appearance, f':root[data-appearance="{appearance}"]')) + for palette in spec["palettes"]: + chunks.append(css_block(spec, palette, appearance, f':root[data-palette="{palette}"][data-appearance="{appearance}"]')) + return "\n\n".join(chunks) + "\n" + + +def swift_color(color): + return "HopeRGB(" + ", ".join(number(c) for c in color) + ")" + + +def render_swift(spec, digest): + lines = [f"// Generated from design/hope-palette.v1.json v{spec['version']}; SHA256 {digest}.", + "// DO NOT EDIT. python3 scripts/generate_hope_themes.py", "import SwiftUI", "import UIKit", "", + "struct HopeRGB: Sendable {", " let red: Double", " let green: Double", " let blue: Double", + " init(_ red: Double, _ green: Double, _ blue: Double) {", + " self.red = red; self.green = green; self.blue = blue", " }", + " var uiColor: UIColor { UIColor(red: CGFloat(red), green: CGFloat(green), blue: CGFloat(blue), alpha: 1) }", "}", "", + "enum HopeAppearance: String, CaseIterable, Sendable {", " case system, light, dark", "}", "", + "enum HopePalette: String, CaseIterable, Sendable {"] + for palette in spec["palettes"]: + lines.append(f" case {palette}") + lines += [f" static let defaultPalette: HopePalette = .{spec['defaultPalette']}", " var label: String {", " switch self {"] + for palette, data in spec["palettes"].items(): + lines.append(f" case .{palette}: return {json.dumps(data['label'])}") + lines += [" }", " }", "}", "", "enum HopeThemeRole: String, CaseIterable, Sendable {"] + for role in list(spec["roles"]) + list(spec["derivedRoles"]): + lines.append(f" case {role}") + lines += ["}", "", "enum HopeTheme {", f" static let version = {json.dumps(spec['version'])}", + " static func rgb(_ role: HopeThemeRole, palette: HopePalette = .defaultPalette, dark: Bool) -> HopeRGB {", " switch palette {"] + for palette in spec["palettes"]: + lines += [f" case .{palette}:", " switch role {"] + light, dark = resolve(spec, palette, "light"), resolve(spec, palette, "dark") + for role in light: + lines.append(f" case .{role}: return dark ? {swift_color(dark[role])} : {swift_color(light[role])}") + lines += [" }"] + lines += [" }", " }", "", " static func color(_ role: HopeThemeRole, palette: HopePalette = .defaultPalette,", " appearance: HopeAppearance = .system) -> Color {", + " let light = rgb(role, palette: palette, dark: false).uiColor", + " let dark = rgb(role, palette: palette, dark: true).uiColor", + " switch appearance {", " case .light: return Color(uiColor: light)", " case .dark: return Color(uiColor: dark)", + " case .system:", " return Color(uiColor: UIColor { traits in", " traits.userInterfaceStyle == .dark ? dark : light", " })", " }", " }", "}"] + return "\n".join(lines) + "\n" + + +def artifacts(spec, source_bytes): + validate(spec) + digest = hashlib.sha256(source_bytes).hexdigest() + return {"css": render_css(spec, digest), "swift": render_swift(spec, digest)} + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--check", action="store_true", help="Check committed outputs without rewriting") + parser.add_argument("--contrast", action="store_true", help="Print every verified contrast pair") + args = parser.parse_args() + source = SPEC.read_bytes() + spec = json.loads(source) + outputs = artifacts(spec, source) + if args.contrast: + for row in contrast_checks(spec): + print(f"{row['palette']}/{row['appearance']} {row['foreground']}/{row['background']}: {row['ratio']:.3f}:1 >= {row['minimum']}:1") + stale = [] + for key, path in OUTPUTS.items(): + if args.check: + if not path.exists() or path.read_text() != outputs[key]: + stale.append(str(path.relative_to(ROOT))) + else: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(outputs[key]) + if stale: + parser.exit(1, "Stale theme artifacts: " + ", ".join(stale) + "\n") + minimum = min(row['ratio'] for row in contrast_checks(spec) if row['minimum'] == 4.5) + print(f"Hope themes v{spec['version']}: {len(spec['palettes'])} palettes, light/dark; minimum text contrast {minimum:.3f}:1; artifacts {'verified' if args.check else 'generated'}.") + + +if __name__ == "__main__": + main() diff --git a/tests/unit/test_hope_themes.py b/tests/unit/test_hope_themes.py new file mode 100644 index 0000000..69260fb --- /dev/null +++ b/tests/unit/test_hope_themes.py @@ -0,0 +1,117 @@ +"""Offline token contract, native identity, generator drift and browser cascade tests.""" +import copy +import importlib.util +import json +from pathlib import Path +import unittest + +ROOT = Path(__file__).resolve().parents[2] +module_spec = importlib.util.spec_from_file_location("hope_theme_generator", ROOT / "scripts/generate_hope_themes.py") +generator = importlib.util.module_from_spec(module_spec) +module_spec.loader.exec_module(generator) + + +class HopeThemeTests(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.source = generator.SPEC.read_bytes() + cls.spec = json.loads(cls.source) + + def test_contract_defaults_and_identifiers(self): + self.assertEqual(self.spec["defaultPalette"], "opal") + self.assertEqual(self.spec["defaultAppearance"], "system") + self.assertEqual(list(self.spec["palettes"]), ["opal", "graphite", "ocean", "sage", "lavender"]) + generator.validate(self.spec) + + def test_opal_exact_native_identity(self): + # Fixed from voice-ios/Sources/App/Nearlight.swift; no external checkout needed in CI. + expected = { + "light": ["#FFFDFD", "#FFF7FA", "#FFF2F7", "#302B40", "#706074", "#A64270", "#FFFFFF"], + "dark": ["#201820", "#302330", "#3B2836", "#FFF2F7", "#CBB6C7", "#F0A8C8", "#201820"], + } + for appearance, values in expected.items(): + for role, value in zip(("ground", "surface", "personaSurface", "ink", "quiet", "accent", "onAccent"), values): + self.assertEqual(self.spec["palettes"]["opal"][appearance][role], value) + self.assertEqual(self.spec["primitives"]["alertLight"], [0.675, 0.208, 0.282]) + self.assertEqual(self.spec["primitives"]["alertDark"], [1.0, 0.604, 0.675]) + + def test_all_required_contrast_pairs(self): + checks = generator.contrast_checks(self.spec) + self.assertEqual(len(checks), 230) + for row in checks: + with self.subTest(**row): + self.assertGreaterEqual(row["ratio"], row["minimum"]) + self.assertAlmostEqual(generator.contrast((1, 1, 1), (0, 0, 0)), 21) + + def test_bad_contrast_rejected(self): + changed = copy.deepcopy(self.spec) + changed["palettes"]["opal"]["light"]["quiet"] = "#FFFDFD" + with self.assertRaisesRegex(ValueError, "Contrast verification failed"): + generator.validate(changed) + + def test_missing_role_rejected(self): + changed = copy.deepcopy(self.spec) + del changed["palettes"]["ocean"]["dark"]["focus"] + with self.assertRaisesRegex(ValueError, "Incomplete semantic roles"): + generator.validate(changed) + + def test_invalid_primitive_rejected(self): + for value in ("#abc", [1.1, 0, 0], [True, 0, 0], "transparent"): + with self.subTest(value=value), self.assertRaises(ValueError): + generator.rgb(value) + + def test_alert_is_not_palette_accent(self): + for appearance in ("light", "dark"): + alerts = {generator.resolve(self.spec, palette, appearance)["alert"] for palette in self.spec["palettes"]} + self.assertEqual(len(alerts), 1) + + def test_derived_roles_are_opaque_surface_composites(self): + colors = generator.resolve(self.spec, "opal", "light") + for role, fg, alpha in (("separator", "ink", .14), ("selectedWash", "accent", .10), ("errorWash", "alert", .08)): + self.assertEqual(colors[role], tuple(f * alpha + b * (1 - alpha) for f, b in zip(colors[fg], colors["surface"]))) + + def test_generated_artifacts_are_current_and_deterministic(self): + first = generator.artifacts(self.spec, self.source) + self.assertEqual(first, generator.artifacts(self.spec, self.source)) + for key, path in generator.OUTPUTS.items(): + self.assertEqual(path.read_text(), first[key], str(path)) + + def test_swift_channels_round_trip(self): + for palette in self.spec["palettes"]: + for appearance in ("light", "dark"): + for color in generator.resolve(self.spec, palette, appearance).values(): + self.assertEqual(tuple(float(generator.number(c)) for c in color), color) + + +class HopeThemeBrowserTests(unittest.TestCase): + def test_browser_palette_appearance_cascade(self): + try: + from playwright.sync_api import sync_playwright + except ImportError: + self.skipTest("Optional Playwright unavailable; install it to verify browser cascade") + spec = json.loads(generator.SPEC.read_bytes()) + with sync_playwright() as playwright: + browser = playwright.chromium.launch(headless=True) + try: + page = browser.new_page() + page.set_content('Hope') + page.add_style_tag(content=generator.OUTPUTS["css"].read_text()) + page.add_style_tag(content='#probe { color: var(--hope-ink); background-color: var(--hope-ground); }') + for system in ("light", "dark"): + page.emulate_media(color_scheme=system) + for palette in [None, "obsolete"] + list(spec["palettes"]): + for appearance in (None, "obsolete", "system", "light", "dark"): + page.evaluate('([p, a]) => { for (const [key, value] of [["palette", p], ["appearance", a]]) { if (value === null) delete document.documentElement.dataset[key]; else document.documentElement.dataset[key] = value; } }', [palette, appearance]) + actual = page.locator("#probe").evaluate('(el) => { const s = getComputedStyle(el); return [s.color, s.backgroundColor]; }') + resolved_palette = palette if palette in spec["palettes"] else "opal" + resolved_appearance = appearance if appearance in ("light", "dark") else system + colors = generator.resolve(spec, resolved_palette, resolved_appearance) + expected = ["rgb(" + ", ".join(str(round(c * 255)) for c in colors[role]) + ")" for role in ("ink", "ground")] + with self.subTest(system=system, palette=palette, appearance=appearance): + self.assertEqual(actual, expected) + finally: + browser.close() + + +if __name__ == "__main__": + unittest.main() diff --git a/web/hope-themes.css b/web/hope-themes.css new file mode 100644 index 0000000..2ec0abc --- /dev/null +++ b/web/hope-themes.css @@ -0,0 +1,1057 @@ +/* Generated from design/hope-palette.v1.json v1.0.0; SHA256 bb76134be477916d776cda4259f45096fc2dcfcf873918b8703a0b716391a350. + * DO NOT EDIT. python3 scripts/generate_hope_themes.py + * Set data-palette and data-appearance on ; missing/unknown values fall back to Opal/system. + */ + +:root { + color-scheme: light; + --hope-primitive-ground: rgb(255 253 253); + --hope-ground: var(--hope-primitive-ground); + --hope-primitive-surface: rgb(255 247 250); + --hope-surface: var(--hope-primitive-surface); + --hope-primitive-persona-surface: rgb(255 242 247); + --hope-persona-surface: var(--hope-primitive-persona-surface); + --hope-primitive-ink: rgb(48 43 64); + --hope-ink: var(--hope-primitive-ink); + --hope-primitive-quiet: rgb(112 96 116); + --hope-quiet: var(--hope-primitive-quiet); + --hope-primitive-accent: rgb(166 66 112); + --hope-accent: var(--hope-primitive-accent); + --hope-primitive-on-accent: rgb(255 255 255); + --hope-on-accent: var(--hope-primitive-on-accent); + --hope-primitive-focus: rgb(166 66 112); + --hope-focus: var(--hope-primitive-focus); + --hope-primitive-control-border: rgb(112 96 116); + --hope-control-border: var(--hope-primitive-control-border); + --hope-primitive-alert: rgb(172.125 53.04 71.91); + --hope-alert: var(--hope-primitive-alert); + --hope-primitive-on-alert: rgb(255 255 255); + --hope-on-alert: var(--hope-primitive-on-alert); + --hope-primitive-separator: rgb(226.01999999999998 218.44 223.95999999999998); + --hope-separator: var(--hope-primitive-separator); + --hope-primitive-selected-wash: rgb(246.1 228.9 236.2); + --hope-selected-wash: var(--hope-primitive-selected-wash); + --hope-primitive-error-wash: rgb(248.37000000000003 231.4832 235.7528); + --hope-error-wash: var(--hope-primitive-error-wash); + --hope-rosewood: var(--hope-accent); + --hope-on-rosewood: var(--hope-on-accent); + --hope-action-background: var(--hope-accent); + --hope-action-foreground: var(--hope-on-accent); + --hope-input-background: var(--hope-surface); + --hope-input-foreground: var(--hope-ink); + --hope-input-placeholder: var(--hope-quiet); + --hope-input-border: var(--hope-control-border); + --hope-input-focus: var(--hope-focus); + --hope-persona-background: var(--hope-persona-surface); + --hope-error-background: var(--hope-error-wash); + --hope-error-foreground: var(--hope-alert); +} + +:root[data-palette="opal"] { + color-scheme: light; + --hope-primitive-ground: rgb(255 253 253); + --hope-ground: var(--hope-primitive-ground); + --hope-primitive-surface: rgb(255 247 250); + --hope-surface: var(--hope-primitive-surface); + --hope-primitive-persona-surface: rgb(255 242 247); + --hope-persona-surface: var(--hope-primitive-persona-surface); + --hope-primitive-ink: rgb(48 43 64); + --hope-ink: var(--hope-primitive-ink); + --hope-primitive-quiet: rgb(112 96 116); + --hope-quiet: var(--hope-primitive-quiet); + --hope-primitive-accent: rgb(166 66 112); + --hope-accent: var(--hope-primitive-accent); + --hope-primitive-on-accent: rgb(255 255 255); + --hope-on-accent: var(--hope-primitive-on-accent); + --hope-primitive-focus: rgb(166 66 112); + --hope-focus: var(--hope-primitive-focus); + --hope-primitive-control-border: rgb(112 96 116); + --hope-control-border: var(--hope-primitive-control-border); + --hope-primitive-alert: rgb(172.125 53.04 71.91); + --hope-alert: var(--hope-primitive-alert); + --hope-primitive-on-alert: rgb(255 255 255); + --hope-on-alert: var(--hope-primitive-on-alert); + --hope-primitive-separator: rgb(226.01999999999998 218.44 223.95999999999998); + --hope-separator: var(--hope-primitive-separator); + --hope-primitive-selected-wash: rgb(246.1 228.9 236.2); + --hope-selected-wash: var(--hope-primitive-selected-wash); + --hope-primitive-error-wash: rgb(248.37000000000003 231.4832 235.7528); + --hope-error-wash: var(--hope-primitive-error-wash); + --hope-rosewood: var(--hope-accent); + --hope-on-rosewood: var(--hope-on-accent); + --hope-action-background: var(--hope-accent); + --hope-action-foreground: var(--hope-on-accent); + --hope-input-background: var(--hope-surface); + --hope-input-foreground: var(--hope-ink); + --hope-input-placeholder: var(--hope-quiet); + --hope-input-border: var(--hope-control-border); + --hope-input-focus: var(--hope-focus); + --hope-persona-background: var(--hope-persona-surface); + --hope-error-background: var(--hope-error-wash); + --hope-error-foreground: var(--hope-alert); +} + +:root[data-palette="graphite"] { + color-scheme: light; + --hope-primitive-ground: rgb(253 253 254); + --hope-ground: var(--hope-primitive-ground); + --hope-primitive-surface: rgb(243 244 246); + --hope-surface: var(--hope-primitive-surface); + --hope-primitive-persona-surface: rgb(237 238 242); + --hope-persona-surface: var(--hope-primitive-persona-surface); + --hope-primitive-ink: rgb(39 42 51); + --hope-ink: var(--hope-primitive-ink); + --hope-primitive-quiet: rgb(94 98 110); + --hope-quiet: var(--hope-primitive-quiet); + --hope-primitive-accent: rgb(80 89 105); + --hope-accent: var(--hope-primitive-accent); + --hope-primitive-on-accent: rgb(255 255 255); + --hope-on-accent: var(--hope-primitive-on-accent); + --hope-primitive-focus: rgb(80 89 105); + --hope-focus: var(--hope-primitive-focus); + --hope-primitive-control-border: rgb(94 98 110); + --hope-control-border: var(--hope-primitive-control-border); + --hope-primitive-alert: rgb(172.125 53.04 71.91); + --hope-alert: var(--hope-primitive-alert); + --hope-primitive-on-alert: rgb(255 255 255); + --hope-on-alert: var(--hope-primitive-on-alert); + --hope-primitive-separator: rgb(214.44 215.72000000000003 218.70000000000002); + --hope-separator: var(--hope-primitive-separator); + --hope-primitive-selected-wash: rgb(226.70000000000002 228.5 231.9); + --hope-selected-wash: var(--hope-primitive-selected-wash); + --hope-primitive-error-wash: rgb(237.32999999999998 228.72320000000002 232.0728); + --hope-error-wash: var(--hope-primitive-error-wash); + --hope-rosewood: var(--hope-accent); + --hope-on-rosewood: var(--hope-on-accent); + --hope-action-background: var(--hope-accent); + --hope-action-foreground: var(--hope-on-accent); + --hope-input-background: var(--hope-surface); + --hope-input-foreground: var(--hope-ink); + --hope-input-placeholder: var(--hope-quiet); + --hope-input-border: var(--hope-control-border); + --hope-input-focus: var(--hope-focus); + --hope-persona-background: var(--hope-persona-surface); + --hope-error-background: var(--hope-error-wash); + --hope-error-foreground: var(--hope-alert); +} + +:root[data-palette="ocean"] { + color-scheme: light; + --hope-primitive-ground: rgb(252 254 255); + --hope-ground: var(--hope-primitive-ground); + --hope-primitive-surface: rgb(240 247 251); + --hope-surface: var(--hope-primitive-surface); + --hope-primitive-persona-surface: rgb(231 242 249); + --hope-persona-surface: var(--hope-primitive-persona-surface); + --hope-primitive-ink: rgb(35 52 66); + --hope-ink: var(--hope-primitive-ink); + --hope-primitive-quiet: rgb(83 104 120); + --hope-quiet: var(--hope-primitive-quiet); + --hope-primitive-accent: rgb(39 101 140); + --hope-accent: var(--hope-primitive-accent); + --hope-primitive-on-accent: rgb(255 255 255); + --hope-on-accent: var(--hope-primitive-on-accent); + --hope-primitive-focus: rgb(39 101 140); + --hope-focus: var(--hope-primitive-focus); + --hope-primitive-control-border: rgb(83 104 120); + --hope-control-border: var(--hope-primitive-control-border); + --hope-primitive-alert: rgb(172.125 53.04 71.91); + --hope-alert: var(--hope-primitive-alert); + --hope-primitive-on-alert: rgb(255 255 255); + --hope-on-alert: var(--hope-primitive-on-alert); + --hope-primitive-separator: rgb(211.29999999999995 219.7 225.1); + --hope-separator: var(--hope-primitive-separator); + --hope-primitive-selected-wash: rgb(219.89999999999998 232.4 239.9); + --hope-selected-wash: var(--hope-primitive-selected-wash); + --hope-primitive-error-wash: rgb(234.57000000000002 231.4832 236.67280000000002); + --hope-error-wash: var(--hope-primitive-error-wash); + --hope-rosewood: var(--hope-accent); + --hope-on-rosewood: var(--hope-on-accent); + --hope-action-background: var(--hope-accent); + --hope-action-foreground: var(--hope-on-accent); + --hope-input-background: var(--hope-surface); + --hope-input-foreground: var(--hope-ink); + --hope-input-placeholder: var(--hope-quiet); + --hope-input-border: var(--hope-control-border); + --hope-input-focus: var(--hope-focus); + --hope-persona-background: var(--hope-persona-surface); + --hope-error-background: var(--hope-error-wash); + --hope-error-foreground: var(--hope-alert); +} + +:root[data-palette="sage"] { + color-scheme: light; + --hope-primitive-ground: rgb(252 254 252); + --hope-ground: var(--hope-primitive-ground); + --hope-primitive-surface: rgb(240 246 241); + --hope-surface: var(--hope-primitive-surface); + --hope-primitive-persona-surface: rgb(231 240 233); + --hope-persona-surface: var(--hope-primitive-persona-surface); + --hope-primitive-ink: rgb(41 56 45); + --hope-ink: var(--hope-primitive-ink); + --hope-primitive-quiet: rgb(89 107 93); + --hope-quiet: var(--hope-primitive-quiet); + --hope-primitive-accent: rgb(60 112 77); + --hope-accent: var(--hope-primitive-accent); + --hope-primitive-on-accent: rgb(255 255 255); + --hope-on-accent: var(--hope-primitive-on-accent); + --hope-primitive-focus: rgb(60 112 77); + --hope-focus: var(--hope-primitive-focus); + --hope-primitive-control-border: rgb(89 107 93); + --hope-control-border: var(--hope-primitive-control-border); + --hope-primitive-alert: rgb(172.125 53.04 71.91); + --hope-alert: var(--hope-primitive-alert); + --hope-primitive-on-alert: rgb(255 255 255); + --hope-on-alert: var(--hope-primitive-on-alert); + --hope-primitive-separator: rgb(212.14 219.39999999999998 213.56); + --hope-separator: var(--hope-primitive-separator); + --hope-primitive-selected-wash: rgb(222 232.60000000000002 224.6); + --hope-selected-wash: var(--hope-primitive-selected-wash); + --hope-primitive-error-wash: rgb(234.57000000000002 230.5632 227.4728); + --hope-error-wash: var(--hope-primitive-error-wash); + --hope-rosewood: var(--hope-accent); + --hope-on-rosewood: var(--hope-on-accent); + --hope-action-background: var(--hope-accent); + --hope-action-foreground: var(--hope-on-accent); + --hope-input-background: var(--hope-surface); + --hope-input-foreground: var(--hope-ink); + --hope-input-placeholder: var(--hope-quiet); + --hope-input-border: var(--hope-control-border); + --hope-input-focus: var(--hope-focus); + --hope-persona-background: var(--hope-persona-surface); + --hope-error-background: var(--hope-error-wash); + --hope-error-foreground: var(--hope-alert); +} + +:root[data-palette="lavender"] { + color-scheme: light; + --hope-primitive-ground: rgb(254 253 255); + --hope-ground: var(--hope-primitive-ground); + --hope-primitive-surface: rgb(246 242 252); + --hope-surface: var(--hope-primitive-surface); + --hope-primitive-persona-surface: rgb(238 232 248); + --hope-persona-surface: var(--hope-primitive-persona-surface); + --hope-primitive-ink: rgb(52 45 67); + --hope-ink: var(--hope-primitive-ink); + --hope-primitive-quiet: rgb(104 94 123); + --hope-quiet: var(--hope-primitive-quiet); + --hope-primitive-accent: rgb(112 82 156); + --hope-accent: var(--hope-primitive-accent); + --hope-primitive-on-accent: rgb(255 255 255); + --hope-on-accent: var(--hope-primitive-on-accent); + --hope-primitive-focus: rgb(112 82 156); + --hope-focus: var(--hope-primitive-focus); + --hope-primitive-control-border: rgb(104 94 123); + --hope-control-border: var(--hope-primitive-control-border); + --hope-primitive-alert: rgb(172.125 53.04 71.91); + --hope-alert: var(--hope-primitive-alert); + --hope-primitive-on-alert: rgb(255 255 255); + --hope-on-alert: var(--hope-primitive-on-alert); + --hope-primitive-separator: rgb(218.84 214.42 226.10000000000002); + --hope-separator: var(--hope-primitive-separator); + --hope-primitive-selected-wash: rgb(232.60000000000002 226 242.40000000000003); + --hope-selected-wash: var(--hope-primitive-selected-wash); + --hope-primitive-error-wash: rgb(240.09 226.8832 237.5928); + --hope-error-wash: var(--hope-primitive-error-wash); + --hope-rosewood: var(--hope-accent); + --hope-on-rosewood: var(--hope-on-accent); + --hope-action-background: var(--hope-accent); + --hope-action-foreground: var(--hope-on-accent); + --hope-input-background: var(--hope-surface); + --hope-input-foreground: var(--hope-ink); + --hope-input-placeholder: var(--hope-quiet); + --hope-input-border: var(--hope-control-border); + --hope-input-focus: var(--hope-focus); + --hope-persona-background: var(--hope-persona-surface); + --hope-error-background: var(--hope-error-wash); + --hope-error-foreground: var(--hope-alert); +} + +@media (prefers-color-scheme: dark) { +:root:not([data-appearance="light"]):not([data-appearance="dark"]) { + color-scheme: dark; + --hope-primitive-ground: rgb(32 24 32); + --hope-ground: var(--hope-primitive-ground); + --hope-primitive-surface: rgb(48 35 48); + --hope-surface: var(--hope-primitive-surface); + --hope-primitive-persona-surface: rgb(59 40 54); + --hope-persona-surface: var(--hope-primitive-persona-surface); + --hope-primitive-ink: rgb(255 242 247); + --hope-ink: var(--hope-primitive-ink); + --hope-primitive-quiet: rgb(203 182 199); + --hope-quiet: var(--hope-primitive-quiet); + --hope-primitive-accent: rgb(240 168 200); + --hope-accent: var(--hope-primitive-accent); + --hope-primitive-on-accent: rgb(32 24 32); + --hope-on-accent: var(--hope-primitive-on-accent); + --hope-primitive-focus: rgb(240 168 200); + --hope-focus: var(--hope-primitive-focus); + --hope-primitive-control-border: rgb(203 182 199); + --hope-control-border: var(--hope-primitive-control-border); + --hope-primitive-alert: rgb(255 154.01999999999998 172.125); + --hope-alert: var(--hope-primitive-alert); + --hope-primitive-on-alert: rgb(32 24 32); + --hope-on-alert: var(--hope-primitive-on-alert); + --hope-primitive-separator: rgb(76.98 63.98 75.86000000000001); + --hope-separator: var(--hope-primitive-separator); + --hope-primitive-selected-wash: rgb(67.2 48.30000000000001 63.199999999999996); + --hope-selected-wash: var(--hope-primitive-selected-wash); + --hope-primitive-error-wash: rgb(64.56 44.5216 57.93000000000001); + --hope-error-wash: var(--hope-primitive-error-wash); + --hope-rosewood: var(--hope-accent); + --hope-on-rosewood: var(--hope-on-accent); + --hope-action-background: var(--hope-accent); + --hope-action-foreground: var(--hope-on-accent); + --hope-input-background: var(--hope-surface); + --hope-input-foreground: var(--hope-ink); + --hope-input-placeholder: var(--hope-quiet); + --hope-input-border: var(--hope-control-border); + --hope-input-focus: var(--hope-focus); + --hope-persona-background: var(--hope-persona-surface); + --hope-error-background: var(--hope-error-wash); + --hope-error-foreground: var(--hope-alert); +} +:root[data-palette="opal"]:not([data-appearance="light"]):not([data-appearance="dark"]) { + color-scheme: dark; + --hope-primitive-ground: rgb(32 24 32); + --hope-ground: var(--hope-primitive-ground); + --hope-primitive-surface: rgb(48 35 48); + --hope-surface: var(--hope-primitive-surface); + --hope-primitive-persona-surface: rgb(59 40 54); + --hope-persona-surface: var(--hope-primitive-persona-surface); + --hope-primitive-ink: rgb(255 242 247); + --hope-ink: var(--hope-primitive-ink); + --hope-primitive-quiet: rgb(203 182 199); + --hope-quiet: var(--hope-primitive-quiet); + --hope-primitive-accent: rgb(240 168 200); + --hope-accent: var(--hope-primitive-accent); + --hope-primitive-on-accent: rgb(32 24 32); + --hope-on-accent: var(--hope-primitive-on-accent); + --hope-primitive-focus: rgb(240 168 200); + --hope-focus: var(--hope-primitive-focus); + --hope-primitive-control-border: rgb(203 182 199); + --hope-control-border: var(--hope-primitive-control-border); + --hope-primitive-alert: rgb(255 154.01999999999998 172.125); + --hope-alert: var(--hope-primitive-alert); + --hope-primitive-on-alert: rgb(32 24 32); + --hope-on-alert: var(--hope-primitive-on-alert); + --hope-primitive-separator: rgb(76.98 63.98 75.86000000000001); + --hope-separator: var(--hope-primitive-separator); + --hope-primitive-selected-wash: rgb(67.2 48.30000000000001 63.199999999999996); + --hope-selected-wash: var(--hope-primitive-selected-wash); + --hope-primitive-error-wash: rgb(64.56 44.5216 57.93000000000001); + --hope-error-wash: var(--hope-primitive-error-wash); + --hope-rosewood: var(--hope-accent); + --hope-on-rosewood: var(--hope-on-accent); + --hope-action-background: var(--hope-accent); + --hope-action-foreground: var(--hope-on-accent); + --hope-input-background: var(--hope-surface); + --hope-input-foreground: var(--hope-ink); + --hope-input-placeholder: var(--hope-quiet); + --hope-input-border: var(--hope-control-border); + --hope-input-focus: var(--hope-focus); + --hope-persona-background: var(--hope-persona-surface); + --hope-error-background: var(--hope-error-wash); + --hope-error-foreground: var(--hope-alert); +} +:root[data-palette="graphite"]:not([data-appearance="light"]):not([data-appearance="dark"]) { + color-scheme: dark; + --hope-primitive-ground: rgb(25 27 32); + --hope-ground: var(--hope-primitive-ground); + --hope-primitive-surface: rgb(37 40 47); + --hope-surface: var(--hope-primitive-surface); + --hope-primitive-persona-surface: rgb(48 52 61); + --hope-persona-surface: var(--hope-primitive-persona-surface); + --hope-primitive-ink: rgb(243 244 247); + --hope-ink: var(--hope-primitive-ink); + --hope-primitive-quiet: rgb(188 193 205); + --hope-quiet: var(--hope-primitive-quiet); + --hope-primitive-accent: rgb(191 201 220); + --hope-accent: var(--hope-primitive-accent); + --hope-primitive-on-accent: rgb(25 27 32); + --hope-on-accent: var(--hope-primitive-on-accent); + --hope-primitive-focus: rgb(191 201 220); + --hope-focus: var(--hope-primitive-focus); + --hope-primitive-control-border: rgb(188 193 205); + --hope-control-border: var(--hope-primitive-control-border); + --hope-primitive-alert: rgb(255 154.01999999999998 172.125); + --hope-alert: var(--hope-primitive-alert); + --hope-primitive-on-alert: rgb(25 27 32); + --hope-on-alert: var(--hope-primitive-on-alert); + --hope-primitive-separator: rgb(65.84000000000002 68.56 75); + --hope-separator: var(--hope-primitive-separator); + --hope-primitive-selected-wash: rgb(52.400000000000006 56.1 64.30000000000001); + --hope-selected-wash: var(--hope-primitive-selected-wash); + --hope-primitive-error-wash: rgb(54.440000000000005 49.1216 57.01000000000001); + --hope-error-wash: var(--hope-primitive-error-wash); + --hope-rosewood: var(--hope-accent); + --hope-on-rosewood: var(--hope-on-accent); + --hope-action-background: var(--hope-accent); + --hope-action-foreground: var(--hope-on-accent); + --hope-input-background: var(--hope-surface); + --hope-input-foreground: var(--hope-ink); + --hope-input-placeholder: var(--hope-quiet); + --hope-input-border: var(--hope-control-border); + --hope-input-focus: var(--hope-focus); + --hope-persona-background: var(--hope-persona-surface); + --hope-error-background: var(--hope-error-wash); + --hope-error-foreground: var(--hope-alert); +} +:root[data-palette="ocean"]:not([data-appearance="light"]):not([data-appearance="dark"]) { + color-scheme: dark; + --hope-primitive-ground: rgb(21 33 43); + --hope-ground: var(--hope-primitive-ground); + --hope-primitive-surface: rgb(32 49 63); + --hope-surface: var(--hope-primitive-surface); + --hope-primitive-persona-surface: rgb(40 61 77); + --hope-persona-surface: var(--hope-primitive-persona-surface); + --hope-primitive-ink: rgb(237 247 255); + --hope-ink: var(--hope-primitive-ink); + --hope-primitive-quiet: rgb(179 203 222); + --hope-quiet: var(--hope-primitive-quiet); + --hope-primitive-accent: rgb(151 206 238); + --hope-accent: var(--hope-primitive-accent); + --hope-primitive-on-accent: rgb(21 33 43); + --hope-on-accent: var(--hope-primitive-on-accent); + --hope-primitive-focus: rgb(151 206 238); + --hope-focus: var(--hope-primitive-focus); + --hope-primitive-control-border: rgb(179 203 222); + --hope-control-border: var(--hope-primitive-control-border); + --hope-primitive-alert: rgb(255 154.01999999999998 172.125); + --hope-alert: var(--hope-primitive-alert); + --hope-primitive-on-alert: rgb(21 33 43); + --hope-on-alert: var(--hope-primitive-on-alert); + --hope-primitive-separator: rgb(60.7 76.72 89.88000000000001); + --hope-separator: var(--hope-primitive-separator); + --hope-primitive-selected-wash: rgb(43.900000000000006 64.7 80.50000000000001); + --hope-selected-wash: var(--hope-primitive-selected-wash); + --hope-primitive-error-wash: rgb(49.839999999999996 57.40160000000001 71.73); + --hope-error-wash: var(--hope-primitive-error-wash); + --hope-rosewood: var(--hope-accent); + --hope-on-rosewood: var(--hope-on-accent); + --hope-action-background: var(--hope-accent); + --hope-action-foreground: var(--hope-on-accent); + --hope-input-background: var(--hope-surface); + --hope-input-foreground: var(--hope-ink); + --hope-input-placeholder: var(--hope-quiet); + --hope-input-border: var(--hope-control-border); + --hope-input-focus: var(--hope-focus); + --hope-persona-background: var(--hope-persona-surface); + --hope-error-background: var(--hope-error-wash); + --hope-error-foreground: var(--hope-alert); +} +:root[data-palette="sage"]:not([data-appearance="light"]):not([data-appearance="dark"]) { + color-scheme: dark; + --hope-primitive-ground: rgb(25 35 28); + --hope-ground: var(--hope-primitive-ground); + --hope-primitive-surface: rgb(38 52 42); + --hope-surface: var(--hope-primitive-surface); + --hope-primitive-persona-surface: rgb(48 66 54); + --hope-persona-surface: var(--hope-primitive-persona-surface); + --hope-primitive-ink: rgb(240 248 241); + --hope-ink: var(--hope-primitive-ink); + --hope-primitive-quiet: rgb(185 207 190); + --hope-quiet: var(--hope-primitive-quiet); + --hope-primitive-accent: rgb(165 212 175); + --hope-accent: var(--hope-primitive-accent); + --hope-primitive-on-accent: rgb(25 35 28); + --hope-on-accent: var(--hope-primitive-on-accent); + --hope-primitive-focus: rgb(165 212 175); + --hope-focus: var(--hope-primitive-focus); + --hope-primitive-control-border: rgb(185 207 190); + --hope-control-border: var(--hope-primitive-control-border); + --hope-primitive-alert: rgb(255 154.01999999999998 172.125); + --hope-alert: var(--hope-primitive-alert); + --hope-primitive-on-alert: rgb(25 35 28); + --hope-on-alert: var(--hope-primitive-on-alert); + --hope-primitive-separator: rgb(66.28 79.44 69.86); + --hope-separator: var(--hope-primitive-separator); + --hope-primitive-selected-wash: rgb(50.70000000000001 68 55.3); + --hope-selected-wash: var(--hope-primitive-selected-wash); + --hope-primitive-error-wash: rgb(55.36 60.1616 52.410000000000004); + --hope-error-wash: var(--hope-primitive-error-wash); + --hope-rosewood: var(--hope-accent); + --hope-on-rosewood: var(--hope-on-accent); + --hope-action-background: var(--hope-accent); + --hope-action-foreground: var(--hope-on-accent); + --hope-input-background: var(--hope-surface); + --hope-input-foreground: var(--hope-ink); + --hope-input-placeholder: var(--hope-quiet); + --hope-input-border: var(--hope-control-border); + --hope-input-focus: var(--hope-focus); + --hope-persona-background: var(--hope-persona-surface); + --hope-error-background: var(--hope-error-wash); + --hope-error-foreground: var(--hope-alert); +} +:root[data-palette="lavender"]:not([data-appearance="light"]):not([data-appearance="dark"]) { + color-scheme: dark; + --hope-primitive-ground: rgb(32 27 41); + --hope-ground: var(--hope-primitive-ground); + --hope-primitive-surface: rgb(46 39 60); + --hope-surface: var(--hope-primitive-surface); + --hope-primitive-persona-surface: rgb(59 50 77); + --hope-persona-surface: var(--hope-primitive-persona-surface); + --hope-primitive-ink: rgb(247 241 255); + --hope-ink: var(--hope-primitive-ink); + --hope-primitive-quiet: rgb(200 186 223); + --hope-quiet: var(--hope-primitive-quiet); + --hope-primitive-accent: rgb(207 180 244); + --hope-accent: var(--hope-primitive-accent); + --hope-primitive-on-accent: rgb(32 27 41); + --hope-on-accent: var(--hope-primitive-on-accent); + --hope-primitive-focus: rgb(207 180 244); + --hope-focus: var(--hope-primitive-focus); + --hope-primitive-control-border: rgb(200 186 223); + --hope-control-border: var(--hope-primitive-control-border); + --hope-primitive-alert: rgb(255 154.01999999999998 172.125); + --hope-alert: var(--hope-primitive-alert); + --hope-primitive-on-alert: rgb(32 27 41); + --hope-on-alert: var(--hope-primitive-on-alert); + --hope-primitive-separator: rgb(74.14 67.28000000000002 87.3); + --hope-separator: var(--hope-primitive-separator); + --hope-primitive-selected-wash: rgb(62.1 53.1 78.4); + --hope-selected-wash: var(--hope-primitive-selected-wash); + --hope-primitive-error-wash: rgb(62.72 48.2016 68.97); + --hope-error-wash: var(--hope-primitive-error-wash); + --hope-rosewood: var(--hope-accent); + --hope-on-rosewood: var(--hope-on-accent); + --hope-action-background: var(--hope-accent); + --hope-action-foreground: var(--hope-on-accent); + --hope-input-background: var(--hope-surface); + --hope-input-foreground: var(--hope-ink); + --hope-input-placeholder: var(--hope-quiet); + --hope-input-border: var(--hope-control-border); + --hope-input-focus: var(--hope-focus); + --hope-persona-background: var(--hope-persona-surface); + --hope-error-background: var(--hope-error-wash); + --hope-error-foreground: var(--hope-alert); +} +} + +:root[data-appearance="light"] { + color-scheme: light; + --hope-primitive-ground: rgb(255 253 253); + --hope-ground: var(--hope-primitive-ground); + --hope-primitive-surface: rgb(255 247 250); + --hope-surface: var(--hope-primitive-surface); + --hope-primitive-persona-surface: rgb(255 242 247); + --hope-persona-surface: var(--hope-primitive-persona-surface); + --hope-primitive-ink: rgb(48 43 64); + --hope-ink: var(--hope-primitive-ink); + --hope-primitive-quiet: rgb(112 96 116); + --hope-quiet: var(--hope-primitive-quiet); + --hope-primitive-accent: rgb(166 66 112); + --hope-accent: var(--hope-primitive-accent); + --hope-primitive-on-accent: rgb(255 255 255); + --hope-on-accent: var(--hope-primitive-on-accent); + --hope-primitive-focus: rgb(166 66 112); + --hope-focus: var(--hope-primitive-focus); + --hope-primitive-control-border: rgb(112 96 116); + --hope-control-border: var(--hope-primitive-control-border); + --hope-primitive-alert: rgb(172.125 53.04 71.91); + --hope-alert: var(--hope-primitive-alert); + --hope-primitive-on-alert: rgb(255 255 255); + --hope-on-alert: var(--hope-primitive-on-alert); + --hope-primitive-separator: rgb(226.01999999999998 218.44 223.95999999999998); + --hope-separator: var(--hope-primitive-separator); + --hope-primitive-selected-wash: rgb(246.1 228.9 236.2); + --hope-selected-wash: var(--hope-primitive-selected-wash); + --hope-primitive-error-wash: rgb(248.37000000000003 231.4832 235.7528); + --hope-error-wash: var(--hope-primitive-error-wash); + --hope-rosewood: var(--hope-accent); + --hope-on-rosewood: var(--hope-on-accent); + --hope-action-background: var(--hope-accent); + --hope-action-foreground: var(--hope-on-accent); + --hope-input-background: var(--hope-surface); + --hope-input-foreground: var(--hope-ink); + --hope-input-placeholder: var(--hope-quiet); + --hope-input-border: var(--hope-control-border); + --hope-input-focus: var(--hope-focus); + --hope-persona-background: var(--hope-persona-surface); + --hope-error-background: var(--hope-error-wash); + --hope-error-foreground: var(--hope-alert); +} + +:root[data-palette="opal"][data-appearance="light"] { + color-scheme: light; + --hope-primitive-ground: rgb(255 253 253); + --hope-ground: var(--hope-primitive-ground); + --hope-primitive-surface: rgb(255 247 250); + --hope-surface: var(--hope-primitive-surface); + --hope-primitive-persona-surface: rgb(255 242 247); + --hope-persona-surface: var(--hope-primitive-persona-surface); + --hope-primitive-ink: rgb(48 43 64); + --hope-ink: var(--hope-primitive-ink); + --hope-primitive-quiet: rgb(112 96 116); + --hope-quiet: var(--hope-primitive-quiet); + --hope-primitive-accent: rgb(166 66 112); + --hope-accent: var(--hope-primitive-accent); + --hope-primitive-on-accent: rgb(255 255 255); + --hope-on-accent: var(--hope-primitive-on-accent); + --hope-primitive-focus: rgb(166 66 112); + --hope-focus: var(--hope-primitive-focus); + --hope-primitive-control-border: rgb(112 96 116); + --hope-control-border: var(--hope-primitive-control-border); + --hope-primitive-alert: rgb(172.125 53.04 71.91); + --hope-alert: var(--hope-primitive-alert); + --hope-primitive-on-alert: rgb(255 255 255); + --hope-on-alert: var(--hope-primitive-on-alert); + --hope-primitive-separator: rgb(226.01999999999998 218.44 223.95999999999998); + --hope-separator: var(--hope-primitive-separator); + --hope-primitive-selected-wash: rgb(246.1 228.9 236.2); + --hope-selected-wash: var(--hope-primitive-selected-wash); + --hope-primitive-error-wash: rgb(248.37000000000003 231.4832 235.7528); + --hope-error-wash: var(--hope-primitive-error-wash); + --hope-rosewood: var(--hope-accent); + --hope-on-rosewood: var(--hope-on-accent); + --hope-action-background: var(--hope-accent); + --hope-action-foreground: var(--hope-on-accent); + --hope-input-background: var(--hope-surface); + --hope-input-foreground: var(--hope-ink); + --hope-input-placeholder: var(--hope-quiet); + --hope-input-border: var(--hope-control-border); + --hope-input-focus: var(--hope-focus); + --hope-persona-background: var(--hope-persona-surface); + --hope-error-background: var(--hope-error-wash); + --hope-error-foreground: var(--hope-alert); +} + +:root[data-palette="graphite"][data-appearance="light"] { + color-scheme: light; + --hope-primitive-ground: rgb(253 253 254); + --hope-ground: var(--hope-primitive-ground); + --hope-primitive-surface: rgb(243 244 246); + --hope-surface: var(--hope-primitive-surface); + --hope-primitive-persona-surface: rgb(237 238 242); + --hope-persona-surface: var(--hope-primitive-persona-surface); + --hope-primitive-ink: rgb(39 42 51); + --hope-ink: var(--hope-primitive-ink); + --hope-primitive-quiet: rgb(94 98 110); + --hope-quiet: var(--hope-primitive-quiet); + --hope-primitive-accent: rgb(80 89 105); + --hope-accent: var(--hope-primitive-accent); + --hope-primitive-on-accent: rgb(255 255 255); + --hope-on-accent: var(--hope-primitive-on-accent); + --hope-primitive-focus: rgb(80 89 105); + --hope-focus: var(--hope-primitive-focus); + --hope-primitive-control-border: rgb(94 98 110); + --hope-control-border: var(--hope-primitive-control-border); + --hope-primitive-alert: rgb(172.125 53.04 71.91); + --hope-alert: var(--hope-primitive-alert); + --hope-primitive-on-alert: rgb(255 255 255); + --hope-on-alert: var(--hope-primitive-on-alert); + --hope-primitive-separator: rgb(214.44 215.72000000000003 218.70000000000002); + --hope-separator: var(--hope-primitive-separator); + --hope-primitive-selected-wash: rgb(226.70000000000002 228.5 231.9); + --hope-selected-wash: var(--hope-primitive-selected-wash); + --hope-primitive-error-wash: rgb(237.32999999999998 228.72320000000002 232.0728); + --hope-error-wash: var(--hope-primitive-error-wash); + --hope-rosewood: var(--hope-accent); + --hope-on-rosewood: var(--hope-on-accent); + --hope-action-background: var(--hope-accent); + --hope-action-foreground: var(--hope-on-accent); + --hope-input-background: var(--hope-surface); + --hope-input-foreground: var(--hope-ink); + --hope-input-placeholder: var(--hope-quiet); + --hope-input-border: var(--hope-control-border); + --hope-input-focus: var(--hope-focus); + --hope-persona-background: var(--hope-persona-surface); + --hope-error-background: var(--hope-error-wash); + --hope-error-foreground: var(--hope-alert); +} + +:root[data-palette="ocean"][data-appearance="light"] { + color-scheme: light; + --hope-primitive-ground: rgb(252 254 255); + --hope-ground: var(--hope-primitive-ground); + --hope-primitive-surface: rgb(240 247 251); + --hope-surface: var(--hope-primitive-surface); + --hope-primitive-persona-surface: rgb(231 242 249); + --hope-persona-surface: var(--hope-primitive-persona-surface); + --hope-primitive-ink: rgb(35 52 66); + --hope-ink: var(--hope-primitive-ink); + --hope-primitive-quiet: rgb(83 104 120); + --hope-quiet: var(--hope-primitive-quiet); + --hope-primitive-accent: rgb(39 101 140); + --hope-accent: var(--hope-primitive-accent); + --hope-primitive-on-accent: rgb(255 255 255); + --hope-on-accent: var(--hope-primitive-on-accent); + --hope-primitive-focus: rgb(39 101 140); + --hope-focus: var(--hope-primitive-focus); + --hope-primitive-control-border: rgb(83 104 120); + --hope-control-border: var(--hope-primitive-control-border); + --hope-primitive-alert: rgb(172.125 53.04 71.91); + --hope-alert: var(--hope-primitive-alert); + --hope-primitive-on-alert: rgb(255 255 255); + --hope-on-alert: var(--hope-primitive-on-alert); + --hope-primitive-separator: rgb(211.29999999999995 219.7 225.1); + --hope-separator: var(--hope-primitive-separator); + --hope-primitive-selected-wash: rgb(219.89999999999998 232.4 239.9); + --hope-selected-wash: var(--hope-primitive-selected-wash); + --hope-primitive-error-wash: rgb(234.57000000000002 231.4832 236.67280000000002); + --hope-error-wash: var(--hope-primitive-error-wash); + --hope-rosewood: var(--hope-accent); + --hope-on-rosewood: var(--hope-on-accent); + --hope-action-background: var(--hope-accent); + --hope-action-foreground: var(--hope-on-accent); + --hope-input-background: var(--hope-surface); + --hope-input-foreground: var(--hope-ink); + --hope-input-placeholder: var(--hope-quiet); + --hope-input-border: var(--hope-control-border); + --hope-input-focus: var(--hope-focus); + --hope-persona-background: var(--hope-persona-surface); + --hope-error-background: var(--hope-error-wash); + --hope-error-foreground: var(--hope-alert); +} + +:root[data-palette="sage"][data-appearance="light"] { + color-scheme: light; + --hope-primitive-ground: rgb(252 254 252); + --hope-ground: var(--hope-primitive-ground); + --hope-primitive-surface: rgb(240 246 241); + --hope-surface: var(--hope-primitive-surface); + --hope-primitive-persona-surface: rgb(231 240 233); + --hope-persona-surface: var(--hope-primitive-persona-surface); + --hope-primitive-ink: rgb(41 56 45); + --hope-ink: var(--hope-primitive-ink); + --hope-primitive-quiet: rgb(89 107 93); + --hope-quiet: var(--hope-primitive-quiet); + --hope-primitive-accent: rgb(60 112 77); + --hope-accent: var(--hope-primitive-accent); + --hope-primitive-on-accent: rgb(255 255 255); + --hope-on-accent: var(--hope-primitive-on-accent); + --hope-primitive-focus: rgb(60 112 77); + --hope-focus: var(--hope-primitive-focus); + --hope-primitive-control-border: rgb(89 107 93); + --hope-control-border: var(--hope-primitive-control-border); + --hope-primitive-alert: rgb(172.125 53.04 71.91); + --hope-alert: var(--hope-primitive-alert); + --hope-primitive-on-alert: rgb(255 255 255); + --hope-on-alert: var(--hope-primitive-on-alert); + --hope-primitive-separator: rgb(212.14 219.39999999999998 213.56); + --hope-separator: var(--hope-primitive-separator); + --hope-primitive-selected-wash: rgb(222 232.60000000000002 224.6); + --hope-selected-wash: var(--hope-primitive-selected-wash); + --hope-primitive-error-wash: rgb(234.57000000000002 230.5632 227.4728); + --hope-error-wash: var(--hope-primitive-error-wash); + --hope-rosewood: var(--hope-accent); + --hope-on-rosewood: var(--hope-on-accent); + --hope-action-background: var(--hope-accent); + --hope-action-foreground: var(--hope-on-accent); + --hope-input-background: var(--hope-surface); + --hope-input-foreground: var(--hope-ink); + --hope-input-placeholder: var(--hope-quiet); + --hope-input-border: var(--hope-control-border); + --hope-input-focus: var(--hope-focus); + --hope-persona-background: var(--hope-persona-surface); + --hope-error-background: var(--hope-error-wash); + --hope-error-foreground: var(--hope-alert); +} + +:root[data-palette="lavender"][data-appearance="light"] { + color-scheme: light; + --hope-primitive-ground: rgb(254 253 255); + --hope-ground: var(--hope-primitive-ground); + --hope-primitive-surface: rgb(246 242 252); + --hope-surface: var(--hope-primitive-surface); + --hope-primitive-persona-surface: rgb(238 232 248); + --hope-persona-surface: var(--hope-primitive-persona-surface); + --hope-primitive-ink: rgb(52 45 67); + --hope-ink: var(--hope-primitive-ink); + --hope-primitive-quiet: rgb(104 94 123); + --hope-quiet: var(--hope-primitive-quiet); + --hope-primitive-accent: rgb(112 82 156); + --hope-accent: var(--hope-primitive-accent); + --hope-primitive-on-accent: rgb(255 255 255); + --hope-on-accent: var(--hope-primitive-on-accent); + --hope-primitive-focus: rgb(112 82 156); + --hope-focus: var(--hope-primitive-focus); + --hope-primitive-control-border: rgb(104 94 123); + --hope-control-border: var(--hope-primitive-control-border); + --hope-primitive-alert: rgb(172.125 53.04 71.91); + --hope-alert: var(--hope-primitive-alert); + --hope-primitive-on-alert: rgb(255 255 255); + --hope-on-alert: var(--hope-primitive-on-alert); + --hope-primitive-separator: rgb(218.84 214.42 226.10000000000002); + --hope-separator: var(--hope-primitive-separator); + --hope-primitive-selected-wash: rgb(232.60000000000002 226 242.40000000000003); + --hope-selected-wash: var(--hope-primitive-selected-wash); + --hope-primitive-error-wash: rgb(240.09 226.8832 237.5928); + --hope-error-wash: var(--hope-primitive-error-wash); + --hope-rosewood: var(--hope-accent); + --hope-on-rosewood: var(--hope-on-accent); + --hope-action-background: var(--hope-accent); + --hope-action-foreground: var(--hope-on-accent); + --hope-input-background: var(--hope-surface); + --hope-input-foreground: var(--hope-ink); + --hope-input-placeholder: var(--hope-quiet); + --hope-input-border: var(--hope-control-border); + --hope-input-focus: var(--hope-focus); + --hope-persona-background: var(--hope-persona-surface); + --hope-error-background: var(--hope-error-wash); + --hope-error-foreground: var(--hope-alert); +} + +:root[data-appearance="dark"] { + color-scheme: dark; + --hope-primitive-ground: rgb(32 24 32); + --hope-ground: var(--hope-primitive-ground); + --hope-primitive-surface: rgb(48 35 48); + --hope-surface: var(--hope-primitive-surface); + --hope-primitive-persona-surface: rgb(59 40 54); + --hope-persona-surface: var(--hope-primitive-persona-surface); + --hope-primitive-ink: rgb(255 242 247); + --hope-ink: var(--hope-primitive-ink); + --hope-primitive-quiet: rgb(203 182 199); + --hope-quiet: var(--hope-primitive-quiet); + --hope-primitive-accent: rgb(240 168 200); + --hope-accent: var(--hope-primitive-accent); + --hope-primitive-on-accent: rgb(32 24 32); + --hope-on-accent: var(--hope-primitive-on-accent); + --hope-primitive-focus: rgb(240 168 200); + --hope-focus: var(--hope-primitive-focus); + --hope-primitive-control-border: rgb(203 182 199); + --hope-control-border: var(--hope-primitive-control-border); + --hope-primitive-alert: rgb(255 154.01999999999998 172.125); + --hope-alert: var(--hope-primitive-alert); + --hope-primitive-on-alert: rgb(32 24 32); + --hope-on-alert: var(--hope-primitive-on-alert); + --hope-primitive-separator: rgb(76.98 63.98 75.86000000000001); + --hope-separator: var(--hope-primitive-separator); + --hope-primitive-selected-wash: rgb(67.2 48.30000000000001 63.199999999999996); + --hope-selected-wash: var(--hope-primitive-selected-wash); + --hope-primitive-error-wash: rgb(64.56 44.5216 57.93000000000001); + --hope-error-wash: var(--hope-primitive-error-wash); + --hope-rosewood: var(--hope-accent); + --hope-on-rosewood: var(--hope-on-accent); + --hope-action-background: var(--hope-accent); + --hope-action-foreground: var(--hope-on-accent); + --hope-input-background: var(--hope-surface); + --hope-input-foreground: var(--hope-ink); + --hope-input-placeholder: var(--hope-quiet); + --hope-input-border: var(--hope-control-border); + --hope-input-focus: var(--hope-focus); + --hope-persona-background: var(--hope-persona-surface); + --hope-error-background: var(--hope-error-wash); + --hope-error-foreground: var(--hope-alert); +} + +:root[data-palette="opal"][data-appearance="dark"] { + color-scheme: dark; + --hope-primitive-ground: rgb(32 24 32); + --hope-ground: var(--hope-primitive-ground); + --hope-primitive-surface: rgb(48 35 48); + --hope-surface: var(--hope-primitive-surface); + --hope-primitive-persona-surface: rgb(59 40 54); + --hope-persona-surface: var(--hope-primitive-persona-surface); + --hope-primitive-ink: rgb(255 242 247); + --hope-ink: var(--hope-primitive-ink); + --hope-primitive-quiet: rgb(203 182 199); + --hope-quiet: var(--hope-primitive-quiet); + --hope-primitive-accent: rgb(240 168 200); + --hope-accent: var(--hope-primitive-accent); + --hope-primitive-on-accent: rgb(32 24 32); + --hope-on-accent: var(--hope-primitive-on-accent); + --hope-primitive-focus: rgb(240 168 200); + --hope-focus: var(--hope-primitive-focus); + --hope-primitive-control-border: rgb(203 182 199); + --hope-control-border: var(--hope-primitive-control-border); + --hope-primitive-alert: rgb(255 154.01999999999998 172.125); + --hope-alert: var(--hope-primitive-alert); + --hope-primitive-on-alert: rgb(32 24 32); + --hope-on-alert: var(--hope-primitive-on-alert); + --hope-primitive-separator: rgb(76.98 63.98 75.86000000000001); + --hope-separator: var(--hope-primitive-separator); + --hope-primitive-selected-wash: rgb(67.2 48.30000000000001 63.199999999999996); + --hope-selected-wash: var(--hope-primitive-selected-wash); + --hope-primitive-error-wash: rgb(64.56 44.5216 57.93000000000001); + --hope-error-wash: var(--hope-primitive-error-wash); + --hope-rosewood: var(--hope-accent); + --hope-on-rosewood: var(--hope-on-accent); + --hope-action-background: var(--hope-accent); + --hope-action-foreground: var(--hope-on-accent); + --hope-input-background: var(--hope-surface); + --hope-input-foreground: var(--hope-ink); + --hope-input-placeholder: var(--hope-quiet); + --hope-input-border: var(--hope-control-border); + --hope-input-focus: var(--hope-focus); + --hope-persona-background: var(--hope-persona-surface); + --hope-error-background: var(--hope-error-wash); + --hope-error-foreground: var(--hope-alert); +} + +:root[data-palette="graphite"][data-appearance="dark"] { + color-scheme: dark; + --hope-primitive-ground: rgb(25 27 32); + --hope-ground: var(--hope-primitive-ground); + --hope-primitive-surface: rgb(37 40 47); + --hope-surface: var(--hope-primitive-surface); + --hope-primitive-persona-surface: rgb(48 52 61); + --hope-persona-surface: var(--hope-primitive-persona-surface); + --hope-primitive-ink: rgb(243 244 247); + --hope-ink: var(--hope-primitive-ink); + --hope-primitive-quiet: rgb(188 193 205); + --hope-quiet: var(--hope-primitive-quiet); + --hope-primitive-accent: rgb(191 201 220); + --hope-accent: var(--hope-primitive-accent); + --hope-primitive-on-accent: rgb(25 27 32); + --hope-on-accent: var(--hope-primitive-on-accent); + --hope-primitive-focus: rgb(191 201 220); + --hope-focus: var(--hope-primitive-focus); + --hope-primitive-control-border: rgb(188 193 205); + --hope-control-border: var(--hope-primitive-control-border); + --hope-primitive-alert: rgb(255 154.01999999999998 172.125); + --hope-alert: var(--hope-primitive-alert); + --hope-primitive-on-alert: rgb(25 27 32); + --hope-on-alert: var(--hope-primitive-on-alert); + --hope-primitive-separator: rgb(65.84000000000002 68.56 75); + --hope-separator: var(--hope-primitive-separator); + --hope-primitive-selected-wash: rgb(52.400000000000006 56.1 64.30000000000001); + --hope-selected-wash: var(--hope-primitive-selected-wash); + --hope-primitive-error-wash: rgb(54.440000000000005 49.1216 57.01000000000001); + --hope-error-wash: var(--hope-primitive-error-wash); + --hope-rosewood: var(--hope-accent); + --hope-on-rosewood: var(--hope-on-accent); + --hope-action-background: var(--hope-accent); + --hope-action-foreground: var(--hope-on-accent); + --hope-input-background: var(--hope-surface); + --hope-input-foreground: var(--hope-ink); + --hope-input-placeholder: var(--hope-quiet); + --hope-input-border: var(--hope-control-border); + --hope-input-focus: var(--hope-focus); + --hope-persona-background: var(--hope-persona-surface); + --hope-error-background: var(--hope-error-wash); + --hope-error-foreground: var(--hope-alert); +} + +:root[data-palette="ocean"][data-appearance="dark"] { + color-scheme: dark; + --hope-primitive-ground: rgb(21 33 43); + --hope-ground: var(--hope-primitive-ground); + --hope-primitive-surface: rgb(32 49 63); + --hope-surface: var(--hope-primitive-surface); + --hope-primitive-persona-surface: rgb(40 61 77); + --hope-persona-surface: var(--hope-primitive-persona-surface); + --hope-primitive-ink: rgb(237 247 255); + --hope-ink: var(--hope-primitive-ink); + --hope-primitive-quiet: rgb(179 203 222); + --hope-quiet: var(--hope-primitive-quiet); + --hope-primitive-accent: rgb(151 206 238); + --hope-accent: var(--hope-primitive-accent); + --hope-primitive-on-accent: rgb(21 33 43); + --hope-on-accent: var(--hope-primitive-on-accent); + --hope-primitive-focus: rgb(151 206 238); + --hope-focus: var(--hope-primitive-focus); + --hope-primitive-control-border: rgb(179 203 222); + --hope-control-border: var(--hope-primitive-control-border); + --hope-primitive-alert: rgb(255 154.01999999999998 172.125); + --hope-alert: var(--hope-primitive-alert); + --hope-primitive-on-alert: rgb(21 33 43); + --hope-on-alert: var(--hope-primitive-on-alert); + --hope-primitive-separator: rgb(60.7 76.72 89.88000000000001); + --hope-separator: var(--hope-primitive-separator); + --hope-primitive-selected-wash: rgb(43.900000000000006 64.7 80.50000000000001); + --hope-selected-wash: var(--hope-primitive-selected-wash); + --hope-primitive-error-wash: rgb(49.839999999999996 57.40160000000001 71.73); + --hope-error-wash: var(--hope-primitive-error-wash); + --hope-rosewood: var(--hope-accent); + --hope-on-rosewood: var(--hope-on-accent); + --hope-action-background: var(--hope-accent); + --hope-action-foreground: var(--hope-on-accent); + --hope-input-background: var(--hope-surface); + --hope-input-foreground: var(--hope-ink); + --hope-input-placeholder: var(--hope-quiet); + --hope-input-border: var(--hope-control-border); + --hope-input-focus: var(--hope-focus); + --hope-persona-background: var(--hope-persona-surface); + --hope-error-background: var(--hope-error-wash); + --hope-error-foreground: var(--hope-alert); +} + +:root[data-palette="sage"][data-appearance="dark"] { + color-scheme: dark; + --hope-primitive-ground: rgb(25 35 28); + --hope-ground: var(--hope-primitive-ground); + --hope-primitive-surface: rgb(38 52 42); + --hope-surface: var(--hope-primitive-surface); + --hope-primitive-persona-surface: rgb(48 66 54); + --hope-persona-surface: var(--hope-primitive-persona-surface); + --hope-primitive-ink: rgb(240 248 241); + --hope-ink: var(--hope-primitive-ink); + --hope-primitive-quiet: rgb(185 207 190); + --hope-quiet: var(--hope-primitive-quiet); + --hope-primitive-accent: rgb(165 212 175); + --hope-accent: var(--hope-primitive-accent); + --hope-primitive-on-accent: rgb(25 35 28); + --hope-on-accent: var(--hope-primitive-on-accent); + --hope-primitive-focus: rgb(165 212 175); + --hope-focus: var(--hope-primitive-focus); + --hope-primitive-control-border: rgb(185 207 190); + --hope-control-border: var(--hope-primitive-control-border); + --hope-primitive-alert: rgb(255 154.01999999999998 172.125); + --hope-alert: var(--hope-primitive-alert); + --hope-primitive-on-alert: rgb(25 35 28); + --hope-on-alert: var(--hope-primitive-on-alert); + --hope-primitive-separator: rgb(66.28 79.44 69.86); + --hope-separator: var(--hope-primitive-separator); + --hope-primitive-selected-wash: rgb(50.70000000000001 68 55.3); + --hope-selected-wash: var(--hope-primitive-selected-wash); + --hope-primitive-error-wash: rgb(55.36 60.1616 52.410000000000004); + --hope-error-wash: var(--hope-primitive-error-wash); + --hope-rosewood: var(--hope-accent); + --hope-on-rosewood: var(--hope-on-accent); + --hope-action-background: var(--hope-accent); + --hope-action-foreground: var(--hope-on-accent); + --hope-input-background: var(--hope-surface); + --hope-input-foreground: var(--hope-ink); + --hope-input-placeholder: var(--hope-quiet); + --hope-input-border: var(--hope-control-border); + --hope-input-focus: var(--hope-focus); + --hope-persona-background: var(--hope-persona-surface); + --hope-error-background: var(--hope-error-wash); + --hope-error-foreground: var(--hope-alert); +} + +:root[data-palette="lavender"][data-appearance="dark"] { + color-scheme: dark; + --hope-primitive-ground: rgb(32 27 41); + --hope-ground: var(--hope-primitive-ground); + --hope-primitive-surface: rgb(46 39 60); + --hope-surface: var(--hope-primitive-surface); + --hope-primitive-persona-surface: rgb(59 50 77); + --hope-persona-surface: var(--hope-primitive-persona-surface); + --hope-primitive-ink: rgb(247 241 255); + --hope-ink: var(--hope-primitive-ink); + --hope-primitive-quiet: rgb(200 186 223); + --hope-quiet: var(--hope-primitive-quiet); + --hope-primitive-accent: rgb(207 180 244); + --hope-accent: var(--hope-primitive-accent); + --hope-primitive-on-accent: rgb(32 27 41); + --hope-on-accent: var(--hope-primitive-on-accent); + --hope-primitive-focus: rgb(207 180 244); + --hope-focus: var(--hope-primitive-focus); + --hope-primitive-control-border: rgb(200 186 223); + --hope-control-border: var(--hope-primitive-control-border); + --hope-primitive-alert: rgb(255 154.01999999999998 172.125); + --hope-alert: var(--hope-primitive-alert); + --hope-primitive-on-alert: rgb(32 27 41); + --hope-on-alert: var(--hope-primitive-on-alert); + --hope-primitive-separator: rgb(74.14 67.28000000000002 87.3); + --hope-separator: var(--hope-primitive-separator); + --hope-primitive-selected-wash: rgb(62.1 53.1 78.4); + --hope-selected-wash: var(--hope-primitive-selected-wash); + --hope-primitive-error-wash: rgb(62.72 48.2016 68.97); + --hope-error-wash: var(--hope-primitive-error-wash); + --hope-rosewood: var(--hope-accent); + --hope-on-rosewood: var(--hope-on-accent); + --hope-action-background: var(--hope-accent); + --hope-action-foreground: var(--hope-on-accent); + --hope-input-background: var(--hope-surface); + --hope-input-foreground: var(--hope-ink); + --hope-input-placeholder: var(--hope-quiet); + --hope-input-border: var(--hope-control-border); + --hope-input-focus: var(--hope-focus); + --hope-persona-background: var(--hope-persona-surface); + --hope-error-background: var(--hope-error-wash); + --hope-error-foreground: var(--hope-alert); +}