A simple TUI Library written in Rust
1use crate::color::Color;
2use crate::style::Style;
3
4/// A named palette of semantic colors for consistent styling.
5/// Pass a `&Theme` through rendering functions — no global state.
6#[derive(Debug, Clone)]
7pub struct Theme {
8 pub primary: Color,
9 pub secondary: Color,
10 pub success: Color,
11 pub warning: Color,
12 pub error: Color,
13 pub muted: Color,
14 pub border: Color,
15 pub text: Color,
16}
17
18impl Theme {
19 /// Default theme suitable for most terminals.
20 pub fn default_theme() -> Self {
21 Theme {
22 primary: Color::Cyan,
23 secondary: Color::Blue,
24 success: Color::Green,
25 warning: Color::Yellow,
26 error: Color::Red,
27 muted: Color::BrightBlack,
28 border: Color::White,
29 text: Color::Default,
30 }
31 }
32
33 /// Dark theme with higher contrast accents.
34 pub fn dark() -> Self {
35 Theme {
36 primary: Color::BrightCyan,
37 secondary: Color::BrightBlue,
38 success: Color::BrightGreen,
39 warning: Color::BrightYellow,
40 error: Color::BrightRed,
41 muted: Color::BrightBlack,
42 border: Color::BrightBlack,
43 text: Color::BrightWhite,
44 }
45 }
46
47 /// Light theme (for terminals with white backgrounds).
48 pub fn light() -> Self {
49 Theme {
50 primary: Color::Blue,
51 secondary: Color::Magenta,
52 success: Color::Green,
53 warning: Color::Yellow,
54 error: Color::Red,
55 muted: Color::Black,
56 border: Color::Black,
57 text: Color::Black,
58 }
59 }
60
61 // --- Style helpers ---
62
63 pub fn primary_style(&self) -> Style {
64 Style::new().fg(self.primary.clone())
65 }
66
67 pub fn success_style(&self) -> Style {
68 Style::new().fg(self.success.clone())
69 }
70
71 pub fn warning_style(&self) -> Style {
72 Style::new().fg(self.warning.clone())
73 }
74
75 pub fn error_style(&self) -> Style {
76 Style::new().fg(self.error.clone())
77 }
78
79 pub fn muted_style(&self) -> Style {
80 Style::new().fg(self.muted.clone()).dim()
81 }
82
83 pub fn border_style(&self) -> Style {
84 Style::new().fg(self.border.clone())
85 }
86}
87
88impl Default for Theme {
89 fn default() -> Self {
90 Self::default_theme()
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 #[test]
99 fn default_theme_has_expected_colors() {
100 let t = Theme::default_theme();
101 assert_eq!(t.primary, Color::Cyan);
102 assert_eq!(t.success, Color::Green);
103 assert_eq!(t.error, Color::Red);
104 }
105
106 #[test]
107 fn dark_theme_is_bright() {
108 let t = Theme::dark();
109 assert_eq!(t.primary, Color::BrightCyan);
110 assert_eq!(t.success, Color::BrightGreen);
111 }
112
113 #[test]
114 fn style_helpers_produce_styles() {
115 let t = Theme::default_theme();
116 assert_eq!(t.primary_style().fg, Some(Color::Cyan));
117 assert_eq!(t.error_style().fg, Some(Color::Red));
118 assert!(t.muted_style().dim);
119 }
120}