wip: refactor

This commit is contained in:
reya
2025-01-13 13:58:24 +07:00
parent 8ab48cb12a
commit 8be41c9bfa
34 changed files with 2799 additions and 2531 deletions
File diff suppressed because it is too large Load Diff
+444
View File
@@ -0,0 +1,444 @@
use crate::scroll::ScrollbarShow;
use colors::hsl;
use gpui::{
blue, hsla, transparent_black, AppContext, Global, Hsla, ModelContext, SharedString,
ViewContext, WindowAppearance, WindowContext,
};
use std::ops::{Deref, DerefMut};
pub mod colors;
pub mod scale;
#[derive(Debug, Clone, Copy, Default)]
pub struct ThemeColors {
pub border: Hsla,
pub window_border: Hsla,
pub accent: Hsla,
pub accent_foreground: Hsla,
pub background: Hsla,
pub card: Hsla,
pub card_foreground: Hsla,
pub danger: Hsla,
pub danger_active: Hsla,
pub danger_foreground: Hsla,
pub danger_hover: Hsla,
pub drag_border: Hsla,
pub drop_target: Hsla,
pub foreground: Hsla,
pub input: Hsla,
pub link: Hsla,
pub link_active: Hsla,
pub link_hover: Hsla,
pub list: Hsla,
pub list_active: Hsla,
pub list_active_border: Hsla,
pub list_even: Hsla,
pub list_head: Hsla,
pub list_hover: Hsla,
pub muted: Hsla,
pub muted_foreground: Hsla,
pub popover: Hsla,
pub popover_foreground: Hsla,
pub primary: Hsla,
pub primary_active: Hsla,
pub primary_foreground: Hsla,
pub primary_hover: Hsla,
pub progress_bar: Hsla,
pub ring: Hsla,
pub scrollbar: Hsla,
pub scrollbar_thumb: Hsla,
pub scrollbar_thumb_hover: Hsla,
pub secondary: Hsla,
pub secondary_active: Hsla,
pub secondary_foreground: Hsla,
pub secondary_hover: Hsla,
pub selection: Hsla,
pub skeleton: Hsla,
pub slider_bar: Hsla,
pub slider_thumb: Hsla,
pub tab: Hsla,
pub tab_active: Hsla,
pub tab_active_foreground: Hsla,
pub tab_bar: Hsla,
pub tab_foreground: Hsla,
pub title_bar: Hsla,
pub title_bar_border: Hsla,
}
impl ThemeColors {
pub fn light() -> Self {
Self {
accent: hsl(240.0, 5.0, 96.0),
accent_foreground: hsl(240.0, 5.9, 10.0),
background: hsl(0.0, 0.0, 100.),
border: hsl(240.0, 5.9, 90.0),
window_border: hsl(240.0, 5.9, 78.0),
card: hsl(0.0, 0.0, 100.0),
card_foreground: hsl(240.0, 10.0, 3.9),
danger: hsl(0.0, 84.2, 60.2),
danger_active: hsl(0.0, 84.2, 47.0),
danger_foreground: hsl(0.0, 0.0, 98.0),
danger_hover: hsl(0.0, 84.2, 65.0),
drag_border: blue(),
drop_target: hsl(235.0, 30., 44.0).opacity(0.25),
foreground: hsl(240.0, 10., 3.9),
input: hsl(240.0, 5.9, 90.0),
link: hsl(221.0, 83.0, 53.0),
link_active: hsl(221.0, 83.0, 53.0).darken(0.2),
link_hover: hsl(221.0, 83.0, 53.0).lighten(0.2),
list: hsl(0.0, 0.0, 100.),
list_active: hsl(211.0, 97.0, 85.0).opacity(0.2),
list_active_border: hsl(211.0, 97.0, 85.0),
list_even: hsl(240.0, 5.0, 96.0),
list_head: hsl(0.0, 0.0, 100.),
list_hover: hsl(240.0, 4.8, 95.0),
muted: hsl(240.0, 4.8, 95.9),
muted_foreground: hsl(240.0, 3.8, 46.1),
popover: hsl(0.0, 0.0, 100.0),
popover_foreground: hsl(240.0, 10.0, 3.9),
primary: hsl(223.0, 5.9, 10.0),
primary_active: hsl(223.0, 1.9, 25.0),
primary_foreground: hsl(223.0, 0.0, 98.0),
primary_hover: hsl(223.0, 5.9, 15.0),
progress_bar: hsl(223.0, 5.9, 10.0),
ring: hsl(240.0, 5.9, 65.0),
scrollbar: hsl(0., 0., 97.).opacity(0.75),
scrollbar_thumb: hsl(0., 0., 69.).opacity(0.9),
scrollbar_thumb_hover: hsl(0., 0., 59.),
secondary: hsl(240.0, 5.9, 96.9),
secondary_active: hsl(240.0, 5.9, 90.),
secondary_foreground: hsl(240.0, 59.0, 10.),
secondary_hover: hsl(240.0, 5.9, 98.),
selection: hsl(211.0, 97.0, 85.0),
skeleton: hsl(223.0, 5.9, 10.0).opacity(0.1),
slider_bar: hsl(223.0, 5.9, 10.0),
slider_thumb: hsl(0.0, 0.0, 100.0),
tab: transparent_black(),
tab_active: hsl(0.0, 0.0, 100.0),
tab_active_foreground: hsl(240.0, 10., 3.9),
tab_bar: hsl(240.0, 4.8, 95.9),
tab_foreground: hsl(240.0, 10., 3.9),
title_bar: hsl(0.0, 0.0, 98.0),
title_bar_border: hsl(220.0, 13.0, 91.0),
}
}
pub fn dark() -> Self {
Self {
accent: hsl(240.0, 3.7, 15.9),
accent_foreground: hsl(0.0, 0.0, 78.0),
background: hsl(0.0, 0.0, 8.0),
border: hsl(240.0, 3.7, 16.9),
window_border: hsl(240.0, 3.7, 28.0),
card: hsl(0.0, 0.0, 8.0),
card_foreground: hsl(0.0, 0.0, 78.0),
danger: hsl(0.0, 62.8, 30.6),
danger_active: hsl(0.0, 62.8, 20.6),
danger_foreground: hsl(0.0, 0.0, 78.0),
danger_hover: hsl(0.0, 62.8, 35.6),
drag_border: blue(),
drop_target: hsl(235.0, 30., 44.0).opacity(0.1),
foreground: hsl(0., 0., 78.),
input: hsl(240.0, 3.7, 15.9),
link: hsl(221.0, 83.0, 53.0),
link_active: hsl(221.0, 83.0, 53.0).darken(0.2),
link_hover: hsl(221.0, 83.0, 53.0).lighten(0.2),
list: hsl(0.0, 0.0, 8.0),
list_active: hsl(240.0, 3.7, 15.0).opacity(0.2),
list_active_border: hsl(240.0, 5.9, 35.5),
list_even: hsl(240.0, 3.7, 10.0),
list_head: hsl(0.0, 0.0, 8.0),
list_hover: hsl(240.0, 3.7, 15.9),
muted: hsl(240.0, 3.7, 15.9),
muted_foreground: hsl(240.0, 5.0, 64.9),
popover: hsl(0.0, 0.0, 10.),
popover_foreground: hsl(0.0, 0.0, 78.0),
primary: hsl(223.0, 0.0, 98.0),
primary_active: hsl(223.0, 0.0, 80.0),
primary_foreground: hsl(223.0, 5.9, 10.0),
primary_hover: hsl(223.0, 0.0, 90.0),
progress_bar: hsl(223.0, 0.0, 98.0),
ring: hsl(240.0, 4.9, 83.9),
scrollbar: hsl(240., 1., 15.).opacity(0.75),
scrollbar_thumb: hsl(0., 0., 48.).opacity(0.9),
scrollbar_thumb_hover: hsl(0., 0., 68.),
secondary: hsl(240.0, 0., 13.0),
secondary_active: hsl(240.0, 0., 10.),
secondary_foreground: hsl(0.0, 0.0, 78.0),
secondary_hover: hsl(240.0, 0., 15.),
selection: hsl(211.0, 97.0, 22.0),
skeleton: hsla(223.0, 0.0, 98.0, 0.1),
slider_bar: hsl(223.0, 0.0, 98.0),
slider_thumb: hsl(0.0, 0.0, 8.0),
tab: transparent_black(),
tab_active: hsl(0.0, 0.0, 8.0),
tab_active_foreground: hsl(0., 0., 78.),
tab_bar: hsl(299.0, 0., 5.5),
tab_foreground: hsl(0., 0., 78.),
title_bar: hsl(240.0, 0.0, 10.0),
title_bar_border: hsl(240.0, 3.7, 15.9),
}
}
}
pub trait Colorize {
fn opacity(&self, opacity: f32) -> Hsla;
fn divide(&self, divisor: f32) -> Hsla;
fn invert(&self) -> Hsla;
fn invert_l(&self) -> Hsla;
fn lighten(&self, amount: f32) -> Hsla;
fn darken(&self, amount: f32) -> Hsla;
fn apply(&self, base_color: Hsla) -> Hsla;
}
impl Colorize for Hsla {
/// Returns a new color with the given opacity.
///
/// The opacity is a value between 0.0 and 1.0, where 0.0 is fully transparent and 1.0 is fully opaque.
fn opacity(&self, factor: f32) -> Hsla {
Hsla {
a: self.a * factor.clamp(0.0, 1.0),
..*self
}
}
/// Returns a new color with each channel divided by the given divisor.
///
/// The divisor in range of 0.0 .. 1.0
fn divide(&self, divisor: f32) -> Hsla {
Hsla {
a: divisor,
..*self
}
}
/// Return inverted color
fn invert(&self) -> Hsla {
Hsla {
h: (self.h + 1.8) % 3.6,
s: 1.0 - self.s,
l: 1.0 - self.l,
a: self.a,
}
}
/// Return inverted lightness
fn invert_l(&self) -> Hsla {
Hsla {
l: 1.0 - self.l,
..*self
}
}
/// Return a new color with the lightness increased by the given factor.
///
/// factor range: 0.0 .. 1.0
fn lighten(&self, factor: f32) -> Hsla {
let l = self.l * (1.0 + factor.clamp(0.0, 1.0));
Hsla { l, ..*self }
}
/// Return a new color with the darkness increased by the given factor.
///
/// factor range: 0.0 .. 1.0
fn darken(&self, factor: f32) -> Hsla {
let l = self.l * (1.0 - factor.clamp(0.0, 1.0));
Hsla { l, ..*self }
}
/// Return a new color with the same lightness and alpha but different hue and saturation.
fn apply(&self, new_color: Hsla) -> Hsla {
Hsla {
h: new_color.h,
s: new_color.s,
l: self.l,
a: self.a,
}
}
}
pub trait ActiveTheme {
fn theme(&self) -> &Theme;
}
impl ActiveTheme for AppContext {
fn theme(&self) -> &Theme {
Theme::global(self)
}
}
impl<V> ActiveTheme for ViewContext<'_, V> {
fn theme(&self) -> &Theme {
self.deref().theme()
}
}
impl<V> ActiveTheme for ModelContext<'_, V> {
fn theme(&self) -> &Theme {
self.deref().theme()
}
}
impl ActiveTheme for WindowContext<'_> {
fn theme(&self) -> &Theme {
self.deref().theme()
}
}
pub fn init(cx: &mut AppContext) {
Theme::sync_system_appearance(cx)
}
#[derive(Debug, Clone)]
pub struct Theme {
pub colors: ThemeColors,
pub mode: ThemeMode,
pub font_family: SharedString,
pub font_size: f32,
pub radius: f32,
pub shadow: bool,
pub transparent: Hsla,
/// Show the scrollbar mode, default: Scrolling
pub scrollbar_show: ScrollbarShow,
}
impl Deref for Theme {
type Target = ThemeColors;
fn deref(&self) -> &Self::Target {
&self.colors
}
}
impl DerefMut for Theme {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.colors
}
}
impl Global for Theme {}
impl Theme {
/// Returns the global theme reference
pub fn global(cx: &AppContext) -> &Theme {
cx.global::<Theme>()
}
/// Returns the global theme mutable reference
pub fn global_mut(cx: &mut AppContext) -> &mut Theme {
cx.global_mut::<Theme>()
}
/// Apply a mask color to the theme.
pub fn apply_color(&mut self, mask_color: Hsla) {
self.title_bar = self.title_bar.apply(mask_color);
self.title_bar_border = self.title_bar_border.apply(mask_color);
self.background = self.background.apply(mask_color);
self.foreground = self.foreground.apply(mask_color);
self.card = self.card.apply(mask_color);
self.card_foreground = self.card_foreground.apply(mask_color);
self.popover = self.popover.apply(mask_color);
self.popover_foreground = self.popover_foreground.apply(mask_color);
self.primary = self.primary.apply(mask_color);
self.primary_hover = self.primary_hover.apply(mask_color);
self.primary_active = self.primary_active.apply(mask_color);
self.primary_foreground = self.primary_foreground.apply(mask_color);
self.secondary = self.secondary.apply(mask_color);
self.secondary_hover = self.secondary_hover.apply(mask_color);
self.secondary_active = self.secondary_active.apply(mask_color);
self.secondary_foreground = self.secondary_foreground.apply(mask_color);
self.muted = self.muted.apply(mask_color);
self.muted_foreground = self.muted_foreground.apply(mask_color);
self.accent = self.accent.apply(mask_color);
self.accent_foreground = self.accent_foreground.apply(mask_color);
self.border = self.border.apply(mask_color);
self.input = self.input.apply(mask_color);
self.ring = self.ring.apply(mask_color);
self.scrollbar = self.scrollbar.apply(mask_color);
self.scrollbar_thumb = self.scrollbar_thumb.apply(mask_color);
self.scrollbar_thumb_hover = self.scrollbar_thumb_hover.apply(mask_color);
self.drag_border = self.drag_border.apply(mask_color);
self.drop_target = self.drop_target.apply(mask_color);
self.tab_bar = self.tab_bar.apply(mask_color);
self.tab = self.tab.apply(mask_color);
self.tab_active = self.tab_active.apply(mask_color);
self.tab_foreground = self.tab_foreground.apply(mask_color);
self.tab_active_foreground = self.tab_active_foreground.apply(mask_color);
self.progress_bar = self.progress_bar.apply(mask_color);
self.slider_bar = self.slider_bar.apply(mask_color);
self.slider_thumb = self.slider_thumb.apply(mask_color);
self.list = self.list.apply(mask_color);
self.list_even = self.list_even.apply(mask_color);
self.list_head = self.list_head.apply(mask_color);
self.list_active = self.list_active.apply(mask_color);
self.list_active_border = self.list_active_border.apply(mask_color);
self.list_hover = self.list_hover.apply(mask_color);
self.link = self.link.apply(mask_color);
self.link_hover = self.link_hover.apply(mask_color);
self.link_active = self.link_active.apply(mask_color);
self.skeleton = self.skeleton.apply(mask_color);
self.title_bar = self.title_bar.apply(mask_color);
self.title_bar_border = self.title_bar_border.apply(mask_color);
}
/// Sync the theme with the system appearance
pub fn sync_system_appearance(cx: &mut AppContext) {
match cx.window_appearance() {
WindowAppearance::Dark | WindowAppearance::VibrantDark => {
Self::change(ThemeMode::Dark, cx)
}
WindowAppearance::Light | WindowAppearance::VibrantLight => {
Self::change(ThemeMode::Light, cx)
}
}
}
pub fn change(mode: ThemeMode, cx: &mut AppContext) {
let colors = match mode {
ThemeMode::Light => ThemeColors::light(),
ThemeMode::Dark => ThemeColors::dark(),
};
let mut theme = Theme::from(colors);
theme.mode = mode;
cx.set_global(theme);
cx.refresh();
}
}
impl From<ThemeColors> for Theme {
fn from(colors: ThemeColors) -> Self {
Theme {
mode: ThemeMode::default(),
transparent: Hsla::transparent_black(),
font_size: 16.0,
font_family: if cfg!(target_os = "macos") {
".SystemUIFont".into()
} else if cfg!(target_os = "windows") {
"Segoe UI".into()
} else {
"FreeMono".into()
},
radius: 6.0,
shadow: false,
scrollbar_show: ScrollbarShow::default(),
colors,
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, PartialOrd, Eq)]
pub enum ThemeMode {
#[default]
Light,
Dark,
}
impl ThemeMode {
pub fn is_dark(&self) -> bool {
matches!(self, Self::Dark)
}
}
+295
View File
@@ -0,0 +1,295 @@
use crate::theme::{ActiveTheme, ThemeMode};
use gpui::{AppContext, Hsla, SharedString};
/// A collection of colors that are used to style the UI.
///
/// Each step has a semantic meaning, and is used to style different parts of the UI.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub struct ColorScaleStep(usize);
impl ColorScaleStep {
pub const ONE: Self = Self(1);
pub const TWO: Self = Self(2);
pub const THREE: Self = Self(3);
pub const FOUR: Self = Self(4);
pub const FIVE: Self = Self(5);
pub const SIX: Self = Self(6);
pub const SEVEN: Self = Self(7);
pub const EIGHT: Self = Self(8);
pub const NINE: Self = Self(9);
pub const TEN: Self = Self(10);
pub const ELEVEN: Self = Self(11);
pub const TWELVE: Self = Self(12);
/// All of the steps in a [`ColorScale`].
pub const ALL: [ColorScaleStep; 12] = [
Self::ONE,
Self::TWO,
Self::THREE,
Self::FOUR,
Self::FIVE,
Self::SIX,
Self::SEVEN,
Self::EIGHT,
Self::NINE,
Self::TEN,
Self::ELEVEN,
Self::TWELVE,
];
}
/// A scale of colors for a given [`ColorScaleSet`].
///
/// Each [`ColorScale`] contains exactly 12 colors. Refer to
/// [`ColorScaleStep`] for a reference of what each step is used for.
pub struct ColorScale(Vec<Hsla>);
impl FromIterator<Hsla> for ColorScale {
fn from_iter<T: IntoIterator<Item = Hsla>>(iter: T) -> Self {
Self(Vec::from_iter(iter))
}
}
impl ColorScale {
/// Returns the specified step in the [`ColorScale`].
#[inline]
pub fn step(&self, step: ColorScaleStep) -> Hsla {
// Steps are one-based, so we need convert to the zero-based vec index.
self.0[step.0 - 1]
}
/// `Step 1` - Used for main application backgrounds.
///
/// This step provides a neutral base for any overlaying components, ideal for applications' main backdrop or empty spaces such as canvas areas.
///
#[inline]
pub fn step_1(&self) -> Hsla {
self.step(ColorScaleStep::ONE)
}
/// `Step 2` - Used for both main application backgrounds and subtle component backgrounds.
///
/// Like `Step 1`, this step allows variations in background styles, from striped tables, sidebar backgrounds, to card backgrounds.
#[inline]
pub fn step_2(&self) -> Hsla {
self.step(ColorScaleStep::TWO)
}
/// `Step 3` - Used for UI component backgrounds in their normal states.
///
/// This step maintains accessibility by guaranteeing a contrast ratio of 4.5:1 with steps 11 and 12 for text. It could also suit hover states for transparent components.
#[inline]
pub fn step_3(&self) -> Hsla {
self.step(ColorScaleStep::THREE)
}
/// `Step 4` - Used for UI component backgrounds in their hover states.
///
/// Also suited for pressed or selected states of components with a transparent background.
#[inline]
pub fn step_4(&self) -> Hsla {
self.step(ColorScaleStep::FOUR)
}
/// `Step 5` - Used for UI component backgrounds in their pressed or selected states.
#[inline]
pub fn step_5(&self) -> Hsla {
self.step(ColorScaleStep::FIVE)
}
/// `Step 6` - Used for subtle borders on non-interactive components.
///
/// Its usage spans from sidebars' borders, headers' dividers, cards' outlines, to alerts' edges and separators.
#[inline]
pub fn step_6(&self) -> Hsla {
self.step(ColorScaleStep::SIX)
}
/// `Step 7` - Used for subtle borders on interactive components.
///
/// This step subtly delineates the boundary of elements users interact with.
#[inline]
pub fn step_7(&self) -> Hsla {
self.step(ColorScaleStep::SEVEN)
}
/// `Step 8` - Used for stronger borders on interactive components and focus rings.
///
/// It strengthens the visibility and accessibility of active elements and their focus states.
#[inline]
pub fn step_8(&self) -> Hsla {
self.step(ColorScaleStep::EIGHT)
}
/// `Step 9` - Used for solid backgrounds.
///
/// `Step 9` is the most saturated step, having the least mix of white or black.
///
/// Due to its high chroma, `Step 9` is versatile and particularly useful for semantic colors such as
/// error, warning, and success indicators.
#[inline]
pub fn step_9(&self) -> Hsla {
self.step(ColorScaleStep::NINE)
}
/// `Step 10` - Used for hovered or active solid backgrounds, particularly when `Step 9` is their normal state.
///
/// May also be used for extremely low contrast text. This should be used sparingly, as it may be difficult to read.
#[inline]
pub fn step_10(&self) -> Hsla {
self.step(ColorScaleStep::TEN)
}
/// `Step 11` - Used for text and icons requiring low contrast or less emphasis.
#[inline]
pub fn step_11(&self) -> Hsla {
self.step(ColorScaleStep::ELEVEN)
}
/// `Step 12` - Used for text and icons requiring high contrast or prominence.
#[inline]
pub fn step_12(&self) -> Hsla {
self.step(ColorScaleStep::TWELVE)
}
}
pub struct ColorScales {
pub gray: ColorScaleSet,
pub mauve: ColorScaleSet,
pub slate: ColorScaleSet,
pub sage: ColorScaleSet,
pub olive: ColorScaleSet,
pub sand: ColorScaleSet,
pub gold: ColorScaleSet,
pub bronze: ColorScaleSet,
pub brown: ColorScaleSet,
pub yellow: ColorScaleSet,
pub amber: ColorScaleSet,
pub orange: ColorScaleSet,
pub tomato: ColorScaleSet,
pub red: ColorScaleSet,
pub ruby: ColorScaleSet,
pub crimson: ColorScaleSet,
pub pink: ColorScaleSet,
pub plum: ColorScaleSet,
pub purple: ColorScaleSet,
pub violet: ColorScaleSet,
pub iris: ColorScaleSet,
pub indigo: ColorScaleSet,
pub blue: ColorScaleSet,
pub cyan: ColorScaleSet,
pub teal: ColorScaleSet,
pub jade: ColorScaleSet,
pub green: ColorScaleSet,
pub grass: ColorScaleSet,
pub lime: ColorScaleSet,
pub mint: ColorScaleSet,
pub sky: ColorScaleSet,
pub black: ColorScaleSet,
pub white: ColorScaleSet,
}
impl IntoIterator for ColorScales {
type Item = ColorScaleSet;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
vec![
self.gray,
self.mauve,
self.slate,
self.sage,
self.olive,
self.sand,
self.gold,
self.bronze,
self.brown,
self.yellow,
self.amber,
self.orange,
self.tomato,
self.red,
self.ruby,
self.crimson,
self.pink,
self.plum,
self.purple,
self.violet,
self.iris,
self.indigo,
self.blue,
self.cyan,
self.teal,
self.jade,
self.green,
self.grass,
self.lime,
self.mint,
self.sky,
self.black,
self.white,
]
.into_iter()
}
}
/// Provides groups of [`ColorScale`]s for light and dark themes, as well as transparent versions of each scale.
pub struct ColorScaleSet {
name: SharedString,
light: ColorScale,
dark: ColorScale,
light_alpha: ColorScale,
dark_alpha: ColorScale,
}
impl ColorScaleSet {
pub fn new(
name: impl Into<SharedString>,
light: ColorScale,
light_alpha: ColorScale,
dark: ColorScale,
dark_alpha: ColorScale,
) -> Self {
Self {
name: name.into(),
light,
light_alpha,
dark,
dark_alpha,
}
}
pub fn name(&self) -> &SharedString {
&self.name
}
pub fn light(&self) -> &ColorScale {
&self.light
}
pub fn light_alpha(&self) -> &ColorScale {
&self.light_alpha
}
pub fn dark(&self) -> &ColorScale {
&self.dark
}
pub fn dark_alpha(&self) -> &ColorScale {
&self.dark_alpha
}
pub fn step(&self, cx: &AppContext, step: ColorScaleStep) -> Hsla {
match cx.theme().mode {
ThemeMode::Light => self.light().step(step),
ThemeMode::Dark => self.dark().step(step),
}
}
pub fn step_alpha(&self, cx: &AppContext, step: ColorScaleStep) -> Hsla {
match cx.theme().mode {
ThemeMode::Light => self.light_alpha.step(step),
ThemeMode::Dark => self.dark_alpha.step(step),
}
}
}