1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
use gpui::{AppContext, Hsla, SharedString};

use crate::{ActiveTheme, Appearance};

/// A one-based step in a [`ColorScale`].
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub struct ColorScaleStep(usize);

impl ColorScaleStep {
    pub const ONE: Self = Self(1);
    pub const TWO: Self = Self(2);
    pub const THREE: Self = Self(3);
    pub const FOUR: Self = Self(4);
    pub const FIVE: Self = Self(5);
    pub const SIX: Self = Self(6);
    pub const SEVEN: Self = Self(7);
    pub const EIGHT: Self = Self(8);
    pub const NINE: Self = Self(9);
    pub const TEN: Self = Self(10);
    pub const ELEVEN: Self = Self(11);
    pub const TWELVE: Self = Self(12);

    /// All of the steps in a [`ColorScale`].
    pub const ALL: [ColorScaleStep; 12] = [
        Self::ONE,
        Self::TWO,
        Self::THREE,
        Self::FOUR,
        Self::FIVE,
        Self::SIX,
        Self::SEVEN,
        Self::EIGHT,
        Self::NINE,
        Self::TEN,
        Self::ELEVEN,
        Self::TWELVE,
    ];
}

pub struct ColorScale(Vec<Hsla>);

impl FromIterator<Hsla> for ColorScale {
    fn from_iter<T: IntoIterator<Item = Hsla>>(iter: T) -> Self {
        Self(Vec::from_iter(iter))
    }
}

impl ColorScale {
    /// Returns the specified step in the [`ColorScale`].
    #[inline]
    pub fn step(&self, step: ColorScaleStep) -> Hsla {
        // Steps are one-based, so we need convert to the zero-based vec index.
        self.0[step.0 - 1]
    }

    /// `Step 1` - Used for main application backgrounds.
    ///
    /// This step provides a neutral base for any overlaying components, ideal for applications' main backdrop or empty spaces such as canvas areas.
    ///
    #[inline]
    pub fn step_1(&self) -> Hsla {
        self.step(ColorScaleStep::ONE)
    }

    /// `Step 2` - Used for both main application backgrounds and subtle component backgrounds.
    ///
    /// Like `Step 1`, this step allows variations in background styles, from striped tables, sidebar backgrounds, to card backgrounds.
    #[inline]
    pub fn step_2(&self) -> Hsla {
        self.step(ColorScaleStep::TWO)
    }

    /// `Step 3` - Used for UI component backgrounds in their normal states.
    ///
    /// 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.
    #[inline]
    pub fn step_3(&self) -> Hsla {
        self.step(ColorScaleStep::THREE)
    }

    /// `Step 4` - Used for UI component backgrounds in their hover states.
    ///
    /// Also suited for pressed or selected states of components with a transparent background.
    #[inline]
    pub fn step_4(&self) -> Hsla {
        self.step(ColorScaleStep::FOUR)
    }

    /// `Step 5` - Used for UI component backgrounds in their pressed or selected states.
    #[inline]
    pub fn step_5(&self) -> Hsla {
        self.step(ColorScaleStep::FIVE)
    }

    /// `Step 6` - Used for subtle borders on non-interactive components.
    ///
    /// Its usage spans from sidebars' borders, headers' dividers, cards' outlines, to alerts' edges and separators.
    #[inline]
    pub fn step_6(&self) -> Hsla {
        self.step(ColorScaleStep::SIX)
    }

    /// `Step 7` - Used for subtle borders on interactive components.
    ///
    /// This step subtly delineates the boundary of elements users interact with.
    #[inline]
    pub fn step_7(&self) -> Hsla {
        self.step(ColorScaleStep::SEVEN)
    }

    /// `Step 8` - Used for stronger borders on interactive components and focus rings.
    ///
    /// It strengthens the visibility and accessibility of active elements and their focus states.
    #[inline]
    pub fn step_8(&self) -> Hsla {
        self.step(ColorScaleStep::EIGHT)
    }

    /// `Step 9` - Used for solid backgrounds.
    ///
    /// `Step 9` is the most saturated step, having the least mix of white or black.
    ///
    /// Due to its high chroma, `Step 9` is versatile and particularly useful for semantic colors such as
    /// error, warning, and success indicators.
    #[inline]
    pub fn step_9(&self) -> Hsla {
        self.step(ColorScaleStep::NINE)
    }

    /// `Step 10` - Used for hovered or active solid backgrounds, particularly when `Step 9` is their normal state.
    ///
    /// May also be used for extremely low contrast text. This should be used sparingly, as it may be difficult to read.
    #[inline]
    pub fn step_10(&self) -> Hsla {
        self.step(ColorScaleStep::TEN)
    }

    /// `Step 11` - Used for text and icons requiring low contrast or less emphasis.
    #[inline]
    pub fn step_11(&self) -> Hsla {
        self.step(ColorScaleStep::ELEVEN)
    }

    /// `Step 12` - Used for text and icons requiring high contrast or prominence.
    #[inline]
    pub fn step_12(&self) -> Hsla {
        self.step(ColorScaleStep::TWELVE)
    }
}

pub struct ColorScales {
    pub gray: ColorScaleSet,
    pub mauve: ColorScaleSet,
    pub slate: ColorScaleSet,
    pub sage: ColorScaleSet,
    pub olive: ColorScaleSet,
    pub sand: ColorScaleSet,
    pub gold: ColorScaleSet,
    pub bronze: ColorScaleSet,
    pub brown: ColorScaleSet,
    pub yellow: ColorScaleSet,
    pub amber: ColorScaleSet,
    pub orange: ColorScaleSet,
    pub tomato: ColorScaleSet,
    pub red: ColorScaleSet,
    pub ruby: ColorScaleSet,
    pub crimson: ColorScaleSet,
    pub pink: ColorScaleSet,
    pub plum: ColorScaleSet,
    pub purple: ColorScaleSet,
    pub violet: ColorScaleSet,
    pub iris: ColorScaleSet,
    pub indigo: ColorScaleSet,
    pub blue: ColorScaleSet,
    pub cyan: ColorScaleSet,
    pub teal: ColorScaleSet,
    pub jade: ColorScaleSet,
    pub green: ColorScaleSet,
    pub grass: ColorScaleSet,
    pub lime: ColorScaleSet,
    pub mint: ColorScaleSet,
    pub sky: ColorScaleSet,
    pub black: ColorScaleSet,
    pub white: ColorScaleSet,
}

impl IntoIterator for ColorScales {
    type Item = ColorScaleSet;

    type IntoIter = std::vec::IntoIter<Self::Item>;

    fn into_iter(self) -> Self::IntoIter {
        vec![
            self.gray,
            self.mauve,
            self.slate,
            self.sage,
            self.olive,
            self.sand,
            self.gold,
            self.bronze,
            self.brown,
            self.yellow,
            self.amber,
            self.orange,
            self.tomato,
            self.red,
            self.ruby,
            self.crimson,
            self.pink,
            self.plum,
            self.purple,
            self.violet,
            self.iris,
            self.indigo,
            self.blue,
            self.cyan,
            self.teal,
            self.jade,
            self.green,
            self.grass,
            self.lime,
            self.mint,
            self.sky,
            self.black,
            self.white,
        ]
        .into_iter()
    }
}

pub struct ColorScaleSet {
    name: SharedString,
    light: ColorScale,
    dark: ColorScale,
    light_alpha: ColorScale,
    dark_alpha: ColorScale,
}

impl ColorScaleSet {
    pub fn new(
        name: impl Into<SharedString>,
        light: ColorScale,
        light_alpha: ColorScale,
        dark: ColorScale,
        dark_alpha: ColorScale,
    ) -> Self {
        Self {
            name: name.into(),
            light,
            light_alpha,
            dark,
            dark_alpha,
        }
    }

    pub fn name(&self) -> &SharedString {
        &self.name
    }

    pub fn light(&self) -> &ColorScale {
        &self.light
    }

    pub fn light_alpha(&self) -> &ColorScale {
        &self.light_alpha
    }

    pub fn dark(&self) -> &ColorScale {
        &self.dark
    }

    pub fn dark_alpha(&self) -> &ColorScale {
        &self.dark_alpha
    }

    pub fn step(&self, cx: &AppContext, step: ColorScaleStep) -> Hsla {
        match cx.theme().appearance {
            Appearance::Light => self.light().step(step),
            Appearance::Dark => self.dark().step(step),
        }
    }

    pub fn step_alpha(&self, cx: &AppContext, step: ColorScaleStep) -> Hsla {
        match cx.theme().appearance {
            Appearance::Light => self.light_alpha.step(step),
            Appearance::Dark => self.dark_alpha.step(step),
        }
    }
}