Match the macOS app appearance to the selected theme - #58902
Merged
Conversation
…ance Add `App::set_window_appearance(Option<WindowAppearance>)`, the setter paired with the existing `App::window_appearance()` getter. `Some(_)` forces a light/dark appearance; `None` clears the override and follows the system again. On macOS this sets `NSApplication.appearance`, which controls the native window chrome (the window border and titlebar) of every window, so a dark-themed app no longer shows a light, washed-out window border when the system is in light mode. Setting it on the application (rather than per window) matches AppKit's app-wide appearance model, so every window inherits it. No-op on other platforms. Demo: `cargo run -p gpui --example window_appearance`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drive App::set_window_appearance from the theme at startup so the native window chrome (the window border and titlebar) matches the selected theme. An explicit light/dark theme (or a static theme) forces the matching appearance; System follows the OS. Wired via a global settings observer, so every window — including the settings window — stays in sync. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
huacnlee
force-pushed
the
add-set-appearance
branch
from
June 9, 2026 08:00
ae8e5ac to
5f56842
Compare
Contributor
Author
Veykril
approved these changes
Jul 15, 2026
Veykril
left a comment
Member
There was a problem hiding this comment.
I don't think the example pulls its weight here, but otherwise lgtm!
Contributor
Author
|
Is there any things need me to change? or I just waiting to merge? |
Member
|
Lets remove the example, otherwise this is good to go I think |
Contributor
Author
|
done |
Veykril
enabled auto-merge
July 28, 2026 08:23
5 tasks
pull Bot
pushed a commit
to gaetschwartz/zed
that referenced
this pull request
Jul 29, 2026
…-industries#61789) ### Objective PR zed-industries#58902 made native macOS window chrome follow the selected theme, but AppKit can synchronously invoke `viewDidChangeEffectiveAppearance` while the settings observer holds an `App` borrow. `handle.update` then fails with `RefCell already borrowed`, leaving `Window::appearance()` and its observers stale. ### Steps to reproduce 1. Set Zed's theme mode to Dark. 2. Set macOS Appearance to Light. 3. Change Zed's theme mode to System. 4. Notice that the window chrome switches to light, but Zed's UI remains dark. https://github.com/user-attachments/assets/f91962cc-76ba-4d8e-b587-2a2343f365ba ### Expectation Zed should switch to its configured light theme when returning to System mode. ### Solution Defer `Window::appearance_changed` to the foreground executor so the current `App` borrow finishes before refreshing the cached appearance and notifying observers. ### Testing - Added a regression test. ### Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [ ] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A
AtlantisPleb
pushed a commit
to OpenAgentsInc/omega
that referenced
this pull request
Jul 30, 2026
…-industries#61789) ### Objective PR zed-industries#58902 made native macOS window chrome follow the selected theme, but AppKit can synchronously invoke `viewDidChangeEffectiveAppearance` while the settings observer holds an `App` borrow. `handle.update` then fails with `RefCell already borrowed`, leaving `Window::appearance()` and its observers stale. ### Steps to reproduce 1. Set Zed's theme mode to Dark. 2. Set macOS Appearance to Light. 3. Change Zed's theme mode to System. 4. Notice that the window chrome switches to light, but Zed's UI remains dark. https://github.com/user-attachments/assets/f91962cc-76ba-4d8e-b587-2a2343f365ba ### Expectation Zed should switch to its configured light theme when returning to System mode. ### Solution Defer `Window::appearance_changed` to the foreground executor so the current `App` borrow finishes before refreshing the cached appearance and notifying observers. ### Testing - Added a regression test. ### Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [ ] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A (cherry picked from commit a11083f)
jolutz
pushed a commit
to jolutz/zed
that referenced
this pull request
Aug 8, 2026
…58902) Closes zed-industries#5049. When the system is in light mode but Zed uses a dark theme, the macOS window chrome — the window border and the titlebar — is rendered light, leaving a pale, low-contrast edge that makes the window look washed out. Zed's own content renders correctly; the affected chrome is drawn by AppKit, not Zed. Why it happens: `NSWindow` conforms to `NSAppearanceCustomization`, so a window's appearance is inherited app → window → view and resolved through `effectiveAppearance` unless an explicit appearance is set. Zed never set one, so every window inherited the *system* appearance and AppKit drew the chrome for light mode regardless of the dark theme. The fix uses the override AppKit provides for exactly this: set `NSApplication.appearance` app-wide, so every window inherits it. VSCode (`window.systemColorTheme`) and JetBrains IDEs do the same. <img width="2600" height="1346" alt="after-light" src="https://github.com/user-attachments/assets/c31376af-8f51-4c36-a62f-55714f63b59c" /> ## GPUI - Add `App::set_window_appearance(Option<WindowAppearance>)`, the setter paired with the existing `App::window_appearance()` getter. `Some(_)` forces a light/dark appearance; `None` clears the override and follows the system again. - macOS sets `NSApplication.appearance` (`None` → `nil`); no-op on other platforms. - Demo: `cargo run -p gpui --example window_appearance`. ## Zed - An explicit light/dark theme (or a static theme) forces the matching chrome; `System` follows the OS. - Wired once at startup via a global settings observer, so every window — including the settings window — stays in sync. | Before | After | | --- | --- | | <img width="3630" height="1878" alt="before-light" src="https://github.com/user-attachments/assets/44c9ab66-c0cf-433a-93b0-17446b34a284" /> | <img width="3686" height="1832" alt="SCR-20260609-nzjk" src="https://github.com/user-attachments/assets/32b0412b-0913-4e68-acba-bf39dcb6aa6a" /> | References: [`NSAppearanceCustomization`](https://developer.apple.com/documentation/appkit/nsappearancecustomization) · [`NSApplication.appearance`](https://developer.apple.com/documentation/appkit/nsapplication/appearance) Release Notes: - Fixed the macOS window border and titlebar not matching the selected theme when the system appearance differed (for example, a dark theme under a light system theme). ([zed-industries#5049](zed-industries#5049)) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jolutz
pushed a commit
to jolutz/zed
that referenced
this pull request
Aug 8, 2026
…-industries#61789) ### Objective PR zed-industries#58902 made native macOS window chrome follow the selected theme, but AppKit can synchronously invoke `viewDidChangeEffectiveAppearance` while the settings observer holds an `App` borrow. `handle.update` then fails with `RefCell already borrowed`, leaving `Window::appearance()` and its observers stale. ### Steps to reproduce 1. Set Zed's theme mode to Dark. 2. Set macOS Appearance to Light. 3. Change Zed's theme mode to System. 4. Notice that the window chrome switches to light, but Zed's UI remains dark. https://github.com/user-attachments/assets/f91962cc-76ba-4d8e-b587-2a2343f365ba ### Expectation Zed should switch to its configured light theme when returning to System mode. ### Solution Defer `Window::appearance_changed` to the foreground executor so the current `App` borrow finishes before refreshing the cached appearance and notifying observers. ### Testing - Added a regression test. ### Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [ ] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A
HyacinthHaru
pushed a commit
to HyacinthHaru/z3rm
that referenced
this pull request
Aug 16, 2026
…-industries#61789) PR zed-industries#58902 made native macOS window chrome follow the selected theme, but AppKit can synchronously invoke `viewDidChangeEffectiveAppearance` while the settings observer holds an `App` borrow. `handle.update` then fails with `RefCell already borrowed`, leaving `Window::appearance()` and its observers stale. 1. Set Zed's theme mode to Dark. 2. Set macOS Appearance to Light. 3. Change Zed's theme mode to System. 4. Notice that the window chrome switches to light, but Zed's UI remains dark. https://github.com/user-attachments/assets/f91962cc-76ba-4d8e-b587-2a2343f365ba Zed should switch to its configured light theme when returning to System mode. Defer `Window::appearance_changed` to the foreground executor so the current `App` borrow finishes before refreshing the cached appearance and notifying observers. - Added a regression test. - [x] I've reviewed my own diff for quality, security, and reliability - [ ] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A (cherry picked from commit a11083f)
playdohface
pushed a commit
to playdohface/zed
that referenced
this pull request
Aug 29, 2026
…58902) Closes zed-industries#5049. When the system is in light mode but Zed uses a dark theme, the macOS window chrome — the window border and the titlebar — is rendered light, leaving a pale, low-contrast edge that makes the window look washed out. Zed's own content renders correctly; the affected chrome is drawn by AppKit, not Zed. Why it happens: `NSWindow` conforms to `NSAppearanceCustomization`, so a window's appearance is inherited app → window → view and resolved through `effectiveAppearance` unless an explicit appearance is set. Zed never set one, so every window inherited the *system* appearance and AppKit drew the chrome for light mode regardless of the dark theme. The fix uses the override AppKit provides for exactly this: set `NSApplication.appearance` app-wide, so every window inherits it. VSCode (`window.systemColorTheme`) and JetBrains IDEs do the same. <img width="2600" height="1346" alt="after-light" src="https://github.com/user-attachments/assets/c31376af-8f51-4c36-a62f-55714f63b59c" /> ## GPUI - Add `App::set_window_appearance(Option<WindowAppearance>)`, the setter paired with the existing `App::window_appearance()` getter. `Some(_)` forces a light/dark appearance; `None` clears the override and follows the system again. - macOS sets `NSApplication.appearance` (`None` → `nil`); no-op on other platforms. - Demo: `cargo run -p gpui --example window_appearance`. ## Zed - An explicit light/dark theme (or a static theme) forces the matching chrome; `System` follows the OS. - Wired once at startup via a global settings observer, so every window — including the settings window — stays in sync. | Before | After | | --- | --- | | <img width="3630" height="1878" alt="before-light" src="https://github.com/user-attachments/assets/44c9ab66-c0cf-433a-93b0-17446b34a284" /> | <img width="3686" height="1832" alt="SCR-20260609-nzjk" src="https://github.com/user-attachments/assets/32b0412b-0913-4e68-acba-bf39dcb6aa6a" /> | References: [`NSAppearanceCustomization`](https://developer.apple.com/documentation/appkit/nsappearancecustomization) · [`NSApplication.appearance`](https://developer.apple.com/documentation/appkit/nsapplication/appearance) Release Notes: - Fixed the macOS window border and titlebar not matching the selected theme when the system appearance differed (for example, a dark theme under a light system theme). ([zed-industries#5049](zed-industries#5049)) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
playdohface
pushed a commit
to playdohface/zed
that referenced
this pull request
Aug 29, 2026
…-industries#61789) ### Objective PR zed-industries#58902 made native macOS window chrome follow the selected theme, but AppKit can synchronously invoke `viewDidChangeEffectiveAppearance` while the settings observer holds an `App` borrow. `handle.update` then fails with `RefCell already borrowed`, leaving `Window::appearance()` and its observers stale. ### Steps to reproduce 1. Set Zed's theme mode to Dark. 2. Set macOS Appearance to Light. 3. Change Zed's theme mode to System. 4. Notice that the window chrome switches to light, but Zed's UI remains dark. https://github.com/user-attachments/assets/f91962cc-76ba-4d8e-b587-2a2343f365ba ### Expectation Zed should switch to its configured light theme when returning to System mode. ### Solution Defer `Window::appearance_changed` to the foreground executor so the current `App` borrow finishes before refreshing the cached appearance and notifying observers. ### Testing - Added a regression test. ### Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [ ] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Closes #5049.
When the system is in light mode but Zed uses a dark theme, the macOS window chrome — the window border and the titlebar — is rendered light, leaving a pale, low-contrast edge that makes the window look washed out. Zed's own content renders correctly; the affected chrome is drawn by AppKit, not Zed.
Why it happens:
NSWindowconforms toNSAppearanceCustomization, so a window's appearance is inherited app → window → view and resolved througheffectiveAppearanceunless an explicit appearance is set. Zed never set one, so every window inherited the system appearance and AppKit drew the chrome for light mode regardless of the dark theme.The fix uses the override AppKit provides for exactly this: set
NSApplication.appearanceapp-wide, so every window inherits it. VSCode (window.systemColorTheme) and JetBrains IDEs do the same.GPUI
App::set_window_appearance(Option<WindowAppearance>), the setter paired with the existingApp::window_appearance()getter.Some(_)forces a light/dark appearance;Noneclears the override and follows the system again.NSApplication.appearance(None→nil); no-op on other platforms.cargo run -p gpui --example window_appearance.Zed
Systemfollows the OS.References:
NSAppearanceCustomization·NSApplication.appearanceRelease Notes: