Skip to content

feat(ui): theme library, picker, and font-size control#58

Merged
ErikChevalier merged 1 commit into
mainfrom
feat/theming
Jun 4, 2026
Merged

feat(ui): theme library, picker, and font-size control#58
ErikChevalier merged 1 commit into
mainfrom
feat/theming

Conversation

@ErikChevalier
Copy link
Copy Markdown
Contributor

Summary

Mirrors the desktop theming feature on Android: a real theme library with a picker and quality-of-life controls (font size, accessibility), across the Compose app and the served page. Part of the current RC feature pile (merge to main behind green CI; no per-feature release).

Themes

A slate of 13: the original SearchMob Light/Dark (kept as defaults, so existing users see no change until they pick a new theme) plus GitHub Light, One Dark, Dracula, Tokyo Night, Catppuccin Mocha/Latte, Gruvbox, Nord, Rose Pine Dawn, and two WCAG-AAA accessibility themes (Paper White, Obsidian Slate). Reused palettes are credited in CREDITS.md (all MIT). Same ids/palettes as the desktop app for parity.

Two-slot model

Layers on the existing light/dark/system control and the Material You toggle: the picker chooses which theme fills the light slot and which fills the dark slot; the quick toggle swaps between them. Material You stays an independent override (when on, it supersedes the named themes, with an in-UI hint).

Font size

A 12pt base with A−/A+ steps of 2pt (bounds 8–24) via a scaled Typography, remembered between uses.

How it's built

  • ui/theme/Themes.kt is the single source of palette truth: colorSchemeFor(theme) builds Material 3 ColorSchemes for Compose; themeVars(theme) builds the served-page CSS custom properties, so a theme looks identical on both surfaces.
  • SearchMobTheme resolves the active theme via resolveActiveTheme(mode, lightId, darkId, systemDark).
  • Served page (SearchServer.kt) mirrors desktop: one [data-theme="<id>"] block per theme, two-slot localStorage init/toggle, root font size in pt + content in rem, the /settings Appearance picker, and the nested home-page scope.
  • Prefs gain lightThemeId, darkThemeId, fontPointSize (DataStore).

Privacy

Theme and font are local prefs (DataStore / localStorage), never transmitted, not search data. No new dependencies, no new network calls. Owner/LAN gating and the paging reveal are untouched. No supportsRtl/i18n (that is a separate later feature).

Tests + verification

  • ThemingTest (13): the slate + desktop-parity id set, the two-slot resolver truth table, colorSchemeFor mapping, scaled Typography, clampFontPointSize, and contrast (all themes clear AA body; the two a11y themes hit AAA).
  • ThemeMarkupTest (9): every theme id emits a [data-theme] block; the two-slot init/toggle JS; the /settings picker; pt root + rem content.
  • PreferencesRepositoryTest (+2): slot/font prefs default + round-trip + clamp.
  • Full gate green: ktlint + lintDebug + testDebugUnitTest + assembleDebug.
  • Verified on the emulator: the Settings Appearance picker renders; selecting Gruvbox recolors the whole app; the served page emits all 13 [data-theme] blocks.

OpenSpec: openspec/changes/add-theming/ (validated --strict).

🤖 Generated with Claude Code

Mirror the desktop theming feature on Android. Add a library of named themes
(the original SearchMob Light/Dark kept as defaults, plus GitHub, One Dark,
Dracula, Tokyo Night, Catppuccin, Gruvbox, Nord, Rose Pine, and two WCAG-AAA
accessibility themes) across the Compose app and the served page. A two-slot
model layers on the existing light/dark/system control and Material You toggle:
the picker chooses which theme fills the light slot and which fills the dark
slot, and the quick toggle swaps between them. A font-size control steps the
interface and result text by 2pt from a 12pt base, remembered between uses.

A shared ui/theme/Themes.kt registry is the single source of palette truth:
colorSchemeFor builds Material 3 ColorSchemes for Compose, and themeVars builds
the served-page CSS custom properties, so a theme looks the same on both
surfaces. The served page reuses the desktop pattern (one [data-theme] block per
theme, two-slot localStorage init/toggle, root font size in pt with content in
rem, the settings picker, and the nested home scope). Theme and font are local
prefs (DataStore / localStorage), never transmitted. Reused palettes are
credited in CREDITS.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ErikChevalier ErikChevalier merged commit 7d00d49 into main Jun 4, 2026
2 checks passed
@ErikChevalier ErikChevalier deleted the feat/theming branch June 4, 2026 01:13
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.

1 participant