Skip to main content

kozan_core/styling/
units.rs

1//! CSS unit helpers — `px(100.0)`, `pct(50.0)`, `em(1.5)`, etc.
2//!
3//! These create Stylo specified types directly. No CSS parsing needed.
4//!
5//! ```ignore
6//! use kozan::prelude::*;
7//!
8//! div.style().width(px(200.0));
9//! div.style().height(pct(100.0));
10//! div.style().margin_top(em(1.5));
11//! ```
12
13use style::values::generics::NonNegative;
14use style::values::generics::length::{GenericMargin, GenericSize};
15use style::values::specified::length::{AbsoluteLength, FontRelativeLength, NoCalcLength};
16use style::values::specified::{self, LengthPercentage, NonNegativeLengthPercentage};
17
18/// A CSS dimension value that can be used in any property context.
19///
20/// This is the user-facing type. Implements `Into<Size>`, `Into<Margin>`,
21/// `Into<NonNegativeLengthPercentage>` so it can be passed to any
22/// `StyleAccess` method.
23#[derive(Debug, Clone, Copy)]
24pub enum CssValue {
25    /// A pixel value (e.g., `200px`).
26    Px(f32),
27    /// A percentage (e.g., `50%`).
28    Pct(f32),
29    /// An em value (e.g., `1.5em`).
30    Em(f32),
31    /// A rem value (e.g., `2rem`).
32    Rem(f32),
33    /// A viewport-width value (e.g., `100vw`).
34    Vw(f32),
35    /// A viewport-height value (e.g., `100vh`).
36    Vh(f32),
37    /// The `auto` keyword.
38    Auto,
39}
40
41// ---- Constructor functions ----
42
43/// Create a pixel value: `px(200.0)` → `200px`.
44#[must_use]
45pub fn px(v: f32) -> CssValue {
46    CssValue::Px(v)
47}
48
49/// Create a percentage: `pct(50.0)` → `50%`.
50#[must_use]
51pub fn pct(v: f32) -> CssValue {
52    CssValue::Pct(v)
53}
54
55/// Create an em value: `em(1.5)` → `1.5em`.
56#[must_use]
57pub fn em(v: f32) -> CssValue {
58    CssValue::Em(v)
59}
60
61/// Create a rem value: `rem(2.0)` → `2rem`.
62#[must_use]
63pub fn rem(v: f32) -> CssValue {
64    CssValue::Rem(v)
65}
66
67/// Create a viewport-width value: `vw(100.0)` → `100vw`.
68#[must_use]
69pub fn vw(v: f32) -> CssValue {
70    CssValue::Vw(v)
71}
72
73/// Create a viewport-height value: `vh(100.0)` → `100vh`.
74#[must_use]
75pub fn vh(v: f32) -> CssValue {
76    CssValue::Vh(v)
77}
78
79/// The `auto` keyword.
80#[must_use]
81pub fn auto() -> CssValue {
82    CssValue::Auto
83}
84
85// ---- Internal: convert to Stylo's LengthPercentage ----
86
87fn to_length_percentage(v: CssValue) -> LengthPercentage {
88    match v {
89        CssValue::Px(n) => LengthPercentage::Length(NoCalcLength::Absolute(AbsoluteLength::Px(n))),
90        CssValue::Pct(n) => {
91            LengthPercentage::Percentage(style::values::computed::Percentage(n / 100.0))
92        }
93        CssValue::Em(n) => {
94            LengthPercentage::Length(NoCalcLength::FontRelative(FontRelativeLength::Em(n)))
95        }
96        CssValue::Rem(n) => {
97            LengthPercentage::Length(NoCalcLength::FontRelative(FontRelativeLength::Rem(n)))
98        }
99        CssValue::Vw(n) => LengthPercentage::Length(NoCalcLength::ViewportPercentage(
100            style::values::specified::length::ViewportPercentageLength::Vw(n),
101        )),
102        CssValue::Vh(n) => LengthPercentage::Length(NoCalcLength::ViewportPercentage(
103            style::values::specified::length::ViewportPercentageLength::Vh(n),
104        )),
105        CssValue::Auto => {
106            // Auto doesn't map to LengthPercentage — fallback to 0px.
107            LengthPercentage::Length(NoCalcLength::Absolute(AbsoluteLength::Px(0.0)))
108        }
109    }
110}
111
112// ---- Conversions to Stylo specified types ----
113
114/// Into `Size` (for width/height): supports auto + length/percentage.
115impl From<CssValue> for specified::Size {
116    fn from(v: CssValue) -> Self {
117        match v {
118            CssValue::Auto => GenericSize::Auto,
119            other => GenericSize::LengthPercentage(NonNegative(to_length_percentage(other))),
120        }
121    }
122}
123
124/// Into `Margin` (for margin-*): supports auto + length/percentage.
125impl From<CssValue> for specified::Margin {
126    fn from(v: CssValue) -> Self {
127        match v {
128            CssValue::Auto => GenericMargin::Auto,
129            other => GenericMargin::LengthPercentage(to_length_percentage(other)),
130        }
131    }
132}
133
134/// Into `NonNegativeLengthPercentage` (for padding-*): no auto.
135impl From<CssValue> for NonNegativeLengthPercentage {
136    fn from(v: CssValue) -> Self {
137        NonNegative(to_length_percentage(v))
138    }
139}
140
141// ---- Color helpers ----
142
143use style::color::AbsoluteColor;
144
145/// Create an sRGB color from f32 [0.0, 1.0]: `rgb(0.9, 0.3, 0.2)`.
146#[must_use]
147pub fn rgb(r: f32, g: f32, b: f32) -> AbsoluteColor {
148    AbsoluteColor::new(style::color::ColorSpace::Srgb, r, g, b, 1.0)
149}
150
151/// Create an sRGB color with alpha: `rgba(0.9, 0.3, 0.2, 0.5)`.
152#[must_use]
153pub fn rgba(r: f32, g: f32, b: f32, a: f32) -> AbsoluteColor {
154    AbsoluteColor::new(style::color::ColorSpace::Srgb, r, g, b, a)
155}
156
157/// Create a color from 0-255 u8 values: `rgb8(232, 76, 61)`.
158#[must_use]
159pub fn rgb8(r: u8, g: u8, b: u8) -> AbsoluteColor {
160    AbsoluteColor::srgb_legacy(r, g, b, 1.0)
161}
162
163/// Create a color from hex: `hex(0xE84C3D)`.
164#[must_use]
165pub fn hex(v: u32) -> AbsoluteColor {
166    let r = ((v >> 16) & 0xFF) as u8;
167    let g = ((v >> 8) & 0xFF) as u8;
168    let b = (v & 0xFF) as u8;
169    AbsoluteColor::srgb_legacy(r, g, b, 1.0)
170}