plot
Header-only C++ SVG charts — heatmap, line, scatter — GR-style API + Solarized themes
Loading...
Searching...
No Matches
theme.hpp
1/* Copyright (c) 2026 Steven Varga, Toronto, ON, Canada
2 * MIT License — see LICENSE
3 *
4 * Colour themes for the SVG plotting layer. A `theme_t` bundles the structural
5 * colours (background, panel, foreground text, grid, axis), a categorical series
6 * palette (cycled by line/scatter), and a 3-stop continuous gradient (wired into
7 * the continuous heatmap). Two Solarized presets plus a set of popular VS Code
8 * palettes (Dark+/Light+, Monokai, Dracula, Nord, One Dark, Gruvbox dark/light,
9 * Tomorrow Night, Night Owl, Material, Tokyo Night) ship inline; a process-global
10 * default current theme can be set with `plot::theme(...)`, and any driver call
11 * may override it for a single render with the `plot::use{...}` named argument.
12 * Dependency-free (standard library only).
13 */
14#ifndef PLOT_THEME_HPP
15#define PLOT_THEME_HPP
16
17#include <cstdint>
18#include <array>
19#include <vector>
20
21#include "tags.hpp"
22
23namespace plot {
24 struct theme_t {
25 std::uint32_t bg; // page / canvas background
26 std::uint32_t panel; // plotting-area panel
27 std::uint32_t fg; // default text / foreground
28 std::uint32_t grid; // grid lines / axis ticks
29 std::uint32_t axis; // axis lines
30 std::vector<std::uint32_t> series; // categorical palette (cycled)
31 std::array<std::uint32_t,3> gradient; // continuous 3-stop gradient
32 };
33
34 // Solarized (Ethan Schoonover) — exact hex.
35 inline const theme_t solarized_dark = {
36 /*bg base03*/ 0x002b36,
37 /*panel base02*/0x073642,
38 /*fg base0 */ 0x839496,
39 /*grid base01*/ 0x586e75,
40 /*axis base01*/ 0x586e75,
41 /*series*/ { 0x268bd2, 0x859900, 0xb58900, 0xcb4b16,
42 0xdc322f, 0xd33682, 0x6c71c4, 0x2aa198 },
43 /*gradient*/ { 0x268bd2, 0xb58900, 0xdc322f }
44 };
45
46 inline const theme_t solarized_light = {
47 /*bg base3 */ 0xfdf6e3,
48 /*panel base2*/ 0xeee8d5,
49 /*fg base00*/ 0x657b83,
50 /*grid base1 */ 0x93a1a1,
51 /*axis base00*/ 0x657b83,
52 /*series*/ { 0x268bd2, 0x859900, 0xb58900, 0xcb4b16,
53 0xdc322f, 0xd33682, 0x6c71c4, 0x2aa198 },
54 /*gradient*/ { 0x268bd2, 0xb58900, 0xdc322f }
55 };
56
57 // ---- Popular VS Code theme presets (canonical palettes) -----------------
58
59 // VS Code Dark+ (default dark).
60 inline const theme_t dark_plus = {
61 /*bg */ 0x1E1E1E,
62 /*panel*/ 0x252526,
63 /*fg */ 0xD4D4D4,
64 /*grid */ 0x3C3C3C,
65 /*axis */ 0x808080,
66 /*series*/ { 0x569CD6, 0x4EC9B0, 0xCE9178, 0xDCDCAA,
67 0xC586C0, 0x9CDCFE, 0xB5CEA8, 0xD16969 },
68 /*gradient*/ { 0x569CD6, 0xDCDCAA, 0xCE9178 }
69 };
70
71 // VS Code Light+ (default light).
72 inline const theme_t light_plus = {
73 /*bg */ 0xFFFFFF,
74 /*panel*/ 0xF3F3F3,
75 /*fg */ 0x1E1E1E,
76 /*grid */ 0xD4D4D4,
77 /*axis */ 0x808080,
78 /*series*/ { 0x0000FF, 0x267F99, 0xA31515, 0x795E26,
79 0xAF00DB, 0x001080, 0x098658, 0xCD3131 },
80 /*gradient*/ { 0x0000FF, 0x795E26, 0xA31515 }
81 };
82
83 // Monokai.
84 inline const theme_t monokai = {
85 /*bg */ 0x272822,
86 /*panel*/ 0x3E3D32,
87 /*fg */ 0xF8F8F2,
88 /*grid */ 0x49483E,
89 /*axis */ 0x75715E,
90 /*series*/ { 0xF92672, 0xA6E22E, 0x66D9EF, 0xFD971F,
91 0xAE81FF, 0xE6DB74, 0x75715E, 0xF8F8F0 },
92 /*gradient*/ { 0x66D9EF, 0xA6E22E, 0xF92672 }
93 };
94
95 // Dracula.
96 inline const theme_t dracula = {
97 /*bg */ 0x282A36,
98 /*panel*/ 0x44475A,
99 /*fg */ 0xF8F8F2,
100 /*grid */ 0x44475A,
101 /*axis */ 0x6272A4,
102 /*series*/ { 0xFF79C6, 0xBD93F9, 0x50FA7B, 0x8BE9FD,
103 0xFFB86C, 0xFF5555, 0xF1FA8C, 0x6272A4 },
104 /*gradient*/ { 0x8BE9FD, 0xBD93F9, 0xFF79C6 }
105 };
106
107 // Nord.
108 inline const theme_t nord = {
109 /*bg */ 0x2E3440,
110 /*panel*/ 0x3B4252,
111 /*fg */ 0xD8DEE9,
112 /*grid */ 0x434C5E,
113 /*axis */ 0x4C566A,
114 /*series*/ { 0x88C0D0, 0x81A1C1, 0xA3BE8C, 0xEBCB8B,
115 0xBF616A, 0xB48EAD, 0xD08770, 0x8FBCBB },
116 /*gradient*/ { 0x88C0D0, 0xEBCB8B, 0xBF616A }
117 };
118
119 // Atom One Dark.
120 inline const theme_t one_dark = {
121 /*bg */ 0x282C34,
122 /*panel*/ 0x21252B,
123 /*fg */ 0xABB2BF,
124 /*grid */ 0x3B4048,
125 /*axis */ 0x5C6370,
126 /*series*/ { 0x61AFEF, 0x98C379, 0xE06C75, 0xE5C07B,
127 0xC678DD, 0x56B6C2, 0xD19A66, 0xABB2BF },
128 /*gradient*/ { 0x61AFEF, 0x98C379, 0xE06C75 }
129 };
130
131 // Gruvbox Dark.
132 inline const theme_t gruvbox_dark = {
133 /*bg */ 0x282828,
134 /*panel*/ 0x3C3836,
135 /*fg */ 0xEBDBB2,
136 /*grid */ 0x504945,
137 /*axis */ 0x665C54,
138 /*series*/ { 0x83A598, 0xB8BB26, 0xFB4934, 0xFABD2F,
139 0xD3869B, 0x8EC07C, 0xFE8019, 0xEBDBB2 },
140 /*gradient*/ { 0x83A598, 0xFABD2F, 0xFB4934 }
141 };
142
143 // Gruvbox Light.
144 inline const theme_t gruvbox_light = {
145 /*bg */ 0xFBF1C7,
146 /*panel*/ 0xEBDBB2,
147 /*fg */ 0x3C3836,
148 /*grid */ 0xD5C4A1,
149 /*axis */ 0x7C6F64,
150 /*series*/ { 0x076678, 0x79740E, 0x9D0006, 0xB57614,
151 0x8F3F71, 0x427B58, 0xAF3A03, 0x3C3836 },
152 /*gradient*/ { 0x076678, 0xB57614, 0x9D0006 }
153 };
154
155 // Tomorrow Night.
156 inline const theme_t tomorrow_night = {
157 /*bg */ 0x1D1F21,
158 /*panel*/ 0x282A2E,
159 /*fg */ 0xC5C8C6,
160 /*grid */ 0x373B41,
161 /*axis */ 0x969896,
162 /*series*/ { 0x81A2BE, 0xB5BD68, 0xCC6666, 0xF0C674,
163 0xB294BB, 0x8ABEB7, 0xDE935F, 0xC5C8C6 },
164 /*gradient*/ { 0x81A2BE, 0xB5BD68, 0xCC6666 }
165 };
166
167 // Night Owl (Sarah Drasner).
168 inline const theme_t night_owl = {
169 /*bg */ 0x011627,
170 /*panel*/ 0x0B2942,
171 /*fg */ 0xD6DEEB,
172 /*grid */ 0x122D42,
173 /*axis */ 0x5F7E97,
174 /*series*/ { 0x82AAFF, 0xADDB67, 0xEF5350, 0xFFCB8B,
175 0xC792EA, 0x7FDBCA, 0xF78C6C, 0xD6DEEB },
176 /*gradient*/ { 0x82AAFF, 0xADDB67, 0xEF5350 }
177 };
178
179 // Material Theme (Mattia Astorino).
180 inline const theme_t material = {
181 /*bg */ 0x263238,
182 /*panel*/ 0x2E3C43,
183 /*fg */ 0xEEFFFF,
184 /*grid */ 0x37474F,
185 /*axis */ 0x546E7A,
186 /*series*/ { 0x82AAFF, 0xC3E88D, 0xF07178, 0xFFCB6B,
187 0xC792EA, 0x89DDFF, 0xF78C6C, 0xEEFFFF },
188 /*gradient*/ { 0x89DDFF, 0xC3E88D, 0xF07178 }
189 };
190
191 // Tokyo Night.
192 inline const theme_t tokyo_night = {
193 /*bg */ 0x1A1B26,
194 /*panel*/ 0x24283B,
195 /*fg */ 0xA9B1D6,
196 /*grid */ 0x2F334D,
197 /*axis */ 0x565F89,
198 /*series*/ { 0x7AA2F7, 0x9ECE6A, 0xF7768E, 0xE0AF68,
199 0xBB9AF7, 0x7DCFFF, 0xFF9E64, 0xA9B1D6 },
200 /*gradient*/ { 0x7AA2F7, 0x9ECE6A, 0xF7768E }
201 };
202
203 // process-global default current theme (header-only inline definition).
204 // The default can be overridden at build time with
205 // -DPLOT_DEFAULT_THEME=<preset> (e.g. nord) — used to regenerate the
206 // theme-aware doxy gallery without editing sources. The runtime default is
207 // unchanged (solarized_dark) unless the macro is defined.
208#ifndef PLOT_DEFAULT_THEME
209#define PLOT_DEFAULT_THEME solarized_dark
210#endif
211 inline theme_t current_theme = PLOT_DEFAULT_THEME;
212
213 // setter for the global default.
214 inline void theme(const theme_t& t){ current_theme = t; }
215
216 // per-call override: `plot::use{ plot::solarized_light }`. Carries a theme as
217 // a named argument that line/scatter/heatmap honour over the global default.
218 struct use {
219 using value_type = tag::theme_t;
220 theme_t value;
221 };
222}
223#endif