Skip to content

feat(theme): add Glass design language - #3130

Open
afonsojramos wants to merge 25 commits into
mainfrom
native-theme-support
Open

feat(theme): add Glass design language#3130
afonsojramos wants to merge 25 commits into
mainfrom
native-theme-support

Conversation

@afonsojramos

@afonsojramos afonsojramos commented Jul 31, 2026

Copy link
Copy Markdown
Member

Summary

After simmering for 2 days @setchy 👀

Adds a second design language, Glass, next to the existing look (now Classic), selectable under Settings → Appearance. Under Glass the whole window becomes one continuous translucent material: the sidebar dissolves into icons floating on the glass, controls (buttons, selects, checkboxes, radios, count pills) render as stepped translucent tiles, notification and repo rows float as rounded cards with glass hover states, and tooltips/popovers come out frosted. Classic is pixel-unchanged: everything Glass lives behind [data-theme='glass'] plus a .gitify-translucent gate, so there is no risk to the default experience.

image image image

Design decisions

  • Native material where the OS offers one. On macOS the window gets a real popover vibrancy material, wired over IPC and ordered so switching designs at runtime never flashes black; the material also follows the app's light/dark mode via nativeTheme sync. Other platforms fall back to CSS backdrop-filter.
  • One token layer, zero component branching. Chrome values (radius, tints, stepped control fills, hairlines) are CSS custom properties re-pointed per language, and Primer's own design tokens are overridden at the data-color-mode boundary, so components stay stock Primer and the entire language lives in App.css.
  • Accessibility degradation. OS Reduce Transparency / Increase Contrast switches Glass to solid, legible surfaces, and the vibrancy material is withheld entirely.
  • Native-feeling details. Desaturated status palette, borderless frosted pills, translucent label tokens that keep their GitHub hue, slim overlay scrollbar, and hover actions that fade row text into the glass instead of painting an opaque band.

Validation

  • Full test suite: 160 files / 1245 tests passing; tsc, lint and format clean.
  • Exercised live on macOS in light and dark: notifications list, settings, filters; hover, focus, disabled and reduced-transparency states; runtime Classic ↔ Glass switching.

New concepts

Theme tokens that re-resolve per language (Tailwind @theme inline)

The radius/shadow/tint utilities are backed by plain CSS variables (--gitify-*) that each design language re-points under its own [data-theme] scope. Declaring them with @theme inline (instead of @theme) makes Tailwind re-resolve the utilities against the active scope rather than freezing them at :root.

@theme inline {
  --radius-gitify-md: var(--gitify-radius-md); /* follows the active [data-theme] */
}

This is why one component tree can serve two languages with no JS branching: the swap is a single attribute on the root. Reach for it when a whole token layer swaps at once; for one-off per-component tweaks, a direct class is simpler.


Note: I now fully prefer this theme 👀

Drop `transparent: true` (it makes behind-window vibrancy render near-opaque over
the desktop) and switch the material to `popover`; keep `visualEffectState: active`
so the menubar popup stays translucent while unfocused. Sync `nativeTheme.themeSource`
with the color mode via a new SET_NATIVE_THEME IPC so dark Glass gets a dark material
instead of light text on a light frost. Clear the Primer `[data-color-mode]` wrapper's
opaque background so the material shows through the whole window.

Also removes the Increase contrast setting, which forced Glass to a solid surface and
whose useAppearance/theme wiring is intertwined with the changes above.
Under translucency: row action buttons become bare icons (no hover band or button
fill) with the row text fading out under them on hover so it never collides;
the scrollbar track goes transparent; and the glass tints are lowered a step so
more of the material shows through.
…witch

applyWindowVibrancy only cleared the window background when disabling, so
switching Classic (which paints an opaque backdrop) to Glass left that backdrop
in place and blocked the vibrancy material from sampling the desktop. Clear it on
enable too; `#00000000` works without a transparent window because the vibrancy
view provides the translucency. Adds regression tests for both toggle sides.
Handle the setNativeTheme IPC rejection (log via rendererLogError) instead of
leaving an unhandled promise, matching the setWindowVibrancy call, and note that
the sync is intentionally not macOS-gated. Adds coverage for the Glass-clamped
path (Glass + DARK_DIMMED still syncs 'dark').
@afonsojramos
afonsojramos requested a review from setchy as a code owner July 31, 2026 22:55
@github-actions github-actions Bot added the enhancement New feature or enhancement to existing functionality label Jul 31, 2026

@setchy setchy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Marking while I have time to review.

@sonarqubecloud

Copy link
Copy Markdown

});

/**
* Sync the native appearance with the app's color mode so the macOS vibrancy

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

i wasn't able to find where in macOS System Settings the vibrancy setting is. Can you point me in the right direction :)

const mockAppearanceSettings: AppearanceSettingsState = {
designLanguage: DesignLanguage.CLASSIC,
theme: Theme.SYSTEM,
increaseContrast: false,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this was used to control an accessibility feature of the GitHub primer themes

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Image

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

was your thoughts to control this from the macOS Accessibility increase contrast setting directly?

/>

<Button aria-label="Zoom percentage" disabled size="small">
{/* Value display, not a control: styled like the group but inert

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

great fix!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

we repeat this a few times - should we extract it out? or ok to duplicate?

@setchy

setchy commented Aug 1, 2026

Copy link
Copy Markdown
Member

Super sharp enhancement, @afonsojramos. I love that we're able to keep the design components as Primer and apply new theme/style overrides. Nice work.

Left a few inline comments for my own curiosity


// Glass is always translucent; it only degrades to solid under the OS Reduce
// Transparency / Increase Contrast settings (a media query in App.css).
const vibrant = designLanguage === DesignLanguage.GLASS && !prefersReducedTransparency;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

it might be nice to allow users to set prefersReducedTransparency just within Gitify, as well as sync it from the macOS system settings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or enhancement to existing functionality

Development

Successfully merging this pull request may close these issues.

2 participants