#![forbid(unsafe_code)]
#![warn(missing_docs)]
use oxiui_core::{Color, FontSpec, Palette, Theme};
pub mod high_contrast;
pub use high_contrast::{cooljapan_high_contrast, cooljapan_high_contrast_light};
pub mod anim_tokens;
pub mod breakpoint;
pub mod builder;
pub mod color;
pub mod compile;
pub mod gallery;
pub mod icons;
pub mod inheritance;
pub mod lazy_palette;
pub mod manager;
pub mod overlay;
pub mod palette_ext;
pub mod spec;
pub mod style_cache;
pub mod stylesheet;
pub mod tokens;
pub mod typography;
pub use anim_tokens::{
fade_in, scale_up, slide_in, AnimationKeyframe, AnimationSpec, EasingKind, FillMode,
IterationCount, TransitionSpec,
};
pub use breakpoint::Breakpoint;
pub use builder::{ContrastWarning, PaletteBuilder, ValidationResult, WcagLevel};
pub use compile::CompiledStyleSheet;
pub use gallery::{
make_catppuccin_latte, make_catppuccin_mocha, make_dracula, make_material_dark,
make_material_light, make_nord_dark, make_nord_light, make_solarized_dark,
make_solarized_light,
};
pub use icons::{BuiltinIcons, IconName, IconSet, IconVariant};
pub use inheritance::resolve as resolve_inheritance;
pub use lazy_palette::LazyPaletteVariants;
pub use manager::{ThemeListener, ThemeManager};
pub use overlay::{overlay, PartialTheme};
pub use palette_ext::ExtendedPalette;
pub use spec::{
elevation_shadow, elevation_shadows, elevation_to_shadow, BorderSpec, BorderSpecs, BorderStyle,
ShadowSpec,
};
pub use style_cache::StyleCache;
pub use stylesheet::{
ComputedStyle, CssValue, ParseDiagnostic, ParseResult, Rule, Selector, SelectorPart,
Specificity, StyleSheet,
};
pub use tokens::{DesignTokens, RadiusStep, SpacingStep};
pub use typography::{TextStyleToken, TypographyScale};
#[derive(Clone, Debug)]
pub struct CooljapanTheme {
palette: Palette,
font: FontSpec,
}
impl CooljapanTheme {
pub fn new(palette: Palette, font: FontSpec) -> Self {
Self { palette, font }
}
}
impl Theme for CooljapanTheme {
fn palette(&self) -> &Palette {
&self.palette
}
fn font(&self) -> &FontSpec {
&self.font
}
}
pub trait ThemeExt: Theme {
fn tokens(&self) -> DesignTokens {
DesignTokens::default()
}
fn design_tokens(&self) -> &DesignTokens {
static DEFAULT: std::sync::OnceLock<DesignTokens> = std::sync::OnceLock::new();
DEFAULT.get_or_init(DesignTokens::default)
}
fn typography(&self) -> TypographyScale {
TypographyScale::default()
}
fn typography_ref(&self) -> &TypographyScale {
static DEFAULT: std::sync::OnceLock<TypographyScale> = std::sync::OnceLock::new();
DEFAULT.get_or_init(TypographyScale::default)
}
fn is_high_contrast(&self) -> bool {
false
}
fn effective_palette(&self) -> Palette {
let prefs_high_contrast = os_prefers_high_contrast();
if prefs_high_contrast && !self.is_high_contrast() {
let mut p = self.palette().clone();
p.background = blend_to_black(p.background, 0.1);
p.surface = blend_to_black(p.surface, 0.05);
p
} else {
self.palette().clone()
}
}
fn extended_palette(&self) -> ExtendedPalette {
let p = self.palette();
let bg = p.background;
let luma = color::wcag_luminance(bg.0, bg.1, bg.2);
ExtendedPalette::derive(p.clone(), luma < 0.5)
}
}
impl<T: Theme + ?Sized> ThemeExt for T {}
pub fn os_prefers_high_contrast() -> bool {
std::env::var("OXIUI_HIGH_CONTRAST")
.map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
.unwrap_or(false)
}
pub fn os_prefers_reduced_motion() -> bool {
std::env::var("OXIUI_REDUCED_MOTION")
.map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
.unwrap_or(false)
}
fn blend_to_black(color: oxiui_core::Color, factor: f32) -> oxiui_core::Color {
let factor = factor.clamp(0.0, 1.0);
let scale = 1.0 - factor;
oxiui_core::Color(
(color.0 as f32 * scale).round() as u8,
(color.1 as f32 * scale).round() as u8,
(color.2 as f32 * scale).round() as u8,
color.3,
)
}
fn make_dark() -> Box<dyn Theme> {
Box::new(CooljapanTheme::new(
Palette {
background: Color(26, 27, 38, 255), surface: Color(36, 40, 59, 255), primary: Color(122, 162, 247, 255), on_primary: Color(26, 27, 38, 255), text: Color(192, 202, 245, 255), muted: Color(86, 95, 137, 255), },
FontSpec::new("Inter", 14.0, 400),
))
}
fn make_light() -> Box<dyn Theme> {
Box::new(CooljapanTheme::new(
Palette {
background: Color(216, 218, 228, 255), surface: Color(255, 255, 255, 255), primary: Color(68, 100, 200, 255), on_primary: Color(255, 255, 255, 255),
text: Color(30, 35, 60, 255), muted: Color(120, 130, 155, 255),
},
FontSpec::new("Inter", 14.0, 400),
))
}
pub fn cooljapan_default() -> Box<dyn Theme> {
make_dark()
}
pub fn dark() -> Box<dyn Theme> {
make_dark()
}
pub fn light() -> Box<dyn Theme> {
make_light()
}