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