diff --git a/.vscode/settings.json b/.vscode/settings.json
index ff995d8..d499842 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -27,6 +27,7 @@
"CARETRIGHTBASE",
"CARETUP",
"CARETUPBASE",
+ "Catppuccin",
"cbook",
"clabel",
"CLOSEPOLY",
@@ -45,6 +46,7 @@
"fileio",
"fontsize",
"fontweight",
+ "frameon",
"gridspec",
"handlelength",
"handletextpad",
@@ -71,6 +73,7 @@
"markevery",
"MOVETO",
"mplot",
+ "nord",
"numpoints",
"numpy",
"patheffects",
@@ -84,6 +87,7 @@
"showfliers",
"stepfilled",
"suptitle",
+ "textcolor",
"TICKDOWN",
"TICKLEFT",
"TICKRIGHT",
@@ -104,6 +108,7 @@
"xmin",
"xnticks",
"xrange",
+ "xtick",
"xticklabels",
"xticks",
"yaxis",
@@ -113,6 +118,7 @@
"ymin",
"ynticks",
"yrange",
+ "ytick",
"yticklabels",
"yticks",
"zaxis",
diff --git a/examples/README.md b/examples/README.md
index 1cac750..7a09c20 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -11,6 +11,7 @@ Some output of integration tests are shown below.
- [Canvas](#canvas)
- [Contour](#contour)
- [Curve](#curve)
+- [DarkMode](#darkmode)
- [FillBetween](#fillbetween)
- [Histogram](#histogram)
- [Image](#image)
@@ -92,6 +93,15 @@ Some output of integration tests are shown below.


+## DarkMode
+
+[test_dark_mode.rs](https://github.com/cpmech/plotpy/tree/main/tests/test_dark_mode.rs)
+
+
+
+
+
+
## FillBetween
[test_fill_between.rs](https://github.com/cpmech/plotpy/tree/main/tests/test_fill_between.rs)
diff --git a/figures/integ_dark_mode_default.svg b/figures/integ_dark_mode_default.svg
new file mode 100644
index 0000000..d828773
--- /dev/null
+++ b/figures/integ_dark_mode_default.svg
@@ -0,0 +1,777 @@
+
+
+
diff --git a/figures/integ_dark_mode_mathematica.svg b/figures/integ_dark_mode_mathematica.svg
new file mode 100644
index 0000000..e17e759
--- /dev/null
+++ b/figures/integ_dark_mode_mathematica.svg
@@ -0,0 +1,777 @@
+
+
+
diff --git a/figures/integ_dark_mode_mocha.svg b/figures/integ_dark_mode_mocha.svg
new file mode 100644
index 0000000..850fbac
--- /dev/null
+++ b/figures/integ_dark_mode_mocha.svg
@@ -0,0 +1,777 @@
+
+
+
diff --git a/figures/integ_dark_mode_nordic.svg b/figures/integ_dark_mode_nordic.svg
new file mode 100644
index 0000000..c0d6096
--- /dev/null
+++ b/figures/integ_dark_mode_nordic.svg
@@ -0,0 +1,777 @@
+
+
+
diff --git a/src/dark_mode.rs b/src/dark_mode.rs
new file mode 100644
index 0000000..9c5f0c7
--- /dev/null
+++ b/src/dark_mode.rs
@@ -0,0 +1,180 @@
+use super::GraphMaker;
+
+/// Implements a dark mode enabler for plots
+///
+/// **Warning;** This instance must be the **first** to be added to the `Plot` object,
+pub struct DarkMode {
+ buffer: String,
+}
+
+impl DarkMode {
+ /// Allocates a new instance
+ ///
+ /// **Warning;** This instance must be the **first** to be added to the `Plot` object,
+ pub fn new() -> Self {
+ let mut dm = DarkMode { buffer: String::new() };
+ dm.set_dark_background();
+ dm
+ }
+
+ /// Sets the Matplotlib native dark mode (dark_background)
+ pub fn set_dark_background(&mut self) {
+ self.buffer.clear();
+ self.buffer.push_str("plt.style.use('dark_background')\n");
+ }
+
+ /// Sets the Mathematica-like dark mode
+ ///
+ /// **Important:** This mode requires `cycler` package in Python environment.
+ pub fn set_mathematica(&mut self) {
+ self.buffer.clear();
+ self.buffer.push_str(
+ r#"
+########### Setting dark mode: begin ###########
+
+from cycler import cycler
+
+# 1. Background and Text Colors
+plt.rcParams.update({
+ 'figure.facecolor': '#000000', # Pure black background
+ 'axes.facecolor': '#000000', # Pure black plotting area
+ 'text.color': '#FFFFFF', # White text
+ 'axes.labelcolor': '#FFFFFF', # White axis labels
+ 'xtick.color': '#FFFFFF', # White x-axis ticks
+ 'ytick.color': '#FFFFFF', # White y-axis ticks
+ 'axes.edgecolor': '#555555', # Muted gray spines (Mathematica style)
+})
+
+# 2. Mathematica 'Vibrant' Color Cycle
+# These hex codes approximate the default Mathematica 10+ plot palette
+mathematica_colors = [
+ '#5E81B5', # Blue
+ '#E19C24', # Orange
+ '#8FB032', # Green
+ '#EB6238', # Red
+ '#9467BD', # Purple
+ '#8C564B', # Brown
+ '#E377C2' # Pink
+]
+plt.rcParams['axes.prop_cycle'] = cycler('color', mathematica_colors)
+
+# 3. Refined Details
+plt.rcParams.update({
+ 'grid.color': '#313244', # Surface 0 (Subtle grid)
+ 'legend.facecolor': '#181825', # Mantle
+ 'legend.edgecolor': '#313244',
+ 'legend.labelcolor': '#cdd6f4'
+})
+
+########### Setting dark mode: end ###########
+
+"#,
+ );
+ }
+
+ /// **Important:** This mode requires `cycler` package in Python environment.
+ pub fn set_mocha(&mut self) {
+ self.buffer.clear();
+ self.buffer.push_str(
+ r#"
+########### Setting dark mode: begin ###########
+
+from cycler import cycler
+
+# 1. Background and Base Colors (Catppuccin Mocha)
+plt.rcParams.update({
+ 'figure.facecolor': '#11111b', # Crust (Deepest dark)
+ 'axes.facecolor': '#1e1e2e', # Base (Slightly lighter for contrast)
+ 'savefig.facecolor': '#11111b',
+ 'text.color': '#cdd6f4', # Text
+ 'axes.labelcolor': '#cdd6f4', # Text
+ 'xtick.color': '#7f849c', # Overlay 1 (Muted gray)
+ 'ytick.color': '#7f849c',
+ 'axes.edgecolor': '#45475a', # Surface 1
+})
+
+# 2. Catppuccin Mocha Palette Color Cycle
+# Selecting the most vibrant "flavor" accents
+mocha_colors = [
+ '#89b4fa', # Blue
+ '#fab387', # Peach
+ '#a6e3a1', # Green
+ '#f38ba8', # Red
+ '#cba6f7', # Mauve
+ '#94e2d5', # Teal
+ '#f9e2af' # Yellow
+]
+plt.rcParams['axes.prop_cycle'] = cycler('color', mocha_colors)
+
+# 3. Refined Details
+plt.rcParams.update({
+ 'grid.color': '#313244', # Surface 0 (Subtle grid)
+ 'legend.facecolor': '#181825', # Mantle
+ 'legend.edgecolor': '#313244',
+ 'legend.labelcolor': '#cdd6f4'
+})
+
+########### Setting dark mode: end ###########
+
+"#,
+ );
+ }
+
+ /// Sets an alternative dark mode ("Nordic Night" or "Material Dark")
+ ///
+ /// **Important:** This mode requires `cycler` package in Python environment.
+ pub fn set_nordic(&mut self) {
+ self.buffer.clear();
+ self.buffer.push_str(
+ r#"
+########### Setting dark mode: begin ###########
+
+from cycler import cycler
+
+# 1. Background and Base Colors
+plt.rcParams.update({
+ 'figure.facecolor': '#2E3440', # Soft charcoal
+ 'axes.facecolor': '#2E3440', # Match axes to figure
+ 'savefig.facecolor': '#2E3440', # Ensure saved images are dark
+ 'text.color': '#D8DEE9', # Off-white/Silver text
+ 'axes.labelcolor': '#D8DEE9',
+ 'xtick.color': '#4C566A', # Muted gray ticks
+ 'ytick.color': '#4C566A',
+ 'axes.edgecolor': '#4C566A', # Muted borders
+})
+
+# 2. Nord Palette Color Cycle (Modern Pastels)
+nord_colors = [
+ '#88C0D0', # Frost Blue
+ '#81A1C1', # Glacial Blue
+ '#BF616A', # Soft Red
+ '#D08770', # Orange
+ '#EBCB8B', # Yellow
+ '#A3BE8C', # Sage Green
+ '#B48EAD' # Muted Purple
+]
+plt.rcParams['axes.prop_cycle'] = cycler('color', nord_colors)
+
+# 3. Refined Details
+plt.rcParams.update({
+ 'grid.color': '#3B4252', # Darker gray grid lines
+ 'legend.facecolor': '#181825', # Mantle
+ 'legend.edgecolor': '#313244',
+ 'legend.labelcolor': '#D8DEE9'
+})
+
+########### Setting dark mode: end ###########
+
+"#,
+ );
+ }
+}
+
+impl GraphMaker for DarkMode {
+ fn get_buffer<'a>(&'a self) -> &'a String {
+ &self.buffer
+ }
+ fn clear_buffer(&mut self) {
+ self.buffer.clear();
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index cec1ed0..bf890c2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -83,6 +83,7 @@ mod constants;
mod contour;
mod conversions;
mod curve;
+mod dark_mode;
mod fileio;
mod fill_between;
mod histogram;
@@ -108,6 +109,7 @@ pub use constants::*;
pub use contour::*;
use conversions::*;
pub use curve::*;
+pub use dark_mode::*;
use fileio::*;
pub use fill_between::*;
pub use histogram::*;
diff --git a/tests/test_dark_mode.rs b/tests/test_dark_mode.rs
new file mode 100644
index 0000000..a20448f
--- /dev/null
+++ b/tests/test_dark_mode.rs
@@ -0,0 +1,133 @@
+use plotpy::{Curve, DarkMode, Plot, StrError};
+use std::fs::File;
+use std::io::{BufRead, BufReader};
+use std::path::Path;
+
+const OUT_DIR: &str = "/tmp/plotpy/integ_tests";
+
+#[test]
+fn test_dark_mode_default() -> Result<(), StrError> {
+ // curve
+ let x = [1.0, 2.0, 3.0, 4.0];
+ let y = [1.0, 4.0, 9.0, 16.0];
+ let mut curve = Curve::new();
+ curve.set_label("curve").draw(&x, &y);
+
+ // dark mode enabler
+ let dm = DarkMode::new();
+
+ // plot
+ let mut plot = Plot::new();
+ plot.add(&dm).add(&curve);
+
+ // save figure
+ let path = Path::new(OUT_DIR).join("integ_dark_mode_default.svg");
+ plot.legend()
+ .grid_and_labels("x", "y")
+ .set_show_errors(true)
+ .save(&path)?;
+
+ // check number of lines
+ let file = File::open(path).map_err(|_| "cannot open file")?;
+ let buffered = BufReader::new(file);
+ let lines_iter = buffered.lines();
+ let n = lines_iter.count();
+ assert!(n > 700 && n < 830);
+ Ok(())
+}
+
+#[test]
+fn test_dark_mode_mathematica() -> Result<(), StrError> {
+ // curve
+ let x = [1.0, 2.0, 3.0, 4.0];
+ let y = [1.0, 4.0, 9.0, 16.0];
+ let mut curve = Curve::new();
+ curve.set_label("curve").draw(&x, &y);
+
+ // dark mode enabler
+ let mut dm = DarkMode::new();
+ dm.set_mathematica();
+
+ // plot
+ let mut plot = Plot::new();
+ plot.add(&dm).add(&curve);
+
+ // save figure
+ let path = Path::new(OUT_DIR).join("integ_dark_mode_mathematica.svg");
+ plot.legend()
+ .grid_and_labels("x", "y")
+ .set_show_errors(true)
+ .save(&path)?;
+
+ // check number of lines
+ let file = File::open(path).map_err(|_| "cannot open file")?;
+ let buffered = BufReader::new(file);
+ let lines_iter = buffered.lines();
+ let n = lines_iter.count();
+ assert!(n > 700 && n < 830);
+ Ok(())
+}
+
+#[test]
+fn test_dark_mode_mocha() -> Result<(), StrError> {
+ // curve
+ let x = [1.0, 2.0, 3.0, 4.0];
+ let y = [1.0, 4.0, 9.0, 16.0];
+ let mut curve = Curve::new();
+ curve.set_label("curve").draw(&x, &y);
+
+ // dark mode enabler
+ let mut dm = DarkMode::new();
+ dm.set_mocha();
+
+ // plot
+ let mut plot = Plot::new();
+ plot.add(&dm).add(&curve);
+
+ // save figure
+ let path = Path::new(OUT_DIR).join("integ_dark_mode_mocha.svg");
+ plot.legend()
+ .grid_and_labels("x", "y")
+ .set_show_errors(true)
+ .save(&path)?;
+
+ // check number of lines
+ let file = File::open(path).map_err(|_| "cannot open file")?;
+ let buffered = BufReader::new(file);
+ let lines_iter = buffered.lines();
+ let n = lines_iter.count();
+ assert!(n > 700 && n < 830);
+ Ok(())
+}
+
+#[test]
+fn test_dark_mode_nordic() -> Result<(), StrError> {
+ // curve
+ let x = [1.0, 2.0, 3.0, 4.0];
+ let y = [1.0, 4.0, 9.0, 16.0];
+ let mut curve = Curve::new();
+ curve.set_label("curve").draw(&x, &y);
+
+ // dark mode enabler
+ let mut dm = DarkMode::new();
+ dm.set_nordic();
+
+ // plot
+ let mut plot = Plot::new();
+ plot.add(&dm).add(&curve);
+
+ // save figure
+ let path = Path::new(OUT_DIR).join("integ_dark_mode_nordic.svg");
+ plot.legend()
+ .grid_and_labels("x", "y")
+ .set_show_errors(true)
+ .save(&path)?;
+
+ // check number of lines
+ let file = File::open(path).map_err(|_| "cannot open file")?;
+ let buffered = BufReader::new(file);
+ let lines_iter = buffered.lines();
+ let n = lines_iter.count();
+ assert!(n > 700 && n < 830);
+ Ok(())
+}