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}