feat(web): add app theme toggle (default / dark / system / blue) and theme support#605
feat(web): add app theme toggle (default / dark / system / blue) and theme support#605donnfelker wants to merge 4 commits into
Conversation
* feat(web): add app theme toggle (default / dark / system)
Adds an "App theme" section to Settings → Appearance, above the
existing "Code highlighting" group. Default / Dark / System map to
next-themes' setTheme(), which already handled the .dark class on
<html>, localStorage persistence, and cross-tab sync — this PR is
the missing UI on top of that.
Also fixes a small set of components that bypassed the design tokens
and broke in dark mode:
- participants-section.tsx, session/[id]/page.tsx: avatar borders
swapped from `border-white` → `border-background` so they remain
visible against either palette.
- sandbox-settings.tsx: toggle thumb swapped from `bg-white` →
`bg-foreground` for high contrast in both modes.
ToggleGroup `value` is always a string (`theme ?? "system"`) to avoid
Radix's "changing from uncontrolled to controlled" warning during
hydration. New regression test guards that.
Adds docs/THEMING.md describing the design token system, the
runtime switching mechanism, and how to add a future themed palette
via `[data-theme="..."]` selectors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(web): named themes + configurable deploy-time default
Builds on the App theme toggle: themes are now a registry of named
entries, the picker lists all of them, and a deployer can pick which
one is applied on first load via a new tfvar. Useful for shipping a
company-branded theme out of the box — users can still switch in
Settings → Appearance and their choice persists.
- packages/web/src/lib/app-themes.ts: new registry of {id, label,
colorScheme} tuples. Built-ins: light/dark/system. Ships a sample
"Blue" palette as a worked example so the feature is verifiable
out of the box; deployers replace or remove it.
- providers.tsx: passes themes={APP_THEME_IDS} and
defaultTheme={APP_DEFAULT_THEME} to next-themes.
- site-config.ts: reads NEXT_PUBLIC_APP_DEFAULT_THEME, validated via
resolveDefaultAppTheme — an unknown id falls back to "system" so
a tfvars typo never ships a broken UI.
- appearance-settings.tsx: ToggleGroup → <select> listing every
registered theme. Matches the existing Code Highlighting <select>
styling and scales as more palettes are added.
- syntax-highlight-theme.tsx: when the active theme is a named
palette ("blue", etc.) and Code Highlighting is on "system", reads
the palette's colorScheme from the registry so hljs picks the
matching light/dark stylesheet.
- globals.css: .blue rule for the sample palette with comments
explaining how to override or remove it.
Terraform plumbing mirrors the existing app_name/app_icon pattern:
- variables.tf: app_default_theme (default "system") with a
contains([...]) validation block that gates unknown ids at plan
time; doc string explains the company-theme use case.
- web-cloudflare.tf: NEXT_PUBLIC_APP_DEFAULT_THEME wired into both
the build env and the wrangler.production.toml [vars] block.
- web-vercel.tf: corresponding Vercel project env entry.
- terraform.tfvars.example: commented example with the
company-theme rationale and list of valid ids.
docs/THEMING.md updated with three new sections: the registry,
"Setting a default theme on deploy", and "Adding a new branded
theme" (4-step recipe covering CSS, registry, the Terraform
validation list, and the optional tfvar default).
Tests: appearance-settings.test.tsx now asserts against the
registry contents and the dropdown UI; controlled/uncontrolled
regression guard preserved. 28 files / 214 web tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(web): explain colorScheme field on AppTheme
Move the rationale next to the type definition so it's visible at
the point of use. Reviewer feedback on PR #3 — the field-level
comment is more discoverable than the file-level docblock when a
maintainer hovers the type or reads it inline.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
📝 WalkthroughWalkthroughThis PR implements a token-based theming system: theme registry/types, a blue branded CSS theme, ThemeProvider wiring with explicit themes and deploy-time default, settings UI and tests for an App theme picker, syntax-highlight integration, component token updates, documentation, and Terraform wiring for NEXT_PUBLIC_APP_DEFAULT_THEME. ChangesApp Theming System
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/web/src/app/globals.css (1)
223-249: ⚡ Quick winConsider adding a dark variant for the Blue theme.
The
.blueclass provides only a light palette. Users who prefer dark mode will see the standard dark theme when "Blue" is selected and their OS is in dark mode, which may be unexpected.Consider adding a
.blue.dark { ... }block with a dark-mode variant of the blue palette for consistency, or document that the Blue theme is light-only.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/web/src/app/globals.css` around lines 223 - 249, The Blue theme (.blue) only defines a light palette so users in dark mode get the default dark theme; add a dark-mode variant by creating a .blue.dark (or .dark .blue depending on your theme class strategy) CSS block that overrides the CSS custom properties (e.g., --background, --foreground, --card, --popover, --primary, --accent, --muted, --border, --input, --ring) with darker values to provide a consistent Blue dark theme, or alternatively add a comment in the .blue block documenting that Blue is light-only so the behavior is explicit.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/web/src/app/globals.css`:
- Around line 223-249: The Blue theme (.blue) only defines a light palette so
users in dark mode get the default dark theme; add a dark-mode variant by
creating a .blue.dark (or .dark .blue depending on your theme class strategy)
CSS block that overrides the CSS custom properties (e.g., --background,
--foreground, --card, --popover, --primary, --accent, --muted, --border,
--input, --ring) with darker values to provide a consistent Blue dark theme, or
alternatively add a comment in the .blue block documenting that Blue is
light-only so the behavior is explicit.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 7dc354b1-2f67-477e-b28b-3fdad82ae550
📒 Files selected for processing (16)
docs/SETUP_GUIDE.mddocs/THEMING.mdpackages/web/src/app/(app)/session/[id]/page.tsxpackages/web/src/app/globals.csspackages/web/src/app/providers.tsxpackages/web/src/components/settings/appearance-settings.test.tsxpackages/web/src/components/settings/appearance-settings.tsxpackages/web/src/components/settings/sandbox-settings.tsxpackages/web/src/components/sidebar/participants-section.tsxpackages/web/src/components/syntax-highlight-theme.tsxpackages/web/src/lib/app-themes.tspackages/web/src/lib/site-config.tsterraform/environments/production/terraform.tfvars.exampleterraform/environments/production/variables.tfterraform/environments/production/web-cloudflare.tfterraform/environments/production/web-vercel.tf
Adds JSDoc to the previously-undocumented exports in app-themes.ts so docstring-coverage clears the 80% threshold, and rewrites the .blue guidance in globals.css and THEMING.md to reflect that next-themes' attribute="class" puts a single theme class on <html> at a time — .blue.dark would never match, so a branded dark variant must be registered as its own theme entry instead. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Pulls the .blue example out of globals.css into its own file imported from app/layout.tsx after globals.css (so the cascade order makes .blue's tokens override :root). Each branded theme is now one self-contained file — adding or removing a brand is a localized change across themes/<id>.css, the layout.tsx import, the APP_THEMES entry, and the variables.tf validation list. THEMING.md and the app-themes.ts docblock are updated to reflect the new file pattern. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
CleanShot.2026-05-09.at.17.40.41.mp4
Adds an App theme section to Settings → Appearance, above "Code highlighting". The CSS variables and
next-themesprovider were already wired — this is the missing UI plus a small registry pattern so deployments can ship branded palettes without touching component code.packages/web/src/lib/app-themes.ts— a list of{id, label, colorScheme}tuples. Built-ins: Default (light), Dark, System. A sample "Blue" palette ships as a worked example; rename, replace, or remove freely.NEXT_PUBLIC_APP_DEFAULT_THEME(and a matchingapp_default_themeTerraform variable, validated against the registry). Lets a deployment land new visitors on a branded theme out of the box; users can still switch and their choice persists.<select>matching the existing Code Highlighting rows. Scales cleanly as palettes are added.colorSchemefield.bg-white/border-whiteand broke in dark mode.docs/THEMING.mddocuments tokens, runtime switching, the registry, the deploy-time default, and a 4-step "add a branded theme" recipe.No breaking changes: existing
localStorage.themevalues keep working;unset or unknownNEXT_PUBLIC_APP_DEFAULT_THEMEfalls back to"system"so a deploy-config typo can't ship a broken UI.Summary by CodeRabbit
Documentation
New Features
Style
Tests