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