Skip to content

feat: add light mode with system/light/dark theme support#2760

Open
real-alexei wants to merge 8 commits into
amnezia-vpn:devfrom
real-alexei:feat/light-mode
Open

feat: add light mode with system/light/dark theme support#2760
real-alexei wants to merge 8 commits into
amnezia-vpn:devfrom
real-alexei:feat/light-mode

Conversation

@real-alexei

@real-alexei real-alexei commented Jun 22, 2026

Copy link
Copy Markdown

Add a user-selectable color theme (System / Light / Dark, default System) that follows the OS appearance live, plus the light-mode visual polish and translation fixes that came out of testing it.

Theme support

  • AppearanceController wraps QStyleHints::colorScheme() and resolves the effective theme from a persisted preference (Conf/appTheme).
  • AmneziaStyle becomes theme-aware: dark/light palettes selected by an isDark flag bound from the controller, so every binding re-themes live with no per-call-site changes.
  • Settings → Application gains a "Color theme" picker (SelectThemeDrawer).
  • Light palette: light surfaces, dark text, inverted hover/press overlays, dark modal scrim; #E38E41 accent highlight; apricot primary buttons; the Connect-ring glow is suppressed in light mode.
  • Migrate stray hardcoded colors into the palette so they theme correctly (ChangelogDrawer, NewsDetail, AddSitePanel, SFTP doc links).
  • Redraw VerticalRadioButton indicator as palette-driven shapes; add a themeable leading-icon tint to CardWithIconsType.

Light-mode polish

Issues found while testing the light theme (dark mode stays pixel-identical throughout — all of these are gated to the light palette or use theme-aware palette roles):

  • Switch (SwitcherType): the checked track kept the dark-mode brown, which read heavier than the knob on a light surface. Light mode now shows the conventional look — a solid apricot track with a white knob; the disabled "on" pill uses a muted apricot track.
  • Notification toast (PopupType): the popup had a hardcoded white background with theme-inverting text, so in light mode it rendered white-on-white — an empty white box (e.g. the "Unable to disconnect during configuration preparation" notice). Now a theme-aware notificationBackground: white in dark mode, dark charcoal in light mode, so the message reads and the toast stands out.
  • Home protocol chip (PageHome / DropDownType): the prominent inverted chip went from a light pill (dark mode) to a harsh near-black pill in light mode — softened to charcoal. Also balanced its chevron on the short content-hugging pill (added rootButtonImageRightMargin, default unchanged for other dropdowns).
  • Disabled selected radio: a checked-but-disabled VerticalRadioButton title now matches the pale-apricot indicator instead of burnt orange.

Translations

  • Add Color theme / System / Light / Dark across all 8 locale files (ru, uk, zh_CN, fa, ar, my, ur, hi), in both the PageSettingsApplication and SelectThemeDrawer contexts.
  • Fix a pre-existing localization bug: the connection-state strings ("Connect", "Connecting…", "Connected", "Reconnecting…", "Disconnecting…", "Preparing…") rendered in English. The controller had been refactored ConnectionControllerConnectionUiController, which orphaned their tr() context, so runtime lookups fell back to source text. Re-added a ConnectionUiController context to all 8 locale files, reusing the existing translations.

🤖 Generated with Claude Code

real-alexei and others added 2 commits June 22, 2026 03:40
Add a user-selectable color theme (System / Light / Dark, default System)
that follows the OS appearance live.

- AppearanceController wraps QStyleHints::colorScheme() and resolves the
  effective theme from a persisted preference (Conf/appTheme).
- AmneziaStyle becomes theme-aware: dark/light palettes selected by an
  `isDark` flag bound from the controller, so every binding re-themes live
  with no per-call-site changes.
- Settings > Application gains a "Color theme" picker (SelectThemeDrawer).
- Light palette: light surfaces, dark text, inverted hover/press overlays,
  dark modal scrim; #E38E41 accent highlight; apricot primary buttons; the
  Connect-ring glow is suppressed in light mode.
- Migrate stray hardcoded colors into the palette so they theme correctly
  (ChangelogDrawer, NewsDetail, AddSitePanel, SFTP doc links).
- Redraw VerticalRadioButton indicator as palette-driven shapes; add a
  themeable leading-icon tint to CardWithIconsType.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Translate the new "Color theme" picker strings (Color theme / System /
Light / Dark) across all 8 supported languages, in both the
PageSettingsApplication and SelectThemeDrawer contexts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
real-alexei and others added 3 commits June 22, 2026 14:17
In light mode the checked switch previously kept the dark-mode brown
track (richBrown), which read as heavier than the apricot knob on a
light surface. Add theme-aware palette roles (switchCheckedTrack,
switchCheckedTrackBorder, switchCheckedKnob) so light mode shows the
conventional look: a solid apricot track with a white knob. Dark mode
is unchanged (still richBrown track + apricot knob).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The connection-state strings ("Connect", "Connecting...", "Connected",
"Reconnecting...", "Disconnecting...", "Preparing...") rendered in
English because the controller was refactored ConnectionController ->
ConnectionUiController, leaving their translations stranded under the
old (now orphaned) tr() context. Re-add a ConnectionUiController
context to all 8 locale files, reusing the existing translations so the
connect ring and status text localize again.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PopupType had a hardcoded white background while its text uses
midnightBlack. In dark mode midnightBlack is near-black (readable on
white), but in light mode it resolves to white, so the toast rendered
white-on-white — an empty white box (e.g. the "Unable to disconnect
during configuration preparation" notice).

Add a theme-aware notificationBackground: pure white in dark mode
(unchanged), a dark charcoal surface in light mode so the white text
reads and the toast stands out against the light app background.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
real-alexei and others added 3 commits June 22, 2026 15:04
The checked+disabled switch kept the dark-mode deep-brown track and
muted-brown knob, which looked heavy on a light surface. Add theme-aware
roles (switchCheckedDisabledTrack/Border/Knob) so light mode shows a
muted apricot track with a white knob — on-but-inactive, consistent with
the enabled apricot pill. Dark mode is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The home protocol dropdown ("XRay v") is a prominent inverted chip:
a light pill in dark mode, which inverted to a harsh near-black pill
in light mode. Soften the light-mode background to a charcoal (#3A3B40)
so it stays prominent without clashing with the light surface.

Also balance the chevron on this short content-hugging pill: the caret
sat right-heavy because the 40px icon touch-target adds ~8px of
centering padding on top of the 16px right margin. Add a
rootButtonImageRightMargin property (default 16, unchanged for other
dropdowns) and trim it to 8 here so the caret is ~symmetric with the
16px text left margin.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A checked-but-disabled VerticalRadioButton showed its title in
selectedTextDisabledColor (burntOrange), which didn't match the pale
apricot radio indicator. Use selectedRingColor at the indicator's 0.3
opacity for the checked+disabled title so the label and the dot read as
one. Unchecked-disabled, enabled, and dark mode are unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@real-alexei

Copy link
Copy Markdown
Author

Here is how it looks at this stage:
Снимок экрана — 2026-06-22 в 18 57 44
Снимок экрана — 2026-06-22 в 18 57 40
Снимок экрана — 2026-06-22 в 18 57 25
Снимок экрана — 2026-06-22 в 18 57 21
Снимок экрана — 2026-06-22 в 18 57 16
Снимок экрана — 2026-06-22 в 18 57 05
Снимок экрана — 2026-06-22 в 18 56 59
Снимок экрана — 2026-06-22 в 18 56 56
Снимок экрана — 2026-06-22 в 18 56 53
Снимок экрана — 2026-06-22 в 18 56 02

This was referenced Jun 22, 2026
@real-alexei

Copy link
Copy Markdown
Author

please check @ygurov @vkamn

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