Skip to main content

cotis_web/
color.rs

1//! CSS color conversion helpers for HTML drawables.
2//!
3//! These functions turn Cotis color types into CSS color strings used by
4//! [`drawable_defaults`](crate::rendering::drawable_defaults) (`background-color`, `color`, etc.).
5//!
6//! # Default vs feature-gated API
7//!
8//! Without features, all helpers take [`Color`].
9//!
10//! With `complex_color`, fill/background helpers take [`ColorLayer`](cotis_defaults::colors::ColorLayer).
11//! With `complex_colored_text`, [`text_color_css`] takes `ColorLayer`.
12//!
13//! # Gradients
14//!
15//! When features are enabled, only [`ColorLayer::Solid`](cotis_defaults::colors::ColorLayer::Solid)
16//! produces a real CSS color. Other layer variants (linear, radial, layered) currently return
17//! `"transparent"`. Full CSS gradient mapping is on the roadmap.
18
19use cotis_defaults::colors::Color;
20#[cfg(any(feature = "complex_color", feature = "complex_colored_text"))]
21use cotis_defaults::colors::ColorLayer;
22
23/// CSS `background-color` (or fill) for a [`Rectangle`](cotis_defaults::render_commands::Rectangle).
24#[cfg(not(feature = "complex_color"))]
25pub fn rectangle_fill_css(color: &Color) -> String {
26    color_css(color)
27}
28
29#[cfg(feature = "complex_color")]
30pub fn rectangle_fill_css(color: &ColorLayer) -> String {
31    match color {
32        ColorLayer::Solid(c) => color_css(c),
33        _ => "transparent".to_string(),
34    }
35}
36
37/// CSS `color` for a [`Text`](cotis_defaults::render_commands::Text) drawable.
38#[cfg(not(feature = "complex_colored_text"))]
39pub fn text_color_css(color: &Color) -> String {
40    color_css(color)
41}
42
43#[cfg(feature = "complex_colored_text")]
44pub fn text_color_css(color: &ColorLayer) -> String {
45    match color {
46        ColorLayer::Solid(c) => color_css(c),
47        _ => "transparent".to_string(),
48    }
49}
50
51/// CSS background color behind an [`Image`](cotis_defaults::render_commands::Image) drawable.
52#[cfg(not(feature = "complex_color"))]
53pub fn image_background_css(color: &Color) -> String {
54    color_css(color)
55}
56
57#[cfg(feature = "complex_color")]
58pub fn image_background_css(color: &ColorLayer) -> String {
59    match color {
60        ColorLayer::Solid(c) => color_css(c),
61        _ => "transparent".to_string(),
62    }
63}
64
65/// CSS background color for custom HTML host elements.
66#[cfg(not(feature = "complex_color"))]
67pub fn custom_background_css(color: &Color) -> String {
68    color_css(color)
69}
70
71#[cfg(feature = "complex_color")]
72pub fn custom_background_css(color: &ColorLayer) -> String {
73    match color {
74        ColorLayer::Solid(c) => color_css(c),
75        _ => "transparent".to_string(),
76    }
77}
78
79/// Normalizes alpha to the 0.0–1.0 range expected by CSS `rgba()`.
80///
81/// Values greater than `1.0` are treated as 0–255 byte alpha and divided by 255.
82/// Values in `0.0..=1.0` are clamped to that range.
83///
84/// # Examples
85///
86/// ```
87/// use cotis_web::color::css_alpha;
88///
89/// assert!((css_alpha(0.5) - 0.5).abs() < f32::EPSILON);
90/// assert!((css_alpha(128.0) - 128.0 / 255.0).abs() < 0.001);
91/// assert!((css_alpha(300.0) - 1.0).abs() < f32::EPSILON);
92/// ```
93pub fn css_alpha(a: f32) -> f32 {
94    if a > 1.0 {
95        (a / 255.0).clamp(0.0, 1.0)
96    } else {
97        a.clamp(0.0, 1.0)
98    }
99}
100
101/// Formats a Cotis [`Color`] as a CSS `rgba(r, g, b, a)` string.
102///
103/// RGB components are passed through as-is (typically 0–255). Alpha is normalized via
104/// [`css_alpha`].
105///
106/// # Examples
107///
108/// ```
109/// use cotis_defaults::colors::Color;
110/// use cotis_web::color::color_css;
111///
112/// let c = Color::rgba(255.0, 0.0, 128.0, 255.0);
113/// assert_eq!(color_css(&c), "rgba(255, 0, 128, 1)");
114/// ```
115pub fn color_css(c: &Color) -> String {
116    format!("rgba({}, {}, {}, {})", c.r, c.g, c.b, css_alpha(c.a))
117}