scale.rs

  1use gpui::{AppContext, Hsla, SharedString};
  2
  3use crate::{ActiveTheme, Appearance};
  4
  5/// A one-based step in a [`ColorScale`].
  6#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
  7pub struct ColorScaleStep(usize);
  8
  9impl ColorScaleStep {
 10    pub const ONE: Self = Self(1);
 11    pub const TWO: Self = Self(2);
 12    pub const THREE: Self = Self(3);
 13    pub const FOUR: Self = Self(4);
 14    pub const FIVE: Self = Self(5);
 15    pub const SIX: Self = Self(6);
 16    pub const SEVEN: Self = Self(7);
 17    pub const EIGHT: Self = Self(8);
 18    pub const NINE: Self = Self(9);
 19    pub const TEN: Self = Self(10);
 20    pub const ELEVEN: Self = Self(11);
 21    pub const TWELVE: Self = Self(12);
 22
 23    /// All of the steps in a [`ColorScale`].
 24    pub const ALL: [ColorScaleStep; 12] = [
 25        Self::ONE,
 26        Self::TWO,
 27        Self::THREE,
 28        Self::FOUR,
 29        Self::FIVE,
 30        Self::SIX,
 31        Self::SEVEN,
 32        Self::EIGHT,
 33        Self::NINE,
 34        Self::TEN,
 35        Self::ELEVEN,
 36        Self::TWELVE,
 37    ];
 38}
 39
 40pub struct ColorScale(Vec<Hsla>);
 41
 42impl FromIterator<Hsla> for ColorScale {
 43    fn from_iter<T: IntoIterator<Item = Hsla>>(iter: T) -> Self {
 44        Self(Vec::from_iter(iter))
 45    }
 46}
 47
 48impl ColorScale {
 49    /// Returns the specified step in the [`ColorScale`].
 50    #[inline]
 51    pub fn step(&self, step: ColorScaleStep) -> Hsla {
 52        // Steps are one-based, so we need convert to the zero-based vec index.
 53        self.0[step.0 - 1]
 54    }
 55
 56    /// `Step 1` - Used for main application backgrounds.
 57    ///
 58    /// This step provides a neutral base for any overlaying components, ideal for applications' main backdrop or empty spaces such as canvas areas.
 59    ///
 60    #[inline]
 61    pub fn step_1(&self) -> Hsla {
 62        self.step(ColorScaleStep::ONE)
 63    }
 64
 65    /// `Step 2` - Used for both main application backgrounds and subtle component backgrounds.
 66    ///
 67    /// Like `Step 1`, this step allows variations in background styles, from striped tables, sidebar backgrounds, to card backgrounds.
 68    #[inline]
 69    pub fn step_2(&self) -> Hsla {
 70        self.step(ColorScaleStep::TWO)
 71    }
 72
 73    /// `Step 3` - Used for UI component backgrounds in their normal states.
 74    ///
 75    /// 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.
 76    #[inline]
 77    pub fn step_3(&self) -> Hsla {
 78        self.step(ColorScaleStep::THREE)
 79    }
 80
 81    /// `Step 4` - Used for UI component backgrounds in their hover states.
 82    ///
 83    /// Also suited for pressed or selected states of components with a transparent background.
 84    #[inline]
 85    pub fn step_4(&self) -> Hsla {
 86        self.step(ColorScaleStep::FOUR)
 87    }
 88
 89    /// `Step 5` - Used for UI component backgrounds in their pressed or selected states.
 90    #[inline]
 91    pub fn step_5(&self) -> Hsla {
 92        self.step(ColorScaleStep::FIVE)
 93    }
 94
 95    /// `Step 6` - Used for subtle borders on non-interactive components.
 96    ///
 97    /// Its usage spans from sidebars' borders, headers' dividers, cards' outlines, to alerts' edges and separators.
 98    #[inline]
 99    pub fn step_6(&self) -> Hsla {
100        self.step(ColorScaleStep::SIX)
101    }
102
103    /// `Step 7` - Used for subtle borders on interactive components.
104    ///
105    /// This step subtly delineates the boundary of elements users interact with.
106    #[inline]
107    pub fn step_7(&self) -> Hsla {
108        self.step(ColorScaleStep::SEVEN)
109    }
110
111    /// `Step 8` - Used for stronger borders on interactive components and focus rings.
112    ///
113    /// It strengthens the visibility and accessibility of active elements and their focus states.
114    #[inline]
115    pub fn step_8(&self) -> Hsla {
116        self.step(ColorScaleStep::EIGHT)
117    }
118
119    /// `Step 9` - Used for solid backgrounds.
120    ///
121    /// `Step 9` is the most saturated step, having the least mix of white or black.
122    ///
123    /// Due to its high chroma, `Step 9` is versatile and particularly useful for semantic colors such as
124    /// error, warning, and success indicators.
125    #[inline]
126    pub fn step_9(&self) -> Hsla {
127        self.step(ColorScaleStep::NINE)
128    }
129
130    /// `Step 10` - Used for hovered or active solid backgrounds, particularly when `Step 9` is their normal state.
131    ///
132    /// May also be used for extremely low contrast text. This should be used sparingly, as it may be difficult to read.
133    #[inline]
134    pub fn step_10(&self) -> Hsla {
135        self.step(ColorScaleStep::TEN)
136    }
137
138    /// `Step 11` - Used for text and icons requiring low contrast or less emphasis.
139    #[inline]
140    pub fn step_11(&self) -> Hsla {
141        self.step(ColorScaleStep::ELEVEN)
142    }
143
144    /// `Step 12` - Used for text and icons requiring high contrast or prominence.
145    #[inline]
146    pub fn step_12(&self) -> Hsla {
147        self.step(ColorScaleStep::TWELVE)
148    }
149}
150
151pub struct ColorScales {
152    pub gray: ColorScaleSet,
153    pub mauve: ColorScaleSet,
154    pub slate: ColorScaleSet,
155    pub sage: ColorScaleSet,
156    pub olive: ColorScaleSet,
157    pub sand: ColorScaleSet,
158    pub gold: ColorScaleSet,
159    pub bronze: ColorScaleSet,
160    pub brown: ColorScaleSet,
161    pub yellow: ColorScaleSet,
162    pub amber: ColorScaleSet,
163    pub orange: ColorScaleSet,
164    pub tomato: ColorScaleSet,
165    pub red: ColorScaleSet,
166    pub ruby: ColorScaleSet,
167    pub crimson: ColorScaleSet,
168    pub pink: ColorScaleSet,
169    pub plum: ColorScaleSet,
170    pub purple: ColorScaleSet,
171    pub violet: ColorScaleSet,
172    pub iris: ColorScaleSet,
173    pub indigo: ColorScaleSet,
174    pub blue: ColorScaleSet,
175    pub cyan: ColorScaleSet,
176    pub teal: ColorScaleSet,
177    pub jade: ColorScaleSet,
178    pub green: ColorScaleSet,
179    pub grass: ColorScaleSet,
180    pub lime: ColorScaleSet,
181    pub mint: ColorScaleSet,
182    pub sky: ColorScaleSet,
183    pub black: ColorScaleSet,
184    pub white: ColorScaleSet,
185}
186
187impl IntoIterator for ColorScales {
188    type Item = ColorScaleSet;
189
190    type IntoIter = std::vec::IntoIter<Self::Item>;
191
192    fn into_iter(self) -> Self::IntoIter {
193        vec![
194            self.gray,
195            self.mauve,
196            self.slate,
197            self.sage,
198            self.olive,
199            self.sand,
200            self.gold,
201            self.bronze,
202            self.brown,
203            self.yellow,
204            self.amber,
205            self.orange,
206            self.tomato,
207            self.red,
208            self.ruby,
209            self.crimson,
210            self.pink,
211            self.plum,
212            self.purple,
213            self.violet,
214            self.iris,
215            self.indigo,
216            self.blue,
217            self.cyan,
218            self.teal,
219            self.jade,
220            self.green,
221            self.grass,
222            self.lime,
223            self.mint,
224            self.sky,
225            self.black,
226            self.white,
227        ]
228        .into_iter()
229    }
230}
231
232pub struct ColorScaleSet {
233    name: SharedString,
234    light: ColorScale,
235    dark: ColorScale,
236    light_alpha: ColorScale,
237    dark_alpha: ColorScale,
238}
239
240impl ColorScaleSet {
241    pub fn new(
242        name: impl Into<SharedString>,
243        light: ColorScale,
244        light_alpha: ColorScale,
245        dark: ColorScale,
246        dark_alpha: ColorScale,
247    ) -> Self {
248        Self {
249            name: name.into(),
250            light,
251            light_alpha,
252            dark,
253            dark_alpha,
254        }
255    }
256
257    pub fn name(&self) -> &SharedString {
258        &self.name
259    }
260
261    pub fn light(&self) -> &ColorScale {
262        &self.light
263    }
264
265    pub fn light_alpha(&self) -> &ColorScale {
266        &self.light_alpha
267    }
268
269    pub fn dark(&self) -> &ColorScale {
270        &self.dark
271    }
272
273    pub fn dark_alpha(&self) -> &ColorScale {
274        &self.dark_alpha
275    }
276
277    pub fn step(&self, cx: &AppContext, step: ColorScaleStep) -> Hsla {
278        match cx.theme().appearance {
279            Appearance::Light => self.light().step(step),
280            Appearance::Dark => self.dark().step(step),
281        }
282    }
283
284    pub fn step_alpha(&self, cx: &AppContext, step: ColorScaleStep) -> Hsla {
285        match cx.theme().appearance {
286            Appearance::Light => self.light_alpha.step(step),
287            Appearance::Dark => self.dark_alpha.step(step),
288        }
289    }
290}