pub struct ThemeSpec {
pub name: String,
pub light: Option<ThemeVariant>,
pub dark: Option<ThemeVariant>,
}Expand description
A complete native theme with a name and optional light/dark variants.
This is the top-level type that theme files deserialize into and that platform readers produce.
§Examples
use native_theme::ThemeSpec;
// Load a bundled preset
let theme = ThemeSpec::preset("dracula").unwrap();
assert_eq!(theme.name, "Dracula");
// Parse from a TOML string
let toml = r##"
name = "Custom"
[light.defaults]
accent = "#ff6600"
"##;
let custom = ThemeSpec::from_toml(toml).unwrap();
assert_eq!(custom.name, "Custom");
// Merge themes (overlay wins for populated fields)
let mut base = ThemeSpec::preset("catppuccin-mocha").unwrap();
base.merge(&custom);
assert_eq!(base.name, "Catppuccin Mocha"); // base name is preservedFields§
§name: StringTheme name (e.g., “Breeze”, “Adwaita”, “Windows 11”).
light: Option<ThemeVariant>Light variant of the theme.
dark: Option<ThemeVariant>Dark variant of the theme.
Implementations§
Source§impl ThemeSpec
impl ThemeSpec
Sourcepub fn new(name: impl Into<String>) -> Self
pub fn new(name: impl Into<String>) -> Self
Create a new theme with the given name and no variants.
Sourcepub fn merge(&mut self, overlay: &Self)
pub fn merge(&mut self, overlay: &Self)
Merge an overlay theme into this theme.
The base name is kept. For each variant (light/dark):
- If both base and overlay have a variant, they are merged recursively.
- If only the overlay has a variant, it is cloned into the base.
- If only the base has a variant (or neither), no change.
Sourcepub fn pick_variant(&self, is_dark: bool) -> Option<&ThemeVariant>
pub fn pick_variant(&self, is_dark: bool) -> Option<&ThemeVariant>
Pick the appropriate variant for the given mode, with cross-fallback.
When is_dark is true, prefers dark and falls back to light.
When is_dark is false, prefers light and falls back to dark.
Returns None only if the theme has no variants at all.
Sourcepub fn into_variant(self, is_dark: bool) -> Option<ThemeVariant>
pub fn into_variant(self, is_dark: bool) -> Option<ThemeVariant>
Extract a variant by consuming the theme, avoiding a clone.
When is_dark is true, returns the dark variant (falling back to
light). When false, returns light (falling back to dark).
Returns None only if the theme has no variants at all.
Use this when you own the ThemeSpec and don’t need it afterward.
For read-only inspection, use pick_variant().
§Examples
let theme = native_theme::ThemeSpec::preset("dracula").unwrap();
let variant = theme.into_variant(true).unwrap();
let resolved = variant.into_resolved().unwrap();Sourcepub fn preset(name: &str) -> Result<Self>
pub fn preset(name: &str) -> Result<Self>
Load a bundled theme preset by name.
Returns the preset as a fully populated ThemeSpec with both
light and dark variants.
§Errors
Returns crate::Error::Unavailable if the preset name is not recognized.
§Examples
let theme = native_theme::ThemeSpec::preset("catppuccin-mocha").unwrap();
assert!(theme.light.is_some());Sourcepub fn from_toml(toml_str: &str) -> Result<Self>
pub fn from_toml(toml_str: &str) -> Result<Self>
Parse a TOML string into a ThemeSpec.
§TOML Format
Theme files use the following structure. All fields are Option<T> –
omit any field you don’t need. Unknown fields are silently ignored.
Hex colors accept #RRGGBB or #RRGGBBAA format.
name = "My Theme"
[light.defaults]
accent = "#4a90d9"
background = "#fafafa"
foreground = "#2e3436"
surface = "#ffffff"
border = "#c0c0c0"
muted = "#929292"
shadow = "#00000018"
danger = "#dc3545"
warning = "#f0ad4e"
success = "#28a745"
info = "#4a90d9"
selection = "#4a90d9"
selection_foreground = "#ffffff"
link = "#2a6cb6"
focus_ring_color = "#4a90d9"
disabled_foreground = "#c0c0c0"
radius = 6.0
radius_lg = 12.0
frame_width = 1.0
disabled_opacity = 0.5
border_opacity = 0.15
shadow_enabled = true
[light.defaults.font]
family = "sans-serif"
size = 10.0
[light.defaults.mono_font]
family = "monospace"
size = 10.0
[light.defaults.spacing]
xxs = 2.0
xs = 4.0
s = 6.0
m = 12.0
l = 18.0
xl = 24.0
xxl = 36.0
[light.button]
background = "#e8e8e8"
foreground = "#2e3436"
min_height = 32.0
padding_horizontal = 12.0
padding_vertical = 6.0
[light.tooltip]
background = "#2e3436"
foreground = "#f0f0f0"
padding_horizontal = 6.0
padding_vertical = 6.0
# [dark.*] mirrors the same structure as [light.*]§Errors
Returns crate::Error::Format if the TOML is invalid.
§Examples
let toml = r##"
name = "My Theme"
[light.defaults]
accent = "#ff0000"
"##;
let theme = native_theme::ThemeSpec::from_toml(toml).unwrap();
assert_eq!(theme.name, "My Theme");Sourcepub fn from_toml_with_base(toml_str: &str, base: &str) -> Result<Self>
pub fn from_toml_with_base(toml_str: &str, base: &str) -> Result<Self>
Parse custom TOML and merge onto a base preset.
This is the recommended way to create custom themes. The base preset provides geometry, spacing, and widget defaults. The custom TOML overrides colors, fonts, and any other fields.
§Errors
Returns crate::Error::Unavailable if the base preset name is not
recognized, or crate::Error::Format if the custom TOML is invalid.
§Examples
let theme = native_theme::ThemeSpec::from_toml_with_base(
r##"name = "My Theme"
[dark.defaults]
accent = "#ff6600"
background = "#1e1e1e"
foreground = "#e0e0e0""##,
"material",
).unwrap();
assert!(theme.dark.is_some());Sourcepub fn from_file(path: impl AsRef<Path>) -> Result<Self>
pub fn from_file(path: impl AsRef<Path>) -> Result<Self>
Load a ThemeSpec from a TOML file.
§Errors
Returns crate::Error::Unavailable if the file cannot be read.
§Examples
let theme = native_theme::ThemeSpec::from_file("my-theme.toml").unwrap();Sourcepub fn list_presets() -> &'static [&'static str]
pub fn list_presets() -> &'static [&'static str]
List all available bundled preset names.
§Examples
let names = native_theme::ThemeSpec::list_presets();
assert_eq!(names.len(), 16);Sourcepub fn list_presets_for_platform() -> Vec<&'static str>
pub fn list_presets_for_platform() -> Vec<&'static str>
List preset names appropriate for the current platform.
Platform-specific presets (kde-breeze, adwaita, windows-11, macos-sonoma, ios) are only included on their native platform. Community themes are always included.
§Examples
let names = native_theme::ThemeSpec::list_presets_for_platform();
// On Linux KDE: includes kde-breeze, adwaita, plus all community themes
// On Windows: includes windows-11 plus all community themes
assert!(!names.is_empty());Sourcepub fn to_toml(&self) -> Result<String>
pub fn to_toml(&self) -> Result<String>
Serialize this theme to a TOML string.
§Errors
Returns crate::Error::Format if serialization fails.
§Examples
let theme = native_theme::ThemeSpec::preset("catppuccin-mocha").unwrap();
let toml_str = theme.to_toml().unwrap();
assert!(toml_str.contains("name = \"Catppuccin Mocha\""));Sourcepub fn lint_toml(toml_str: &str) -> Result<Vec<String>>
pub fn lint_toml(toml_str: &str) -> Result<Vec<String>>
Check a TOML string for unrecognized field names.
Parses the TOML as a generic table and walks all keys, comparing
against the known fields for each section. Returns a Vec<String>
of warnings for any keys that don’t match a known field. An empty
vec means all keys are recognized.
This is an opt-in linting tool for theme authors. It does NOT affect
from_toml() behavior (which silently ignores unknown fields via serde).
§Errors
Returns Err if the TOML string cannot be parsed at all.
§Examples
let warnings = native_theme::ThemeSpec::lint_toml(r##"
name = "Test"
[light.defaults]
backround = "#ffffff"
"##).unwrap();
assert_eq!(warnings.len(), 1);
assert!(warnings[0].contains("backround"));