Skip to main content

vizia_core/style/
mod.rs

1//! Styling determines the appearance of a view.
2//!
3//! # Styling Views
4//! Vizia provides two ways to style views:
5//! - Inline
6//! - Shared
7//!
8//! ## Inline Styling
9//! Inline styling refers to setting the style and layout properties of a view using view [modifiers](crate::modifiers).
10//! ```
11//! # use vizia_core::prelude::*;
12//! # let cx = &mut Context::default();
13//! Element::new(cx).background_color(Color::red());
14//! ```
15//! Properties set inline affect only the modified view and override any shared styling for the same property.
16//!
17//! ## Shared Styling
18//! Shared styling refers to setting the style and layout properties using css rules.
19//! ```
20//! # use vizia_core::prelude::*;
21//! # let cx = &mut Context::default();
22//! Element::new(cx).class("foo");
23//! ```
24//! ```css
25//! .foo {
26//!     background-color: red;
27//! }
28//! ```
29//! Rules defined in css can apply to many views but are overridden by inline properties on a view.
30//!
31//! ### Adding Stylesheets
32//! To add a css string to an application, use [`add_theme()`](crate::context::Context::add_theme()) on [`Context`].
33//! This can be used with the `include_str!()` macro to embed an external stylesheet file into the application binary when compiled.
34//! Alternatively a constant string literal can be used to embed the CSS in the application.
35//!
36//! ```
37//! # use vizia_core::prelude::*;
38//! # let cx = &mut Context::default();
39//!
40//! const STYLE: &str = r#"
41//!     .foo {
42//!         background-color: red;
43//!     }
44//! "#;
45//!
46//! cx.add_stylesheet(STYLE);
47//!
48//! Element::new(cx).class("foo");
49//! ```
50//!
51//! To add an external css stylesheet which is read from a file at runtime, use [`add_stylesheet()`](crate::context::Context::add_stylesheet()) on [`Context`].
52//! Stylesheets added this way can be hot-reloaded by pressing the F5 key in the application window.
53//!
54//! ```
55//! # use vizia_core::prelude::*;
56//! # let cx = &mut Context::default();
57//!
58//! cx.add_stylesheet("path/to/stylesheet.css");
59//!
60//! Element::new(cx).class("foo");
61//! ```
62
63use hashbrown::{HashMap, HashSet};
64use indexmap::IndexMap;
65use log::warn;
66use std::fmt::Debug;
67use std::hash::{DefaultHasher, Hash, Hasher};
68use std::ops::Range;
69use vizia_style::selectors::parser::{AncestorHashes, Selector};
70
71use crate::prelude::*;
72use crate::storage::animatable_var_set::AnimatableVarSet;
73
74pub use vizia_style::{
75    Alignment, Angle, BackgroundImage, BackgroundSize, BorderStyleKeyword, ClipPath, Color,
76    CornerShape, CssRule, CursorIcon, Direction, Display, Filter, FontFamily, FontSize,
77    FontSizeKeyword, FontSlant, FontVariation, FontWeight, FontWeightKeyword, FontWidth,
78    GenericFontFamily, Gradient, HorizontalPosition, HorizontalPositionKeyword, LayoutWrap, Length,
79    LengthOrPercentage, LengthValue, LetterSpacing, LineClamp, LineDirection, LineHeight,
80    LinearGradient, Matrix, Opacity, Overflow, PointerEvents, Position, PositionType, RGBA, Scale,
81    Shadow, TextAlign, TextDecorationLine, TextDecorationStyle, TextOverflow, TextStroke,
82    TextStrokeStyle, Transform, Transition, Translate, VerticalPosition, VerticalPositionKeyword,
83    Visibility,
84};
85
86use cssparser::Token as CssToken;
87use vizia_style::{
88    BlendMode, EasingFunction, KeyframeSelector, ParserOptions, Property, Selectors, StyleSheet,
89    TokenList, TokenOrValue, Variable,
90};
91
92mod rule;
93pub(crate) use rule::Rule;
94
95mod pseudoclass;
96pub(crate) use pseudoclass::*;
97
98mod transform;
99pub(crate) use transform::*;
100
101use crate::animation::{AnimationState, Interpolator, Keyframe, TimingFunction};
102use crate::storage::animatable_set::AnimatableSet;
103use crate::storage::style_set::StyleSet;
104use bitflags::bitflags;
105use vizia_id::IdManager;
106use vizia_storage::SparseSet;
107
108bitflags! {
109    /// Describes the capabilities of a view with respect to user interaction.
110    #[derive(Debug, Clone, Copy)]
111    pub(crate) struct Abilities: u8 {
112        // Whether a view will be included in hit tests and receive mouse input events.
113        const HOVERABLE = 1 << 0;
114        // Whether a view can be focused to receive keyboard events.
115        const FOCUSABLE = 1 << 1;
116        // Whether a view can be checked.
117        const CHECKABLE = 1 << 2;
118        // Whether a view can be focused via keyboard navigation.
119        const NAVIGABLE = 1 << 3;
120        // Whether a view can be dragged during a drag and drop.
121        const DRAGGABLE = 1 << 4;
122    }
123}
124
125impl Default for Abilities {
126    fn default() -> Abilities {
127        Abilities::HOVERABLE
128    }
129}
130
131bitflags! {
132    pub(crate) struct SystemFlags: u8 {
133        const RELAYOUT = 1;
134        const RESTYLE = 1 << 1;
135        const REFLOW = 1 << 2;
136        const REDRAW = 1 << 3;
137        const RETRANSFORM = 1 << 4;
138        const RECLIP = 1 << 5;
139        const REACCESS = 1 << 6;
140    }
141}
142
143impl Default for SystemFlags {
144    fn default() -> Self {
145        SystemFlags::all()
146    }
147}
148
149/// An enum which represents an image or a gradient.
150#[derive(Debug, Clone, PartialEq)]
151pub enum ImageOrGradient {
152    /// Represents an image by name.
153    Image(String),
154    /// A gradient.
155    Gradient(Gradient),
156}
157
158/// A font-family.
159#[derive(Debug, Clone, PartialEq, Eq)]
160pub enum FamilyOwned {
161    /// A generic font-family.
162    Generic(GenericFontFamily),
163    /// A named front-family.
164    Named(String),
165}
166
167impl AsRef<str> for FamilyOwned {
168    fn as_ref(&self) -> &str {
169        match self {
170            FamilyOwned::Generic(generic) => match generic {
171                GenericFontFamily::Serif => "serif",
172                GenericFontFamily::SansSerif => "sans-serif",
173                GenericFontFamily::Cursive => todo!(),
174                GenericFontFamily::Fantasy => todo!(),
175                GenericFontFamily::Monospace => "Cascadia Mono",
176            },
177            FamilyOwned::Named(family) => family.as_str(),
178        }
179    }
180}
181
182pub(crate) struct StyleRule {
183    pub(crate) selector: Selector<Selectors>,
184    /// The ancestor hashes associated with the selector.
185    pub(crate) hashes: AncestorHashes,
186}
187
188impl StyleRule {
189    pub(crate) fn new(selector: Selector<Selectors>) -> Self {
190        let hashes = AncestorHashes::new(&selector, vizia_style::QuirksMode::NoQuirks);
191        Self { selector, hashes }
192    }
193}
194
195/// Stores the style properties of all entities in the application.
196#[derive(Default)]
197pub struct Style {
198    pub(crate) rule_manager: IdManager<Rule>,
199
200    // Creates and destroys animation ids
201    pub(crate) animation_manager: IdManager<Animation>,
202    pub(crate) animations: HashMap<String, Animation>,
203    // List of animations to be started on the next frame
204    pub(crate) pending_animations: Vec<(Entity, Animation, Duration, Duration)>,
205
206    // List of rules
207    pub(crate) rules: IndexMap<Rule, StyleRule>,
208
209    pub(crate) default_font: Vec<FamilyOwned>,
210
211    // CSS Selector Properties
212    pub(crate) element: SparseSet<u32>,
213    pub(crate) ids: SparseSet<String>,
214    pub(crate) classes: SparseSet<HashSet<String>>,
215    pub(crate) pseudo_classes: SparseSet<PseudoClassFlags>,
216    pub(crate) disabled: StyleSet<bool>,
217    pub(crate) abilities: SparseSet<Abilities>,
218
219    // Accessibility Properties
220    pub(crate) name: StyleSet<String>,
221    pub(crate) role: SparseSet<Role>,
222    pub(crate) live: SparseSet<Live>,
223    pub(crate) labelled_by: SparseSet<String>,
224    pub(crate) described_by: SparseSet<String>,
225    pub(crate) controls: SparseSet<String>,
226    pub(crate) active_descendant: SparseSet<String>,
227    pub(crate) expanded: SparseSet<bool>,
228    pub(crate) selected: SparseSet<bool>,
229    pub(crate) hidden: SparseSet<bool>,
230    pub(crate) orientation: SparseSet<Orientation>,
231    pub(crate) text_value: SparseSet<String>,
232    pub(crate) numeric_value: SparseSet<f64>,
233
234    // Visibility
235    pub(crate) visibility: StyleSet<Visibility>,
236
237    // Opacity
238    pub(crate) opacity: AnimatableVarSet<Opacity>,
239
240    // Z Order
241    pub(crate) z_index: StyleSet<i32>,
242
243    // Controls whether an entity should ignore ancestor clipping during drawing.
244    pub(crate) ignore_clipping: StyleSet<bool>,
245
246    // Clipping
247    pub(crate) clip_path: AnimatableSet<ClipPath>,
248
249    // Overflow
250    pub(crate) overflowx: StyleSet<Overflow>,
251    pub(crate) overflowy: StyleSet<Overflow>,
252
253    // Filters
254    pub(crate) filter: AnimatableSet<Filter>,
255    pub(crate) backdrop_filter: AnimatableSet<Filter>,
256
257    pub(crate) blend_mode: StyleSet<BlendMode>,
258
259    // Transform
260    pub(crate) transform: AnimatableSet<Vec<Transform>>,
261    pub(crate) transform_origin: AnimatableSet<Translate>,
262    pub(crate) translate: AnimatableSet<Translate>,
263    pub(crate) rotate: AnimatableSet<Angle>,
264    pub(crate) scale: AnimatableSet<Scale>,
265
266    // Border
267    pub(crate) border_width: AnimatableVarSet<LengthOrPercentage>,
268    pub(crate) border_color: AnimatableVarSet<Color>,
269    pub(crate) border_style: StyleSet<BorderStyleKeyword>,
270
271    // Corner Shape
272    pub(crate) corner_top_left_shape: StyleSet<CornerShape>,
273    pub(crate) corner_top_right_shape: StyleSet<CornerShape>,
274    pub(crate) corner_bottom_left_shape: StyleSet<CornerShape>,
275    pub(crate) corner_bottom_right_shape: StyleSet<CornerShape>,
276
277    // Corner Radius
278    pub(crate) corner_top_left_radius: AnimatableVarSet<LengthOrPercentage>,
279    pub(crate) corner_top_right_radius: AnimatableVarSet<LengthOrPercentage>,
280    pub(crate) corner_bottom_left_radius: AnimatableVarSet<LengthOrPercentage>,
281    pub(crate) corner_bottom_right_radius: AnimatableVarSet<LengthOrPercentage>,
282
283    // Corner Smoothing
284    pub(crate) corner_top_left_smoothing: AnimatableSet<f32>,
285    pub(crate) corner_top_right_smoothing: AnimatableSet<f32>,
286    pub(crate) corner_bottom_left_smoothing: AnimatableSet<f32>,
287    pub(crate) corner_bottom_right_smoothing: AnimatableSet<f32>,
288
289    // Outline
290    pub(crate) outline_width: AnimatableVarSet<LengthOrPercentage>,
291    pub(crate) outline_color: AnimatableVarSet<Color>,
292    pub(crate) outline_offset: AnimatableVarSet<LengthOrPercentage>,
293
294    // Background
295    pub(crate) background_color: AnimatableVarSet<Color>,
296    pub(crate) background_image: AnimatableSet<Vec<ImageOrGradient>>,
297    pub(crate) background_size: AnimatableSet<Vec<BackgroundSize>>,
298
299    // Shadow
300    pub(crate) shadow: AnimatableVarSet<Vec<Shadow>>,
301
302    // Text
303    pub(crate) text: SparseSet<String>,
304    pub(crate) text_wrap: StyleSet<bool>,
305    pub(crate) text_overflow: StyleSet<TextOverflow>,
306    pub(crate) letter_spacing: AnimatableVarSet<LetterSpacing>,
307    pub(crate) line_height: AnimatableVarSet<LineHeight>,
308    pub(crate) line_clamp: StyleSet<LineClamp>,
309    pub(crate) text_align: StyleSet<TextAlign>,
310    pub(crate) text_decoration_line: StyleSet<TextDecorationLine>,
311    pub(crate) text_decoration_style: StyleSet<TextDecorationStyle>,
312    pub(crate) text_decoration_color: AnimatableVarSet<Color>,
313    pub(crate) text_stroke_width: StyleSet<Length>,
314    pub(crate) text_stroke_style: StyleSet<TextStrokeStyle>,
315    pub(crate) font_family: StyleSet<Vec<FamilyOwned>>,
316    pub(crate) font_color: AnimatableVarSet<Color>,
317    pub(crate) font_size: AnimatableVarSet<FontSize>,
318    pub(crate) font_weight: StyleSet<FontWeight>,
319    pub(crate) font_slant: StyleSet<FontSlant>,
320    pub(crate) font_width: StyleSet<FontWidth>,
321    pub(crate) font_variation_settings: StyleSet<Vec<FontVariation>>,
322    pub(crate) caret_color: AnimatableVarSet<Color>,
323    pub(crate) selection_color: AnimatableVarSet<Color>,
324
325    pub(crate) fill: AnimatableVarSet<Color>,
326
327    // cursor Icon
328    pub(crate) cursor: StyleSet<CursorIcon>,
329
330    pub(crate) pointer_events: StyleSet<PointerEvents>,
331
332    // LAYOUT
333
334    // Display
335    pub(crate) display: AnimatableSet<Display>,
336
337    // Layout Type
338    pub(crate) layout_type: StyleSet<LayoutType>,
339
340    // Position
341    pub(crate) position_type: StyleSet<PositionType>,
342
343    pub(crate) alignment: StyleSet<Alignment>,
344    pub(crate) direction: StyleSet<Direction>,
345    pub(crate) wrap: StyleSet<LayoutWrap>,
346
347    // Grid
348    pub(crate) grid_columns: StyleSet<Vec<Units>>,
349    pub(crate) grid_rows: StyleSet<Vec<Units>>,
350
351    pub(crate) column_start: StyleSet<usize>,
352    pub(crate) column_span: StyleSet<usize>,
353    pub(crate) row_start: StyleSet<usize>,
354    pub(crate) row_span: StyleSet<usize>,
355
356    // Spacing
357    pub(crate) left: AnimatableVarSet<Units>,
358    pub(crate) right: AnimatableVarSet<Units>,
359    pub(crate) top: AnimatableVarSet<Units>,
360    pub(crate) bottom: AnimatableVarSet<Units>,
361
362    // Padding
363    pub(crate) padding_left: AnimatableVarSet<Units>,
364    pub(crate) padding_right: AnimatableVarSet<Units>,
365    pub(crate) padding_top: AnimatableVarSet<Units>,
366    pub(crate) padding_bottom: AnimatableVarSet<Units>,
367    pub(crate) vertical_gap: AnimatableVarSet<Units>,
368    pub(crate) horizontal_gap: AnimatableVarSet<Units>,
369
370    // Scrolling
371    pub(crate) vertical_scroll: AnimatableSet<f32>,
372    pub(crate) horizontal_scroll: AnimatableSet<f32>,
373
374    // Size
375    pub(crate) width: AnimatableVarSet<Units>,
376    pub(crate) height: AnimatableVarSet<Units>,
377
378    // Size Constraints
379    pub(crate) min_width: AnimatableVarSet<Units>,
380    pub(crate) max_width: AnimatableVarSet<Units>,
381    pub(crate) min_height: AnimatableVarSet<Units>,
382    pub(crate) max_height: AnimatableVarSet<Units>,
383
384    // Gap Constraints
385    pub(crate) min_horizontal_gap: AnimatableVarSet<Units>,
386    pub(crate) max_horizontal_gap: AnimatableVarSet<Units>,
387    pub(crate) min_vertical_gap: AnimatableVarSet<Units>,
388    pub(crate) max_vertical_gap: AnimatableVarSet<Units>,
389
390    pub(crate) system_flags: SystemFlags,
391
392    pub(crate) restyle: HashSet<Entity>,
393    pub(crate) text_construction: HashSet<Entity>,
394    pub(crate) text_layout: HashSet<Entity>,
395    pub(crate) reaccess: HashSet<Entity>,
396    pub(crate) retransform: HashSet<Entity>,
397    pub(crate) reclip: HashSet<Entity>,
398
399    pub(crate) text_range: SparseSet<Range<usize>>,
400    pub(crate) text_span: SparseSet<bool>,
401
402    /// This includes both the system's HiDPI scaling factor as well as `cx.user_scale_factor`.
403    pub(crate) dpi_factor: f64,
404
405    pub(crate) custom_color_props: HashMap<u64, AnimatableVarSet<Color>>,
406    pub(crate) custom_length_props: HashMap<u64, AnimatableVarSet<LengthOrPercentage>>,
407    pub(crate) custom_font_size_props: HashMap<u64, AnimatableVarSet<FontSize>>,
408    pub(crate) custom_letter_spacing_props: HashMap<u64, AnimatableVarSet<LetterSpacing>>,
409    pub(crate) custom_line_height_props: HashMap<u64, AnimatableVarSet<LineHeight>>,
410    pub(crate) custom_units_props: HashMap<u64, AnimatableVarSet<Units>>,
411    pub(crate) custom_opacity_props: HashMap<u64, AnimatableVarSet<Opacity>>,
412    pub(crate) custom_shadow_props: HashMap<u64, AnimatableVarSet<Vec<Shadow>>>,
413}
414
415impl Style {
416    /// Returns the scale factor of the application.
417    pub fn scale_factor(&self) -> f32 {
418        self.dpi_factor as f32
419    }
420
421    /// Function to convert logical points to physical pixels.
422    pub fn logical_to_physical(&self, logical: f32) -> f32 {
423        (logical * self.dpi_factor as f32).round()
424    }
425
426    /// Function to convert physical pixels to logical points.
427    pub fn physical_to_logical(&self, physical: f32) -> f32 {
428        physical / self.dpi_factor as f32
429    }
430
431    pub(crate) fn remove_rules(&mut self) {
432        self.rule_manager.reset();
433        self.rules.clear();
434    }
435
436    pub(crate) fn get_animation(&self, name: &str) -> Option<&Animation> {
437        self.animations.get(name)
438    }
439
440    pub(crate) fn add_keyframe(
441        &mut self,
442        animation_id: Animation,
443        time: f32,
444        properties: &[Property],
445    ) {
446        fn insert_keyframe<T: 'static + Interpolator + Debug + Clone + PartialEq + Default>(
447            storage: &mut AnimatableSet<T>,
448            animation_id: Animation,
449            time: f32,
450            value: T,
451        ) {
452            let keyframe = Keyframe { time, value, timing_function: TimingFunction::linear() };
453
454            if let Some(anim_state) = storage.get_animation_mut(animation_id) {
455                anim_state.keyframes.push(keyframe)
456            } else {
457                let anim_state = AnimationState::new(animation_id).with_keyframe(keyframe);
458                storage.insert_animation(animation_id, anim_state);
459            }
460        }
461
462        fn insert_keyframe2<T: 'static + Interpolator + Debug + Clone + PartialEq + Default>(
463            storage: &mut AnimatableVarSet<T>,
464            animation_id: Animation,
465            time: f32,
466            value: T,
467        ) {
468            let keyframe = Keyframe { time, value, timing_function: TimingFunction::linear() };
469
470            if let Some(anim_state) = storage.get_animation_mut(animation_id) {
471                anim_state.keyframes.push(keyframe)
472            } else {
473                let anim_state = AnimationState::new(animation_id).with_keyframe(keyframe);
474                storage.insert_animation(animation_id, anim_state);
475            }
476        }
477
478        for property in properties.iter() {
479            match property {
480                // DISPLAY
481                Property::Display(value) => {
482                    insert_keyframe(&mut self.display, animation_id, time, *value);
483                }
484
485                Property::Opacity(value) => {
486                    insert_keyframe2(&mut self.opacity, animation_id, time, *value);
487                }
488
489                Property::ClipPath(value) => {
490                    insert_keyframe(&mut self.clip_path, animation_id, time, value.clone());
491                }
492
493                // TRANSFORM
494                Property::Transform(value) => {
495                    insert_keyframe(&mut self.transform, animation_id, time, value.clone());
496                }
497
498                Property::TransformOrigin(transform_origin) => {
499                    let x = transform_origin.x.to_length_or_percentage();
500                    let y = transform_origin.y.to_length_or_percentage();
501                    let value = Translate { x, y };
502                    insert_keyframe(&mut self.transform_origin, animation_id, time, value);
503                }
504
505                Property::Translate(value) => {
506                    insert_keyframe(&mut self.translate, animation_id, time, value.clone());
507                }
508
509                Property::Rotate(value) => {
510                    insert_keyframe(&mut self.rotate, animation_id, time, *value);
511                }
512
513                Property::Scale(value) => {
514                    insert_keyframe(&mut self.scale, animation_id, time, *value);
515                }
516
517                // BORDER
518                Property::BorderWidth(value) => {
519                    insert_keyframe2(
520                        &mut self.border_width,
521                        animation_id,
522                        time,
523                        value.left.0.clone(),
524                    );
525                }
526
527                Property::BorderColor(value) => {
528                    insert_keyframe2(&mut self.border_color, animation_id, time, *value);
529                }
530
531                Property::CornerTopLeftRadius(value) => {
532                    insert_keyframe2(
533                        &mut self.corner_top_left_radius,
534                        animation_id,
535                        time,
536                        value.clone(),
537                    );
538                }
539
540                Property::CornerTopRightRadius(value) => {
541                    insert_keyframe2(
542                        &mut self.corner_top_right_radius,
543                        animation_id,
544                        time,
545                        value.clone(),
546                    );
547                }
548
549                Property::CornerBottomLeftRadius(value) => {
550                    insert_keyframe2(
551                        &mut self.corner_bottom_left_radius,
552                        animation_id,
553                        time,
554                        value.clone(),
555                    );
556                }
557
558                Property::CornerBottomRightRadius(value) => {
559                    insert_keyframe2(
560                        &mut self.corner_bottom_right_radius,
561                        animation_id,
562                        time,
563                        value.clone(),
564                    );
565                }
566
567                // OUTLINE
568                Property::OutlineWidth(value) => {
569                    insert_keyframe2(
570                        &mut self.outline_width,
571                        animation_id,
572                        time,
573                        value.left.0.clone(),
574                    );
575                }
576
577                Property::OutlineColor(value) => {
578                    insert_keyframe2(&mut self.outline_color, animation_id, time, *value);
579                }
580
581                Property::OutlineOffset(value) => {
582                    insert_keyframe2(&mut self.outline_offset, animation_id, time, value.clone());
583                }
584
585                // BACKGROUND
586                Property::BackgroundColor(value) => {
587                    insert_keyframe2(&mut self.background_color, animation_id, time, *value);
588                }
589
590                Property::BackgroundImage(images) => {
591                    let images = images
592                        .iter()
593                        .filter_map(|img| match img {
594                            BackgroundImage::None => None,
595                            BackgroundImage::Gradient(gradient) => {
596                                Some(ImageOrGradient::Gradient(*gradient.clone()))
597                            }
598                            BackgroundImage::Url(url) => {
599                                Some(ImageOrGradient::Image(url.url.to_string()))
600                            }
601                        })
602                        .collect::<Vec<_>>();
603                    insert_keyframe(&mut self.background_image, animation_id, time, images);
604                }
605
606                Property::BackgroundSize(value) => {
607                    insert_keyframe(&mut self.background_size, animation_id, time, value.clone());
608                }
609
610                // BOX SHADOW
611                Property::Shadow(value) => {
612                    insert_keyframe2(&mut self.shadow, animation_id, time, value.clone());
613                }
614
615                // TEXT
616                Property::FontColor(value) => {
617                    insert_keyframe2(&mut self.font_color, animation_id, time, *value);
618                }
619
620                Property::FontSize(value) => {
621                    insert_keyframe2(&mut self.font_size, animation_id, time, value.clone());
622                }
623
624                Property::LetterSpacing(value) => {
625                    insert_keyframe2(&mut self.letter_spacing, animation_id, time, value.clone());
626                }
627
628                Property::LineHeight(value) => {
629                    insert_keyframe2(&mut self.line_height, animation_id, time, value.clone());
630                }
631
632                Property::CaretColor(value) => {
633                    insert_keyframe2(&mut self.caret_color, animation_id, time, *value);
634                }
635
636                Property::SelectionColor(value) => {
637                    insert_keyframe2(&mut self.selection_color, animation_id, time, *value);
638                }
639
640                // SPACE
641                Property::Left(value) => {
642                    insert_keyframe2(&mut self.left, animation_id, time, *value);
643                }
644
645                Property::Right(value) => {
646                    insert_keyframe2(&mut self.right, animation_id, time, *value);
647                }
648
649                Property::Top(value) => {
650                    insert_keyframe2(&mut self.top, animation_id, time, *value);
651                }
652
653                Property::Bottom(value) => {
654                    insert_keyframe2(&mut self.bottom, animation_id, time, *value);
655                }
656
657                // Padding
658                Property::PaddingLeft(value) => {
659                    insert_keyframe2(&mut self.padding_left, animation_id, time, *value);
660                }
661
662                Property::PaddingRight(value) => {
663                    insert_keyframe2(&mut self.padding_right, animation_id, time, *value);
664                }
665
666                Property::PaddingTop(value) => {
667                    insert_keyframe2(&mut self.padding_top, animation_id, time, *value);
668                }
669
670                Property::PaddingBottom(value) => {
671                    insert_keyframe2(&mut self.padding_bottom, animation_id, time, *value);
672                }
673
674                Property::HorizontalGap(value) => {
675                    insert_keyframe2(&mut self.horizontal_gap, animation_id, time, *value);
676                }
677
678                Property::VerticalGap(value) => {
679                    insert_keyframe2(&mut self.vertical_gap, animation_id, time, *value);
680                }
681
682                Property::Gap(value) => {
683                    insert_keyframe2(&mut self.horizontal_gap, animation_id, time, *value);
684                    insert_keyframe2(&mut self.vertical_gap, animation_id, time, *value);
685                }
686
687                // GAP CONSSTRAINTS
688                Property::MinGap(value) => {
689                    insert_keyframe2(&mut self.min_horizontal_gap, animation_id, time, *value);
690                    insert_keyframe2(&mut self.min_vertical_gap, animation_id, time, *value);
691                }
692
693                Property::MaxGap(value) => {
694                    insert_keyframe2(&mut self.max_horizontal_gap, animation_id, time, *value);
695                    insert_keyframe2(&mut self.max_vertical_gap, animation_id, time, *value);
696                }
697
698                Property::MinHorizontalGap(value) => {
699                    insert_keyframe2(&mut self.min_horizontal_gap, animation_id, time, *value);
700                }
701
702                Property::MaxHorizontalGap(value) => {
703                    insert_keyframe2(&mut self.max_horizontal_gap, animation_id, time, *value);
704                }
705
706                Property::MinVerticalGap(value) => {
707                    insert_keyframe2(&mut self.min_vertical_gap, animation_id, time, *value);
708                }
709
710                Property::MaxVerticalGap(value) => {
711                    insert_keyframe2(&mut self.max_vertical_gap, animation_id, time, *value);
712                }
713
714                // SIZE
715                Property::Width(value) => {
716                    insert_keyframe2(&mut self.width, animation_id, time, *value);
717                }
718
719                Property::Height(value) => {
720                    insert_keyframe2(&mut self.height, animation_id, time, *value);
721                }
722
723                // SIZE CONSTRAINTS
724                Property::MinWidth(value) => {
725                    insert_keyframe2(&mut self.min_width, animation_id, time, *value);
726                }
727
728                Property::MaxWidth(value) => {
729                    insert_keyframe2(&mut self.max_width, animation_id, time, *value);
730                }
731
732                Property::MinHeight(value) => {
733                    insert_keyframe2(&mut self.min_height, animation_id, time, *value);
734                }
735
736                Property::MaxHeight(value) => {
737                    insert_keyframe2(&mut self.max_height, animation_id, time, *value);
738                }
739
740                Property::TextDecorationColor(value) => {
741                    insert_keyframe2(&mut self.text_decoration_color, animation_id, time, *value);
742                }
743
744                Property::Fill(value) => {
745                    insert_keyframe2(&mut self.fill, animation_id, time, *value);
746                }
747
748                _ => {}
749            }
750        }
751    }
752
753    pub(crate) fn add_animation(&mut self, animation: AnimationBuilder) -> Animation {
754        let animation_id = self.animation_manager.create();
755        for keyframe in animation.keyframes.iter() {
756            self.add_keyframe(animation_id, keyframe.time, &keyframe.properties);
757        }
758
759        animation_id
760    }
761
762    pub(crate) fn enqueue_animation(
763        &mut self,
764        entity: Entity,
765        animation: Animation,
766        duration: Duration,
767        delay: Duration,
768    ) {
769        self.pending_animations.push((entity, animation, duration, delay));
770    }
771
772    pub(crate) fn play_pending_animations(&mut self) {
773        let start_time = Instant::now();
774
775        let pending_animations = self.pending_animations.drain(..).collect::<Vec<_>>();
776
777        for (entity, animation, duration, delay) in pending_animations {
778            self.play_animation(entity, animation, start_time + delay, duration, delay)
779        }
780    }
781
782    pub(crate) fn play_animation(
783        &mut self,
784        entity: Entity,
785        animation: Animation,
786        start_time: Instant,
787        duration: Duration,
788        delay: Duration,
789    ) {
790        self.display.play_animation(entity, animation, start_time, duration, delay);
791        self.opacity.play_animation(entity, animation, start_time, duration, delay);
792        self.clip_path.play_animation(entity, animation, start_time, duration, delay);
793
794        self.transform.play_animation(entity, animation, start_time, duration, delay);
795        self.transform_origin.play_animation(entity, animation, start_time, duration, delay);
796        self.translate.play_animation(entity, animation, start_time, duration, delay);
797        self.rotate.play_animation(entity, animation, start_time, duration, delay);
798        self.scale.play_animation(entity, animation, start_time, duration, delay);
799
800        self.border_width.play_animation(entity, animation, start_time, duration, delay);
801        self.border_color.play_animation(entity, animation, start_time, duration, delay);
802
803        self.corner_top_left_radius.play_animation(entity, animation, start_time, duration, delay);
804        self.corner_top_right_radius.play_animation(entity, animation, start_time, duration, delay);
805        self.corner_bottom_left_radius
806            .play_animation(entity, animation, start_time, duration, delay);
807        self.corner_bottom_right_radius
808            .play_animation(entity, animation, start_time, duration, delay);
809
810        self.outline_width.play_animation(entity, animation, start_time, duration, delay);
811        self.outline_color.play_animation(entity, animation, start_time, duration, delay);
812        self.outline_offset.play_animation(entity, animation, start_time, duration, delay);
813
814        self.background_color.play_animation(entity, animation, start_time, duration, delay);
815        self.background_image.play_animation(entity, animation, start_time, duration, delay);
816        self.background_size.play_animation(entity, animation, start_time, duration, delay);
817
818        self.shadow.play_animation(entity, animation, start_time, duration, delay);
819
820        self.font_color.play_animation(entity, animation, start_time, duration, delay);
821        self.font_size.play_animation(entity, animation, start_time, duration, delay);
822        self.letter_spacing.play_animation(entity, animation, start_time, duration, delay);
823        self.line_height.play_animation(entity, animation, start_time, duration, delay);
824        self.caret_color.play_animation(entity, animation, start_time, duration, delay);
825        self.selection_color.play_animation(entity, animation, start_time, duration, delay);
826
827        self.left.play_animation(entity, animation, start_time, duration, delay);
828        self.right.play_animation(entity, animation, start_time, duration, delay);
829        self.top.play_animation(entity, animation, start_time, duration, delay);
830        self.bottom.play_animation(entity, animation, start_time, duration, delay);
831
832        self.padding_left.play_animation(entity, animation, start_time, duration, delay);
833        self.padding_right.play_animation(entity, animation, start_time, duration, delay);
834        self.padding_top.play_animation(entity, animation, start_time, duration, delay);
835        self.padding_bottom.play_animation(entity, animation, start_time, duration, delay);
836        self.horizontal_gap.play_animation(entity, animation, start_time, duration, delay);
837        self.vertical_gap.play_animation(entity, animation, start_time, duration, delay);
838
839        self.width.play_animation(entity, animation, start_time, duration, delay);
840        self.height.play_animation(entity, animation, start_time, duration, delay);
841
842        self.min_width.play_animation(entity, animation, start_time, duration, delay);
843        self.max_width.play_animation(entity, animation, start_time, duration, delay);
844        self.min_height.play_animation(entity, animation, start_time, duration, delay);
845        self.max_height.play_animation(entity, animation, start_time, duration, delay);
846
847        self.min_horizontal_gap.play_animation(entity, animation, start_time, duration, delay);
848        self.max_horizontal_gap.play_animation(entity, animation, start_time, duration, delay);
849        self.min_vertical_gap.play_animation(entity, animation, start_time, duration, delay);
850        self.max_vertical_gap.play_animation(entity, animation, start_time, duration, delay);
851
852        self.text_decoration_color.play_animation(entity, animation, start_time, duration, delay);
853
854        self.fill.play_animation(entity, animation, start_time, duration, delay);
855
856        // Play animations on custom color properties
857        for store in self.custom_color_props.values_mut() {
858            store.play_animation(entity, animation, start_time, duration, delay);
859        }
860        // Play animations on custom length properties
861        for store in self.custom_length_props.values_mut() {
862            store.play_animation(entity, animation, start_time, duration, delay);
863        }
864        // Play animations on custom font-size properties
865        for store in self.custom_font_size_props.values_mut() {
866            store.play_animation(entity, animation, start_time, duration, delay);
867        }
868        // Play animations on custom letter-spacing properties
869        for store in self.custom_letter_spacing_props.values_mut() {
870            store.play_animation(entity, animation, start_time, duration, delay);
871        }
872        // Play animations on custom line-height properties
873        for store in self.custom_line_height_props.values_mut() {
874            store.play_animation(entity, animation, start_time, duration, delay);
875        }
876        // Play animations on custom units properties
877        for store in self.custom_units_props.values_mut() {
878            store.play_animation(entity, animation, start_time, duration, delay);
879        }
880        // Play animations on custom opacity properties
881        for store in self.custom_opacity_props.values_mut() {
882            store.play_animation(entity, animation, start_time, duration, delay);
883        }
884        // Play animations on custom shadow properties
885        for store in self.custom_shadow_props.values_mut() {
886            store.play_animation(entity, animation, start_time, duration, delay);
887        }
888    }
889
890    pub(crate) fn is_animating(&self, entity: Entity, animation: Animation) -> bool {
891        self.display.has_active_animation(entity, animation)
892            | self.opacity.has_active_animation(entity, animation)
893            | self.clip_path.has_active_animation(entity, animation)
894            | self.transform.has_active_animation(entity, animation)
895            | self.transform_origin.has_active_animation(entity, animation)
896            | self.translate.has_active_animation(entity, animation)
897            | self.rotate.has_active_animation(entity, animation)
898            | self.scale.has_active_animation(entity, animation)
899            | self.border_width.has_active_animation(entity, animation)
900            | self.border_color.has_active_animation(entity, animation)
901            | self.corner_top_left_radius.has_active_animation(entity, animation)
902            | self.corner_top_right_radius.has_active_animation(entity, animation)
903            | self.corner_bottom_left_radius.has_active_animation(entity, animation)
904            | self.corner_bottom_right_radius.has_active_animation(entity, animation)
905            | self.outline_width.has_active_animation(entity, animation)
906            | self.outline_color.has_active_animation(entity, animation)
907            | self.outline_offset.has_active_animation(entity, animation)
908            | self.background_color.has_active_animation(entity, animation)
909            | self.background_image.has_active_animation(entity, animation)
910            | self.background_size.has_active_animation(entity, animation)
911            | self.shadow.has_active_animation(entity, animation)
912            | self.font_color.has_active_animation(entity, animation)
913            | self.font_size.has_active_animation(entity, animation)
914            | self.letter_spacing.has_active_animation(entity, animation)
915            | self.line_height.has_active_animation(entity, animation)
916            | self.caret_color.has_active_animation(entity, animation)
917            | self.selection_color.has_active_animation(entity, animation)
918            | self.left.has_active_animation(entity, animation)
919            | self.right.has_active_animation(entity, animation)
920            | self.top.has_active_animation(entity, animation)
921            | self.bottom.has_active_animation(entity, animation)
922            | self.padding_left.has_active_animation(entity, animation)
923            | self.padding_right.has_active_animation(entity, animation)
924            | self.padding_top.has_active_animation(entity, animation)
925            | self.padding_bottom.has_active_animation(entity, animation)
926            | self.horizontal_gap.has_active_animation(entity, animation)
927            | self.vertical_gap.has_active_animation(entity, animation)
928            | self.width.has_active_animation(entity, animation)
929            | self.height.has_active_animation(entity, animation)
930            | self.min_width.has_active_animation(entity, animation)
931            | self.max_width.has_active_animation(entity, animation)
932            | self.min_height.has_active_animation(entity, animation)
933            | self.max_height.has_active_animation(entity, animation)
934            | self.min_horizontal_gap.has_active_animation(entity, animation)
935            | self.max_horizontal_gap.has_active_animation(entity, animation)
936            | self.min_vertical_gap.has_active_animation(entity, animation)
937            | self.max_vertical_gap.has_active_animation(entity, animation)
938            | self.text_decoration_color.has_active_animation(entity, animation)
939            | self.fill.has_active_animation(entity, animation)
940    }
941
942    pub(crate) fn parse_theme(&mut self, stylesheet: &str) {
943        if let Ok(stylesheet) = StyleSheet::parse(stylesheet, ParserOptions::new()) {
944            let rules = stylesheet.rules.0;
945
946            for rule in rules {
947                match rule {
948                    CssRule::Style(style_rule) => {
949                        // let selectors = style_rule.selectors;
950
951                        for selector in style_rule.selectors.slice() {
952                            let rule_id = self.rule_manager.create();
953
954                            for property in style_rule.declarations.declarations.iter() {
955                                match property {
956                                    Property::Transition(transitions) => {
957                                        for transition in transitions.iter() {
958                                            self.insert_transition(rule_id, transition);
959                                        }
960                                    }
961
962                                    _ => {
963                                        self.insert_property(rule_id, property);
964                                    }
965                                }
966                            }
967
968                            self.rules.insert(rule_id, StyleRule::new(selector.clone()));
969                        }
970                    }
971
972                    CssRule::Keyframes(keyframes_rule) => {
973                        let name = keyframes_rule.name.as_string();
974
975                        let animation_id = self.animation_manager.create();
976
977                        for keyframes in keyframes_rule.keyframes {
978                            for selector in keyframes.selectors.iter() {
979                                let time = match selector {
980                                    KeyframeSelector::From => 0.0,
981                                    KeyframeSelector::To => 1.0,
982                                    KeyframeSelector::Percentage(percentage) => {
983                                        percentage.0 / 100.0
984                                    }
985                                };
986
987                                self.add_keyframe(
988                                    animation_id,
989                                    time,
990                                    &keyframes.declarations.declarations,
991                                );
992                            }
993                        }
994
995                        self.animations.insert(name, animation_id);
996                    }
997
998                    _ => {}
999                }
1000            }
1001        } else {
1002            println!("Failed to parse stylesheet");
1003        }
1004    }
1005
1006    fn insert_transition(&mut self, rule_id: Rule, transition: &Transition) {
1007        let animation = self.animation_manager.create();
1008        match transition.property.as_ref() {
1009            "display" => {
1010                self.display.insert_animation(animation, self.add_transition(transition));
1011                self.display.insert_transition(rule_id, animation);
1012            }
1013
1014            "opacity" => {
1015                self.opacity.insert_animation(animation, self.add_transition(transition));
1016                self.opacity.insert_transition(rule_id, animation);
1017            }
1018
1019            "clip-path" => {
1020                self.clip_path.insert_animation(animation, self.add_transition(transition));
1021                self.clip_path.insert_transition(rule_id, animation);
1022            }
1023
1024            "transform" => {
1025                self.transform.insert_animation(animation, self.add_transition(transition));
1026                self.transform.insert_transition(rule_id, animation);
1027            }
1028
1029            "transform-origin" => {
1030                self.transform_origin.insert_animation(animation, self.add_transition(transition));
1031                self.transform_origin.insert_transition(rule_id, animation);
1032            }
1033
1034            "translate" => {
1035                self.translate.insert_animation(animation, self.add_transition(transition));
1036                self.translate.insert_transition(rule_id, animation);
1037            }
1038
1039            "rotate" => {
1040                self.rotate.insert_animation(animation, self.add_transition(transition));
1041                self.rotate.insert_transition(rule_id, animation);
1042            }
1043
1044            "scale" => {
1045                self.scale.insert_animation(animation, self.add_transition(transition));
1046                self.scale.insert_transition(rule_id, animation);
1047            }
1048
1049            "border" => {
1050                self.border_width.insert_animation(animation, self.add_transition(transition));
1051                self.border_width.insert_transition(rule_id, animation);
1052                self.border_color.insert_animation(animation, self.add_transition(transition));
1053                self.border_color.insert_transition(rule_id, animation);
1054            }
1055
1056            "border-width" => {
1057                self.border_width.insert_animation(animation, self.add_transition(transition));
1058                self.border_width.insert_transition(rule_id, animation);
1059            }
1060
1061            "border-color" => {
1062                self.border_color.insert_animation(animation, self.add_transition(transition));
1063                self.border_color.insert_transition(rule_id, animation);
1064            }
1065
1066            "corner-radius" => {
1067                self.corner_bottom_left_radius
1068                    .insert_animation(animation, self.add_transition(transition));
1069                self.corner_bottom_left_radius.insert_transition(rule_id, animation);
1070                self.corner_bottom_right_radius
1071                    .insert_animation(animation, self.add_transition(transition));
1072                self.corner_bottom_right_radius.insert_transition(rule_id, animation);
1073                self.corner_top_left_radius
1074                    .insert_animation(animation, self.add_transition(transition));
1075                self.corner_top_left_radius.insert_transition(rule_id, animation);
1076                self.corner_top_right_radius
1077                    .insert_animation(animation, self.add_transition(transition));
1078                self.corner_top_right_radius.insert_transition(rule_id, animation);
1079            }
1080
1081            "corner-top-left-radius" => {
1082                self.corner_top_left_radius
1083                    .insert_animation(animation, self.add_transition(transition));
1084                self.corner_top_left_radius.insert_transition(rule_id, animation);
1085            }
1086
1087            "corner-top-right-radius" => {
1088                self.corner_top_right_radius
1089                    .insert_animation(animation, self.add_transition(transition));
1090                self.corner_top_right_radius.insert_transition(rule_id, animation);
1091            }
1092
1093            "corner-bottom-left-radius" => {
1094                self.corner_bottom_left_radius
1095                    .insert_animation(animation, self.add_transition(transition));
1096                self.corner_bottom_left_radius.insert_transition(rule_id, animation);
1097            }
1098
1099            "corner-bottom-right-radius" => {
1100                self.corner_bottom_right_radius
1101                    .insert_animation(animation, self.add_transition(transition));
1102                self.corner_bottom_right_radius.insert_transition(rule_id, animation);
1103            }
1104
1105            "outline" => {
1106                self.outline_width.insert_animation(animation, self.add_transition(transition));
1107                self.outline_width.insert_transition(rule_id, animation);
1108                self.outline_color.insert_animation(animation, self.add_transition(transition));
1109                self.outline_color.insert_transition(rule_id, animation);
1110            }
1111
1112            "outline-width" => {
1113                self.outline_width.insert_animation(animation, self.add_transition(transition));
1114                self.outline_width.insert_transition(rule_id, animation);
1115            }
1116
1117            "outline-color" => {
1118                self.outline_color.insert_animation(animation, self.add_transition(transition));
1119                self.outline_color.insert_transition(rule_id, animation);
1120            }
1121
1122            "outline-offset" => {
1123                self.outline_offset.insert_animation(animation, self.add_transition(transition));
1124                self.outline_offset.insert_transition(rule_id, animation);
1125            }
1126
1127            "background-color" => {
1128                self.background_color.insert_animation(animation, self.add_transition(transition));
1129                self.background_color.insert_transition(rule_id, animation);
1130            }
1131
1132            "background-image" => {
1133                self.background_image.insert_animation(animation, self.add_transition(transition));
1134                self.background_image.insert_transition(rule_id, animation);
1135            }
1136
1137            "background-size" => {
1138                self.background_size.insert_animation(animation, self.add_transition(transition));
1139                self.background_size.insert_transition(rule_id, animation);
1140            }
1141
1142            "shadow" => {
1143                self.shadow.insert_animation(animation, self.add_transition(transition));
1144                self.shadow.insert_transition(rule_id, animation);
1145            }
1146
1147            "color" => {
1148                self.font_color.insert_animation(animation, self.add_transition(transition));
1149                self.font_color.insert_transition(rule_id, animation);
1150            }
1151
1152            "font-size" => {
1153                self.font_size.insert_animation(animation, self.add_transition(transition));
1154                self.font_size.insert_transition(rule_id, animation);
1155            }
1156
1157            "letter-spacing" => {
1158                self.letter_spacing.insert_animation(animation, self.add_transition(transition));
1159                self.letter_spacing.insert_transition(rule_id, animation);
1160            }
1161
1162            "line-height" => {
1163                self.line_height.insert_animation(animation, self.add_transition(transition));
1164                self.line_height.insert_transition(rule_id, animation);
1165            }
1166
1167            "caret-color" => {
1168                self.caret_color.insert_animation(animation, self.add_transition(transition));
1169                self.caret_color.insert_transition(rule_id, animation);
1170            }
1171
1172            "selection-color" => {
1173                self.selection_color.insert_animation(animation, self.add_transition(transition));
1174                self.selection_color.insert_transition(rule_id, animation);
1175            }
1176
1177            "left" => {
1178                self.left.insert_animation(animation, self.add_transition(transition));
1179                self.left.insert_transition(rule_id, animation);
1180            }
1181
1182            "right" => {
1183                self.right.insert_animation(animation, self.add_transition(transition));
1184                self.right.insert_transition(rule_id, animation);
1185            }
1186
1187            "top" => {
1188                self.top.insert_animation(animation, self.add_transition(transition));
1189                self.top.insert_transition(rule_id, animation);
1190            }
1191
1192            "bottom" => {
1193                self.bottom.insert_animation(animation, self.add_transition(transition));
1194                self.bottom.insert_transition(rule_id, animation);
1195            }
1196
1197            "padding-left" => {
1198                self.padding_left.insert_animation(animation, self.add_transition(transition));
1199                self.padding_left.insert_transition(rule_id, animation);
1200            }
1201
1202            "padding-right" => {
1203                self.padding_right.insert_animation(animation, self.add_transition(transition));
1204                self.padding_right.insert_transition(rule_id, animation);
1205            }
1206
1207            "padding-top" => {
1208                self.padding_top.insert_animation(animation, self.add_transition(transition));
1209                self.padding_top.insert_transition(rule_id, animation);
1210            }
1211
1212            "padding-bottom" => {
1213                self.padding_bottom.insert_animation(animation, self.add_transition(transition));
1214                self.padding_bottom.insert_transition(rule_id, animation);
1215            }
1216
1217            "horizontal-gap" => {
1218                self.horizontal_gap.insert_animation(animation, self.add_transition(transition));
1219                self.horizontal_gap.insert_transition(rule_id, animation);
1220            }
1221
1222            "vertical-gap" => {
1223                self.vertical_gap.insert_animation(animation, self.add_transition(transition));
1224                self.vertical_gap.insert_transition(rule_id, animation);
1225            }
1226
1227            "gap" => {
1228                self.horizontal_gap.insert_animation(animation, self.add_transition(transition));
1229                self.horizontal_gap.insert_transition(rule_id, animation);
1230                self.vertical_gap.insert_animation(animation, self.add_transition(transition));
1231                self.vertical_gap.insert_transition(rule_id, animation);
1232            }
1233
1234            "width" => {
1235                self.width.insert_animation(animation, self.add_transition(transition));
1236                self.width.insert_transition(rule_id, animation);
1237            }
1238
1239            "height" => {
1240                self.height.insert_animation(animation, self.add_transition(transition));
1241                self.height.insert_transition(rule_id, animation);
1242            }
1243
1244            "min-width" => {
1245                self.min_width.insert_animation(animation, self.add_transition(transition));
1246                self.min_width.insert_transition(rule_id, animation);
1247            }
1248
1249            "max-width" => {
1250                self.max_width.insert_animation(animation, self.add_transition(transition));
1251                self.max_width.insert_transition(rule_id, animation);
1252            }
1253
1254            "min-height" => {
1255                self.min_height.insert_animation(animation, self.add_transition(transition));
1256                self.min_height.insert_transition(rule_id, animation);
1257            }
1258
1259            "max-height" => {
1260                self.max_height.insert_animation(animation, self.add_transition(transition));
1261                self.max_height.insert_transition(rule_id, animation);
1262            }
1263
1264            "min-horizontal-gap" => {
1265                self.min_horizontal_gap
1266                    .insert_animation(animation, self.add_transition(transition));
1267                self.min_horizontal_gap.insert_transition(rule_id, animation);
1268            }
1269
1270            "max-horizontal-gap" => {
1271                self.max_horizontal_gap
1272                    .insert_animation(animation, self.add_transition(transition));
1273                self.max_horizontal_gap.insert_transition(rule_id, animation);
1274            }
1275
1276            "min-vertical-gap" => {
1277                self.min_vertical_gap.insert_animation(animation, self.add_transition(transition));
1278                self.min_vertical_gap.insert_transition(rule_id, animation);
1279            }
1280
1281            "max-vertical-gap" => {
1282                self.max_vertical_gap.insert_animation(animation, self.add_transition(transition));
1283                self.max_vertical_gap.insert_transition(rule_id, animation);
1284            }
1285
1286            "text-decoration-color" => {
1287                self.text_decoration_color
1288                    .insert_animation(animation, self.add_transition(transition));
1289                self.text_decoration_color.insert_transition(rule_id, animation);
1290            }
1291
1292            "fill" => {
1293                self.fill.insert_animation(animation, self.add_transition(transition));
1294                self.fill.insert_transition(rule_id, animation);
1295            }
1296
1297            property_name if property_name.starts_with("--") => {
1298                let mut s = DefaultHasher::new();
1299                property_name.hash(&mut s);
1300                let variable_name_hash = s.finish();
1301                // Pre-compute one typed AnimationState per store before taking any mutable
1302                // borrows (add_transition takes &self so this is fine).
1303                let anim_color: AnimationState<Color> = self.add_transition(transition);
1304                let anim_length: AnimationState<LengthOrPercentage> =
1305                    self.add_transition(transition);
1306                let anim_font_size: AnimationState<FontSize> = self.add_transition(transition);
1307                let anim_letter_spacing: AnimationState<LetterSpacing> =
1308                    self.add_transition(transition);
1309                let anim_line_height: AnimationState<LineHeight> = self.add_transition(transition);
1310                let anim_units: AnimationState<Units> = self.add_transition(transition);
1311                let anim_opacity: AnimationState<Opacity> = self.add_transition(transition);
1312
1313                // Register in every custom-property store so whichever store the variable's
1314                // concrete value ends up in will actually animate.
1315                if let Some(store) = self.custom_color_props.get_mut(&variable_name_hash) {
1316                    store.insert_animation(animation, anim_color);
1317                    store.insert_transition(rule_id, animation);
1318                } else {
1319                    let mut store = AnimatableVarSet::default();
1320                    store.insert_animation(animation, anim_color);
1321                    store.insert_transition(rule_id, animation);
1322                    self.custom_color_props.insert(variable_name_hash, store);
1323                }
1324
1325                if let Some(store) = self.custom_length_props.get_mut(&variable_name_hash) {
1326                    store.insert_animation(animation, anim_length);
1327                    store.insert_transition(rule_id, animation);
1328                } else {
1329                    let mut store = AnimatableVarSet::default();
1330                    store.insert_animation(animation, anim_length);
1331                    store.insert_transition(rule_id, animation);
1332                    self.custom_length_props.insert(variable_name_hash, store);
1333                }
1334
1335                if let Some(store) = self.custom_font_size_props.get_mut(&variable_name_hash) {
1336                    store.insert_animation(animation, anim_font_size);
1337                    store.insert_transition(rule_id, animation);
1338                } else {
1339                    let mut store = AnimatableVarSet::default();
1340                    store.insert_animation(animation, anim_font_size);
1341                    store.insert_transition(rule_id, animation);
1342                    self.custom_font_size_props.insert(variable_name_hash, store);
1343                }
1344
1345                if let Some(store) = self.custom_letter_spacing_props.get_mut(&variable_name_hash) {
1346                    store.insert_animation(animation, anim_letter_spacing);
1347                    store.insert_transition(rule_id, animation);
1348                } else {
1349                    let mut store = AnimatableVarSet::default();
1350                    store.insert_animation(animation, anim_letter_spacing);
1351                    store.insert_transition(rule_id, animation);
1352                    self.custom_letter_spacing_props.insert(variable_name_hash, store);
1353                }
1354
1355                if let Some(store) = self.custom_line_height_props.get_mut(&variable_name_hash) {
1356                    store.insert_animation(animation, anim_line_height);
1357                    store.insert_transition(rule_id, animation);
1358                } else {
1359                    let mut store = AnimatableVarSet::default();
1360                    store.insert_animation(animation, anim_line_height);
1361                    store.insert_transition(rule_id, animation);
1362                    self.custom_line_height_props.insert(variable_name_hash, store);
1363                }
1364
1365                if let Some(store) = self.custom_units_props.get_mut(&variable_name_hash) {
1366                    store.insert_animation(animation, anim_units);
1367                    store.insert_transition(rule_id, animation);
1368                } else {
1369                    let mut store = AnimatableVarSet::default();
1370                    store.insert_animation(animation, anim_units);
1371                    store.insert_transition(rule_id, animation);
1372                    self.custom_units_props.insert(variable_name_hash, store);
1373                }
1374
1375                if let Some(store) = self.custom_opacity_props.get_mut(&variable_name_hash) {
1376                    store.insert_animation(animation, anim_opacity);
1377                    store.insert_transition(rule_id, animation);
1378                } else {
1379                    let mut store = AnimatableVarSet::default();
1380                    store.insert_animation(animation, anim_opacity);
1381                    store.insert_transition(rule_id, animation);
1382                    self.custom_opacity_props.insert(variable_name_hash, store);
1383                }
1384            }
1385
1386            _ => {}
1387        }
1388    }
1389
1390    fn insert_property(&mut self, rule_id: Rule, property: &Property) {
1391        fn variable_hash(var: &Variable<'_>) -> u64 {
1392            let mut s = DefaultHasher::new();
1393            var.name.hash(&mut s);
1394            s.finish()
1395        }
1396
1397        fn first_fallback_token<'i>(var: &'i Variable<'i>) -> Option<&'i TokenOrValue<'i>> {
1398            var.fallback.as_ref().and_then(|TokenList(tokens)| tokens.first())
1399        }
1400
1401        fn color_fallback(var: &Variable<'_>) -> Option<Color> {
1402            match first_fallback_token(var) {
1403                Some(TokenOrValue::Color(color)) => Some(*color),
1404                _ => None,
1405            }
1406        }
1407
1408        fn length_fallback(var: &Variable<'_>) -> Option<LengthOrPercentage> {
1409            match first_fallback_token(var) {
1410                Some(TokenOrValue::Token(CssToken::Dimension { value, unit, .. }))
1411                    if unit.as_ref().eq_ignore_ascii_case("px") =>
1412                {
1413                    Some(LengthOrPercentage::Length(Length::Value(LengthValue::Px(*value))))
1414                }
1415
1416                Some(TokenOrValue::Token(CssToken::Percentage { unit_value, .. })) => {
1417                    Some(LengthOrPercentage::Percentage(*unit_value * 100.0))
1418                }
1419
1420                _ => None,
1421            }
1422        }
1423
1424        fn font_size_fallback(var: &Variable<'_>) -> Option<FontSize> {
1425            match first_fallback_token(var) {
1426                Some(TokenOrValue::Token(CssToken::Dimension { value, unit, .. }))
1427                    if unit.as_ref().eq_ignore_ascii_case("px") =>
1428                {
1429                    Some(FontSize(Length::Value(LengthValue::Px(*value))))
1430                }
1431
1432                _ => None,
1433            }
1434        }
1435
1436        fn letter_spacing_fallback(var: &Variable<'_>) -> Option<LetterSpacing> {
1437            match first_fallback_token(var) {
1438                Some(TokenOrValue::Token(CssToken::Dimension { value, unit, .. }))
1439                    if unit.as_ref().eq_ignore_ascii_case("px") =>
1440                {
1441                    Some(LetterSpacing::Length(Length::Value(LengthValue::Px(*value))))
1442                }
1443
1444                Some(TokenOrValue::Token(CssToken::Ident(ident)))
1445                    if ident.as_ref().eq_ignore_ascii_case("normal") =>
1446                {
1447                    Some(LetterSpacing::Normal)
1448                }
1449
1450                _ => None,
1451            }
1452        }
1453
1454        fn line_height_fallback(var: &Variable<'_>) -> Option<LineHeight> {
1455            match first_fallback_token(var) {
1456                Some(TokenOrValue::Token(CssToken::Dimension { value, unit, .. }))
1457                    if unit.as_ref().eq_ignore_ascii_case("px") =>
1458                {
1459                    Some(LineHeight::Length(Length::Value(LengthValue::Px(*value))))
1460                }
1461
1462                Some(TokenOrValue::Token(CssToken::Percentage { unit_value, .. })) => {
1463                    Some(LineHeight::Percentage(*unit_value * 100.0))
1464                }
1465
1466                Some(TokenOrValue::Token(CssToken::Number { value, .. })) => {
1467                    Some(LineHeight::Number(*value))
1468                }
1469
1470                Some(TokenOrValue::Token(CssToken::Ident(ident)))
1471                    if ident.as_ref().eq_ignore_ascii_case("normal") =>
1472                {
1473                    Some(LineHeight::Normal)
1474                }
1475
1476                _ => None,
1477            }
1478        }
1479
1480        fn units_fallback(var: &Variable<'_>) -> Option<Units> {
1481            match first_fallback_token(var) {
1482                Some(TokenOrValue::Token(CssToken::Dimension { value, unit, .. }))
1483                    if unit.as_ref().eq_ignore_ascii_case("px") =>
1484                {
1485                    Some(Units::Pixels(*value))
1486                }
1487
1488                Some(TokenOrValue::Token(CssToken::Dimension { value, unit, .. }))
1489                    if unit.as_ref().eq_ignore_ascii_case("s") =>
1490                {
1491                    Some(Units::Stretch(*value))
1492                }
1493
1494                Some(TokenOrValue::Token(CssToken::Ident(ident)))
1495                    if ident.as_ref().eq_ignore_ascii_case("auto") =>
1496                {
1497                    Some(Units::Auto)
1498                }
1499
1500                Some(TokenOrValue::Token(CssToken::Percentage { unit_value, .. })) => {
1501                    Some(Units::Percentage(*unit_value * 100.0))
1502                }
1503
1504                _ => None,
1505            }
1506        }
1507
1508        fn opacity_fallback(var: &Variable<'_>) -> Option<Opacity> {
1509            match first_fallback_token(var) {
1510                Some(TokenOrValue::Token(CssToken::Percentage { unit_value, .. })) => {
1511                    Some(Opacity(*unit_value))
1512                }
1513
1514                Some(TokenOrValue::Token(CssToken::Number { value, .. })) => Some(Opacity(*value)),
1515
1516                _ => None,
1517            }
1518        }
1519
1520        fn parse_shadow_list(tokens: &[TokenOrValue<'_>]) -> Option<Vec<Shadow>> {
1521            fn parse_shadow_length(token: &TokenOrValue<'_>) -> Option<Length> {
1522                match token {
1523                    TokenOrValue::Token(CssToken::Dimension { value, unit, .. })
1524                        if unit.as_ref().eq_ignore_ascii_case("px") =>
1525                    {
1526                        Some(Length::Value(LengthValue::Px(*value)))
1527                    }
1528
1529                    TokenOrValue::Token(CssToken::Number { value, .. }) if *value == 0.0 => {
1530                        Some(Length::Value(LengthValue::Px(0.0)))
1531                    }
1532
1533                    _ => None,
1534                }
1535            }
1536
1537            fn parse_single_shadow(tokens: &[TokenOrValue<'_>]) -> Option<Shadow> {
1538                let mut lengths: Vec<Length> = Vec::new();
1539                let mut color = None;
1540                let mut inset = false;
1541
1542                for token in tokens {
1543                    match token {
1544                        TokenOrValue::Token(CssToken::WhiteSpace(_)) => {}
1545
1546                        TokenOrValue::Color(c) => {
1547                            if color.is_some() {
1548                                return None;
1549                            }
1550                            color = Some(*c);
1551                        }
1552
1553                        TokenOrValue::Token(CssToken::Ident(ident))
1554                            if ident.as_ref().eq_ignore_ascii_case("inset") =>
1555                        {
1556                            if inset {
1557                                return None;
1558                            }
1559                            inset = true;
1560                        }
1561
1562                        other => {
1563                            if let Some(length) = parse_shadow_length(other) {
1564                                lengths.push(length);
1565                            } else {
1566                                return None;
1567                            }
1568                        }
1569                    }
1570                }
1571
1572                if !(2..=4).contains(&lengths.len()) {
1573                    return None;
1574                }
1575
1576                let x_offset = lengths[0].clone();
1577                let y_offset = lengths[1].clone();
1578                let blur_radius = lengths.get(2).cloned();
1579                let spread_radius = lengths.get(3).cloned();
1580
1581                Some(Shadow::new(x_offset, y_offset, blur_radius, spread_radius, color, inset))
1582            }
1583
1584            let mut parts = Vec::<&[TokenOrValue<'_>]>::new();
1585            let mut start = 0usize;
1586            for (idx, token) in tokens.iter().enumerate() {
1587                if matches!(token, TokenOrValue::Token(CssToken::Comma)) {
1588                    parts.push(&tokens[start..idx]);
1589                    start = idx + 1;
1590                }
1591            }
1592            parts.push(&tokens[start..]);
1593
1594            let mut parsed = Vec::new();
1595            for part in parts {
1596                let shadow = parse_single_shadow(part)?;
1597                parsed.push(shadow);
1598            }
1599
1600            Some(parsed)
1601        }
1602
1603        fn shadow_fallback(var: &Variable<'_>) -> Option<Vec<Shadow>> {
1604            var.fallback.as_ref().and_then(|TokenList(tokens)| parse_shadow_list(tokens))
1605        }
1606
1607        match property.clone() {
1608            // Display
1609            Property::Display(display) => {
1610                self.display.insert_rule(rule_id, display);
1611            }
1612
1613            // Visibility
1614            Property::Visibility(visibility) => {
1615                self.visibility.insert_rule(rule_id, visibility);
1616            }
1617
1618            // Opacity
1619            Property::Opacity(opacity) => {
1620                self.opacity.insert_rule(rule_id, opacity);
1621            }
1622
1623            // Clipping
1624            Property::ClipPath(clip) => {
1625                self.clip_path.insert_rule(rule_id, clip);
1626            }
1627
1628            // Filters
1629            Property::Filter(filter) => {
1630                self.filter.insert_rule(rule_id, filter);
1631            }
1632
1633            Property::BackdropFilter(filter) => {
1634                self.backdrop_filter.insert_rule(rule_id, filter);
1635            }
1636
1637            // Blend Mode
1638            Property::BlendMode(blend_mode) => {
1639                self.blend_mode.insert_rule(rule_id, blend_mode);
1640            }
1641
1642            // Layout Type
1643            Property::LayoutType(layout_type) => {
1644                self.layout_type.insert_rule(rule_id, layout_type);
1645            }
1646
1647            // Position Type
1648            Property::PositionType(position) => {
1649                self.position_type.insert_rule(rule_id, position);
1650            }
1651
1652            Property::Alignment(alignment) => {
1653                self.alignment.insert_rule(rule_id, alignment);
1654            }
1655
1656            Property::Direction(direction) => {
1657                self.direction.insert_rule(rule_id, direction);
1658            }
1659
1660            Property::Wrap(value) => {
1661                self.wrap.insert_rule(rule_id, value);
1662            }
1663            Property::GridColumns(columns) => {
1664                self.grid_columns.insert_rule(rule_id, columns);
1665            }
1666
1667            Property::GridRows(rows) => {
1668                self.grid_rows.insert_rule(rule_id, rows);
1669            }
1670
1671            Property::ColumnStart(start) => {
1672                self.column_start.insert_rule(rule_id, start);
1673            }
1674
1675            Property::ColumnSpan(span) => {
1676                self.column_span.insert_rule(rule_id, span);
1677            }
1678
1679            Property::RowStart(start) => {
1680                self.row_start.insert_rule(rule_id, start);
1681            }
1682
1683            Property::RowSpan(span) => {
1684                self.row_span.insert_rule(rule_id, span);
1685            }
1686
1687            // Space
1688            Property::Space(space) => {
1689                self.left.insert_rule(rule_id, space);
1690                self.right.insert_rule(rule_id, space);
1691                self.top.insert_rule(rule_id, space);
1692                self.bottom.insert_rule(rule_id, space);
1693            }
1694
1695            Property::Left(left) => {
1696                self.left.insert_rule(rule_id, left);
1697            }
1698
1699            Property::Right(right) => {
1700                self.right.insert_rule(rule_id, right);
1701            }
1702
1703            Property::Top(top) => {
1704                self.top.insert_rule(rule_id, top);
1705            }
1706
1707            Property::Bottom(bottom) => {
1708                self.bottom.insert_rule(rule_id, bottom);
1709            }
1710
1711            // Size
1712            Property::Size(size) => {
1713                self.width.insert_rule(rule_id, size);
1714                self.height.insert_rule(rule_id, size);
1715            }
1716
1717            Property::Width(width) => {
1718                self.width.insert_rule(rule_id, width);
1719            }
1720
1721            Property::Height(height) => {
1722                self.height.insert_rule(rule_id, height);
1723            }
1724
1725            // Padding
1726            Property::Padding(padding) => {
1727                self.padding_left.insert_rule(rule_id, padding);
1728                self.padding_right.insert_rule(rule_id, padding);
1729                self.padding_top.insert_rule(rule_id, padding);
1730                self.padding_bottom.insert_rule(rule_id, padding);
1731            }
1732
1733            Property::PaddingLeft(padding_left) => {
1734                self.padding_left.insert_rule(rule_id, padding_left);
1735            }
1736
1737            Property::PaddingRight(padding_right) => {
1738                self.padding_right.insert_rule(rule_id, padding_right);
1739            }
1740
1741            Property::PaddingTop(padding_top) => {
1742                self.padding_top.insert_rule(rule_id, padding_top);
1743            }
1744
1745            Property::PaddingBottom(padding_bottom) => {
1746                self.padding_bottom.insert_rule(rule_id, padding_bottom);
1747            }
1748
1749            Property::VerticalGap(vertical_gap) => {
1750                self.vertical_gap.insert_rule(rule_id, vertical_gap);
1751            }
1752
1753            Property::HorizontalGap(horizontal_gap) => {
1754                self.horizontal_gap.insert_rule(rule_id, horizontal_gap);
1755            }
1756
1757            Property::Gap(gap) => {
1758                self.horizontal_gap.insert_rule(rule_id, gap);
1759                self.vertical_gap.insert_rule(rule_id, gap);
1760            }
1761
1762            // Size Constraints
1763            Property::MinSize(min_size) => {
1764                self.min_width.insert_rule(rule_id, min_size);
1765                self.min_height.insert_rule(rule_id, min_size);
1766            }
1767
1768            Property::MinWidth(min_width) => {
1769                self.min_width.insert_rule(rule_id, min_width);
1770            }
1771
1772            Property::MinHeight(min_height) => {
1773                self.min_height.insert_rule(rule_id, min_height);
1774            }
1775
1776            Property::MaxSize(max_size) => {
1777                self.max_width.insert_rule(rule_id, max_size);
1778                self.max_height.insert_rule(rule_id, max_size);
1779            }
1780
1781            Property::MaxWidth(max_width) => {
1782                self.max_width.insert_rule(rule_id, max_width);
1783            }
1784
1785            Property::MaxHeight(max_height) => {
1786                self.max_height.insert_rule(rule_id, max_height);
1787            }
1788
1789            // Gap Constraints
1790            Property::MinGap(min_gap) => {
1791                self.min_horizontal_gap.insert_rule(rule_id, min_gap);
1792                self.min_vertical_gap.insert_rule(rule_id, min_gap);
1793            }
1794
1795            Property::MinHorizontalGap(min_gap) => {
1796                self.min_horizontal_gap.insert_rule(rule_id, min_gap);
1797            }
1798
1799            Property::MinVerticalGap(min_gap) => {
1800                self.min_vertical_gap.insert_rule(rule_id, min_gap);
1801            }
1802
1803            Property::MaxGap(max_gap) => {
1804                self.max_horizontal_gap.insert_rule(rule_id, max_gap);
1805                self.max_vertical_gap.insert_rule(rule_id, max_gap);
1806            }
1807
1808            Property::MaxHorizontalGap(max_gap) => {
1809                self.max_horizontal_gap.insert_rule(rule_id, max_gap);
1810            }
1811
1812            Property::MaxVerticalGap(max_gap) => {
1813                self.max_vertical_gap.insert_rule(rule_id, max_gap);
1814            }
1815
1816            // Background Colour
1817            Property::BackgroundColor(color) => {
1818                self.background_color.insert_rule(rule_id, color);
1819            }
1820
1821            // Border
1822            Property::Border(border) => {
1823                if let Some(border_color) = border.color {
1824                    self.border_color.insert_rule(rule_id, border_color);
1825                }
1826
1827                if let Some(border_width) = border.width {
1828                    self.border_width.insert_rule(rule_id, border_width.into());
1829                }
1830
1831                if let Some(border_style) = border.style {
1832                    self.border_style.insert_rule(rule_id, border_style.top);
1833                }
1834            }
1835
1836            // Border
1837            Property::BorderWidth(border_width) => {
1838                self.border_width.insert_rule(rule_id, border_width.top.0);
1839            }
1840
1841            Property::BorderColor(color) => {
1842                self.border_color.insert_rule(rule_id, color);
1843            }
1844
1845            Property::BorderStyle(style) => {
1846                self.border_style.insert_rule(rule_id, style.top);
1847            }
1848
1849            // Border Radius
1850            Property::CornerRadius(corner_radius) => {
1851                self.corner_bottom_left_radius.insert_rule(rule_id, corner_radius.bottom_left);
1852                self.corner_bottom_right_radius.insert_rule(rule_id, corner_radius.bottom_right);
1853                self.corner_top_left_radius.insert_rule(rule_id, corner_radius.top_left);
1854                self.corner_top_right_radius.insert_rule(rule_id, corner_radius.top_right);
1855            }
1856
1857            Property::CornerBottomLeftRadius(corner_radius) => {
1858                self.corner_bottom_left_radius.insert_rule(rule_id, corner_radius);
1859            }
1860
1861            Property::CornerTopLeftRadius(corner_radius) => {
1862                self.corner_top_left_radius.insert_rule(rule_id, corner_radius);
1863            }
1864
1865            Property::CornerBottomRightRadius(corner_radius) => {
1866                self.corner_bottom_right_radius.insert_rule(rule_id, corner_radius);
1867            }
1868
1869            Property::CornerTopRightRadius(corner_radius) => {
1870                self.corner_top_right_radius.insert_rule(rule_id, corner_radius);
1871            }
1872
1873            // Corner Shape
1874            Property::CornerShape(corner_shape) => {
1875                self.corner_top_left_shape.insert_rule(rule_id, corner_shape.0);
1876                self.corner_top_right_shape.insert_rule(rule_id, corner_shape.1);
1877                self.corner_bottom_right_shape.insert_rule(rule_id, corner_shape.2);
1878                self.corner_bottom_left_shape.insert_rule(rule_id, corner_shape.3);
1879            }
1880
1881            Property::CornerTopLeftShape(corner_shape) => {
1882                self.corner_top_left_shape.insert_rule(rule_id, corner_shape);
1883            }
1884
1885            Property::CornerTopRightShape(corner_shape) => {
1886                self.corner_top_right_shape.insert_rule(rule_id, corner_shape);
1887            }
1888
1889            Property::CornerBottomLeftShape(corner_shape) => {
1890                self.corner_bottom_left_shape.insert_rule(rule_id, corner_shape);
1891            }
1892
1893            Property::CornerBottomRightShape(corner_shape) => {
1894                self.corner_bottom_right_shape.insert_rule(rule_id, corner_shape);
1895            }
1896
1897            // Font Family
1898            Property::FontFamily(font_family) => {
1899                self.font_family.insert_rule(
1900                    rule_id,
1901                    font_family
1902                        .iter()
1903                        .map(|family| match family {
1904                            FontFamily::Named(name) => FamilyOwned::Named(name.to_string()),
1905                            FontFamily::Generic(generic) => FamilyOwned::Generic(*generic),
1906                        })
1907                        .collect::<Vec<_>>(),
1908                );
1909            }
1910
1911            // Font Color
1912            Property::FontColor(font_color) => {
1913                self.font_color.insert_rule(rule_id, font_color);
1914            }
1915
1916            // Font Size
1917            Property::FontSize(font_size) => {
1918                self.font_size.insert_rule(rule_id, font_size);
1919            }
1920
1921            // Letter Spacing
1922            Property::LetterSpacing(letter_spacing) => {
1923                self.letter_spacing.insert_rule(rule_id, letter_spacing);
1924            }
1925
1926            // Font Weight
1927            Property::FontWeight(font_weight) => {
1928                self.font_weight.insert_rule(rule_id, font_weight);
1929            }
1930
1931            // Font Slant
1932            Property::FontSlant(font_slant) => {
1933                self.font_slant.insert_rule(rule_id, font_slant);
1934            }
1935
1936            // Font Width
1937            Property::FontWidth(font_width) => {
1938                self.font_width.insert_rule(rule_id, font_width);
1939            }
1940
1941            // Font Variation Settings
1942            Property::FontVariationSettings(font_variation_settings) => {
1943                self.font_variation_settings.insert_rule(rule_id, font_variation_settings);
1944            }
1945
1946            // Caret Color
1947            Property::CaretColor(caret_color) => {
1948                self.caret_color.insert_rule(rule_id, caret_color);
1949            }
1950
1951            // Selection Color
1952            Property::SelectionColor(selection_color) => {
1953                self.selection_color.insert_rule(rule_id, selection_color);
1954            }
1955
1956            // Transform
1957            Property::Transform(transforms) => {
1958                self.transform.insert_rule(rule_id, transforms);
1959            }
1960
1961            Property::TransformOrigin(transform_origin) => {
1962                let x = transform_origin.x.to_length_or_percentage();
1963                let y = transform_origin.y.to_length_or_percentage();
1964                self.transform_origin.insert_rule(rule_id, Translate { x, y });
1965            }
1966
1967            Property::Translate(translate) => {
1968                self.translate.insert_rule(rule_id, translate);
1969            }
1970
1971            Property::Rotate(rotate) => {
1972                self.rotate.insert_rule(rule_id, rotate);
1973            }
1974
1975            Property::Scale(scale) => {
1976                self.scale.insert_rule(rule_id, scale);
1977            }
1978
1979            // Overflow
1980            Property::Overflow(overflow) => {
1981                self.overflowx.insert_rule(rule_id, overflow);
1982                self.overflowy.insert_rule(rule_id, overflow);
1983            }
1984
1985            Property::OverflowX(overflow) => {
1986                self.overflowx.insert_rule(rule_id, overflow);
1987            }
1988
1989            Property::OverflowY(overflow) => {
1990                self.overflowy.insert_rule(rule_id, overflow);
1991            }
1992
1993            // Z Index
1994            Property::ZIndex(z_index) => self.z_index.insert_rule(rule_id, z_index),
1995
1996            // Outline
1997            Property::Outline(outline) => {
1998                if let Some(outline_color) = outline.color {
1999                    self.outline_color.insert_rule(rule_id, outline_color);
2000                }
2001
2002                if let Some(outline_width) = outline.width {
2003                    self.outline_width.insert_rule(rule_id, outline_width.into());
2004                }
2005            }
2006
2007            Property::OutlineColor(outline_color) => {
2008                self.outline_color.insert_rule(rule_id, outline_color);
2009            }
2010
2011            Property::OutlineWidth(outline_width) => {
2012                self.outline_width.insert_rule(rule_id, outline_width.left.0);
2013            }
2014
2015            Property::OutlineOffset(outline_offset) => {
2016                self.outline_offset.insert_rule(rule_id, outline_offset);
2017            }
2018
2019            // Background Images & Gradients
2020            Property::BackgroundImage(images) => {
2021                let images = images
2022                    .into_iter()
2023                    .filter_map(|img| match img {
2024                        BackgroundImage::None => None,
2025                        BackgroundImage::Gradient(gradient) => {
2026                            Some(ImageOrGradient::Gradient(*gradient))
2027                        }
2028                        BackgroundImage::Url(url) => {
2029                            Some(ImageOrGradient::Image(url.url.to_string()))
2030                        }
2031                    })
2032                    .collect::<Vec<_>>();
2033
2034                self.background_image.insert_rule(rule_id, images);
2035            }
2036
2037            // Background Size
2038            Property::BackgroundSize(sizes) => {
2039                self.background_size.insert_rule(rule_id, sizes);
2040            }
2041
2042            // Text Wrapping
2043            Property::TextWrap(text_wrap) => {
2044                self.text_wrap.insert_rule(rule_id, text_wrap);
2045            }
2046
2047            // Text Alignment
2048            Property::TextAlign(text_align) => {
2049                self.text_align.insert_rule(rule_id, text_align);
2050            }
2051
2052            // Box Shadows
2053            Property::Shadow(shadows) => {
2054                self.shadow.insert_rule(rule_id, shadows);
2055            }
2056
2057            // Cursor Icon
2058            Property::Cursor(cursor) => {
2059                self.cursor.insert_rule(rule_id, cursor);
2060            }
2061
2062            Property::PointerEvents(pointer_events) => {
2063                self.pointer_events.insert_rule(rule_id, pointer_events);
2064            }
2065
2066            Property::TextOverflow(text_overflow) => {
2067                self.text_overflow.insert_rule(rule_id, text_overflow);
2068            }
2069            Property::LineHeight(line_height) => {
2070                self.line_height.insert_rule(rule_id, line_height);
2071            }
2072            Property::LineClamp(line_clamp) => {
2073                self.line_clamp.insert_rule(rule_id, line_clamp);
2074            }
2075            Property::TextDecoration(decoration) => {
2076                self.text_decoration_line.insert_rule(rule_id, decoration.line);
2077                self.text_decoration_style.insert_rule(rule_id, decoration.style);
2078                self.text_decoration_color.insert_rule(rule_id, decoration.color.into());
2079            }
2080            Property::TextDecorationLine(line) => {
2081                self.text_decoration_line.insert_rule(rule_id, line);
2082            }
2083            Property::TextDecorationColor(decoration_color) => {
2084                self.text_decoration_color.insert_rule(rule_id, decoration_color);
2085            }
2086            Property::TextDecorationStyle(decoration_style) => {
2087                self.text_decoration_style.insert_rule(rule_id, decoration_style);
2088            }
2089            Property::TextStroke(stroke) => {
2090                self.text_stroke_width.insert_rule(rule_id, stroke.width);
2091                self.text_stroke_style.insert_rule(rule_id, stroke.style);
2092            }
2093            Property::TextStrokeWidth(stroke_width) => {
2094                self.text_stroke_width.insert_rule(rule_id, stroke_width);
2095            }
2096            Property::TextStrokeStyle(stroke_style) => {
2097                self.text_stroke_style.insert_rule(rule_id, stroke_style);
2098            }
2099            Property::Fill(fill) => {
2100                self.fill.insert_rule(rule_id, fill);
2101            }
2102
2103            // Unparsed. TODO: Log the error.
2104            Property::Unparsed(unparsed) => {
2105                macro_rules! parse_color_var {
2106                    ($prop:expr) => {
2107                        if let Some(TokenOrValue::Var(var)) = unparsed.value.0.first() {
2108                            $prop.insert_variable_rule(
2109                                rule_id,
2110                                variable_hash(var),
2111                                color_fallback(var),
2112                            );
2113                        }
2114                    };
2115                }
2116                macro_rules! parse_length_var {
2117                    ($($prop:expr),+) => {
2118                        if let Some(TokenOrValue::Var(var)) = unparsed.value.0.first() {
2119                            let hash = variable_hash(var);
2120                            let fallback = length_fallback(var);
2121                            $($prop.insert_variable_rule(rule_id, hash, fallback.clone());)+
2122                        }
2123                    };
2124                }
2125                macro_rules! parse_font_size_var {
2126                    ($prop:expr) => {
2127                        if let Some(TokenOrValue::Var(var)) = unparsed.value.0.first() {
2128                            $prop.insert_variable_rule(
2129                                rule_id,
2130                                variable_hash(var),
2131                                font_size_fallback(var),
2132                            );
2133                        }
2134                    };
2135                }
2136                macro_rules! parse_letter_spacing_var {
2137                    ($prop:expr) => {
2138                        if let Some(TokenOrValue::Var(var)) = unparsed.value.0.first() {
2139                            $prop.insert_variable_rule(
2140                                rule_id,
2141                                variable_hash(var),
2142                                letter_spacing_fallback(var),
2143                            );
2144                        }
2145                    };
2146                }
2147                macro_rules! parse_line_height_var {
2148                    ($prop:expr) => {
2149                        if let Some(TokenOrValue::Var(var)) = unparsed.value.0.first() {
2150                            $prop.insert_variable_rule(
2151                                rule_id,
2152                                variable_hash(var),
2153                                line_height_fallback(var),
2154                            );
2155                        }
2156                    };
2157                }
2158                macro_rules! parse_units_var {
2159                    ($($prop:expr),+) => {
2160                        if let Some(TokenOrValue::Var(var)) = unparsed.value.0.first() {
2161                            let hash = variable_hash(var);
2162                            let fallback = units_fallback(var);
2163                            $($prop.insert_variable_rule(rule_id, hash, fallback.clone());)+
2164                        }
2165                    };
2166                }
2167                match unparsed.name.as_ref() {
2168                    "background-color" => parse_color_var!(self.background_color),
2169                    "border-color" => parse_color_var!(self.border_color),
2170                    "outline-color" => parse_color_var!(self.outline_color),
2171                    "color" => parse_color_var!(self.font_color),
2172                    "caret-color" => parse_color_var!(self.caret_color),
2173                    "selection-color" => parse_color_var!(self.selection_color),
2174                    "fill" => parse_color_var!(self.fill),
2175                    "text-decoration-color" => parse_color_var!(self.text_decoration_color),
2176                    "font-size" => parse_font_size_var!(self.font_size),
2177                    "letter-spacing" => parse_letter_spacing_var!(self.letter_spacing),
2178                    "line-height" => parse_line_height_var!(self.line_height),
2179                    "corner-radius" => parse_length_var!(
2180                        self.corner_top_left_radius,
2181                        self.corner_top_right_radius,
2182                        self.corner_bottom_left_radius,
2183                        self.corner_bottom_right_radius
2184                    ),
2185                    "corner-top-left-radius" => parse_length_var!(self.corner_top_left_radius),
2186                    "corner-top-right-radius" => parse_length_var!(self.corner_top_right_radius),
2187                    "corner-bottom-left-radius" => {
2188                        parse_length_var!(self.corner_bottom_left_radius)
2189                    }
2190                    "corner-bottom-right-radius" => {
2191                        parse_length_var!(self.corner_bottom_right_radius)
2192                    }
2193                    "border-width" => parse_length_var!(self.border_width),
2194                    "border" => {
2195                        if let Some(TokenOrValue::Var(var)) = unparsed.value.0.first() {
2196                            let hash = variable_hash(var);
2197                            self.border_width.insert_variable_rule(
2198                                rule_id,
2199                                hash,
2200                                length_fallback(var),
2201                            );
2202                            self.border_color.insert_variable_rule(
2203                                rule_id,
2204                                hash,
2205                                color_fallback(var),
2206                            );
2207                        }
2208                    }
2209                    "outline" => {
2210                        if let Some(TokenOrValue::Var(var)) = unparsed.value.0.first() {
2211                            let hash = variable_hash(var);
2212                            self.outline_width.insert_variable_rule(
2213                                rule_id,
2214                                hash,
2215                                length_fallback(var),
2216                            );
2217                            self.outline_color.insert_variable_rule(
2218                                rule_id,
2219                                hash,
2220                                color_fallback(var),
2221                            );
2222                        }
2223                    }
2224                    "outline-width" => parse_length_var!(self.outline_width),
2225                    "outline-offset" => parse_length_var!(self.outline_offset),
2226                    "left" => parse_units_var!(self.left),
2227                    "right" => parse_units_var!(self.right),
2228                    "top" => parse_units_var!(self.top),
2229                    "bottom" => parse_units_var!(self.bottom),
2230                    "space" => parse_units_var!(self.left, self.right, self.top, self.bottom),
2231                    "width" => parse_units_var!(self.width),
2232                    "height" => parse_units_var!(self.height),
2233                    "size" => parse_units_var!(self.width, self.height),
2234                    "min-width" => parse_units_var!(self.min_width),
2235                    "max-width" => parse_units_var!(self.max_width),
2236                    "min-height" => parse_units_var!(self.min_height),
2237                    "max-height" => parse_units_var!(self.max_height),
2238                    "min-size" => parse_units_var!(self.min_width, self.min_height),
2239                    "max-size" => parse_units_var!(self.max_width, self.max_height),
2240                    "padding-left" => parse_units_var!(self.padding_left),
2241                    "padding-right" => parse_units_var!(self.padding_right),
2242                    "padding-top" => parse_units_var!(self.padding_top),
2243                    "padding-bottom" => parse_units_var!(self.padding_bottom),
2244                    "padding" => parse_units_var!(
2245                        self.padding_left,
2246                        self.padding_right,
2247                        self.padding_top,
2248                        self.padding_bottom
2249                    ),
2250                    "row-gap" | "vertical-gap" => parse_units_var!(self.vertical_gap),
2251                    "column-gap" | "horizontal-gap" => parse_units_var!(self.horizontal_gap),
2252                    "gap" => parse_units_var!(self.vertical_gap, self.horizontal_gap),
2253                    "min-gap" => {
2254                        parse_units_var!(self.min_horizontal_gap, self.min_vertical_gap)
2255                    }
2256                    "max-gap" => {
2257                        parse_units_var!(self.max_horizontal_gap, self.max_vertical_gap)
2258                    }
2259                    "opacity" => {
2260                        if let Some(TokenOrValue::Var(var)) = unparsed.value.0.first() {
2261                            self.opacity.insert_variable_rule(
2262                                rule_id,
2263                                variable_hash(var),
2264                                opacity_fallback(var),
2265                            );
2266                        }
2267                    }
2268                    "shadow" => {
2269                        if let Some(TokenOrValue::Var(var)) = unparsed.value.0.first() {
2270                            self.shadow.insert_variable_rule(
2271                                rule_id,
2272                                variable_hash(var),
2273                                shadow_fallback(var),
2274                            );
2275                        }
2276                    }
2277                    n => warn!("Unparsed {} {:?}", n, unparsed.value),
2278                }
2279            }
2280
2281            Property::Custom(custom) => {
2282                let mut s = DefaultHasher::new();
2283                custom.name.hash(&mut s);
2284                let variable_name_hash = s.finish();
2285
2286                if let Some(shadows) = parse_shadow_list(&custom.value.0) {
2287                    if let Some(store) = self.custom_shadow_props.get_mut(&variable_name_hash) {
2288                        store.insert_rule(rule_id, shadows);
2289                    } else {
2290                        let mut store = AnimatableVarSet::default();
2291                        store.insert_rule(rule_id, shadows);
2292                        self.custom_shadow_props.insert(variable_name_hash, store);
2293                    }
2294                }
2295
2296                // Parse custom properties and store them
2297                for token in custom.value.0.iter() {
2298                    // Try parsing colors
2299                    if let TokenOrValue::Color(color) = token {
2300                        if let Some(store) = self.custom_color_props.get_mut(&variable_name_hash) {
2301                            store.insert_rule(rule_id, *color);
2302                        } else {
2303                            let mut store = AnimatableVarSet::default();
2304                            store.insert_rule(rule_id, *color);
2305                            self.custom_color_props.insert(variable_name_hash, store);
2306                        }
2307                    }
2308
2309                    // Parse length/percentage tokens into custom_length_props
2310                    match token {
2311                        TokenOrValue::Token(CssToken::Dimension { value, unit, .. }) => {
2312                            let lop = if unit.as_ref().eq_ignore_ascii_case("px") {
2313                                Some(LengthOrPercentage::Length(Length::Value(LengthValue::Px(
2314                                    *value,
2315                                ))))
2316                            } else {
2317                                None
2318                            };
2319                            if let Some(lop) = lop {
2320                                if let Some(store) =
2321                                    self.custom_length_props.get_mut(&variable_name_hash)
2322                                {
2323                                    store.insert_rule(rule_id, lop.clone());
2324                                } else {
2325                                    let mut store = AnimatableVarSet::default();
2326                                    store.insert_rule(rule_id, lop.clone());
2327                                    self.custom_length_props.insert(variable_name_hash, store);
2328                                }
2329                                // Also try storing as FontSize
2330                                let fs = FontSize(Length::Value(LengthValue::Px(*value)));
2331                                if let Some(store) =
2332                                    self.custom_font_size_props.get_mut(&variable_name_hash)
2333                                {
2334                                    store.insert_rule(rule_id, fs);
2335                                } else {
2336                                    let mut store = AnimatableVarSet::default();
2337                                    store.insert_rule(rule_id, fs);
2338                                    self.custom_font_size_props.insert(variable_name_hash, store);
2339                                }
2340                                // Also try storing as LetterSpacing::Length
2341                                let letter_spacing =
2342                                    LetterSpacing::Length(Length::Value(LengthValue::Px(*value)));
2343                                if let Some(store) =
2344                                    self.custom_letter_spacing_props.get_mut(&variable_name_hash)
2345                                {
2346                                    store.insert_rule(rule_id, letter_spacing);
2347                                } else {
2348                                    let mut store = AnimatableVarSet::default();
2349                                    store.insert_rule(rule_id, letter_spacing);
2350                                    self.custom_letter_spacing_props
2351                                        .insert(variable_name_hash, store);
2352                                }
2353                                // Also try storing as LineHeight::Length
2354                                let line_height =
2355                                    LineHeight::Length(Length::Value(LengthValue::Px(*value)));
2356                                if let Some(store) =
2357                                    self.custom_line_height_props.get_mut(&variable_name_hash)
2358                                {
2359                                    store.insert_rule(rule_id, line_height);
2360                                } else {
2361                                    let mut store = AnimatableVarSet::default();
2362                                    store.insert_rule(rule_id, line_height);
2363                                    self.custom_line_height_props.insert(variable_name_hash, store);
2364                                }
2365                                // Also store as Units::Pixels
2366                                let units_val = Units::Pixels(*value);
2367                                if let Some(store) =
2368                                    self.custom_units_props.get_mut(&variable_name_hash)
2369                                {
2370                                    store.insert_rule(rule_id, units_val);
2371                                } else {
2372                                    let mut store = AnimatableVarSet::default();
2373                                    store.insert_rule(rule_id, units_val);
2374                                    self.custom_units_props.insert(variable_name_hash, store);
2375                                }
2376                            } else if unit.as_ref().eq_ignore_ascii_case("s") {
2377                                // "1s" => Units::Stretch(1.0)
2378                                let units_val = Units::Stretch(*value);
2379                                if let Some(store) =
2380                                    self.custom_units_props.get_mut(&variable_name_hash)
2381                                {
2382                                    store.insert_rule(rule_id, units_val);
2383                                } else {
2384                                    let mut store = AnimatableVarSet::default();
2385                                    store.insert_rule(rule_id, units_val);
2386                                    self.custom_units_props.insert(variable_name_hash, store);
2387                                }
2388                            }
2389                        }
2390                        TokenOrValue::Token(CssToken::Ident(ident))
2391                            if ident.as_ref().eq_ignore_ascii_case("auto") =>
2392                        {
2393                            let units_val = Units::Auto;
2394                            if let Some(store) =
2395                                self.custom_units_props.get_mut(&variable_name_hash)
2396                            {
2397                                store.insert_rule(rule_id, units_val);
2398                            } else {
2399                                let mut store = AnimatableVarSet::default();
2400                                store.insert_rule(rule_id, units_val);
2401                                self.custom_units_props.insert(variable_name_hash, store);
2402                            }
2403                        }
2404                        TokenOrValue::Token(CssToken::Percentage { unit_value, .. }) => {
2405                            let lop = LengthOrPercentage::Percentage(*unit_value * 100.0);
2406                            if let Some(store) =
2407                                self.custom_length_props.get_mut(&variable_name_hash)
2408                            {
2409                                store.insert_rule(rule_id, lop);
2410                            } else {
2411                                let mut store = AnimatableVarSet::default();
2412                                store.insert_rule(rule_id, lop);
2413                                self.custom_length_props.insert(variable_name_hash, store);
2414                            }
2415                            // Also store as LineHeight::Percentage
2416                            let line_height = LineHeight::Percentage(*unit_value * 100.0);
2417                            if let Some(store) =
2418                                self.custom_line_height_props.get_mut(&variable_name_hash)
2419                            {
2420                                store.insert_rule(rule_id, line_height);
2421                            } else {
2422                                let mut store = AnimatableVarSet::default();
2423                                store.insert_rule(rule_id, line_height);
2424                                self.custom_line_height_props.insert(variable_name_hash, store);
2425                            }
2426                            // Also store as Units::Percentage
2427                            let units_val = Units::Percentage(*unit_value * 100.0);
2428                            if let Some(store) =
2429                                self.custom_units_props.get_mut(&variable_name_hash)
2430                            {
2431                                store.insert_rule(rule_id, units_val);
2432                            } else {
2433                                let mut store = AnimatableVarSet::default();
2434                                store.insert_rule(rule_id, units_val);
2435                                self.custom_units_props.insert(variable_name_hash, store);
2436                            }
2437                            // Also store as Opacity (percentage as 0..1)
2438                            let opacity_val = Opacity(*unit_value);
2439                            if let Some(store) =
2440                                self.custom_opacity_props.get_mut(&variable_name_hash)
2441                            {
2442                                store.insert_rule(rule_id, opacity_val);
2443                            } else {
2444                                let mut store = AnimatableVarSet::default();
2445                                store.insert_rule(rule_id, opacity_val);
2446                                self.custom_opacity_props.insert(variable_name_hash, store);
2447                            }
2448                        }
2449                        TokenOrValue::Var(var) => {
2450                            let name_hash = variable_hash(var);
2451                            // Store var reference in all maps (type is unknown at parse time)
2452                            if let Some(store) =
2453                                self.custom_color_props.get_mut(&variable_name_hash)
2454                            {
2455                                store.insert_variable_rule(rule_id, name_hash, color_fallback(var));
2456                            } else {
2457                                let mut store = AnimatableVarSet::default();
2458                                store.insert_variable_rule(rule_id, name_hash, color_fallback(var));
2459                                self.custom_color_props.insert(variable_name_hash, store);
2460                            }
2461                            if let Some(store) =
2462                                self.custom_length_props.get_mut(&variable_name_hash)
2463                            {
2464                                store.insert_variable_rule(
2465                                    rule_id,
2466                                    name_hash,
2467                                    length_fallback(var),
2468                                );
2469                            } else {
2470                                let mut store: AnimatableVarSet<LengthOrPercentage> =
2471                                    AnimatableVarSet::default();
2472                                store.insert_variable_rule(
2473                                    rule_id,
2474                                    name_hash,
2475                                    length_fallback(var),
2476                                );
2477                                self.custom_length_props.insert(variable_name_hash, store);
2478                            }
2479                            if let Some(store) =
2480                                self.custom_font_size_props.get_mut(&variable_name_hash)
2481                            {
2482                                store.insert_variable_rule(
2483                                    rule_id,
2484                                    name_hash,
2485                                    font_size_fallback(var),
2486                                );
2487                            } else {
2488                                let mut store: AnimatableVarSet<FontSize> =
2489                                    AnimatableVarSet::default();
2490                                store.insert_variable_rule(
2491                                    rule_id,
2492                                    name_hash,
2493                                    font_size_fallback(var),
2494                                );
2495                                self.custom_font_size_props.insert(variable_name_hash, store);
2496                            }
2497                            if let Some(store) =
2498                                self.custom_letter_spacing_props.get_mut(&variable_name_hash)
2499                            {
2500                                store.insert_variable_rule(
2501                                    rule_id,
2502                                    name_hash,
2503                                    letter_spacing_fallback(var),
2504                                );
2505                            } else {
2506                                let mut store: AnimatableVarSet<LetterSpacing> =
2507                                    AnimatableVarSet::default();
2508                                store.insert_variable_rule(
2509                                    rule_id,
2510                                    name_hash,
2511                                    letter_spacing_fallback(var),
2512                                );
2513                                self.custom_letter_spacing_props.insert(variable_name_hash, store);
2514                            }
2515                            if let Some(store) =
2516                                self.custom_line_height_props.get_mut(&variable_name_hash)
2517                            {
2518                                store.insert_variable_rule(
2519                                    rule_id,
2520                                    name_hash,
2521                                    line_height_fallback(var),
2522                                );
2523                            } else {
2524                                let mut store: AnimatableVarSet<LineHeight> =
2525                                    AnimatableVarSet::default();
2526                                store.insert_variable_rule(
2527                                    rule_id,
2528                                    name_hash,
2529                                    line_height_fallback(var),
2530                                );
2531                                self.custom_line_height_props.insert(variable_name_hash, store);
2532                            }
2533                            if let Some(store) =
2534                                self.custom_units_props.get_mut(&variable_name_hash)
2535                            {
2536                                store.insert_variable_rule(rule_id, name_hash, units_fallback(var));
2537                            } else {
2538                                let mut store: AnimatableVarSet<Units> =
2539                                    AnimatableVarSet::default();
2540                                store.insert_variable_rule(rule_id, name_hash, units_fallback(var));
2541                                self.custom_units_props.insert(variable_name_hash, store);
2542                            }
2543                            if let Some(store) =
2544                                self.custom_opacity_props.get_mut(&variable_name_hash)
2545                            {
2546                                store.insert_variable_rule(
2547                                    rule_id,
2548                                    name_hash,
2549                                    opacity_fallback(var),
2550                                );
2551                            } else {
2552                                let mut store: AnimatableVarSet<Opacity> =
2553                                    AnimatableVarSet::default();
2554                                store.insert_variable_rule(
2555                                    rule_id,
2556                                    name_hash,
2557                                    opacity_fallback(var),
2558                                );
2559                                self.custom_opacity_props.insert(variable_name_hash, store);
2560                            }
2561
2562                            if let Some(store) =
2563                                self.custom_shadow_props.get_mut(&variable_name_hash)
2564                            {
2565                                store.insert_variable_rule(
2566                                    rule_id,
2567                                    name_hash,
2568                                    shadow_fallback(var),
2569                                );
2570                            } else {
2571                                let mut store: AnimatableVarSet<Vec<Shadow>> =
2572                                    AnimatableVarSet::default();
2573                                store.insert_variable_rule(
2574                                    rule_id,
2575                                    name_hash,
2576                                    shadow_fallback(var),
2577                                );
2578                                self.custom_shadow_props.insert(variable_name_hash, store);
2579                            }
2580                        }
2581                        TokenOrValue::Token(CssToken::Number { value, .. }) => {
2582                            // Plain number like 0.5 → Opacity
2583                            let opacity_val = Opacity(*value);
2584                            if let Some(store) =
2585                                self.custom_opacity_props.get_mut(&variable_name_hash)
2586                            {
2587                                store.insert_rule(rule_id, opacity_val);
2588                            } else {
2589                                let mut store = AnimatableVarSet::default();
2590                                store.insert_rule(rule_id, opacity_val);
2591                                self.custom_opacity_props.insert(variable_name_hash, store);
2592                            }
2593
2594                            // Plain number like 1.2 -> LineHeight::Number
2595                            let line_height = LineHeight::Number(*value);
2596                            if let Some(store) =
2597                                self.custom_line_height_props.get_mut(&variable_name_hash)
2598                            {
2599                                store.insert_rule(rule_id, line_height);
2600                            } else {
2601                                let mut store = AnimatableVarSet::default();
2602                                store.insert_rule(rule_id, line_height);
2603                                self.custom_line_height_props.insert(variable_name_hash, store);
2604                            }
2605                        }
2606                        TokenOrValue::Token(CssToken::Ident(ident))
2607                            if ident.as_ref().eq_ignore_ascii_case("normal") =>
2608                        {
2609                            let line_height = LineHeight::Normal;
2610                            if let Some(store) =
2611                                self.custom_line_height_props.get_mut(&variable_name_hash)
2612                            {
2613                                store.insert_rule(rule_id, line_height);
2614                            } else {
2615                                let mut store = AnimatableVarSet::default();
2616                                store.insert_rule(rule_id, line_height);
2617                                self.custom_line_height_props.insert(variable_name_hash, store);
2618                            }
2619
2620                            let letter_spacing = LetterSpacing::Normal;
2621                            if let Some(store) =
2622                                self.custom_letter_spacing_props.get_mut(&variable_name_hash)
2623                            {
2624                                store.insert_rule(rule_id, letter_spacing);
2625                            } else {
2626                                let mut store = AnimatableVarSet::default();
2627                                store.insert_rule(rule_id, letter_spacing);
2628                                self.custom_letter_spacing_props.insert(variable_name_hash, store);
2629                            }
2630                        }
2631                        _ => {}
2632                    }
2633                }
2634            }
2635            _ => {}
2636        }
2637    }
2638
2639    // Helper function for generating AnimationState from a transition definition.
2640    fn add_transition<T: Default + Interpolator>(
2641        &self,
2642        transition: &Transition,
2643    ) -> AnimationState<T> {
2644        let timing_function = transition
2645            .timing_function
2646            .map(|easing| match easing {
2647                EasingFunction::Linear => TimingFunction::linear(),
2648                EasingFunction::Ease => TimingFunction::ease(),
2649                EasingFunction::EaseIn => TimingFunction::ease_in(),
2650                EasingFunction::EaseOut => TimingFunction::ease_out(),
2651                EasingFunction::EaseInOut => TimingFunction::ease_in_out(),
2652                EasingFunction::CubicBezier(x1, y1, x2, y2) => TimingFunction::new(x1, y1, x2, y2),
2653            })
2654            .unwrap_or_default();
2655
2656        AnimationState::new(Animation::null())
2657            .with_duration(transition.duration)
2658            .with_delay(transition.delay.unwrap_or_default())
2659            .with_keyframe(Keyframe { time: 0.0, value: Default::default(), timing_function })
2660            .with_keyframe(Keyframe { time: 1.0, value: Default::default(), timing_function })
2661    }
2662
2663    // Add style data for the given entity.
2664    pub(crate) fn add(&mut self, entity: Entity) {
2665        self.pseudo_classes.insert(entity, PseudoClassFlags::VALID);
2666        self.classes.insert(entity, HashSet::new());
2667        self.abilities.insert(entity, Abilities::default());
2668        self.system_flags = SystemFlags::RELAYOUT;
2669        self.restyle.insert(entity);
2670        self.reaccess.insert(entity);
2671        self.retransform.insert(entity);
2672        self.reclip.insert(entity);
2673    }
2674
2675    // Remove style data for the given entity.
2676    pub(crate) fn remove(&mut self, entity: Entity) {
2677        self.ids.remove(entity);
2678        self.classes.remove(entity);
2679        self.pseudo_classes.remove(entity);
2680        self.disabled.remove(entity);
2681        self.abilities.remove(entity);
2682
2683        self.name.remove(entity);
2684        self.role.remove(entity);
2685        // self.default_action_verb.remove(entity);
2686        self.live.remove(entity);
2687        self.labelled_by.remove(entity);
2688        self.described_by.remove(entity);
2689        self.controls.remove(entity);
2690        self.active_descendant.remove(entity);
2691        self.expanded.remove(entity);
2692        self.selected.remove(entity);
2693        self.hidden.remove(entity);
2694        self.orientation.remove(entity);
2695        self.text_value.remove(entity);
2696        self.numeric_value.remove(entity);
2697
2698        // Display
2699        self.display.remove(entity);
2700        // Visibility
2701        self.visibility.remove(entity);
2702        // Opacity
2703        self.opacity.remove(entity);
2704        // Z Order
2705        self.z_index.remove(entity);
2706        self.ignore_clipping.remove(entity);
2707        // Clipping
2708        self.clip_path.remove(entity);
2709
2710        self.overflowx.remove(entity);
2711        self.overflowy.remove(entity);
2712
2713        // Filters
2714        self.filter.remove(entity);
2715        self.backdrop_filter.remove(entity);
2716
2717        // Blend Mode
2718        self.blend_mode.remove(entity);
2719
2720        // Transform
2721        self.transform.remove(entity);
2722        self.transform_origin.remove(entity);
2723        self.translate.remove(entity);
2724        self.rotate.remove(entity);
2725        self.scale.remove(entity);
2726
2727        // Border
2728        self.border_width.remove(entity);
2729        self.border_color.remove(entity);
2730        self.border_style.remove(entity);
2731
2732        // Corner Shape
2733        self.corner_bottom_left_shape.remove(entity);
2734        self.corner_bottom_right_shape.remove(entity);
2735        self.corner_top_left_shape.remove(entity);
2736        self.corner_top_right_shape.remove(entity);
2737
2738        // Corner Radius
2739        self.corner_bottom_left_radius.remove(entity);
2740        self.corner_bottom_right_radius.remove(entity);
2741        self.corner_top_left_radius.remove(entity);
2742        self.corner_top_right_radius.remove(entity);
2743
2744        // Corner Smoothing
2745        self.corner_bottom_left_smoothing.remove(entity);
2746        self.corner_bottom_right_smoothing.remove(entity);
2747        self.corner_top_left_smoothing.remove(entity);
2748        self.corner_top_right_smoothing.remove(entity);
2749
2750        // Outline
2751        self.outline_width.remove(entity);
2752        self.outline_color.remove(entity);
2753        self.outline_offset.remove(entity);
2754
2755        // Background
2756        self.background_color.remove(entity);
2757        self.background_image.remove(entity);
2758        self.background_size.remove(entity);
2759
2760        // Box Shadow
2761        self.shadow.remove(entity);
2762
2763        // Text and Font
2764        self.text.remove(entity);
2765        self.text_wrap.remove(entity);
2766        self.text_overflow.remove(entity);
2767        self.letter_spacing.remove(entity);
2768        self.line_height.remove(entity);
2769        self.line_clamp.remove(entity);
2770        self.text_align.remove(entity);
2771        self.font_family.remove(entity);
2772        self.font_color.remove(entity);
2773        self.font_size.remove(entity);
2774        self.font_weight.remove(entity);
2775        self.font_slant.remove(entity);
2776        self.font_width.remove(entity);
2777        self.font_variation_settings.remove(entity);
2778        self.caret_color.remove(entity);
2779        self.selection_color.remove(entity);
2780        self.text_decoration_line.remove(entity);
2781        self.text_decoration_style.remove(entity);
2782        self.text_decoration_color.remove(entity);
2783        self.text_stroke_width.remove(entity);
2784        self.text_stroke_style.remove(entity);
2785
2786        // Cursor
2787        self.cursor.remove(entity);
2788
2789        self.pointer_events.remove(entity);
2790
2791        // Layout Type
2792        self.layout_type.remove(entity);
2793
2794        // Position Type
2795        self.position_type.remove(entity);
2796
2797        self.alignment.remove(entity);
2798        self.direction.remove(entity);
2799        self.wrap.remove(entity);
2800
2801        // Grid
2802        self.grid_columns.remove(entity);
2803        self.grid_rows.remove(entity);
2804        self.column_start.remove(entity);
2805        self.column_span.remove(entity);
2806        self.row_start.remove(entity);
2807        self.row_span.remove(entity);
2808
2809        // Space
2810        self.left.remove(entity);
2811        self.right.remove(entity);
2812        self.top.remove(entity);
2813        self.bottom.remove(entity);
2814
2815        // Padding
2816        self.padding_left.remove(entity);
2817        self.padding_right.remove(entity);
2818        self.padding_top.remove(entity);
2819        self.padding_bottom.remove(entity);
2820        self.vertical_gap.remove(entity);
2821        self.horizontal_gap.remove(entity);
2822
2823        // Scrolling
2824        self.vertical_scroll.remove(entity);
2825        self.horizontal_scroll.remove(entity);
2826
2827        // Size
2828        self.width.remove(entity);
2829        self.height.remove(entity);
2830
2831        // Size Constraints
2832        self.min_width.remove(entity);
2833        self.max_width.remove(entity);
2834        self.min_height.remove(entity);
2835        self.max_height.remove(entity);
2836
2837        self.min_horizontal_gap.remove(entity);
2838        self.max_horizontal_gap.remove(entity);
2839        self.min_vertical_gap.remove(entity);
2840        self.max_vertical_gap.remove(entity);
2841
2842        self.text_range.remove(entity);
2843        self.text_span.remove(entity);
2844
2845        self.fill.remove(entity);
2846
2847        // Remove per-entity data from custom property stores
2848        for store in self.custom_color_props.values_mut() {
2849            store.remove(entity);
2850        }
2851        for store in self.custom_length_props.values_mut() {
2852            store.remove(entity);
2853        }
2854        for store in self.custom_font_size_props.values_mut() {
2855            store.remove(entity);
2856        }
2857        for store in self.custom_letter_spacing_props.values_mut() {
2858            store.remove(entity);
2859        }
2860        for store in self.custom_line_height_props.values_mut() {
2861            store.remove(entity);
2862        }
2863        for store in self.custom_units_props.values_mut() {
2864            store.remove(entity);
2865        }
2866        for store in self.custom_opacity_props.values_mut() {
2867            store.remove(entity);
2868        }
2869    }
2870
2871    pub(crate) fn needs_restyle(&mut self, entity: Entity) {
2872        if entity == Entity::null() || self.restyle.contains(&entity) {
2873            return;
2874        }
2875        self.restyle.insert(entity);
2876    }
2877
2878    pub(crate) fn needs_relayout(&mut self) {
2879        self.system_flags.set(SystemFlags::RELAYOUT, true);
2880    }
2881
2882    pub(crate) fn needs_access_update(&mut self, entity: Entity) {
2883        self.reaccess.insert(entity);
2884    }
2885
2886    pub(crate) fn needs_text_update(&mut self, entity: Entity) {
2887        self.text_construction.insert(entity);
2888        self.text_layout.insert(entity);
2889    }
2890
2891    pub(crate) fn needs_text_layout(&mut self, entity: Entity) {
2892        self.text_layout.insert(entity);
2893    }
2894
2895    pub(crate) fn needs_retransform(&mut self, entity: Entity) {
2896        self.retransform.insert(entity);
2897    }
2898
2899    pub(crate) fn needs_reclip(&mut self, entity: Entity) {
2900        self.reclip.insert(entity);
2901    }
2902
2903    // pub fn should_redraw<F: FnOnce()>(&mut self, f: F) {
2904    //     if !self.redraw_list.is_empty() {
2905    //         f();
2906    //     }
2907    // }
2908
2909    // Remove all shared style data.
2910    pub(crate) fn clear_style_rules(&mut self) {
2911        self.disabled.clear_rules();
2912        // Display
2913        self.display.clear_rules();
2914        // Visibility
2915        self.visibility.clear_rules();
2916        // Opacity
2917        self.opacity.clear_rules();
2918        // Z Order
2919        self.z_index.clear_rules();
2920
2921        // Clipping
2922        self.clip_path.clear_rules();
2923
2924        // Filters
2925        self.filter.clear_rules();
2926        self.backdrop_filter.clear_rules();
2927
2928        // Blend Mode
2929        self.blend_mode.clear_rules();
2930
2931        // Transform
2932        self.transform.clear_rules();
2933        self.transform_origin.clear_rules();
2934        self.translate.clear_rules();
2935        self.rotate.clear_rules();
2936        self.scale.clear_rules();
2937
2938        self.overflowx.clear_rules();
2939        self.overflowy.clear_rules();
2940
2941        // Border
2942        self.border_width.clear_rules();
2943        self.border_color.clear_rules();
2944        self.border_style.clear_rules();
2945
2946        // Corner Shape
2947        self.corner_bottom_left_shape.clear_rules();
2948        self.corner_bottom_right_shape.clear_rules();
2949        self.corner_top_left_shape.clear_rules();
2950        self.corner_top_right_shape.clear_rules();
2951
2952        // Corner Radius
2953        self.corner_bottom_left_radius.clear_rules();
2954        self.corner_bottom_right_radius.clear_rules();
2955        self.corner_top_left_radius.clear_rules();
2956        self.corner_top_right_radius.clear_rules();
2957
2958        // Corner Smoothing
2959        self.corner_bottom_left_smoothing.clear_rules();
2960        self.corner_bottom_right_smoothing.clear_rules();
2961        self.corner_top_left_smoothing.clear_rules();
2962        self.corner_top_right_smoothing.clear_rules();
2963
2964        // Outline
2965        self.outline_width.clear_rules();
2966        self.outline_color.clear_rules();
2967        self.outline_offset.clear_rules();
2968
2969        // Background
2970        self.background_color.clear_rules();
2971        self.background_image.clear_rules();
2972        self.background_size.clear_rules();
2973
2974        self.shadow.clear_rules();
2975
2976        self.layout_type.clear_rules();
2977        self.position_type.clear_rules();
2978        self.alignment.clear_rules();
2979        self.direction.clear_rules();
2980        self.wrap.clear_rules();
2981
2982        // Grid
2983        self.grid_columns.clear_rules();
2984        self.grid_rows.clear_rules();
2985        self.column_start.clear_rules();
2986        self.column_span.clear_rules();
2987
2988        // Space
2989        self.left.clear_rules();
2990        self.right.clear_rules();
2991        self.top.clear_rules();
2992        self.bottom.clear_rules();
2993
2994        // Size
2995        self.width.clear_rules();
2996        self.height.clear_rules();
2997
2998        // Size Constraints
2999        self.min_width.clear_rules();
3000        self.max_width.clear_rules();
3001        self.min_height.clear_rules();
3002        self.max_height.clear_rules();
3003
3004        self.min_horizontal_gap.clear_rules();
3005        self.max_horizontal_gap.clear_rules();
3006        self.min_vertical_gap.clear_rules();
3007        self.max_vertical_gap.clear_rules();
3008
3009        // Padding
3010        self.padding_left.clear_rules();
3011        self.padding_right.clear_rules();
3012        self.padding_top.clear_rules();
3013        self.padding_bottom.clear_rules();
3014        self.horizontal_gap.clear_rules();
3015        self.vertical_gap.clear_rules();
3016
3017        // Scrolling
3018        self.horizontal_scroll.clear_rules();
3019        self.vertical_scroll.clear_rules();
3020
3021        // Text and Font
3022        self.text_wrap.clear_rules();
3023        self.text_overflow.clear_rules();
3024        self.letter_spacing.clear_rules();
3025        self.line_height.clear_rules();
3026        self.line_clamp.clear_rules();
3027        self.text_align.clear_rules();
3028        self.font_family.clear_rules();
3029        self.font_weight.clear_rules();
3030        self.font_slant.clear_rules();
3031        self.font_color.clear_rules();
3032        self.font_size.clear_rules();
3033        self.font_variation_settings.clear_rules();
3034        self.selection_color.clear_rules();
3035        self.caret_color.clear_rules();
3036        self.text_decoration_line.clear_rules();
3037        self.text_decoration_style.clear_rules();
3038        self.text_decoration_color.clear_rules();
3039        self.text_stroke_width.clear_rules();
3040        self.text_stroke_style.clear_rules();
3041
3042        self.cursor.clear_rules();
3043
3044        self.pointer_events.clear_rules();
3045
3046        self.name.clear_rules();
3047
3048        self.fill.clear_rules();
3049
3050        // Clear all custom property rule data on stylesheet reload
3051        for store in self.custom_color_props.values_mut() {
3052            store.clear_rules();
3053        }
3054        self.custom_color_props
3055            .retain(|_, store| !store.shared_data.is_empty() || !store.inline_data.is_empty());
3056        for store in self.custom_length_props.values_mut() {
3057            store.clear_rules();
3058        }
3059        self.custom_length_props
3060            .retain(|_, store| !store.shared_data.is_empty() || !store.inline_data.is_empty());
3061        for store in self.custom_font_size_props.values_mut() {
3062            store.clear_rules();
3063        }
3064        self.custom_font_size_props
3065            .retain(|_, store| !store.shared_data.is_empty() || !store.inline_data.is_empty());
3066        for store in self.custom_letter_spacing_props.values_mut() {
3067            store.clear_rules();
3068        }
3069        self.custom_letter_spacing_props
3070            .retain(|_, store| !store.shared_data.is_empty() || !store.inline_data.is_empty());
3071        for store in self.custom_line_height_props.values_mut() {
3072            store.clear_rules();
3073        }
3074        self.custom_line_height_props
3075            .retain(|_, store| !store.shared_data.is_empty() || !store.inline_data.is_empty());
3076        for store in self.custom_units_props.values_mut() {
3077            store.clear_rules();
3078        }
3079        self.custom_units_props
3080            .retain(|_, store| !store.shared_data.is_empty() || !store.inline_data.is_empty());
3081        for store in self.custom_opacity_props.values_mut() {
3082            store.clear_rules();
3083        }
3084        self.custom_opacity_props
3085            .retain(|_, store| !store.shared_data.is_empty() || !store.inline_data.is_empty());
3086    }
3087}