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 fn name(&self) -> &SharedString {
266 &self.name
267 }
268
269 pub fn light(&self) -> &ColorScale {
270 &self.light
271 }
272
273 pub fn light_alpha(&self) -> &ColorScale {
274 &self.light_alpha
275 }
276
277 pub fn dark(&self) -> &ColorScale {
278 &self.dark
279 }
280
281 pub 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}