commit c8451f89d9982e4b00ccf8f94bb6aad7a104dc8e
Author: Coder Agent <coder@agents.omni>
Date: Wed Feb 18 14:24:54 2026
Add light theme support via prefers-color-scheme media query
Define CSS custom properties on :root with dark theme defaults and
override them in a @media (prefers-color-scheme: light) block with a
warm-cream light palette. Append var() override rules after all
Clay-generated dark styles so they win the cascade for both the shared
design system (Omni/Web/Style.hs) and task-specific selectors
(Omni/Task/Web/Style.hs).
Light palette (inverse of ef-dream dark theme):
- Background: #faf8f6 (warm cream), raised: #ffffff
- Foreground: #232025, muted: #6b6568, faint: #9a9497
- Borders: #d4d0cc / #e8e5e2
- Accent colors darkened for contrast on light backgrounds
Also update theme-color meta tags in both sharedShell and Task web
Components to include media attributes for dark/light scheme switching.
Task-Id: t-637
diff --git a/Omni/Task/Web/Components.hs b/Omni/Task/Web/Components.hs
index eaaca370..e93e0bc5 100644
--- a/Omni/Task/Web/Components.hs
+++ b/Omni/Task/Web/Components.hs
@@ -201,7 +201,8 @@ pageHead title =
]
Lucid.meta_ [Lucid.name_ "apple-mobile-web-app-capable", Lucid.content_ "yes"]
Lucid.meta_ [Lucid.name_ "apple-mobile-web-app-status-bar-style", Lucid.content_ "black-translucent"]
- Lucid.meta_ [Lucid.name_ "theme-color", Lucid.content_ "#232025"]
+ Lucid.meta_ [Lucid.name_ "theme-color", Lucid.content_ "#232025", Lucid.makeAttribute "media" "(prefers-color-scheme: dark)"]
+ Lucid.meta_ [Lucid.name_ "theme-color", Lucid.content_ "#faf8f6", Lucid.makeAttribute "media" "(prefers-color-scheme: light)"]
Lucid.link_ [Lucid.rel_ "stylesheet", Lucid.href_ "/style.css"]
Lucid.script_
[ Lucid.src_ "https://unpkg.com/htmx.org@2.0.4",
diff --git a/Omni/Task/Web/Style.hs b/Omni/Task/Web/Style.hs
index 1f98bc23..13972ecf 100644
--- a/Omni/Task/Web/Style.hs
+++ b/Omni/Task/Web/Style.hs
@@ -18,7 +18,7 @@ import qualified Data.Text.Lazy as LazyText
import qualified Omni.Web.Style as WebStyle
css :: LazyText.Text
-css = WebStyle.css <> render stylesheet
+css = WebStyle.css <> render stylesheet <> WebStyle.lightModeCss <> taskLightModeCss
stylesheet :: Css
stylesheet = do
@@ -1937,6 +1937,258 @@ responsiveStyles = do
darkModeStyles :: Css
darkModeStyles = pure ()
+-- | Light mode var() overrides for task-specific selectors.
+-- Appended after all Clay-generated dark styles AND after
+-- 'WebStyle.lightModeCss' (which defines the CSS custom properties
+-- and overrides shared design-system selectors).
+taskLightModeCss :: LazyText.Text
+taskLightModeCss =
+ LazyText.unlines
+ [ "/* ── Light theme: task-specific var() overrides ────────── */",
+ "",
+ "/* Task navigation */",
+ ".navbar { background-color: var(--c-bg); border-bottom-color: var(--c-border); }",
+ ".navbar-brand { color: var(--c-accent); }",
+ ".hamburger-line { background-color: var(--c-fg-muted); }",
+ ".navbar-link { color: var(--c-fg-muted); }",
+ ".navbar-link:hover { background-color: var(--c-bg-hover); color: var(--c-fg); }",
+ ".navbar-dropdown-btn { color: var(--c-fg-muted); }",
+ ".navbar-dropdown-btn:hover { background-color: var(--c-bg-hover); color: var(--c-fg); }",
+ ".navbar-dropdown-content { background-color: var(--c-bg-raised); border-color: var(--c-border); }",
+ ".navbar-dropdown-item { color: var(--c-fg-muted); }",
+ ".navbar-dropdown-item:hover { background-color: var(--c-bg-hover); color: var(--c-fg); }",
+ "header { background-color: var(--c-bg); border-bottom-color: var(--c-border); }",
+ ".nav-brand { color: var(--c-accent); }",
+ "",
+ "/* Breadcrumbs */",
+ ".breadcrumb-sep { color: var(--c-fg-faint); }",
+ ".breadcrumb-current { color: var(--c-fg-muted); }",
+ ".breadcrumb-list a { color: var(--c-accent); }",
+ "",
+ "/* Task cards & panels */",
+ ".task-card, .stat-card, .task-detail, .task-summary,",
+ ".filter-form, .status-form, .diff-section, .review-actions",
+ "{ background-color: var(--c-bg-raised); border-color: var(--c-border); }",
+ ".task-card:hover { border-color: var(--c-accent-dim); }",
+ ".task-card-link, .task-card-link:visited { color: var(--c-fg); }",
+ ".task-id { color: var(--c-accent); }",
+ ".priority { color: var(--c-fg-muted); }",
+ ".blocking-impact { color: var(--c-fg-muted); background-color: var(--c-border); }",
+ ".task-title { color: var(--c-fg); }",
+ ".empty-msg, .info-msg { color: var(--c-fg-muted); }",
+ ".kb-preview { color: var(--c-fg-muted); }",
+ ".ready-link { color: var(--c-accent); }",
+ ".count-badge { background-color: var(--c-accent-dim); }",
+ ".description { background-color: var(--c-bg-raised); color: var(--c-fg); }",
+ "",
+ "/* Stat cards */",
+ ".stat-label { color: var(--c-fg-muted); }",
+ ".stat-card.badge-open { border-left-color: var(--c-yellow); }",
+ ".stat-card.badge-open > .stat-count { color: var(--c-yellow); }",
+ ".stat-card.badge-inprogress { border-left-color: var(--c-blue); }",
+ ".stat-card.badge-inprogress > .stat-count { color: var(--c-blue); }",
+ ".stat-card.badge-review { border-left-color: var(--c-purple); }",
+ ".stat-card.badge-review > .stat-count { color: var(--c-purple); }",
+ ".stat-card.badge-approved { border-left-color: var(--c-accent); }",
+ ".stat-card.badge-approved > .stat-count { color: var(--c-accent); }",
+ ".stat-card.badge-done { border-left-color: var(--c-green); }",
+ ".stat-card.badge-done > .stat-count { color: var(--c-green); }",
+ ".stat-card.badge-neutral { border-left-color: var(--c-fg-muted); }",
+ ".stat-card.badge-neutral > .stat-count { color: var(--c-fg-muted); }",
+ "",
+ "/* List groups */",
+ ".list-group { background-color: var(--c-bg-raised); border-color: var(--c-border); }",
+ ".list-group-item { border-bottom-color: var(--c-border-subtle); color: var(--c-fg); }",
+ ".list-group-item:visited { color: var(--c-fg); }",
+ ".list-group-item:hover { background-color: var(--c-bg-hover); }",
+ ".list-group-item-id { color: var(--c-accent); }",
+ ".list-group-item-title { color: var(--c-fg); }",
+ "",
+ "/* Status/priority/complexity dropdowns */",
+ ".status-dropdown-menu, .priority-dropdown-menu, .complexity-dropdown-menu",
+ "{ background-color: var(--c-bg-raised); border-color: var(--c-border); }",
+ "",
+ "/* Complexity badges */",
+ ".badge-complexity { color: var(--c-accent-dim); }",
+ ".badge-complexity-1 { color: var(--c-green); }",
+ ".badge-complexity-2 { color: var(--c-blue); }",
+ ".badge-complexity-3 { color: var(--c-yellow); }",
+ ".badge-complexity-4 { color: var(--c-orange); }",
+ ".badge-complexity-5 { color: var(--c-red); }",
+ ".badge-complexity-none { background-color: var(--c-bg-hover); color: var(--c-fg-muted); }",
+ "",
+ "/* Progress bars */",
+ ".progress-bar, .multi-progress-bar { background-color: var(--c-border); }",
+ ".progress-fill { background-color: var(--c-accent-dim); }",
+ ".progress-done, .legend-done { background-color: var(--c-green); }",
+ ".progress-inprogress, .legend-inprogress { background-color: var(--c-yellow); }",
+ ".progress-open, .legend-open { background-color: var(--c-blue); }",
+ ".progress-legend { color: var(--c-fg-muted); }",
+ "",
+ "/* Stats & summary sections */",
+ ".stats-section, .summary-section { background-color: var(--c-bg-raised); border-color: var(--c-border); }",
+ ".stats-label { color: var(--c-fg); }",
+ ".stats-count { color: var(--c-fg); }",
+ "",
+ "/* Task buttons */",
+ ".action-btn { background-color: var(--c-bg-raised); border-color: var(--c-border); color: var(--c-fg); }",
+ ".action-btn:hover { background-color: var(--c-bg-hover); border-color: var(--c-fg-faint); }",
+ ".action-btn-primary, .filter-btn, .submit-btn { background-color: var(--c-accent-dim); border-color: var(--c-accent-dim); }",
+ ".accept-btn { color: var(--c-green); }",
+ ".reject-btn { color: var(--c-red); }",
+ ".review-link-btn { color: var(--c-purple); }",
+ ".clear-btn { background-color: var(--c-bg-hover); color: var(--c-fg); border-color: var(--c-border); }",
+ ".clear-btn:hover { background-color: var(--c-bg-active); }",
+ ".btn-secondary, .load-more-btn { background-color: var(--c-fg-muted); }",
+ "",
+ "/* Task forms */",
+ ".filter-select, .filter-input, .status-select { border-color: var(--c-border); background-color: var(--c-bg); color: var(--c-fg); }",
+ ".reject-notes { border-color: var(--c-border); }",
+ ".description-textarea { border-color: var(--c-border); }",
+ ".form-group > label { color: var(--c-fg); }",
+ ".form-input, .form-textarea { border-color: var(--c-border); }",
+ ".form-input:focus, .form-textarea:focus { border-color: var(--c-accent-dim); }",
+ ".fact-create-form { background-color: var(--c-bg-raised); border-color: var(--c-border); }",
+ ".edit-link, .cancel-link { color: var(--c-accent); }",
+ "button.cancel-link { color: var(--c-red); }",
+ ".edit-description > summary { color: var(--c-accent); }",
+ ".task-link { color: var(--c-accent); }",
+ ".error-msg { color: var(--c-red); border-color: var(--c-red); }",
+ ".danger-zone { border-color: var(--c-red); }",
+ ".danger-zone > h2 { color: var(--c-red); }",
+ ".back-link { border-top-color: var(--c-border-subtle); }",
+ ".back-link > a { color: var(--c-fg-muted); }",
+ ".back-link > a:hover { color: var(--c-fg); }",
+ "",
+ "/* Execution details */",
+ ".execution-section { background-color: var(--c-bg-raised); border-color: var(--c-border); }",
+ ".metric-label { color: var(--c-fg-muted); }",
+ ".metric-card { background-color: var(--c-bg-raised); border-color: var(--c-border-subtle); }",
+ ".metric-card > .metric-value { color: var(--c-fg); }",
+ ".metric-card > .metric-label { color: var(--c-fg-muted); }",
+ ".amp-link { color: var(--c-accent); }",
+ ".amp-thread-btn { background-color: var(--c-purple); }",
+ ".retry-count { color: var(--c-orange); }",
+ ".attempts-divider { border-top-color: var(--c-border-subtle); }",
+ ".attempt-header { color: var(--c-fg); border-bottom-color: var(--c-border-subtle); }",
+ "",
+ "/* Activity timeline */",
+ ".activity-section { background-color: var(--c-bg-raised); border-color: var(--c-border); }",
+ ".activity-timeline::before { background-color: var(--c-border); }",
+ ".activity-icon { background-color: var(--c-bg-raised); border-color: var(--c-border); }",
+ ".activity-time { color: var(--c-fg-muted); }",
+ ".activity-message { color: var(--c-fg); }",
+ ".metadata-json { background-color: var(--c-bg-hover); }",
+ ".stage-claiming > .activity-icon { border-color: var(--c-blue); color: var(--c-blue); }",
+ ".stage-running > .activity-icon { border-color: var(--c-yellow); color: var(--c-yellow); }",
+ ".stage-reviewing > .activity-icon { border-color: var(--c-purple); color: var(--c-purple); }",
+ ".stage-retrying > .activity-icon { border-color: var(--c-orange); color: var(--c-orange); }",
+ ".stage-completed > .activity-icon { border-color: var(--c-green); color: var(--c-green); }",
+ ".stage-failed > .activity-icon { border-color: var(--c-red); color: var(--c-red); }",
+ "",
+ "/* Commits */",
+ ".commit-item { background-color: var(--c-bg-raised); border-color: var(--c-border-subtle); }",
+ ".commit-hash { color: var(--c-accent); background-color: var(--c-border); }",
+ ".commit-summary { color: var(--c-fg); }",
+ ".commit-meta { color: var(--c-fg-muted); }",
+ ".commit-files { color: var(--c-fg-faint); }",
+ "",
+ "/* Markdown */",
+ ".markdown-content { color: var(--c-fg); }",
+ ".md-h1 { border-bottom-color: var(--c-border-subtle); }",
+ ".md-code { background-color: var(--c-bg-raised); color: var(--c-fg); border-color: var(--c-border); }",
+ ".md-inline-code { background-color: var(--c-bg-hover); }",
+ "",
+ "/* Comments */",
+ ".comment-card { background-color: var(--c-bg-raised); border-color: var(--c-border-subtle); }",
+ ".comment-text { color: var(--c-fg); }",
+ ".author-human { color: var(--c-blue); }",
+ ".author-junior { color: var(--c-green); }",
+ ".comment-time { color: var(--c-fg-faint); }",
+ ".comment-textarea { border-color: var(--c-border); }",
+ ".comment-textarea:focus { border-color: var(--c-accent-dim); }",
+ "",
+ "/* Retry banners */",
+ ".retry-attempt { color: var(--c-fg); }",
+ ".retry-value { color: var(--c-fg-muted); }",
+ ".retry-hint { color: var(--c-fg-muted); }",
+ ".retry-warning-message { color: var(--c-red); }",
+ ".retry-commit { background-color: var(--c-bg-hover); }",
+ "",
+ "/* Time filters */",
+ ".time-filter-btn { border-color: var(--c-border); background-color: var(--c-bg-raised); color: var(--c-fg); }",
+ ".time-filter-btn:hover { border-color: var(--c-fg-faint); background-color: var(--c-bg-hover); }",
+ ".time-filter-btn.active { background-color: var(--c-accent-dim); border-color: var(--c-accent-dim); }",
+ ".time-filter-btn.active:hover { background-color: var(--c-accent); border-color: var(--c-accent); }",
+ "",
+ "/* Sort dropdown */",
+ ".sort-label { color: var(--c-fg-muted); }",
+ ".sort-dropdown-btn { border-color: var(--c-border); background-color: var(--c-bg-raised); color: var(--c-fg); }",
+ ".sort-dropdown-btn:hover { border-color: var(--c-fg-faint); background-color: var(--c-bg-hover); }",
+ "",
+ "/* Task meta */",
+ ".task-meta-id { background-color: var(--c-bg-hover); }",
+ ".task-meta-label { color: var(--c-fg-muted); }",
+ ".meta-sep { color: var(--c-fg); }",
+ "",
+ "/* Detail rows */",
+ ".detail-label { color: var(--c-fg-muted); }",
+ ".detail-section { border-top-color: var(--c-border-subtle); }",
+ ".dep-type, .child-status { color: var(--c-fg-muted); }",
+ ".child-title { color: var(--c-fg); }",
+ ".priority-desc { color: var(--c-fg-muted); }",
+ "",
+ "/* Timeline events */",
+ ".event-bubble { background-color: var(--c-bg-hover); }",
+ ".event-truncated { color: var(--c-fg-muted); }",
+ ".event-tool-call { border-left-color: var(--c-blue); }",
+ ".tool-name { color: var(--c-blue); }",
+ ".tool-summary { color: var(--c-fg-muted); }",
+ ".tool-output-pre, .tool-output { background-color: var(--c-bg); color: var(--c-fg); }",
+ ".event-tool-result { border-left-color: var(--c-green); }",
+ ".event-cost, .timeline-cost { color: var(--c-fg-muted); }",
+ ".line-count { color: var(--c-fg-muted); background-color: var(--c-bg-hover); }",
+ ".result-collapsible > summary, .output-collapsible > summary { color: var(--c-accent); }",
+ ".event-error { border-left-color: var(--c-red); }",
+ ".event-error > .event-label { color: var(--c-red); }",
+ ".error-message { color: var(--c-red); }",
+ ".event-complete { color: var(--c-green); }",
+ "",
+ "/* Unified timeline */",
+ ".unified-timeline-section { border-top-color: var(--c-border-subtle); }",
+ ".timeline-live-toggle { color: var(--c-green); border-color: var(--c-green); }",
+ ".timeline-live-toggle.timeline-live-paused { color: var(--c-fg-muted); background-color: var(--c-bg-hover); border-color: var(--c-border); }",
+ ".timeline-autoscroll-toggle { color: var(--c-blue); border-color: var(--c-blue); }",
+ ".timeline-autoscroll-toggle.timeline-autoscroll-disabled { color: var(--c-fg-muted); background-color: var(--c-bg-hover); border-color: var(--c-border); }",
+ ".timeline-live { color: var(--c-green); }",
+ ".actor-human { color: var(--c-purple); }",
+ ".actor-junior { color: var(--c-blue); }",
+ ".actor-system { color: var(--c-fg-muted); background-color: var(--c-bg-hover); }",
+ ".timeline-comment > .comment-bubble { background-color: var(--c-bg-hover); color: var(--c-fg); }",
+ ".status-change-text { color: var(--c-green); }",
+ ".timeline-activity { color: var(--c-fg-muted); }",
+ ".activity-detail { color: var(--c-fg-faint); }",
+ ".timeline-error > .error-message { color: var(--c-red); }",
+ ".timeline-thought > .thought-bubble { color: var(--c-yellow); }",
+ ".timeline-tool-call { border-left-color: var(--c-blue); }",
+ ".timeline-tool-result { border-left-color: var(--c-green); }",
+ ".timeline-checkpoint { border-left-color: var(--c-purple); }",
+ ".timeline-guardrail { border-left-color: var(--c-yellow); }",
+ ".timeline-guardrail > .guardrail-content { color: var(--c-yellow); }",
+ ".timeline-generic { color: var(--c-fg-muted); }",
+ ".formatted-json { background-color: var(--c-bg-raised); }",
+ "",
+ "/* Tool compact styles */",
+ ".tool-check { color: var(--c-green); }",
+ ".tool-label { color: var(--c-fg-muted); }",
+ ".tool-path { color: var(--c-blue); }",
+ ".tool-pattern { color: var(--c-purple); }",
+ ".tool-path-suffix { color: var(--c-fg-muted); }",
+ ".tool-bash-prompt { color: var(--c-yellow); }",
+ ".tool-bash-cmd { color: var(--c-fg); background-color: var(--c-bg-hover); }",
+ ".tool-args-pre { background-color: var(--c-bg-raised); }"
+ ]
+
statusBadgeClass :: Text -> Text
statusBadgeClass status = case status of
"Open" -> "badge badge-open"
diff --git a/Omni/Web/Style.hs b/Omni/Web/Style.hs
index 41c8d5fe..be4266a7 100644
--- a/Omni/Web/Style.hs
+++ b/Omni/Web/Style.hs
@@ -15,6 +15,9 @@ module Omni.Web.Style
( -- * Full rendered CSS
css,
+ -- * Light mode CSS (append after all dark styles)
+ lightModeCss,
+
-- * Design tokens (re-export for other Clay modules)
cBg,
cBgRaised,
@@ -53,6 +56,7 @@ import qualified Clay.Media as Media
import qualified Clay.Stylesheet as Stylesheet
import qualified Data.Text.Lazy as LazyText
import qualified Lucid
+import qualified Lucid.Base as Lucid
-- ============================================================================
-- Design Tokens
@@ -553,6 +557,122 @@ selectionStyles :: Css
selectionStyles =
Stylesheet.key "::selection" ("{ background: rgba(111,179,192,0.35); }" :: Text)
+-- ============================================================================
+-- Light Mode Support
+-- ============================================================================
+
+-- | CSS custom properties with dark defaults, light-mode @media overrides,
+-- and var() rules for all shared design-system selectors.
+--
+-- Append this AFTER all Clay-generated dark styles so the var() declarations
+-- win the cascade. In dark mode the vars resolve to the same values Clay
+-- hard-coded; in light mode they resolve to the light palette.
+lightModeCss :: LazyText.Text
+lightModeCss =
+ LazyText.unlines
+ [ "/* ── Light theme: custom properties ─────────────────────── */",
+ ":root {",
+ " --c-bg: #232025;",
+ " --c-bg-raised: #2c292e;",
+ " --c-bg-hover: #322f34;",
+ " --c-bg-active: #3b393e;",
+ " --c-border: #3b393e;",
+ " --c-border-subtle: #322f34;",
+ " --c-fg: #efd5c5;",
+ " --c-fg-muted: #8f8886;",
+ " --c-fg-faint: #6b6568;",
+ " --c-accent: #6fb3c0;",
+ " --c-accent-dim: #5a9aa6;",
+ " --c-link: #57b0ff;",
+ " --c-green: #51b04f;",
+ " --c-yellow: #c0b24f;",
+ " --c-orange: #f3c09a;",
+ " --c-red: #ff6f6f;",
+ " --c-purple: #d0b0ff;",
+ " --c-blue: #57b0ff;",
+ "}",
+ "@media (prefers-color-scheme: light) {",
+ " :root {",
+ " --c-bg: #faf8f6;",
+ " --c-bg-raised: #ffffff;",
+ " --c-bg-hover: #f0eeec;",
+ " --c-bg-active: #e6e3e0;",
+ " --c-border: #d4d0cc;",
+ " --c-border-subtle: #e8e5e2;",
+ " --c-fg: #232025;",
+ " --c-fg-muted: #6b6568;",
+ " --c-fg-faint: #9a9497;",
+ " --c-accent: #4a8f9a;",
+ " --c-accent-dim: #3d7a84;",
+ " --c-link: #2563eb;",
+ " --c-green: #16a34a;",
+ " --c-yellow: #a38a00;",
+ " --c-orange: #c2601a;",
+ " --c-red: #dc2626;",
+ " --c-purple: #7c3aed;",
+ " --c-blue: #2563eb;",
+ " }",
+ " ::selection { background: rgba(37,99,235,0.2); }",
+ " ::-webkit-scrollbar-thumb { background: #d4d0cc; }",
+ "}",
+ "",
+ "/* ── Light theme: var() overrides for shared design system ─ */",
+ "body { background-color: var(--c-bg); color: var(--c-fg); }",
+ "a, a:visited { color: var(--c-link); }",
+ "h2 { color: var(--c-fg); }",
+ "code { background-color: var(--c-bg-hover); border-color: var(--c-border); }",
+ "pre { background-color: var(--c-bg); border-color: var(--c-border); }",
+ ".reading > blockquote { border-left-color: var(--c-accent-dim); color: var(--c-fg-muted); }",
+ "",
+ "/* Navigation */",
+ ".on-nav { background-color: var(--c-bg); border-bottom-color: var(--c-border); }",
+ ".on-nav-brand { color: var(--c-accent); }",
+ ".on-nav-link { color: var(--c-fg-muted); }",
+ ".on-nav-link:hover { background-color: var(--c-bg-hover); color: var(--c-fg); }",
+ ".on-active { color: var(--c-accent); background-color: var(--c-bg-hover); }",
+ "",
+ "/* Cards */",
+ ".card { background-color: var(--c-bg-raised); border-color: var(--c-border); }",
+ ".card-link:hover > .card { border-color: var(--c-accent-dim); }",
+ "",
+ "/* Badges */",
+ ".badge-open { color: var(--c-yellow); }",
+ ".badge-inprogress { color: var(--c-blue); }",
+ ".badge-review { color: var(--c-purple); }",
+ ".badge-approved { color: var(--c-accent); }",
+ ".badge-done { color: var(--c-green); }",
+ ".badge-needshelp { color: var(--c-orange); }",
+ ".badge-p0 { color: var(--c-red); }",
+ ".badge-p1 { color: var(--c-orange); }",
+ ".badge-p2 { color: var(--c-blue); }",
+ ".badge-p3 { background-color: var(--c-bg-hover); color: var(--c-fg-muted); }",
+ ".badge-p4 { background-color: var(--c-bg-hover); color: var(--c-fg-faint); }",
+ "",
+ "/* Buttons */",
+ ".btn { background-color: var(--c-bg-raised); border-color: var(--c-border); color: var(--c-fg); }",
+ ".btn:hover { background-color: var(--c-bg-hover); border-color: var(--c-fg-faint); }",
+ ".btn:active { background-color: var(--c-bg-active); }",
+ ".btn-primary { background-color: var(--c-accent-dim); border-color: var(--c-accent-dim); }",
+ ".btn-primary:hover { background-color: var(--c-accent); border-color: var(--c-accent); }",
+ ".btn-ghost:hover { background-color: var(--c-bg-hover); }",
+ "",
+ "/* Forms */",
+ "input, select, textarea { color: var(--c-fg); background-color: var(--c-bg); border-color: var(--c-border); }",
+ "input:focus, select:focus, textarea:focus { border-color: var(--c-accent-dim); }",
+ "",
+ "/* Tables */",
+ "th, td { border-bottom-color: var(--c-border); }",
+ "th { color: var(--c-fg-muted); }",
+ "tr:hover { background-color: var(--c-bg-hover); }",
+ "",
+ "/* Utilities */",
+ ".muted { color: var(--c-fg-muted); }",
+ ".faint { color: var(--c-fg-faint); }",
+ ".accent { color: var(--c-accent); }",
+ ".empty-state { color: var(--c-fg-faint); }",
+ ".divider { border-top-color: var(--c-border); }"
+ ]
+
-- ============================================================================
-- Shared HTML Components
-- ============================================================================
@@ -583,9 +703,10 @@ sharedShell title' activePage' content' =
Lucid.meta_ [Lucid.name_ "viewport", Lucid.content_ "width=device-width, initial-scale=1, viewport-fit=cover"]
Lucid.meta_ [Lucid.name_ "apple-mobile-web-app-capable", Lucid.content_ "yes"]
Lucid.meta_ [Lucid.name_ "apple-mobile-web-app-status-bar-style", Lucid.content_ "black-translucent"]
- Lucid.meta_ [Lucid.name_ "theme-color", Lucid.content_ "#232025"]
+ Lucid.meta_ [Lucid.name_ "theme-color", Lucid.content_ "#232025", Lucid.makeAttribute "media" "(prefers-color-scheme: dark)"]
+ Lucid.meta_ [Lucid.name_ "theme-color", Lucid.content_ "#faf8f6", Lucid.makeAttribute "media" "(prefers-color-scheme: light)"]
Lucid.title_ (Lucid.toHtml title')
- Lucid.style_ (LazyText.toStrict css)
+ Lucid.style_ (LazyText.toStrict (css <> lightModeCss))
Lucid.body_ <| do
sharedNav activePage'
content'