Convert chrome-local color constants to theme-aware accessors

The previous theme refactor only flipped `colors::*` tokens; the
~50 chrome-local `const FOO: u32 = 0xffffff…;` overlays kept their
white-glass values, so dark mode still painted a light omnibar pill,
light search field, light sidebar tab card, light command-overlay
backdrop, light sync page chips, etc.

Promote `colors::pick(light, dark)` to `pub` so component-internal
constants can reach the same thread-local mode the global palette
uses, then convert every white-glass (`0xffffff??`) and warm-dark
wash (`0x281e14??`) constant in:

- chrome/topbar (omnibar pill, hover chip)
- chrome/sidebar_chrome + chrome/sidebar (rail nav row hover/active,
  highlight border, close-button hover, unread badge)
- chrome/sidebar_header (workspace picker, disclosure card)
- chrome/command_overlay + chrome/command_rows + chrome/command_footer
- chrome/settings_layout, chrome/appearance_form,
  chrome/appearance_layout_cards, chrome/plugin_detail_view
- chrome/home/style (search field, arrow chip, pill, card, add-tile)
- internal_pages/sync (status pill, metrics card, button row)
- internal_pages/plugin_catalog
- shell/render (main pane highlight border)

Each constant becomes `fn name() -> u32 { colors::pick(<light>, <dark>) }`
where the dark counterpart preserves the original alpha but swaps the
warm-white prefix `ffffff` for warm-graphite `1f1d1b` (or the inverse:
`281e14` warm-dark wash → `f2efe9` warm-white wash). Brand accents
(`0xc96442??`, `0xffaa7733`) and the global slate backdrop keep their
single value across modes.
This commit is contained in:
2026-05-15 20:22:51 -04:00
parent 467dcb1f87
commit 39d10bf3d7
20 changed files with 227 additions and 121 deletions
@@ -200,7 +200,7 @@ fn translucency_preset(
.px(px(10.0))
.py(px(4.0))
.rounded(px(6.0))
.bg(rgba(SEGMENT_BG))
.bg(rgba(segment_bg()))
.text_size(px(11.5))
.font_weight(FontWeight(500.0))
.text_color(rgb(colors::ink_2()))
@@ -224,7 +224,7 @@ fn render_theme_mode_row(active: ThemeMode, cx: &mut Context<ElyShell>) -> AnyEl
.gap(px(2.0))
.p(px(2.0))
.rounded(px(8.0))
.bg(rgba(SEGMENT_BG))
.bg(rgba(segment_bg()))
.child(theme_segment("System", ThemeMode::System, active, cx))
.child(theme_segment("Light", ThemeMode::Light, active, cx))
.child(theme_segment("Dark", ThemeMode::Dark, active, cx))
@@ -326,7 +326,7 @@ fn render_reset_row(cx: &mut Context<ElyShell>) -> AnyElement {
.px(px(12.0))
.py(px(7.0))
.rounded(px(8.0))
.bg(rgba(SEGMENT_BG))
.bg(rgba(segment_bg()))
.text_size(px(12.0))
.font_weight(FontWeight(500.0))
.text_color(rgb(colors::ink_2()))
@@ -399,4 +399,6 @@ fn transparent_like(color: Hsla) -> Hsla {
hsla(color.h, color.s, color.l, 0.0)
}
const SEGMENT_BG: u32 = 0x281e140d;
fn segment_bg() -> u32 {
colors::pick(0x281e140d, 0xf2efe90d)
}
@@ -119,7 +119,7 @@ fn render_layout_card(
.id(SharedString::from(mode.id()))
.p(px(14.0))
.rounded(px(14.0))
.bg(rgba(LAYOUT_CARD_BG))
.bg(rgba(layout_card_bg()))
.when(selected, |el| el.border_2().border_color(rgb(colors::accent())))
.when(!selected, |el| el.border_1().border_color(rgba(colors::stroke())))
.flex()
@@ -165,7 +165,7 @@ fn render_layout_preview(mode: LayoutMode) -> AnyElement {
.bottom(px(6.0))
.w(px(sidebar_width))
.rounded(px(5.0))
.bg(rgba(LAYOUT_PANEL_BG)),
.bg(rgba(layout_panel_bg())),
)
.child(
div()
@@ -175,11 +175,17 @@ fn render_layout_preview(mode: LayoutMode) -> AnyElement {
.right(px(6.0))
.bottom(px(6.0))
.rounded(px(5.0))
.bg(rgba(LAYOUT_CANVAS_BG)),
.bg(rgba(layout_canvas_bg())),
)
.into_any_element()
}
const LAYOUT_CARD_BG: u32 = 0xffffffd9;
const LAYOUT_PANEL_BG: u32 = 0x281e1414;
const LAYOUT_CANVAS_BG: u32 = 0x281e140a;
fn layout_card_bg() -> u32 {
colors::pick(0xffffffd9, 0x1f1d1bd9)
}
fn layout_panel_bg() -> u32 {
colors::pick(0x281e1414, 0xf2efe914)
}
fn layout_canvas_bg() -> u32 {
colors::pick(0x281e140a, 0xf2efe90a)
}
@@ -13,7 +13,7 @@ pub(crate) fn render_command_footer() -> AnyElement {
.border_color(rgba(colors::divider()))
.text_size(px(10.5))
.text_color(rgb(colors::ink_3()))
.bg(rgba(FOOTER_BG))
.bg(rgba(footer_bg()))
// Only the shortcuts that actually do something today.
// `⌘↵` (split-on-open) and `⇥` (filter) are real product
// features but not yet wired through the command overlay's
@@ -41,12 +41,16 @@ pub(crate) fn render_kbd(label: &'static str) -> AnyElement {
.px(px(5.0))
.py(px(1.0))
.rounded(px(4.0))
.bg(rgba(KBD_BG))
.bg(rgba(kbd_bg()))
.text_size(px(10.0))
.text_color(rgb(colors::ink_3()))
.child(label)
.into_any_element()
}
const KBD_BG: u32 = 0xffffffd9;
const FOOTER_BG: u32 = 0xffffff8c;
fn kbd_bg() -> u32 {
colors::pick(0xffffffd9, 0x1f1d1bd9)
}
fn footer_bg() -> u32 {
colors::pick(0xffffff8c, 0x1f1d1b8c)
}
@@ -45,7 +45,7 @@ fn render_overlay(
div()
.absolute()
.inset_0()
.bg(rgba(BACKDROP_BG))
.bg(rgba(backdrop_bg()))
.flex()
.flex_col()
.items_center()
@@ -67,9 +67,9 @@ fn render_panel(
div()
.w(px(640.0))
.rounded(px(16.0))
.bg(rgba(PANEL_BG))
.bg(rgba(panel_bg()))
.border_1()
.border_color(rgba(PANEL_BORDER))
.border_color(rgba(panel_border()))
.shadow(panel_shadow())
.overflow_hidden()
.flex()
@@ -111,7 +111,7 @@ fn render_header(query_label: String, is_empty: bool) -> AnyElement {
.px(px(8.0))
.py(px(2.0))
.rounded(px(6.0))
.bg(rgba(BADGE_BG))
.bg(rgba(badge_bg()))
.text_size(px(10.5))
.text_color(rgb(colors::ink_3()))
.child("Switcher"),
@@ -199,10 +199,18 @@ fn render_empty_state() -> AnyElement {
.into_any_element()
}
const PANEL_BG: u32 = 0xfffffff5;
const PANEL_BORDER: u32 = 0xffffff80;
const BACKDROP_BG: u32 = 0x140f0a3d;
const BADGE_BG: u32 = 0x281e140f;
fn panel_bg() -> u32 {
colors::pick(0xfffffff5, 0x1f1d1bf5)
}
fn panel_border() -> u32 {
colors::pick(0xffffff80, 0x1f1d1b80)
}
fn backdrop_bg() -> u32 {
colors::pick(0x140f0a3d, 0x0d0c0a3d)
}
fn badge_bg() -> u32 {
colors::pick(0x281e140f, 0xf2efe90f)
}
fn panel_shadow() -> Vec<BoxShadow> {
vec![BoxShadow {
@@ -175,7 +175,7 @@ where
let leading = div()
.size(px(24.0))
.rounded(px(6.0))
.bg(rgba(ROW_ICON_BG))
.bg(rgba(row_icon_bg()))
.flex()
.items_center()
.justify_center()
@@ -268,4 +268,6 @@ where
const ROW_HOVER_BG: u32 = 0xc9644214;
const ROW_SELECTED_BG: u32 = 0xc964421f;
const ROW_ICON_BG: u32 = 0xffffffd9;
fn row_icon_bg() -> u32 {
colors::pick(0xffffffd9, 0x1f1d1bd9)
}
@@ -11,7 +11,7 @@ use crate::shell::ElyShell;
use crate::shell::chrome::render_glyph_for;
use super::section::render_section_chevron_label;
use super::style::{ADD_TILE_BG, CARD_BG, CARD_BG_HOVER, card_shadow};
use super::style::{add_tile_bg, card_bg, card_bg_hover, card_shadow};
pub(crate) fn render_favorites_grid(
snapshot: &BrowserSnapshot,
@@ -53,7 +53,7 @@ fn render_favorite_tile(index: usize, tab: &BrowserTab, cx: &mut Context<ElyShel
.id(SharedString::from(format!("fav-tile-{index}")))
.h(px(96.0))
.rounded(px(16.0))
.bg(rgba(CARD_BG))
.bg(rgba(card_bg()))
.shadow(card_shadow())
.flex()
.flex_col()
@@ -61,7 +61,7 @@ fn render_favorite_tile(index: usize, tab: &BrowserTab, cx: &mut Context<ElyShel
.justify_center()
.gap(px(8.0))
.cursor_pointer()
.hover(|style| style.bg(rgba(CARD_BG_HOVER)))
.hover(|style| style.bg(rgba(card_bg_hover())))
.active(|style| style.opacity(0.85))
.on_click(cx.listener(move |shell, _, window, cx| {
shell.select_tab(&tab_id, window, cx);
@@ -83,13 +83,13 @@ fn render_add_favorite_tile(cx: &mut Context<ElyShell>) -> AnyElement {
.id(SharedString::from("fav-tile-add"))
.h(px(96.0))
.rounded(px(16.0))
.bg(rgba(ADD_TILE_BG))
.bg(rgba(add_tile_bg()))
.flex()
.items_center()
.justify_center()
.text_color(rgb(colors::ink_4()))
.cursor_pointer()
.hover(|style| style.bg(rgba(CARD_BG)))
.hover(|style| style.bg(rgba(card_bg())))
.on_click(cx.listener(|shell, _, window, cx| {
shell.open_internal_tab("ely://bookmarks", window, cx);
}))
+5 -5
View File
@@ -9,7 +9,7 @@ use crate::shell::ElyShell;
use crate::shell::chrome::{SERIF_FAMILY, render_glyph_for};
use super::style::{ARROW_CHIP_BG, PILL_BG, PILL_BG_HOVER, SEARCH_BG, card_shadow, soft_shadow};
use super::style::{arrow_chip_bg, card_shadow, pill_bg, pill_bg_hover, search_bg, soft_shadow};
use super::time::DayPhase;
pub(crate) fn render_hero(
@@ -84,7 +84,7 @@ fn render_search_bar(shell: &ElyShell, cx: &mut Context<ElyShell>) -> AnyElement
.w(px(640.0))
.h(px(54.0))
.rounded(px(14.0))
.bg(rgba(SEARCH_BG))
.bg(rgba(search_bg()))
.shadow(card_shadow())
.px(px(16.0))
.flex()
@@ -110,7 +110,7 @@ fn render_search_bar(shell: &ElyShell, cx: &mut Context<ElyShell>) -> AnyElement
div()
.size(px(28.0))
.rounded(px(8.0))
.bg(rgba(ARROW_CHIP_BG))
.bg(rgba(arrow_chip_bg()))
.flex()
.items_center()
.justify_center()
@@ -178,7 +178,7 @@ where
div()
.id(SharedString::from(id))
.rounded(px(999.0))
.bg(rgba(PILL_BG))
.bg(rgba(pill_bg()))
.shadow(soft_shadow())
.px(px(12.0))
.py(px(6.0))
@@ -186,7 +186,7 @@ where
.items_center()
.gap(px(6.0))
.cursor_pointer()
.hover(|style| style.bg(rgba(PILL_BG_HOVER)))
.hover(|style| style.bg(rgba(pill_bg_hover())))
.active(|style| style.opacity(0.82))
.on_click(cx.listener(move |shell, _, window, cx| handler(shell, window, cx)))
.child(leading)
@@ -13,7 +13,7 @@ use gpui_component::IconName;
use crate::shell::ElyShell;
use crate::shell::chrome::{SERIF_FAMILY, render_glyph_for};
use super::style::{CARD_BG, card_shadow};
use super::style::{card_bg, card_shadow};
use super::time::relative_time_label;
pub(crate) fn render_recap(snapshot: &BrowserSnapshot, cx: &mut Context<ElyShell>) -> AnyElement {
@@ -44,7 +44,7 @@ fn render_continue_card(
div()
.rounded(px(16.0))
.bg(rgba(CARD_BG))
.bg(rgba(card_bg()))
.shadow(card_shadow())
.p(px(16.0))
.flex()
@@ -244,7 +244,7 @@ fn render_activity_card(
div()
.rounded(px(16.0))
.bg(rgba(CARD_BG))
.bg(rgba(card_bg()))
.shadow(card_shadow())
.p(px(16.0))
.flex()
+22 -7
View File
@@ -1,12 +1,27 @@
use ely_design_system::colors;
use gpui::{BoxShadow, hsla, point, px};
pub(crate) const SEARCH_BG: u32 = 0xffffffd9;
pub(crate) const ARROW_CHIP_BG: u32 = 0x281e140a;
pub(crate) const PILL_BG: u32 = 0xffffff8c;
pub(crate) const PILL_BG_HOVER: u32 = 0xffffffd9;
pub(crate) const CARD_BG: u32 = 0xffffffc7;
pub(crate) const CARD_BG_HOVER: u32 = 0xffffffeb;
pub(crate) const ADD_TILE_BG: u32 = 0xffffff7f;
pub(crate) fn search_bg() -> u32 {
colors::pick(0xffffffd9, 0x1f1d1bd9)
}
pub(crate) fn arrow_chip_bg() -> u32 {
colors::pick(0x281e140a, 0xf2efe90a)
}
pub(crate) fn pill_bg() -> u32 {
colors::pick(0xffffff8c, 0x1f1d1b8c)
}
pub(crate) fn pill_bg_hover() -> u32 {
colors::pick(0xffffffd9, 0x1f1d1bd9)
}
pub(crate) fn card_bg() -> u32 {
colors::pick(0xffffffc7, 0x1f1d1bc7)
}
pub(crate) fn card_bg_hover() -> u32 {
colors::pick(0xffffffeb, 0x1f1d1beb)
}
pub(crate) fn add_tile_bg() -> u32 {
colors::pick(0xffffff7f, 0x1f1d1b7f)
}
pub(crate) fn card_shadow() -> Vec<BoxShadow> {
vec![BoxShadow {
@@ -32,7 +32,7 @@ pub(crate) fn render_plugin_detail_view(
.mx_auto()
.p(px(24.0))
.rounded(px(16.0))
.bg(rgba(CARD_BG))
.bg(rgba(card_bg()))
.grid()
.grid_cols(8)
.gap(px(24.0))
@@ -138,13 +138,13 @@ where
.id(SharedString::from("plugin-detail-secondary"))
.size(px(40.0))
.rounded(px(10.0))
.bg(rgba(SECONDARY_BG))
.bg(rgba(secondary_bg()))
.flex()
.items_center()
.justify_center()
.text_color(rgb(colors::ink_3()))
.cursor_pointer()
.hover(|style| style.bg(rgba(SECONDARY_BG_HOVER)).text_color(rgb(colors::ink())))
.hover(|style| style.bg(rgba(secondary_bg_hover())).text_color(rgb(colors::ink())))
.active(|style| style.opacity(0.82))
.on_click(cx.listener(move |shell, _, window, cx| handler(shell, window, cx)))
.child(icon)
@@ -304,7 +304,7 @@ fn stat_card(label: &'static str, value: String) -> AnyElement {
.px(px(12.0))
.py(px(10.0))
.rounded(px(10.0))
.bg(rgba(STAT_BG))
.bg(rgba(stat_bg()))
.flex()
.flex_col()
.gap(px(2.0))
@@ -358,7 +358,7 @@ fn render_contribution_row(contribution: &PluginContributionPoint) -> AnyElement
.px(px(12.0))
.py(px(10.0))
.rounded(px(10.0))
.bg(rgba(CONTRIBUTION_BG))
.bg(rgba(contribution_bg()))
.child(
div()
.size(px(18.0))
@@ -401,9 +401,19 @@ fn category_label(plugin: &InstalledPlugin) -> &'static str {
if high_risk { "ELY · AUDIT NEEDED" } else { "ELY · SANDBOXED" }
}
const CARD_BG: u32 = 0xffffffd9;
const SECONDARY_BG: u32 = 0xffffffd9;
const SECONDARY_BG_HOVER: u32 = 0xffffffeb;
const STAT_BG: u32 = 0xffffffb3;
const CONTRIBUTION_BG: u32 = 0xffffff8c;
fn card_bg() -> u32 {
colors::pick(0xffffffd9, 0x1f1d1bd9)
}
fn secondary_bg() -> u32 {
colors::pick(0xffffffd9, 0x1f1d1bd9)
}
fn secondary_bg_hover() -> u32 {
colors::pick(0xffffffeb, 0x1f1d1beb)
}
fn stat_bg() -> u32 {
colors::pick(0xffffffb3, 0x1f1d1bb3)
}
fn contribution_bg() -> u32 {
colors::pick(0xffffff8c, 0x1f1d1b8c)
}
const CONTRIBUTION_BADGE_BG: u32 = 0xc964421f;
@@ -202,7 +202,7 @@ fn render_nav_item(
cx: &mut Context<ElyShell>,
) -> AnyElement {
let active = item.route == active_route;
let bg = if active { ACTIVE_BG } else { 0x00000000 };
let bg = if active { active_bg() } else { 0x00000000 };
let text_color = if active { colors::ink() } else { colors::ink_2() };
let route = item.route;
let icon = item.icon.clone();
@@ -219,7 +219,7 @@ fn render_nav_item(
.text_size(px(13.0))
.text_color(rgb(text_color))
.cursor_pointer()
.hover(|style| style.bg(rgba(HOVER_BG)))
.hover(|style| style.bg(rgba(hover_bg())))
.active(|style| style.opacity(0.85))
.on_click(cx.listener(move |shell, _, window, cx| {
shell.open_internal_tab(route, window, cx);
@@ -229,5 +229,9 @@ fn render_nav_item(
.into_any_element()
}
const ACTIVE_BG: u32 = 0xffffffd9;
const HOVER_BG: u32 = 0xffffff8c;
fn active_bg() -> u32 {
colors::pick(0xffffffd9, 0x1f1d1bd9)
}
fn hover_bg() -> u32 {
colors::pick(0xffffff8c, 0x1f1d1b8c)
}
+10 -10
View File
@@ -10,8 +10,8 @@ use gpui_component::{IconName, StyledExt, scroll::ScrollableElement};
use crate::shell::ElyShell;
use crate::shell::chrome::sidebar_chrome::{
ACTIVE_NAV_BG, ACTIVE_NAV_BG_HOVER, CLOSE_HOVER_BG, HIGHLIGHT_BORDER, HOVER_NAV_BG,
ROW_CLOSE_SIZE, panel_bg, panel_shadow, profile_initial, render_sidebar_resize_handle,
ROW_CLOSE_SIZE, active_nav_bg, active_nav_bg_hover, close_hover_bg, highlight_border,
hover_nav_bg, panel_bg, panel_shadow, profile_initial, render_sidebar_resize_handle,
render_unread_badge, section_label, section_tabs_label, soft_shadow,
};
use crate::shell::chrome::{render_glyph_for, render_sidebar_header};
@@ -43,7 +43,7 @@ impl ElyShell {
.rounded(px(spacing::RADIUS_CARD))
.bg(rgba(panel_color))
.border_1()
.border_color(rgba(HIGHLIGHT_BORDER))
.border_color(rgba(highlight_border()))
.shadow(panel_shadow())
.overflow_hidden()
.child(render_sidebar_header(self, snapshot, cx))
@@ -102,7 +102,7 @@ impl ElyShell {
fn render_settings_row(&mut self, cx: &mut Context<Self>) -> AnyElement {
// Settings is never an "active" row — only ever rest or hover —
// so it uses HOVER_NAV_BG directly. Keeping it lighter than the
// so it uses hover_nav_bg() directly. Keeping it lighter than the
// active nav card means the eye still finds the active selection
// first when both are visible.
div()
@@ -116,7 +116,7 @@ impl ElyShell {
.text_size(px(13.0))
.text_color(rgb(colors::ink_2()))
.cursor_pointer()
.hover(|style| style.bg(rgba(HOVER_NAV_BG)).text_color(rgb(colors::ink())))
.hover(|style| style.bg(rgba(hover_nav_bg())).text_color(rgb(colors::ink())))
.active(|style| style.opacity(0.82))
.on_click(cx.listener(|shell, _, window, cx| {
shell.open_internal_tab("ely://settings", window, cx);
@@ -142,7 +142,7 @@ impl ElyShell {
.flex()
.items_center()
.cursor_pointer()
.hover(|style| style.bg(rgba(HOVER_NAV_BG)))
.hover(|style| style.bg(rgba(hover_nav_bg())))
.active(|style| style.opacity(0.82))
.on_click(cx.listener(|shell, _, window, cx| {
shell.open_internal_tab("ely://settings/profiles", window, cx);
@@ -293,7 +293,7 @@ impl ElyShell {
.text_color(rgb(colors::ink_3()))
.text_size(px(13.0))
.cursor_pointer()
.hover(|style| style.bg(rgba(HOVER_NAV_BG)).text_color(rgb(colors::ink())))
.hover(|style| style.bg(rgba(hover_nav_bg())).text_color(rgb(colors::ink())))
.active(|style| style.opacity(0.82))
.on_click(cx.listener(|shell, _, window, cx| {
shell.open_new_tab(window, cx);
@@ -377,9 +377,9 @@ impl ElyShell {
/// place rather than three transparent-sentinel triples.
fn nav_row_palette(active: bool) -> NavRowPalette {
if active {
NavRowPalette { bg: ACTIVE_NAV_BG, hover_bg: ACTIVE_NAV_BG_HOVER, text: colors::ink() }
NavRowPalette { bg: active_nav_bg(), hover_bg: active_nav_bg_hover(), text: colors::ink() }
} else {
NavRowPalette { bg: 0x00000000, hover_bg: HOVER_NAV_BG, text: colors::ink_2() }
NavRowPalette { bg: 0x00000000, hover_bg: hover_nav_bg(), text: colors::ink_2() }
}
}
@@ -417,7 +417,7 @@ where
.text_color(rgb(colors::ink_4()))
.opacity(0.0)
.group_hover(group_name, |style| style.opacity(1.0))
.hover(|style| style.bg(rgba(CLOSE_HOVER_BG)).text_color(rgb(colors::ink())))
.hover(|style| style.bg(rgba(close_hover_bg())).text_color(rgb(colors::ink())))
.cursor_pointer()
.on_click(on_click)
.child(IconName::Close)
@@ -10,36 +10,48 @@ use crate::shell::ElyShell;
/// Hover/active palette for the vertical sidebar rows.
///
/// The original code reused `ACTIVE_NAV_BG` for both `active` and
/// The original code reused `active_nav_bg()` for both `active` and
/// `hover`, which made an already-active row visually inert under the
/// cursor — the object refused to acknowledge the touch. The four
/// tokens below split that single value into a four-step ladder:
///
/// rest (transparent) → HOVER_NAV_BG → ACTIVE_NAV_BG → ACTIVE_NAV_BG_HOVER
/// rest (transparent) → hover_nav_bg() → active_nav_bg() → active_nav_bg_hover()
///
/// so every state transition produces a real, perceptible change.
///
/// Values were picked by eye against the warm panel tint, then nudged
/// until the wash on a Dawn-themed panel reads as "you touched this"
/// without competing with the active selection's authority.
pub(crate) const HOVER_NAV_BG: u32 = 0xffffff66; // 40% white wash
pub(crate) const ACTIVE_NAV_BG: u32 = 0xffffffd9; // 85% white card
pub(crate) const ACTIVE_NAV_BG_HOVER: u32 = 0xfffffff2; // 95% white — active + hover
pub(crate) fn hover_nav_bg() -> u32 {
colors::pick(0xffffff66, 0x1f1d1b66)
} // 40% white wash
pub(crate) fn active_nav_bg() -> u32 {
colors::pick(0xffffffd9, 0x1f1d1bd9)
} // 85% white card
pub(crate) fn active_nav_bg_hover() -> u32 {
colors::pick(0xfffffff2, 0x1f1d1bf2)
} // 95% white — active + hover
/// Hover tint behind the per-row close (×) button. Was 8% alpha, which
/// was visually indistinguishable from the panel background and made
/// the click target read as inert. Brought to ~30% alpha so the
/// hover registers as a real "press here" surface, matching the
/// confidence of close buttons in Arc/Dia/Zen.
pub(crate) const CLOSE_HOVER_BG: u32 = 0x281e144d;
pub(crate) fn close_hover_bg() -> u32 {
colors::pick(0x281e144d, 0xf2efe94d)
}
pub(crate) const UNREAD_BADGE_BG: u32 = 0x281e140f;
pub(crate) fn unread_badge_bg() -> u32 {
colors::pick(0x281e140f, 0xf2efe90f)
}
/// 50% white inner border that traces every glass panel — the GPUI
/// substitute for the design's `box-shadow: inset 0 0 0 1px rgba(255,255,255,0.5)`.
/// Painted as the panel's own border so it stays part of the frame and
/// never participates in hit testing.
pub(crate) const HIGHLIGHT_BORDER: u32 = 0xffffff80;
pub(crate) fn highlight_border() -> u32 {
colors::pick(0xffffff80, 0x1f1d1b80)
}
pub(crate) const RESIZE_HANDLE_HOVER_BG: u32 = 0xffaa7733;
@@ -62,7 +74,7 @@ pub(crate) fn render_unread_badge(count: u32) -> impl IntoElement {
.px(px(6.0))
.py(px(1.0))
.rounded(px(999.0))
.bg(rgba(UNREAD_BADGE_BG))
.bg(rgba(unread_badge_bg()))
.text_size(px(10.0))
.font_weight(FontWeight(500.0))
.text_color(rgb(colors::ink_3()))
@@ -136,7 +136,7 @@ fn render_picker_pill(
let space_name = active_space.map(|space| space.name().to_string()).unwrap_or_default();
let space_glyph = active_space.map(|space| space.icon().to_string()).unwrap_or_default();
let chevron = if picker_open { IconName::ChevronUp } else { IconName::ChevronDown };
let bg = if picker_open { PICKER_BG_HOVER } else { PICKER_BG };
let bg = if picker_open { picker_bg_hover() } else { picker_bg() };
div()
.id(SharedString::from("workspace-picker"))
@@ -151,7 +151,7 @@ fn render_picker_pill(
.bg(rgba(bg))
.shadow(soft_shadow())
.cursor_pointer()
.hover(|style| style.bg(rgba(PICKER_BG_HOVER)))
.hover(|style| style.bg(rgba(picker_bg_hover())))
.active(|style| style.opacity(0.85))
.on_click(cx.listener(|shell, _, _, cx| {
shell.toggle_workspace_picker(cx);
@@ -237,9 +237,9 @@ pub(crate) fn render_workspace_disclosure(
.gap(px(2.0))
.p(px(4.0))
.rounded(px(10.0))
.bg(rgba(DISCLOSURE_BG))
.bg(rgba(disclosure_bg()))
.border_1()
.border_color(rgba(DISCLOSURE_BORDER))
.border_color(rgba(disclosure_border()))
.shadow(soft_shadow())
.children(
snapshot
@@ -280,7 +280,7 @@ fn render_disclosure_row(
) -> AnyElement {
let space_id = space.id().clone();
let active = space.id() == active_id;
let bg = if active { DISCLOSURE_ROW_ACTIVE_BG } else { 0x00000000 };
let bg = if active { disclosure_row_active_bg() } else { 0x00000000 };
div()
.id(SharedString::from(format!("workspace-row-{index}")))
@@ -292,7 +292,7 @@ fn render_disclosure_row(
.rounded(px(8.0))
.bg(rgba(bg))
.cursor_pointer()
.hover(|style| style.bg(rgba(DISCLOSURE_ROW_HOVER_BG)))
.hover(|style| style.bg(rgba(disclosure_row_hover_bg())))
.active(|style| style.opacity(0.85))
.on_click(cx.listener(move |shell, _, window, cx| {
shell.select_space_from_picker(&space_id, window, cx);
@@ -327,7 +327,7 @@ fn render_disclosure_footer(cx: &mut Context<ElyShell>) -> AnyElement {
.text_size(px(12.0))
.text_color(rgb(colors::ink_3()))
.cursor_pointer()
.hover(|style| style.bg(rgba(DISCLOSURE_ROW_HOVER_BG)).text_color(rgb(colors::ink())))
.hover(|style| style.bg(rgba(disclosure_row_hover_bg())).text_color(rgb(colors::ink())))
.active(|style| style.opacity(0.85))
.on_click(cx.listener(|shell, _, window, cx| {
shell.close_workspace_picker(cx);
@@ -361,14 +361,14 @@ fn render_add_workspace_button(cx: &mut Context<ElyShell>) -> AnyElement {
.id(SharedString::from("workspace-add"))
.size(px(PICKER_BUTTON_SIZE))
.rounded(px(9.0))
.bg(rgba(ADD_BUTTON_BG))
.bg(rgba(add_button_bg()))
.shadow(soft_shadow())
.flex()
.items_center()
.justify_center()
.text_color(rgb(colors::ink_3()))
.cursor_pointer()
.hover(|style| style.bg(rgba(PICKER_BG)).text_color(rgb(colors::ink())))
.hover(|style| style.bg(rgba(picker_bg())).text_color(rgb(colors::ink())))
.active(|style| style.opacity(0.82))
.on_click(cx.listener(|shell, _, window, cx| {
shell.open_internal_tab("ely://settings/spaces", window, cx);
@@ -377,13 +377,27 @@ fn render_add_workspace_button(cx: &mut Context<ElyShell>) -> AnyElement {
.into_any_element()
}
const PICKER_BG: u32 = 0xffffff99;
const PICKER_BG_HOVER: u32 = 0xffffffd9;
const ADD_BUTTON_BG: u32 = 0xffffff66;
const DISCLOSURE_BG: u32 = 0xffffffd9;
const DISCLOSURE_BORDER: u32 = 0xffffff80;
const DISCLOSURE_ROW_ACTIVE_BG: u32 = 0xffffffeb;
const DISCLOSURE_ROW_HOVER_BG: u32 = 0xffffffb3;
fn picker_bg() -> u32 {
colors::pick(0xffffff99, 0x1f1d1b99)
}
fn picker_bg_hover() -> u32 {
colors::pick(0xffffffd9, 0x1f1d1bd9)
}
fn add_button_bg() -> u32 {
colors::pick(0xffffff66, 0x1f1d1b66)
}
fn disclosure_bg() -> u32 {
colors::pick(0xffffffd9, 0x1f1d1bd9)
}
fn disclosure_border() -> u32 {
colors::pick(0xffffff80, 0x1f1d1b80)
}
fn disclosure_row_active_bg() -> u32 {
colors::pick(0xffffffeb, 0x1f1d1beb)
}
fn disclosure_row_hover_bg() -> u32 {
colors::pick(0xffffffb3, 0x1f1d1bb3)
}
fn soft_shadow() -> Vec<BoxShadow> {
vec![
+10 -6
View File
@@ -79,7 +79,7 @@ fn render_omnibar(
.flex_1()
.h(px(spacing::OMNIBAR_HEIGHT))
.rounded(px(spacing::RADIUS_PILL))
.bg(rgba(OMNIBAR_BG))
.bg(rgba(omnibar_bg()))
.shadow(soft_shadow())
.px(px(14.0))
.flex()
@@ -168,7 +168,7 @@ where
.justify_center()
.text_color(rgb(color))
.cursor_pointer()
.hover(|style| style.bg(rgba(CHIP_HOVER_BG)).text_color(rgb(colors::ink())))
.hover(|style| style.bg(rgba(chip_hover_bg())).text_color(rgb(colors::ink())))
.active(|style| style.opacity(0.7))
.on_click(cx.listener(move |shell, _, window, cx| handler(shell, window, cx)))
.child(icon)
@@ -196,7 +196,7 @@ where
.text_color(rgb(color))
.when(enabled, |el| {
el.cursor_pointer()
.hover(|style| style.bg(rgba(OMNIBAR_BG)).text_color(rgb(colors::ink())))
.hover(|style| style.bg(rgba(omnibar_bg())).text_color(rgb(colors::ink())))
.active(|style| style.opacity(0.82))
.on_click(cx.listener(move |shell, _, window, cx| handler(shell, window, cx)))
})
@@ -222,15 +222,19 @@ where
.justify_center()
.cursor_pointer()
.text_color(rgb(colors::ink_3()))
.hover(|style| style.bg(rgba(OMNIBAR_BG)).text_color(rgb(colors::ink())))
.hover(|style| style.bg(rgba(omnibar_bg())).text_color(rgb(colors::ink())))
.active(|style| style.opacity(0.82))
.on_click(cx.listener(move |shell, _, window, cx| handler(shell, window, cx)))
.child(icon)
.into_any_element()
}
const OMNIBAR_BG: u32 = 0xffffff8c;
const CHIP_HOVER_BG: u32 = 0xffffffd9;
fn omnibar_bg() -> u32 {
colors::pick(0xffffff8c, 0x1f1d1b8c)
}
fn chip_hover_bg() -> u32 {
colors::pick(0xffffffd9, 0x1f1d1bd9)
}
/// Topbar quick-toggle icon for the current theme mode. The button
/// cycles System → Light → Dark → System, and the icon previews the
@@ -91,7 +91,7 @@ impl ElyShell {
.h(px(42.0))
.px(px(16.0))
.rounded(px(12.0))
.bg(rgba(SEARCH_BG))
.bg(rgba(search_bg()))
.flex()
.items_center()
.gap(px(10.0))
@@ -258,12 +258,12 @@ fn render_plugin_card(
.id(SharedString::from(format!("plugin-card-{index}")))
.p(px(14.0))
.rounded(px(16.0))
.bg(rgba(CARD_BG))
.bg(rgba(card_bg()))
.flex()
.flex_col()
.gap(px(10.0))
.cursor_pointer()
.hover(|style| style.bg(rgba(CARD_BG_HOVER)))
.hover(|style| style.bg(rgba(card_bg_hover())))
.active(|style| style.opacity(0.85))
.on_click(cx.listener(move |shell, _, window, cx| {
shell.open_internal_tab(&detail_route, window, cx);
@@ -317,7 +317,7 @@ fn render_plugin_card(
.px(px(9.0))
.py(px(3.0))
.rounded(px(6.0))
.bg(rgba(STATUS_BG))
.bg(rgba(status_bg()))
.text_size(px(11.0))
.font_weight(FontWeight(500.0))
.text_color(rgb(status_color))
@@ -378,11 +378,19 @@ fn sandbox_chip_label(snapshot: &BrowserSnapshot) -> &'static str {
}
}
const SEARCH_BG: u32 = 0xffffffd9;
fn search_bg() -> u32 {
colors::pick(0xffffffd9, 0x1f1d1bd9)
}
const INSTALL_BG: u32 = 0x1d1c1aff;
const CARD_BG: u32 = 0xffffffc7;
const CARD_BG_HOVER: u32 = 0xffffffeb;
const STATUS_BG: u32 = 0xffffffd9;
fn card_bg() -> u32 {
colors::pick(0xffffffc7, 0x1f1d1bc7)
}
fn card_bg_hover() -> u32 {
colors::pick(0xffffffeb, 0x1f1d1beb)
}
fn status_bg() -> u32 {
colors::pick(0xffffffd9, 0x1f1d1bd9)
}
/// Pick a deterministic gradient pair for a plugin cover. Mirrors the
/// design's palette of 8 pastel-to-bold ramps used across `plugins.jsx`,
@@ -55,7 +55,7 @@ fn render_status_pill(snapshot: &BrowserSnapshot) -> AnyElement {
.px(px(12.0))
.py(px(5.0))
.rounded(px(999.0))
.bg(rgba(PILL_BG))
.bg(rgba(pill_bg()))
.text_size(px(11.0))
.text_color(rgb(colors::ink_3()))
.child(div().text_color(rgb(colors::accent())).child(IconName::Globe))
@@ -93,7 +93,7 @@ fn render_metrics_card(snapshot: &BrowserSnapshot, cx: &mut Context<ElyShell>) -
.max_w(px(380.0))
.p(px(20.0))
.rounded(px(16.0))
.bg(rgba(CARD_BG))
.bg(rgba(card_bg()))
.flex()
.flex_col()
.gap(px(16.0))
@@ -160,12 +160,12 @@ fn render_reset_button(cx: &mut Context<ElyShell>) -> AnyElement {
.px(px(12.0))
.py(px(7.0))
.rounded(px(8.0))
.bg(rgba(BUTTON_BG))
.bg(rgba(button_bg()))
.text_size(px(12.0))
.font_weight(FontWeight(500.0))
.text_color(rgb(colors::ink_2()))
.cursor_pointer()
.hover(|style| style.bg(rgba(BUTTON_BG_HOVER)))
.hover(|style| style.bg(rgba(button_bg_hover())))
.active(|style| style.opacity(0.85))
.on_click(cx.listener(|shell, _, _, cx| shell.reset_sync_settings(cx)))
.child("Reset to defaults"),
@@ -186,7 +186,7 @@ fn render_what_syncs_card(snapshot: &BrowserSnapshot, cx: &mut Context<ElyShell>
div()
.p(px(18.0))
.rounded(px(16.0))
.bg(rgba(CARD_BG))
.bg(rgba(card_bg()))
.flex()
.flex_col()
.gap(px(12.0))
@@ -370,7 +370,15 @@ fn sync_object_state_label(state: SyncObjectState) -> &'static str {
}
}
const PILL_BG: u32 = 0xffffffb3;
const CARD_BG: u32 = 0xffffffd9;
const BUTTON_BG: u32 = 0xffffffd9;
const BUTTON_BG_HOVER: u32 = 0xffffffeb;
fn pill_bg() -> u32 {
colors::pick(0xffffffb3, 0x1f1d1bb3)
}
fn card_bg() -> u32 {
colors::pick(0xffffffd9, 0x1f1d1bd9)
}
fn button_bg() -> u32 {
colors::pick(0xffffffd9, 0x1f1d1bd9)
}
fn button_bg_hover() -> u32 {
colors::pick(0xffffffeb, 0x1f1d1beb)
}
+4 -2
View File
@@ -192,7 +192,7 @@ impl ElyShell {
.rounded(px(spacing::RADIUS_CARD))
.bg(rgba(panel_color))
.border_1()
.border_color(rgba(MAIN_PANE_HIGHLIGHT_BORDER))
.border_color(rgba(main_pane_highlight_border()))
.shadow(panel_shadow())
.overflow_hidden()
.child(render_topbar_chrome(self, snapshot, active_tab, sidebar_collapsed, window, cx))
@@ -339,7 +339,9 @@ impl ElyShell {
/// substitute for the design's `box-shadow: inset 0 0 0 1px rgba(255,255,255,0.5)`.
/// Painted as the panel's own border so it stays part of the frame and
/// never participates in hit testing.
const MAIN_PANE_HIGHLIGHT_BORDER: u32 = 0xffffff80;
fn main_pane_highlight_border() -> u32 {
colors::pick(0xffffff80, 0x1f1d1b80)
}
/// Lower clamp for the live sidebar-resize drag. Stays a touch above
/// the per-space DEFAULT so a casual flick doesn't snap the user to a
+4 -2
View File
@@ -45,7 +45,7 @@ impl ElyShell {
.rounded(px(spacing::RADIUS_CARD))
.bg(rgba(panel_color))
.border_1()
.border_color(rgba(HIGHLIGHT_BORDER))
.border_color(rgba(highlight_border()))
.shadow(panel_shadow())
.children(snapshot.favorites.iter().enumerate().map(|(index, tab)| {
self.render_compact_tab_button(
@@ -276,7 +276,9 @@ pub(super) fn collapsed_sidebar_active(sidebar_width: f32) -> bool {
/// Painting it as the panel's own border keeps it part of the frame
/// (no separate absolute overlay) so it never participates in hit
/// testing and never blocks clicks on inner content.
const HIGHLIGHT_BORDER: u32 = 0xffffff80;
fn highlight_border() -> u32 {
colors::pick(0xffffff80, 0x1f1d1b80)
}
fn panel_shadow() -> Vec<BoxShadow> {
vec![