Menu ▾ ▴

#405 feat(appearance): themes, accent color, message font and app icon

closed
nobody
None
2026-11-07
2026-11-07
Anonymous
No

Originally created by: kristofferR

Carrier only offered light, dark and system plus custom.css, and had a single app icon. Rivals ship named themes, an OLED black mode, accent colors, a font picker and alternate icons.

Themes, accent color and font (Ref [#357])

  • Settings → Appearance opens with a theme gallery: Default, OLED Black (dark only), Graphite, Warm and High Contrast, each drawn as a small Messenger preview, plus imported themes. A theme is a palette on top of light/dark, never a third mode.
  • "Add theme…" checks a .css file and copies it into the themes folder: a header comment with a name and five preview colors, at most 2 MB, no @import, and no URL other than data:. The check uses a real CSS tokenizer (cssparser), and errors name the line. Remove and "Open themes folder" sit beside the gallery.
  • Themes set --carrier-* palette tokens, which Carrier maps onto Messenger's own CSS variables, and can key dark colors on html[data-carrier-dark]. Recolor themes written for other Messenger apps with the same header shape import as they are.
  • Accent color (seven presets or a custom color) recolors your messages, links, buttons and unread dots; message text takes white or black, whichever contrasts more. Message font can be any installed font (fontdb).
  • The page gets the theme, High Contrast, accent and font as separate <style> elements before custom.css, which still applies last. High Contrast takes over whenever the system asks for more contrast: prefers-contrast on macOS and Windows, the settings portal's contrast key on Linux.
  • Settings widens to 900px so the gallery fits above the fold.

App icon (Ref [#369])

  • Settings → Appearance → App icon: a Color and a Monochrome folded paper plane, each with a light and a dark tile, plus Match theme / Light / Dark.
  • macOS swaps the running Dock icon (Color in light restores the bundle icon). Windows and Linux set the Messenger and Settings window icons, which is what the Windows taskbar shows; Linux launchers keep the installed icon, and Settings says so there. Match theme follows light/dark changes on all three platforms. The unread badge is drawn separately, so it works on every icon.
  • The installed icon set is regenerated from the new Color artwork (same size and margins). SVG sources and render.sh live in src-tauri/icons/app-icon/.

Testing

  • bun run check, cargo fmt, cargo clippy --all-targets -D warnings (Linux, plus cross-checks for macOS and Windows) and cargo test --lib pass.
  • New unit tests cover header parsing, the CSS safety check (escaped url(, src(), image-set(), @import inside @media), import ids, token mapping, single-mode themes, the High Contrast handoff, accent and font validation, and icon selection.
  • Checked in a browser: the Settings tab with mocked commands (light, dark, import error, accent, remove), and the generated page styles against a mock Messenger page.
  • Still to check in the running app on each OS: themes on a signed-in Messenger page, the Dock/taskbar icon swaps, and High Contrast following the system setting.

Summary by CodeRabbit

  • New Features
  • Added an Appearance interface for choosing light or dark mode, browsing and managing themes, and customizing accent colors, message fonts, and app icons.
  • Added Graphite, High Contrast, OLED Black, and Warm themes, with previews and support for themes that are limited to light or dark mode.
  • Added system high-contrast support and automatic app-icon updates when appearance settings change.
  • Improvements
  • Expanded the Settings window to accommodate the new appearance controls.

Related

Tickets: #357
Tickets: #369
Tickets: #435

Discussion

  • Anonymous

    Anonymous - 2026-11-07

    Originally posted by: coderabbitai[bot]

    Action performed

    Review finished. > Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.
     
  • Anonymous

    Anonymous - 2026-11-07

    Originally posted by: chatgpt-codex-connector[bot]

    Codex Review Summary: ✅ Completed This comment shows the latest Codex review activity on this pull request. | Review | Status | Commit | Review trigger | | --- | --- | --- | --- | | 📝 **Code Review** | ✅ **Completed** <relative-time datetime="2026-11-07T21:20:59.302463Z">2026-11-07T21:20:59.302463Z</relative-time> | `4971607` | Manual request |
    ℹ️ About Codex in GitHub
    [Your team has set up Codex to review pull requests in this repo](https://chatgpt.com/codex/cloud/settings/general). Reviews are triggered when you - Open a pull request for review - Mark a draft as ready - Comment "@codex review" or "@codex security review". Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.
     
  • Anonymous

    Anonymous - 2026-11-07

    Originally posted by: chatgpt-codex-connector[bot]

    Codex Review: Didn't find any major issues. Keep it up!

    Reviewed commit: c4d626863e

    ℹ️ About Codex in GitHub
    [Your team has set up Codex to review pull requests in this repo](https://chatgpt.com/codex/cloud/settings/general). Reviews are triggered when you - Open a pull request for review - Mark a draft as ready - Comment "@codex review". If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
     
  • Anonymous

    Anonymous - 2026-11-07

    Originally posted by: coderabbitai[bot]

    <picture>Explain Change →</picture>

    [!WARNING]

    Review limit reached

    You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

    Next included review available in 50 minutes.

    Check out review usage here.


    View limit details



    Limit details: You’ve used the included review currently available.

    Learn how review limits work.

    Review configuration:


    <picture></picture> Run configuration






    <picture></picture> Commits



    Reviewing files that changed from the base of the PR and between [c4d626] and d3f4bdea3686188831b6c6a047941d2d20aa71bc.




    <picture></picture> Files ignored due to path filters: 62



    • dist/app-icons/color-dark.png is excluded by !**/*.png
    • dist/app-icons/color-light.png is excluded by !**/*.png
    • dist/app-icons/mono-dark.png is excluded by !**/*.png
    • dist/app-icons/mono-light.png is excluded by !**/*.png
    • src-tauri/Cargo.lock is excluded by !**/*.lock
    • src-tauri/icons/128x128.png is excluded by !**/*.png
    • src-tauri/icons/128x128@2x.png is excluded by !**/*.png
    • src-tauri/icons/32x32.png is excluded by !**/*.png
    • src-tauri/icons/64x64.png is excluded by !**/*.png
    • src-tauri/icons/Square107x107Logo.png is excluded by !**/*.png
    • src-tauri/icons/Square142x142Logo.png is excluded by !**/*.png
    • src-tauri/icons/Square150x150Logo.png is excluded by !**/*.png
    • src-tauri/icons/Square284x284Logo.png is excluded by !**/*.png
    • src-tauri/icons/Square30x30Logo.png is excluded by !**/*.png
    • src-tauri/icons/Square310x310Logo.png is excluded by !**/*.png
    • src-tauri/icons/Square44x44Logo.png is excluded by !**/*.png
    • src-tauri/icons/Square71x71Logo.png is excluded by !**/*.png
    • src-tauri/icons/Square89x89Logo.png is excluded by !**/*.png
    • src-tauri/icons/StoreLogo.png is excluded by !**/*.png
    • src-tauri/icons/android/mipmap-hdpi/ic_launcher.png is excluded by !**/*.png
    • src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png is excluded by !**/*.png
    • src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png is excluded by !**/*.png
    • src-tauri/icons/android/mipmap-mdpi/ic_launcher.png is excluded by !**/*.png
    • src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png is excluded by !**/*.png
    • src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png is excluded by !**/*.png
    • src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png is excluded by !**/*.png
    • src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png is excluded by !**/*.png
    • src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png is excluded by !**/*.png
    • src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png is excluded by !**/*.png
    • src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png is excluded by !**/*.png
    • src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png is excluded by !**/*.png
    • src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png is excluded by !**/*.png
    • src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png is excluded by !**/*.png
    • src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png is excluded by !**/*.png
    • src-tauri/icons/app-icon/color-dark.png is excluded by !**/*.png
    • src-tauri/icons/app-icon/color-dark.svg is excluded by !**/*.svg
    • src-tauri/icons/app-icon/color-light.png is excluded by !**/*.png
    • src-tauri/icons/app-icon/color-light.svg is excluded by !**/*.svg
    • src-tauri/icons/app-icon/mono-dark.png is excluded by !**/*.png
    • src-tauri/icons/app-icon/mono-dark.svg is excluded by !**/*.svg
    • src-tauri/icons/app-icon/mono-light.png is excluded by !**/*.png
    • src-tauri/icons/app-icon/mono-light.svg is excluded by !**/*.svg
    • src-tauri/icons/icon.ico is excluded by !**/*.ico
    • src-tauri/icons/icon.png is excluded by !**/*.png
    • src-tauri/icons/ios/AppIcon-20x20@1x.png is excluded by !**/*.png
    • src-tauri/icons/ios/AppIcon-20x20@2x-1.png is excluded by !**/*.png
    • src-tauri/icons/ios/AppIcon-20x20@2x.png is excluded by !**/*.png
    • src-tauri/icons/ios/AppIcon-20x20@3x.png is excluded by !**/*.png
    • src-tauri/icons/ios/AppIcon-29x29@1x.png is excluded by !**/*.png
    • src-tauri/icons/ios/AppIcon-29x29@2x-1.png is excluded by !**/*.png
    • src-tauri/icons/ios/AppIcon-29x29@2x.png is excluded by !**/*.png
    • src-tauri/icons/ios/AppIcon-29x29@3x.png is excluded by !**/*.png
    • src-tauri/icons/ios/AppIcon-40x40@1x.png is excluded by !**/*.png
    • src-tauri/icons/ios/AppIcon-40x40@2x-1.png is excluded by !**/*.png
    • src-tauri/icons/ios/AppIcon-40x40@2x.png is excluded by !**/*.png
    • src-tauri/icons/ios/AppIcon-40x40@3x.png is excluded by !**/*.png
    • src-tauri/icons/ios/AppIcon-512@2x.png is excluded by !**/*.png
    • src-tauri/icons/ios/AppIcon-60x60@2x.png is excluded by !**/*.png
    • src-tauri/icons/ios/AppIcon-60x60@3x.png is excluded by !**/*.png
    • src-tauri/icons/ios/AppIcon-76x76@1x.png is excluded by !**/*.png
    • src-tauri/icons/ios/AppIcon-76x76@2x.png is excluded by !**/*.png
    • src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png is excluded by !**/*.png




    <picture></picture> Files selected for processing: 18



    • dist/settings.html
    • inject/src/messenger/features/force-theme.ts
    • inject/src/messenger/index.ts
    • src-tauri/Cargo.toml
    • src-tauri/icons/app-icon/render.sh
    • src-tauri/icons/icon.icns
    • src-tauri/inject/messenger.js
    • src-tauri/src/app_icon.rs
    • src-tauri/src/lib.rs
    • src-tauri/src/linux.rs
    • src-tauri/src/macos/theme.rs
    • src-tauri/src/settings.rs
    • src-tauri/src/themes.rs
    • src-tauri/src/window.rs
    • src-tauri/themes/graphite.css
    • src-tauri/themes/high-contrast.css
    • src-tauri/themes/oled-black.css
    • src-tauri/themes/warm.css





    Walkthrough

    The pull request adds theme, accent-color, message-font, and app-icon settings. It adds theme validation and management, applies appearance settings to Messenger windows, and updates appearance handling for system theme and contrast changes. **Changes: Appearance customization** |Layer / File(s)|Summary| |:---|:---| |**Appearance settings and theme processing**
    `src-tauri/src/settings.rs`, `src-tauri/src/themes.rs`, `src-tauri/Cargo.toml`, `src-tauri/themes/*`|Settings gain persisted theme, accent, font, and app-icon preferences. The new theme module parses and validates theme CSS, maps palette tokens, and generates appearance styles. Four built-in theme stylesheets and related tests are added.| |**Applying appearance across windows**
    `src-tauri/src/themes.rs`, `src-tauri/src/settings.rs`, `src-tauri/src/window.rs`, `src-tauri/src/linux.rs`, `inject/src/messenger/*`, `src-tauri/inject/messenger.js`, `src-tauri/src/lib.rs`|Messenger windows receive the configured appearance. System color-scheme changes update dark-mode markers; Linux portal contrast changes update appearance when the stored contrast state changes.| |**Theme management and settings gallery**
    `src-tauri/src/themes.rs`, `src-tauri/src/lib.rs`, `dist/settings.html`, `src-tauri/src/window.rs`|Theme commands support listing, importing, removing, and opening themes, and listing installed fonts. Settings display theme cards, accent and font controls, and theme-management actions. The Settings window size is increased.| |**App icon selection and platform updates**
    `src-tauri/src/app_icon.rs`, `src-tauri/src/settings.rs`, `src-tauri/src/window.rs`, `src-tauri/src/linux.rs`, `src-tauri/src/macos/theme.rs`, `src-tauri/src/lib.rs`, `dist/settings.html`, `src-tauri/icons/app-icon/render.sh`, `src-tauri/Cargo.toml`|The settings UI and runtime support automatic, light, and dark variants with color or monochrome styles. The selected icon is applied to the macOS Dock or to Messenger and Settings windows on other platforms. An icon rendering script and icon tests are added.| **Estimated code review effort:** 4 (Complex) | ~60 minutes **Change:** Feature ### Sequence Diagram(s) :::mermaid sequenceDiagram participant SettingsUI participant InvokeHandler participant ThemesModule participant MessengerWindow SettingsUI->>InvokeHandler: Request theme list InvokeHandler->>ThemesModule: list_themes ThemesModule-->>SettingsUI: ThemeInfo entries SettingsUI->>SettingsUI: Persist selected theme SettingsUI->>MessengerWindow: Apply updated settings MessengerWindow->>ThemesModule: apply_appearance ThemesModule-->>MessengerWindow: Evaluate appearance script **Possibly related issues** - kristofferR/Carrier issue 357: Requests the theme gallery, theme import, OLED Black theme, accent color, and message font; this pull request adds those features. - kristofferR/Carrier issue 349: Tracks the theme and app-icon work listed in issues 357 and 369; this pull request implements both sets of features. - kristofferR/Carrier issue 369: Requests selectable app icons, including dark variants and platform-specific updates; this pull request adds icon style and variant controls and applies icons across platforms.
    Merge Risk: <picture></picture> Low · previous review c4d62

    A malformed or hostile theme file could crash the app at import or load, and the theme-header error message points users to an example that does not work. Both are narrow, easy fixes that should be made, but they do not block merging.
    Pre-merge checks: <picture>Passed</picture> 3 · <picture>Failed</picture> 1 · <picture>Inconclusive</picture> 1

    <picture>Failed</picture> Failed checks: 1 warning · 1 inconclusive | Check name | Status | Explanation | Resolution | | :---------------- | :--------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Description check | ![Warning](https://storage.googleapis.com/coderabbit_public_assets/pre-merge-checks-warning.svg) | The description covers the required template sections: a Summary that explains the problem and the change, and an AI models used section. The AI models section is absent from the template-compliant fo… | Add an "AI models used" section to the description. Replace the placeholder text with the model names and versions used to create or edit code, tests, or PR text. If no AI helped, write "None". Optionally add the Reasoning levels line, or w… | | Docstring Coverage | ![Inconclusive](https://storage.googleapis.com/coderabbit_public_assets/pre-merge-checks-inconclusive.svg) | Docstring coverage is 57.89% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta… | Write docstrings for the functions missing them to satisfy the coverage threshold. |
    <picture>Passed</picture> Passed checks: 3 passed

    | Check name | Status | Explanation | | :------------------------ | :--------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Title check | ![Passed](https://storage.googleapis.com/coderabbit_public_assets/pre-merge-checks-passed.svg) | The title "feat(appearance): themes, accent color, message font and app icon" matches the main changes in the changeset. The raw summary covers the new theme module and gallery, accent color and messa… | | Linked Issues check | ![Passed](https://storage.googleapis.com/coderabbit_public_assets/pre-merge-checks-passed.svg) | Check skipped because no linked issues were found for this pull request. | | Out of Scope Changes check | ![Passed](https://storage.googleapis.com/coderabbit_public_assets/pre-merge-checks-passed.svg) | Check skipped because no linked issues were found for this pull request. |
    Full details: Title check

    **Explanation** The title "feat(appearance): themes, accent color, message font and app icon" matches the main changes in the changeset. The raw summary covers the new theme module and gallery, accent color and message font settings, and app icon selection with runtime application. The title names the two primary features (themes and app icon) and the other appearance controls. It is specific and follows conventional commit style. It does not cover every detail, which is expected.
    Full details: Description check

    **Explanation** The description covers the required template sections: a Summary that explains the problem and the change, and an AI models used section. The AI models section is absent from the template-compliant form, since the author did not list a model name or write None. The Reasoning levels section is optional and does not block the PR. The Summary is detailed and covers themes, accent color, font, app icon, and testing, with clear scope. The AI models used field needs an entry to meet the template. **Resolution** Add an "AI models used" section to the description. Replace the placeholder text with the model names and versions used to create or edit code, tests, or PR text. If no AI helped, write "None". Optionally add the Reasoning levels line, or write "Reasoning levels: Unavailable (not exposed by tool)" when the tool does not expose it.
    Full details: Docstring Coverage

    **Explanation** Docstring coverage is 57.89% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 38 functions across 10 files. (7 skipped: 6 unsupported, 1 too large.) **Resolution** Write docstrings for the functions missing them to satisfy the coverage threshold.
    Finishing Touches

    Generate docstrings

    - [ ] Commit to this branch - [ ] Create a new PR
    Generate unit tests (beta)

    - [ ] Commit to this branch - [ ] Create a new PR
    • [ ] Autofix · Keep fixing CodeRabbit findings and required CI, and resolving 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.

    Share

    - [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai) - [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai) - [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai) - [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)
     
  • Anonymous

    Anonymous - 2026-11-07

    Originally posted by: coderabbitai[bot]

    Action not completed

    Review rate limited. > Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.
     
  • Anonymous

    Anonymous - 2026-11-07

    Originally posted by: kristofferR

    @codex review for consequential defects introduced by this PR; do not expand its original scope with unrelated refactors, features, or speculative hardening

     
  • Anonymous

    Anonymous - 2026-11-07

    Originally posted by: kristofferR

    [!NOTE]
    🤖 Claude Opus 5.5 responding on behalf of Kristoffer

    @codex review

     
  • Anonymous

    Anonymous - 2026-11-07

    Originally posted by: chatgpt-codex-connector[bot]

    Codex Review: Didn't find any major issues. 👍

    Reviewed commit: 49716079ae

    ℹ️ About Codex in GitHub
    [Your team has set up Codex to review pull requests in this repo](https://chatgpt.com/codex/cloud/settings/general). Reviews are triggered when you - Open a pull request for review - Mark a draft as ready - Comment "@codex review". If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
     
  • Anonymous

    Anonymous - 2026-11-07

    Ticket changed by: kristofferR

    • status: open --> closed
     

Log in to post a comment.