Skip to content

feat(appearance): add system font preference - #1038

Open
sudo-eugene wants to merge 5 commits into
siteboon:mainfrom
TheWebEng:codex/system-font-setting
Open

feat(appearance): add system font preference#1038
sudo-eugene wants to merge 5 commits into
siteboon:mainfrom
TheWebEng:codex/system-font-setting

Conversation

@sudo-eugene

@sudo-eugene sudo-eugene commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

What changed

CloudCLI now offers a persistent Use system font option under Appearance → Typography. Enabling it replaces both Encode Sans in the general interface and Merriweather in chat and reading views with the device's native system font; disabling it restores the existing typography.

Why

The current font choices are applied globally, leaving users who prefer their platform's native typography without a consistent alternative. A single preference covers both font layers so the interface does not end up with a mixture of system and bundled fonts.

The switch changes shared font variables rather than replacing individual component classes. This keeps the existing typography as the default, applies the preference to current and future views that use the shared font definitions, and avoids maintaining separate font overrides throughout the component tree.

What this enables

Users can match CloudCLI's typography to their operating system for a more native and personally readable interface, with their choice retained across reloads.

Screenshots

Before

CleanShot 2026-07-18 at 20 43 14

After

CleanShot 2026-07-18 at 20 41 27 CleanShot 2026-07-18 at 20 41 12

Test plan

  • npm run typecheck
  • npm run lint (passes with the repository's existing warnings)
  • npm run build (passes with the repository's existing CSS and bundle-size warnings)
  • Verified the toggle restores Encode Sans and Merriweather when disabled
  • Verified the toggle applies the system font to both interface and reading styles when enabled
  • Verified the enabled preference persists after a full page reload

Summary by CodeRabbit

  • New Features
    • Added an Appearance → Typography option to switch between the app’s fonts and system fonts.
    • The preference is saved and applied when the app starts, preventing visible font changes during loading.
  • UI/Localization
    • Added labels and descriptive text for the system font setting.
  • Styling
    • Updated typography styling to support theme-based fonts and a system-font override.
  • Reliability
    • Improved handling of saved font preferences when browser storage is unavailable.

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c36f34b7-602d-477e-bc1f-b7b86bec77e2

📥 Commits

Reviewing files that changed from the base of the PR and between 8f28b23 and ce7ed85.

📒 Files selected for processing (3)
  • src/contexts/ThemeContext.jsx
  • src/main.jsx
  • src/utils/localStorage.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main.jsx
  • src/contexts/ThemeContext.jsx

📝 Walkthrough

Walkthrough

Changes

The settings UI now controls a persisted system-font preference. Theme context synchronizes the preference with the root class and localStorage. CSS variables and Tailwind font definitions apply the selected font stack.

System Font Preference

Layer / File(s) Summary
Font token configuration
src/index.css, tailwind.config.js
Font stacks now use CSS variables. Root-level system-font overrides apply to body and Tailwind typography.
Theme preference state
src/contexts/ThemeContext.jsx, src/utils/localStorage.js, src/components/settings/hooks/useSettingsController.ts
The preference is initialized, persisted, synchronized to the root class, memoized in theme context, and exposed through the settings controller.
Early preference initialization
src/main.jsx
The saved preference is applied to the root element before React renders.
Appearance settings integration
src/components/settings/view/Settings.tsx, src/components/settings/view/tabs/AppearanceSettingsTab.tsx, src/i18n/locales/en/settings.json
The appearance tab renders a localized system-font toggle wired to the controller state.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant AppearanceSettingsTab
  participant SettingsController
  participant ThemeProvider
  participant LocalStorage
  participant DocumentRoot
  User->>AppearanceSettingsTab: Toggle system font
  AppearanceSettingsTab->>SettingsController: onUseSystemFontChange(value)
  SettingsController->>ThemeProvider: setUseSystemFont(value)
  ThemeProvider->>LocalStorage: Save preference
  ThemeProvider->>DocumentRoot: Toggle system-font class
  DocumentRoot->>User: Apply system font variables
Loading

Possibly related PRs

Poem

A rabbit taps the font-control light,
System letters bloom in sight.
The root class shifts, the toggle springs,
Local storage guards the settings.
Soft paws hop through type tonight.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding a system font preference under Appearance.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/contexts/ThemeContext.jsx (1)

96-97: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Memoize the context value to prevent unnecessary re-renders.

The context value object is recreated on every render of ThemeProvider, forcing all context consumers to re-render even if the state hasn't changed. Consider memoizing the value and the toggleDarkMode function.

⚡ Proposed refactor
-  const toggleDarkMode = () => {
-    setIsDarkMode(prev => !prev);
-  };
+  const toggleDarkMode = useCallback(() => {
+    setIsDarkMode(prev => !prev);
+  }, []);
 
-  const value = {
-    isDarkMode,
-    toggleDarkMode,
-    useSystemFont,
-    setUseSystemFont,
-  };
+  const value = useMemo(() => ({
+    isDarkMode,
+    toggleDarkMode,
+    useSystemFont,
+    setUseSystemFont,
+  }), [isDarkMode, toggleDarkMode, useSystemFont]);

(Ensure useMemo and useCallback are imported from 'react')

🤖 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 `@src/contexts/ThemeContext.jsx` around lines 96 - 97, Update ThemeProvider to
memoize the context value with useMemo and memoize toggleDarkMode with
useCallback, ensuring both React hooks are imported and their dependency arrays
include all referenced state and functions. Keep the existing context fields and
behavior unchanged.
🤖 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.

Inline comments:
In `@src/contexts/ThemeContext.jsx`:
- Around line 67-71: Move the `system-font` class initialization from the
`useEffect` in `ThemeContext` to the bootstrap or initial render path so it is
applied before first paint, while preserving the existing `useSystemFont`
preference and localStorage behavior. Keep the effect only for subsequent
preference changes if needed.

In `@src/index.css`:
- Around line 46-47: Quote the specified font family names in both affected
declarations: update BlinkMacSystemFont, Roboto, and Arial in src/index.css
lines 46-47 and 78-82, and also quote Georgia and Cambria in lines 46-47.
Preserve all existing fallback order and values.

---

Nitpick comments:
In `@src/contexts/ThemeContext.jsx`:
- Around line 96-97: Update ThemeProvider to memoize the context value with
useMemo and memoize toggleDarkMode with useCallback, ensuring both React hooks
are imported and their dependency arrays include all referenced state and
functions. Keep the existing context fields and behavior unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: fba3d9b2-6b85-4c6e-b0ec-cc9287154244

📥 Commits

Reviewing files that changed from the base of the PR and between 27eaf01 and 3f0a7fe.

📒 Files selected for processing (7)
  • src/components/settings/hooks/useSettingsController.ts
  • src/components/settings/view/Settings.tsx
  • src/components/settings/view/tabs/AppearanceSettingsTab.tsx
  • src/contexts/ThemeContext.jsx
  • src/i18n/locales/en/settings.json
  • src/index.css
  • tailwind.config.js

Comment on lines +67 to +71
useEffect(() => {
document.documentElement.classList.toggle('system-font', useSystemFont);
localStorage.setItem('useSystemFont', String(useSystemFont));
}, [useSystemFont]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check for inline theme initialization scripts in HTML files.

rg "localStorage\.getItem" -g "*.html" -C 2

Repository: siteboon/claudecodeui

Length of output: 159


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- files ---'
git ls-files | rg '(^|/)(index\.html|ThemeContext\.jsx|ThemeContext\.[jt]sx|main\.[jt]sx|App\.[jt]sx)$'

echo
echo '--- search for theme bootstrap/localStorage in html/jsx ---'
rg -n "localStorage\.getItem|system-font|ThemeContext|document\.documentElement\.classList\.toggle|classList\.add|classList\.remove" src . -g '*.html' -g '*.js' -g '*.jsx' -g '*.ts' -g '*.tsx'

echo
echo '--- candidate html files ---'
fd -a -t f 'index.html|*.html' .

Repository: siteboon/claudecodeui

Length of output: 18963


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the theme context around the relevant effect.
FILE='src/contexts/ThemeContext.jsx'
wc -l "$FILE"
sed -n '1,140p' "$FILE"

# If there is an index.html, inspect it for inline scripts.
if git ls-files | rg -q '(^|/)index\.html$'; then
  HTML=$(git ls-files | rg '(^|/)index\.html$' | head -n 1)
  echo
  echo "--- $HTML ---"
  cat -n "$HTML"
fi

Repository: siteboon/claudecodeui

Length of output: 4049


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- tracked html and theme files ---'
git ls-files | rg '(^|/)(index\.html|.*ThemeContext\.jsx|main\.[jt]sx|App\.[jt]sx)$' || true

echo
echo '--- ThemeContext.jsx ---'
sed -n '1,120p' src/contexts/ThemeContext.jsx

echo
echo '--- html files containing theme/bootstrap terms ---'
rg -n "localStorage\.getItem|system-font|theme" --glob '*.html' . || true

Repository: siteboon/claudecodeui

Length of output: 3946


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- root index.html ---'
cat -n index.html | sed -n '1,220p'

echo
echo '--- any inline scripts in HTML files ---'
rg -n "<script|localStorage|getItem|matchMedia|theme|system-font" index.html electron/launcher/index.html public/api-docs.html -C 3

Repository: siteboon/claudecodeui

Length of output: 6308


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
for p in [Path('index.html'), Path('electron/launcher/index.html')]:
    if p.exists():
        text = p.read_text()
        print(f'--- {p} script tags ---')
        for i, line in enumerate(text.splitlines(), 1):
            if '<script' in line or 'theme' in line.lower() or 'system-font' in line.lower():
                print(f'{i}: {line}')
PY

Repository: siteboon/claudecodeui

Length of output: 441


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- root index.html ---'
cat -n index.html | sed -n '1,220p'

echo
echo '--- root and launcher HTML theme-related lines ---'
rg -n "<script|localStorage|getItem|matchMedia|theme|system-font" index.html electron/launcher/index.html -C 3

Repository: siteboon/claudecodeui

Length of output: 3930


Apply system-font before first paint.
useEffect runs after hydration, so users who prefer the system font can see a brief font flash on load. Initialize this class in the bootstrap/initial render path instead.

🤖 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 `@src/contexts/ThemeContext.jsx` around lines 67 - 71, Move the `system-font`
class initialization from the `useEffect` in `ThemeContext` to the bootstrap or
initial render path so it is applied before first paint, while preserving the
existing `useSystemFont` preference and localStorage behavior. Keep the effect
only for subsequent preference changes if needed.

Comment thread src/index.css Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@src/main.jsx`:
- Around line 12-16: Guard all localStorage reads and writes in ThemeContext,
including state initialization and effect persistence, so unavailable or blocked
storage cannot crash the app. Update the relevant ThemeContext symbols to catch
storage failures or reuse centralized safe getItem/setItem helpers, while
preserving the existing font preference behavior when storage is available.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: d9b54375-3cf7-4ad8-916a-c2b95398cc45

📥 Commits

Reviewing files that changed from the base of the PR and between 3f0a7fe and 8f30c85.

📒 Files selected for processing (3)
  • src/contexts/ThemeContext.jsx
  • src/index.css
  • src/main.jsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/contexts/ThemeContext.jsx
  • src/index.css

Comment thread src/main.jsx Outdated
@blackmammoth

Copy link
Copy Markdown
Member

Hey @sudo-eugene, can you address the coderabbit comments?

@blackmammoth
blackmammoth marked this pull request as draft July 31, 2026 09:25
@sudo-eugene
sudo-eugene marked this pull request as ready for review July 31, 2026 21:05
@sudo-eugene

Copy link
Copy Markdown
Contributor Author

@blackmammoth done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants