tui_kit/theme.rs
1use ratatui::style::{Color, Modifier, Style};
2
3/// Color and style palette for tui-kit components.
4///
5/// ## Usage
6///
7/// Use [`Theme::native()`] (the default) for a palette that adapts to the
8/// user's terminal color scheme, or [`Theme::dark()`] for a fixed
9/// lazygit-inspired dark palette.
10///
11/// ## Extending for your app
12///
13/// Wrap `Theme` in your own struct for app-specific semantic roles:
14///
15/// ```rust
16/// pub struct AppTheme {
17/// pub base: Theme, // passed to all tui-kit calls
18/// pub my_semantic: Style, // app-specific roles
19/// }
20///
21/// impl AppTheme {
22/// pub fn native() -> Self {
23/// Self {
24/// base: Theme::native(),
25/// my_semantic: Style::default().fg(Color::LightGreen),
26/// }
27/// }
28/// }
29/// ```
30///
31/// `Theme` is `Copy` so it is cheap to pass by value or reference.
32#[derive(Debug, Clone, Copy)]
33pub struct Theme {
34 /// Border of the currently-focused interactive panel.
35 pub border_focused: Style,
36 /// Border of passive / display-only panels.
37 pub border_unfocused: Style,
38 /// Border of floating popups (always treated as focused).
39 pub border_popup: Style,
40 /// Border of error popups.
41 pub border_error: Style,
42 /// Border of warning popups.
43 pub border_warning: Style,
44
45 /// Label of the active tab.
46 pub tab_active: Style,
47 /// Label of an inactive tab.
48 pub tab_inactive: Style,
49
50 /// Style applied to the selected row in a list (fg + bg combined).
51 pub selection: Style,
52
53 /// Inline keyboard shortcut labels (e.g. "Enter", "Tab").
54 pub shortcut_key: Style,
55 /// The `-[n]-` digit indicator shown in widget titles.
56 pub shortcut_indicator: Style,
57
58 /// Section / group headers inside popups.
59 pub section_header: Style,
60 /// Normal body text.
61 pub body: Style,
62 /// Dimmed hint and footer text.
63 pub hint: Style,
64 /// Separator lines (─ characters).
65 pub separator: Style,
66
67 /// Positive / success signal (e.g. toast success, CI pass).
68 pub success: Style,
69}
70
71impl Theme {
72 /// 16-color native palette — adapts to the user's terminal color scheme.
73 ///
74 /// Uses only the 16 named ANSI colors so the result respects the terminal
75 /// theme (dark/light, Solarized, Nord, etc.).
76 ///
77 /// | Role | Color |
78 /// |--------------------|----------------|
79 /// | Accent / focused | LightBlue |
80 /// | Tabs / headers | LightBlue |
81 /// | Shortcuts | Yellow |
82 /// | Selection | Black on LightBlue |
83 /// | Positive signal | LightGreen |
84 /// | Negative signal | LightRed |
85 /// | Muted / hints | DarkGray |
86 pub fn native() -> Self {
87 Self {
88 border_focused: Style::default()
89 .fg(Color::LightBlue)
90 .add_modifier(Modifier::BOLD),
91 border_unfocused: Style::default().fg(Color::DarkGray),
92 border_popup: Style::default()
93 .fg(Color::LightBlue)
94 .add_modifier(Modifier::BOLD),
95 border_error: Style::default()
96 .fg(Color::LightRed)
97 .add_modifier(Modifier::BOLD),
98 border_warning: Style::default()
99 .fg(Color::Yellow)
100 .add_modifier(Modifier::BOLD),
101
102 tab_active: Style::default()
103 .fg(Color::LightBlue)
104 .add_modifier(Modifier::BOLD),
105 tab_inactive: Style::default().fg(Color::DarkGray),
106
107 selection: Style::default()
108 .fg(Color::Black)
109 .bg(Color::LightBlue)
110 .add_modifier(Modifier::BOLD),
111
112 shortcut_key: Style::default().fg(Color::Yellow),
113 shortcut_indicator: Style::default()
114 .fg(Color::Yellow)
115 .add_modifier(Modifier::BOLD),
116
117 section_header: Style::default()
118 .fg(Color::LightBlue)
119 .add_modifier(Modifier::BOLD),
120 body: Style::default().fg(Color::Gray),
121 hint: Style::default().fg(Color::DarkGray),
122 separator: Style::default().fg(Color::DarkGray),
123
124 success: Style::default().fg(Color::LightGreen),
125 }
126 }
127
128 /// Fixed dark palette with green accents — does not adapt to terminal theme.
129 ///
130 /// Use when you know the terminal has a dark background and want a
131 /// consistent lazygit-inspired look regardless of terminal settings.
132 ///
133 /// | Role | Color |
134 /// |--------------------|--------------|
135 /// | Accent / focused | Green + Bold |
136 /// | Unfocused border | White |
137 /// | Active tab | Green + Bold |
138 /// | Selection | White on Blue + Bold |
139 /// | Shortcut keys | Yellow |
140 /// | Section headers | Cyan + Bold |
141 /// | Hints | DarkGray |
142 pub fn dark() -> Self {
143 Self {
144 border_focused: Style::default()
145 .fg(Color::Green)
146 .add_modifier(Modifier::BOLD),
147 border_unfocused: Style::default().fg(Color::White),
148 border_popup: Style::default()
149 .fg(Color::Green)
150 .add_modifier(Modifier::BOLD),
151 border_error: Style::default().fg(Color::Red).add_modifier(Modifier::BOLD),
152 border_warning: Style::default()
153 .fg(Color::Yellow)
154 .add_modifier(Modifier::BOLD),
155
156 tab_active: Style::default()
157 .fg(Color::Green)
158 .add_modifier(Modifier::BOLD),
159 tab_inactive: Style::default().fg(Color::White),
160
161 selection: Style::default()
162 .fg(Color::Indexed(7))
163 .bg(Color::Indexed(6))
164 .add_modifier(Modifier::BOLD),
165
166 shortcut_key: Style::default().fg(Color::Yellow),
167 shortcut_indicator: Style::default()
168 .fg(Color::Yellow)
169 .add_modifier(Modifier::BOLD),
170
171 section_header: Style::default()
172 .fg(Color::Cyan)
173 .add_modifier(Modifier::BOLD),
174 body: Style::default().fg(Color::Gray),
175 hint: Style::default().fg(Color::DarkGray),
176 separator: Style::default().fg(Color::Green),
177
178 success: Style::default().fg(Color::Green),
179 }
180 }
181}
182
183impl Default for Theme {
184 fn default() -> Self {
185 Self::native()
186 }
187}