Skip to main content

blinc_layout/
div.rs

1//! GPUI-style div builder with tailwind-style methods
2//!
3//! Provides a fluent builder API for creating layout elements:
4//! ```rust
5//! use blinc_layout::prelude::*;
6//! use blinc_core::Color;
7//!
8//! let ui = div()
9//!     .flex_row()
10//!     .gap(4.0)
11//!     .p(2.0)
12//!     .bg(Color::RED)
13//!     .child(text("Hello"));
14//! ```
15
16use std::sync::{
17    atomic::{AtomicBool, Ordering},
18    Arc, Mutex,
19};
20
21use blinc_core::{
22    BlurQuality, BlurStyle, Brush, ClipPath, Color, CornerRadius, CornerShape, LayerEffect,
23    OverflowFade, Shadow, Transform,
24};
25use blinc_theme::ThemeState;
26use taffy::prelude::*;
27use taffy::Overflow;
28
29use crate::element::{
30    ElementBounds, GlassMaterial, Material, MetallicMaterial, RenderLayer, RenderProps,
31    WoodMaterial,
32};
33use crate::element_style::ElementStyle;
34use crate::tree::{LayoutNodeId, LayoutTree};
35
36// ============================================================================
37// ElementRef - Generic reference binding for external access
38// ============================================================================
39
40/// Shared storage for element references
41type RefStorage<T> = Arc<Mutex<Option<T>>>;
42
43/// Shared dirty flag for automatic rebuild triggering
44type DirtyFlag = Arc<AtomicBool>;
45
46/// A generic reference binding to an element that can be accessed externally
47///
48/// Similar to React's `useRef`, this allows capturing a reference to an element
49/// for external manipulation while maintaining the fluent API flow.
50///
51/// # Example
52///
53/// ```ignore
54/// use blinc_layout::prelude::*;
55///
56/// // Create a reference
57/// let button_ref = ElementRef::<StatefulButton>::new();
58///
59/// // Build UI - .bind() works seamlessly in the fluent chain
60/// let ui = div()
61///     .flex_col()
62///     .child(
63///         stateful_button()
64///             .bind(&button_ref)  // Binds AND continues the chain
65///             .on_state(|state, div| { ... })
66///     );
67///
68/// // Later, access the bound element's full API
69/// button_ref.with_mut(|btn| {
70///     btn.dispatch_state(ButtonState::Pressed);
71/// });
72/// ```
73/// Storage for computed layout bounds
74type LayoutBoundsStorage = Arc<Mutex<Option<ElementBounds>>>;
75
76pub struct ElementRef<T> {
77    inner: RefStorage<T>,
78    /// Shared dirty flag - when set, signals that the UI needs to be rebuilt
79    dirty_flag: DirtyFlag,
80    /// Computed layout bounds (set after layout is computed)
81    layout_bounds: LayoutBoundsStorage,
82}
83
84impl<T> Clone for ElementRef<T> {
85    fn clone(&self) -> Self {
86        Self {
87            inner: Arc::clone(&self.inner),
88            dirty_flag: Arc::clone(&self.dirty_flag),
89            layout_bounds: Arc::clone(&self.layout_bounds),
90        }
91    }
92}
93
94impl<T> Default for ElementRef<T> {
95    fn default() -> Self {
96        Self::new()
97    }
98}
99
100impl<T> ElementRef<T> {
101    /// Create a new empty ElementRef
102    pub fn new() -> Self {
103        Self {
104            inner: Arc::new(Mutex::new(None)),
105            dirty_flag: Arc::new(AtomicBool::new(false)),
106            layout_bounds: Arc::new(Mutex::new(None)),
107        }
108    }
109
110    /// Create an ElementRef with a shared dirty flag
111    ///
112    /// This is used internally to share the same dirty flag across
113    /// multiple refs, allowing the windowed app to check for changes.
114    pub fn with_dirty_flag(dirty_flag: DirtyFlag) -> Self {
115        Self {
116            inner: Arc::new(Mutex::new(None)),
117            dirty_flag,
118            layout_bounds: Arc::new(Mutex::new(None)),
119        }
120    }
121
122    /// Get the dirty flag handle (for sharing with other refs)
123    pub fn dirty_flag(&self) -> DirtyFlag {
124        Arc::clone(&self.dirty_flag)
125    }
126
127    /// Check if the element was modified and clear the flag
128    ///
129    /// Returns `true` if the element was modified since the last check.
130    pub fn take_dirty(&self) -> bool {
131        self.dirty_flag.swap(false, Ordering::SeqCst)
132    }
133
134    /// Check if the element was modified (without clearing)
135    pub fn is_dirty(&self) -> bool {
136        self.dirty_flag.load(Ordering::SeqCst)
137    }
138
139    /// Mark the element as dirty (needs rebuild)
140    pub fn mark_dirty(&self) {
141        self.dirty_flag.store(true, Ordering::SeqCst);
142    }
143
144    /// Check if an element is bound to this reference
145    pub fn is_bound(&self) -> bool {
146        self.inner.lock().unwrap().is_some()
147    }
148
149    /// Get the internal storage handle for shared access
150    ///
151    /// This is used by `.bind()` implementations to share storage
152    /// between the bound element wrapper and this ref.
153    pub fn storage(&self) -> RefStorage<T> {
154        Arc::clone(&self.inner)
155    }
156
157    /// Set the element in storage (used by bind implementations)
158    pub fn set(&self, elem: T) {
159        *self.inner.lock().unwrap() = Some(elem);
160    }
161
162    /// Access the bound element immutably with a callback
163    ///
164    /// Returns `Some(result)` if an element is bound, `None` otherwise.
165    ///
166    /// # Example
167    ///
168    /// ```ignore
169    /// let state = button_ref.with(|btn| *btn.state());
170    /// ```
171    pub fn with<F, R>(&self, f: F) -> Option<R>
172    where
173        F: FnOnce(&T) -> R,
174    {
175        self.inner.lock().unwrap().as_ref().map(f)
176    }
177
178    /// Access the bound element mutably with a callback
179    ///
180    /// Returns `Some(result)` if an element is bound, `None` otherwise.
181    /// This is the primary way to call methods on the bound element.
182    ///
183    /// # Example
184    ///
185    /// ```ignore
186    /// // Dispatch state changes
187    /// button_ref.with_mut(|btn| {
188    ///     btn.dispatch_state(ButtonState::Pressed);
189    /// });
190    ///
191    /// // Modify element styling
192    /// div_ref.with_mut(|div| {
193    ///     *div = div.swap().bg(Color::RED).rounded(8.0);
194    /// });
195    /// ```
196    ///
197    /// **Note:** This automatically marks the element as dirty after the callback,
198    /// triggering a UI rebuild.
199    pub fn with_mut<F, R>(&self, f: F) -> Option<R>
200    where
201        F: FnOnce(&mut T) -> R,
202    {
203        let result = self.inner.lock().unwrap().as_mut().map(f);
204        if result.is_some() {
205            // Mark dirty after successful mutation
206            self.dirty_flag.store(true, Ordering::SeqCst);
207        }
208        result
209    }
210
211    /// Get a clone of the bound element, if any
212    pub fn get(&self) -> Option<T>
213    where
214        T: Clone,
215    {
216        self.inner.lock().unwrap().clone()
217    }
218
219    /// Replace the bound element with a new one, returning the old value
220    ///
221    /// **Note:** This automatically marks the element as dirty, triggering a UI rebuild.
222    pub fn replace(&self, new_elem: T) -> Option<T> {
223        let old = self.inner.lock().unwrap().replace(new_elem);
224        self.dirty_flag.store(true, Ordering::SeqCst);
225        old
226    }
227
228    /// Take the bound element out of the reference, leaving None
229    pub fn take(&self) -> Option<T> {
230        self.inner.lock().unwrap().take()
231    }
232
233    /// Borrow the bound element immutably
234    ///
235    /// Returns a guard that dereferences to &T. Panics if not bound.
236    /// For fallible access, use `with()` instead.
237    ///
238    /// # Example
239    ///
240    /// ```ignore
241    /// let state = button_ref.borrow().state();
242    /// ```
243    ///
244    /// # Panics
245    ///
246    /// Panics if no element is bound to this reference.
247    pub fn borrow(&self) -> ElementRefGuard<'_, T> {
248        ElementRefGuard {
249            guard: self.inner.lock().unwrap(),
250        }
251    }
252
253    /// Borrow the bound element mutably
254    ///
255    /// Returns a guard that dereferences to &mut T. Panics if not bound.
256    /// **When the guard is dropped, the element is automatically marked dirty**,
257    /// triggering a UI rebuild.
258    ///
259    /// For fallible access, use `with_mut()` instead.
260    ///
261    /// # Example
262    ///
263    /// ```ignore
264    /// // This automatically triggers a rebuild when the guard is dropped
265    /// button_ref.borrow_mut().dispatch_state(ButtonState::Hovered);
266    /// ```
267    ///
268    /// # Panics
269    ///
270    /// Panics if no element is bound to this reference.
271    pub fn borrow_mut(&self) -> ElementRefGuardMut<'_, T> {
272        ElementRefGuardMut {
273            guard: self.inner.lock().unwrap(),
274            dirty_flag: Arc::clone(&self.dirty_flag),
275        }
276    }
277
278    // =========================================================================
279    // Layout Bounds
280    // =========================================================================
281
282    /// Get the computed layout bounds for this element
283    ///
284    /// Returns `None` if layout hasn't been computed yet or if the element
285    /// is not part of the layout tree.
286    ///
287    /// # Example
288    ///
289    /// ```ignore
290    /// if let Some(bounds) = input_ref.get_layout_bounds() {
291    ///     println!("Width: {}, Height: {}", bounds.width, bounds.height);
292    /// }
293    /// ```
294    pub fn get_layout_bounds(&self) -> Option<ElementBounds> {
295        *self.layout_bounds.lock().unwrap()
296    }
297
298    /// Set the computed layout bounds for this element
299    ///
300    /// This is called internally after layout is computed to store
301    /// the element's position and dimensions.
302    pub fn set_layout_bounds(&self, bounds: ElementBounds) {
303        *self.layout_bounds.lock().unwrap() = Some(bounds);
304    }
305
306    /// Clear the stored layout bounds
307    ///
308    /// Called when the element is removed from the layout tree or
309    /// when layout needs to be recomputed.
310    pub fn clear_layout_bounds(&self) {
311        *self.layout_bounds.lock().unwrap() = None;
312    }
313
314    /// Get the layout bounds storage handle for sharing
315    ///
316    /// This allows other parts of the system to update the layout bounds
317    /// when layout is computed.
318    pub fn layout_bounds_storage(&self) -> LayoutBoundsStorage {
319        Arc::clone(&self.layout_bounds)
320    }
321}
322
323/// Guard for immutable access to a bound element
324pub struct ElementRefGuard<'a, T> {
325    guard: std::sync::MutexGuard<'a, Option<T>>,
326}
327
328impl<T> std::ops::Deref for ElementRefGuard<'_, T> {
329    type Target = T;
330
331    fn deref(&self) -> &Self::Target {
332        self.guard.as_ref().expect("ElementRef not bound")
333    }
334}
335
336/// Guard for mutable access to a bound element
337///
338/// When this guard is dropped, the dirty flag is automatically set,
339/// signaling that the UI needs to be rebuilt.
340pub struct ElementRefGuardMut<'a, T> {
341    guard: std::sync::MutexGuard<'a, Option<T>>,
342    dirty_flag: DirtyFlag,
343}
344
345impl<T> std::ops::Deref for ElementRefGuardMut<'_, T> {
346    type Target = T;
347
348    fn deref(&self) -> &Self::Target {
349        self.guard.as_ref().expect("ElementRef not bound")
350    }
351}
352
353impl<T> std::ops::DerefMut for ElementRefGuardMut<'_, T> {
354    fn deref_mut(&mut self) -> &mut Self::Target {
355        self.guard.as_mut().expect("ElementRef not bound")
356    }
357}
358
359impl<T> Drop for ElementRefGuardMut<'_, T> {
360    fn drop(&mut self) {
361        // Mark dirty when the mutable borrow ends - user modified the element
362        self.dirty_flag.store(true, Ordering::SeqCst);
363    }
364}
365
366/// Type alias for Div references
367pub type DivRef = ElementRef<Div>;
368
369/// A div element builder with GPUI/Tailwind-style methods
370pub struct Div {
371    pub(crate) style: Style,
372    pub(crate) children: Vec<Box<dyn ElementBuilder>>,
373    pub(crate) background: Option<Brush>,
374    pub(crate) border_radius: CornerRadius,
375    pub(crate) corner_shape: CornerShape,
376    pub(crate) border_color: Option<Color>,
377    pub(crate) border_width: f32,
378    pub(crate) border_sides: crate::element::BorderSides,
379    pub(crate) render_layer: RenderLayer,
380    pub(crate) material: Option<Material>,
381    pub(crate) shadow: Option<Shadow>,
382    pub(crate) transform: Option<Transform>,
383    pub(crate) opacity: f32,
384    pub(crate) cursor: Option<crate::element::CursorStyle>,
385    pub(crate) pointer_events_none: bool,
386    /// Layer effects (blur, drop shadow, glow, color matrix) applied to this element
387    pub(crate) layer_effects: Vec<LayerEffect>,
388    /// Marks this as a stack layer for z-ordering (increments z_layer for interleaved rendering)
389    pub(crate) is_stack_layer: bool,
390    pub(crate) event_handlers: crate::event_handler::EventHandlers,
391    /// Element ID for selector API queries
392    pub(crate) element_id: Option<String>,
393    /// CSS class names for selector matching
394    pub(crate) classes: Vec<String>,
395    // 3D transform properties (stored separately to flow through to render_props)
396    pub(crate) rotate_x: Option<f32>,
397    pub(crate) rotate_y: Option<f32>,
398    pub(crate) perspective_3d: Option<f32>,
399    pub(crate) shape_3d: Option<f32>,
400    pub(crate) depth: Option<f32>,
401    pub(crate) light_direction: Option<[f32; 3]>,
402    pub(crate) light_intensity: Option<f32>,
403    pub(crate) ambient: Option<f32>,
404    pub(crate) specular: Option<f32>,
405    pub(crate) translate_z: Option<f32>,
406    pub(crate) op_3d: Option<f32>,
407    pub(crate) blend_3d: Option<f32>,
408    /// Overflow fade distances (smooth alpha fade at clip edges)
409    pub(crate) overflow_fade: OverflowFade,
410    /// CSS clip-path shape function
411    pub(crate) clip_path: Option<ClipPath>,
412    /// @flow shader name (references a FlowGraph in the stylesheet)
413    pub(crate) flow_name: Option<String>,
414    /// Direct @flow graph (from `flow!` macro), bypasses stylesheet lookup
415    pub(crate) flow_graph: Option<std::sync::Arc<blinc_core::FlowGraph>>,
416    /// Fixed positioning (stays in place when ancestors scroll)
417    pub(crate) is_fixed: bool,
418    /// Sticky positioning (sticks when scrolled past threshold)
419    pub(crate) is_sticky: bool,
420    /// Sticky top threshold
421    pub(crate) sticky_top: Option<f32>,
422    /// Outline width in pixels
423    pub(crate) outline_width: f32,
424    /// Outline color
425    pub(crate) outline_color: Option<Color>,
426    /// Outline offset in pixels (gap between border and outline)
427    pub(crate) outline_offset: f32,
428    /// CSS z-index for stacking order
429    pub(crate) z_index: i32,
430    /// Scroll physics for overflow:scroll containers
431    pub(crate) scroll_physics: Option<crate::scroll::SharedScrollPhysics>,
432    /// Layout animation configuration for FLIP-style bounds animation
433    pub(crate) layout_animation: Option<crate::layout_animation::LayoutAnimationConfig>,
434    /// Visual animation configuration (new FLIP-style system, read-only layout)
435    pub(crate) visual_animation: Option<crate::visual_animation::VisualAnimationConfig>,
436    /// Ancestor stateful context key for automatic key derivation
437    ///
438    /// When set, motion containers and layout animations will use this key
439    /// as a prefix for auto-generated stable keys.
440    pub(crate) stateful_context_key: Option<String>,
441}
442
443impl Default for Div {
444    fn default() -> Self {
445        Self::new()
446    }
447}
448
449impl Div {
450    /// Create a new div element
451    pub fn new() -> Self {
452        Self {
453            style: Style::default(),
454            children: Vec::new(),
455            background: None,
456            border_radius: CornerRadius::default(),
457            corner_shape: CornerShape::default(),
458            border_color: None,
459            border_width: 0.0,
460            border_sides: crate::element::BorderSides::default(),
461            render_layer: RenderLayer::default(),
462            material: None,
463            shadow: None,
464            transform: None,
465            opacity: 1.0,
466            cursor: None,
467            pointer_events_none: false,
468            layer_effects: Vec::new(),
469            is_stack_layer: false,
470            event_handlers: crate::event_handler::EventHandlers::new(),
471            element_id: None,
472            classes: Vec::new(),
473            rotate_x: None,
474            rotate_y: None,
475            perspective_3d: None,
476            shape_3d: None,
477            depth: None,
478            light_direction: None,
479            light_intensity: None,
480            ambient: None,
481            specular: None,
482            translate_z: None,
483            op_3d: None,
484            blend_3d: None,
485            overflow_fade: OverflowFade::default(),
486            clip_path: None,
487            flow_name: None,
488            flow_graph: None,
489            outline_width: 0.0,
490            outline_color: None,
491            outline_offset: 0.0,
492            is_fixed: false,
493            is_sticky: false,
494            sticky_top: None,
495            z_index: 0,
496            scroll_physics: None,
497            layout_animation: None,
498            visual_animation: None,
499            stateful_context_key: None,
500        }
501    }
502
503    /// Create a new div element with a pre-configured taffy Style
504    ///
505    /// This is useful for preserving layout properties (width, height, overflow, etc.)
506    /// when rebuilding subtrees in Stateful elements.
507    pub fn with_style(style: Style) -> Self {
508        Self {
509            style,
510            children: Vec::new(),
511            background: None,
512            border_radius: CornerRadius::default(),
513            corner_shape: CornerShape::default(),
514            border_color: None,
515            border_width: 0.0,
516            border_sides: crate::element::BorderSides::default(),
517            render_layer: RenderLayer::default(),
518            material: None,
519            shadow: None,
520            transform: None,
521            opacity: 1.0,
522            cursor: None,
523            pointer_events_none: false,
524            layer_effects: Vec::new(),
525            is_stack_layer: false,
526            event_handlers: crate::event_handler::EventHandlers::new(),
527            element_id: None,
528            classes: Vec::new(),
529            rotate_x: None,
530            rotate_y: None,
531            perspective_3d: None,
532            shape_3d: None,
533            depth: None,
534            light_direction: None,
535            light_intensity: None,
536            ambient: None,
537            specular: None,
538            translate_z: None,
539            op_3d: None,
540            blend_3d: None,
541            overflow_fade: OverflowFade::default(),
542            clip_path: None,
543            flow_name: None,
544            flow_graph: None,
545            outline_width: 0.0,
546            outline_color: None,
547            outline_offset: 0.0,
548            is_fixed: false,
549            is_sticky: false,
550            sticky_top: None,
551            z_index: 0,
552            scroll_physics: None,
553            layout_animation: None,
554            visual_animation: None,
555            stateful_context_key: None,
556        }
557    }
558
559    /// Set an element ID for selector API queries
560    ///
561    /// Elements with IDs can be looked up programmatically:
562    /// ```rust,ignore
563    /// div().id("my-container").child(...)
564    ///
565    /// // Later:
566    /// if let Some(handle) = ctx.query("my-container") {
567    ///     handle.scroll_into_view();
568    /// }
569    /// ```
570    pub fn id(mut self, id: impl Into<String>) -> Self {
571        self.element_id = Some(id.into());
572        self
573    }
574
575    /// Get the element ID if set
576    pub fn element_id(&self) -> Option<&str> {
577        self.element_id.as_deref()
578    }
579
580    /// Add a CSS class name for selector matching
581    ///
582    /// Classes can be used with `.class` selectors in stylesheets.
583    /// Multiple classes can be added by chaining `.class()` calls.
584    pub fn class(mut self, name: impl Into<String>) -> Self {
585        self.classes.push(name.into());
586        self
587    }
588
589    /// Get the element's class list
590    pub fn classes(&self) -> &[String] {
591        &self.classes
592    }
593
594    /// Set the stateful context key for automatic key derivation
595    ///
596    /// This is typically set automatically by `stateful()` callbacks.
597    /// When set, motion containers and layout animations will use this key
598    /// as a prefix for auto-generated stable keys.
599    pub(crate) fn with_stateful_context(mut self, key: impl Into<String>) -> Self {
600        self.stateful_context_key = Some(key.into());
601        self
602    }
603
604    /// Get the stateful context key if set
605    pub fn stateful_context_key(&self) -> Option<&str> {
606        self.stateful_context_key.as_deref()
607    }
608
609    /// Enable layout animation for this element
610    ///
611    /// # Deprecated
612    ///
613    /// Use [`animate_bounds()`](Self::animate_bounds) instead. The new system:
614    /// - Never modifies taffy (read-only layout)
615    /// - Tracks visual offsets that animate back to zero (FLIP technique)
616    /// - Properly propagates parent animation offsets to children
617    ///
618    /// When enabled, changes to the element's bounds (position/size) after layout
619    /// computation will be smoothly animated using spring physics instead of
620    /// snapping instantly.
621    ///
622    /// This is useful for accordion/collapsible content, list reordering,
623    /// and any UI where elements change size or position dynamically.
624    ///
625    /// # Example
626    ///
627    /// ```rust,ignore
628    /// // Animate height changes with default spring
629    /// div()
630    ///     .animate_layout(LayoutAnimationConfig::height())
631    ///     .child(content)
632    ///
633    /// // Animate all bounds with custom spring
634    /// div()
635    ///     .animate_layout(LayoutAnimationConfig::all().with_spring(SpringConfig::gentle()))
636    ///     .child(content)
637    /// ```
638    #[deprecated(
639        since = "0.3.0",
640        note = "Use animate_bounds() instead. The old system modifies taffy which causes parent-child misalignment issues."
641    )]
642    pub fn animate_layout(
643        mut self,
644        config: crate::layout_animation::LayoutAnimationConfig,
645    ) -> Self {
646        // Auto-apply stable key if inside a stateful context
647        let config = if let Some(ref ctx_key) = self.stateful_context_key {
648            if config.stable_key.is_none() {
649                let auto_key = format!("{}:layout_anim", ctx_key);
650                config.with_key(auto_key)
651            } else {
652                config
653            }
654        } else {
655            config
656        };
657        self.layout_animation = Some(config);
658        self
659    }
660
661    /// Enable visual bounds animation using the new FLIP-style system
662    ///
663    /// Unlike `animate_layout()`, this system never modifies the layout tree.
664    /// Instead, it tracks visual offsets and animates them back to zero.
665    ///
666    /// Key differences:
667    /// - Layout runs once with final positions (taffy is read-only)
668    /// - Animation tracks visual offset from layout position
669    /// - Parent offsets propagate to children hierarchically
670    /// - Pre-computed bounds used during rendering
671    ///
672    /// # Example
673    ///
674    /// ```ignore
675    /// // Animate height changes with FLIP-style animation
676    /// div()
677    ///     .animate_bounds(VisualAnimationConfig::height().with_key("my-content"))
678    ///     .overflow_clip()
679    ///     .child(content)
680    /// ```
681    pub fn animate_bounds(
682        mut self,
683        config: crate::visual_animation::VisualAnimationConfig,
684    ) -> Self {
685        // Auto-apply stable key if inside a stateful context
686        let config = if let Some(ref ctx_key) = self.stateful_context_key {
687            if config.key.is_none() {
688                let auto_key = format!("{}:visual_anim", ctx_key);
689                config.with_key(auto_key)
690            } else {
691                config
692            }
693        } else {
694            config
695        };
696        self.visual_animation = Some(config);
697        self
698    }
699
700    /// Wrap this Div in a Motion container with automatic key derivation
701    ///
702    /// If this Div is inside a stateful context (has `stateful_context_key` set),
703    /// the Motion will use an auto-derived stable key. Otherwise, it falls back
704    /// to a location-based key.
705    ///
706    /// # Example
707    ///
708    /// ```ignore
709    /// stateful::<AccordionState>()
710    ///     .on_state(|ctx| {
711    ///         // This motion gets an auto-derived key like "stateful:...:motion"
712    ///         div()
713    ///             .motion()  // Auto-keyed!
714    ///             .fade_in(200)
715    ///             .child(content)
716    ///     })
717    /// ```
718    #[track_caller]
719    pub fn motion(self) -> crate::motion::Motion {
720        if let Some(ref ctx_key) = self.stateful_context_key {
721            // Auto-derive key from stateful context
722            let motion_key = format!("{}:motion", ctx_key);
723            crate::motion::motion_derived(&motion_key).child(self)
724        } else {
725            // Fallback to normal motion with location-based key
726            crate::motion::motion().child(self)
727        }
728    }
729
730    /// Swap this Div with a default, returning the original
731    ///
732    /// This is a convenience method for use in state callbacks where you need
733    /// to consume `self` to chain builder methods, then assign back.
734    ///
735    /// **Note**: This takes ownership of the current Div and leaves a default in its place.
736    /// All properties are preserved in the returned Div. You must assign the result back
737    /// to complete the update.
738    ///
739    /// For updating specific properties without the swap pattern, consider using
740    /// the setter methods directly (e.g., `set_bg()`, `set_transform()`).
741    ///
742    /// # Example
743    ///
744    /// ```ignore
745    /// .on_state(|state, div| match state {
746    ///     ButtonState::Idle => {
747    ///         *div = div.swap().bg(Color::BLUE).rounded(4.0);
748    ///     }
749    ///     ButtonState::Hovered => {
750    ///         *div = div.swap().bg(Color::CYAN).rounded(8.0);
751    ///     }
752    /// })
753    /// ```
754    #[inline]
755    pub fn swap(&mut self) -> Self {
756        std::mem::take(self)
757    }
758
759    /// Conditionally apply modifications based on a boolean condition
760    ///
761    /// This is useful for conditional styling without verbose if-else blocks.
762    ///
763    /// # Example
764    ///
765    /// ```rust,ignore
766    /// // Instead of:
767    /// let mut d = div();
768    /// if is_collapsed {
769    ///     d = d.h(0.0);
770    /// }
771    ///
772    /// // Write:
773    /// div().when(is_collapsed, |d| d.h(0.0))
774    /// ```
775    #[inline]
776    pub fn when<F>(self, condition: bool, f: F) -> Self
777    where
778        F: FnOnce(Self) -> Self,
779    {
780        if condition {
781            f(self)
782        } else {
783            self
784        }
785    }
786
787    /// Set the background color/brush without consuming self
788    ///
789    /// This is useful in state callbacks where you want to update
790    /// properties without using the swap pattern.
791    #[inline]
792    pub fn set_bg(&mut self, color: impl Into<Brush>) {
793        self.background = Some(color.into());
794    }
795
796    /// Set the corner radius without consuming self
797    #[inline]
798    pub fn set_rounded(&mut self, radius: f32) {
799        self.border_radius = CornerRadius::uniform(radius);
800    }
801
802    /// Set the transform without consuming self
803    #[inline]
804    pub fn set_transform(&mut self, transform: Transform) {
805        self.transform = Some(transform);
806    }
807
808    /// Set the shadow without consuming self
809    #[inline]
810    pub fn set_shadow(&mut self, shadow: Shadow) {
811        self.shadow = Some(shadow);
812    }
813
814    /// Set the opacity without consuming self
815    #[inline]
816    pub fn set_opacity(&mut self, opacity: f32) {
817        self.opacity = opacity;
818    }
819
820    /// Set border with width and color without consuming self
821    #[inline]
822    pub fn set_border(&mut self, width: f32, color: Color) {
823        self.border_width = width;
824        self.border_color = Some(color);
825    }
826
827    /// Set overflow clip without consuming self
828    #[inline]
829    pub fn set_overflow_clip(&mut self, clip: bool) {
830        if clip {
831            self.style.overflow.x = taffy::Overflow::Hidden;
832            self.style.overflow.y = taffy::Overflow::Hidden;
833        } else {
834            self.style.overflow.x = taffy::Overflow::Visible;
835            self.style.overflow.y = taffy::Overflow::Visible;
836        }
837    }
838
839    /// Set horizontal padding without consuming self
840    #[inline]
841    pub fn set_padding_x(&mut self, px: f32) {
842        self.style.padding.left = taffy::LengthPercentage::Length(px);
843        self.style.padding.right = taffy::LengthPercentage::Length(px);
844    }
845
846    /// Set vertical padding without consuming self
847    #[inline]
848    pub fn set_padding_y(&mut self, px: f32) {
849        self.style.padding.top = taffy::LengthPercentage::Length(px);
850        self.style.padding.bottom = taffy::LengthPercentage::Length(px);
851    }
852
853    /// Clear all children and add a single child
854    #[inline]
855    pub fn set_child(&mut self, child: impl ElementBuilder + 'static) {
856        self.children.clear();
857        self.children.push(Box::new(child));
858    }
859
860    /// Clear all children
861    #[inline]
862    pub fn clear_children(&mut self) {
863        self.children.clear();
864    }
865
866    /// Set width in pixels without consuming self
867    ///
868    /// This is useful in state callbacks where you want to update
869    /// layout properties without using the swap pattern.
870    #[inline]
871    pub fn set_w(&mut self, px: f32) {
872        self.style.size.width = taffy::Dimension::Length(px);
873    }
874
875    /// Set height in pixels without consuming self
876    #[inline]
877    pub fn set_h(&mut self, px: f32) {
878        self.style.size.height = taffy::Dimension::Length(px);
879    }
880
881    /// Set height to auto without consuming self
882    #[inline]
883    pub fn set_h_auto(&mut self) {
884        self.style.size.height = taffy::Dimension::Auto;
885    }
886
887    // =========================================================================
888    // ElementStyle Application
889    // =========================================================================
890
891    /// Apply an ElementStyle to this div
892    ///
893    /// Only properties that are set (Some) in the style will be applied.
894    /// This allows for style composition and sharing reusable styles.
895    ///
896    /// # Example
897    ///
898    /// ```ignore
899    /// use blinc_layout::prelude::*;
900    ///
901    /// // Define a reusable card style
902    /// let card_style = ElementStyle::new()
903    ///     .bg_surface()
904    ///     .rounded_lg()
905    ///     .shadow_md();
906    ///
907    /// // Apply to multiple elements
908    /// div().style(&card_style).child(text("Card 1"))
909    /// div().style(&card_style).child(text("Card 2"))
910    /// ```
911    pub fn style(mut self, style: &ElementStyle) -> Self {
912        self.set_style(style);
913        self
914    }
915
916    /// Apply an ElementStyle without consuming self
917    ///
918    /// This is useful in state callbacks where you want to update
919    /// styling without using the swap pattern.
920    ///
921    /// # Example
922    ///
923    /// ```ignore
924    /// .on_state(|state, div| {
925    ///     div.set_style(&hover_style);
926    /// })
927    /// ```
928    #[inline]
929    pub fn set_style(&mut self, style: &ElementStyle) {
930        use crate::element_style::{
931            SpacingRect, StyleAlign, StyleDisplay, StyleFlexDirection, StyleJustify, StyleOverflow,
932        };
933
934        // Visual properties
935        if let Some(ref bg) = style.background {
936            self.background = Some(bg.clone());
937        }
938        if let Some(radius) = style.corner_radius {
939            self.border_radius = radius;
940        }
941        if let Some(cs) = style.corner_shape {
942            self.corner_shape = cs;
943        }
944        if let Some(fade) = style.overflow_fade {
945            self.overflow_fade = fade;
946        }
947        if let Some(ref shadow) = style.shadow {
948            self.shadow = Some(*shadow);
949        }
950        if let Some(ref transform) = style.transform {
951            self.transform = Some(transform.clone());
952        }
953        if let Some(ref material) = style.material {
954            self.material = Some(material.clone());
955        }
956        if let Some(layer) = style.render_layer {
957            self.render_layer = layer;
958        }
959        if let Some(opacity) = style.opacity {
960            self.opacity = opacity;
961        }
962
963        // Layout: sizing
964        if let Some(w) = style.width {
965            match w {
966                crate::element_style::StyleDimension::Length(px) => {
967                    self.style.size.width = Dimension::Length(px);
968                }
969                crate::element_style::StyleDimension::Percent(p) => {
970                    self.style.size.width = Dimension::Percent(p);
971                }
972                crate::element_style::StyleDimension::Auto => {
973                    self.style.size.width = Dimension::Auto;
974                    self.style.flex_basis = Dimension::Auto;
975                    self.style.flex_grow = 0.0;
976                    self.style.flex_shrink = 0.0;
977                }
978            }
979        }
980        if let Some(h) = style.height {
981            match h {
982                crate::element_style::StyleDimension::Length(px) => {
983                    self.style.size.height = Dimension::Length(px);
984                }
985                crate::element_style::StyleDimension::Percent(p) => {
986                    self.style.size.height = Dimension::Percent(p);
987                }
988                crate::element_style::StyleDimension::Auto => {
989                    self.style.size.height = Dimension::Auto;
990                    self.style.flex_basis = Dimension::Auto;
991                    self.style.flex_grow = 0.0;
992                    self.style.flex_shrink = 0.0;
993                }
994            }
995        }
996        if let Some(w) = style.min_width {
997            self.style.min_size.width = Dimension::Length(w);
998        }
999        if let Some(h) = style.min_height {
1000            self.style.min_size.height = Dimension::Length(h);
1001        }
1002        if let Some(w) = style.max_width {
1003            self.style.max_size.width = Dimension::Length(w);
1004        }
1005        if let Some(h) = style.max_height {
1006            self.style.max_size.height = Dimension::Length(h);
1007        }
1008
1009        // Layout: display & flex direction
1010        if let Some(display) = style.display {
1011            self.style.display = match display {
1012                StyleDisplay::Flex => Display::Flex,
1013                StyleDisplay::Block => Display::Block,
1014                StyleDisplay::None => Display::None,
1015            };
1016        }
1017        if let Some(dir) = style.flex_direction {
1018            self.style.flex_direction = match dir {
1019                StyleFlexDirection::Row => FlexDirection::Row,
1020                StyleFlexDirection::Column => FlexDirection::Column,
1021                StyleFlexDirection::RowReverse => FlexDirection::RowReverse,
1022                StyleFlexDirection::ColumnReverse => FlexDirection::ColumnReverse,
1023            };
1024        }
1025        if let Some(wrap) = style.flex_wrap {
1026            self.style.flex_wrap = if wrap {
1027                FlexWrap::Wrap
1028            } else {
1029                FlexWrap::NoWrap
1030            };
1031        }
1032        if let Some(grow) = style.flex_grow {
1033            self.style.flex_grow = grow;
1034        }
1035        if let Some(shrink) = style.flex_shrink {
1036            self.style.flex_shrink = shrink;
1037        }
1038
1039        // Layout: alignment
1040        if let Some(align) = style.align_items {
1041            self.style.align_items = Some(match align {
1042                StyleAlign::Start => AlignItems::Start,
1043                StyleAlign::Center => AlignItems::Center,
1044                StyleAlign::End => AlignItems::End,
1045                StyleAlign::Stretch => AlignItems::Stretch,
1046                StyleAlign::Baseline => AlignItems::Baseline,
1047            });
1048        }
1049        if let Some(justify) = style.justify_content {
1050            self.style.justify_content = Some(match justify {
1051                StyleJustify::Start => JustifyContent::Start,
1052                StyleJustify::Center => JustifyContent::Center,
1053                StyleJustify::End => JustifyContent::End,
1054                StyleJustify::SpaceBetween => JustifyContent::SpaceBetween,
1055                StyleJustify::SpaceAround => JustifyContent::SpaceAround,
1056                StyleJustify::SpaceEvenly => JustifyContent::SpaceEvenly,
1057            });
1058        }
1059        if let Some(align) = style.align_self {
1060            self.style.align_self = Some(match align {
1061                StyleAlign::Start => AlignSelf::Start,
1062                StyleAlign::Center => AlignSelf::Center,
1063                StyleAlign::End => AlignSelf::End,
1064                StyleAlign::Stretch => AlignSelf::Stretch,
1065                StyleAlign::Baseline => AlignSelf::Baseline,
1066            });
1067        }
1068
1069        // Layout: spacing
1070        if let Some(SpacingRect {
1071            top,
1072            right,
1073            bottom,
1074            left,
1075        }) = style.padding
1076        {
1077            self.style.padding = Rect {
1078                top: LengthPercentage::Length(top),
1079                right: LengthPercentage::Length(right),
1080                bottom: LengthPercentage::Length(bottom),
1081                left: LengthPercentage::Length(left),
1082            };
1083        }
1084        if let Some(SpacingRect {
1085            top,
1086            right,
1087            bottom,
1088            left,
1089        }) = style.margin
1090        {
1091            self.style.margin = Rect {
1092                top: LengthPercentageAuto::Length(top),
1093                right: LengthPercentageAuto::Length(right),
1094                bottom: LengthPercentageAuto::Length(bottom),
1095                left: LengthPercentageAuto::Length(left),
1096            };
1097        }
1098        if let Some(gap) = style.gap {
1099            self.style.gap = taffy::Size {
1100                width: LengthPercentage::Length(gap),
1101                height: LengthPercentage::Length(gap),
1102            };
1103        }
1104
1105        // Layout: overflow
1106        if let Some(overflow) = style.overflow {
1107            let val = match overflow {
1108                StyleOverflow::Visible => Overflow::Visible,
1109                StyleOverflow::Clip => Overflow::Clip,
1110                StyleOverflow::Scroll => Overflow::Scroll,
1111            };
1112            self.style.overflow.x = val;
1113            self.style.overflow.y = val;
1114            if overflow == StyleOverflow::Scroll {
1115                self.ensure_scroll_physics(crate::scroll::ScrollDirection::Both);
1116            }
1117        }
1118        // Per-axis overflow
1119        if let Some(ox) = style.overflow_x {
1120            let val = match ox {
1121                StyleOverflow::Visible => Overflow::Visible,
1122                StyleOverflow::Clip => Overflow::Clip,
1123                StyleOverflow::Scroll => Overflow::Scroll,
1124            };
1125            self.style.overflow.x = val;
1126            if ox == StyleOverflow::Scroll {
1127                self.ensure_scroll_physics(crate::scroll::ScrollDirection::Horizontal);
1128            }
1129        }
1130        if let Some(oy) = style.overflow_y {
1131            let val = match oy {
1132                StyleOverflow::Visible => Overflow::Visible,
1133                StyleOverflow::Clip => Overflow::Clip,
1134                StyleOverflow::Scroll => Overflow::Scroll,
1135            };
1136            self.style.overflow.y = val;
1137            if oy == StyleOverflow::Scroll {
1138                self.ensure_scroll_physics(crate::scroll::ScrollDirection::Vertical);
1139            }
1140        }
1141
1142        // Layout: border
1143        if let Some(width) = style.border_width {
1144            self.border_width = width;
1145        }
1146        if let Some(color) = style.border_color {
1147            self.border_color = Some(color);
1148        }
1149
1150        // 3D properties
1151        if let Some(v) = style.rotate_x {
1152            self.rotate_x = Some(v);
1153        }
1154        if let Some(v) = style.rotate_y {
1155            self.rotate_y = Some(v);
1156        }
1157        if let Some(v) = style.perspective {
1158            self.perspective_3d = Some(v);
1159        }
1160        if let Some(ref s) = style.shape_3d {
1161            self.shape_3d = Some(crate::css_parser::shape_3d_to_float(s));
1162        }
1163        if let Some(v) = style.depth {
1164            self.depth = Some(v);
1165        }
1166        if let Some(dir) = style.light_direction {
1167            self.light_direction = Some(dir);
1168        }
1169        if let Some(v) = style.light_intensity {
1170            self.light_intensity = Some(v);
1171        }
1172        if let Some(v) = style.ambient {
1173            self.ambient = Some(v);
1174        }
1175        if let Some(v) = style.specular {
1176            self.specular = Some(v);
1177        }
1178        if let Some(v) = style.translate_z {
1179            self.translate_z = Some(v);
1180        }
1181        if let Some(ref op) = style.op_3d {
1182            self.op_3d = Some(crate::css_parser::op_3d_to_float(op));
1183        }
1184        if let Some(v) = style.blend_3d {
1185            self.blend_3d = Some(v);
1186        }
1187        if let Some(ref cp) = style.clip_path {
1188            self.clip_path = Some(cp.clone());
1189        }
1190        if let Some(ref f) = style.flow {
1191            self.flow_name = Some(f.clone());
1192        }
1193    }
1194
1195    /// Merge properties from another Div into this one
1196    ///
1197    /// This applies the other Div's non-default properties on top of this one.
1198    /// Useful in `on_state` callbacks to apply changes without reassignment:
1199    ///
1200    /// ```ignore
1201    /// .on_state(|state, div| {
1202    ///     div.merge(div().bg(color).child(label));
1203    /// })
1204    /// ```
1205    #[inline]
1206    pub fn merge(&mut self, other: Div) {
1207        // Create a default for comparison
1208        let default = Div::new();
1209
1210        // Merge style if it differs from default
1211        // Style is complex, so we merge it field by field via taffy
1212        self.merge_style(&other.style, &default.style);
1213
1214        // Merge render properties - take other's value if non-default
1215        if other.background.is_some() {
1216            self.background = other.background;
1217        }
1218        if other.border_radius != default.border_radius {
1219            self.border_radius = other.border_radius;
1220        }
1221        if other.border_color.is_some() {
1222            self.border_color = other.border_color;
1223        }
1224        if other.border_width != default.border_width {
1225            self.border_width = other.border_width;
1226        }
1227        if other.render_layer != default.render_layer {
1228            self.render_layer = other.render_layer;
1229        }
1230        if other.material.is_some() {
1231            self.material = other.material;
1232        }
1233        if other.shadow.is_some() {
1234            self.shadow = other.shadow;
1235        }
1236        if other.transform.is_some() {
1237            self.transform = other.transform;
1238        }
1239        if other.opacity != default.opacity {
1240            self.opacity = other.opacity;
1241        }
1242        if other.cursor.is_some() {
1243            self.cursor = other.cursor;
1244        }
1245
1246        // Merge children - if other has children, replace ours
1247        if !other.children.is_empty() {
1248            self.children = other.children;
1249        }
1250
1251        // Merge stateful context key - take other's if set
1252        if other.stateful_context_key.is_some() {
1253            self.stateful_context_key = other.stateful_context_key;
1254        }
1255
1256        // Merge visual animation config - take other's if set
1257        if other.visual_animation.is_some() {
1258            self.visual_animation = other.visual_animation;
1259        }
1260
1261        // Merge layout animation config (deprecated) - take other's if set
1262        if other.layout_animation.is_some() {
1263            self.layout_animation = other.layout_animation;
1264        }
1265
1266        // Merge 3D properties
1267        if other.rotate_x.is_some() {
1268            self.rotate_x = other.rotate_x;
1269        }
1270        if other.rotate_y.is_some() {
1271            self.rotate_y = other.rotate_y;
1272        }
1273        if other.perspective_3d.is_some() {
1274            self.perspective_3d = other.perspective_3d;
1275        }
1276        if other.shape_3d.is_some() {
1277            self.shape_3d = other.shape_3d;
1278        }
1279        if other.depth.is_some() {
1280            self.depth = other.depth;
1281        }
1282        if other.light_direction.is_some() {
1283            self.light_direction = other.light_direction;
1284        }
1285        if other.light_intensity.is_some() {
1286            self.light_intensity = other.light_intensity;
1287        }
1288        if other.ambient.is_some() {
1289            self.ambient = other.ambient;
1290        }
1291        if other.specular.is_some() {
1292            self.specular = other.specular;
1293        }
1294        if other.translate_z.is_some() {
1295            self.translate_z = other.translate_z;
1296        }
1297        if other.op_3d.is_some() {
1298            self.op_3d = other.op_3d;
1299        }
1300        if other.blend_3d.is_some() {
1301            self.blend_3d = other.blend_3d;
1302        }
1303        if other.clip_path.is_some() {
1304            self.clip_path = other.clip_path;
1305        }
1306        if other.flow_name.is_some() {
1307            self.flow_name = other.flow_name;
1308            self.flow_graph = other.flow_graph;
1309        }
1310
1311        // Note: event_handlers are NOT merged - they're set on the base element
1312    }
1313
1314    /// Merge taffy Style fields from other if they differ from default
1315    fn merge_style(&mut self, other: &Style, default: &Style) {
1316        // Display & position
1317        if other.display != default.display {
1318            self.style.display = other.display;
1319        }
1320        if other.position != default.position {
1321            self.style.position = other.position;
1322        }
1323        if other.overflow != default.overflow {
1324            self.style.overflow = other.overflow;
1325        }
1326
1327        // Flex container properties
1328        if other.flex_direction != default.flex_direction {
1329            self.style.flex_direction = other.flex_direction;
1330        }
1331        if other.flex_wrap != default.flex_wrap {
1332            self.style.flex_wrap = other.flex_wrap;
1333        }
1334        if other.justify_content != default.justify_content {
1335            self.style.justify_content = other.justify_content;
1336        }
1337        if other.align_items != default.align_items {
1338            self.style.align_items = other.align_items;
1339        }
1340        if other.align_content != default.align_content {
1341            self.style.align_content = other.align_content;
1342        }
1343        // Gap - merge per axis
1344        if other.gap.width != default.gap.width {
1345            self.style.gap.width = other.gap.width;
1346        }
1347        if other.gap.height != default.gap.height {
1348            self.style.gap.height = other.gap.height;
1349        }
1350
1351        // Flex item properties
1352        if other.flex_grow != default.flex_grow {
1353            self.style.flex_grow = other.flex_grow;
1354        }
1355        if other.flex_shrink != default.flex_shrink {
1356            self.style.flex_shrink = other.flex_shrink;
1357        }
1358        if other.flex_basis != default.flex_basis {
1359            self.style.flex_basis = other.flex_basis;
1360        }
1361        if other.align_self != default.align_self {
1362            self.style.align_self = other.align_self;
1363        }
1364
1365        // Size constraints - merge per dimension to allow w() then h() separately
1366        if other.size.width != default.size.width {
1367            self.style.size.width = other.size.width;
1368        }
1369        if other.size.height != default.size.height {
1370            self.style.size.height = other.size.height;
1371        }
1372        if other.min_size.width != default.min_size.width {
1373            self.style.min_size.width = other.min_size.width;
1374        }
1375        if other.min_size.height != default.min_size.height {
1376            self.style.min_size.height = other.min_size.height;
1377        }
1378        if other.max_size.width != default.max_size.width {
1379            self.style.max_size.width = other.max_size.width;
1380        }
1381        if other.max_size.height != default.max_size.height {
1382            self.style.max_size.height = other.max_size.height;
1383        }
1384        if other.aspect_ratio != default.aspect_ratio {
1385            self.style.aspect_ratio = other.aspect_ratio;
1386        }
1387
1388        // Spacing - merge per side to allow partial updates (e.g., px then py)
1389        // Margin
1390        if other.margin.left != default.margin.left {
1391            self.style.margin.left = other.margin.left;
1392        }
1393        if other.margin.right != default.margin.right {
1394            self.style.margin.right = other.margin.right;
1395        }
1396        if other.margin.top != default.margin.top {
1397            self.style.margin.top = other.margin.top;
1398        }
1399        if other.margin.bottom != default.margin.bottom {
1400            self.style.margin.bottom = other.margin.bottom;
1401        }
1402        // Padding
1403        if other.padding.left != default.padding.left {
1404            self.style.padding.left = other.padding.left;
1405        }
1406        if other.padding.right != default.padding.right {
1407            self.style.padding.right = other.padding.right;
1408        }
1409        if other.padding.top != default.padding.top {
1410            self.style.padding.top = other.padding.top;
1411        }
1412        if other.padding.bottom != default.padding.bottom {
1413            self.style.padding.bottom = other.padding.bottom;
1414        }
1415        // Border
1416        if other.border.left != default.border.left {
1417            self.style.border.left = other.border.left;
1418        }
1419        if other.border.right != default.border.right {
1420            self.style.border.right = other.border.right;
1421        }
1422        if other.border.top != default.border.top {
1423            self.style.border.top = other.border.top;
1424        }
1425        if other.border.bottom != default.border.bottom {
1426            self.style.border.bottom = other.border.bottom;
1427        }
1428
1429        // Inset (for absolute positioning) - merge per side
1430        if other.inset.left != default.inset.left {
1431            self.style.inset.left = other.inset.left;
1432        }
1433        if other.inset.right != default.inset.right {
1434            self.style.inset.right = other.inset.right;
1435        }
1436        if other.inset.top != default.inset.top {
1437            self.style.inset.top = other.inset.top;
1438        }
1439        if other.inset.bottom != default.inset.bottom {
1440            self.style.inset.bottom = other.inset.bottom;
1441        }
1442    }
1443
1444    // =========================================================================
1445    // Display & Flex Direction
1446    // =========================================================================
1447
1448    /// Set display to flex (default)
1449    pub fn flex(mut self) -> Self {
1450        self.style.display = Display::Flex;
1451        self
1452    }
1453
1454    /// Set display to block
1455    pub fn block(mut self) -> Self {
1456        self.style.display = Display::Block;
1457        self
1458    }
1459
1460    /// Set display to grid
1461    pub fn grid(mut self) -> Self {
1462        self.style.display = Display::Grid;
1463        self
1464    }
1465
1466    /// Set display to none
1467    pub fn hidden(mut self) -> Self {
1468        self.style.display = Display::None;
1469        self
1470    }
1471
1472    /// Set flex direction to row (horizontal)
1473    pub fn flex_row(mut self) -> Self {
1474        self.style.display = Display::Flex;
1475        self.style.flex_direction = FlexDirection::Row;
1476        self
1477    }
1478
1479    /// Set flex direction to column (vertical)
1480    pub fn flex_col(mut self) -> Self {
1481        self.style.display = Display::Flex;
1482        self.style.flex_direction = FlexDirection::Column;
1483        self
1484    }
1485
1486    /// Set flex direction to row-reverse
1487    pub fn flex_row_reverse(mut self) -> Self {
1488        self.style.display = Display::Flex;
1489        self.style.flex_direction = FlexDirection::RowReverse;
1490        self
1491    }
1492
1493    /// Set flex direction to column-reverse
1494    pub fn flex_col_reverse(mut self) -> Self {
1495        self.style.display = Display::Flex;
1496        self.style.flex_direction = FlexDirection::ColumnReverse;
1497        self
1498    }
1499
1500    // =========================================================================
1501    // Flex Properties
1502    // =========================================================================
1503
1504    /// Set flex-grow to 1 (element will grow to fill space)
1505    pub fn flex_grow(mut self) -> Self {
1506        self.style.flex_grow = 1.0;
1507        self
1508    }
1509
1510    /// Set flex-grow to a specific value
1511    ///
1512    /// Use this for proportional sizing. For example, an element with flex_grow_value(2.0)
1513    /// will grow to twice the size of an element with flex_grow_value(1.0).
1514    pub fn flex_grow_value(mut self, value: f32) -> Self {
1515        self.style.flex_grow = value;
1516        self
1517    }
1518
1519    /// Set flex-shrink to 1 (element will shrink if needed)
1520    pub fn flex_shrink(mut self) -> Self {
1521        self.style.flex_shrink = 1.0;
1522        self
1523    }
1524
1525    /// Set flex-shrink to 0 (element won't shrink)
1526    pub fn flex_shrink_0(mut self) -> Self {
1527        self.style.flex_shrink = 0.0;
1528        self
1529    }
1530
1531    /// Set flex-basis to auto
1532    pub fn flex_auto(mut self) -> Self {
1533        self.style.flex_grow = 1.0;
1534        self.style.flex_shrink = 1.0;
1535        self.style.flex_basis = Dimension::Auto;
1536        self
1537    }
1538
1539    /// Set flex: 1 1 0% (grow, shrink, basis 0)
1540    pub fn flex_1(mut self) -> Self {
1541        self.style.flex_grow = 1.0;
1542        self.style.flex_shrink = 1.0;
1543        self.style.flex_basis = Dimension::Length(0.0);
1544        self
1545    }
1546
1547    /// Allow wrapping
1548    pub fn flex_wrap(mut self) -> Self {
1549        self.style.flex_wrap = FlexWrap::Wrap;
1550        self
1551    }
1552
1553    // =========================================================================
1554    // Alignment & Justification
1555    // =========================================================================
1556
1557    /// Center items both horizontally and vertically
1558    pub fn items_center(mut self) -> Self {
1559        self.style.align_items = Some(AlignItems::Center);
1560        self
1561    }
1562
1563    /// Align items to start
1564    pub fn items_start(mut self) -> Self {
1565        self.style.align_items = Some(AlignItems::Start);
1566        self
1567    }
1568
1569    /// Align items to end
1570    pub fn items_end(mut self) -> Self {
1571        self.style.align_items = Some(AlignItems::End);
1572        self
1573    }
1574
1575    /// Stretch items to fill (default)
1576    pub fn items_stretch(mut self) -> Self {
1577        self.style.align_items = Some(AlignItems::Stretch);
1578        self
1579    }
1580
1581    /// Align items to baseline
1582    pub fn items_baseline(mut self) -> Self {
1583        self.style.align_items = Some(AlignItems::Baseline);
1584        self
1585    }
1586
1587    /// Align self to start (overrides parent's align_items for this element)
1588    pub fn align_self_start(mut self) -> Self {
1589        self.style.align_self = Some(AlignSelf::Start);
1590        self
1591    }
1592
1593    /// Align self to center (overrides parent's align_items for this element)
1594    pub fn align_self_center(mut self) -> Self {
1595        self.style.align_self = Some(AlignSelf::Center);
1596        self
1597    }
1598
1599    /// Align self to end (overrides parent's align_items for this element)
1600    pub fn align_self_end(mut self) -> Self {
1601        self.style.align_self = Some(AlignSelf::End);
1602        self
1603    }
1604
1605    /// Stretch self to fill (overrides parent's align_items for this element)
1606    pub fn align_self_stretch(mut self) -> Self {
1607        self.style.align_self = Some(AlignSelf::Stretch);
1608        self
1609    }
1610
1611    /// Justify content to start
1612    pub fn justify_start(mut self) -> Self {
1613        self.style.justify_content = Some(JustifyContent::Start);
1614        self
1615    }
1616
1617    /// Justify content to center
1618    pub fn justify_center(mut self) -> Self {
1619        self.style.justify_content = Some(JustifyContent::Center);
1620        self
1621    }
1622
1623    /// Justify content to end
1624    pub fn justify_end(mut self) -> Self {
1625        self.style.justify_content = Some(JustifyContent::End);
1626        self
1627    }
1628
1629    /// Space between items
1630    pub fn justify_between(mut self) -> Self {
1631        self.style.justify_content = Some(JustifyContent::SpaceBetween);
1632        self
1633    }
1634
1635    /// Space around items
1636    pub fn justify_around(mut self) -> Self {
1637        self.style.justify_content = Some(JustifyContent::SpaceAround);
1638        self
1639    }
1640
1641    /// Space evenly between items
1642    pub fn justify_evenly(mut self) -> Self {
1643        self.style.justify_content = Some(JustifyContent::SpaceEvenly);
1644        self
1645    }
1646
1647    /// Align content to start (for multi-line flex containers)
1648    pub fn content_start(mut self) -> Self {
1649        self.style.align_content = Some(taffy::AlignContent::Start);
1650        self
1651    }
1652
1653    /// Align content to center (for multi-line flex containers)
1654    pub fn content_center(mut self) -> Self {
1655        self.style.align_content = Some(taffy::AlignContent::Center);
1656        self
1657    }
1658
1659    /// Align content to end (for multi-line flex containers)
1660    pub fn content_end(mut self) -> Self {
1661        self.style.align_content = Some(taffy::AlignContent::End);
1662        self
1663    }
1664
1665    /// Align this element to start on cross-axis (overrides parent's align-items)
1666    ///
1667    /// In a flex-row parent, this prevents height stretching.
1668    /// In a flex-col parent, this prevents width stretching.
1669    pub fn self_start(mut self) -> Self {
1670        self.style.align_self = Some(AlignSelf::FlexStart);
1671        self
1672    }
1673
1674    /// Align this element to center on cross-axis (overrides parent's align-items)
1675    pub fn self_center(mut self) -> Self {
1676        self.style.align_self = Some(AlignSelf::Center);
1677        self
1678    }
1679
1680    /// Align this element to end on cross-axis (overrides parent's align-items)
1681    pub fn self_end(mut self) -> Self {
1682        self.style.align_self = Some(AlignSelf::FlexEnd);
1683        self
1684    }
1685
1686    /// Stretch this element on cross-axis (overrides parent's align-items)
1687    pub fn self_stretch(mut self) -> Self {
1688        self.style.align_self = Some(AlignSelf::Stretch);
1689        self
1690    }
1691
1692    // =========================================================================
1693    // Sizing (pixel values)
1694    // =========================================================================
1695
1696    /// Set width in pixels
1697    pub fn w(mut self, px: f32) -> Self {
1698        self.style.size.width = Dimension::Length(px);
1699        self
1700    }
1701
1702    /// Set width to 100%
1703    pub fn w_full(mut self) -> Self {
1704        self.style.size.width = Dimension::Percent(1.0);
1705        self
1706    }
1707
1708    /// Set width to auto
1709    pub fn w_auto(mut self) -> Self {
1710        self.style.size.width = Dimension::Auto;
1711        self
1712    }
1713
1714    /// Set width to fit content (shrink-wrap to children)
1715    ///
1716    /// This sets width to auto with flex_basis auto and prevents flex growing/shrinking,
1717    /// so the element will size exactly to fit its content.
1718    pub fn w_fit(mut self) -> Self {
1719        self.style.size.width = Dimension::Auto;
1720        self.style.flex_basis = Dimension::Auto;
1721        self.style.flex_grow = 0.0;
1722        self.style.flex_shrink = 0.0;
1723        self
1724    }
1725
1726    /// Set height in pixels
1727    pub fn h(mut self, px: f32) -> Self {
1728        self.style.size.height = Dimension::Length(px);
1729        self
1730    }
1731
1732    /// Set height to 100%
1733    pub fn h_full(mut self) -> Self {
1734        self.style.size.height = Dimension::Percent(1.0);
1735        self
1736    }
1737
1738    /// Set height to auto
1739    pub fn h_auto(mut self) -> Self {
1740        self.style.size.height = Dimension::Auto;
1741        self
1742    }
1743
1744    /// Set height to fit content (shrink-wrap to children)
1745    ///
1746    /// This sets height to auto and prevents flex growing/shrinking, so the element
1747    /// will size exactly to fit its content.
1748    pub fn h_fit(mut self) -> Self {
1749        self.style.size.height = Dimension::Auto;
1750        self.style.flex_basis = Dimension::Auto;
1751        self.style.flex_grow = 0.0;
1752        self.style.flex_shrink = 0.0;
1753        self
1754    }
1755
1756    /// Set both width and height to fit content
1757    ///
1758    /// This makes the element shrink-wrap to its content in both dimensions.
1759    pub fn size_fit(mut self) -> Self {
1760        self.style.size.width = Dimension::Auto;
1761        self.style.size.height = Dimension::Auto;
1762        self.style.flex_basis = Dimension::Auto;
1763        self.style.flex_grow = 0.0;
1764        self.style.flex_shrink = 0.0;
1765        self
1766    }
1767
1768    /// Set both width and height in pixels
1769    pub fn size(mut self, w: f32, h: f32) -> Self {
1770        self.style.size.width = Dimension::Length(w);
1771        self.style.size.height = Dimension::Length(h);
1772        self
1773    }
1774
1775    /// Set square size (width and height equal)
1776    pub fn square(mut self, size: f32) -> Self {
1777        self.style.size.width = Dimension::Length(size);
1778        self.style.size.height = Dimension::Length(size);
1779        self
1780    }
1781
1782    /// Set min-width in pixels
1783    pub fn min_w(mut self, px: f32) -> Self {
1784        self.style.min_size.width = Dimension::Length(px);
1785        self
1786    }
1787
1788    /// Set min-height in pixels
1789    pub fn min_h(mut self, px: f32) -> Self {
1790        self.style.min_size.height = Dimension::Length(px);
1791        self
1792    }
1793
1794    /// Set max-width in pixels
1795    pub fn max_w(mut self, px: f32) -> Self {
1796        self.style.max_size.width = Dimension::Length(px);
1797        self
1798    }
1799
1800    /// Set max-height in pixels
1801    pub fn max_h(mut self, px: f32) -> Self {
1802        self.style.max_size.height = Dimension::Length(px);
1803        self
1804    }
1805
1806    // =========================================================================
1807    // Spacing (4px base unit like Tailwind)
1808    // =========================================================================
1809
1810    /// Set gap between children (in 4px units)
1811    /// gap(4) = 16px
1812    pub fn gap(mut self, units: f32) -> Self {
1813        let px = units * 4.0;
1814        self.style.gap = taffy::Size {
1815            width: LengthPercentage::Length(px),
1816            height: LengthPercentage::Length(px),
1817        };
1818        self
1819    }
1820
1821    /// Set gap in pixels directly
1822    pub fn gap_px(mut self, px: f32) -> Self {
1823        self.style.gap = taffy::Size {
1824            width: LengthPercentage::Length(px),
1825            height: LengthPercentage::Length(px),
1826        };
1827        self
1828    }
1829
1830    /// Set column gap (horizontal spacing between items)
1831    pub fn gap_x(mut self, units: f32) -> Self {
1832        self.style.gap.width = LengthPercentage::Length(units * 4.0);
1833        self
1834    }
1835
1836    /// Set row gap (vertical spacing between items)
1837    pub fn gap_y(mut self, units: f32) -> Self {
1838        self.style.gap.height = LengthPercentage::Length(units * 4.0);
1839        self
1840    }
1841
1842    // -------------------------------------------------------------------------
1843    // Theme-based gap spacing
1844    // -------------------------------------------------------------------------
1845
1846    /// Set gap using theme spacing scale (space_1 = 4px)
1847    pub fn gap_1(self) -> Self {
1848        self.gap_px(ThemeState::get().spacing().space_1)
1849    }
1850
1851    /// Set gap using theme spacing scale (space_2 = 8px)
1852    pub fn gap_2(self) -> Self {
1853        self.gap_px(ThemeState::get().spacing().space_2)
1854    }
1855
1856    /// Set gap using theme spacing scale (space_3 = 12px)
1857    pub fn gap_3(self) -> Self {
1858        self.gap_px(ThemeState::get().spacing().space_3)
1859    }
1860
1861    /// Set gap using theme spacing scale (space_4 = 16px)
1862    pub fn gap_4(self) -> Self {
1863        self.gap_px(ThemeState::get().spacing().space_4)
1864    }
1865
1866    /// Set gap using theme spacing scale (space_5 = 20px)
1867    pub fn gap_5(self) -> Self {
1868        self.gap_px(ThemeState::get().spacing().space_5)
1869    }
1870
1871    /// Set gap using theme spacing scale (space_6 = 24px)
1872    pub fn gap_6(self) -> Self {
1873        self.gap_px(ThemeState::get().spacing().space_6)
1874    }
1875
1876    /// Set gap using theme spacing scale (space_8 = 32px)
1877    pub fn gap_8(self) -> Self {
1878        self.gap_px(ThemeState::get().spacing().space_8)
1879    }
1880
1881    /// Set gap using theme spacing scale (space_10 = 40px)
1882    pub fn gap_10(self) -> Self {
1883        self.gap_px(ThemeState::get().spacing().space_10)
1884    }
1885
1886    /// Set gap using theme spacing scale (space_12 = 48px)
1887    pub fn gap_12(self) -> Self {
1888        self.gap_px(ThemeState::get().spacing().space_12)
1889    }
1890
1891    // -------------------------------------------------------------------------
1892    // Padding
1893    // -------------------------------------------------------------------------
1894
1895    /// Set padding on all sides (in 4px units)
1896    /// p(4) = 16px padding
1897    pub fn p(mut self, units: f32) -> Self {
1898        let px = LengthPercentage::Length(units * 4.0);
1899        self.style.padding = Rect {
1900            left: px,
1901            right: px,
1902            top: px,
1903            bottom: px,
1904        };
1905        self
1906    }
1907
1908    /// Set padding in pixels
1909    pub fn p_px(mut self, px: f32) -> Self {
1910        let val = LengthPercentage::Length(px);
1911        self.style.padding = Rect {
1912            left: val,
1913            right: val,
1914            top: val,
1915            bottom: val,
1916        };
1917        self
1918    }
1919
1920    /// Set horizontal padding (in 4px units)
1921    pub fn px(mut self, units: f32) -> Self {
1922        let px = LengthPercentage::Length(units * 4.0);
1923        self.style.padding.left = px;
1924        self.style.padding.right = px;
1925        self
1926    }
1927
1928    /// Set vertical padding (in 4px units)
1929    pub fn py(mut self, units: f32) -> Self {
1930        let px = LengthPercentage::Length(units * 4.0);
1931        self.style.padding.top = px;
1932        self.style.padding.bottom = px;
1933        self
1934    }
1935
1936    /// Set left padding (in 4px units)
1937    pub fn pl(mut self, units: f32) -> Self {
1938        self.style.padding.left = LengthPercentage::Length(units * 4.0);
1939        self
1940    }
1941
1942    /// Set right padding (in 4px units)
1943    pub fn pr(mut self, units: f32) -> Self {
1944        self.style.padding.right = LengthPercentage::Length(units * 4.0);
1945        self
1946    }
1947
1948    /// Set top padding (in 4px units)
1949    pub fn pt(mut self, units: f32) -> Self {
1950        self.style.padding.top = LengthPercentage::Length(units * 4.0);
1951        self
1952    }
1953
1954    /// Set bottom padding (in 4px units)
1955    pub fn pb(mut self, units: f32) -> Self {
1956        self.style.padding.bottom = LengthPercentage::Length(units * 4.0);
1957        self
1958    }
1959
1960    // -------------------------------------------------------------------------
1961    // Theme-based padding
1962    // -------------------------------------------------------------------------
1963
1964    /// Set padding using theme spacing scale (space_1 = 4px)
1965    pub fn p_1(self) -> Self {
1966        let px = ThemeState::get().spacing().space_1;
1967        self.padding(crate::units::Length::Px(px))
1968    }
1969
1970    /// Set padding using theme spacing scale (space_2 = 8px)
1971    pub fn p_2(self) -> Self {
1972        let px = ThemeState::get().spacing().space_2;
1973        self.padding(crate::units::Length::Px(px))
1974    }
1975
1976    /// Set padding using theme spacing scale (space_3 = 12px)
1977    pub fn p_3(self) -> Self {
1978        let px = ThemeState::get().spacing().space_3;
1979        self.padding(crate::units::Length::Px(px))
1980    }
1981
1982    /// Set padding using theme spacing scale (space_4 = 16px)
1983    pub fn p_4(self) -> Self {
1984        let px = ThemeState::get().spacing().space_4;
1985        self.padding(crate::units::Length::Px(px))
1986    }
1987
1988    /// Set padding using theme spacing scale (space_5 = 20px)
1989    pub fn p_5(self) -> Self {
1990        let px = ThemeState::get().spacing().space_5;
1991        self.padding(crate::units::Length::Px(px))
1992    }
1993
1994    /// Set padding using theme spacing scale (space_6 = 24px)
1995    pub fn p_6(self) -> Self {
1996        let px = ThemeState::get().spacing().space_6;
1997        self.padding(crate::units::Length::Px(px))
1998    }
1999
2000    /// Set padding using theme spacing scale (space_8 = 32px)
2001    pub fn p_8(self) -> Self {
2002        let px = ThemeState::get().spacing().space_8;
2003        self.padding(crate::units::Length::Px(px))
2004    }
2005
2006    /// Set padding using theme spacing scale (space_10 = 40px)
2007    pub fn p_10(self) -> Self {
2008        let px = ThemeState::get().spacing().space_10;
2009        self.padding(crate::units::Length::Px(px))
2010    }
2011
2012    /// Set padding using theme spacing scale (space_12 = 48px)
2013    pub fn p_12(self) -> Self {
2014        let px = ThemeState::get().spacing().space_12;
2015        self.padding(crate::units::Length::Px(px))
2016    }
2017
2018    // =========================================================================
2019    // Raw Pixel Padding (for internal/widget use)
2020    // =========================================================================
2021
2022    /// Set horizontal padding in raw pixels (no unit conversion)
2023    pub fn padding_x_px(mut self, pixels: f32) -> Self {
2024        let px = LengthPercentage::Length(pixels);
2025        self.style.padding.left = px;
2026        self.style.padding.right = px;
2027        self
2028    }
2029
2030    /// Set vertical padding in raw pixels (no unit conversion)
2031    pub fn padding_y_px(mut self, pixels: f32) -> Self {
2032        let px = LengthPercentage::Length(pixels);
2033        self.style.padding.top = px;
2034        self.style.padding.bottom = px;
2035        self
2036    }
2037
2038    // =========================================================================
2039    // Semantic Length-based API (uses Length type for clarity)
2040    // =========================================================================
2041
2042    /// Set padding using a semantic Length value
2043    ///
2044    /// # Examples
2045    /// ```rust,ignore
2046    /// use blinc_layout::prelude::*;
2047    ///
2048    /// div().padding(px(16.0))  // 16 raw pixels
2049    /// div().padding(sp(4.0))   // 4 spacing units = 16px
2050    /// ```
2051    pub fn padding(mut self, len: crate::units::Length) -> Self {
2052        let val: LengthPercentage = len.into();
2053        self.style.padding = Rect {
2054            left: val,
2055            right: val,
2056            top: val,
2057            bottom: val,
2058        };
2059        self
2060    }
2061
2062    /// Set horizontal padding using a semantic Length value
2063    pub fn padding_x(mut self, len: crate::units::Length) -> Self {
2064        let val: LengthPercentage = len.into();
2065        self.style.padding.left = val;
2066        self.style.padding.right = val;
2067        self
2068    }
2069
2070    /// Set vertical padding using a semantic Length value
2071    pub fn padding_y(mut self, len: crate::units::Length) -> Self {
2072        let val: LengthPercentage = len.into();
2073        self.style.padding.top = val;
2074        self.style.padding.bottom = val;
2075        self
2076    }
2077
2078    /// Set margin on all sides (in 4px units)
2079    pub fn m(mut self, units: f32) -> Self {
2080        let px = LengthPercentageAuto::Length(units * 4.0);
2081        self.style.margin = Rect {
2082            left: px,
2083            right: px,
2084            top: px,
2085            bottom: px,
2086        };
2087        self
2088    }
2089
2090    /// Set margin in pixels
2091    pub fn m_px(mut self, px: f32) -> Self {
2092        let val = LengthPercentageAuto::Length(px);
2093        self.style.margin = Rect {
2094            left: val,
2095            right: val,
2096            top: val,
2097            bottom: val,
2098        };
2099        self
2100    }
2101
2102    /// Set horizontal margin (in 4px units)
2103    pub fn mx(mut self, units: f32) -> Self {
2104        let px = LengthPercentageAuto::Length(units * 4.0);
2105        self.style.margin.left = px;
2106        self.style.margin.right = px;
2107        self
2108    }
2109
2110    /// Set vertical margin (in 4px units)
2111    pub fn my(mut self, units: f32) -> Self {
2112        let px = LengthPercentageAuto::Length(units * 4.0);
2113        self.style.margin.top = px;
2114        self.style.margin.bottom = px;
2115        self
2116    }
2117
2118    /// Set auto horizontal margin (centering)
2119    pub fn mx_auto(mut self) -> Self {
2120        self.style.margin.left = LengthPercentageAuto::Auto;
2121        self.style.margin.right = LengthPercentageAuto::Auto;
2122        self
2123    }
2124
2125    /// Set left margin (in 4px units)
2126    pub fn ml(mut self, units: f32) -> Self {
2127        self.style.margin.left = LengthPercentageAuto::Length(units * 4.0);
2128        self
2129    }
2130
2131    /// Set right margin (in 4px units)
2132    pub fn mr(mut self, units: f32) -> Self {
2133        self.style.margin.right = LengthPercentageAuto::Length(units * 4.0);
2134        self
2135    }
2136
2137    /// Set top margin (in 4px units)
2138    pub fn mt(mut self, units: f32) -> Self {
2139        self.style.margin.top = LengthPercentageAuto::Length(units * 4.0);
2140        self
2141    }
2142
2143    /// Set bottom margin (in 4px units)
2144    pub fn mb(mut self, units: f32) -> Self {
2145        self.style.margin.bottom = LengthPercentageAuto::Length(units * 4.0);
2146        self
2147    }
2148
2149    // -------------------------------------------------------------------------
2150    // Theme-based margin
2151    // -------------------------------------------------------------------------
2152
2153    /// Set margin using theme spacing scale (space_1 = 4px)
2154    pub fn m_1(self) -> Self {
2155        self.m_px(ThemeState::get().spacing().space_1)
2156    }
2157
2158    /// Set margin using theme spacing scale (space_2 = 8px)
2159    pub fn m_2(self) -> Self {
2160        self.m_px(ThemeState::get().spacing().space_2)
2161    }
2162
2163    /// Set margin using theme spacing scale (space_3 = 12px)
2164    pub fn m_3(self) -> Self {
2165        self.m_px(ThemeState::get().spacing().space_3)
2166    }
2167
2168    /// Set margin using theme spacing scale (space_4 = 16px)
2169    pub fn m_4(self) -> Self {
2170        self.m_px(ThemeState::get().spacing().space_4)
2171    }
2172
2173    /// Set margin using theme spacing scale (space_5 = 20px)
2174    pub fn m_5(self) -> Self {
2175        self.m_px(ThemeState::get().spacing().space_5)
2176    }
2177
2178    /// Set margin using theme spacing scale (space_6 = 24px)
2179    pub fn m_6(self) -> Self {
2180        self.m_px(ThemeState::get().spacing().space_6)
2181    }
2182
2183    /// Set margin using theme spacing scale (space_8 = 32px)
2184    pub fn m_8(self) -> Self {
2185        self.m_px(ThemeState::get().spacing().space_8)
2186    }
2187
2188    // =========================================================================
2189    // Position
2190    // =========================================================================
2191
2192    /// Set position to absolute
2193    pub fn absolute(mut self) -> Self {
2194        self.style.position = Position::Absolute;
2195        self
2196    }
2197
2198    /// Set position to relative (default)
2199    pub fn relative(mut self) -> Self {
2200        self.style.position = Position::Relative;
2201        self
2202    }
2203
2204    /// Set position to fixed (stays in place when ancestors scroll)
2205    pub fn fixed(mut self) -> Self {
2206        self.style.position = Position::Absolute;
2207        self.is_fixed = true;
2208        self.is_sticky = false;
2209        self
2210    }
2211
2212    /// Set position to sticky with a top threshold
2213    pub fn sticky(mut self, top: f32) -> Self {
2214        self.style.position = Position::Relative;
2215        self.is_sticky = true;
2216        self.is_fixed = false;
2217        self.sticky_top = Some(top);
2218        self
2219    }
2220
2221    /// Set inset (position from all edges)
2222    pub fn inset(mut self, px: f32) -> Self {
2223        let val = LengthPercentageAuto::Length(px);
2224        self.style.inset = Rect {
2225            left: val,
2226            right: val,
2227            top: val,
2228            bottom: val,
2229        };
2230        self
2231    }
2232
2233    /// Set top position
2234    pub fn top(mut self, px: f32) -> Self {
2235        self.style.inset.top = LengthPercentageAuto::Length(px);
2236        self
2237    }
2238
2239    /// Set bottom position
2240    pub fn bottom(mut self, px: f32) -> Self {
2241        self.style.inset.bottom = LengthPercentageAuto::Length(px);
2242        self
2243    }
2244
2245    /// Set left position
2246    pub fn left(mut self, px: f32) -> Self {
2247        self.style.inset.left = LengthPercentageAuto::Length(px);
2248        self
2249    }
2250
2251    /// Set right position
2252    pub fn right(mut self, px: f32) -> Self {
2253        self.style.inset.right = LengthPercentageAuto::Length(px);
2254        self
2255    }
2256
2257    // =========================================================================
2258    // Overflow
2259    // =========================================================================
2260
2261    /// Set overflow to hidden (clip content)
2262    ///
2263    /// Content that extends beyond the element's bounds will be clipped.
2264    /// This is essential for scroll containers.
2265    pub fn overflow_clip(mut self) -> Self {
2266        self.style.overflow.x = Overflow::Clip;
2267        self.style.overflow.y = Overflow::Clip;
2268        self
2269    }
2270
2271    /// Set overflow to visible (default, content can extend beyond bounds)
2272    pub fn overflow_visible(mut self) -> Self {
2273        self.style.overflow.x = Overflow::Visible;
2274        self.style.overflow.y = Overflow::Visible;
2275        self
2276    }
2277
2278    /// Set overflow to scroll (enable scrolling with full physics)
2279    ///
2280    /// Creates a scroll container with momentum scrolling, bounce, and scrollbar.
2281    /// This is equivalent to using the `scroll()` widget but configured via the builder API.
2282    pub fn overflow_scroll(mut self) -> Self {
2283        self.style.overflow.x = Overflow::Scroll;
2284        self.style.overflow.y = Overflow::Scroll;
2285        self.ensure_scroll_physics(crate::scroll::ScrollDirection::Both);
2286        self
2287    }
2288
2289    /// Set overflow to scroll (taffy style only, no physics)
2290    ///
2291    /// Used internally by the `Scroll` widget which manages its own physics.
2292    pub(crate) fn overflow_scroll_style_only(mut self) -> Self {
2293        self.style.overflow.x = Overflow::Scroll;
2294        self.style.overflow.y = Overflow::Scroll;
2295        self
2296    }
2297
2298    /// Set horizontal overflow only (X-axis)
2299    pub fn overflow_x(mut self, overflow: Overflow) -> Self {
2300        self.style.overflow.x = overflow;
2301        if overflow == Overflow::Scroll {
2302            self.ensure_scroll_physics(crate::scroll::ScrollDirection::Horizontal);
2303        }
2304        self
2305    }
2306
2307    /// Set vertical overflow only (Y-axis)
2308    pub fn overflow_y(mut self, overflow: Overflow) -> Self {
2309        self.style.overflow.y = overflow;
2310        if overflow == Overflow::Scroll {
2311            self.ensure_scroll_physics(crate::scroll::ScrollDirection::Vertical);
2312        }
2313        self
2314    }
2315
2316    /// Set overflow to scroll on X-axis only
2317    pub fn overflow_x_scroll(mut self) -> Self {
2318        self.style.overflow.x = Overflow::Scroll;
2319        self.style.overflow.y = Overflow::Clip;
2320        self.ensure_scroll_physics(crate::scroll::ScrollDirection::Horizontal);
2321        self
2322    }
2323
2324    /// Set overflow to scroll on Y-axis only
2325    pub fn overflow_y_scroll(mut self) -> Self {
2326        self.style.overflow.x = Overflow::Clip;
2327        self.style.overflow.y = Overflow::Scroll;
2328        self.ensure_scroll_physics(crate::scroll::ScrollDirection::Vertical);
2329        self
2330    }
2331
2332    /// Ensure scroll physics are created for this div
2333    fn ensure_scroll_physics(&mut self, direction: crate::scroll::ScrollDirection) {
2334        if self.scroll_physics.is_none() {
2335            let config = crate::scroll::ScrollConfig {
2336                direction,
2337                ..Default::default()
2338            };
2339            let physics = std::sync::Arc::new(std::sync::Mutex::new(
2340                crate::scroll::ScrollPhysics::new(config),
2341            ));
2342            let handlers =
2343                crate::scroll::Scroll::create_internal_handlers(std::sync::Arc::clone(&physics));
2344            // Merge scroll handlers into existing event handlers
2345            self.event_handlers.merge(handlers);
2346            self.scroll_physics = Some(physics);
2347        }
2348    }
2349
2350    // =========================================================================
2351    // Visual Properties
2352    // =========================================================================
2353
2354    /// Set background color
2355    pub fn bg(mut self, color: Color) -> Self {
2356        self.background = Some(Brush::Solid(color));
2357        self
2358    }
2359
2360    /// Set background brush (for gradients)
2361    pub fn background(mut self, brush: impl Into<Brush>) -> Self {
2362        self.background = Some(brush.into());
2363        self
2364    }
2365
2366    // -------------------------------------------------------------------------
2367    // Backdrop Blur (CSS backdrop-filter: blur())
2368    // -------------------------------------------------------------------------
2369
2370    /// Apply backdrop blur effect to content behind this element
2371    ///
2372    /// This blurs whatever is rendered behind this element, similar to
2373    /// CSS `backdrop-filter: blur()`. For a full frosted glass effect with
2374    /// tinting and noise, use `.glass()` instead.
2375    ///
2376    /// # Example
2377    ///
2378    /// ```ignore
2379    /// div()
2380    ///     .w(200.0).h(100.0)
2381    ///     .backdrop_blur(10.0) // 10px blur radius
2382    ///     .bg(Color::rgba(255, 255, 255, 128)) // Semi-transparent overlay
2383    /// ```
2384    pub fn backdrop_blur(self, radius: f32) -> Self {
2385        self.background(Brush::Blur(BlurStyle::with_radius(radius)))
2386    }
2387
2388    /// Apply backdrop blur with full style control
2389    ///
2390    /// # Example
2391    ///
2392    /// ```ignore
2393    /// div()
2394    ///     .backdrop_blur_style(
2395    ///         BlurStyle::new()
2396    ///             .radius(20.0)
2397    ///             .quality(BlurQuality::High)
2398    ///             .tint(Color::rgba(255, 255, 255, 50))
2399    ///     )
2400    /// ```
2401    pub fn backdrop_blur_style(self, style: BlurStyle) -> Self {
2402        self.background(Brush::Blur(style))
2403    }
2404
2405    /// Light backdrop blur (5px, subtle)
2406    pub fn backdrop_blur_light(self) -> Self {
2407        self.background(Brush::Blur(BlurStyle::light()))
2408    }
2409
2410    /// Medium backdrop blur (10px, default)
2411    pub fn backdrop_blur_medium(self) -> Self {
2412        self.background(Brush::Blur(BlurStyle::medium()))
2413    }
2414
2415    /// Heavy backdrop blur (20px, strong)
2416    pub fn backdrop_blur_heavy(self) -> Self {
2417        self.background(Brush::Blur(BlurStyle::heavy()))
2418    }
2419
2420    /// Maximum backdrop blur (50px)
2421    pub fn backdrop_blur_max(self) -> Self {
2422        self.background(Brush::Blur(BlurStyle::max()))
2423    }
2424
2425    // -------------------------------------------------------------------------
2426    // Theme-based background colors
2427    // -------------------------------------------------------------------------
2428
2429    /// Set background to theme primary color
2430    pub fn bg_primary(self) -> Self {
2431        self.bg(ThemeState::get().color(blinc_theme::ColorToken::Primary))
2432    }
2433
2434    /// Set background to theme secondary color
2435    pub fn bg_secondary(self) -> Self {
2436        self.bg(ThemeState::get().color(blinc_theme::ColorToken::Secondary))
2437    }
2438
2439    /// Set background to theme background color
2440    pub fn bg_background(self) -> Self {
2441        self.bg(ThemeState::get().color(blinc_theme::ColorToken::Background))
2442    }
2443
2444    /// Set background to theme surface color
2445    pub fn bg_surface(self) -> Self {
2446        self.bg(ThemeState::get().color(blinc_theme::ColorToken::Surface))
2447    }
2448
2449    /// Set background to theme elevated surface color
2450    pub fn bg_surface_elevated(self) -> Self {
2451        self.bg(ThemeState::get().color(blinc_theme::ColorToken::SurfaceElevated))
2452    }
2453
2454    /// Set background to theme success color
2455    pub fn bg_success(self) -> Self {
2456        self.bg(ThemeState::get().color(blinc_theme::ColorToken::SuccessBg))
2457    }
2458
2459    /// Set background to theme warning color
2460    pub fn bg_warning(self) -> Self {
2461        self.bg(ThemeState::get().color(blinc_theme::ColorToken::WarningBg))
2462    }
2463
2464    /// Set background to theme error color
2465    pub fn bg_error(self) -> Self {
2466        self.bg(ThemeState::get().color(blinc_theme::ColorToken::ErrorBg))
2467    }
2468
2469    /// Set background to theme info color
2470    pub fn bg_info(self) -> Self {
2471        self.bg(ThemeState::get().color(blinc_theme::ColorToken::InfoBg))
2472    }
2473
2474    /// Set background to theme accent color
2475    pub fn bg_accent(self) -> Self {
2476        self.bg(ThemeState::get().color(blinc_theme::ColorToken::Accent))
2477    }
2478
2479    // -------------------------------------------------------------------------
2480    // Corner Radius
2481    // -------------------------------------------------------------------------
2482
2483    /// Set corner radius (all corners)
2484    pub fn rounded(mut self, radius: f32) -> Self {
2485        self.border_radius = CornerRadius::uniform(radius);
2486        self
2487    }
2488
2489    /// Set corner radius with full pill shape (radius = min(w,h)/2)
2490    pub fn rounded_full(mut self) -> Self {
2491        // Use a large value; actual pill shape depends on element size
2492        self.border_radius = CornerRadius::uniform(9999.0);
2493        self
2494    }
2495
2496    /// Set individual corner radii
2497    pub fn rounded_corners(mut self, tl: f32, tr: f32, br: f32, bl: f32) -> Self {
2498        self.border_radius = CornerRadius::new(tl, tr, br, bl);
2499        self
2500    }
2501
2502    // -------------------------------------------------------------------------
2503    // Theme-based corner radii
2504    // -------------------------------------------------------------------------
2505
2506    /// Set corner radius to theme's small radius
2507    pub fn rounded_sm(self) -> Self {
2508        self.rounded(ThemeState::get().radii().radius_sm)
2509    }
2510
2511    /// Set corner radius to theme's default radius
2512    pub fn rounded_default(self) -> Self {
2513        self.rounded(ThemeState::get().radii().radius_default)
2514    }
2515
2516    /// Set corner radius to theme's medium radius
2517    pub fn rounded_md(self) -> Self {
2518        self.rounded(ThemeState::get().radii().radius_md)
2519    }
2520
2521    /// Set corner radius to theme's large radius
2522    pub fn rounded_lg(self) -> Self {
2523        self.rounded(ThemeState::get().radii().radius_lg)
2524    }
2525
2526    /// Set corner radius to theme's extra large radius
2527    pub fn rounded_xl(self) -> Self {
2528        self.rounded(ThemeState::get().radii().radius_xl)
2529    }
2530
2531    /// Set corner radius to theme's 2xl radius
2532    pub fn rounded_2xl(self) -> Self {
2533        self.rounded(ThemeState::get().radii().radius_2xl)
2534    }
2535
2536    /// Set corner radius to theme's 3xl radius
2537    pub fn rounded_3xl(self) -> Self {
2538        self.rounded(ThemeState::get().radii().radius_3xl)
2539    }
2540
2541    /// Set corner radius to none (0)
2542    pub fn rounded_none(self) -> Self {
2543        self.rounded(0.0)
2544    }
2545
2546    // =========================================================================
2547    // Corner Shape (superellipse)
2548    // =========================================================================
2549
2550    /// Set uniform corner shape (superellipse exponent)
2551    ///
2552    /// Values: 0.0 = bevel, 1.0 = round (default), 2.0 = squircle, -1.0 = scoop
2553    pub fn corner_shape(mut self, n: f32) -> Self {
2554        self.corner_shape = CornerShape::uniform(n);
2555        self
2556    }
2557
2558    /// Set per-corner shape values (top-left, top-right, bottom-right, bottom-left)
2559    pub fn corner_shapes(mut self, tl: f32, tr: f32, br: f32, bl: f32) -> Self {
2560        self.corner_shape = CornerShape::new(tl, tr, br, bl);
2561        self
2562    }
2563
2564    /// Set corners to bevel (diamond/L1 norm)
2565    pub fn corner_bevel(self) -> Self {
2566        self.corner_shape(0.0)
2567    }
2568
2569    /// Set corners to squircle (L4 superellipse)
2570    pub fn corner_squircle(self) -> Self {
2571        self.corner_shape(2.0)
2572    }
2573
2574    /// Set corners to scoop (concave)
2575    pub fn corner_scoop(self) -> Self {
2576        self.corner_shape(-1.0)
2577    }
2578
2579    // =========================================================================
2580    // Overflow Fade
2581    // =========================================================================
2582
2583    /// Set uniform overflow fade distance on all edges (in CSS pixels)
2584    pub fn overflow_fade(mut self, distance: f32) -> Self {
2585        self.overflow_fade = OverflowFade::uniform(distance);
2586        self
2587    }
2588
2589    /// Set horizontal overflow fade (left and right edges)
2590    pub fn overflow_fade_x(mut self, distance: f32) -> Self {
2591        self.overflow_fade = OverflowFade::horizontal(distance);
2592        self
2593    }
2594
2595    /// Set vertical overflow fade (top and bottom edges)
2596    pub fn overflow_fade_y(mut self, distance: f32) -> Self {
2597        self.overflow_fade = OverflowFade::vertical(distance);
2598        self
2599    }
2600
2601    /// Set per-edge overflow fade distances (top, right, bottom, left)
2602    pub fn overflow_fade_edges(mut self, top: f32, right: f32, bottom: f32, left: f32) -> Self {
2603        self.overflow_fade = OverflowFade::new(top, right, bottom, left);
2604        self
2605    }
2606
2607    // =========================================================================
2608    // Border
2609    // =========================================================================
2610
2611    /// Set border with color and width
2612    pub fn border(mut self, width: f32, color: Color) -> Self {
2613        self.border_width = width;
2614        self.border_color = Some(color);
2615        self
2616    }
2617
2618    /// Set border color only
2619    pub fn border_color(mut self, color: Color) -> Self {
2620        self.border_color = Some(color);
2621        self
2622    }
2623
2624    /// Set border width only
2625    pub fn border_width(mut self, width: f32) -> Self {
2626        self.border_width = width;
2627        self
2628    }
2629
2630    /// Set outline width in pixels
2631    pub fn outline_width(mut self, width: f32) -> Self {
2632        self.outline_width = width;
2633        self
2634    }
2635
2636    /// Set outline color
2637    pub fn outline_color(mut self, color: Color) -> Self {
2638        self.outline_color = Some(color);
2639        self
2640    }
2641
2642    /// Set outline offset in pixels (gap between border and outline)
2643    pub fn outline_offset(mut self, offset: f32) -> Self {
2644        self.outline_offset = offset;
2645        self
2646    }
2647
2648    /// Set left border only (useful for blockquotes)
2649    ///
2650    /// If a uniform border was previously set, other sides will inherit from it.
2651    ///
2652    /// # Example
2653    /// ```ignore
2654    /// div().border_left(4.0, Color::BLUE).pl(16.0)
2655    /// ```
2656    pub fn border_left(mut self, width: f32, color: Color) -> Self {
2657        self.inherit_uniform_border_to_sides();
2658        self.border_sides.left = Some(crate::element::BorderSide::new(width, color));
2659        self
2660    }
2661
2662    /// Set right border only
2663    ///
2664    /// If a uniform border was previously set, other sides will inherit from it.
2665    pub fn border_right(mut self, width: f32, color: Color) -> Self {
2666        self.inherit_uniform_border_to_sides();
2667        self.border_sides.right = Some(crate::element::BorderSide::new(width, color));
2668        self
2669    }
2670
2671    /// Set top border only
2672    ///
2673    /// If a uniform border was previously set, other sides will inherit from it.
2674    pub fn border_top(mut self, width: f32, color: Color) -> Self {
2675        self.inherit_uniform_border_to_sides();
2676        self.border_sides.top = Some(crate::element::BorderSide::new(width, color));
2677        self
2678    }
2679
2680    /// Set bottom border only
2681    ///
2682    /// If a uniform border was previously set, other sides will inherit from it.
2683    pub fn border_bottom(mut self, width: f32, color: Color) -> Self {
2684        self.inherit_uniform_border_to_sides();
2685        self.border_sides.bottom = Some(crate::element::BorderSide::new(width, color));
2686        self
2687    }
2688
2689    /// Set horizontal borders (left and right)
2690    ///
2691    /// If a uniform border was previously set, vertical sides will inherit from it.
2692    pub fn border_x(mut self, width: f32, color: Color) -> Self {
2693        self.inherit_uniform_border_to_sides();
2694        let side = crate::element::BorderSide::new(width, color);
2695        self.border_sides.left = Some(side);
2696        self.border_sides.right = Some(side);
2697        self
2698    }
2699
2700    /// Set vertical borders (top and bottom)
2701    ///
2702    /// If a uniform border was previously set, horizontal sides will inherit from it.
2703    pub fn border_y(mut self, width: f32, color: Color) -> Self {
2704        self.inherit_uniform_border_to_sides();
2705        let side = crate::element::BorderSide::new(width, color);
2706        self.border_sides.top = Some(side);
2707        self.border_sides.bottom = Some(side);
2708        self
2709    }
2710
2711    /// Helper to inherit uniform border to per-side borders before modifying a specific side
2712    ///
2713    /// This ensures that when you do `.border(1.0, color).border_bottom(3.0, color)`,
2714    /// the other three sides get the uniform border instead of being empty.
2715    fn inherit_uniform_border_to_sides(&mut self) {
2716        // Only inherit if we have a uniform border and no per-side borders yet
2717        if self.border_width > 0.0 && !self.border_sides.has_any() {
2718            if let Some(color) = self.border_color {
2719                let side = crate::element::BorderSide::new(self.border_width, color);
2720                self.border_sides.top = Some(side);
2721                self.border_sides.right = Some(side);
2722                self.border_sides.bottom = Some(side);
2723                self.border_sides.left = Some(side);
2724            }
2725        }
2726    }
2727
2728    /// Configure borders using a builder pattern
2729    ///
2730    /// This provides a fluent API for setting multiple border sides at once
2731    /// without accidentally overriding previously set sides.
2732    ///
2733    /// # Example
2734    ///
2735    /// ```ignore
2736    /// // Set left and bottom borders
2737    /// div().borders(|b| b.left(4.0, Color::BLUE).bottom(1.0, Color::gray(0.3)))
2738    ///
2739    /// // Set all borders, then override one
2740    /// div().borders(|b| b.all(1.0, Color::gray(0.2)).left(4.0, Color::BLUE))
2741    ///
2742    /// // Set horizontal borders only
2743    /// div().borders(|b| b.x(1.0, Color::RED))
2744    /// ```
2745    pub fn borders<F>(mut self, f: F) -> Self
2746    where
2747        F: FnOnce(crate::element::BorderBuilder) -> crate::element::BorderBuilder,
2748    {
2749        let builder = f(crate::element::BorderBuilder::new());
2750        self.border_sides = builder.build();
2751        self
2752    }
2753
2754    // =========================================================================
2755    // Layer (for rendering order)
2756    // =========================================================================
2757
2758    /// Set the render layer
2759    pub fn layer(mut self, layer: RenderLayer) -> Self {
2760        self.render_layer = layer;
2761        self
2762    }
2763
2764    /// Render in foreground (on top of glass)
2765    pub fn foreground(self) -> Self {
2766        self.layer(RenderLayer::Foreground)
2767    }
2768
2769    // =========================================================================
2770    // Material System
2771    // =========================================================================
2772
2773    /// Apply a material to this element
2774    pub fn material(mut self, material: Material) -> Self {
2775        // Glass materials also set the render layer to Glass
2776        if matches!(material, Material::Glass(_)) {
2777            self.render_layer = RenderLayer::Glass;
2778        }
2779        self.material = Some(material);
2780        self
2781    }
2782
2783    /// Apply a visual effect to this element
2784    ///
2785    /// Effects include glass (blur), metallic (reflection), wood (texture), etc.
2786    /// This is the general-purpose method for applying any material effect.
2787    ///
2788    /// Example:
2789    /// ```ignore
2790    /// // Glass effect
2791    /// div().effect(GlassMaterial::thick().tint_rgba(1.0, 0.9, 0.9, 0.5))
2792    ///
2793    /// // Metallic effect
2794    /// div().effect(MetallicMaterial::chrome())
2795    /// ```
2796    pub fn effect(self, effect: impl Into<Material>) -> Self {
2797        self.material(effect.into())
2798    }
2799
2800    /// Apply glass material with default settings (shorthand for common case)
2801    ///
2802    /// Creates a frosted glass effect that blurs content behind the element.
2803    pub fn glass(self) -> Self {
2804        self.material(Material::Glass(GlassMaterial::new()))
2805    }
2806
2807    /// Apply metallic material with default settings
2808    pub fn metallic(self) -> Self {
2809        self.material(Material::Metallic(MetallicMaterial::new()))
2810    }
2811
2812    /// Apply chrome metallic preset
2813    pub fn chrome(self) -> Self {
2814        self.material(Material::Metallic(MetallicMaterial::chrome()))
2815    }
2816
2817    /// Apply gold metallic preset
2818    pub fn gold(self) -> Self {
2819        self.material(Material::Metallic(MetallicMaterial::gold()))
2820    }
2821
2822    /// Apply wood material with default settings
2823    pub fn wood(self) -> Self {
2824        self.material(Material::Wood(WoodMaterial::new()))
2825    }
2826
2827    // =========================================================================
2828    // Shadow
2829    // =========================================================================
2830
2831    /// Apply a drop shadow to this element
2832    pub fn shadow(mut self, shadow: Shadow) -> Self {
2833        self.shadow = Some(shadow);
2834        self
2835    }
2836
2837    /// Apply a drop shadow with the given parameters
2838    pub fn shadow_params(self, offset_x: f32, offset_y: f32, blur: f32, color: Color) -> Self {
2839        self.shadow(Shadow::new(offset_x, offset_y, blur, color))
2840    }
2841
2842    /// Apply a small drop shadow using theme colors
2843    pub fn shadow_sm(self) -> Self {
2844        self.shadow(ThemeState::get().shadows().shadow_sm.into())
2845    }
2846
2847    /// Apply a medium drop shadow using theme colors
2848    pub fn shadow_md(self) -> Self {
2849        self.shadow(ThemeState::get().shadows().shadow_md.into())
2850    }
2851
2852    /// Apply a large drop shadow using theme colors
2853    pub fn shadow_lg(self) -> Self {
2854        self.shadow(ThemeState::get().shadows().shadow_lg.into())
2855    }
2856
2857    /// Apply an extra large drop shadow using theme colors
2858    pub fn shadow_xl(self) -> Self {
2859        self.shadow(ThemeState::get().shadows().shadow_xl.into())
2860    }
2861
2862    // =========================================================================
2863    // Transform
2864    // =========================================================================
2865
2866    /// Apply a transform to this element
2867    pub fn transform(mut self, transform: Transform) -> Self {
2868        self.transform = Some(transform);
2869        self
2870    }
2871
2872    /// Translate this element by the given x and y offset
2873    pub fn translate(self, x: f32, y: f32) -> Self {
2874        self.transform(Transform::translate(x, y))
2875    }
2876
2877    /// Scale this element uniformly
2878    pub fn scale(self, factor: f32) -> Self {
2879        self.transform(Transform::scale(factor, factor))
2880    }
2881
2882    /// Scale this element with different x and y factors
2883    pub fn scale_xy(self, sx: f32, sy: f32) -> Self {
2884        self.transform(Transform::scale(sx, sy))
2885    }
2886
2887    /// Rotate this element by the given angle in radians
2888    pub fn rotate(self, angle: f32) -> Self {
2889        self.transform(Transform::rotate(angle))
2890    }
2891
2892    /// Rotate this element by the given angle in degrees
2893    pub fn rotate_deg(self, degrees: f32) -> Self {
2894        self.rotate(degrees * std::f32::consts::PI / 180.0)
2895    }
2896
2897    // =========================================================================
2898    // Opacity
2899    // =========================================================================
2900
2901    /// Set opacity (0.0 = transparent, 1.0 = opaque)
2902    pub fn opacity(mut self, opacity: f32) -> Self {
2903        self.opacity = opacity.clamp(0.0, 1.0);
2904        self
2905    }
2906
2907    /// Fully opaque (opacity = 1.0)
2908    pub fn opaque(self) -> Self {
2909        self.opacity(1.0)
2910    }
2911
2912    /// Set a @flow shader on this element.
2913    ///
2914    /// Accepts either a `FlowGraph` (from the `flow!` macro) or a `&str` name
2915    /// referencing a flow defined in a CSS stylesheet.
2916    ///
2917    /// ```rust,ignore
2918    /// // Direct flow graph
2919    /// div().flow(flow!(ripple, fragment, { ... }))
2920    ///
2921    /// // Name reference to CSS-defined flow
2922    /// div().flow("ripple")
2923    /// ```
2924    pub fn flow(mut self, f: impl Into<crate::element::FlowRef>) -> Self {
2925        match f.into() {
2926            crate::element::FlowRef::Name(name) => {
2927                self.flow_name = Some(name);
2928            }
2929            crate::element::FlowRef::Graph(g) => {
2930                self.flow_name = Some(g.name.clone());
2931                self.flow_graph = Some(g);
2932            }
2933        }
2934        self
2935    }
2936
2937    /// Semi-transparent (opacity = 0.5)
2938    pub fn translucent(self) -> Self {
2939        self.opacity(0.5)
2940    }
2941
2942    /// Invisible (opacity = 0.0)
2943    pub fn invisible(self) -> Self {
2944        self.opacity(0.0)
2945    }
2946
2947    // =========================================================================
2948    // Layer Effects (Post-Processing)
2949    // =========================================================================
2950
2951    /// Add a layer effect to this element
2952    ///
2953    /// Layer effects are applied during layer composition and include:
2954    /// - Blur (Gaussian blur)
2955    /// - DropShadow (offset shadow behind element)
2956    /// - Glow (outer glow)
2957    /// - ColorMatrix (color transformation)
2958    ///
2959    /// # Example
2960    ///
2961    /// ```ignore
2962    /// div()
2963    ///     .w(100.0).h(100.0)
2964    ///     .bg(Color::BLUE)
2965    ///     .layer_effect(LayerEffect::blur(8.0))
2966    /// ```
2967    pub fn layer_effect(mut self, effect: LayerEffect) -> Self {
2968        self.layer_effects.push(effect);
2969        self
2970    }
2971
2972    /// Add a blur effect
2973    ///
2974    /// # Example
2975    ///
2976    /// ```ignore
2977    /// div()
2978    ///     .w(100.0).h(100.0)
2979    ///     .bg(Color::BLUE)
2980    ///     .blur(8.0) // 8px blur radius
2981    /// ```
2982    pub fn blur(self, radius: f32) -> Self {
2983        self.layer_effect(LayerEffect::blur(radius))
2984    }
2985
2986    /// Add a blur effect with specific quality
2987    ///
2988    /// Quality options:
2989    /// - `BlurQuality::Low` - Single-pass box blur (fastest)
2990    /// - `BlurQuality::Medium` - Two-pass separable Gaussian (balanced)
2991    /// - `BlurQuality::High` - Multi-pass Kawase blur (highest quality)
2992    pub fn blur_with_quality(self, radius: f32, quality: BlurQuality) -> Self {
2993        self.layer_effect(LayerEffect::blur_with_quality(radius, quality))
2994    }
2995
2996    /// Add a drop shadow effect
2997    ///
2998    /// This is a layer-level shadow effect (post-processing), different from
2999    /// the element shadow set via `.shadow()` which is rendered inline.
3000    ///
3001    /// # Example
3002    ///
3003    /// ```ignore
3004    /// div()
3005    ///     .w(100.0).h(100.0)
3006    ///     .bg(Color::WHITE)
3007    ///     .drop_shadow_effect(4.0, 4.0, 8.0, Color::rgba(0, 0, 0, 128))
3008    /// ```
3009    pub fn drop_shadow_effect(self, offset_x: f32, offset_y: f32, blur: f32, color: Color) -> Self {
3010        self.layer_effect(LayerEffect::drop_shadow(offset_x, offset_y, blur, color))
3011    }
3012
3013    /// Add a glow effect
3014    ///
3015    /// Creates an outer glow around the element.
3016    ///
3017    /// # Example
3018    ///
3019    /// ```ignore
3020    /// div()
3021    ///     .w(100.0).h(100.0)
3022    ///     .bg(Color::BLUE)
3023    ///     .glow_effect(Color::CYAN, 8.0, 4.0, 0.8)
3024    /// ```
3025    ///
3026    /// ## Parameters
3027    /// - `color`: Glow color
3028    /// - `blur`: Blur softness (higher = softer edges), typically 4-24
3029    /// - `range`: How far the glow extends from the element, typically 0-20
3030    /// - `opacity`: Glow visibility (0.0 to 1.0)
3031    pub fn glow_effect(self, color: Color, blur: f32, range: f32, opacity: f32) -> Self {
3032        self.layer_effect(LayerEffect::glow(color, blur, range, opacity))
3033    }
3034
3035    /// Apply grayscale filter
3036    pub fn grayscale(self) -> Self {
3037        self.layer_effect(LayerEffect::grayscale())
3038    }
3039
3040    /// Apply sepia filter
3041    pub fn sepia(self) -> Self {
3042        self.layer_effect(LayerEffect::sepia())
3043    }
3044
3045    /// Adjust brightness (1.0 = normal, <1.0 = darker, >1.0 = brighter)
3046    pub fn brightness(self, factor: f32) -> Self {
3047        self.layer_effect(LayerEffect::brightness(factor))
3048    }
3049
3050    /// Adjust contrast (1.0 = normal, <1.0 = less contrast, >1.0 = more contrast)
3051    pub fn contrast(self, factor: f32) -> Self {
3052        self.layer_effect(LayerEffect::contrast(factor))
3053    }
3054
3055    /// Adjust saturation (0.0 = grayscale, 1.0 = normal, >1.0 = oversaturated)
3056    pub fn saturation(self, factor: f32) -> Self {
3057        self.layer_effect(LayerEffect::saturation(factor))
3058    }
3059
3060    // =========================================================================
3061    // Cursor Style
3062    // =========================================================================
3063
3064    /// Set the cursor style when hovering over this element
3065    ///
3066    /// # Example
3067    ///
3068    /// ```ignore
3069    /// div()
3070    ///     .w(100.0).h(50.0)
3071    ///     .bg(Color::BLUE)
3072    ///     .cursor(CursorStyle::Pointer) // Hand cursor for clickable elements
3073    ///     .on_click(|_| println!("Clicked!"))
3074    /// ```
3075    pub fn cursor(mut self, cursor: crate::element::CursorStyle) -> Self {
3076        self.cursor = Some(cursor);
3077        self
3078    }
3079
3080    /// Set cursor to pointer (hand) - convenience for clickable elements
3081    pub fn cursor_pointer(self) -> Self {
3082        self.cursor(crate::element::CursorStyle::Pointer)
3083    }
3084
3085    /// Set cursor to text (I-beam) - for text input areas
3086    pub fn cursor_text(self) -> Self {
3087        self.cursor(crate::element::CursorStyle::Text)
3088    }
3089
3090    /// Set cursor to move - for draggable elements
3091    pub fn cursor_move(self) -> Self {
3092        self.cursor(crate::element::CursorStyle::Move)
3093    }
3094
3095    /// Set cursor to grab (open hand) - for grabbable elements
3096    pub fn cursor_grab(self) -> Self {
3097        self.cursor(crate::element::CursorStyle::Grab)
3098    }
3099
3100    /// Set cursor to grabbing (closed hand) - while dragging
3101    pub fn cursor_grabbing(self) -> Self {
3102        self.cursor(crate::element::CursorStyle::Grabbing)
3103    }
3104
3105    /// Set cursor to not-allowed - for disabled elements
3106    pub fn cursor_not_allowed(self) -> Self {
3107        self.cursor(crate::element::CursorStyle::NotAllowed)
3108    }
3109
3110    /// Make this element transparent to hit-testing (pointer-events: none)
3111    ///
3112    /// When set, this element will not capture mouse clicks or hover events.
3113    /// Only its children can receive events. Useful for wrapper elements that
3114    /// should not block interaction with elements behind them.
3115    pub fn pointer_events_none(mut self) -> Self {
3116        self.pointer_events_none = true;
3117        self
3118    }
3119
3120    /// Mark this element as a stack layer for z-ordering
3121    ///
3122    /// When set, entering this element increments the z_layer counter,
3123    /// ensuring its content renders above previous siblings in the
3124    /// interleaved primitive/text rendering. This is used internally
3125    /// by Stack and can be used for overlay containers that need to
3126    /// render above other content.
3127    pub fn stack_layer(mut self) -> Self {
3128        self.is_stack_layer = true;
3129        self
3130    }
3131
3132    /// Set CSS z-index for stacking order (higher values render on top)
3133    pub fn z_index(mut self, z: i32) -> Self {
3134        self.z_index = z;
3135        self
3136    }
3137
3138    // =========================================================================
3139    // Children
3140    // =========================================================================
3141
3142    /// Add a child element
3143    pub fn child(mut self, child: impl ElementBuilder + 'static) -> Self {
3144        self.children.push(Box::new(child));
3145        self
3146    }
3147
3148    /// Add a boxed child element (for dynamic element types)
3149    pub fn child_box(mut self, child: Box<dyn ElementBuilder>) -> Self {
3150        self.children.push(child);
3151        self
3152    }
3153
3154    /// Add multiple children
3155    pub fn children<I>(mut self, children: I) -> Self
3156    where
3157        I: IntoIterator,
3158        I::Item: ElementBuilder + 'static,
3159    {
3160        for child in children {
3161            self.children.push(Box::new(child));
3162        }
3163        self
3164    }
3165
3166    /// Get direct access to the taffy style for advanced configuration
3167    pub fn style_mut(&mut self) -> &mut Style {
3168        &mut self.style
3169    }
3170
3171    // =========================================================================
3172    // Event Handlers
3173    // =========================================================================
3174
3175    /// Get a reference to the event handlers
3176    pub fn event_handlers(&self) -> &crate::event_handler::EventHandlers {
3177        &self.event_handlers
3178    }
3179
3180    /// Get a mutable reference to the event handlers
3181    pub fn event_handlers_mut(&mut self) -> &mut crate::event_handler::EventHandlers {
3182        &mut self.event_handlers
3183    }
3184
3185    /// Register a click handler (fired on POINTER_UP)
3186    ///
3187    /// # Example
3188    ///
3189    /// ```ignore
3190    /// div()
3191    ///     .w(100.0).h(50.0)
3192    ///     .bg(Color::BLUE)
3193    ///     .on_click(|ctx| {
3194    ///         println!("Clicked at ({}, {})", ctx.local_x, ctx.local_y);
3195    ///     })
3196    /// ```
3197    pub fn on_click<F>(mut self, handler: F) -> Self
3198    where
3199        F: Fn(&crate::event_handler::EventContext) + 'static,
3200    {
3201        self.event_handlers.on_click(handler);
3202        self
3203    }
3204
3205    /// Register a mouse down handler
3206    pub fn on_mouse_down<F>(mut self, handler: F) -> Self
3207    where
3208        F: Fn(&crate::event_handler::EventContext) + 'static,
3209    {
3210        self.event_handlers.on_mouse_down(handler);
3211        self
3212    }
3213
3214    /// Register a mouse up handler
3215    pub fn on_mouse_up<F>(mut self, handler: F) -> Self
3216    where
3217        F: Fn(&crate::event_handler::EventContext) + 'static,
3218    {
3219        self.event_handlers.on_mouse_up(handler);
3220        self
3221    }
3222
3223    /// Register a hover enter handler
3224    ///
3225    /// # Example
3226    ///
3227    /// ```ignore
3228    /// div()
3229    ///     .on_hover_enter(|_| println!("Mouse entered!"))
3230    ///     .on_hover_leave(|_| println!("Mouse left!"))
3231    /// ```
3232    pub fn on_hover_enter<F>(mut self, handler: F) -> Self
3233    where
3234        F: Fn(&crate::event_handler::EventContext) + 'static,
3235    {
3236        self.event_handlers.on_hover_enter(handler);
3237        self
3238    }
3239
3240    /// Register a hover leave handler
3241    pub fn on_hover_leave<F>(mut self, handler: F) -> Self
3242    where
3243        F: Fn(&crate::event_handler::EventContext) + 'static,
3244    {
3245        self.event_handlers.on_hover_leave(handler);
3246        self
3247    }
3248
3249    /// Register a focus handler
3250    pub fn on_focus<F>(mut self, handler: F) -> Self
3251    where
3252        F: Fn(&crate::event_handler::EventContext) + 'static,
3253    {
3254        self.event_handlers.on_focus(handler);
3255        self
3256    }
3257
3258    /// Register a blur handler
3259    pub fn on_blur<F>(mut self, handler: F) -> Self
3260    where
3261        F: Fn(&crate::event_handler::EventContext) + 'static,
3262    {
3263        self.event_handlers.on_blur(handler);
3264        self
3265    }
3266
3267    /// Register a mount handler (element added to tree)
3268    pub fn on_mount<F>(mut self, handler: F) -> Self
3269    where
3270        F: Fn(&crate::event_handler::EventContext) + 'static,
3271    {
3272        self.event_handlers.on_mount(handler);
3273        self
3274    }
3275
3276    /// Register an unmount handler (element removed from tree)
3277    pub fn on_unmount<F>(mut self, handler: F) -> Self
3278    where
3279        F: Fn(&crate::event_handler::EventContext) + 'static,
3280    {
3281        self.event_handlers.on_unmount(handler);
3282        self
3283    }
3284
3285    /// Register a key down handler (requires focus)
3286    pub fn on_key_down<F>(mut self, handler: F) -> Self
3287    where
3288        F: Fn(&crate::event_handler::EventContext) + 'static,
3289    {
3290        self.event_handlers.on_key_down(handler);
3291        self
3292    }
3293
3294    /// Register a key up handler (requires focus)
3295    pub fn on_key_up<F>(mut self, handler: F) -> Self
3296    where
3297        F: Fn(&crate::event_handler::EventContext) + 'static,
3298    {
3299        self.event_handlers.on_key_up(handler);
3300        self
3301    }
3302
3303    /// Register a scroll handler
3304    pub fn on_scroll<F>(mut self, handler: F) -> Self
3305    where
3306        F: Fn(&crate::event_handler::EventContext) + 'static,
3307    {
3308        self.event_handlers.on_scroll(handler);
3309        self
3310    }
3311
3312    /// Register a mouse move handler (pointer movement over this element)
3313    pub fn on_mouse_move<F>(mut self, handler: F) -> Self
3314    where
3315        F: Fn(&crate::event_handler::EventContext) + 'static,
3316    {
3317        self.event_handlers
3318            .on(blinc_core::events::event_types::POINTER_MOVE, handler);
3319        self
3320    }
3321
3322    /// Register a drag handler (pointer movement while button is pressed)
3323    pub fn on_drag<F>(mut self, handler: F) -> Self
3324    where
3325        F: Fn(&crate::event_handler::EventContext) + 'static,
3326    {
3327        self.event_handlers
3328            .on(blinc_core::events::event_types::DRAG, handler);
3329        self
3330    }
3331
3332    /// Register a drag end handler (pointer movement while button is pressed)
3333    pub fn on_drag_end<F>(mut self, handler: F) -> Self
3334    where
3335        F: Fn(&crate::event_handler::EventContext) + 'static,
3336    {
3337        self.event_handlers
3338            .on(blinc_core::events::event_types::DRAG_END, handler);
3339        self
3340    }
3341
3342    /// Register a text input handler (receives character input when focused)
3343    pub fn on_text_input<F>(mut self, handler: F) -> Self
3344    where
3345        F: Fn(&crate::event_handler::EventContext) + 'static,
3346    {
3347        self.event_handlers.on_text_input(handler);
3348        self
3349    }
3350
3351    /// Register a resize handler
3352    pub fn on_resize<F>(mut self, handler: F) -> Self
3353    where
3354        F: Fn(&crate::event_handler::EventContext) + 'static,
3355    {
3356        self.event_handlers.on_resize(handler);
3357        self
3358    }
3359
3360    /// Register a handler for a specific event type
3361    ///
3362    /// This is the low-level method for registering handlers for any event type.
3363    ///
3364    /// # Example
3365    ///
3366    /// ```ignore
3367    /// use blinc_core::events::event_types;
3368    ///
3369    /// div().on_event(event_types::POINTER_MOVE, |ctx| {
3370    ///     println!("Mouse moved to ({}, {})", ctx.mouse_x, ctx.mouse_y);
3371    /// })
3372    /// ```
3373    pub fn on_event<F>(mut self, event_type: blinc_core::events::EventType, handler: F) -> Self
3374    where
3375        F: Fn(&crate::event_handler::EventContext) + 'static,
3376    {
3377        self.event_handlers.on(event_type, handler);
3378        self
3379    }
3380}
3381
3382/// Element type identifier for downcasting
3383#[derive(Clone, Copy, Debug, PartialEq, Eq)]
3384pub enum ElementTypeId {
3385    Div,
3386    Text,
3387    /// Styled text with inline formatting spans
3388    StyledText,
3389    Svg,
3390    Image,
3391    Canvas,
3392    /// Motion container (transparent wrapper for animations)
3393    Motion,
3394}
3395
3396/// Text alignment options
3397#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
3398pub enum TextAlign {
3399    /// Align text to the left (default)
3400    #[default]
3401    Left,
3402    /// Center text
3403    Center,
3404    /// Align text to the right
3405    Right,
3406}
3407
3408/// Font weight options
3409#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
3410pub enum FontWeight {
3411    /// Thin (100)
3412    Thin,
3413    /// Extra Light (200)
3414    ExtraLight,
3415    /// Light (300)
3416    Light,
3417    /// Normal/Regular (400)
3418    #[default]
3419    Normal,
3420    /// Medium (500)
3421    Medium,
3422    /// Semi Bold (600)
3423    SemiBold,
3424    /// Bold (700)
3425    Bold,
3426    /// Extra Bold (800)
3427    ExtraBold,
3428    /// Black (900)
3429    Black,
3430}
3431
3432impl FontWeight {
3433    /// Get the numeric weight value (100-900)
3434    pub fn weight(&self) -> u16 {
3435        match self {
3436            FontWeight::Thin => 100,
3437            FontWeight::ExtraLight => 200,
3438            FontWeight::Light => 300,
3439            FontWeight::Normal => 400,
3440            FontWeight::Medium => 500,
3441            FontWeight::SemiBold => 600,
3442            FontWeight::Bold => 700,
3443            FontWeight::ExtraBold => 800,
3444            FontWeight::Black => 900,
3445        }
3446    }
3447}
3448
3449/// Vertical alignment for text within its bounding box
3450#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
3451pub enum TextVerticalAlign {
3452    /// Text is positioned at the top of its bounding box (default for multi-line text).
3453    /// Glyphs are centered within the line height for proper vertical centering.
3454    #[default]
3455    Top,
3456    /// Text is optically centered within its bounding box (for single-line centered text).
3457    /// Uses cap-height based centering for better visual appearance.
3458    Center,
3459    /// Text is positioned by its baseline. Use this for inline text that should
3460    /// align by baseline with other text elements (e.g., mixing fonts in a paragraph).
3461    /// The baseline position is at a standardized offset from the top of the bounding box.
3462    Baseline,
3463}
3464
3465/// Generic font category for fallback when a named font isn't available
3466#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
3467pub enum GenericFont {
3468    /// Default system UI font
3469    #[default]
3470    System,
3471    /// Monospace font for code (Menlo, Consolas, Monaco, etc.)
3472    Monospace,
3473    /// Serif font (Times, Georgia, etc.)
3474    Serif,
3475    /// Sans-serif font (Helvetica, Arial, etc.)
3476    SansSerif,
3477}
3478
3479/// Font family specification
3480///
3481/// Allows specifying either a named font (e.g., "Fira Code", "Inter") or
3482/// a generic category. When a named font is specified, the generic category
3483/// serves as a fallback if the font isn't available.
3484///
3485/// # Example
3486///
3487/// ```ignore
3488/// // Use a specific font with monospace fallback
3489/// text("code").font("Fira Code")
3490///
3491/// // Use system monospace
3492/// text("code").monospace()
3493/// ```
3494#[derive(Debug, Clone, PartialEq, Eq, Default)]
3495pub struct FontFamily {
3496    /// Specific font name (e.g., "Fira Code", "Inter", "SF Pro")
3497    pub name: Option<String>,
3498    /// Generic fallback category
3499    pub generic: GenericFont,
3500}
3501
3502impl FontFamily {
3503    /// Create a font family with just a generic category
3504    pub fn generic(generic: GenericFont) -> Self {
3505        Self {
3506            name: None,
3507            generic,
3508        }
3509    }
3510
3511    /// Create a font family with a specific font name
3512    pub fn named(name: impl Into<String>) -> Self {
3513        Self {
3514            name: Some(name.into()),
3515            generic: GenericFont::System,
3516        }
3517    }
3518
3519    /// Create a font family with a specific name and fallback category
3520    pub fn named_with_fallback(name: impl Into<String>, generic: GenericFont) -> Self {
3521        Self {
3522            name: Some(name.into()),
3523            generic,
3524        }
3525    }
3526
3527    /// System UI font
3528    pub fn system() -> Self {
3529        Self::generic(GenericFont::System)
3530    }
3531
3532    /// Monospace font
3533    pub fn monospace() -> Self {
3534        Self::generic(GenericFont::Monospace)
3535    }
3536
3537    /// Serif font
3538    pub fn serif() -> Self {
3539        Self::generic(GenericFont::Serif)
3540    }
3541
3542    /// Sans-serif font
3543    pub fn sans_serif() -> Self {
3544        Self::generic(GenericFont::SansSerif)
3545    }
3546}
3547
3548/// Text render data extracted from element
3549#[derive(Clone)]
3550pub struct TextRenderInfo {
3551    pub content: String,
3552    pub font_size: f32,
3553    pub color: [f32; 4],
3554    pub align: TextAlign,
3555    pub weight: FontWeight,
3556    /// Whether to use italic style
3557    pub italic: bool,
3558    pub v_align: TextVerticalAlign,
3559    /// Whether to wrap text at container bounds (default: true for text())
3560    pub wrap: bool,
3561    /// Line height multiplier (default: 1.2)
3562    pub line_height: f32,
3563    /// Measured width of the text (before any layout constraints)
3564    /// Used to determine if wrapping is actually needed at render time
3565    pub measured_width: f32,
3566    /// Font family category
3567    pub font_family: FontFamily,
3568    /// Word spacing in pixels (0.0 = normal)
3569    pub word_spacing: f32,
3570    /// Letter spacing in pixels (0.0 = normal)
3571    pub letter_spacing: f32,
3572    /// Font ascender in pixels (distance from baseline to top)
3573    /// Used for accurate baseline alignment across different fonts
3574    pub ascender: f32,
3575    /// Whether text has strikethrough decoration
3576    pub strikethrough: bool,
3577    /// Whether text has underline decoration
3578    pub underline: bool,
3579}
3580
3581/// A span within styled text (for rich_text element)
3582#[derive(Clone)]
3583pub struct StyledTextSpanInfo {
3584    /// Start byte index
3585    pub start: usize,
3586    /// End byte index (exclusive)
3587    pub end: usize,
3588    /// RGBA color
3589    pub color: [f32; 4],
3590    /// Whether span is bold
3591    pub bold: bool,
3592    /// Whether span is italic
3593    pub italic: bool,
3594    /// Whether span has underline
3595    pub underline: bool,
3596    /// Whether span has strikethrough
3597    pub strikethrough: bool,
3598    /// Optional link URL
3599    pub link_url: Option<String>,
3600}
3601
3602/// Styled text render data (for rich_text element with inline formatting)
3603#[derive(Clone)]
3604pub struct StyledTextRenderInfo {
3605    /// Plain text content (tags stripped)
3606    pub content: String,
3607    /// Style spans
3608    pub spans: Vec<StyledTextSpanInfo>,
3609    /// Font size
3610    pub font_size: f32,
3611    /// Default color for unspanned regions
3612    pub default_color: [f32; 4],
3613    /// Text alignment
3614    pub align: TextAlign,
3615    /// Vertical alignment
3616    pub v_align: TextVerticalAlign,
3617    /// Font family
3618    pub font_family: FontFamily,
3619    /// Line height multiplier
3620    pub line_height: f32,
3621    /// Default font weight (for unspanned regions or spans without explicit weight)
3622    pub weight: FontWeight,
3623    /// Default italic style (for unspanned regions or spans without explicit italic)
3624    pub italic: bool,
3625    /// Measured ascender from font metrics (for consistent baseline alignment)
3626    pub ascender: f32,
3627}
3628
3629/// SVG render data extracted from element
3630#[derive(Clone)]
3631pub struct SvgRenderInfo {
3632    pub source: String,
3633    pub tint: Option<blinc_core::Color>,
3634    pub fill: Option<blinc_core::Color>,
3635    pub stroke: Option<blinc_core::Color>,
3636    pub stroke_width: Option<f32>,
3637}
3638
3639/// Image render data extracted from element
3640#[derive(Clone)]
3641pub struct ImageRenderInfo {
3642    /// Image source (file path, URL, or base64 data)
3643    pub source: String,
3644    /// Object-fit mode (cover, contain, fill, scale-down, none)
3645    pub object_fit: u8,
3646    /// Object position (x: 0.0-1.0, y: 0.0-1.0)
3647    pub object_position: [f32; 2],
3648    /// Opacity (0.0 - 1.0)
3649    pub opacity: f32,
3650    /// Border radius for rounded corners
3651    pub border_radius: f32,
3652    /// Tint color [r, g, b, a]
3653    pub tint: [f32; 4],
3654    /// Filter: [grayscale, sepia, brightness, contrast, saturate, hue_rotate, invert, blur]
3655    pub filter: [f32; 8],
3656    /// Loading strategy: 0 = Eager (default), 1 = Lazy
3657    pub loading_strategy: u8,
3658    /// Placeholder type: 0 = None, 1 = Color, 2 = Image, 3 = Skeleton
3659    pub placeholder_type: u8,
3660    /// Placeholder color (only used when placeholder_type == 1)
3661    pub placeholder_color: [f32; 4],
3662    /// Placeholder image source (only used when placeholder_type == 2)
3663    pub placeholder_image: Option<String>,
3664    /// Fade-in duration in milliseconds
3665    pub fade_duration_ms: u32,
3666}
3667
3668impl Default for ImageRenderInfo {
3669    fn default() -> Self {
3670        Self {
3671            source: String::new(),
3672            object_fit: 0,               // Cover
3673            object_position: [0.5, 0.5], // Center
3674            opacity: 1.0,
3675            border_radius: 0.0,
3676            tint: [1.0, 1.0, 1.0, 1.0],
3677            filter: [0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0], // identity filter
3678            loading_strategy: 0,                              // Eager
3679            placeholder_type: 1,                              // Color (default)
3680            placeholder_color: [0.15, 0.15, 0.15, 0.5],       // Default gray
3681            placeholder_image: None,
3682            fade_duration_ms: 200,
3683        }
3684    }
3685}
3686
3687/// Trait for types that can build into layout elements
3688///
3689/// Note: No Send/Sync requirement - UI is single-threaded.
3690pub trait ElementBuilder {
3691    /// Build this element into a layout tree, returning the node ID
3692    fn build(&self, tree: &mut LayoutTree) -> LayoutNodeId;
3693
3694    /// Get the render properties for this element
3695    fn render_props(&self) -> RenderProps;
3696
3697    /// Get children builders (for recursive traversal)
3698    fn children_builders(&self) -> &[Box<dyn ElementBuilder>];
3699
3700    /// Get the element type identifier
3701    fn element_type_id(&self) -> ElementTypeId {
3702        ElementTypeId::Div
3703    }
3704
3705    /// Get text render info if this is a text element
3706    fn text_render_info(&self) -> Option<TextRenderInfo> {
3707        None
3708    }
3709
3710    /// Get styled text render info if this is a styled text element (rich_text)
3711    fn styled_text_render_info(&self) -> Option<StyledTextRenderInfo> {
3712        None
3713    }
3714
3715    /// Get SVG render info if this is an SVG element
3716    fn svg_render_info(&self) -> Option<SvgRenderInfo> {
3717        None
3718    }
3719
3720    /// Get image render info if this is an image element
3721    fn image_render_info(&self) -> Option<ImageRenderInfo> {
3722        None
3723    }
3724
3725    /// Get canvas render info if this is a canvas element
3726    fn canvas_render_info(&self) -> Option<crate::canvas::CanvasRenderFn> {
3727        None
3728    }
3729
3730    /// Get event handlers for this element
3731    ///
3732    /// Returns a reference to the element's event handlers for registration
3733    /// with the handler registry during tree building.
3734    fn event_handlers(&self) -> Option<&crate::event_handler::EventHandlers> {
3735        None
3736    }
3737
3738    /// Get scroll render info if this is a scroll element
3739    fn scroll_info(&self) -> Option<crate::scroll::ScrollRenderInfo> {
3740        None
3741    }
3742
3743    /// Get scroll physics handle if this is a scroll element
3744    fn scroll_physics(&self) -> Option<crate::scroll::SharedScrollPhysics> {
3745        None
3746    }
3747
3748    /// Get motion animation config for a child at given index
3749    ///
3750    /// This is only implemented by Motion containers. The index corresponds
3751    /// to the order of children as returned by children_builders().
3752    fn motion_animation_for_child(
3753        &self,
3754        _child_index: usize,
3755    ) -> Option<crate::element::MotionAnimation> {
3756        None
3757    }
3758
3759    /// Get motion bindings for continuous animation
3760    ///
3761    /// If this element has bound animated values (e.g., translate_y, opacity),
3762    /// they are returned here so the RenderTree can sample them each frame.
3763    fn motion_bindings(&self) -> Option<crate::motion::MotionBindings> {
3764        None
3765    }
3766
3767    /// Get stable ID for motion animation tracking
3768    ///
3769    /// When set, the motion animation state is tracked by this stable key
3770    /// instead of node ID, allowing animations to persist across tree rebuilds.
3771    /// This is essential for overlays which are rebuilt every frame.
3772    fn motion_stable_id(&self) -> Option<&str> {
3773        None
3774    }
3775
3776    /// Check if this motion should replay its animation
3777    ///
3778    /// When true, the animation restarts from the beginning even if
3779    /// the motion already exists with the same stable key.
3780    fn motion_should_replay(&self) -> bool {
3781        false
3782    }
3783
3784    /// Check if this motion should start in suspended state
3785    ///
3786    /// When true, the motion starts with opacity 0 and waits for
3787    /// `query_motion(key).start()` to trigger the enter animation.
3788    fn motion_is_suspended(&self) -> bool {
3789        false
3790    }
3791
3792    /// DEPRECATED: Check if this motion should start its exit animation
3793    ///
3794    /// This method is deprecated. Motion exit is now triggered explicitly via
3795    /// `MotionHandle.exit()` / `query_motion(key).exit()` instead of capturing
3796    /// the is_overlay_closing() flag at build time.
3797    ///
3798    /// The old mechanism was flawed because the flag reset after build_content(),
3799    /// breaking multi-frame exit animations.
3800    #[deprecated(
3801        since = "0.1.0",
3802        note = "Use query_motion(key).exit() to explicitly trigger motion exit"
3803    )]
3804    fn motion_is_exiting(&self) -> bool {
3805        false
3806    }
3807
3808    /// Get the layout style for this element
3809    ///
3810    /// This is used for hashing layout-affecting properties like size,
3811    /// padding, margin, flex properties, etc.
3812    fn layout_style(&self) -> Option<&taffy::Style> {
3813        None
3814    }
3815
3816    /// Get layout bounds storage for this element
3817    ///
3818    /// If this element wants to be notified of its computed layout bounds
3819    /// after layout is calculated, it returns a storage that will be updated.
3820    fn layout_bounds_storage(&self) -> Option<crate::renderer::LayoutBoundsStorage> {
3821        None
3822    }
3823
3824    /// Get layout bounds change callback for this element
3825    ///
3826    /// If this element needs to react when its layout bounds change (e.g., TextInput
3827    /// needs to recalculate scroll when width changes), it returns a callback that
3828    /// will be invoked when the bounds differ from the previous layout.
3829    fn layout_bounds_callback(&self) -> Option<crate::renderer::LayoutBoundsCallback> {
3830        None
3831    }
3832
3833    /// Semantic HTML-like tag name for CSS type selectors (e.g., "button", "a", "ul")
3834    ///
3835    /// Widgets override this to declare their semantic type, enabling global CSS
3836    /// rules like `button { background: blue; }` to match all instances.
3837    fn semantic_type_name(&self) -> Option<&'static str> {
3838        None
3839    }
3840
3841    /// Get the element ID for selector API queries
3842    ///
3843    /// Elements with IDs can be looked up programmatically via `ctx.query("id")`.
3844    fn element_id(&self) -> Option<&str> {
3845        None
3846    }
3847
3848    /// Get the element's CSS class list for selector matching
3849    fn element_classes(&self) -> &[String] {
3850        &[]
3851    }
3852
3853    /// Get the bound ScrollRef for programmatic scroll control
3854    ///
3855    /// Only scroll containers return a ScrollRef. This is used by the renderer
3856    /// to bind the ScrollRef to the node and process pending scroll commands.
3857    fn bound_scroll_ref(&self) -> Option<&crate::selector::ScrollRef> {
3858        None
3859    }
3860
3861    /// Get the on_ready callback for this motion container
3862    ///
3863    /// Motion containers can register a callback that fires once after the
3864    /// motion is laid out. Used with suspended animations to start the
3865    /// animation after content is mounted.
3866    fn motion_on_ready_callback(
3867        &self,
3868    ) -> Option<std::sync::Arc<dyn Fn(crate::element::ElementBounds) + Send + Sync>> {
3869        None
3870    }
3871
3872    /// Get layout animation configuration for this element
3873    ///
3874    /// If this element should animate its layout bounds changes (position, size),
3875    /// it returns a configuration specifying which properties to animate
3876    /// and the spring physics settings.
3877    ///
3878    /// Layout animation uses FLIP-style animation: layout settles instantly
3879    /// while the visual rendering interpolates from old bounds to new bounds.
3880    fn layout_animation_config(&self) -> Option<crate::layout_animation::LayoutAnimationConfig> {
3881        None
3882    }
3883
3884    /// Get visual animation config for new FLIP-style animations (read-only layout)
3885    ///
3886    /// Unlike layout_animation_config, this system never modifies taffy.
3887    /// The animation tracks visual offsets that get animated back to zero.
3888    fn visual_animation_config(&self) -> Option<crate::visual_animation::VisualAnimationConfig> {
3889        None
3890    }
3891}
3892
3893impl ElementBuilder for Div {
3894    fn build(&self, tree: &mut LayoutTree) -> LayoutNodeId {
3895        let node = tree.create_node(self.style.clone());
3896
3897        // Build and add children
3898        for child in &self.children {
3899            let child_node = child.build(tree);
3900            tree.add_child(node, child_node);
3901        }
3902
3903        node
3904    }
3905
3906    #[allow(deprecated)]
3907    fn render_props(&self) -> RenderProps {
3908        // Check if overflow is set to clip content (Clip or Scroll)
3909        // Overflow::Visible is the only mode that doesn't clip
3910        let clips_content = !matches!(self.style.overflow.x, Overflow::Visible)
3911            || !matches!(self.style.overflow.y, Overflow::Visible);
3912
3913        RenderProps {
3914            background: self.background.clone(),
3915            border_radius: self.border_radius,
3916            corner_shape: self.corner_shape,
3917            border_color: self.border_color,
3918            border_width: self.border_width,
3919            border_sides: self.border_sides,
3920            layer: self.render_layer,
3921            material: self.material.clone(),
3922            shadow: self.shadow,
3923            transform: self.transform.clone(),
3924            opacity: self.opacity,
3925            clips_content,
3926            is_stack_layer: self.is_stack_layer,
3927            pointer_events_none: self.pointer_events_none,
3928            cursor: self.cursor,
3929            layer_effects: self.layer_effects.clone(),
3930            rotate_x: self.rotate_x,
3931            rotate_y: self.rotate_y,
3932            perspective: self.perspective_3d,
3933            shape_3d: self.shape_3d,
3934            depth: self.depth,
3935            light_direction: self.light_direction,
3936            light_intensity: self.light_intensity,
3937            ambient: self.ambient,
3938            specular: self.specular,
3939            translate_z: self.translate_z,
3940            op_3d: self.op_3d,
3941            blend_3d: self.blend_3d,
3942            clip_path: self.clip_path.clone(),
3943            overflow_fade: self.overflow_fade,
3944            flow: self.flow_name.clone(),
3945            flow_graph: self.flow_graph.clone(),
3946            outline_color: self.outline_color,
3947            outline_width: self.outline_width,
3948            outline_offset: self.outline_offset,
3949            is_fixed: self.is_fixed,
3950            is_sticky: self.is_sticky,
3951            sticky_top: self.sticky_top,
3952            z_index: self.z_index,
3953            ..Default::default()
3954        }
3955    }
3956
3957    fn children_builders(&self) -> &[Box<dyn ElementBuilder>] {
3958        &self.children
3959    }
3960
3961    fn event_handlers(&self) -> Option<&crate::event_handler::EventHandlers> {
3962        if self.event_handlers.is_empty() {
3963            None
3964        } else {
3965            Some(&self.event_handlers)
3966        }
3967    }
3968
3969    fn layout_style(&self) -> Option<&taffy::Style> {
3970        Some(&self.style)
3971    }
3972
3973    fn element_id(&self) -> Option<&str> {
3974        self.element_id.as_deref()
3975    }
3976
3977    fn element_classes(&self) -> &[String] {
3978        &self.classes
3979    }
3980
3981    fn layout_animation_config(&self) -> Option<crate::layout_animation::LayoutAnimationConfig> {
3982        self.layout_animation.clone()
3983    }
3984
3985    fn visual_animation_config(&self) -> Option<crate::visual_animation::VisualAnimationConfig> {
3986        self.visual_animation.clone()
3987    }
3988
3989    fn scroll_physics(&self) -> Option<crate::scroll::SharedScrollPhysics> {
3990        self.scroll_physics.clone()
3991    }
3992}
3993
3994/// Convenience function to create a new div
3995pub fn div() -> Div {
3996    Div::new()
3997}
3998
3999// Stack has been moved to stack.rs
4000
4001#[cfg(test)]
4002mod tests {
4003    use super::*;
4004    use crate::renderer::RenderTree;
4005
4006    #[test]
4007    fn test_div_builder() {
4008        let d = div().w(100.0).h(50.0).flex_row().gap(2.0).p(4.0);
4009
4010        assert!(matches!(d.style.display, Display::Flex));
4011        assert!(matches!(d.style.flex_direction, FlexDirection::Row));
4012    }
4013
4014    #[test]
4015    fn test_div_with_children() {
4016        let parent = div().flex_col().child(div().h(20.0)).child(div().h(30.0));
4017
4018        assert_eq!(parent.children.len(), 2);
4019    }
4020
4021    #[test]
4022    fn test_build_tree() {
4023        let ui = div().flex_col().child(div().h(20.0)).child(div().h(30.0));
4024
4025        let mut tree = LayoutTree::new();
4026        let root = ui.build(&mut tree);
4027
4028        assert_eq!(tree.len(), 3);
4029        assert_eq!(tree.children(root).len(), 2);
4030    }
4031
4032    #[test]
4033    fn test_layout_flex_row_with_fixed_children() {
4034        // Three fixed-width children in a row
4035        let ui = div()
4036            .w(300.0)
4037            .h(100.0)
4038            .flex_row()
4039            .child(div().w(50.0).h(100.0))
4040            .child(div().w(100.0).h(100.0))
4041            .child(div().w(50.0).h(100.0));
4042
4043        let mut tree = RenderTree::from_element(&ui);
4044        tree.compute_layout(300.0, 100.0);
4045
4046        let root = tree.root().unwrap();
4047        let children: Vec<_> = tree.layout_tree.children(root);
4048
4049        // First child at x=0
4050        let first = tree
4051            .layout_tree
4052            .get_bounds(children[0], (0.0, 0.0))
4053            .unwrap();
4054        assert_eq!(first.x, 0.0);
4055        assert_eq!(first.width, 50.0);
4056
4057        // Second child at x=50
4058        let second = tree
4059            .layout_tree
4060            .get_bounds(children[1], (0.0, 0.0))
4061            .unwrap();
4062        assert_eq!(second.x, 50.0);
4063        assert_eq!(second.width, 100.0);
4064
4065        // Third child at x=150
4066        let third = tree
4067            .layout_tree
4068            .get_bounds(children[2], (0.0, 0.0))
4069            .unwrap();
4070        assert_eq!(third.x, 150.0);
4071        assert_eq!(third.width, 50.0);
4072    }
4073
4074    #[test]
4075    fn test_layout_flex_col_with_gap() {
4076        // Column with gap between children (10px gap using gap_px)
4077        let ui = div()
4078            .w(100.0)
4079            .h(200.0)
4080            .flex_col()
4081            .gap_px(10.0) // 10px gap
4082            .child(div().w_full().h(40.0))
4083            .child(div().w_full().h(40.0))
4084            .child(div().w_full().h(40.0));
4085
4086        let mut tree = RenderTree::from_element(&ui);
4087        tree.compute_layout(100.0, 200.0);
4088
4089        let root = tree.root().unwrap();
4090        let children: Vec<_> = tree.layout_tree.children(root);
4091
4092        // First child at y=0
4093        let first = tree
4094            .layout_tree
4095            .get_bounds(children[0], (0.0, 0.0))
4096            .unwrap();
4097        assert_eq!(first.y, 0.0);
4098        assert_eq!(first.height, 40.0);
4099
4100        // Second child at y=50 (40 + 10 gap)
4101        let second = tree
4102            .layout_tree
4103            .get_bounds(children[1], (0.0, 0.0))
4104            .unwrap();
4105        assert_eq!(second.y, 50.0);
4106        assert_eq!(second.height, 40.0);
4107
4108        // Third child at y=100 (50 + 40 + 10 gap)
4109        let third = tree
4110            .layout_tree
4111            .get_bounds(children[2], (0.0, 0.0))
4112            .unwrap();
4113        assert_eq!(third.y, 100.0);
4114        assert_eq!(third.height, 40.0);
4115    }
4116
4117    #[test]
4118    fn test_layout_flex_grow() {
4119        // One fixed child, one growing child
4120        let ui = div()
4121            .w(200.0)
4122            .h(100.0)
4123            .flex_row()
4124            .child(div().w(50.0).h(100.0))
4125            .child(div().flex_grow().h(100.0));
4126
4127        let mut tree = RenderTree::from_element(&ui);
4128        tree.compute_layout(200.0, 100.0);
4129
4130        let root = tree.root().unwrap();
4131        let children: Vec<_> = tree.layout_tree.children(root);
4132
4133        // Fixed child
4134        let fixed = tree
4135            .layout_tree
4136            .get_bounds(children[0], (0.0, 0.0))
4137            .unwrap();
4138        assert_eq!(fixed.width, 50.0);
4139
4140        // Growing child should fill remaining space
4141        let growing = tree
4142            .layout_tree
4143            .get_bounds(children[1], (0.0, 0.0))
4144            .unwrap();
4145        assert_eq!(growing.x, 50.0);
4146        assert_eq!(growing.width, 150.0);
4147    }
4148
4149    #[test]
4150    fn test_layout_padding() {
4151        // Container with padding
4152        let ui = div()
4153            .w(100.0)
4154            .h(100.0)
4155            .p(2.0) // 8px padding (2 * 4px base unit)
4156            .child(div().w_full().h_full());
4157
4158        let mut tree = RenderTree::from_element(&ui);
4159        tree.compute_layout(100.0, 100.0);
4160
4161        let root = tree.root().unwrap();
4162        let children: Vec<_> = tree.layout_tree.children(root);
4163
4164        // Child should be inset by padding
4165        let child = tree
4166            .layout_tree
4167            .get_bounds(children[0], (0.0, 0.0))
4168            .unwrap();
4169        assert_eq!(child.x, 8.0);
4170        assert_eq!(child.y, 8.0);
4171        assert_eq!(child.width, 84.0); // 100 - 8 - 8
4172        assert_eq!(child.height, 84.0);
4173    }
4174
4175    #[test]
4176    fn test_layout_justify_between() {
4177        // Three children with space between
4178        let ui = div()
4179            .w(200.0)
4180            .h(50.0)
4181            .flex_row()
4182            .justify_between()
4183            .child(div().w(30.0).h(50.0))
4184            .child(div().w(30.0).h(50.0))
4185            .child(div().w(30.0).h(50.0));
4186
4187        let mut tree = RenderTree::from_element(&ui);
4188        tree.compute_layout(200.0, 50.0);
4189
4190        let root = tree.root().unwrap();
4191        let children: Vec<_> = tree.layout_tree.children(root);
4192
4193        // First at start
4194        let first = tree
4195            .layout_tree
4196            .get_bounds(children[0], (0.0, 0.0))
4197            .unwrap();
4198        assert_eq!(first.x, 0.0);
4199
4200        // Last at end
4201        let third = tree
4202            .layout_tree
4203            .get_bounds(children[2], (0.0, 0.0))
4204            .unwrap();
4205        assert_eq!(third.x, 170.0); // 200 - 30
4206
4207        // Middle should be centered between first and third
4208        let second = tree
4209            .layout_tree
4210            .get_bounds(children[1], (0.0, 0.0))
4211            .unwrap();
4212        assert_eq!(second.x, 85.0); // (170 - 0) / 2 - 30/2 + 30/2 = 85
4213    }
4214
4215    #[test]
4216    fn test_nested_layout() {
4217        // Nested flex containers
4218        let ui = div()
4219            .w(200.0)
4220            .h(200.0)
4221            .flex_col()
4222            .child(
4223                div()
4224                    .w_full()
4225                    .h(50.0)
4226                    .flex_row()
4227                    .child(div().w(50.0).h(50.0))
4228                    .child(div().flex_grow().h(50.0)),
4229            )
4230            .child(div().w_full().flex_grow());
4231
4232        let mut tree = RenderTree::from_element(&ui);
4233        tree.compute_layout(200.0, 200.0);
4234
4235        let root = tree.root().unwrap();
4236        let root_bounds = tree.get_bounds(root).unwrap();
4237        assert_eq!(root_bounds.width, 200.0);
4238        assert_eq!(root_bounds.height, 200.0);
4239
4240        let root_children: Vec<_> = tree.layout_tree.children(root);
4241
4242        // First row
4243        let row = tree
4244            .layout_tree
4245            .get_bounds(root_children[0], (0.0, 0.0))
4246            .unwrap();
4247        assert_eq!(row.height, 50.0);
4248        assert_eq!(row.width, 200.0);
4249
4250        // Second element should fill remaining height
4251        let bottom = tree
4252            .layout_tree
4253            .get_bounds(root_children[1], (0.0, 0.0))
4254            .unwrap();
4255        assert_eq!(bottom.y, 50.0);
4256        assert_eq!(bottom.height, 150.0);
4257    }
4258
4259    #[test]
4260    fn test_element_ref_basic() {
4261        // Create a ref
4262        let div_ref: ElementRef<Div> = ElementRef::new();
4263
4264        assert!(!div_ref.is_bound());
4265
4266        // Set a div
4267        div_ref.set(div().w(100.0).h(50.0).bg(Color::BLUE));
4268
4269        assert!(div_ref.is_bound());
4270
4271        // Read from the ref
4272        let width = div_ref.with(|d| d.style.size.width.clone());
4273        assert!(matches!(width, Some(Dimension::Length(100.0))));
4274    }
4275
4276    #[test]
4277    fn test_element_ref_with_mut() {
4278        let div_ref: ElementRef<Div> = ElementRef::new();
4279
4280        div_ref.set(div().w(100.0));
4281
4282        // Modify through the ref
4283        div_ref.with_mut(|d| {
4284            *d = d.swap().h(200.0).bg(Color::RED);
4285        });
4286
4287        // Verify modification
4288        let height = div_ref.with(|d| d.style.size.height.clone());
4289        assert!(matches!(height, Some(Dimension::Length(200.0))));
4290    }
4291
4292    #[test]
4293    fn test_element_ref_clone() {
4294        let div_ref: ElementRef<Div> = ElementRef::new();
4295        let div_ref_clone = div_ref.clone();
4296
4297        // Set on original
4298        div_ref.set(div().w(100.0));
4299
4300        // Should be visible on clone (shared storage)
4301        assert!(div_ref_clone.is_bound());
4302        let width = div_ref_clone.with(|d| d.style.size.width.clone());
4303        assert!(matches!(width, Some(Dimension::Length(100.0))));
4304    }
4305}