scale.rs

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