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}