Skip to content

feat(ui): enable windows mica effect#13476

Merged
EurFelux merged 5 commits intoCherryHQ:mainfrom
lacser:feat/windows-mica
Mar 15, 2026
Merged

feat(ui): enable windows mica effect#13476
EurFelux merged 5 commits intoCherryHQ:mainfrom
lacser:feat/windows-mica

Conversation

@lacser
Copy link
Contributor

@lacser lacser commented Mar 14, 2026

What this PR does

This PR enables Mica backdrop effect on Windows11.

Before this PR:

Previously, CherryStudio only supports window blur effect on MacOS.
screenshot prev

After this PR:

Now, Mica effect is enabled for Windows. No CSS change is being made. Use following custom css to change transparency of ui components so that you can see mica effect.

screenshot after

CSS below is generated by Codex. I didn't review it. Use it for testing purpose only.

body[theme-mode='light'] {
  --navbar-background: rgba(244, 244, 244, 0);
  --navbar-background-mac: rgba(244, 244, 244, 0);

  --mica-surface-1: rgba(255, 255, 255, 0.42);
  --mica-surface-2: rgba(255, 255, 255, 0.56);
  --mica-surface-3: rgba(255, 255, 255, 0.68);
  --mica-border: rgba(255, 255, 255, 0.22);
}

body[theme-mode='dark'] {
  --navbar-background: rgba(22, 22, 22, 0);
  --navbar-background-mac: rgba(22, 22, 22, 0);

  --mica-surface-1: rgba(24, 24, 24, 0.42);
  --mica-surface-2: rgba(24, 24, 24, 0.56);
  --mica-surface-3: rgba(24, 24, 24, 0.68);
  --mica-border: rgba(255, 255, 255, 0.08);
}

/* Root transparency */
html,
body,
#root,
#root[style] {
  background: transparent !important;
  background-color: transparent !important;
  background-image: none !important;
}

/* Titlebar and window chrome: fully transparent, no mask */
#app-sidebar,
#app-sidebar[style],
.home-navbar,
.home-navbar[style],
[class*='NavbarContainer'],
[class*='NavbarMainContainer'],
[class*='NavbarHeaderContent'],
[class*='TabsBar'],
[class*='WindowControlsContainer'] {
  background: transparent !important;
  background-color: transparent !important;
  background-image: none !important;
  box-shadow: none !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
  border: none !important;
}

/* Remove the top-left titlebar mask feeling in left-navbar layout */
[navbar-position='left'] #content-container {
  border-top: none !important;
  border-left: none !important;
}

/* Main content shell: add stronger translucent mask */
#content-container,
#content-container[style],
[class*='TabContent'],
.home-tabs,
[class*='SettingContainer'],
[class*='MainContainer'],
[class*='ProgramSection'],
[class*='IconSection'] {
  background: var(--mica-surface-1) !important;
  background-color: var(--mica-surface-1) !important;
  box-shadow: none !important;
}

/* Slightly stronger mask for cards / groups / major panels */
[theme-mode='light'] #chat,
[theme-mode='light'] [class*='SettingGroup'],
[theme-mode='light'] [class*='AgentCardContainer'] {
  background: var(--mica-surface-2) !important;
  background-color: var(--mica-surface-2) !important;
  border: 1px solid var(--mica-border) !important;
}

[theme-mode='dark'] #chat,
[theme-mode='dark'] [class*='SettingGroup'],
[theme-mode='dark'] [class*='AgentCardContainer'] {
  background: var(--mica-surface-2) !important;
  background-color: var(--mica-surface-2) !important;
  border: 1px solid var(--mica-border) !important;
}

/* Input / editor area: a bit more solid for readability */
#inputbar,
.system-prompt,
[class*='CardContent'] {
  background: var(--mica-surface-3) !important;
  background-color: var(--mica-surface-3) !important;
  border: 1px solid var(--mica-border) !important;
}

/* Minapp drawer: keep readable, but remove titlebar mask */
.minapp-drawer .ant-drawer-header {
  background: transparent !important;
  background-color: transparent !important;
  border: none !important;
  box-shadow: none !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}

.minapp-drawer .ant-drawer-body {
  background: var(--mica-surface-1) !important;
  background-color: var(--mica-surface-1) !important;
}

.minapp-drawer .minapp-mask {
  background: transparent !important;
}

Why we need it and why it was done in this way

The following tradeoffs were made:

No CSS change is made. This aligns with current implementation for MacOS. Even if window blur effect is currently enabled for MacOS, because the UI color is not transparent, the effect will not be visible unless you use a custom css. Below is a css I found for MacOS. https://github.com/hakadao/CherryStudio-Aero

Breaking changes

No breaking changes being made.

Special notes for your reviewer

Below are relevant docs
https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwm_systembackdrop_type
https://www.electronjs.org/zh/docs/latest/api/base-window#winsetbackgroundmaterialmaterial-windows

Checklist

This checklist is not enforcing, but it's a reminder of items that could be relevant to every PR.
Approvers are expected to review this list.

Release note

Enable Mica Window backdrop effect. Supported on and above Windows 11 22H2.

@lacser lacser changed the title Feat/enable windows mica effect feat/enable windows mica effect Mar 14, 2026
@lacser lacser changed the title feat/enable windows mica effect feat(ui): enable windows mica effect Mar 14, 2026
Copy link
Collaborator

@EurFelux EurFelux left a comment

Choose a reason for hiding this comment

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

Not sure what does it look like because I use macOS. The code seems generally OK.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you add some test cases for getWindowsBackgroundMaterial?

isWindowsMicaSupported is never used by external module, so it should not be exported.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

removed unnecessary export

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added some tests, not sure if they are necessary tho. I feel the main problem with current implementation is the way we get buildNumber is not very robust as @kangfenmao pointed out. But I didn't find a better way.

With that saying, worst case scenario, you simply lose Mica effect, nothing will break. Because:

...(windowsBackgroundMaterial ? { backgroundMaterial: windowsBackgroundMaterial } : {}),
      backgroundColor: mainWindowBackgroundColor,

}),
backgroundColor: isMac ? undefined : nativeTheme.shouldUseDarkColors ? '#181818' : '#FFFFFF',
...(windowsBackgroundMaterial ? { backgroundMaterial: windowsBackgroundMaterial } : {}),
backgroundColor: mainWindowBackgroundColor,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should use consistent pattern like L91

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There will be 3 nested ternary operator if we choose to use it. Three conditions are !isMac, !windowsBackgroundMaterial, nativeTheme.shouldUseDarkColors.
Which is bad readability.

Or maybe you want to use an inline defined function?

backgroundColor: (() => {
  if (!isMac && !windowsBackgroundMaterial) {
    return nativeTheme.shouldUseDarkColors ? '#181818' : '#FFFFFF'
  }
  return undefined
})(),

Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
backgroundColor: mainWindowBackgroundColor,
backgroundColor: ...(mainWindowBackgroundColor ? { backgroundColor: mainWindowBackgroundColor }),

Copy link
Contributor Author

Choose a reason for hiding this comment

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

before this PR, it was

backgroundColor: isMac ? undefined : nativeTheme.shouldUseDarkColors ? '#181818' : '#FFFFFF',

I didn't intend to change this. But sure, fixed now.

kangfenmao

This comment was marked as duplicate.

kangfenmao

This comment was marked as duplicate.

Copy link
Collaborator

@kangfenmao kangfenmao left a comment

Choose a reason for hiding this comment

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

LGTM - Clean implementation following macOS blur pattern. The version gating logic is appropriate for this feature.

One small suggestion for the version parsing:

This handles edge cases more robustly if the version format varies.

kangfenmao

This comment was marked as resolved.

lacser added 3 commits March 15, 2026 00:13
Signed-off-by: Yaron <yaronching@outlook.com>
Signed-off-by: Yaron <yaronching@outlook.com>
Signed-off-by: Yaron <yaronching@outlook.com>
Copy link
Collaborator

@EurFelux EurFelux left a comment

Choose a reason for hiding this comment

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

LGTM! Clean implementation following the existing macOS vibrancy pattern. One minor nit about version parsing for future-proofing, but not blocking.

Comment on lines +85 to +88
const systemVersion = process.getSystemVersion()
const buildNumber = Number.parseInt(systemVersion.split('.')[2] ?? '', 10)

return Number.isFinite(buildNumber) && buildNumber >= WINDOWS_11_22H2_BUILD
Copy link
Collaborator

@EurFelux EurFelux Mar 15, 2026

Choose a reason for hiding this comment

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

nit: Only checking the build number while ignoring major.minor is not fully future-proof. Currently all Windows 11 versions use 10.0.xxxxx so this works fine in practice, but if a future Windows version ever changes the major version and decrease build number, this check would incorrectly disable Mica.

A more defensive approach would be to compare the full version tuple:

Suggested change
const systemVersion = process.getSystemVersion()
const buildNumber = Number.parseInt(systemVersion.split('.')[2] ?? '', 10)
return Number.isFinite(buildNumber) && buildNumber >= WINDOWS_11_22H2_BUILD
const systemVersion = process.getSystemVersion()
const [major, minor, build] = systemVersion.split('.').map(Number)
if ([major, minor, build].some((n) => !Number.isFinite(n))) return false
// Windows 10.0.22621+ supports Mica; future major versions (11.x+) are assumed to support it too
return major > 10 || (major === 10 && minor > 0) || (major === 10 && minor === 0 && build >= WINDOWS_11_22H2_BUILD)

Not blocking — totally fine to keep as-is given Microsoft's current versioning.

@EurFelux EurFelux merged commit 19bd2cd into CherryHQ:main Mar 15, 2026
11 checks passed
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.

3 participants