orbital-theme 0.1.1

Theme provider and design tokens for Orbital
Documentation
use getset::{Getters, Setters};
use orbital_macros::WriteCSSVars;

#[derive(Clone, WriteCSSVars, Getters, Setters)]
#[getset(get = "pub", set = "pub")]
pub struct CommonTheme {
    font_family_base: String,
    font_family_monospace: String,
    font_family_numeric: String,
    font_family_display: String,

    font_size_base_100: String,
    font_size_base_200: String,
    font_size_base_300: String,
    font_size_base_400: String,
    font_size_base_500: String,
    font_size_base_600: String,
    font_size_base_700: String,
    font_size_base_800: String,
    font_size_base_900: String,
    font_size_base_1000: String,

    line_height_base_200: String,
    line_height_base_300: String,
    line_height_base_400: String,
    line_height_base_500: String,

    font_weight_regular: String,
    font_weight_semibold: String,
    font_weight_bold: String,

    stroke_width_thin: String,
    stroke_width_thick: String,
    stroke_width_thicker: String,
    stroke_width_thickest: String,

    border_radius_none: String,
    border_radius_small: String,
    border_radius_medium: String,
    border_radius_large: String,
    border_radius_x_large: String,
    border_radius_floating: String,
    border_radius_circular: String,

    spacing_horizontal_none: String,
    spacing_horizontal_x_x_s: String,
    spacing_horizontal_x_s: String,
    spacing_horizontal_s_nudge: String,
    spacing_horizontal_s: String,
    spacing_horizontal_m_nudge: String,
    spacing_horizontal_m: String,
    spacing_horizontal_l: String,
    spacing_horizontal_x_l: String,
    spacing_horizontal_x_x_l: String,
    spacing_horizontal_x_x_x_l: String,
    spacing_vertical_none: String,
    spacing_vertical_x_x_s: String,
    spacing_vertical_x_s: String,
    spacing_vertical_s_nudge: String,
    spacing_vertical_s: String,
    spacing_vertical_m_nudge: String,
    spacing_vertical_m: String,
    spacing_vertical_l: String,
    spacing_vertical_x_l: String,
    spacing_vertical_x_x_l: String,
    spacing_vertical_x_x_x_l: String,

    duration_ultra_fast: String,
    duration_faster: String,
    duration_normal: String,
    duration_gentle: String,
    duration_slow: String,
    curve_accelerate_mid: String,
    curve_decelerate_max: String,
    curve_decelerate_mid: String,
    curve_easy_ease: String,
    curve_easy_ease_max: String,
}

impl Default for CommonTheme {
    fn default() -> Self {
        Self::new()
    }
}

impl CommonTheme {
    pub fn new() -> Self {
        Self {
            font_family_base:
                "'League Spartan', ui-sans-serif, system-ui, -apple-system, sans-serif".into(),
            font_family_monospace: "'League Mono', ui-monospace, monospace".into(),
            font_family_numeric: "'League Spartan', ui-sans-serif, system-ui, sans-serif".into(),
            font_family_display:
                "'Orbitron', 'League Spartan', ui-sans-serif, system-ui, sans-serif".into(),

            font_size_base_100: "10px".into(),
            font_size_base_200: "12px".into(),
            font_size_base_300: "14px".into(),
            font_size_base_400: "16px".into(),
            font_size_base_500: "20px".into(),
            font_size_base_600: "24px".into(),
            font_size_base_700: "28px".into(),
            font_size_base_800: "32px".into(),
            font_size_base_900: "40px".into(),
            font_size_base_1000: "60px".into(),

            line_height_base_200: "16px".into(),
            line_height_base_300: "20px".into(),
            line_height_base_400: "22px".into(),
            line_height_base_500: "28px".into(),

            font_weight_regular: "400".into(),
            font_weight_semibold: "600".into(),
            font_weight_bold: "700".into(),

            stroke_width_thin: "1px".into(),
            stroke_width_thick: "2px".into(),
            stroke_width_thicker: "3px".into(),
            stroke_width_thickest: "4px".into(),

            border_radius_none: "0".into(),
            border_radius_small: "2px".into(),
            border_radius_medium: "4px".into(),
            border_radius_large: "6px".into(),
            border_radius_x_large: "8px".into(),
            border_radius_floating: "16px".into(),
            border_radius_circular: "10000px".into(),

            spacing_horizontal_none: "0".into(),
            spacing_horizontal_x_x_s: "2px".into(),
            spacing_horizontal_x_s: "4px".into(),
            spacing_horizontal_s_nudge: "6px".into(),
            spacing_horizontal_s: "8px".into(),
            spacing_horizontal_m_nudge: "10px".into(),
            spacing_horizontal_m: "12px".into(),
            spacing_horizontal_l: "16px".into(),
            spacing_horizontal_x_l: "20px".into(),
            spacing_horizontal_x_x_l: "24px".into(),
            spacing_horizontal_x_x_x_l: "32px".into(),
            spacing_vertical_none: "0".into(),
            spacing_vertical_x_x_s: "2px".into(),
            spacing_vertical_x_s: "4px".into(),
            spacing_vertical_s_nudge: "6px".into(),
            spacing_vertical_s: "8px".into(),
            spacing_vertical_m_nudge: "10px".into(),
            spacing_vertical_m: "12px".into(),
            spacing_vertical_l: "16px".into(),
            spacing_vertical_x_l: "20px".into(),
            spacing_vertical_x_x_l: "24px".into(),
            spacing_vertical_x_x_x_l: "32px".into(),

            duration_ultra_fast: "50ms".into(),
            duration_faster: "100ms".into(),
            duration_normal: "200ms".into(),
            duration_gentle: "250ms".into(),
            duration_slow: "300ms".into(),
            curve_accelerate_mid: "cubic-bezier(1,0,1,1)".into(),
            curve_decelerate_max: "cubic-bezier(0.1,0.9,0.2,1)".into(),
            curve_decelerate_mid: "cubic-bezier(0,0,0,1)".into(),
            curve_easy_ease: "cubic-bezier(0.33,0,0.67,1)".into(),
            curve_easy_ease_max: "cubic-bezier(0.8,0,0.2,1)".into(),
        }
    }

    /// Emits Orbital `--orb-motion-*` tokens plus legacy `--duration*` / `--curve*` aliases.
    pub fn write_orb_motion_css_vars(&self, css_vars: &mut String) {
        const DURATION_FAST: &str = "150ms";
        const DURATION_SLOWER: &str = "400ms";
        const DURATION_ULTRA_SLOW: &str = "500ms";
        const CURVE_LINEAR: &str = "cubic-bezier(0, 0, 1, 1)";
        const CURVE_ACCELERATE_MAX: &str = "cubic-bezier(0.9, 0.1, 1, 0.2)";
        const CURVE_ACCELERATE_MIN: &str = "cubic-bezier(0.8, 0, 0.78, 1)";
        const CURVE_DECELERATE_MIN: &str = "cubic-bezier(0.33, 0, 0.1, 1)";

        let durations = [
            (
                "--orb-motion-duration-2xs",
                "--durationUltraFast",
                self.duration_ultra_fast.as_str(),
            ),
            (
                "--orb-motion-duration-xs",
                "--durationFaster",
                self.duration_faster.as_str(),
            ),
            ("--orb-motion-duration-sm", "--durationFast", DURATION_FAST),
            (
                "--orb-motion-duration-md",
                "--durationNormal",
                self.duration_normal.as_str(),
            ),
            (
                "--orb-motion-duration-lg",
                "--durationGentle",
                self.duration_gentle.as_str(),
            ),
            (
                "--orb-motion-duration-xl",
                "--durationSlow",
                self.duration_slow.as_str(),
            ),
            (
                "--orb-motion-duration-2xl",
                "--durationSlower",
                DURATION_SLOWER,
            ),
            (
                "--orb-motion-duration-3xl",
                "--durationUltraSlow",
                DURATION_ULTRA_SLOW,
            ),
        ];

        for (orb, legacy, value) in durations {
            css_vars.push_str(&format!("{orb}: {value};"));
            css_vars.push_str(&format!("{legacy}: {value};"));
        }

        let curves = [
            ("--orb-motion-ease-linear", "--curveLinear", CURVE_LINEAR),
            (
                "--orb-motion-ease-accelerate-strong",
                "--curveAccelerateMax",
                CURVE_ACCELERATE_MAX,
            ),
            (
                "--orb-motion-ease-accelerate",
                "--curveAccelerateMid",
                self.curve_accelerate_mid.as_str(),
            ),
            (
                "--orb-motion-ease-accelerate-subtle",
                "--curveAccelerateMin",
                CURVE_ACCELERATE_MIN,
            ),
            (
                "--orb-motion-ease-decelerate-strong",
                "--curveDecelerateMax",
                self.curve_decelerate_max.as_str(),
            ),
            (
                "--orb-motion-ease-decelerate",
                "--curveDecelerateMid",
                self.curve_decelerate_mid.as_str(),
            ),
            (
                "--orb-motion-ease-decelerate-subtle",
                "--curveDecelerateMin",
                CURVE_DECELERATE_MIN,
            ),
            (
                "--orb-motion-ease-standard",
                "--curveEasyEase",
                self.curve_easy_ease.as_str(),
            ),
            (
                "--orb-motion-ease-emphasis",
                "--curveEasyEaseMax",
                self.curve_easy_ease_max.as_str(),
            ),
        ];

        for (orb, legacy, value) in curves {
            css_vars.push_str(&format!("{orb}: {value};"));
            css_vars.push_str(&format!("{legacy}: {value};"));
        }
    }
}