Skip to main content

egui_map/map/
theme.rs

1//! Color palettes and visual styling for the [`Map`](super::Map) widget.
2//!
3//! [`Theme`] is a built-in set of named color palettes, each with a light and
4//! a dark variant ([`ColorMode`]); [`Theme::colors`] resolves a `(Theme,
5//! ColorMode)` pair to the actual [`ThemeColors`] palette. [`MapTheme`] is
6//! the customization point: implement it to install your own palette instead
7//! of one of the built-ins, the same way [`NodeTemplate`](super::objects::NodeTemplate)
8//! and [`SegmentTemplate`](super::objects::SegmentTemplate) let you replace
9//! the built-in node/segment rendering. [`Style`] is the non-palette visual
10//! configuration (stroke widths, font, background) the widget paints with; it
11//! holds no color of its own -- every color comes live from the active
12//! [`MapTheme`], resolved fresh each frame, so nothing in `Style` can drift
13//! out of sync with the installed theme.
14
15use crate::map::theme::{
16    ColorMode::{Dark, Light},
17    Theme::*,
18};
19use egui::FontId;
20use egui::ecolor::Color32;
21use std::ops::{Div, Mul};
22
23/// A built-in, named color palette, in both a light and a dark variant.
24///
25/// Resolve it to actual colors with [`Theme::colors`]. Install one on a
26/// [`Map`](super::Map) as its active [`MapTheme`] with
27/// [`Map::set_theme`](super::Map::set_theme) -- `Theme` implements
28/// [`MapTheme`] directly, so any variant can be passed straight in.
29#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
30pub enum Theme {
31    /// Muted blues over slate grays.
32    SlateOcean,
33    /// Violet and teal on a soft neutral backdrop. The default theme.
34    #[default]
35    NebulaViolet,
36    /// Greens and blues reminiscent of a terminal color scheme.
37    TerminalGreen,
38    /// Warm oranges and pinks over charcoal/cream.
39    EmberForge,
40    /// Amber and teal on a warm neutral backdrop.
41    SolarAmber,
42    /// Cyan and gold over deep blue-gray.
43    ArticCyan,
44    /// Signal red and steel blue over near-black.
45    CrimsonSignal,
46    /// Indigo and teal on deep midnight blue.
47    MidnightIndigo,
48    /// Copper and teal over warm taupe.
49    CopperRose,
50    /// Lime green and violet over dark olive.
51    LimeCircuit,
52    /// Coral and ocean blue over sea-glass teal.
53    CoralReef,
54    /// Grayscale with a cool blue accent.
55    GraphiteMono,
56    /// Plum and jade over muted mauve.
57    PlumStatic,
58    /// Sand and clay over warm khaki.
59    SandstoneTrail,
60}
61
62impl Theme {
63    /// Resolves this theme to its actual [`ThemeColors`] for the given
64    /// [`ColorMode`].
65    pub const fn colors(self, mode: ColorMode) -> ThemeColors {
66        match (self, mode) {
67            (SlateOcean, Dark) => ThemeColors {
68                node: Color32::from_rgb(0x6E, 0x93, 0xBF),
69                segment: Color32::from_rgb(0x4A, 0x5A, 0x6E),
70                selected: Color32::from_rgb(0x38, 0xBD, 0xF8),
71                alert: Color32::from_rgb(0xFF, 0x8A, 0x4C),
72                text: Color32::from_rgb(0xD7, 0xDE, 0xE6),
73            },
74            (SlateOcean, Light) => ThemeColors {
75                node: Color32::from_rgb(0x2E, 0x4C, 0x6D),
76                segment: Color32::from_rgb(0x8A, 0x99, 0xA8),
77                selected: Color32::from_rgb(0x0E, 0xA5, 0xE9),
78                alert: Color32::from_rgb(0xE0, 0x59, 0x2A),
79                text: Color32::from_rgb(0x2B, 0x33, 0x3D),
80            },
81            (NebulaViolet, Light) => ThemeColors {
82                node: Color32::from_rgb(0x5B, 0x3E, 0x96),
83                segment: Color32::from_rgb(0xB3, 0xA4, 0xD6),
84                selected: Color32::from_rgb(0x16, 0xB8, 0xA6),
85                alert: Color32::from_rgb(0xE6, 0x33, 0x7A),
86                text: Color32::from_rgb(0x37, 0x2F, 0x45),
87            },
88            (NebulaViolet, Dark) => ThemeColors {
89                node: Color32::from_rgb(0xA7, 0x8B, 0xFA),
90                segment: Color32::from_rgb(0x5C, 0x4A, 0x80),
91                selected: Color32::from_rgb(0x2D, 0xD4, 0xBF),
92                alert: Color32::from_rgb(0xFF, 0x5F, 0xA3),
93                text: Color32::from_rgb(0xE4, 0xDC, 0xF2),
94            },
95            (TerminalGreen, Light) => ThemeColors {
96                node: Color32::from_rgb(0x1F, 0x7A, 0x3D),
97                segment: Color32::from_rgb(0x9B, 0xB8, 0x9E),
98                selected: Color32::from_rgb(0x25, 0x63, 0xEB),
99                alert: Color32::from_rgb(0xD6, 0xA4, 0x29),
100                text: Color32::from_rgb(0x2A, 0x33, 0x2C),
101            },
102            (TerminalGreen, Dark) => ThemeColors {
103                node: Color32::from_rgb(0x4A, 0xDE, 0x80),
104                segment: Color32::from_rgb(0x3A, 0x52, 0x40),
105                selected: Color32::from_rgb(0x60, 0xA5, 0xFA),
106                alert: Color32::from_rgb(0xFF, 0xC9, 0x4D),
107                text: Color32::from_rgb(0xD7, 0xE6, 0xDA),
108            },
109            (EmberForge, Light) => ThemeColors {
110                node: Color32::from_rgb(0x8C, 0x4A, 0x1F),
111                segment: Color32::from_rgb(0xC9, 0xA9, 0x8C),
112                selected: Color32::from_rgb(0xC4, 0x25, 0x8C),
113                alert: Color32::from_rgb(0x2E, 0x86, 0xAB),
114                text: Color32::from_rgb(0x36, 0x2E, 0x28),
115            },
116            (EmberForge, Dark) => ThemeColors {
117                node: Color32::from_rgb(0xD9, 0x7F, 0x3D),
118                segment: Color32::from_rgb(0x5A, 0x46, 0x32),
119                selected: Color32::from_rgb(0xF4, 0x72, 0xB6),
120                alert: Color32::from_rgb(0x4F, 0xC3, 0xE8),
121                text: Color32::from_rgb(0xED, 0xE0, 0xD3),
122            },
123            (SolarAmber, Dark) => ThemeColors {
124                node: Color32::from_rgb(0x8A, 0x6D, 0x1E),
125                segment: Color32::from_rgb(0xD8, 0xC4, 0x8F),
126                selected: Color32::from_rgb(0x2D, 0x6E, 0x5E),
127                alert: Color32::from_rgb(0xC1, 0x44, 0x2A),
128                text: Color32::from_rgb(0x36, 0x2E, 0x1C),
129            },
130            (SolarAmber, Light) => ThemeColors {
131                node: Color32::from_rgb(0xF0, 0xC2, 0x4C),
132                segment: Color32::from_rgb(0x5A, 0x4E, 0x2E),
133                selected: Color32::from_rgb(0x4F, 0xBF, 0x9E),
134                alert: Color32::from_rgb(0xE8, 0x65, 0x4A),
135                text: Color32::from_rgb(0xEF, 0xE4, 0xC4),
136            },
137            (ArticCyan, Dark) => ThemeColors {
138                node: Color32::from_rgb(0x12, 0x70, 0x8A),
139                segment: Color32::from_rgb(0x9A, 0xC6, 0xD1),
140                selected: Color32::from_rgb(0xF5, 0xA5, 0x24),
141                alert: Color32::from_rgb(0xE0, 0x52, 0x7A),
142                text: Color32::from_rgb(0x1F, 0x2E, 0x30),
143            },
144            (ArticCyan, Light) => ThemeColors {
145                node: Color32::from_rgb(0x4F, 0xE0, 0xFF),
146                segment: Color32::from_rgb(0x37, 0x5E, 0x68),
147                selected: Color32::from_rgb(0xFB, 0xBF, 0x24),
148                alert: Color32::from_rgb(0xFF, 0x6B, 0x95),
149                text: Color32::from_rgb(0xD3, 0xEA, 0xEF),
150            },
151            (CrimsonSignal, Dark) => ThemeColors {
152                node: Color32::from_rgb(0xE3, 0x5B, 0x6B),
153                segment: Color32::from_rgb(0x5C, 0x45, 0x48),
154                selected: Color32::from_rgb(0x58, 0xA6, 0xFF),
155                alert: Color32::from_rgb(0xFF, 0xC4, 0x59),
156                text: Color32::from_rgb(0xE8, 0xD6, 0xD8),
157            },
158            (CrimsonSignal, Light) => ThemeColors {
159                node: Color32::from_rgb(0x7A, 0x1F, 0x2B),
160                segment: Color32::from_rgb(0xB9, 0xA8, 0xA8),
161                selected: Color32::from_rgb(0x1F, 0x6F, 0xEB),
162                alert: Color32::from_rgb(0xE8, 0xA6, 0x28),
163                text: Color32::from_rgb(0x33, 0x26, 0x28),
164            },
165
166            (MidnightIndigo, Dark) => ThemeColors {
167                node: Color32::from_rgb(0x7B, 0x82, 0xE0),
168                segment: Color32::from_rgb(0x3A, 0x3D, 0x66),
169                selected: Color32::from_rgb(0x2D, 0xD4, 0xC8),
170                alert: Color32::from_rgb(0xFF, 0xD1, 0x66),
171                text: Color32::from_rgb(0xD8, 0xDA, 0xF0),
172            },
173            (MidnightIndigo, Light) => ThemeColors {
174                node: Color32::from_rgb(0x2B, 0x2F, 0x77),
175                segment: Color32::from_rgb(0xA6, 0xA9, 0xC9),
176                selected: Color32::from_rgb(0x00, 0xB8, 0xA9),
177                alert: Color32::from_rgb(0xE0, 0xA4, 0x00),
178                text: Color32::from_rgb(0x26, 0x29, 0x40),
179            },
180
181            (CopperRose, Dark) => ThemeColors {
182                node: Color32::from_rgb(0xE0, 0x8B, 0x6F),
183                segment: Color32::from_rgb(0x5E, 0x45, 0x3D),
184                selected: Color32::from_rgb(0x4F, 0xC3, 0xAE),
185                alert: Color32::from_rgb(0xFF, 0xC6, 0x5C),
186                text: Color32::from_rgb(0xEF, 0xDA, 0xD0),
187            },
188            (CopperRose, Light) => ThemeColors {
189                node: Color32::from_rgb(0x9C, 0x4A, 0x3C),
190                segment: Color32::from_rgb(0xD9, 0xB8, 0xAE),
191                selected: Color32::from_rgb(0x2F, 0x7A, 0x6B),
192                alert: Color32::from_rgb(0xE0, 0xA2, 0x3C),
193                text: Color32::from_rgb(0x3D, 0x2E, 0x29),
194            },
195
196            (LimeCircuit, Dark) => ThemeColors {
197                node: Color32::from_rgb(0xA8, 0xE0, 0x5F),
198                segment: Color32::from_rgb(0x44, 0x52, 0x30),
199                selected: Color32::from_rgb(0xA7, 0x8B, 0xFA),
200                alert: Color32::from_rgb(0xFF, 0x85, 0x52),
201                text: Color32::from_rgb(0xDC, 0xEA, 0xC0),
202            },
203            (LimeCircuit, Light) => ThemeColors {
204                node: Color32::from_rgb(0x4D, 0x7A, 0x1F),
205                segment: Color32::from_rgb(0xB9, 0xC7, 0x9A),
206                selected: Color32::from_rgb(0x7B, 0x3F, 0xE4),
207                alert: Color32::from_rgb(0xE8, 0x5D, 0x2E),
208                text: Color32::from_rgb(0x2E, 0x33, 0x20),
209            },
210
211            (CoralReef, Dark) => ThemeColors {
212                node: Color32::from_rgb(0xFF, 0x8B, 0x73),
213                segment: Color32::from_rgb(0x38, 0x65, 0x60),
214                selected: Color32::from_rgb(0x5C, 0xA8, 0xE0),
215                alert: Color32::from_rgb(0xFF, 0xC1, 0x5E),
216                text: Color32::from_rgb(0xD6, 0xED, 0xE8),
217            },
218            (CoralReef, Light) => ThemeColors {
219                node: Color32::from_rgb(0xD6, 0x5A, 0x45),
220                segment: Color32::from_rgb(0xA8, 0xD4, 0xCE),
221                selected: Color32::from_rgb(0x1D, 0x5C, 0x9E),
222                alert: Color32::from_rgb(0xF2, 0xA9, 0x3C),
223                text: Color32::from_rgb(0x33, 0x40, 0x3E),
224            },
225
226            (GraphiteMono, Dark) => ThemeColors {
227                node: Color32::from_rgb(0xD6, 0xD6, 0xD2),
228                segment: Color32::from_rgb(0x4A, 0x4A, 0x46),
229                selected: Color32::from_rgb(0x4F, 0xB3, 0xF5),
230                alert: Color32::from_rgb(0xFF, 0x6B, 0x5C),
231                text: Color32::from_rgb(0xE8, 0xE8, 0xE4),
232            },
233            (GraphiteMono, Light) => ThemeColors {
234                node: Color32::from_rgb(0x3A, 0x3A, 0x3A),
235                segment: Color32::from_rgb(0xB8, 0xB8, 0xB4),
236                selected: Color32::from_rgb(0x1F, 0x8F, 0xE0),
237                alert: Color32::from_rgb(0xE0, 0x48, 0x3A),
238                text: Color32::from_rgb(0x23, 0x23, 0x23),
239            },
240
241            (PlumStatic, Dark) => ThemeColors {
242                node: Color32::from_rgb(0xC9, 0x94, 0xBB),
243                segment: Color32::from_rgb(0x4A, 0x3A, 0x45),
244                selected: Color32::from_rgb(0x52, 0xC9, 0x9A),
245                alert: Color32::from_rgb(0xFF, 0xA0, 0x5C),
246                text: Color32::from_rgb(0xEB, 0xD9, 0xE5),
247            },
248            (PlumStatic, Light) => ThemeColors {
249                node: Color32::from_rgb(0x6B, 0x3B, 0x5E),
250                segment: Color32::from_rgb(0xC7, 0xAE, 0xC0),
251                selected: Color32::from_rgb(0x2E, 0x8B, 0x6E),
252                alert: Color32::from_rgb(0xE0, 0x79, 0x3D),
253                text: Color32::from_rgb(0x36, 0x2B, 0x33),
254            },
255
256            (SandstoneTrail, Dark) => ThemeColors {
257                node: Color32::from_rgb(0xC9, 0xAD, 0x72),
258                segment: Color32::from_rgb(0x4E, 0x45, 0x30),
259                selected: Color32::from_rgb(0x4F, 0xA8, 0xCC),
260                alert: Color32::from_rgb(0xF0, 0x70, 0x8A),
261                text: Color32::from_rgb(0xE6, 0xD9, 0xB8),
262            },
263            (SandstoneTrail, Light) => ThemeColors {
264                node: Color32::from_rgb(0x6B, 0x5A, 0x3A),
265                segment: Color32::from_rgb(0xDC, 0xCB, 0xA0),
266                selected: Color32::from_rgb(0x2A, 0x6E, 0x8C),
267                alert: Color32::from_rgb(0xD1, 0x49, 0x5B),
268                text: Color32::from_rgb(0x3A, 0x31, 0x21),
269            },
270        }
271    }
272}
273
274/// A resolved color palette: the actual colors a [`Theme`] (or any other
275/// [`MapTheme`] implementation) uses to paint the map under one
276/// [`ColorMode`].
277#[derive(Clone, Copy, Debug, PartialEq, Eq)]
278pub struct ThemeColors {
279    /// Color used to fill node shapes.
280    pub node: Color32,
281    /// Color used for connection lines between nodes.
282    pub segment: Color32,
283    /// Color used for the selection highlight over the nearest node.
284    pub selected: Color32,
285    /// Color used for notification and alert animations.
286    pub alert: Color32,
287    /// Color used for node names and map labels.
288    pub text: Color32,
289}
290
291/// Which of a theme's two color variants to use.
292///
293/// A re-export of [`egui::Theme`] under this crate's existing name -- egui
294/// already models exactly this dark/light distinction (down to a
295/// [`ColorMode::from_dark_mode`] conversion from `ui.visuals().dark_mode`),
296/// so a second, identical `Light`/`Dark` enum here would just be another
297/// copy of the same two variants to keep in sync. Not to be confused with
298/// this crate's own [`Theme`], a named color palette.
299pub use egui::Theme as ColorMode;
300
301/// Customization point for the color palette used to paint the map.
302///
303/// Implement this to install your own palette instead of one of the
304/// built-in [`Theme`] variants -- install it with
305/// [`Map::set_theme`](super::Map::set_theme). `Theme` itself implements
306/// `MapTheme` by delegating to [`Theme::colors`], so switching between a
307/// built-in palette and a custom one is just a different argument to
308/// `set_theme`.
309///
310/// ```
311/// use egui_map::map::theme::{ColorMode, MapTheme, ThemeColors};
312/// use egui::Color32;
313///
314/// struct HighContrast;
315///
316/// impl MapTheme for HighContrast {
317///     fn colors(&self, mode: ColorMode) -> ThemeColors {
318///         match mode {
319///             ColorMode::Light => ThemeColors {
320///                 node: Color32::BLACK,
321///                 segment: Color32::DARK_GRAY,
322///                 selected: Color32::RED,
323///                 alert: Color32::RED,
324///                 text: Color32::BLACK,
325///             },
326///             ColorMode::Dark => ThemeColors {
327///                 node: Color32::WHITE,
328///                 segment: Color32::LIGHT_GRAY,
329///                 selected: Color32::YELLOW,
330///                 alert: Color32::YELLOW,
331///                 text: Color32::WHITE,
332///             },
333///         }
334///     }
335/// }
336///
337/// let mut map = egui_map::map::Map::new();
338/// map.set_theme(std::rc::Rc::new(HighContrast));
339/// ```
340pub trait MapTheme {
341    /// Returns the color palette to use for the given [`ColorMode`].
342    fn colors(&self, mode: ColorMode) -> ThemeColors;
343}
344
345impl MapTheme for Theme {
346    fn colors(&self, mode: ColorMode) -> ThemeColors {
347        Theme::colors(*self, mode)
348    }
349}
350
351/// Visual style used to paint the map under a given theme.
352///
353/// Holds the non-palette visual configuration -- stroke width, font,
354/// background -- that combines with the active [`MapTheme`]'s
355/// [`ThemeColors`] when the widget paints. `Style` itself carries no color:
356/// every color the widget paints with (node fill, connection lines, alerts,
357/// selection, text) is resolved live from the installed [`MapTheme`] for the
358/// current [`ColorMode`] -- see [`Map::set_theme`](super::Map::set_theme) --
359/// instead of living here as a copy that would need to be kept in sync.
360///
361/// Multiplying or dividing a `Style` by a number scales
362/// [`line_width`](Self::line_width) and the font size; the widget uses this
363/// to scale the active style with the current zoom factor. Fields that are
364/// `None` are left untouched by those operators.
365#[derive(Clone, Debug)]
366pub struct Style {
367    /// Width of the connection lines between nodes; their color comes from
368    /// the active [`MapTheme`]'s [`ThemeColors::segment`]. `None` disables
369    /// the default stroke -- a
370    /// [`SegmentTemplate`](super::objects::SegmentTemplate) or a segment
371    /// effect installed through [`Map::segment`](super::Map::segment) still
372    /// runs either way.
373    pub line_width: Option<f32>,
374    /// Font used for map labels.
375    pub font: Option<FontId>,
376    /// Background color of the map canvas.
377    pub background_color: Color32,
378}
379
380impl Style {
381    /// Creates a fully transparent style with no line or font.
382    pub fn new() -> Self {
383        Style {
384            line_width: None,
385            font: None,
386            background_color: Color32::TRANSPARENT,
387        }
388    }
389}
390
391impl Default for Style {
392    fn default() -> Self {
393        Style::new()
394    }
395}
396
397impl Style {
398    /// Returns a copy with the stroke widths and font size scaled by `factor`.
399    /// Fields that are `None` are left untouched.
400    fn scaled(mut self, factor: f32) -> Self {
401        if let Some(width) = self.line_width.as_mut() {
402            *width *= factor;
403        }
404        if let Some(font) = self.font.as_mut() {
405            font.size *= factor;
406        }
407        self
408    }
409}
410
411impl Mul<i64> for Style {
412    type Output = Self;
413
414    fn mul(self, rhs: i64) -> Self::Output {
415        self.scaled(rhs as f32)
416    }
417}
418
419impl Mul<i32> for Style {
420    type Output = Self;
421
422    fn mul(self, rhs: i32) -> Self::Output {
423        self.scaled(rhs as f32)
424    }
425}
426
427impl Mul<f32> for Style {
428    type Output = Self;
429
430    fn mul(self, rhs: f32) -> Self::Output {
431        self.scaled(rhs)
432    }
433}
434
435impl Mul<f64> for Style {
436    type Output = Self;
437
438    fn mul(self, rhs: f64) -> Self::Output {
439        self.scaled(rhs as f32)
440    }
441}
442
443impl Div<i64> for Style {
444    type Output = Self;
445
446    fn div(self, rhs: i64) -> Self::Output {
447        self.scaled(1.0 / rhs as f32)
448    }
449}
450
451impl Div<i32> for Style {
452    type Output = Self;
453
454    fn div(self, rhs: i32) -> Self::Output {
455        self.scaled(1.0 / rhs as f32)
456    }
457}
458
459impl Div<f32> for Style {
460    type Output = Self;
461
462    fn div(self, rhs: f32) -> Self::Output {
463        self.scaled(1.0 / rhs)
464    }
465}
466
467impl Div<f64> for Style {
468    type Output = Self;
469
470    fn div(self, rhs: f64) -> Self::Output {
471        self.scaled(1.0 / rhs as f32)
472    }
473}
474
475#[cfg(test)]
476mod tests {
477    use super::*;
478
479    const ALL_THEMES: [Theme; 14] = [
480        Theme::SlateOcean,
481        Theme::NebulaViolet,
482        Theme::TerminalGreen,
483        Theme::EmberForge,
484        Theme::SolarAmber,
485        Theme::ArticCyan,
486        Theme::CrimsonSignal,
487        Theme::MidnightIndigo,
488        Theme::CopperRose,
489        Theme::LimeCircuit,
490        Theme::CoralReef,
491        Theme::GraphiteMono,
492        Theme::PlumStatic,
493        Theme::SandstoneTrail,
494    ];
495
496    #[test]
497    fn every_theme_has_distinct_light_and_dark_palettes() {
498        // Regression test for a typo (`Ligth` instead of `Light`) that once
499        // made an unqualified identifier bind irrefutably in a match arm,
500        // silently turning `(SolarAmber, Light)` into a catch-all -- it only
501        // still looked right because `(SolarAmber, Dark)`, listed first,
502        // matched exactly and won for that case, leaving the buggy arm to
503        // fire (and return the *wrong*, `Dark`-shaped) colors for `Light`.
504        for theme in ALL_THEMES {
505            let light = theme.colors(ColorMode::Light);
506            let dark = theme.colors(ColorMode::Dark);
507            assert_ne!(
508                light, dark,
509                "{theme:?}'s Light and Dark palettes must not be identical"
510            );
511        }
512    }
513
514    #[test]
515    fn solar_amber_light_matches_its_defined_palette() {
516        // Pins the exact SolarAmber/Light values so a regression back to the
517        // `Ligth` typo -- which made this arm silently reuse whatever
518        // `(SolarAmber, Dark)` produced -- is caught even though `assert_ne`
519        // alone only proves the two differ, not that `Light` is *this*
520        // palette.
521        assert_eq!(
522            Theme::SolarAmber.colors(ColorMode::Light),
523            ThemeColors {
524                node: Color32::from_rgb(0xF0, 0xC2, 0x4C),
525                segment: Color32::from_rgb(0x5A, 0x4E, 0x2E),
526                selected: Color32::from_rgb(0x4F, 0xBF, 0x9E),
527                alert: Color32::from_rgb(0xE8, 0x65, 0x4A),
528                text: Color32::from_rgb(0xEF, 0xE4, 0xC4),
529            }
530        );
531    }
532
533    #[test]
534    fn theme_implements_map_theme_by_delegating_to_colors() {
535        let theme: &dyn MapTheme = &Theme::TerminalGreen;
536        assert_eq!(
537            theme.colors(ColorMode::Dark),
538            Theme::TerminalGreen.colors(ColorMode::Dark)
539        );
540        assert_eq!(
541            theme.colors(ColorMode::Light),
542            Theme::TerminalGreen.colors(ColorMode::Light)
543        );
544    }
545}