feat(theme): light/dark theme system v0.1 (designer tokens)#61
Merged
Conversation
Per designer/openforge-theme-tokens-v0.1: introduce three-layer token system. Adds full Semantic token set (surface/text/border/accent/status/ shadow) on :root, plus dark layer via @media (prefers-color-scheme: dark) and explicit :root[data-theme="dark"] override. Existing legacy vars (--slack-purple, --rail-bg, etc.) kept as aliases to the new tokens for zero-regression on current selectors. Step 2 will sweep var(--x, #hex) fallbacks; step 3 adds the sidebar theme toggle UI.
Per tokens-v0.1 §8 禁忌清单: hex fallback is the temperature that breeds PR #40 -style dark-mode bugs (fallback黑字 落到 dark 黑底). Replace 54 occurrences of `var(--name, #hex)` with bare `var(--name)`, remapping a handful of legacy/typo'd token names (--soft, --link, --muted, --text-muted, --text-sub, --border-soft, --chip-bg, --chip-fg, --row-hover, --panel, --color-primary) onto the semantic tokens added in step 1. Non-hex var() fallbacks (layout dims, rgba) untouched — those are not the bug class the rule targets.
icon-rail bottom now hosts a three-state theme toggle (light / dark / system). Bootstrap script in index.html <head> applies the persisted choice (localStorage `openforge.theme`) by setting <html data-theme> before first paint, so there is no FOUC on reload. Default = system (no data-theme attribute) → @media (prefers-color-scheme: dark) governs. Explicit light/dark wins. Closes the v0.1 rollout described in designer/openforge-theme-tokens-v0.1.md.
🤖 bot-review (comment-only · phase 1)Diff: Red-line checks:
Needs human review — these paths are not eligible for future auto-approve:
Phase 1: this bot leaves comments only. Auto-approve will be enabled per-path after 1–2 weeks of clean runs. Promotion plan: judy PR #42 follow-up. |
Per dora review on PR #61: clearing var(--x, #hex) fallbacks was not enough. Components were bypassing the token layer entirely with direct hex literals (background: #fff, color: #1d1c1d, …) so flipping data-theme had no effect on most surfaces. This sweep: - background: #fff/#ffffff/#fafafa/#f8f8f8/#f4f5f7 → --bg / --surface-{1,2,3} - color: #1d1c1d/#1c1917/#616061/#78716c/#5B6675/#8d8d8d/#a8a29e → --text / --text-mute / --text-soft - color: #fff (on accents) → --text-on-accent - border 1px greys (#ddd/#e5e5e5/#eaeaea/#e7e5e4/#E1E4E8) → --border - accent blacks (#0a0a0a/#1a1a1a) → --accent / --accent-hover - brand blues (#3B82F6/#2563EB/#60A5FA/#5b9bff/#1264a3) → --brand-blue{,-hover,-soft} - soft surfaces (#ececec/#f1f1f1/#f0f0f0/#e8f1ff) → --surface-hover / --accent-soft / --brand-blue-soft - status (#dc2626/#16a34a/#007a5a/#1a7a48/#c00/#fee/#fef2f2/#f0fdf4/#fffbeb/#fff1f2/#e6f5ed/#fecaca/#bbf7d0) → --danger / --ok / --warn + -soft variants Deliberately NOT remapped: - icon-rail (#1a1d24/#2a2f3a/#cfd2d8/#6f7480) — always-dark sidebar by design, not theme-driven - taskcenter (.tc-*) — Portal-style component with its own dark palette (#171a21/#262b36/#7c5cff/…), per tokens-v0.1 §2 "Portal components keep their own tokens" - brand-fixed avatars (.av-milk/sentry/bugfix/milly/kb) — slack-style accent identifiers, locked - gradients in activity row avatars — purely decorative - mermaid-error scoped dark callout — locked by upstream convention - Forge orange ★ (#F5A623) — locked per Favorites PRD §7 Should fix dora's screenshots: white cards (modal/dropdown/composer/ preview/files panels) now follow --bg, text follows --text, borders follow --border.
PR #61 dora review caught: squad-rail (left column) inverted in dark because it consumed --slack-purple → --accent, which flips to white in dark; .squad-item color used --text-on-accent which flips to black → black text on white panel. Fix per tokens-v0.1 §2 (Portal/always-dark components keep own tokens): turn #squad-rail into a token island — locally declare --rail-bg / --rail-fg / --rail-hover / --rail-active = fixed dark, and shadow inherited --text-on-accent / --slack-active with fixed dark values so all descendant selectors (.brand, .squad-item, .new-squad-btn, .squad-unread-badge, .squad-delete:hover, etc.) keep working unchanged without needing per-selector edits. Drops the --slack-purple / --slack-purple-hover aliases (no other consumer in web/, grep clean). post-composer dark contrast nit (--surface-2 #1C1F25 vs --bg #0E1014 visually close) deferred to v0.2 per dora — semantic is correct, only visual gap to widen.
SymbolStar
added a commit
that referenced
this pull request
Jun 4, 2026
* feat(theme): add --surface-input semantic token for composers New --surface-input token gives post/thread composer textareas a distinct input surface separate from --surface-2 cards/aside: - light: #FFFFFF (relies on existing --border for delineation) - dark: #22262E (~8% brighter than --bg #0E1014, layer separation) Applied to #post-composer textarea and #thread-composer textarea only. Modals / nested cards untouched (still --surface-2 / --bg). Part of dark theme v0.2 (follow-up to #61). * feat(theme): split --text-on-* axis for status surfaces --text-on-accent was overloaded as the foreground for accent/brand AND for status-colored surfaces (.btn-danger hover red, #post-composer green send btn, modal submit). Dark mode flipped --text-on-accent to near-black which broke contrast on those status fills. Add three status-aligned foreground tokens: --text-on-ok = #FFFFFF (light + dark) --text-on-warn = #1D1C1D (yellow surfaces; white fails WCAG AA) --text-on-danger = #FFFFFF (light + dark) Switch: .btn-danger:hover -> --text-on-danger #post-composer button (send/ok) -> --text-on-ok .modal-actions button[type=submit] -> --text-on-ok Accent/brand buttons keep --text-on-accent (theme-inverted by design). * fix(theme): bump dark --text-mute for AAA readability Dark --text-mute was #A4ABB6 — visually drifted on dark surfaces, placeholders and secondary text felt washed out. Bump to #B7BFCA: contrast vs --bg #0E1014 climbs from ~7:1 to ~8.5:1, still AAA, and visibly lifts placeholder/secondary text without flattening the mute/text hierarchy. Light --text-mute untouched. * fix(theme): make .ghost-btn read as a button .ghost-btn already carries border-1px in its base, so 'transparent until hover' isn't an option here — instead lift the hover affordance: - hover: border-color jumps to --border-strong (was --text-soft) - hover: background fills with --surface-hover light + dark both gain a tactile press surface without any token change. Border thickness stays 1px in all states, so no layout shift on hover. (Designer note used '.btn-ghost' / transparent-base; codebase ships '.ghost-btn' with a permanent 1px border, so executed equivalent without layout regression.) * fix(theme): brighten dark warn/danger soft banners Dark --warn-soft #2A1F0A and --danger-soft #2A1010 read as muddy near-black brown/maroon — banner intent was bleeding into the surface. --warn-soft #2A1F0A -> #3A2C0F (brighter, slightly desaturated) --danger-soft #2A1010 -> #3A1818 (brighter, slightly desaturated) --ok-soft #0F2A1F kept as-is (designer sign-off retained). Light mode untouched. Completes dark theme v0.2.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
落地 dora 的 [[designer/openforge-theme-tokens-v0.1.md]] —— 三层 token (Primitive → Semantic → Component),light/dark 都是一等公民。
按设计稿 §7 分 3 个独立 commit,方便逐步 review / 回滚:
Commit 1 —
8ca5c8fsemantic token 层:root补齐 Semantic token(surface/text/border/accent/status/shadow)@media (prefers-color-scheme: dark) :root:not([data-theme="light"])+:root[data-theme="dark"]--slack-purple/--rail-bg…)保留作别名,零回归Commit 2 —
90208b1清 fallbackvar(--x, #hex),54 处全清--soft → --surface-2、--muted → --text-mute、--color-primary → --brand-blue…)Commit 3 —
449a98a切换 UI<head>内联 bootstrap script 在 first paint 前读localStorage.openforge.theme并打<html data-theme>,无 FOUC还没动的(dora v0.2 调整 + 后续)
验收
web/app.jsNode syntax check 过、web/index.htmlparse 过cc @scott @designer