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    #[inline]
132    pub fn step_10(&self) -> Hsla {
133        self.step(ColorScaleStep::TEN)
134    }
135
136    /// `Step 11` - Used for text and icons requiring low contrast or less emphasis.
137    #[inline]
138    pub fn step_11(&self) -> Hsla {
139        self.step(ColorScaleStep::ELEVEN)
140    }
141
142    /// `Step 12` - Used for text and icons requiring high contrast or prominence.
143    #[inline]
144    pub fn step_12(&self) -> Hsla {
145        self.step(ColorScaleStep::TWELVE)
146    }
147}
148
149pub struct ColorScales {
150    pub gray: ColorScaleSet,
151    pub mauve: ColorScaleSet,
152    pub slate: ColorScaleSet,
153    pub sage: ColorScaleSet,
154    pub olive: ColorScaleSet,
155    pub sand: ColorScaleSet,
156    pub gold: ColorScaleSet,
157    pub bronze: ColorScaleSet,
158    pub brown: ColorScaleSet,
159    pub yellow: ColorScaleSet,
160    pub amber: ColorScaleSet,
161    pub orange: ColorScaleSet,
162    pub tomato: ColorScaleSet,
163    pub red: ColorScaleSet,
164    pub ruby: ColorScaleSet,
165    pub crimson: ColorScaleSet,
166    pub pink: ColorScaleSet,
167    pub plum: ColorScaleSet,
168    pub purple: ColorScaleSet,
169    pub violet: ColorScaleSet,
170    pub iris: ColorScaleSet,
171    pub indigo: ColorScaleSet,
172    pub blue: ColorScaleSet,
173    pub cyan: ColorScaleSet,
174    pub teal: ColorScaleSet,
175    pub jade: ColorScaleSet,
176    pub green: ColorScaleSet,
177    pub grass: ColorScaleSet,
178    pub lime: ColorScaleSet,
179    pub mint: ColorScaleSet,
180    pub sky: ColorScaleSet,
181    pub black: ColorScaleSet,
182    pub white: ColorScaleSet,
183}
184
185impl IntoIterator for ColorScales {
186    type Item = ColorScaleSet;
187
188    type IntoIter = std::vec::IntoIter<Self::Item>;
189
190    fn into_iter(self) -> Self::IntoIter {
191        vec![
192            self.gray,
193            self.mauve,
194            self.slate,
195            self.sage,
196            self.olive,
197            self.sand,
198            self.gold,
199            self.bronze,
200            self.brown,
201            self.yellow,
202            self.amber,
203            self.orange,
204            self.tomato,
205            self.red,
206            self.ruby,
207            self.crimson,
208            self.pink,
209            self.plum,
210            self.purple,
211            self.violet,
212            self.iris,
213            self.indigo,
214            self.blue,
215            self.cyan,
216            self.teal,
217            self.jade,
218            self.green,
219            self.grass,
220            self.lime,
221            self.mint,
222            self.sky,
223            self.black,
224            self.white,
225        ]
226        .into_iter()
227    }
228}
229
230pub struct ColorScaleSet {
231    name: SharedString,
232    light: ColorScale,
233    dark: ColorScale,
234    light_alpha: ColorScale,
235    dark_alpha: ColorScale,
236}
237
238impl ColorScaleSet {
239    pub fn new(
240        name: impl Into<SharedString>,
241        light: ColorScale,
242        light_alpha: ColorScale,
243        dark: ColorScale,
244        dark_alpha: ColorScale,
245    ) -> Self {
246        Self {
247            name: name.into(),
248            light,
249            light_alpha,
250            dark,
251            dark_alpha,
252        }
253    }
254
255    pub fn name(&self) -> &SharedString {
256        &self.name
257    }
258
259    pub fn light(&self) -> &ColorScale {
260        &self.light
261    }
262
263    pub fn light_alpha(&self) -> &ColorScale {
264        &self.light_alpha
265    }
266
267    pub fn dark(&self) -> &ColorScale {
268        &self.dark
269    }
270
271    pub fn dark_alpha(&self) -> &ColorScale {
272        &self.dark_alpha
273    }
274
275    pub fn step(&self, cx: &AppContext, step: ColorScaleStep) -> Hsla {
276        match cx.theme().appearance {
277            Appearance::Light => self.light().step(step),
278            Appearance::Dark => self.dark().step(step),
279        }
280    }
281
282    pub fn step_alpha(&self, cx: &AppContext, step: ColorScaleStep) -> Hsla {
283        match cx.theme().appearance {
284            Appearance::Light => self.light_alpha.step(step),
285            Appearance::Dark => self.dark_alpha.step(step),
286        }
287    }
288}