Skip to main content

rosace_theme/
shadow.rs

1//! Shadow tokens — elevation levels expressed as named shadow descriptors.
2
3use crate::color::Color;
4
5/// A single drop-shadow descriptor.
6#[derive(Debug, Clone, Copy)]
7pub struct ShadowLayer {
8    /// Horizontal offset in logical pixels.
9    pub offset_x: f32,
10    /// Vertical offset in logical pixels.
11    pub offset_y: f32,
12    /// Blur radius in logical pixels.
13    pub blur: f32,
14    /// Spread radius in logical pixels.
15    pub spread: f32,
16    /// Shadow color (includes opacity via alpha channel).
17    pub color: Color,
18}
19
20impl ShadowLayer {
21    /// A shadow layer with no visible effect.
22    pub const NONE: Self = Self {
23        offset_x: 0.0,
24        offset_y: 0.0,
25        blur: 0.0,
26        spread: 0.0,
27        color: Color::TRANSPARENT,
28    };
29}
30
31/// Named elevation levels, each expressed as a `ShadowLayer`.
32#[derive(Debug, Clone, Copy)]
33pub struct Shadows {
34    /// No shadow (elevation 0).
35    pub none: ShadowLayer,
36    /// Subtle shadow (elevation 1).
37    pub sm: ShadowLayer,
38    /// Medium shadow (elevation 2).
39    pub md: ShadowLayer,
40    /// Pronounced shadow (elevation 3).
41    pub lg: ShadowLayer,
42}
43
44impl Default for Shadows {
45    fn default() -> Self {
46        let shadow_color = Color::BLACK.with_alpha(0.15);
47        Self {
48            none: ShadowLayer::NONE,
49            sm: ShadowLayer {
50                offset_x: 0.0,
51                offset_y: 1.0,
52                blur: 3.0,
53                spread: 0.0,
54                color: shadow_color,
55            },
56            md: ShadowLayer {
57                offset_x: 0.0,
58                offset_y: 2.0,
59                blur: 6.0,
60                spread: 0.0,
61                color: shadow_color,
62            },
63            lg: ShadowLayer {
64                offset_x: 0.0,
65                offset_y: 4.0,
66                blur: 12.0,
67                spread: 0.0,
68                color: shadow_color,
69            },
70        }
71    }
72}
73
74// ---------------------------------------------------------------------------
75// Tests
76// ---------------------------------------------------------------------------
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[test]
83    fn shadow_none_is_transparent() {
84        let s = Shadows::default();
85        assert_eq!(s.none.color.a, 0.0);
86        assert_eq!(s.none.blur, 0.0);
87    }
88
89    #[test]
90    fn shadow_blur_is_ascending() {
91        let s = Shadows::default();
92        assert!(s.none.blur < s.sm.blur);
93        assert!(s.sm.blur < s.md.blur);
94        assert!(s.md.blur < s.lg.blur);
95    }
96}