tailwind_rs_core/utilities/effects/
blend_modes.rs

1//! Blend mode utilities for tailwind-rs
2//!
3//! This module provides utilities for mix blend mode and background blend mode effects.
4
5use crate::classes::ClassBuilder;
6use serde::{Deserialize, Serialize};
7use std::fmt;
8
9/// Mix blend mode values
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11pub enum MixBlendMode {
12    /// Normal blend
13    Normal,
14    /// Multiply blend
15    Multiply,
16    /// Screen blend
17    Screen,
18    /// Overlay blend
19    Overlay,
20    /// Darken blend
21    Darken,
22    /// Lighten blend
23    Lighten,
24    /// Color dodge blend
25    ColorDodge,
26    /// Color burn blend
27    ColorBurn,
28    /// Hard light blend
29    HardLight,
30    /// Soft light blend
31    SoftLight,
32    /// Difference blend
33    Difference,
34    /// Exclusion blend
35    Exclusion,
36    /// Hue blend
37    Hue,
38    /// Saturation blend
39    Saturation,
40    /// Color blend
41    Color,
42    /// Luminosity blend
43    Luminosity,
44}
45
46/// Background blend mode values
47#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
48pub enum BackgroundBlendMode {
49    /// Normal blend
50    Normal,
51    /// Multiply blend
52    Multiply,
53    /// Screen blend
54    Screen,
55    /// Overlay blend
56    Overlay,
57    /// Darken blend
58    Darken,
59    /// Lighten blend
60    Lighten,
61    /// Color dodge blend
62    ColorDodge,
63    /// Color burn blend
64    ColorBurn,
65    /// Hard light blend
66    HardLight,
67    /// Soft light blend
68    SoftLight,
69    /// Difference blend
70    Difference,
71    /// Exclusion blend
72    Exclusion,
73    /// Hue blend
74    Hue,
75    /// Saturation blend
76    Saturation,
77    /// Color blend
78    Color,
79    /// Luminosity blend
80    Luminosity,
81}
82
83impl fmt::Display for MixBlendMode {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        match self {
86            MixBlendMode::Normal => write!(f, "mix-blend-normal"),
87            MixBlendMode::Multiply => write!(f, "mix-blend-multiply"),
88            MixBlendMode::Screen => write!(f, "mix-blend-screen"),
89            MixBlendMode::Overlay => write!(f, "mix-blend-overlay"),
90            MixBlendMode::Darken => write!(f, "mix-blend-darken"),
91            MixBlendMode::Lighten => write!(f, "mix-blend-lighten"),
92            MixBlendMode::ColorDodge => write!(f, "mix-blend-color-dodge"),
93            MixBlendMode::ColorBurn => write!(f, "mix-blend-color-burn"),
94            MixBlendMode::HardLight => write!(f, "mix-blend-hard-light"),
95            MixBlendMode::SoftLight => write!(f, "mix-blend-soft-light"),
96            MixBlendMode::Difference => write!(f, "mix-blend-difference"),
97            MixBlendMode::Exclusion => write!(f, "mix-blend-exclusion"),
98            MixBlendMode::Hue => write!(f, "mix-blend-hue"),
99            MixBlendMode::Saturation => write!(f, "mix-blend-saturation"),
100            MixBlendMode::Color => write!(f, "mix-blend-color"),
101            MixBlendMode::Luminosity => write!(f, "mix-blend-luminosity"),
102        }
103    }
104}
105
106impl fmt::Display for BackgroundBlendMode {
107    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108        match self {
109            BackgroundBlendMode::Normal => write!(f, "bg-blend-normal"),
110            BackgroundBlendMode::Multiply => write!(f, "bg-blend-multiply"),
111            BackgroundBlendMode::Screen => write!(f, "bg-blend-screen"),
112            BackgroundBlendMode::Overlay => write!(f, "bg-blend-overlay"),
113            BackgroundBlendMode::Darken => write!(f, "bg-blend-darken"),
114            BackgroundBlendMode::Lighten => write!(f, "bg-blend-lighten"),
115            BackgroundBlendMode::ColorDodge => write!(f, "bg-blend-color-dodge"),
116            BackgroundBlendMode::ColorBurn => write!(f, "bg-blend-color-burn"),
117            BackgroundBlendMode::HardLight => write!(f, "bg-blend-hard-light"),
118            BackgroundBlendMode::SoftLight => write!(f, "bg-blend-soft-light"),
119            BackgroundBlendMode::Difference => write!(f, "bg-blend-difference"),
120            BackgroundBlendMode::Exclusion => write!(f, "bg-blend-exclusion"),
121            BackgroundBlendMode::Hue => write!(f, "bg-blend-hue"),
122            BackgroundBlendMode::Saturation => write!(f, "bg-blend-saturation"),
123            BackgroundBlendMode::Color => write!(f, "bg-blend-color"),
124            BackgroundBlendMode::Luminosity => write!(f, "bg-blend-luminosity"),
125        }
126    }
127}
128
129/// Trait for adding mix blend mode utilities to a class builder
130pub trait MixBlendModeUtilities {
131    fn mix_blend_mode(self, mode: MixBlendMode) -> Self;
132}
133
134/// Trait for adding background blend mode utilities to a class builder
135pub trait BackgroundBlendModeUtilities {
136    fn background_blend_mode(self, mode: BackgroundBlendMode) -> Self;
137}
138
139impl MixBlendModeUtilities for ClassBuilder {
140    fn mix_blend_mode(self, mode: MixBlendMode) -> Self {
141        self.class(mode.to_string())
142    }
143}
144
145impl BackgroundBlendModeUtilities for ClassBuilder {
146    fn background_blend_mode(self, mode: BackgroundBlendMode) -> Self {
147        self.class(mode.to_string())
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use super::*;
154
155    #[test]
156    fn test_mix_blend_mode_display() {
157        assert_eq!(MixBlendMode::Normal.to_string(), "mix-blend-normal");
158        assert_eq!(MixBlendMode::Multiply.to_string(), "mix-blend-multiply");
159        assert_eq!(MixBlendMode::Screen.to_string(), "mix-blend-screen");
160        assert_eq!(MixBlendMode::Overlay.to_string(), "mix-blend-overlay");
161        assert_eq!(MixBlendMode::Darken.to_string(), "mix-blend-darken");
162        assert_eq!(MixBlendMode::Lighten.to_string(), "mix-blend-lighten");
163        assert_eq!(
164            MixBlendMode::ColorDodge.to_string(),
165            "mix-blend-color-dodge"
166        );
167        assert_eq!(MixBlendMode::ColorBurn.to_string(), "mix-blend-color-burn");
168        assert_eq!(MixBlendMode::HardLight.to_string(), "mix-blend-hard-light");
169        assert_eq!(MixBlendMode::SoftLight.to_string(), "mix-blend-soft-light");
170        assert_eq!(MixBlendMode::Difference.to_string(), "mix-blend-difference");
171        assert_eq!(MixBlendMode::Exclusion.to_string(), "mix-blend-exclusion");
172        assert_eq!(MixBlendMode::Hue.to_string(), "mix-blend-hue");
173        assert_eq!(MixBlendMode::Saturation.to_string(), "mix-blend-saturation");
174        assert_eq!(MixBlendMode::Color.to_string(), "mix-blend-color");
175        assert_eq!(MixBlendMode::Luminosity.to_string(), "mix-blend-luminosity");
176    }
177
178    #[test]
179    fn test_background_blend_mode_display() {
180        assert_eq!(BackgroundBlendMode::Normal.to_string(), "bg-blend-normal");
181        assert_eq!(
182            BackgroundBlendMode::Multiply.to_string(),
183            "bg-blend-multiply"
184        );
185        assert_eq!(BackgroundBlendMode::Screen.to_string(), "bg-blend-screen");
186        assert_eq!(BackgroundBlendMode::Overlay.to_string(), "bg-blend-overlay");
187        assert_eq!(BackgroundBlendMode::Darken.to_string(), "bg-blend-darken");
188        assert_eq!(BackgroundBlendMode::Lighten.to_string(), "bg-blend-lighten");
189        assert_eq!(
190            BackgroundBlendMode::ColorDodge.to_string(),
191            "bg-blend-color-dodge"
192        );
193        assert_eq!(
194            BackgroundBlendMode::ColorBurn.to_string(),
195            "bg-blend-color-burn"
196        );
197        assert_eq!(
198            BackgroundBlendMode::HardLight.to_string(),
199            "bg-blend-hard-light"
200        );
201        assert_eq!(
202            BackgroundBlendMode::SoftLight.to_string(),
203            "bg-blend-soft-light"
204        );
205        assert_eq!(
206            BackgroundBlendMode::Difference.to_string(),
207            "bg-blend-difference"
208        );
209        assert_eq!(
210            BackgroundBlendMode::Exclusion.to_string(),
211            "bg-blend-exclusion"
212        );
213        assert_eq!(BackgroundBlendMode::Hue.to_string(), "bg-blend-hue");
214        assert_eq!(
215            BackgroundBlendMode::Saturation.to_string(),
216            "bg-blend-saturation"
217        );
218        assert_eq!(BackgroundBlendMode::Color.to_string(), "bg-blend-color");
219        assert_eq!(
220            BackgroundBlendMode::Luminosity.to_string(),
221            "bg-blend-luminosity"
222        );
223    }
224
225    #[test]
226    fn test_mix_blend_mode_utilities() {
227        let classes = ClassBuilder::new()
228            .mix_blend_mode(MixBlendMode::Multiply)
229            .build();
230
231        assert!(classes.to_css_classes().contains("mix-blend-multiply"));
232    }
233
234    #[test]
235    fn test_background_blend_mode_utilities() {
236        let classes = ClassBuilder::new()
237            .background_blend_mode(BackgroundBlendMode::Screen)
238            .build();
239
240        assert!(classes.to_css_classes().contains("bg-blend-screen"));
241    }
242
243    #[test]
244    fn test_blend_mode_serialization() {
245        let mix_blend_mode = MixBlendMode::Overlay;
246        let serialized = serde_json::to_string(&mix_blend_mode).unwrap();
247        let deserialized: MixBlendMode = serde_json::from_str(&serialized).unwrap();
248        assert_eq!(mix_blend_mode, deserialized);
249
250        let background_blend_mode = BackgroundBlendMode::Overlay;
251        let serialized = serde_json::to_string(&background_blend_mode).unwrap();
252        let deserialized: BackgroundBlendMode = serde_json::from_str(&serialized).unwrap();
253        assert_eq!(background_blend_mode, deserialized);
254    }
255
256    #[test]
257    fn test_blend_mode_equality_and_hash() {
258        let mix_blend_mode1 = MixBlendMode::Multiply;
259        let mix_blend_mode2 = MixBlendMode::Multiply;
260        let mix_blend_mode3 = MixBlendMode::Screen;
261
262        assert_eq!(mix_blend_mode1, mix_blend_mode2);
263        assert_ne!(mix_blend_mode1, mix_blend_mode3);
264
265        let background_blend_mode1 = BackgroundBlendMode::Multiply;
266        let background_blend_mode2 = BackgroundBlendMode::Multiply;
267        let background_blend_mode3 = BackgroundBlendMode::Screen;
268
269        assert_eq!(background_blend_mode1, background_blend_mode2);
270        assert_ne!(background_blend_mode1, background_blend_mode3);
271
272        // Test that equal effects have the same hash
273        use std::collections::hash_map::DefaultHasher;
274        use std::hash::{Hash, Hasher};
275
276        let mut hasher1 = DefaultHasher::new();
277        let mut hasher2 = DefaultHasher::new();
278        mix_blend_mode1.hash(&mut hasher1);
279        mix_blend_mode2.hash(&mut hasher2);
280        assert_eq!(hasher1.finish(), hasher2.finish());
281
282        let mut hasher1 = DefaultHasher::new();
283        let mut hasher2 = DefaultHasher::new();
284        background_blend_mode1.hash(&mut hasher1);
285        background_blend_mode2.hash(&mut hasher2);
286        assert_eq!(hasher1.finish(), hasher2.finish());
287    }
288}