Skip to main content

openapi_ui/
theme.rs

1const LIGHT: &str = r#"
2:root {
3    --bg: #faf9f5;
4    --bg-sub: #f3f1ea;
5    --bg-card: #ffffff;
6    --bg-code: #f0ede4;
7    --bd: #e5e1d6;
8    --bd-sub: #ede9df;
9    --t1: #1f1e1d;
10    --t2: #5c5852;
11    --t3: #9c9589;
12    --t4: #c4beb4;
13    --accent: #c96442;
14    --accent-h: #b5572d;
15    --accent-bg: #f9ede8;
16    --green: #2d8a4e;
17    --orange: #b56613;
18    --red: #c0392b;
19    --purple: #7952b3;
20    --cyan: #1a7a8a;
21    --blue: #1c6bbb;
22    --s2xx: #2d8a4e;
23    --s3xx: #1a7a8a;
24    --s4xx: #b56613;
25    --s5xx: #c0392b;
26    --sb-t: #eceae2;
27    --sb-th: #c8c3b8;
28    --shadow-sm: 0 1px 2px rgba(31, 30, 29, 0.06);
29    --shadow-md: 0 4px 12px rgba(31, 30, 29, 0.1);
30}
31"#;
32
33const DARK: &str = r#"
34[data-theme="dark"] {
35    --bg: #1e1c19;
36    --bg-sub: #252320;
37    --bg-card: #2d2b27;
38    --bg-code: #201f1c;
39    --bd: #3a3733;
40    --bd-sub: #302e2b;
41    --t1: #ede8df;
42    --t2: #9c9487;
43    --t3: #6b6560;
44    --t4: #3d3a36;
45    --accent: #d4714f;
46    --accent-h: #e07d5a;
47    --accent-bg: #2e2119;
48    --green: #4caf72;
49    --orange: #d4943a;
50    --red: #e05c4b;
51    --purple: #a07fd0;
52    --cyan: #38bcd4;
53    --blue: #5a9fe0;
54    --s2xx: #4caf72;
55    --s3xx: #38bcd4;
56    --s4xx: #d4943a;
57    --s5xx: #e05c4b;
58    --sb-t: #252320;
59    --sb-th: #3a3733;
60    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
61    --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.45);
62}
63"#;
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
66pub enum ThemeMode {
67    /// Light theme only
68    Light,
69    /// Dark theme only
70    Dark,
71    /// Both themes with user toggle (default)
72    #[default]
73    System,
74}
75
76impl ThemeMode {
77    /// Create a ThemeMode from a string slice.
78    pub fn from_str(s: &str) -> Self {
79        match s.to_lowercase().as_str() {
80            "light" => ThemeMode::Light,
81            "dark" => ThemeMode::Dark,
82            _ => ThemeMode::System,
83        }
84    }
85
86    /// Convert ThemeMode to a string slice.
87    pub fn as_str(&self) -> &'static str {
88        match self {
89            ThemeMode::Light => "light",
90            ThemeMode::Dark => "dark",
91            ThemeMode::System => "system",
92        }
93    }
94
95    pub fn get_css(&self) -> String {
96        match self {
97            ThemeMode::Light => LIGHT.to_string(),
98            ThemeMode::Dark => DARK.to_string(),
99            ThemeMode::System => format!("{}{}", LIGHT, DARK),
100        }
101    }
102}
103
104pub fn get_theme_css(theme: &str) -> String {
105    ThemeMode::from_str(theme).get_css()
106}
107
108pub fn get_complete_theme_css() -> String {
109    ThemeMode::System.get_css()
110}
111
112#[cfg(test)]
113mod tests {
114    use super::*;
115
116    #[test]
117    fn test_theme_mode_from_str() {
118        assert_eq!(ThemeMode::from_str("dark"), ThemeMode::Dark);
119        assert_eq!(ThemeMode::from_str("DARK"), ThemeMode::Dark);
120        assert_eq!(ThemeMode::from_str("light"), ThemeMode::Light);
121        assert_eq!(ThemeMode::from_str("system"), ThemeMode::System);
122        assert_eq!(ThemeMode::from_str(""), ThemeMode::System);
123    }
124
125    #[test]
126    fn test_theme_mode_get_css() {
127        let light_css = ThemeMode::Light.get_css();
128        assert!(light_css.contains(":root"));
129        assert!(!light_css.contains("[data-theme=\"dark\"]"));
130
131        let dark_css = ThemeMode::Dark.get_css();
132        assert!(dark_css.contains("[data-theme=\"dark\"]"));
133
134        let system_css = ThemeMode::System.get_css();
135        assert!(system_css.contains(":root"));
136        assert!(system_css.contains("[data-theme=\"dark\"]"));
137    }
138
139    #[test]
140    fn test_get_theme_css() {
141        let css = get_theme_css("");
142        assert!(css.contains(":root"));
143        assert!(css.contains("[data-theme=\"dark\"]"));
144
145        let css = get_theme_css("dark");
146        assert!(css.contains("[data-theme=\"dark\"]"));
147
148        let css = get_theme_css("light");
149        assert!(css.contains(":root"));
150    }
151
152    #[test]
153    fn test_get_complete_theme_css() {
154        let css = get_complete_theme_css();
155        assert!(css.contains(":root"));
156        assert!(css.contains("[data-theme=\"dark\"]"));
157    }
158}