Skip to main content

cssbox_core/
style.rs

1//! Computed CSS style types for layout.
2
3use crate::values::{LengthPercentage, LengthPercentageAuto, LengthPercentageNone};
4
5/// CSS `display` outer type.
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum DisplayOuter {
8    Block,
9    Inline,
10    None,
11}
12
13/// CSS `display` inner type.
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum DisplayInner {
16    Flow,
17    FlowRoot,
18    Flex,
19    Grid,
20    Table,
21    TableRowGroup,
22    TableRow,
23    TableCell,
24    TableColumn,
25    TableColumnGroup,
26    TableCaption,
27    TableHeaderGroup,
28    TableFooterGroup,
29}
30
31/// Resolved `display` value.
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub struct Display {
34    pub outer: DisplayOuter,
35    pub inner: DisplayInner,
36}
37
38impl Display {
39    pub const BLOCK: Self = Self {
40        outer: DisplayOuter::Block,
41        inner: DisplayInner::Flow,
42    };
43    pub const INLINE: Self = Self {
44        outer: DisplayOuter::Inline,
45        inner: DisplayInner::Flow,
46    };
47    pub const INLINE_BLOCK: Self = Self {
48        outer: DisplayOuter::Inline,
49        inner: DisplayInner::FlowRoot,
50    };
51    pub const FLEX: Self = Self {
52        outer: DisplayOuter::Block,
53        inner: DisplayInner::Flex,
54    };
55    pub const INLINE_FLEX: Self = Self {
56        outer: DisplayOuter::Inline,
57        inner: DisplayInner::Flex,
58    };
59    pub const GRID: Self = Self {
60        outer: DisplayOuter::Block,
61        inner: DisplayInner::Grid,
62    };
63    pub const INLINE_GRID: Self = Self {
64        outer: DisplayOuter::Inline,
65        inner: DisplayInner::Grid,
66    };
67    pub const TABLE: Self = Self {
68        outer: DisplayOuter::Block,
69        inner: DisplayInner::Table,
70    };
71    pub const TABLE_ROW: Self = Self {
72        outer: DisplayOuter::Block,
73        inner: DisplayInner::TableRow,
74    };
75    pub const TABLE_CELL: Self = Self {
76        outer: DisplayOuter::Block,
77        inner: DisplayInner::TableCell,
78    };
79    pub const TABLE_ROW_GROUP: Self = Self {
80        outer: DisplayOuter::Block,
81        inner: DisplayInner::TableRowGroup,
82    };
83    pub const TABLE_COLUMN: Self = Self {
84        outer: DisplayOuter::Block,
85        inner: DisplayInner::TableColumn,
86    };
87    pub const TABLE_COLUMN_GROUP: Self = Self {
88        outer: DisplayOuter::Block,
89        inner: DisplayInner::TableColumnGroup,
90    };
91    pub const TABLE_CAPTION: Self = Self {
92        outer: DisplayOuter::Block,
93        inner: DisplayInner::TableCaption,
94    };
95    pub const TABLE_HEADER_GROUP: Self = Self {
96        outer: DisplayOuter::Block,
97        inner: DisplayInner::TableHeaderGroup,
98    };
99    pub const TABLE_FOOTER_GROUP: Self = Self {
100        outer: DisplayOuter::Block,
101        inner: DisplayInner::TableFooterGroup,
102    };
103    pub const NONE: Self = Self {
104        outer: DisplayOuter::None,
105        inner: DisplayInner::Flow,
106    };
107    pub const FLOW_ROOT: Self = Self {
108        outer: DisplayOuter::Block,
109        inner: DisplayInner::FlowRoot,
110    };
111
112    pub fn is_none(&self) -> bool {
113        self.outer == DisplayOuter::None
114    }
115
116    pub fn is_block_level(&self) -> bool {
117        self.outer == DisplayOuter::Block
118    }
119
120    pub fn is_inline_level(&self) -> bool {
121        self.outer == DisplayOuter::Inline
122    }
123
124    pub fn establishes_bfc(&self) -> bool {
125        matches!(
126            self.inner,
127            DisplayInner::FlowRoot | DisplayInner::Flex | DisplayInner::Grid | DisplayInner::Table
128        )
129    }
130
131    pub fn is_table_part(&self) -> bool {
132        matches!(
133            self.inner,
134            DisplayInner::Table
135                | DisplayInner::TableRow
136                | DisplayInner::TableCell
137                | DisplayInner::TableRowGroup
138                | DisplayInner::TableColumn
139                | DisplayInner::TableColumnGroup
140                | DisplayInner::TableCaption
141                | DisplayInner::TableHeaderGroup
142                | DisplayInner::TableFooterGroup
143        )
144    }
145}
146
147impl Default for Display {
148    fn default() -> Self {
149        Self::INLINE
150    }
151}
152
153/// CSS `position` property.
154#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
155pub enum Position {
156    #[default]
157    Static,
158    Relative,
159    Absolute,
160    Fixed,
161    Sticky,
162}
163
164impl Position {
165    pub fn is_positioned(&self) -> bool {
166        !matches!(self, Position::Static)
167    }
168
169    pub fn is_absolutely_positioned(&self) -> bool {
170        matches!(self, Position::Absolute | Position::Fixed)
171    }
172
173    pub fn is_in_flow(&self) -> bool {
174        matches!(
175            self,
176            Position::Static | Position::Relative | Position::Sticky
177        )
178    }
179}
180
181/// CSS `box-sizing` property.
182#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
183pub enum BoxSizing {
184    #[default]
185    ContentBox,
186    BorderBox,
187}
188
189/// CSS `float` property.
190#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
191pub enum Float {
192    #[default]
193    None,
194    Left,
195    Right,
196}
197
198/// CSS `clear` property.
199#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
200pub enum Clear {
201    #[default]
202    None,
203    Left,
204    Right,
205    Both,
206}
207
208/// CSS `overflow` property (per axis).
209#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
210pub enum Overflow {
211    #[default]
212    Visible,
213    Hidden,
214    Scroll,
215    Auto,
216}
217
218/// CSS `text-align` property.
219#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
220pub enum TextAlign {
221    #[default]
222    Left,
223    Right,
224    Center,
225    Justify,
226}
227
228/// CSS `vertical-align` (simplified for inline layout).
229#[derive(Debug, Clone, Copy, PartialEq, Default)]
230pub enum VerticalAlign {
231    #[default]
232    Baseline,
233    Top,
234    Middle,
235    Bottom,
236    Length(f32),
237}
238
239/// CSS `white-space` property.
240#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
241pub enum WhiteSpace {
242    #[default]
243    Normal,
244    Nowrap,
245    Pre,
246    PreWrap,
247    PreLine,
248}
249
250impl WhiteSpace {
251    pub fn preserves_newlines(&self) -> bool {
252        matches!(
253            self,
254            WhiteSpace::Pre | WhiteSpace::PreWrap | WhiteSpace::PreLine
255        )
256    }
257
258    pub fn collapses_spaces(&self) -> bool {
259        matches!(
260            self,
261            WhiteSpace::Normal | WhiteSpace::Nowrap | WhiteSpace::PreLine
262        )
263    }
264
265    pub fn wraps(&self) -> bool {
266        matches!(
267            self,
268            WhiteSpace::Normal | WhiteSpace::PreWrap | WhiteSpace::PreLine
269        )
270    }
271}
272
273// --- Flexbox properties ---
274
275/// CSS `flex-direction`.
276#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
277pub enum FlexDirection {
278    #[default]
279    Row,
280    RowReverse,
281    Column,
282    ColumnReverse,
283}
284
285impl FlexDirection {
286    pub fn is_row(&self) -> bool {
287        matches!(self, FlexDirection::Row | FlexDirection::RowReverse)
288    }
289
290    pub fn is_column(&self) -> bool {
291        !self.is_row()
292    }
293
294    pub fn is_reverse(&self) -> bool {
295        matches!(
296            self,
297            FlexDirection::RowReverse | FlexDirection::ColumnReverse
298        )
299    }
300}
301
302/// CSS `flex-wrap`.
303#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
304pub enum FlexWrap {
305    #[default]
306    Nowrap,
307    Wrap,
308    WrapReverse,
309}
310
311/// CSS alignment values (for justify-content, align-items, etc.).
312#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
313pub enum AlignItems {
314    #[default]
315    Stretch,
316    FlexStart,
317    FlexEnd,
318    Center,
319    Baseline,
320    Start,
321    End,
322}
323
324/// CSS `align-self`.
325#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
326pub enum AlignSelf {
327    #[default]
328    Auto,
329    Stretch,
330    FlexStart,
331    FlexEnd,
332    Center,
333    Baseline,
334    Start,
335    End,
336}
337
338/// CSS `justify-content`.
339#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
340pub enum JustifyContent {
341    #[default]
342    FlexStart,
343    FlexEnd,
344    Center,
345    SpaceBetween,
346    SpaceAround,
347    SpaceEvenly,
348    Start,
349    End,
350}
351
352/// CSS `align-content`.
353#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
354pub enum AlignContent {
355    #[default]
356    Stretch,
357    FlexStart,
358    FlexEnd,
359    Center,
360    SpaceBetween,
361    SpaceAround,
362    SpaceEvenly,
363    Start,
364    End,
365}
366
367// --- Grid properties ---
368
369/// A grid track sizing function.
370#[derive(Debug, Clone, PartialEq, Default)]
371pub enum TrackSizingFunction {
372    /// Fixed length.
373    Length(f32),
374    /// Percentage of grid container.
375    Percentage(f32),
376    /// Fraction of remaining space.
377    Fr(f32),
378    /// Minimum content size.
379    MinContent,
380    /// Maximum content size.
381    MaxContent,
382    /// Auto sizing.
383    #[default]
384    Auto,
385    /// minmax(min, max).
386    MinMax(Box<TrackSizingFunction>, Box<TrackSizingFunction>),
387    /// fit-content(limit).
388    FitContent(f32),
389}
390
391/// A grid track definition.
392#[derive(Debug, Clone, PartialEq)]
393pub struct TrackDefinition {
394    pub sizing: TrackSizingFunction,
395    pub line_name: Option<String>,
396}
397
398impl TrackDefinition {
399    pub fn new(sizing: TrackSizingFunction) -> Self {
400        Self {
401            sizing,
402            line_name: None,
403        }
404    }
405}
406
407/// Grid auto-flow direction.
408#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
409pub enum GridAutoFlow {
410    #[default]
411    Row,
412    Column,
413    RowDense,
414    ColumnDense,
415}
416
417/// Grid placement value for a single edge.
418#[derive(Debug, Clone, PartialEq, Default)]
419pub enum GridPlacement {
420    #[default]
421    Auto,
422    Line(i32),
423    Span(u32),
424    Named(String),
425}
426
427// --- Table properties ---
428
429/// CSS `table-layout`.
430#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
431pub enum TableLayout {
432    #[default]
433    Auto,
434    Fixed,
435}
436
437/// CSS `border-collapse`.
438#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
439pub enum BorderCollapse {
440    #[default]
441    Separate,
442    Collapse,
443}
444
445/// CSS `caption-side`.
446#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
447pub enum CaptionSide {
448    #[default]
449    Top,
450    Bottom,
451}
452
453// --- Computed style ---
454
455/// Full computed style for a layout node.
456#[derive(Debug, Clone, PartialEq)]
457pub struct ComputedStyle {
458    // Display & positioning
459    pub display: Display,
460    pub position: Position,
461    pub float: Float,
462    pub clear: Clear,
463
464    // Box model
465    pub box_sizing: BoxSizing,
466    pub width: LengthPercentageAuto,
467    pub height: LengthPercentageAuto,
468    pub min_width: LengthPercentage,
469    pub min_height: LengthPercentage,
470    pub max_width: LengthPercentageNone,
471    pub max_height: LengthPercentageNone,
472
473    pub margin_top: LengthPercentageAuto,
474    pub margin_right: LengthPercentageAuto,
475    pub margin_bottom: LengthPercentageAuto,
476    pub margin_left: LengthPercentageAuto,
477
478    pub padding_top: LengthPercentage,
479    pub padding_right: LengthPercentage,
480    pub padding_bottom: LengthPercentage,
481    pub padding_left: LengthPercentage,
482
483    pub border_top_width: f32,
484    pub border_right_width: f32,
485    pub border_bottom_width: f32,
486    pub border_left_width: f32,
487
488    // Positioning offsets
489    pub top: LengthPercentageAuto,
490    pub right: LengthPercentageAuto,
491    pub bottom: LengthPercentageAuto,
492    pub left: LengthPercentageAuto,
493
494    // Overflow
495    pub overflow_x: Overflow,
496    pub overflow_y: Overflow,
497
498    // Text/inline
499    pub text_align: TextAlign,
500    pub vertical_align: VerticalAlign,
501    pub line_height: f32,
502    pub white_space: WhiteSpace,
503
504    // Flexbox
505    pub flex_direction: FlexDirection,
506    pub flex_wrap: FlexWrap,
507    pub flex_grow: f32,
508    pub flex_shrink: f32,
509    pub flex_basis: LengthPercentageAuto,
510    pub align_items: AlignItems,
511    pub align_self: AlignSelf,
512    pub align_content: AlignContent,
513    pub justify_content: JustifyContent,
514    pub order: i32,
515
516    // Grid container
517    pub grid_template_rows: Vec<TrackDefinition>,
518    pub grid_template_columns: Vec<TrackDefinition>,
519    pub grid_auto_rows: Vec<TrackSizingFunction>,
520    pub grid_auto_columns: Vec<TrackSizingFunction>,
521    pub grid_auto_flow: GridAutoFlow,
522    pub row_gap: f32,
523    pub column_gap: f32,
524
525    // Grid item
526    pub grid_row_start: GridPlacement,
527    pub grid_row_end: GridPlacement,
528    pub grid_column_start: GridPlacement,
529    pub grid_column_end: GridPlacement,
530
531    // Table
532    pub table_layout: TableLayout,
533    pub border_collapse: BorderCollapse,
534    pub border_spacing: f32,
535    pub caption_side: CaptionSide,
536
537    // Z-index
538    pub z_index: crate::values::NumberOrAuto,
539}
540
541impl Default for ComputedStyle {
542    fn default() -> Self {
543        Self {
544            display: Display::INLINE,
545            position: Position::Static,
546            float: Float::None,
547            clear: Clear::None,
548
549            box_sizing: BoxSizing::ContentBox,
550            width: LengthPercentageAuto::Auto,
551            height: LengthPercentageAuto::Auto,
552            min_width: LengthPercentage::Length(0.0),
553            min_height: LengthPercentage::Length(0.0),
554            max_width: LengthPercentageNone::None,
555            max_height: LengthPercentageNone::None,
556
557            margin_top: LengthPercentageAuto::px(0.0),
558            margin_right: LengthPercentageAuto::px(0.0),
559            margin_bottom: LengthPercentageAuto::px(0.0),
560            margin_left: LengthPercentageAuto::px(0.0),
561
562            padding_top: LengthPercentage::Length(0.0),
563            padding_right: LengthPercentage::Length(0.0),
564            padding_bottom: LengthPercentage::Length(0.0),
565            padding_left: LengthPercentage::Length(0.0),
566
567            border_top_width: 0.0,
568            border_right_width: 0.0,
569            border_bottom_width: 0.0,
570            border_left_width: 0.0,
571
572            top: LengthPercentageAuto::Auto,
573            right: LengthPercentageAuto::Auto,
574            bottom: LengthPercentageAuto::Auto,
575            left: LengthPercentageAuto::Auto,
576
577            overflow_x: Overflow::Visible,
578            overflow_y: Overflow::Visible,
579
580            text_align: TextAlign::Left,
581            vertical_align: VerticalAlign::Baseline,
582            line_height: 1.2,
583            white_space: WhiteSpace::Normal,
584
585            flex_direction: FlexDirection::Row,
586            flex_wrap: FlexWrap::Nowrap,
587            flex_grow: 0.0,
588            flex_shrink: 1.0,
589            flex_basis: LengthPercentageAuto::Auto,
590            align_items: AlignItems::Stretch,
591            align_self: AlignSelf::Auto,
592            align_content: AlignContent::Stretch,
593            justify_content: JustifyContent::FlexStart,
594            order: 0,
595
596            grid_template_rows: Vec::new(),
597            grid_template_columns: Vec::new(),
598            grid_auto_rows: Vec::new(),
599            grid_auto_columns: Vec::new(),
600            grid_auto_flow: GridAutoFlow::Row,
601            row_gap: 0.0,
602            column_gap: 0.0,
603
604            grid_row_start: GridPlacement::Auto,
605            grid_row_end: GridPlacement::Auto,
606            grid_column_start: GridPlacement::Auto,
607            grid_column_end: GridPlacement::Auto,
608
609            table_layout: TableLayout::Auto,
610            border_collapse: BorderCollapse::Separate,
611            border_spacing: 0.0,
612            caption_side: CaptionSide::Top,
613
614            z_index: crate::values::NumberOrAuto::Auto,
615        }
616    }
617}
618
619impl ComputedStyle {
620    /// Create a block-level element style with default values.
621    pub fn block() -> Self {
622        Self {
623            display: Display::BLOCK,
624            ..Default::default()
625        }
626    }
627
628    /// Create an inline element style.
629    pub fn inline() -> Self {
630        Self::default()
631    }
632
633    /// Whether this element establishes a new block formatting context.
634    pub fn establishes_bfc(&self) -> bool {
635        self.display.establishes_bfc()
636            || self.overflow_x != Overflow::Visible
637            || self.overflow_y != Overflow::Visible
638            || self.float != Float::None
639            || self.position.is_absolutely_positioned()
640    }
641
642    /// Whether this element is out of flow.
643    pub fn is_out_of_flow(&self) -> bool {
644        self.position.is_absolutely_positioned() || self.float != Float::None
645    }
646}