Skip to content

Commit cd55b30

Browse files
authored
Merge pull request #108 from ryanoneill/feature/additional-themes
Add Dracula, Solarized Dark, and Gruvbox Dark built-in themes
2 parents b5d1515 + 0af2c2a commit cd55b30

File tree

3 files changed

+343
-9
lines changed

3 files changed

+343
-9
lines changed

examples/themed_app.rs

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,29 @@ enum ActiveTheme {
2626
#[default]
2727
Default,
2828
Nord,
29+
Dracula,
30+
SolarizedDark,
31+
GruvboxDark,
2932
}
3033

3134
impl ActiveTheme {
3235
fn name(&self) -> &'static str {
3336
match self {
3437
ActiveTheme::Default => "Default",
3538
ActiveTheme::Nord => "Nord",
39+
ActiveTheme::Dracula => "Dracula",
40+
ActiveTheme::SolarizedDark => "Solarized Dark",
41+
ActiveTheme::GruvboxDark => "Gruvbox Dark",
3642
}
3743
}
3844

39-
fn toggle(&self) -> Self {
45+
fn next(&self) -> Self {
4046
match self {
4147
ActiveTheme::Default => ActiveTheme::Nord,
42-
ActiveTheme::Nord => ActiveTheme::Default,
48+
ActiveTheme::Nord => ActiveTheme::Dracula,
49+
ActiveTheme::Dracula => ActiveTheme::SolarizedDark,
50+
ActiveTheme::SolarizedDark => ActiveTheme::GruvboxDark,
51+
ActiveTheme::GruvboxDark => ActiveTheme::Default,
4352
}
4453
}
4554
}
@@ -103,7 +112,7 @@ impl App for ThemedApp {
103112
fn update(state: &mut State, msg: Msg) -> Command<Msg> {
104113
match msg {
105114
Msg::ToggleTheme => {
106-
state.active_theme = state.active_theme.toggle();
115+
state.active_theme = state.active_theme.next();
107116
}
108117
Msg::ButtonPressed => {
109118
// Toggle button focused state for visual feedback
@@ -140,6 +149,9 @@ impl App for ThemedApp {
140149
let theme = match state.active_theme {
141150
ActiveTheme::Default => Theme::default(),
142151
ActiveTheme::Nord => Theme::nord(),
152+
ActiveTheme::Dracula => Theme::dracula(),
153+
ActiveTheme::SolarizedDark => Theme::solarized_dark(),
154+
ActiveTheme::GruvboxDark => Theme::gruvbox_dark(),
143155
};
144156

145157
let area = frame.area();
@@ -295,12 +307,21 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
295307
println!("Nord Theme (after interactions):");
296308
println!("{}\n", vt.display_ansi());
297309

310+
// Cycle through remaining themes
311+
for _ in 0..3 {
312+
vt.dispatch(Msg::ToggleTheme);
313+
vt.tick()?;
314+
println!("{} Theme:", vt.state().active_theme.name());
315+
println!("{}\n", vt.display_ansi());
316+
}
317+
298318
// Show theme comparison
299319
println!("=== Theme Comparison ===");
300-
println!("Default theme uses: Yellow focus, DarkGray disabled, Cyan primary");
301-
println!("Nord theme uses: Light Blue focus (#88C0D0), Muted gray disabled, Dark blue primary");
302-
println!("\nThe Nord theme provides a cohesive, eye-friendly color palette");
303-
println!("inspired by the Arctic's colors - ideal for extended coding sessions.");
320+
println!("Default: Yellow focus, DarkGray disabled, Cyan primary");
321+
println!("Nord: Light Blue focus (#88C0D0), Muted gray disabled, Dark blue primary");
322+
println!("Dracula: Purple focus (#BD93F9), Comment gray disabled, Cyan primary");
323+
println!("Solarized Dark: Blue focus (#268BD2), Base01 disabled, Blue primary");
324+
println!("Gruvbox Dark: Yellow focus (#FABD2F), Gray disabled, Aqua primary");
304325

305326
Ok(())
306327
}

src/theme/mod.rs

Lines changed: 221 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
//! Theming support for Envision components.
22
//!
33
//! The theme module provides customizable color schemes for all UI components.
4-
//! Two themes are included by default: a `Default` theme matching ratatui's
5-
//! standard colors, and a `Nord` theme based on the popular Nord color palette.
4+
//! Five themes are included by default: a `Default` theme matching ratatui's
5+
//! standard colors, and four popular dark themes (Nord, Dracula, Solarized Dark,
6+
//! Gruvbox Dark).
67
//!
78
//! # Example
89
//!
@@ -15,6 +16,9 @@
1516
//! // Use Nord theme (blue focused, muted disabled)
1617
//! let nord_theme = Theme::nord();
1718
//!
19+
//! // Use Dracula theme (purple focused, dark disabled)
20+
//! let dracula_theme = Theme::dracula();
21+
//!
1822
//! // Components use theme in their view() method:
1923
//! // Component::view(&state, frame, area, &nord_theme);
2024
//! ```
@@ -76,6 +80,89 @@ pub const NORD14: Color = Color::Rgb(163, 190, 140);
7680
/// Nord Aurora - purple
7781
pub const NORD15: Color = Color::Rgb(180, 142, 173);
7882

83+
// =============================================================================
84+
// Dracula Color Palette Constants
85+
// =============================================================================
86+
87+
/// Dracula - background (#282A36)
88+
pub const DRACULA_BG: Color = Color::Rgb(40, 42, 54);
89+
/// Dracula - current line (#44475A)
90+
pub const DRACULA_CURRENT: Color = Color::Rgb(68, 71, 90);
91+
/// Dracula - foreground (#F8F8F2)
92+
pub const DRACULA_FG: Color = Color::Rgb(248, 248, 242);
93+
/// Dracula - comment (#6272A4)
94+
pub const DRACULA_COMMENT: Color = Color::Rgb(98, 114, 164);
95+
/// Dracula - cyan (#8BE9FD)
96+
pub const DRACULA_CYAN: Color = Color::Rgb(139, 233, 253);
97+
/// Dracula - green (#50FA7B)
98+
pub const DRACULA_GREEN: Color = Color::Rgb(80, 250, 123);
99+
/// Dracula - orange (#FFB86C)
100+
pub const DRACULA_ORANGE: Color = Color::Rgb(255, 184, 108);
101+
/// Dracula - pink (#FF79C6)
102+
pub const DRACULA_PINK: Color = Color::Rgb(255, 121, 198);
103+
/// Dracula - purple (#BD93F9)
104+
pub const DRACULA_PURPLE: Color = Color::Rgb(189, 147, 249);
105+
/// Dracula - red (#FF5555)
106+
pub const DRACULA_RED: Color = Color::Rgb(255, 85, 85);
107+
/// Dracula - yellow (#F1FA8C)
108+
pub const DRACULA_YELLOW: Color = Color::Rgb(241, 250, 140);
109+
110+
// =============================================================================
111+
// Solarized Dark Color Palette Constants
112+
// =============================================================================
113+
114+
/// Solarized Dark - base03 (darkest background, #002B36)
115+
pub const SOLARIZED_BASE03: Color = Color::Rgb(0, 43, 54);
116+
/// Solarized Dark - base02 (background highlights, #073642)
117+
pub const SOLARIZED_BASE02: Color = Color::Rgb(7, 54, 66);
118+
/// Solarized Dark - base01 (comments, #586E75)
119+
pub const SOLARIZED_BASE01: Color = Color::Rgb(88, 110, 117);
120+
/// Solarized Dark - base0 (primary text, #839496)
121+
pub const SOLARIZED_BASE0: Color = Color::Rgb(131, 148, 150);
122+
/// Solarized Dark - base1 (emphasized text, #93A1A1)
123+
pub const SOLARIZED_BASE1: Color = Color::Rgb(147, 161, 161);
124+
/// Solarized Dark - blue (#268BD2)
125+
pub const SOLARIZED_BLUE: Color = Color::Rgb(38, 139, 210);
126+
/// Solarized Dark - cyan (#2AA198)
127+
pub const SOLARIZED_CYAN: Color = Color::Rgb(42, 161, 152);
128+
/// Solarized Dark - green (#859900)
129+
pub const SOLARIZED_GREEN: Color = Color::Rgb(133, 153, 0);
130+
/// Solarized Dark - yellow (#B58900)
131+
pub const SOLARIZED_YELLOW: Color = Color::Rgb(181, 137, 0);
132+
/// Solarized Dark - orange (#CB4B16)
133+
pub const SOLARIZED_ORANGE: Color = Color::Rgb(203, 75, 22);
134+
/// Solarized Dark - red (#DC322F)
135+
pub const SOLARIZED_RED: Color = Color::Rgb(220, 50, 47);
136+
/// Solarized Dark - magenta (#D33682)
137+
pub const SOLARIZED_MAGENTA: Color = Color::Rgb(211, 54, 130);
138+
139+
// =============================================================================
140+
// Gruvbox Dark Color Palette Constants
141+
// =============================================================================
142+
143+
/// Gruvbox Dark - bg (dark background, #282828)
144+
pub const GRUVBOX_BG: Color = Color::Rgb(40, 40, 40);
145+
/// Gruvbox Dark - bg1 (lighter background, #3C3836)
146+
pub const GRUVBOX_BG1: Color = Color::Rgb(60, 56, 54);
147+
/// Gruvbox Dark - fg (light foreground, #EBDBB2)
148+
pub const GRUVBOX_FG: Color = Color::Rgb(235, 219, 178);
149+
/// Gruvbox Dark - gray (#928374)
150+
pub const GRUVBOX_GRAY: Color = Color::Rgb(146, 131, 116);
151+
/// Gruvbox Dark - red (#FB4934)
152+
pub const GRUVBOX_RED: Color = Color::Rgb(251, 73, 52);
153+
/// Gruvbox Dark - green (#B8BB26)
154+
pub const GRUVBOX_GREEN: Color = Color::Rgb(184, 187, 38);
155+
/// Gruvbox Dark - yellow (#FABD2F)
156+
pub const GRUVBOX_YELLOW: Color = Color::Rgb(250, 189, 47);
157+
/// Gruvbox Dark - blue (#83A598)
158+
pub const GRUVBOX_BLUE: Color = Color::Rgb(131, 165, 152);
159+
/// Gruvbox Dark - purple (#D3869B)
160+
pub const GRUVBOX_PURPLE: Color = Color::Rgb(211, 134, 155);
161+
/// Gruvbox Dark - aqua (#8EC07C)
162+
pub const GRUVBOX_AQUA: Color = Color::Rgb(142, 192, 124);
163+
/// Gruvbox Dark - orange (#FE8019)
164+
pub const GRUVBOX_ORANGE: Color = Color::Rgb(254, 128, 25);
165+
79166
// =============================================================================
80167
// Theme Struct
81168
// =============================================================================
@@ -216,6 +303,138 @@ impl Theme {
216303
}
217304
}
218305

306+
/// Creates a new Dracula-themed color scheme.
307+
///
308+
/// The Dracula theme uses the popular Dracula color palette with its
309+
/// characteristic purples, pinks, and vibrant accent colors.
310+
///
311+
/// # Colors
312+
///
313+
/// - Focused: Purple (#BD93F9)
314+
/// - Selected: Pink (#FF79C6)
315+
/// - Disabled: Comment (#6272A4)
316+
/// - Success: Green (#50FA7B)
317+
/// - Warning: Yellow (#F1FA8C)
318+
/// - Error: Red (#FF5555)
319+
///
320+
/// # Example
321+
///
322+
/// ```rust
323+
/// use envision::theme::Theme;
324+
///
325+
/// let theme = Theme::dracula();
326+
/// assert_eq!(theme.focused, envision::theme::DRACULA_PURPLE);
327+
/// ```
328+
pub fn dracula() -> Self {
329+
Self {
330+
background: DRACULA_BG,
331+
foreground: DRACULA_FG,
332+
border: DRACULA_COMMENT,
333+
334+
focused: DRACULA_PURPLE,
335+
selected: DRACULA_PINK,
336+
disabled: DRACULA_COMMENT,
337+
placeholder: DRACULA_COMMENT,
338+
339+
primary: DRACULA_CYAN,
340+
success: DRACULA_GREEN,
341+
warning: DRACULA_YELLOW,
342+
error: DRACULA_RED,
343+
info: DRACULA_CYAN,
344+
345+
progress_filled: DRACULA_PURPLE,
346+
progress_empty: DRACULA_CURRENT,
347+
}
348+
}
349+
350+
/// Creates a new Solarized Dark-themed color scheme.
351+
///
352+
/// The Solarized Dark theme uses Ethan Schoonover's carefully designed
353+
/// color palette optimized for readability and reduced eye strain.
354+
///
355+
/// # Colors
356+
///
357+
/// - Focused: Blue (#268BD2)
358+
/// - Selected: Cyan (#2AA198)
359+
/// - Disabled: Base01 (#586E75)
360+
/// - Success: Green (#859900)
361+
/// - Warning: Yellow (#B58900)
362+
/// - Error: Red (#DC322F)
363+
///
364+
/// # Example
365+
///
366+
/// ```rust
367+
/// use envision::theme::Theme;
368+
///
369+
/// let theme = Theme::solarized_dark();
370+
/// assert_eq!(theme.focused, envision::theme::SOLARIZED_BLUE);
371+
/// ```
372+
pub fn solarized_dark() -> Self {
373+
Self {
374+
background: SOLARIZED_BASE03,
375+
foreground: SOLARIZED_BASE0,
376+
border: SOLARIZED_BASE01,
377+
378+
focused: SOLARIZED_BLUE,
379+
selected: SOLARIZED_CYAN,
380+
disabled: SOLARIZED_BASE01,
381+
placeholder: SOLARIZED_BASE01,
382+
383+
primary: SOLARIZED_BLUE,
384+
success: SOLARIZED_GREEN,
385+
warning: SOLARIZED_YELLOW,
386+
error: SOLARIZED_RED,
387+
info: SOLARIZED_CYAN,
388+
389+
progress_filled: SOLARIZED_BLUE,
390+
progress_empty: SOLARIZED_BASE02,
391+
}
392+
}
393+
394+
/// Creates a new Gruvbox Dark-themed color scheme.
395+
///
396+
/// The Gruvbox Dark theme uses the retro-groove Gruvbox color palette
397+
/// with its warm, earthy tones and high contrast.
398+
///
399+
/// # Colors
400+
///
401+
/// - Focused: Yellow (#FABD2F)
402+
/// - Selected: Blue (#83A598)
403+
/// - Disabled: Gray (#928374)
404+
/// - Success: Green (#B8BB26)
405+
/// - Warning: Orange (#FE8019)
406+
/// - Error: Red (#FB4934)
407+
///
408+
/// # Example
409+
///
410+
/// ```rust
411+
/// use envision::theme::Theme;
412+
///
413+
/// let theme = Theme::gruvbox_dark();
414+
/// assert_eq!(theme.focused, envision::theme::GRUVBOX_YELLOW);
415+
/// ```
416+
pub fn gruvbox_dark() -> Self {
417+
Self {
418+
background: GRUVBOX_BG,
419+
foreground: GRUVBOX_FG,
420+
border: GRUVBOX_GRAY,
421+
422+
focused: GRUVBOX_YELLOW,
423+
selected: GRUVBOX_BLUE,
424+
disabled: GRUVBOX_GRAY,
425+
placeholder: GRUVBOX_GRAY,
426+
427+
primary: GRUVBOX_AQUA,
428+
success: GRUVBOX_GREEN,
429+
warning: GRUVBOX_ORANGE,
430+
error: GRUVBOX_RED,
431+
info: GRUVBOX_BLUE,
432+
433+
progress_filled: GRUVBOX_YELLOW,
434+
progress_empty: GRUVBOX_BG1,
435+
}
436+
}
437+
219438
// =========================================================================
220439
// Style Helper Methods
221440
// =========================================================================

0 commit comments

Comments
 (0)