UI Unification Work#
This document records UI inconsistencies across OpenProgram surfaces and outlines prioritized work for a unified visual identity.
Context#
OpenProgram consists of multiple UI surfaces:
- Web App (
apps/web/) — the main browser-based interface - CLI/TUI (
apps/cli/) — terminal interface - Docs Site (
scripts/docs_site/) — documentation website - Marketing Page (
website/index.html) — landing page - Desktop Chrome (
apps/desktop/main.js) — Electron window chrome
Each surface evolved independently, resulting in divergent color palettes, typography, and design tokens.
Current Appearance#
Settings opens on General. Appearance follows an Obsidian-like stack and keeps schema 3 (mode × named package):
- Mode: Auto / Light / Dark. Auto follows the system; it is a mode, not a theme.
- Theme package: Beige / Neutral / Aurora only. Each package has a light and a dark
data-theme. Custom is not a fourth skin. - Accent color: a color input, presets taken from each package's default
--accent-orange, and Reset to theme default. Empty/default uses the current package accent. The override writes--accent-orange,--accent-fill, and a derived--accent-orange-hover. - CSS overlay: user CSS sits on top of the selected package, gated by Enable custom CSS. Insert template writes a starter that targets
html/ currentdata-themevalues. Clear empties the snippet. Old[data-theme="custom"]/[data-theme="custom-light"]selectors are rewritten ontodata-custom-cssso they keep applying.
Storage stays schema 3 (agentic_theme_style + agentic_theme_mode). Accent uses agentic_theme_accent. Overlay enable uses agentic_custom_css_enabled. Combined agentic_theme values still migrate:
auto→ beige + autobeige-dark/beige-light→ beige + dark/lightdark/light(schema 2) → neutral + dark/lightaurora/aurora-light→ aurora + dark/lightcustom/custom-light→ beige + the saved mode; overlay turns on when any custom CSS is saved
custom / custom-light remain fallback theme ids and CSS files for old snippets. They do not appear as style cards. The theme contract still validates the six built-in package token files; default accents stay sourced from those CSS files.
Later Work (Prioritized TODO)#
The following inconsistencies remain and should be addressed in future PRs.
1. Web Token Cleanup (High Priority)#
Issue: :root fallback in base.css mixes beige surfaces with neutral blue accents.
/* base.css :root claims to be beige-dark but has: */
--accent-orange: #6ea8fe; /* This is neutral blue, not beige coral */
Files:
apps/web/app/styles/base.css— fallback palette should be consistent beige-dark
Action: Decide whether :root fallback should be beige or neutral, then reconcile accent colors.
2. Docs Site Palette Drift (High Priority)#
Issue: Docs site (scripts/docs_site/assets/site.css) claims to follow base.css but values diverge:
| Token | Docs light | Web beige-light | Docs dark | Web beige-dark |
|---|---|---|---|---|
--acc / --accent-orange |
#b8651f |
#c15f3c |
#d19a66 |
#d97757 |
--bg / --bg-primary |
— | — | #1f1f1e |
#262624 |
Files:
scripts/docs_site/assets/site.css- Storage key is
op-docs-theme(light/dark only), independent of app theme
Additional drift:
- Docs body type is 15.5px vs app
--fs-base: 14px
Action: Align docs palette with Web's beige theme, or explicitly document that docs site is independent.
3. CLI/TUI Theme Mismatch (Medium Priority)#
Issue: CLI/TUI always uses Claude orange #d97757 regardless of Web theme. Python/Rich setup wizard doesn't read Web tokens.
Files:
apps/cli/src/theme/themes.ts— TypeScript theme definitionsapps/cli/python/openprogram_cli/_impl/repl/banner.py— Rich colors (bright_blue, rainbow)- Setup wizard comments mention "OpenClaw-style"
Action: Either:
- Make CLI/TUI read Web's theme preference and apply consistent colors, or
- Document that CLI is intentionally a separate brand
4. Marketing Page Independent Brand (Low Priority)#
Issue: Marketing page (website/index.html) has a third brand:
--bg: #07080a
--teal: #5eead4
--violet: #a78bfa
Desktop icon SVG uses blue/purple (#4A9FE1 / #915FD5).
Action: Decide whether marketing page should align with Web beige/neutral or remain independent.
5. Desktop Chrome Follows the Resolved Theme#
The first pixel of the Electron window matches the already resolved web theme. BrowserWindow is created with --bg-primary from that theme — not #141416.
Resolution uses the same schema-3 keys the web app writes (agentic_theme_style + agentic_theme_mode, plus legacy agentic_theme). Desktop reads Chromium localStorage under userData, then a theme-prefs.json cache written on theme change. auto follows nativeTheme.shouldUseDarkColors.
apps/desktop/theme-chrome.js holds the --bg-primary / --accent-orange map copied from apps/web/app/styles/themes/*.css. Stored custom / custom-light resolve to the beige pair (#262624 / #faf9f5). Worker error pages and directory-listing HTML use the same chrome tokens; when an accent override is set, listing/error link color uses that accent, while window backgroundColor stays the package --bg-primary. After a theme change the renderer calls theme.setChrome, which updates every BrowserWindow background so the next show/reload does not flash the old color.
Window-state persistence (normal bounds vs maximize/fullscreen, display fallback, titlebar resize hit-testing) is unchanged; see window-state.md.
6. Ghost Tokens / Spec Drift (Low Priority)#
Issue: Tokens used in code but not in the 58-token contract, or documented in specs but not implemented:
Used but not contracted:
--text-dim(used in manage-page, files-panel, plugins, skills; contract has--text-muted)
Documented but not implemented:
--bg-surface(mentioned insurface-system.md)--text-on-accent(mentioned in specs)
Files:
docs/reference/design/ui/surface-system.md
Action: Either add missing tokens to contract or remove references.
7. Button Spec Drift (Low Priority)#
Issue: button.tsx hover uses bg-secondary instead of brand fill. Comments mention 36px / --ui-button-h but base.css is 30px. Manage-page tabs use border-radius: 6px vs --ui-button-radius 10px.
Action: Reconcile button heights and radii across components.
8. Naming Debt (Low Priority)#
Issue:
--accent-orangeused for blue/teal themes (confusing name)- shadcn
--accentmeans--bg-hover, not primary color - Stale comments in
dark.css/aurora.cssstill mention "coral"
Action: Rename --accent-orange → --accent-primary or similar. Update stale comments.
9. Mixed Component Kits (Low Priority)#
Issue:
- Composer menus use Base UI
- Rest of app uses Radix/shadcn
- Tailwind v3 config (
tailwind.config.ts) and v4@themeboth exist - Effort color logic (
effort-color.ts) uses independent HSL, not theme accent - Browser glyphs (
center-tabs.module.css) hardcode#8b5cf6
Action: Converge on one component library (Radix) and one Tailwind version (v4).
Stale Branch Note#
origin/codex/theme-style-mode started mode × style separation under the old web/ tree. This PR supersedes that branch; it can be archived.
Prioritized Roadmap#
- Web token cleanup — fix
:rootfallback, ensure beige-dark is consistent default - Docs palette — align with Web beige or document independence
- CLI/TUI colors — either integrate with Web theme or document separate brand
- Ghost tokens — add to contract or remove from code/specs
- Marketing/icon — decide alignment or independence
- Button/naming debt — reconcile heights, radii, and token names
- Component kit — converge on Radix + Tailwind v4
Implementation Status#
- Settings Appearance: ✅ Complete (Mode × Beige/Neutral/Aurora × accent picker × CSS overlay; stored Custom migrates to beige)
- Web token cleanup: ❌ Not started
- Docs palette: ❌ Not started
- Desktop window-state: ✅ Complete
- Desktop chrome color flash: ✅ Complete (first pixel and chrome HTML follow resolved
--bg-primary) - CLI/TUI colors: ❌ Not started
- Ghost tokens: ❌ Not started
- Marketing/icon: ❌ Not started
- Button/naming debt: ❌ Not started
- Component kit: ❌ Not started