tawasuyu

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 231Rust 10TOML 3CSS 1348 Definitions

"Traitify" the `Style` struct (#568)

* Create style traits

* Add algorithm-specific PartialLayoutTree traits

* Taffy tree changes

* Convert algorithms to traitified styles

* Remove style clone in grid algorithm

* Fixup convert

* Make CacheMut an associated type

* Update examples to use new traits

* Fix --no-default-features

* Fixup is_block

* Fix clippy lints

* Fix --no-default-features taffy_tree

* Give style trait associated types unique names

f953fcbebbb7a0a3c158e538190ea88b252354fe · by Nico Burns · (2 years ago) · parent 6fb38bfe0 · browse the tree at this commit

Semantic delta · 210

src/compute/block.rs

src/compute/flexbox.rs

src/compute/grid/alignment.rs

src/compute/grid/explicit_grid.rs

src/compute/grid/implicit_grid.rs

src/compute/grid/mod.rs

src/compute/grid/placement.rs

src/compute/grid/types/grid_item.rs

src/compute/leaf.rs

src/compute/mod.rs

src/style/flex.rs

src/style/grid.rs

src/style/mod.rs

src/tree/traits.rs

19 files · +1286 −374

diff --git a/examples/custom_tree_owned_partial.rs b/examples/custom_tree_owned_partial.rs
index 79f693296..f9fb37897 100644
--- a/examples/custom_tree_owned_partial.rs
+++ b/examples/custom_tree_owned_partial.rs
@@ -126,7 +126,12 @@ impl taffy::TraversePartialTree for Node {
 }
 
 impl taffy::LayoutPartialTree for Node {
-    fn get_style(&self, node_id: NodeId) -> &Style {
+    type CoreContainerStyle<'a> = &'a Style where
+        Self: 'a;
+
+    type CacheMut<'b> = &'b mut Cache where Self: 'b;
+
+    fn get_core_container_style(&self, node_id: NodeId) -> Self::CoreContainerStyle<'_> {
         &self.node_from_id(node_id).style
     }
 
@@ -162,6 +167,38 @@ impl taffy::LayoutPartialTree for Node {
     }
 }
 
+impl taffy::LayoutFlexboxContainer for Node {
+    type FlexboxContainerStyle<'a> = &'a Style where
+        Self: 'a;
+
+    type FlexboxItemStyle<'a> = &'a Style where
+        Self: 'a;
+
+    fn get_flexbox_container_style(&self, node_id: NodeId) -> Self::FlexboxContainerStyle<'_> {
+        &self.node_from_id(node_id).style
+    }
+
+    fn get_flexbox_child_style(&self, child_node_id: NodeId) -> Self::FlexboxItemStyle<'_> {
+        &self.node_from_id(child_node_id).style
+    }
+}
+
+impl taffy::LayoutGridContainer for Node {
+    type GridContainerStyle<'a> = &'a Style where
+        Self: 'a;
+
+    type GridItemStyle<'a> = &'a Style where
+        Self: 'a;
+
+    fn get_grid_container_style(&self, node_id: NodeId) -> Self::GridContainerStyle<'_> {
+        &self.node_from_id(node_id).style
+    }
+
+    fn get_grid_child_style(&self, child_node_id: NodeId) -> Self::GridItemStyle<'_> {
+        &self.node_from_id(child_node_id).style
+    }
+}
+
 fn main() -> Result<(), taffy::TaffyError> {
     let mut root = Node::new_column(Style::DEFAULT);
 
diff --git a/examples/custom_tree_owned_unsafe.rs b/examples/custom_tree_owned_unsafe.rs
index 96f31527f..c6bf0d24e 100644
--- a/examples/custom_tree_owned_unsafe.rs
+++ b/examples/custom_tree_owned_unsafe.rs
@@ -130,7 +130,12 @@ impl TraversePartialTree for StatelessLayoutTree {
 impl TraverseTree for StatelessLayoutTree {}
 
 impl LayoutPartialTree for StatelessLayoutTree {
-    fn get_style(&self, node_id: NodeId) -> &Style {
+    type CoreContainerStyle<'a> = &'a Style where
+        Self: 'a;
+
+    type CacheMut<'b> = &'b mut Cache where Self: 'b;
+
+    fn get_core_container_style(&self, node_id: NodeId) -> Self::CoreContainerStyle<'_> {
         unsafe { &node_from_id(node_id).style }
     }
 
@@ -166,6 +171,38 @@ impl LayoutPartialTree for StatelessLayoutTree {
     }
 }
 
+impl taffy::LayoutFlexboxContainer for StatelessLayoutTree {
+    type FlexboxContainerStyle<'a> = &'a Style where
+        Self: 'a;
+
+    type FlexboxItemStyle<'a> = &'a Style where
+        Self: 'a;
+
+    fn get_flexbox_container_style(&self, node_id: NodeId) -> Self::FlexboxContainerStyle<'_> {
+        unsafe { &node_from_id(node_id).style }
+    }
+
+    fn get_flexbox_child_style(&self, child_node_id: NodeId) -> Self::FlexboxItemStyle<'_> {
+        unsafe { &node_from_id(child_node_id).style }
+    }
+}
+
+impl taffy::LayoutGridContainer for StatelessLayoutTree {
+    type GridContainerStyle<'a> = &'a Style where
+        Self: 'a;
+
+    type GridItemStyle<'a> = &'a Style where
+        Self: 'a;
+
+    fn get_grid_container_style(&self, node_id: NodeId) -> Self::GridContainerStyle<'_> {
+        unsafe { &node_from_id(node_id).style }
+    }
+
+    fn get_grid_child_style(&self, child_node_id: NodeId) -> Self::GridItemStyle<'_> {
+        unsafe { &node_from_id(child_node_id).style }
+    }
+}
+
 impl RoundTree for StatelessLayoutTree {
     fn get_unrounded_layout(&self, node_id: NodeId) -> &Layout {
         unsafe { &node_from_id_mut(node_id).unrounded_layout }
diff --git a/examples/custom_tree_vec.rs b/examples/custom_tree_vec.rs
index f3e86568a..98ef83f7c 100644
--- a/examples/custom_tree_vec.rs
+++ b/examples/custom_tree_vec.rs
@@ -139,7 +139,12 @@ impl taffy::TraversePartialTree for Tree {
 impl taffy::TraverseTree for Tree {}
 
 impl taffy::LayoutPartialTree for Tree {
-    fn get_style(&self, node_id: NodeId) -> &Style {
+    type CoreContainerStyle<'a> = &'a Style where
+        Self: 'a;
+
+    type CacheMut<'b> = &'b mut Cache where Self: 'b;
+
+    fn get_core_container_style(&self, node_id: NodeId) -> Self::CoreContainerStyle<'_> {
         &self.node_from_id(node_id).style
     }
 
@@ -175,6 +180,38 @@ impl taffy::LayoutPartialTree for Tree {
     }
 }
 
+impl taffy::LayoutFlexboxContainer for Tree {
+    type FlexboxContainerStyle<'a> = &'a Style where
+        Self: 'a;
+
+    type FlexboxItemStyle<'a> = &'a Style where
+        Self: 'a;
+
+    fn get_flexbox_container_style(&self, node_id: NodeId) -> Self::FlexboxContainerStyle<'_> {
+        &self.node_from_id(node_id).style
+    }
+
+    fn get_flexbox_child_style(&self, child_node_id: NodeId) -> Self::FlexboxItemStyle<'_> {
+        &self.node_from_id(child_node_id).style
+    }
+}
+
+impl taffy::LayoutGridContainer for Tree {
+    type GridContainerStyle<'a> = &'a Style where
+        Self: 'a;
+
+    type GridItemStyle<'a> = &'a Style where
+        Self: 'a;
+
+    fn get_grid_container_style(&self, node_id: NodeId) -> Self::GridContainerStyle<'_> {
+        &self.node_from_id(node_id).style
+    }
+
+    fn get_grid_child_style(&self, child_node_id: NodeId) -> Self::GridItemStyle<'_> {
+        &self.node_from_id(child_node_id).style
+    }
+}
+
 impl taffy::RoundTree for Tree {
     fn get_unrounded_layout(&self, node_id: NodeId) -> &Layout {
         &self.node_from_id(node_id).unrounded_layout
diff --git a/src/compute/block.rs b/src/compute/block.rs
index 8070dab04..7e0bc1aa5 100644
--- a/src/compute/block.rs
+++ b/src/compute/block.rs
@@ -1,6 +1,6 @@
 //! Computes the CSS block layout algorithm in the case that the block container being laid out contains only block-level boxes
 use crate::geometry::{Line, Point, Rect, Size};
-use crate::style::{AvailableSpace, Display, LengthPercentageAuto, Overflow, Position};
+use crate::style::{AvailableSpace, CoreStyle, LengthPercentageAuto, Overflow, Position};
 use crate::style_helpers::TaffyMaxContent;
 use crate::tree::{CollapsibleMarginSet, Layout, LayoutInput, LayoutOutput, RunMode, SizingMode};
 use crate::tree::{LayoutPartialTree, LayoutPartialTreeExt, NodeId};
@@ -9,7 +9,7 @@ use crate::util::sys::f32_max;
 use crate::util::sys::Vec;
 use crate::util::MaybeMath;
 use crate::util::{MaybeResolve, ResolveOrZero};
-use crate::BoxSizing;
+use crate::{BoxGenerationMode, BoxSizing, LayoutBlockContainer};
 
 #[cfg(feature = "content_size")]
 use super::common::content_size::compute_content_size_contribution;
@@ -58,31 +58,35 @@ struct BlockItem {
 }
 
 /// Computes the layout of [`LayoutPartialTree`] according to the block layout algorithm
-pub fn compute_block_layout(tree: &mut impl LayoutPartialTree, node_id: NodeId, inputs: LayoutInput) -> LayoutOutput {
+pub fn compute_block_layout(
+    tree: &mut impl LayoutBlockContainer,
+    node_id: NodeId,
+    inputs: LayoutInput,
+) -> LayoutOutput {
     let LayoutInput { known_dimensions, parent_size, run_mode, .. } = inputs;
-    let style = tree.get_style(node_id);
+    let style = tree.get_block_container_style(node_id);
 
     // Pull these out earlier to avoid borrowing issues
-    let aspect_ratio = style.aspect_ratio;
-    let padding = style.padding.resolve_or_zero(parent_size.width);
-    let border = style.border.resolve_or_zero(parent_size.width);
+    let aspect_ratio = style.aspect_ratio();
+    let padding = style.padding().resolve_or_zero(parent_size.width);
+    let border = style.border().resolve_or_zero(parent_size.width);
     let padding_border_size = (padding + border).sum_axes();
     let box_sizing_adjustment =
-        if style.box_sizing == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO };
+        if style.box_sizing() == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO };
 
     let min_size = style
-        .min_size
+        .min_size()
         .maybe_resolve(parent_size)
         .maybe_apply_aspect_ratio(aspect_ratio)
         .maybe_add(box_sizing_adjustment);
     let max_size = style
-        .max_size
+        .max_size()
         .maybe_resolve(parent_size)
         .maybe_apply_aspect_ratio(aspect_ratio)
         .maybe_add(box_sizing_adjustment);
     let clamped_style_size = if inputs.sizing_mode == SizingMode::InherentSize {
         style
-            .size
+            .size()
             .maybe_resolve(parent_size)
             .maybe_apply_aspect_ratio(aspect_ratio)
             .maybe_add(box_sizing_adjustment)
@@ -91,6 +95,8 @@ pub fn compute_block_layout(tree: &mut impl LayoutPartialTree, node_id: NodeId,
         Size::NONE
     };
 
+    drop(style);
+
     // If both min and max in a given axis are set and max <= min then this determines the size in that axis
     let min_max_definite_size = min_size.zip_map(max_size, |min, max| match (min, max) {
         (Some(min), Some(max)) if max <= min => Some(min),
@@ -112,26 +118,26 @@ pub fn compute_block_layout(tree: &mut impl LayoutPartialTree, node_id: NodeId,
     compute_inner(tree, node_id, LayoutInput { known_dimensions: styled_based_known_dimensions, ..inputs })
 }
 
-/// Computes the layout of [`LayoutPartialTree`] according to the block layout algorithm
-fn compute_inner(tree: &mut impl LayoutPartialTree, node_id: NodeId, inputs: LayoutInput) -> LayoutOutput {
+/// Computes the layout of [`LayoutBlockContainer`] according to the block layout algorithm
+fn compute_inner(tree: &mut impl LayoutBlockContainer, node_id: NodeId, inputs: LayoutInput) -> LayoutOutput {
     let LayoutInput {
         known_dimensions, parent_size, available_space, run_mode, vertical_margins_are_collapsible, ..
     } = inputs;
 
-    let style = tree.get_style(node_id);
-    let raw_padding = style.padding;
-    let raw_border = style.border;
-    let raw_margin = style.margin;
-    let aspect_ratio = style.aspect_ratio;
-    let padding = style.padding.resolve_or_zero(parent_size.width);
-    let border = style.border.resolve_or_zero(parent_size.width);
+    let style = tree.get_block_container_style(node_id);
+    let raw_padding = style.padding();
+    let raw_border = style.border();
+    let raw_margin = style.margin();
+    let aspect_ratio = style.aspect_ratio();
+    let padding = raw_padding.resolve_or_zero(parent_size.width);
+    let border = raw_border.resolve_or_zero(parent_size.width);
 
     // Scrollbar gutters are reserved when the `overflow` property is set to `Overflow::Scroll`.
     // However, the axis are switched (transposed) because a node that scrolls vertically needs
     // *horizontal* space to be reserved for a scrollbar
     let scrollbar_gutter = {
-        let offsets = style.overflow.transpose().map(|overflow| match overflow {
-            Overflow::Scroll => style.scrollbar_width,
+        let offsets = style.overflow().transpose().map(|overflow| match overflow {
+            Overflow::Scroll => style.scrollbar_width(),
             _ => 0.0,
         });
         // TODO: make side configurable based on the `direction` property
@@ -143,16 +149,16 @@ fn compute_inner(tree: &mut impl LayoutPartialTree, node_id: NodeId, inputs: Lay
     let container_content_box_size = known_dimensions.maybe_sub(content_box_inset.sum_axes());
 
     let box_sizing_adjustment =
-        if style.box_sizing == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO };
+        if style.box_sizing() == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO };
     let size =
-        style.size.maybe_resolve(parent_size).maybe_apply_aspect_ratio(aspect_ratio).maybe_add(box_sizing_adjustment);
+        style.size().maybe_resolve(parent_size).maybe_apply_aspect_ratio(aspect_ratio).maybe_add(box_sizing_adjustment);
     let min_size = style
-        .min_size
+        .min_size()
         .maybe_resolve(parent_size)
         .maybe_apply_aspect_ratio(aspect_ratio)
         .maybe_add(box_sizing_adjustment);
     let max_size = style
-        .max_size
+        .max_size()
         .maybe_resolve(parent_size)
         .maybe_apply_aspect_ratio(aspect_ratio)
         .maybe_add(box_sizing_adjustment);
@@ -160,28 +166,30 @@ fn compute_inner(tree: &mut impl LayoutPartialTree, node_id: NodeId, inputs: Lay
     // Determine margin collapsing behaviour
     let own_margins_collapse_with_children = Line {
         start: vertical_margins_are_collapsible.start
-            && !style.overflow.x.is_scroll_container()
-            && !style.overflow.y.is_scroll_container()
-            && style.position == Position::Relative
+            && !style.overflow().x.is_scroll_container()
+            && !style.overflow().y.is_scroll_container()
+            && style.position() == Position::Relative
             && padding.top == 0.0
             && border.top == 0.0,
         end: vertical_margins_are_collapsible.end
-            && !style.overflow.x.is_scroll_container()
-            && !style.overflow.y.is_scroll_container()
-            && style.position == Position::Relative
+            && !style.overflow().x.is_scroll_container()
+            && !style.overflow().y.is_scroll_container()
+            && style.position() == Position::Relative
             && padding.bottom == 0.0
             && border.bottom == 0.0
             && size.height.is_none(),
     };
-    let has_styles_preventing_being_collapsed_through = style.display != Display::Block
-        || style.overflow.x.is_scroll_container()
-        || style.overflow.y.is_scroll_container()
-        || style.position == Position::Absolute
+    let has_styles_preventing_being_collapsed_through = !style.is_block()
+        || style.overflow().x.is_scroll_container()
+        || style.overflow().y.is_scroll_container()
+        || style.position() == Position::Absolute
         || padding.top > 0.0
         || padding.bottom > 0.0
         || border.top > 0.0
         || border.bottom > 0.0;
 
+    drop(style);
+
     // 1. Generate items
     let mut items = generate_item_list(tree, node_id, container_content_box_size);
 
@@ -233,7 +241,7 @@ fn compute_inner(tree: &mut impl LayoutPartialTree, node_id: NodeId, inputs: Lay
     let len = tree.child_count(node_id);
     for order in 0..len {
         let child = tree.get_child_id(node_id, order);
-        if tree.get_style(child).display == Display::None {
+        if tree.get_block_child_style(child).box_generation_mode() == BoxGenerationMode::None {
             tree.set_unrounded_layout(child, &Layout::with_order(order as u32));
             tree.perform_child_layout(
                 child,
@@ -279,48 +287,47 @@ fn compute_inner(tree: &mut impl LayoutPartialTree, node_id: NodeId, inputs: Lay
 /// Create a `Vec` of `BlockItem` structs where each item in the `Vec` represents a child of the current node
 #[inline]
 fn generate_item_list(
-    tree: &impl LayoutPartialTree,
+    tree: &impl LayoutBlockContainer,
     node: NodeId,
     node_inner_size: Size<Option<f32>>,
 ) -> Vec<BlockItem> {
     tree.child_ids(node)
-        .map(|child_node_id| (child_node_id, tree.get_style(child_node_id)))
-        .filter(|(_, style)| style.display != Display::None)
+        .map(|child_node_id| (child_node_id, tree.get_block_child_style(child_node_id)))
+        .filter(|(_, style)| style.box_generation_mode() != BoxGenerationMode::None)
         .enumerate()
         .map(|(order, (child_node_id, child_style))| {
-            let aspect_ratio = child_style.aspect_ratio;
-            let padding = child_style.padding.resolve_or_zero(node_inner_size);
-            let border = child_style.border.resolve_or_zero(node_inner_size);
+            let aspect_ratio = child_style.aspect_ratio();
+            let padding = child_style.padding().resolve_or_zero(node_inner_size);
+            let border = child_style.border().resolve_or_zero(node_inner_size);
             let pb_sum = (padding + border).sum_axes();
             let box_sizing_adjustment =
-                if child_style.box_sizing == BoxSizing::ContentBox { pb_sum } else { Size::ZERO };
+                if child_style.box_sizing() == BoxSizing::ContentBox { pb_sum } else { Size::ZERO };
             BlockItem {
                 node_id: child_node_id,
                 order: order as u32,
-
                 size: child_style
-                    .size
+                    .size()
                     .maybe_resolve(node_inner_size)
                     .maybe_apply_aspect_ratio(aspect_ratio)
                     .maybe_add(box_sizing_adjustment),
                 min_size: child_style
-                    .min_size
+                    .min_size()
                     .maybe_resolve(node_inner_size)
                     .maybe_apply_aspect_ratio(aspect_ratio)
                     .maybe_add(box_sizing_adjustment),
                 max_size: child_style
-                    .max_size
+                    .max_size()
                     .maybe_resolve(node_inner_size)
                     .maybe_apply_aspect_ratio(aspect_ratio)
                     .maybe_add(box_sizing_adjustment),
-                overflow: child_style.overflow,
-                scrollbar_width: child_style.scrollbar_width,
-                position: child_style.position,
-                inset: child_style.inset,
-                margin: child_style.margin,
+                overflow: child_style.overflow(),
+                scrollbar_width: child_style.scrollbar_width(),
+                position: child_style.position(),
+                inset: child_style.inset(),
+                margin: child_style.margin(),
                 padding,
                 border,
-                padding_border_sum: (padding + border).sum_axes(),
+                padding_border_sum: pb_sum,
 
                 // Fields to be computed later (for now we initialise with dummy values)
                 computed_size: Size::zero(),
@@ -531,7 +538,7 @@ fn perform_final_layout_on_in_flow_children(
 /// Perform absolute layout on all absolutely positioned children.
 #[inline]
 fn perform_absolute_layout_on_absolute_children(
-    tree: &mut impl LayoutPartialTree,
+    tree: &mut impl LayoutBlockContainer,
     items: &[BlockItem],
     area_size: Size<f32>,
     area_offset: Point<f32>,
@@ -543,47 +550,50 @@ fn perform_absolute_layout_on_absolute_children(
     let mut absolute_content_size = Size::ZERO;
 
     for item in items.iter().filter(|item| item.position == Position::Absolute) {
-        let child_style = tree.get_style(item.node_id);
+        let child_style = tree.get_block_child_style(item.node_id);
 
         // Skip items that are display:none or are not position:absolute
-        if child_style.display == Display::None || child_style.position != Position::Absolute {
+        if child_style.box_generation_mode() == BoxGenerationMode::None || child_style.position() != Position::Absolute
+        {
             continue;
         }
 
-        let aspect_ratio = child_style.aspect_ratio;
-        let margin = child_style.margin.map(|margin| margin.resolve_to_option(area_width));
-        let padding = child_style.padding.resolve_or_zero(Some(area_width));
-        let border = child_style.border.resolve_or_zero(Some(area_width));
+        let aspect_ratio = child_style.aspect_ratio();
+        let margin = child_style.margin().map(|margin| margin.resolve_to_option(area_width));
+        let padding = child_style.padding().resolve_or_zero(Some(area_width));
+        let border = child_style.border().resolve_or_zero(Some(area_width));
         let padding_border_sum = (padding + border).sum_axes();
         let box_sizing_adjustment =
-            if child_style.box_sizing == BoxSizing::ContentBox { padding_border_sum } else { Size::ZERO };
+            if child_style.box_sizing() == BoxSizing::ContentBox { padding_border_sum } else { Size::ZERO };
 
         // Resolve inset
-        let left = child_style.inset.left.maybe_resolve(area_width);
-        let right = child_style.inset.right.maybe_resolve(area_width);
-        let top = child_style.inset.top.maybe_resolve(area_height);
-        let bottom = child_style.inset.bottom.maybe_resolve(area_height);
+        let left = child_style.inset().left.maybe_resolve(area_width);
+        let right = child_style.inset().right.maybe_resolve(area_width);
+        let top = child_style.inset().top.maybe_resolve(area_height);
+        let bottom = child_style.inset().bottom.maybe_resolve(area_height);
 
         // Compute known dimensions from min/max/inherent size styles
         let style_size = child_style
-            .size
+            .size()
             .maybe_resolve(area_size)
             .maybe_apply_aspect_ratio(aspect_ratio)
             .maybe_add(box_sizing_adjustment);
         let min_size = child_style
-            .min_size
+            .min_size()
             .maybe_resolve(area_size)
             .maybe_apply_aspect_ratio(aspect_ratio)
             .maybe_add(box_sizing_adjustment)
             .or(padding_border_sum.map(Some))
             .maybe_max(padding_border_sum);
         let max_size = child_style
-            .max_size
+            .max_size()
             .maybe_resolve(area_size)
             .maybe_apply_aspect_ratio(aspect_ratio)
             .maybe_add(box_sizing_adjustment);
         let mut known_dimensions = style_size.maybe_clamp(min_size, max_size);
 
+        drop(child_style);
+
         // Fill in width from left/right and reapply aspect ratio if:
         //   - Width is not already known
         //   - Item has both left and right inset properties set
diff --git a/src/compute/flexbox.rs b/src/compute/flexbox.rs
index c814711f9..358c76bf7 100644
--- a/src/compute/flexbox.rs
+++ b/src/compute/flexbox.rs
@@ -2,18 +2,18 @@
 use crate::compute::common::alignment::compute_alignment_offset;
 use crate::geometry::{Line, Point, Rect, Size};
 use crate::style::{
-    AlignContent, AlignItems, AlignSelf, AvailableSpace, Dimension, Display, FlexWrap, JustifyContent,
-    LengthPercentageAuto, Overflow, Position,
+    AlignContent, AlignItems, AlignSelf, AvailableSpace, Dimension, FlexWrap, JustifyContent, LengthPercentageAuto,
+    Overflow, Position,
 };
-use crate::style::{FlexDirection, Style};
+use crate::style::{CoreStyle, FlexDirection, FlexboxContainerStyle, FlexboxItemStyle};
 use crate::style_helpers::{TaffyMaxContent, TaffyMinContent};
 use crate::tree::{Layout, LayoutInput, LayoutOutput, RunMode, SizingMode};
-use crate::tree::{LayoutPartialTree, LayoutPartialTreeExt, NodeId};
+use crate::tree::{LayoutFlexboxContainer, LayoutPartialTreeExt, NodeId};
 use crate::util::debug::debug_log;
 use crate::util::sys::{f32_max, new_vec_with_capacity, Vec};
 use crate::util::MaybeMath;
 use crate::util::{MaybeResolve, ResolveOrZero};
-use crate::BoxSizing;
+use crate::{BoxGenerationMode, BoxSizing};
 
 use super::common::alignment::apply_alignment_fallback;
 #[cfg(feature = "content_size")]
@@ -154,30 +154,35 @@ struct AlgoConstants {
 }
 
 /// Computes the layout of [`LayoutPartialTree`] according to the flexbox algorithm
-pub fn compute_flexbox_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inputs: LayoutInput) -> LayoutOutput {
+pub fn compute_flexbox_layout(
+    tree: &mut impl LayoutFlexboxContainer,
+    node: NodeId,
+    inputs: LayoutInput,
+) -> LayoutOutput {
     let LayoutInput { known_dimensions, parent_size, run_mode, .. } = inputs;
-    let style = tree.get_style(node);
+    let style = tree.get_flexbox_container_style(node);
 
     // Pull these out earlier to avoid borrowing issues
-    let aspect_ratio = style.aspect_ratio;
-    let padding = style.padding.resolve_or_zero(parent_size.width);
-    let border = style.border.resolve_or_zero(parent_size.width);
+    let aspect_ratio = style.aspect_ratio();
+    let padding = style.padding().resolve_or_zero(parent_size.width);
+    let border = style.border().resolve_or_zero(parent_size.width);
     let padding_border_sum = padding.sum_axes() + border.sum_axes();
-    let box_sizing_adjustment = if style.box_sizing == BoxSizing::ContentBox { padding_border_sum } else { Size::ZERO };
+    let box_sizing_adjustment =
+        if style.box_sizing() == BoxSizing::ContentBox { padding_border_sum } else { Size::ZERO };
 
     let min_size = style
-        .min_size
+        .min_size()
         .maybe_resolve(parent_size)
         .maybe_apply_aspect_ratio(aspect_ratio)
         .maybe_add(box_sizing_adjustment);
     let max_size = style
-        .max_size
+        .max_size()
         .maybe_resolve(parent_size)
         .maybe_apply_aspect_ratio(aspect_ratio)
         .maybe_add(box_sizing_adjustment);
     let clamped_style_size = if inputs.sizing_mode == SizingMode::InherentSize {
         style
-            .size
+            .size()
             .maybe_resolve(parent_size)
             .maybe_apply_aspect_ratio(aspect_ratio)
             .maybe_add(box_sizing_adjustment)
@@ -204,16 +209,18 @@ pub fn compute_flexbox_layout(tree: &mut impl LayoutPartialTree, node: NodeId, i
         }
     }
 
-    debug_log!("FLEX:", dbg:style.flex_direction);
+    debug_log!("FLEX:", dbg:style.flex_direction());
+    drop(style);
+
     compute_preliminary(tree, node, LayoutInput { known_dimensions: styled_based_known_dimensions, ..inputs })
 }
 
 /// Compute a preliminary size for an item
-fn compute_preliminary(tree: &mut impl LayoutPartialTree, node: NodeId, inputs: LayoutInput) -> LayoutOutput {
+fn compute_preliminary(tree: &mut impl LayoutFlexboxContainer, node: NodeId, inputs: LayoutInput) -> LayoutOutput {
     let LayoutInput { known_dimensions, parent_size, available_space, run_mode, .. } = inputs;
 
     // Define some general constants we will need for the remainder of the algorithm.
-    let mut constants = compute_constants(tree.get_style(node), known_dimensions, parent_size);
+    let mut constants = compute_constants(tree.get_flexbox_container_style(node), known_dimensions, parent_size);
 
     // 9. Flex Layout Algorithm
 
@@ -269,9 +276,9 @@ fn compute_preliminary(tree: &mut impl LayoutPartialTree, node: NodeId, inputs:
         debug_log!("constants.node_inner_size", dbg:constants.node_inner_size);
 
         // Re-resolve percentage gaps
-        let style = tree.get_style(node);
+        let style = tree.get_flexbox_container_style(node);
         let inner_container_size = constants.inner_container_size.main(constants.dir);
-        let new_gap = style.gap.main(constants.dir).maybe_resolve(inner_container_size).unwrap_or(0.0);
+        let new_gap = style.gap().main(constants.dir).maybe_resolve(inner_container_size).unwrap_or(0.0);
         constants.gap.set_main(constants.dir, new_gap);
     }
 
@@ -359,7 +366,7 @@ fn compute_preliminary(tree: &mut impl LayoutPartialTree, node: NodeId, inputs:
     let len = tree.child_count(node);
     for order in 0..len {
         let child = tree.get_child_id(node, order);
-        if tree.get_style(child).display == Display::None {
+        if tree.get_flexbox_child_style(child).box_generation_mode() == BoxGenerationMode::None {
             tree.set_unrounded_layout(child, &Layout::with_order(order as u32));
             tree.perform_child_layout(
                 child,
@@ -398,32 +405,33 @@ fn compute_preliminary(tree: &mut impl LayoutPartialTree, node: NodeId, inputs:
 /// Compute constants that can be reused during the flexbox algorithm.
 #[inline]
 fn compute_constants(
-    style: &Style,
+    style: impl FlexboxContainerStyle,
     known_dimensions: Size<Option<f32>>,
     parent_size: Size<Option<f32>>,
 ) -> AlgoConstants {
-    let dir = style.flex_direction;
+    let dir = style.flex_direction();
     let is_row = dir.is_row();
     let is_column = dir.is_column();
-    let is_wrap = matches!(style.flex_wrap, FlexWrap::Wrap | FlexWrap::WrapReverse);
-    let is_wrap_reverse = style.flex_wrap == FlexWrap::WrapReverse;
+    let is_wrap = matches!(style.flex_wrap(), FlexWrap::Wrap | FlexWrap::WrapReverse);
+    let is_wrap_reverse = style.flex_wrap() == FlexWrap::WrapReverse;
 
-    let aspect_ratio = style.aspect_ratio;
-    let margin = style.margin.resolve_or_zero(parent_size.width);
-    let padding = style.padding.resolve_or_zero(parent_size.width);
-    let border = style.border.resolve_or_zero(parent_size.width);
+    let aspect_ratio = style.aspect_ratio();
+    let margin = style.margin().resolve_or_zero(parent_size.width);
+    let padding = style.padding().resolve_or_zero(parent_size.width);
+    let border = style.border().resolve_or_zero(parent_size.width);
     let padding_border_sum = padding.sum_axes() + border.sum_axes();
-    let box_sizing_adjustment = if style.box_sizing == BoxSizing::ContentBox { padding_border_sum } else { Size::ZERO };
+    let box_sizing_adjustment =
+        if style.box_sizing() == BoxSizing::ContentBox { padding_border_sum } else { Size::ZERO };
 
-    let align_items = style.align_items.unwrap_or(AlignItems::Stretch);
-    let align_content = style.align_content.unwrap_or(AlignContent::Stretch);
-    let justify_content = style.justify_content;
+    let align_items = style.align_items().unwrap_or(AlignItems::Stretch);
+    let align_content = style.align_content().unwrap_or(AlignContent::Stretch);
+    let justify_content = style.justify_content();
 
     // Scrollbar gutters are reserved when the `overflow` property is set to `Overflow::Scroll`.
     // However, the axis are switched (transposed) because a node that scrolls vertically needs
     // *horizontal* space to be reserved for a scrollbar
-    let scrollbar_gutter = style.overflow.transpose().map(|overflow| match overflow {
-        Overflow::Scroll => style.scrollbar_width,
+    let scrollbar_gutter = style.overflow().transpose().map(|overflow| match overflow {
+        Overflow::Scroll => style.scrollbar_width(),
         _ => 0.0,
     });
     // TODO: make side configurable based on the `direction` property
@@ -433,7 +441,7 @@ fn compute_constants(
 
     let node_outer_size = known_dimensions;
     let node_inner_size = node_outer_size.maybe_sub(content_box_inset.sum_axes());
-    let gap = style.gap.resolve_or_zero(node_inner_size.or(Size::zero()));
+    let gap = style.gap().resolve_or_zero(node_inner_size.or(Size::zero()));
 
     let container_size = Size::zero();
     let inner_container_size = Size::zero();
@@ -445,12 +453,12 @@ fn compute_constants(
         is_wrap,
         is_wrap_reverse,
         min_size: style
-            .min_size
+            .min_size()
             .maybe_resolve(parent_size)
             .maybe_apply_aspect_ratio(aspect_ratio)
             .maybe_add(box_sizing_adjustment),
         max_size: style
-            .max_size
+            .max_size()
             .maybe_resolve(parent_size)
             .maybe_apply_aspect_ratio(aspect_ratio)
             .maybe_add(box_sizing_adjustment),
@@ -476,51 +484,51 @@ fn compute_constants(
 /// - [**Generate anonymous flex items**](https://www.w3.org/TR/css-flexbox-1/#algo-anon-box) as described in [§4 Flex Items](https://www.w3.org/TR/css-flexbox-1/#flex-items).
 #[inline]
 fn generate_anonymous_flex_items(
-    tree: &impl LayoutPartialTree,
+    tree: &impl LayoutFlexboxContainer,
     node: NodeId,
     constants: &AlgoConstants,
 ) -> Vec<FlexItem> {
     tree.child_ids(node)
         .enumerate()
-        .map(|(index, child)| (index, child, tree.get_style(child)))
-        .filter(|(_, _, style)| style.position != Position::Absolute)
-        .filter(|(_, _, style)| style.display != Display::None)
+        .map(|(index, child)| (index, child, tree.get_flexbox_child_style(child)))
+        .filter(|(_, _, style)| style.position() != Position::Absolute)
+        .filter(|(_, _, style)| style.box_generation_mode() != BoxGenerationMode::None)
         .map(|(index, child, child_style)| {
-            let aspect_ratio = child_style.aspect_ratio;
-            let padding = child_style.padding.resolve_or_zero(constants.node_inner_size.width);
-            let border = child_style.border.resolve_or_zero(constants.node_inner_size.width);
+            let aspect_ratio = child_style.aspect_ratio();
+            let padding = child_style.padding().resolve_or_zero(constants.node_inner_size.width);
+            let border = child_style.border().resolve_or_zero(constants.node_inner_size.width);
             let pb_sum = (padding + border).sum_axes();
             let box_sizing_adjustment =
-                if child_style.box_sizing == BoxSizing::ContentBox { pb_sum } else { Size::ZERO };
+                if child_style.box_sizing() == BoxSizing::ContentBox { pb_sum } else { Size::ZERO };
             FlexItem {
                 node: child,
                 order: index as u32,
                 size: child_style
-                    .size
+                    .size()
                     .maybe_resolve(constants.node_inner_size)
                     .maybe_apply_aspect_ratio(aspect_ratio)
                     .maybe_add(box_sizing_adjustment),
                 min_size: child_style
-                    .min_size
+                    .min_size()
                     .maybe_resolve(constants.node_inner_size)
                     .maybe_apply_aspect_ratio(aspect_ratio)
                     .maybe_add(box_sizing_adjustment),
                 max_size: child_style
-                    .max_size
+                    .max_size()
                     .maybe_resolve(constants.node_inner_size)
                     .maybe_apply_aspect_ratio(aspect_ratio)
                     .maybe_add(box_sizing_adjustment),
 
-                inset: child_style.inset.zip_size(constants.node_inner_size, |p, s| p.maybe_resolve(s)),
-                margin: child_style.margin.resolve_or_zero(constants.node_inner_size.width),
-                margin_is_auto: child_style.margin.map(|m| m == LengthPercentageAuto::Auto),
-                padding: child_style.padding.resolve_or_zero(constants.node_inner_size.width),
-                border: child_style.border.resolve_or_zero(constants.node_inner_size.width),
-                align_self: child_style.align_self.unwrap_or(constants.align_items),
-                overflow: child_style.overflow,
-                scrollbar_width: child_style.scrollbar_width,
-                flex_grow: child_style.flex_grow,
-                flex_shrink: child_style.flex_shrink,
+                inset: child_style.inset().zip_size(constants.node_inner_size, |p, s| p.maybe_resolve(s)),
+                margin: child_style.margin().resolve_or_zero(constants.node_inner_size.width),
+                margin_is_auto: child_style.margin().map(|m| m == LengthPercentageAuto::Auto),
+                padding: child_style.padding().resolve_or_zero(constants.node_inner_size.width),
+                border: child_style.border().resolve_or_zero(constants.node_inner_size.width),
+                align_self: child_style.align_self().unwrap_or(constants.align_items),
+                overflow: child_style.overflow(),
+                scrollbar_width: child_style.scrollbar_width(),
+                flex_grow: child_style.flex_grow(),
+                flex_shrink: child_style.flex_shrink(),
                 flex_basis: 0.0,
                 inner_flex_basis: 0.0,
                 violation: 0.0,
@@ -608,7 +616,7 @@ fn determine_available_space(
 ///     (For example, an item with a specified size of zero, positive padding, and box-sizing: border-box will have an outer flex base size of zero—and hence a negative inner flex base size.)
 #[inline]
 fn determine_flex_base_size(
-    tree: &mut impl LayoutPartialTree,
+    tree: &mut impl LayoutFlexboxContainer,
     constants: &AlgoConstants,
     available_space: Size<AvailableSpace>,
     flex_items: &mut [FlexItem],
@@ -616,7 +624,7 @@ fn determine_flex_base_size(
     let dir = constants.dir;
 
     for child in flex_items.iter_mut() {
-        let child_style = tree.get_style(child.node);
+        let child_style = tree.get_flexbox_child_style(child.node);
 
         // Parent size for child sizing
         let cross_axis_parent_size = constants.node_inner_size.cross(dir);
@@ -643,6 +651,19 @@ fn determine_flex_base_size(
             ckd
         };
 
+        let container_width = constants.node_inner_size.main(dir);
+        let box_sizing_adjustment = if child_style.box_sizing() == BoxSizing::ContentBox {
+            let padding = child_style.padding().resolve_or_zero(container_width);
+            let border = child_style.border().resolve_or_zero(container_width);
+            (padding + border).sum_axes()
+        } else {
+            Size::ZERO
+        }
+        .main(dir);
+        let flex_basis = child_style.flex_basis().maybe_resolve(container_width).maybe_add(box_sizing_adjustment);
+
+        drop(child_style);
+
         child.flex_basis = 'flex_basis: {
             // A. If the item has a definite used flex basis, that’s the flex base size.
 
@@ -653,17 +674,6 @@ fn determine_flex_base_size(
 
             // Note: `child.size` has already been resolved against aspect_ratio in generate_anonymous_flex_items
             // So B will just work here by using main_size without special handling for aspect_ratio
-
-            let container_width = constants.node_inner_size.main(dir);
-            let box_sizing_adjustment = if child_style.box_sizing == BoxSizing::ContentBox {
-                let padding = child_style.padding.resolve_or_zero(container_width);
-                let border = child_style.border.resolve_or_zero(container_width);
-                (padding + border).sum_axes()
-            } else {
-                Size::ZERO
-            }
-            .main(dir);
-            let flex_basis = child_style.flex_basis.maybe_resolve(container_width).maybe_add(box_sizing_adjustment);
             let main_size = child.size.main(dir);
             if let Some(flex_basis) = flex_basis.or(main_size) {
                 break 'flex_basis flex_basis;
@@ -859,7 +869,7 @@ fn collect_flex_lines<'a>(
 
 /// Determine the container's main size (if not already known)
 fn determine_container_main_size(
-    tree: &mut impl LayoutPartialTree,
+    tree: &mut impl LayoutFlexboxContainer,
     available_space: Size<AvailableSpace>,
     lines: &mut [FlexLine<'_>],
     constants: &mut AlgoConstants,
@@ -1280,7 +1290,7 @@ fn resolve_flexible_lengths(line: &mut FlexLine, constants: &AlgoConstants, orig
 ///     by performing layout with the used main size and the available space, treating auto as fit-content.
 #[inline]
 fn determine_hypothetical_cross_size(
-    tree: &mut impl LayoutPartialTree,
+    tree: &mut impl LayoutFlexboxContainer,
     line: &mut FlexLine,
     constants: &AlgoConstants,
     available_space: Size<AvailableSpace>,
@@ -1330,7 +1340,7 @@ fn determine_hypothetical_cross_size(
 /// Calculate the base lines of the children.
 #[inline]
 fn calculate_children_base_lines(
-    tree: &mut impl LayoutPartialTree,
+    tree: &mut impl LayoutFlexboxContainer,
     node_size: Size<Option<f32>>,
     available_space: Size<AvailableSpace>,
     flex_lines: &mut [FlexLine],
@@ -1525,31 +1535,36 @@ fn handle_align_content_stretch(flex_lines: &mut [FlexLine], node_size: Size<Opt
 ///
 ///     **Note that this step does not affect the main size of the flex item, even if it has an intrinsic aspect ratio**.
 #[inline]
-fn determine_used_cross_size(tree: &impl LayoutPartialTree, flex_lines: &mut [FlexLine], constants: &AlgoConstants) {
+fn determine_used_cross_size(
+    tree: &impl LayoutFlexboxContainer,
+    flex_lines: &mut [FlexLine],
+    constants: &AlgoConstants,
+) {
     for line in flex_lines {
         let line_cross_size = line.cross_size;
 
         for child in line.items.iter_mut() {
-            let child_style = tree.get_style(child.node);
+            let child_style = tree.get_flexbox_child_style(child.node);
             child.target_size.set_cross(
                 constants.dir,
                 if child.align_self == AlignSelf::Stretch
                     && !child.margin_is_auto.cross_start(constants.dir)
                     && !child.margin_is_auto.cross_end(constants.dir)
-                    && child_style.size.cross(constants.dir) == Dimension::Auto
+                    && child_style.size().cross(constants.dir) == Dimension::Auto
                 {
                     // For some reason this particular usage of max_width is an exception to the rule that max_width's transfer
                     // using the aspect_ratio (if set). Both Chrome and Firefox agree on this. And reading the spec, it seems like
                     // a reasonable interpretation. Although it seems to me that the spec *should* apply aspect_ratio here.
-
-                    let padding = child_style.padding.resolve_or_zero(constants.node_inner_size);
-                    let border = child_style.border.resolve_or_zero(constants.node_inner_size);
+                    let padding = child_style.padding().resolve_or_zero(constants.node_inner_size);
+                    let border = child_style.border().resolve_or_zero(constants.node_inner_size);
                     let pb_sum = (padding + border).sum_axes();
                     let box_sizing_adjustment =
-                        if child_style.box_sizing == BoxSizing::ContentBox { pb_sum } else { Size::ZERO };
+                        if child_style.box_sizing() == BoxSizing::ContentBox { pb_sum } else { Size::ZERO };
 
-                    let max_size_ignoring_aspect_ratio =
-                        child_style.max_size.maybe_resolve(constants.node_inner_size).maybe_add(box_sizing_adjustment);
+                    let max_size_ignoring_aspect_ratio = child_style
+                        .max_size()
+                        .maybe_resolve(constants.node_inner_size)
+                        .maybe_add(box_sizing_adjustment);
 
                     (line_cross_size - child.margin.cross_axis_sum(constants.dir)).maybe_clamp(
                         child.min_size.cross(constants.dir),
@@ -1807,7 +1822,7 @@ fn align_flex_lines_per_align_content(flex_lines: &mut [FlexLine], constants: &A
 /// Calculates the layout for a flex-item
 #[allow(clippy::too_many_arguments)]
 fn calculate_flex_item(
-    tree: &mut impl LayoutPartialTree,
+    tree: &mut impl LayoutFlexboxContainer,
     item: &mut FlexItem,
     total_offset_main: &mut f32,
     total_offset_cross: f32,
@@ -1889,7 +1904,7 @@ fn calculate_flex_item(
 /// Calculates the layout line
 #[allow(clippy::too_many_arguments)]
 fn calculate_layout_line(
-    tree: &mut impl LayoutPartialTree,
+    tree: &mut impl LayoutFlexboxContainer,
     line: &mut FlexLine,
     total_offset_cross: &mut f32,
     #[cfg(feature = "content_size")] content_size: &mut Size<f32>,
@@ -1939,7 +1954,7 @@ fn calculate_layout_line(
 /// Do a final layout pass and collect the resulting layouts.
 #[inline]
 fn final_layout_pass(
-    tree: &mut impl LayoutPartialTree,
+    tree: &mut impl LayoutFlexboxContainer,
     flex_lines: &mut [FlexLine],
     constants: &AlgoConstants,
 ) -> Size<f32> {
@@ -1984,7 +1999,7 @@ fn final_layout_pass(
 /// Perform absolute layout on all absolutely positioned children.
 #[inline]
 fn perform_absolute_layout_on_absolute_children(
-    tree: &mut impl LayoutPartialTree,
+    tree: &mut impl LayoutFlexboxContainer,
     node: NodeId,
     constants: &AlgoConstants,
 ) -> Size<f32> {
@@ -1998,58 +2013,59 @@ fn perform_absolute_layout_on_absolute_children(
 
     for order in 0..tree.child_count(node) {
         let child = tree.get_child_id(node, order);
-        let child_style = tree.get_style(child);
+        let child_style = tree.get_flexbox_child_style(child);
 
         // Skip items that are display:none or are not position:absolute
-        if child_style.display == Display::None || child_style.position != Position::Absolute {
+        if child_style.box_generation_mode() == BoxGenerationMode::None || child_style.position() != Position::Absolute
+        {
             continue;
         }
 
-        let overflow = child_style.overflow;
-        let scrollbar_width = child_style.scrollbar_width;
-        let aspect_ratio = child_style.aspect_ratio;
-        let align_self = child_style.align_self.unwrap_or(constants.align_items);
-        let margin = child_style.margin.map(|margin| margin.resolve_to_option(container_width));
-        let padding = child_style.padding.resolve_or_zero(Some(container_width));
-        let border = child_style.border.resolve_or_zero(Some(container_width));
+        let overflow = child_style.overflow();
+        let scrollbar_width = child_style.scrollbar_width();
+        let aspect_ratio = child_style.aspect_ratio();
+        let align_self = child_style.align_self().unwrap_or(constants.align_items);
+        let margin = child_style.margin().map(|margin| margin.resolve_to_option(inset_relative_size.width));
+        let padding = child_style.padding().resolve_or_zero(Some(inset_relative_size.width));
+        let border = child_style.border().resolve_or_zero(Some(inset_relative_size.width));
         let padding_border_sum = (padding + border).sum_axes();
         let box_sizing_adjustment =
-            if child_style.box_sizing == BoxSizing::ContentBox { padding_border_sum } else { Size::ZERO };
+            if child_style.box_sizing() == BoxSizing::ContentBox { padding_border_sum } else { Size::ZERO };
 
         // Resolve inset
         // Insets are resolved against the container size minus border
-        let left = child_style.inset.left.maybe_resolve(inset_relative_size.width);
-        let right =
-            child_style.inset.right.maybe_resolve(inset_relative_size.width).maybe_add(constants.scrollbar_gutter.x);
-        let top = child_style.inset.top.maybe_resolve(inset_relative_size.height);
-        let bottom =
-            child_style.inset.bottom.maybe_resolve(inset_relative_size.height).maybe_add(constants.scrollbar_gutter.y);
+        let left = child_style.inset().left.maybe_resolve(inset_relative_size.width);
+        let right = child_style.inset().right.maybe_resolve(inset_relative_size.width);
+        let top = child_style.inset().top.maybe_resolve(inset_relative_size.height);
+        let bottom = child_style.inset().bottom.maybe_resolve(inset_relative_size.height);
 
         // Compute known dimensions from min/max/inherent size styles
         let style_size = child_style
-            .size
+            .size()
             .maybe_resolve(inset_relative_size)
             .maybe_apply_aspect_ratio(aspect_ratio)
             .maybe_add(box_sizing_adjustment);
         let min_size = child_style
-            .min_size
+            .min_size()
             .maybe_resolve(inset_relative_size)
             .maybe_apply_aspect_ratio(aspect_ratio)
             .maybe_add(box_sizing_adjustment)
             .or(padding_border_sum.map(Some))
             .maybe_max(padding_border_sum);
         let max_size = child_style
-            .max_size
+            .max_size()
             .maybe_resolve(inset_relative_size)
             .maybe_apply_aspect_ratio(aspect_ratio)
             .maybe_add(box_sizing_adjustment);
         let mut known_dimensions = style_size.maybe_clamp(min_size, max_size);
 
+        drop(child_style);
+
         // Fill in width from left/right and reapply aspect ratio if:
         //   - Width is not already known
         //   - Item has both left and right inset properties set
         if let (None, Some(left), Some(right)) = (known_dimensions.width, left, right) {
-            let new_width_raw = container_width.maybe_sub(margin.left).maybe_sub(margin.right) - left - right;
+            let new_width_raw = inset_relative_size.width.maybe_sub(margin.left).maybe_sub(margin.right) - left - right;
             known_dimensions.width = Some(f32_max(new_width_raw, 0.0));
             known_dimensions = known_dimensions.maybe_apply_aspect_ratio(aspect_ratio).maybe_clamp(min_size, max_size);
         }
@@ -2058,11 +2074,11 @@ fn perform_absolute_layout_on_absolute_children(
         //   - Height is not already known
         //   - Item has both top and bottom inset properties set
         if let (None, Some(top), Some(bottom)) = (known_dimensions.height, top, bottom) {
-            let new_height_raw = container_height.maybe_sub(margin.top).maybe_sub(margin.bottom) - top - bottom;
+            let new_height_raw =
+                inset_relative_size.height.maybe_sub(margin.top).maybe_sub(margin.bottom) - top - bottom;
             known_dimensions.height = Some(f32_max(new_height_raw, 0.0));
             known_dimensions = known_dimensions.maybe_apply_aspect_ratio(aspect_ratio).maybe_clamp(min_size, max_size);
         }
-
         let layout_output = tree.perform_child_layout(
             child,
             known_dimensions,
@@ -2125,6 +2141,7 @@ fn perform_absolute_layout_on_absolute_children(
         } else if let Some(end) = end_main {
             constants.container_size.main(constants.dir)
                 - constants.border.main_end(constants.dir)
+                - constants.scrollbar_gutter.main(constants.dir)
                 - final_size.main(constants.dir)
                 - end
                 - resolved_margin.main_end(constants.dir)
@@ -2167,6 +2184,7 @@ fn perform_absolute_layout_on_absolute_children(
         } else if let Some(end) = end_cross {
             constants.container_size.cross(constants.dir)
                 - constants.border.cross_end(constants.dir)
+                - constants.scrollbar_gutter.cross(constants.dir)
                 - final_size.cross(constants.dir)
                 - end
                 - resolved_margin.cross_end(constants.dir)
diff --git a/src/compute/grid/alignment.rs b/src/compute/grid/alignment.rs
index 3cec376d6..aeeed4424 100644
--- a/src/compute/grid/alignment.rs
+++ b/src/compute/grid/alignment.rs
@@ -2,14 +2,14 @@
 use super::types::GridTrack;
 use crate::compute::common::alignment::{apply_alignment_fallback, compute_alignment_offset};
 use crate::geometry::{InBothAbsAxis, Line, Point, Rect, Size};
-use crate::style::{AlignContent, AlignItems, AlignSelf, AvailableSpace, Overflow, Position};
-use crate::tree::{Layout, LayoutPartialTree, LayoutPartialTreeExt, NodeId, SizingMode};
+use crate::style::{AlignContent, AlignItems, AlignSelf, AvailableSpace, CoreStyle, GridItemStyle, Overflow, Position};
+use crate::tree::{Layout, LayoutPartialTreeExt, NodeId, SizingMode};
 use crate::util::sys::f32_max;
 use crate::util::{MaybeMath, MaybeResolve, ResolveOrZero};
 
 #[cfg(feature = "content_size")]
 use crate::compute::common::content_size::compute_content_size_contribution;
-use crate::BoxSizing;
+use crate::{BoxSizing, LayoutGridContainer};
 
 /// Align the grid tracks within the grid according to the align-content (rows) or
 /// justify-content (columns) property. This only does anything if the size of the
@@ -57,7 +57,7 @@ pub(super) fn align_tracks(
 
 /// Align and size a grid item into it's final position
 pub(super) fn align_and_position_item(
-    tree: &mut impl LayoutPartialTree,
+    tree: &mut impl LayoutGridContainer,
     node: NodeId,
     order: u32,
     grid_area: Rect<f32>,
@@ -66,36 +66,39 @@ pub(super) fn align_and_position_item(
 ) -> (Size<f32>, f32, f32) {
     let grid_area_size = Size { width: grid_area.right - grid_area.left, height: grid_area.bottom - grid_area.top };
 
-    let style = tree.get_style(node);
+    let style = tree.get_grid_child_style(node);
 
-    let overflow = style.overflow;
-    let scrollbar_width = style.scrollbar_width;
-    let aspect_ratio = style.aspect_ratio;
-    let justify_self = style.justify_self;
-    let align_self = style.align_self;
+    let overflow = style.overflow();
+    let scrollbar_width = style.scrollbar_width();
+    let aspect_ratio = style.aspect_ratio();
+    let justify_self = style.justify_self();
+    let align_self = style.align_self();
 
-    let position = style.position;
-    let inset_horizontal = style.inset.horizontal_components().map(|size| size.resolve_to_option(grid_area_size.width));
-    let inset_vertical = style.inset.vertical_components().map(|size| size.resolve_to_option(grid_area_size.height));
-    let padding = style.padding.map(|p| p.resolve_or_zero(Some(grid_area_size.width)));
-    let border = style.border.map(|p| p.resolve_or_zero(Some(grid_area_size.width)));
+    let position = style.position();
+    let inset_horizontal =
+        style.inset().horizontal_components().map(|size| size.resolve_to_option(grid_area_size.width));
+    let inset_vertical = style.inset().vertical_components().map(|size| size.resolve_to_option(grid_area_size.height));
+    let padding = style.padding().map(|p| p.resolve_or_zero(Some(grid_area_size.width)));
+    let border = style.border().map(|p| p.resolve_or_zero(Some(grid_area_size.width)));
     let padding_border_size = (padding + border).sum_axes();
+
     let box_sizing_adjustment =
-        if style.box_sizing == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO };
+        if style.box_sizing() == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO };
+
     let inherent_size = style
-        .size
+        .size()
         .maybe_resolve(grid_area_size)
         .maybe_apply_aspect_ratio(aspect_ratio)
         .maybe_add(box_sizing_adjustment);
     let min_size = style
-        .min_size
+        .min_size()
         .maybe_resolve(grid_area_size)
         .maybe_add(box_sizing_adjustment)
         .or(padding_border_size.map(Some))
         .maybe_max(padding_border_size)
         .maybe_apply_aspect_ratio(aspect_ratio);
     let max_size = style
-        .max_size
+        .max_size()
         .maybe_resolve(grid_area_size)
         .maybe_apply_aspect_ratio(aspect_ratio)
         .maybe_add(box_sizing_adjustment);
@@ -123,7 +126,7 @@ pub(super) fn align_and_position_item(
 
     // Note: This is not a bug. It is part of the CSS spec that both horizontal and vertical margins
     // resolve against the WIDTH of the grid area.
-    let margin = style.margin.map(|margin| margin.resolve_to_option(grid_area_size.width));
+    let margin = style.margin().map(|margin| margin.resolve_to_option(grid_area_size.width));
 
     let grid_area_minus_item_margins_size = Size {
         width: grid_area_size.width.maybe_sub(margin.left).maybe_sub(margin.right),
@@ -187,6 +190,7 @@ pub(super) fn align_and_position_item(
     let Size { width, height } = Size { width, height }.maybe_clamp(min_size, max_size);
 
     // Layout node
+    drop(style);
     let layout_output = tree.perform_child_layout(
         node,
         Size { width, height },
diff --git a/src/compute/grid/explicit_grid.rs b/src/compute/grid/explicit_grid.rs
index dbc450627..793aa2477 100644
--- a/src/compute/grid/explicit_grid.rs
+++ b/src/compute/grid/explicit_grid.rs
@@ -2,18 +2,19 @@
 //! This mainly consists of evaluating GridAutoTracks
 use super::types::{GridTrack, TrackCounts};
 use crate::geometry::{AbsoluteAxis, Size};
-use crate::style::{GridTrackRepetition, LengthPercentage, NonRepeatedTrackSizingFunction, Style, TrackSizingFunction};
+use crate::style::{GridTrackRepetition, LengthPercentage, NonRepeatedTrackSizingFunction, TrackSizingFunction};
 use crate::style_helpers::TaffyAuto;
-use crate::util::sys::{GridTrackVec, Vec};
+use crate::util::sys::Vec;
 use crate::util::MaybeMath;
 use crate::util::ResolveOrZero;
+use crate::GridContainerStyle;
 
 #[cfg(not(feature = "std"))]
 use num_traits::float::FloatCore;
 
 /// Compute the number of rows and columns in the explicit grid
 pub(crate) fn compute_explicit_grid_size_in_axis(
-    style: &Style,
+    style: &impl GridContainerStyle,
     inner_container_size: Size<Option<f32>>,
     axis: AbsoluteAxis,
 ) -> u16 {
@@ -93,7 +94,7 @@ pub(crate) fn compute_explicit_grid_size_in_axis(
     //   - then the number of repetitions is the smallest possible positive integer that fulfills that minimum requirement
     // Otherwise, the specified track list repeats only once.
     let size_is_maximum =
-        style.size.get_abs(axis).into_option().is_some() || style.max_size.get_abs(axis).into_option().is_some();
+        style.size().get_abs(axis).into_option().is_some() || style.max_size().get_abs(axis).into_option().is_some();
 
     // Determine the number of repetitions
     let num_repetitions: u16 = match inner_container_size.get_abs(axis) {
@@ -128,7 +129,7 @@ pub(crate) fn compute_explicit_grid_size_in_axis(
                     }
                 })
                 .sum();
-            let gap_size = style.gap.get_abs(axis).resolve_or_zero(Some(inner_container_size));
+            let gap_size = style.gap().get_abs(axis).resolve_or_zero(Some(inner_container_size));
 
             // Compute the amount of space that a single repetition of the repeated track list takes
             let per_repetition_track_used_space: f32 = repetition_definition
@@ -176,7 +177,7 @@ pub(crate) fn compute_explicit_grid_size_in_axis(
 pub(super) fn initialize_grid_tracks(
     tracks: &mut Vec<GridTrack>,
     counts: TrackCounts,
-    track_template: &GridTrackVec<TrackSizingFunction>,
+    track_template: &[TrackSizingFunction],
     auto_tracks: &[NonRepeatedTrackSizingFunction],
     gap: LengthPercentage,
     track_has_items: impl Fn(usize) -> bool,
diff --git a/src/compute/grid/implicit_grid.rs b/src/compute/grid/implicit_grid.rs
index 1836cbb5b..d1651218b 100644
--- a/src/compute/grid/implicit_grid.rs
+++ b/src/compute/grid/implicit_grid.rs
@@ -1,7 +1,8 @@
 //! This module is not required for spec compliance, but is used as a performance optimisation
 //! to reduce the number of allocations required when creating a grid.
 use crate::geometry::Line;
-use crate::style::{GenericGridPlacement, GridPlacement, Style};
+use crate::style::{GenericGridPlacement, GridPlacement};
+use crate::GridItemStyle;
 use core::cmp::{max, min};
 
 use super::types::TrackCounts;
@@ -15,10 +16,10 @@ use super::OriginZeroLine;
 ///     in ways which are impossible to predict until the auto-placement algorithm is run.
 ///
 /// Note that this function internally mixes use of grid track numbers and grid line numbers
-pub(crate) fn compute_grid_size_estimate<'a>(
+pub(crate) fn compute_grid_size_estimate<'a, S: GridItemStyle + 'a>(
     explicit_col_count: u16,
     explicit_row_count: u16,
-    child_styles_iter: impl Iterator<Item = &'a Style>,
+    child_styles_iter: impl Iterator<Item = S>,
 ) -> (TrackCounts, TrackCounts) {
     // Iterate over children, producing an estimate of the min and max grid lines (in origin-zero coordinates where)
     // along with the span of each item
@@ -60,20 +61,20 @@ pub(crate) fn compute_grid_size_estimate<'a>(
 ///
 /// Min and max grid lines are returned in origin-zero coordinates)
 /// The span is measured in tracks spanned
-fn get_known_child_positions<'a>(
-    children_iter: impl Iterator<Item = &'a Style>,
+fn get_known_child_positions<'a, S: GridItemStyle + 'a>(
+    children_iter: impl Iterator<Item = S>,
     explicit_col_count: u16,
     explicit_row_count: u16,
 ) -> (OriginZeroLine, OriginZeroLine, u16, OriginZeroLine, OriginZeroLine, u16) {
     let (mut col_min, mut col_max, mut col_max_span) = (OriginZeroLine(0), OriginZeroLine(0), 0);
     let (mut row_min, mut row_max, mut row_max_span) = (OriginZeroLine(0), OriginZeroLine(0), 0);
-    children_iter.for_each(|child_style: &Style| {
+    children_iter.for_each(|child_style| {
         // Note: that the children reference the lines in between (and around) the tracks not tracks themselves,
         // and thus we must subtract 1 to get an accurate estimate of the number of tracks
         let (child_col_min, child_col_max, child_col_span) =
-            child_min_line_max_line_span(child_style.grid_column, explicit_col_count);
+            child_min_line_max_line_span(child_style.grid_column(), explicit_col_count);
         let (child_row_min, child_row_max, child_row_span) =
-            child_min_line_max_line_span(child_style.grid_row, explicit_row_count);
+            child_min_line_max_line_span(child_style.grid_row(), explicit_row_count);
         col_min = min(col_min, child_col_min);
         col_max = max(col_max, child_col_max);
         col_max_span = max(col_max_span, child_col_span);
diff --git a/src/compute/grid/mod.rs b/src/compute/grid/mod.rs
index 2fc02e5bf..c15c98e7b 100644
--- a/src/compute/grid/mod.rs
+++ b/src/compute/grid/mod.rs
@@ -2,14 +2,16 @@
 //! <https://www.w3.org/TR/css-grid-1>
 use crate::geometry::{AbsoluteAxis, AbstractAxis, InBothAbsAxis};
 use crate::geometry::{Line, Point, Rect, Size};
-use crate::style::{AlignContent, AlignItems, AlignSelf, AvailableSpace, Display, Overflow, Position};
-use crate::tree::{Layout, LayoutInput, LayoutOutput, RunMode, SizingMode};
-use crate::tree::{LayoutPartialTree, LayoutPartialTreeExt, NodeId};
+use crate::style::{AlignItems, AlignSelf, AvailableSpace, Overflow, Position};
+use crate::tree::{Layout, LayoutInput, LayoutOutput, LayoutPartialTreeExt, NodeId, RunMode, SizingMode};
 use crate::util::debug::debug_log;
 use crate::util::sys::{f32_max, GridTrackVec, Vec};
 use crate::util::MaybeMath;
 use crate::util::{MaybeResolve, ResolveOrZero};
-use crate::{style_helpers::*, BoxSizing};
+use crate::{
+    style_helpers::*, AlignContent, BoxGenerationMode, BoxSizing, CoreStyle, GridContainerStyle, GridItemStyle,
+    JustifyContent, LayoutGridContainer,
+};
 use alignment::{align_and_position_item, align_tracks};
 use explicit_grid::{compute_explicit_grid_size_in_axis, initialize_grid_tracks};
 use implicit_grid::compute_grid_size_estimate;
@@ -35,36 +37,36 @@ mod util;
 ///   - Placing items (which also resolves the implicit grid)
 ///   - Track (row/column) sizing
 ///   - Alignment & Final item placement
-pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inputs: LayoutInput) -> LayoutOutput {
+pub fn compute_grid_layout(tree: &mut impl LayoutGridContainer, node: NodeId, inputs: LayoutInput) -> LayoutOutput {
     let LayoutInput { known_dimensions, parent_size, available_space, run_mode, .. } = inputs;
 
-    let style = tree.get_style(node).clone();
+    let style = tree.get_grid_container_style(node);
 
     // 1. Compute "available grid space"
     // https://www.w3.org/TR/css-grid-1/#available-grid-space
-    let aspect_ratio = style.aspect_ratio;
-    let padding = style.padding.resolve_or_zero(parent_size.width);
-    let border = style.border.resolve_or_zero(parent_size.width);
+    let aspect_ratio = style.aspect_ratio();
+    let padding = style.padding().resolve_or_zero(parent_size.width);
+    let border = style.border().resolve_or_zero(parent_size.width);
     let padding_border = padding + border;
     let padding_border_size = padding_border.sum_axes();
     let box_sizing_adjustment =
-        if style.box_sizing == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO };
+        if style.box_sizing() == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO };
 
     let min_size = style
-        .min_size
+        .min_size()
         .maybe_resolve(parent_size)
         .maybe_apply_aspect_ratio(aspect_ratio)
         .maybe_add(box_sizing_adjustment);
     let max_size = style
-        .max_size
+        .max_size()
         .maybe_resolve(parent_size)
         .maybe_apply_aspect_ratio(aspect_ratio)
         .maybe_add(box_sizing_adjustment);
     let preferred_size = if inputs.sizing_mode == SizingMode::InherentSize {
         style
-            .size
+            .size()
             .maybe_resolve(parent_size)
-            .maybe_apply_aspect_ratio(style.aspect_ratio)
+            .maybe_apply_aspect_ratio(style.aspect_ratio())
             .maybe_add(box_sizing_adjustment)
     } else {
         Size::NONE
@@ -73,8 +75,8 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu
     // Scrollbar gutters are reserved when the `overflow` property is set to `Overflow::Scroll`.
     // However, the axis are switched (transposed) because a node that scrolls vertically needs
     // *horizontal* space to be reserved for a scrollbar
-    let scrollbar_gutter = style.overflow.transpose().map(|overflow| match overflow {
-        Overflow::Scroll => style.scrollbar_width,
+    let scrollbar_gutter = style.overflow().transpose().map(|overflow| match overflow {
+        Overflow::Scroll => style.scrollbar_width(),
         _ => 0.0,
     });
     // TODO: make side configurable based on the `direction` property
@@ -82,6 +84,11 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu
     content_box_inset.right += scrollbar_gutter.x;
     content_box_inset.bottom += scrollbar_gutter.y;
 
+    let align_content = style.align_content().unwrap_or(AlignContent::Stretch);
+    let justify_content = style.justify_content().unwrap_or(JustifyContent::Stretch);
+    let align_items = style.align_items();
+    let justify_items = style.justify_items();
+
     let constrained_available_space = known_dimensions
         .or(preferred_size)
         .map(|size| size.map(AvailableSpace::Definite))
@@ -114,7 +121,8 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu
         return LayoutOutput::from_outer_size(Size { width, height });
     }
 
-    let get_child_styles_iter = |node| tree.child_ids(node).map(|child_node: NodeId| tree.get_style(child_node));
+    let get_child_styles_iter =
+        |node| tree.child_ids(node).map(|child_node: NodeId| tree.get_grid_child_style(child_node));
     let child_styles_iter = get_child_styles_iter(node);
 
     // 2. Resolve the explicit grid
@@ -147,16 +155,18 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu
     let in_flow_children_iter = || {
         tree.child_ids(node)
             .enumerate()
-            .map(|(index, child_node)| (index, child_node, tree.get_style(child_node)))
-            .filter(|(_, _, style)| style.display != Display::None && style.position != Position::Absolute)
+            .map(|(index, child_node)| (index, child_node, tree.get_grid_child_style(child_node)))
+            .filter(|(_, _, style)| {
+                style.box_generation_mode() != BoxGenerationMode::None && style.position() != Position::Absolute
+            })
     };
     place_grid_items(
         &mut cell_occupancy_matrix,
         &mut items,
         in_flow_children_iter,
-        style.grid_auto_flow,
-        style.align_items.unwrap_or(AlignItems::Stretch),
-        style.justify_items.unwrap_or(AlignItems::Stretch),
+        style.grid_auto_flow(),
+        align_items.unwrap_or(AlignItems::Stretch),
+        justify_items.unwrap_or(AlignItems::Stretch),
     );
 
     // Extract track counts from previous step (auto-placement can expand the number of tracks)
@@ -171,20 +181,22 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu
     initialize_grid_tracks(
         &mut columns,
         final_col_counts,
-        &style.grid_template_columns,
-        &style.grid_auto_columns,
-        style.gap.width,
+        style.grid_template_columns(),
+        style.grid_auto_columns(),
+        style.gap().width,
         |column_index| cell_occupancy_matrix.column_is_occupied(column_index),
     );
     initialize_grid_tracks(
         &mut rows,
         final_row_counts,
-        &style.grid_template_rows,
-        &style.grid_auto_rows,
-        style.gap.height,
+        style.grid_template_rows(),
+        style.grid_auto_rows(),
+        style.gap().height,
         |row_index| cell_occupancy_matrix.row_is_occupied(row_index),
     );
 
+    drop(style);
+
     // 6. Track Sizing
 
     // Convert grid placements in origin-zero coordinates to indexes into the GridTrack (rows and columns) vectors
@@ -205,7 +217,7 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu
         AbstractAxis::Inline,
         min_size.get(AbstractAxis::Inline),
         max_size.get(AbstractAxis::Inline),
-        style.grid_align_content(AbstractAxis::Block),
+        align_content,
         available_grid_space,
         inner_node_size,
         &mut columns,
@@ -225,7 +237,7 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu
         AbstractAxis::Block,
         min_size.get(AbstractAxis::Block),
         max_size.get(AbstractAxis::Block),
-        style.grid_align_content(AbstractAxis::Inline),
+        justify_content,
         available_grid_space,
         inner_node_size,
         &mut rows,
@@ -338,7 +350,7 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu
             AbstractAxis::Inline,
             min_size.get(AbstractAxis::Inline),
             max_size.get(AbstractAxis::Inline),
-            style.grid_align_content(AbstractAxis::Block),
+            align_content,
             available_grid_space,
             inner_node_size,
             &mut columns,
@@ -400,7 +412,7 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu
                 AbstractAxis::Block,
                 min_size.get(AbstractAxis::Block),
                 max_size.get(AbstractAxis::Block),
-                style.grid_align_content(AbstractAxis::Inline),
+                justify_content,
                 available_grid_space,
                 inner_node_size,
                 &mut rows,
@@ -420,7 +432,7 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu
         Line { start: padding.left, end: padding.right },
         Line { start: border.left, end: border.right },
         &mut columns,
-        style.justify_content.unwrap_or(AlignContent::Stretch),
+        justify_content,
     );
     // Align rows
     align_tracks(
@@ -428,7 +440,7 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu
         Line { start: padding.top, end: padding.bottom },
         Line { start: border.top, end: border.bottom },
         &mut rows,
-        style.align_content.unwrap_or(AlignContent::Stretch),
+        align_content,
     );
 
     // 9. Size, Align, and Position Grid Items
@@ -439,7 +451,7 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu
     // Sort items back into original order to allow them to be matched up with styles
     items.sort_by_key(|item| item.source_order);
 
-    let container_alignment_styles = InBothAbsAxis { horizontal: style.justify_items, vertical: style.align_items };
+    let container_alignment_styles = InBothAbsAxis { horizontal: justify_items, vertical: align_items };
 
     // Position in-flow children (stored in items vector)
     for (index, item) in items.iter_mut().enumerate() {
@@ -471,10 +483,11 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu
     let mut order = items.len() as u32;
     (0..tree.child_count(node)).for_each(|index| {
         let child = tree.get_child_id(node, index);
-        let child_style = tree.get_style(child);
+        let child_style = tree.get_grid_child_style(child);
 
         // Position hidden child
-        if child_style.display == Display::None {
+        if child_style.box_generation_mode() == BoxGenerationMode::None {
+            drop(child_style);
             tree.set_unrounded_layout(child, &Layout::with_order(order));
             tree.perform_child_layout(
                 child,
@@ -489,11 +502,11 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu
         }
 
         // Position absolutely positioned child
-        if child_style.position == Position::Absolute {
+        if child_style.position() == Position::Absolute {
             // Convert grid-col-{start/end} into Option's of indexes into the columns vector
             // The Option is None if the style property is Auto and an unresolvable Span
             let maybe_col_indexes = child_style
-                .grid_column
+                .grid_column()
                 .into_origin_zero(final_col_counts.explicit)
                 .resolve_absolutely_positioned_grid_tracks()
                 .map(|maybe_grid_line| {
@@ -502,7 +515,7 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu
             // Convert grid-row-{start/end} into Option's of indexes into the row vector
             // The Option is None if the style property is Auto and an unresolvable Span
             let maybe_row_indexes = child_style
-                .grid_row
+                .grid_row()
                 .into_origin_zero(final_row_counts.explicit)
                 .resolve_absolutely_positioned_grid_tracks()
                 .map(|maybe_grid_line| {
@@ -521,6 +534,8 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu
                     .map(|index| columns[index].offset)
                     .unwrap_or(container_border_box.width - border.right - scrollbar_gutter.x),
             };
+            drop(child_style);
+
             // TODO: Baseline alignment support for absolutely positioned items (should check if is actuallty specified)
             #[cfg_attr(not(feature = "content_size"), allow(unused_variables))]
             let (content_size_contribution, _, _) =
diff --git a/src/compute/grid/placement.rs b/src/compute/grid/placement.rs
index bd760accb..695ac0291 100644
--- a/src/compute/grid/placement.rs
+++ b/src/compute/grid/placement.rs
@@ -4,15 +4,16 @@ use super::types::{CellOccupancyMatrix, CellOccupancyState, GridItem};
 use super::OriginZeroLine;
 use crate::geometry::Line;
 use crate::geometry::{AbsoluteAxis, InBothAbsAxis};
-use crate::style::{AlignItems, GridAutoFlow, OriginZeroGridPlacement, Style};
+use crate::style::{AlignItems, GridAutoFlow, OriginZeroGridPlacement};
 use crate::tree::NodeId;
 use crate::util::sys::Vec;
+use crate::GridItemStyle;
 
 /// 8.5. Grid Item Placement Algorithm
 /// Place items into the grid, generating new rows/column into the implicit grid as required
 ///
 /// [Specification](https://www.w3.org/TR/css-grid-2/#auto-placement-algo)
-pub(super) fn place_grid_items<'a, ChildIter>(
+pub(super) fn place_grid_items<'a, S, ChildIter>(
     cell_occupancy_matrix: &mut CellOccupancyMatrix,
     items: &mut Vec<GridItem>,
     children_iter: impl Fn() -> ChildIter,
@@ -20,7 +21,8 @@ pub(super) fn place_grid_items<'a, ChildIter>(
     align_items: AlignItems,
     justify_items: AlignItems,
 ) where
-    ChildIter: Iterator<Item = (usize, NodeId, &'a Style)>,
+    S: GridItemStyle + 'a,
+    ChildIter: Iterator<Item = (usize, NodeId, S)>,
 {
     let primary_axis = grid_auto_flow.primary_axis();
     let secondary_axis = primary_axis.other_axis();
@@ -28,10 +30,12 @@ pub(super) fn place_grid_items<'a, ChildIter>(
     let map_child_style_to_origin_zero_placement = {
         let explicit_col_count = cell_occupancy_matrix.track_counts(AbsoluteAxis::Horizontal).explicit;
         let explicit_row_count = cell_occupancy_matrix.track_counts(AbsoluteAxis::Vertical).explicit;
-        move |(index, node, style): (usize, NodeId, &'a Style)| -> (_, _, _, &'a Style) {
+        move |(index, node, style): (usize, NodeId, S)| -> (_, _, _, S) {
             let origin_zero_placement = InBothAbsAxis {
-                horizontal: style.grid_column.map(|placement| placement.into_origin_zero_placement(explicit_col_count)),
-                vertical: style.grid_row.map(|placement| placement.into_origin_zero_placement(explicit_row_count)),
+                horizontal: style
+                    .grid_column()
+                    .map(|placement| placement.into_origin_zero_placement(explicit_col_count)),
+                vertical: style.grid_row().map(|placement| placement.into_origin_zero_placement(explicit_row_count)),
             };
             (index, node, origin_zero_placement, style)
         }
@@ -40,7 +44,7 @@ pub(super) fn place_grid_items<'a, ChildIter>(
     // 1. Place children with definite positions
     let mut idx = 0;
     children_iter()
-        .filter(|(_, _, child_style)| child_style.grid_row.is_definite() && child_style.grid_column.is_definite())
+        .filter(|(_, _, child_style)| child_style.grid_row().is_definite() && child_style.grid_column().is_definite())
         .map(map_child_style_to_origin_zero_placement)
         .for_each(|(index, child_node, child_placement, style)| {
             idx += 1;
@@ -296,12 +300,12 @@ fn place_indefinitely_positioned_item(
 /// Record the grid item in both CellOccupancyMatric and the GridItems list
 /// once a definite placement has been determined
 #[allow(clippy::too_many_arguments)]
-fn record_grid_placement(
+fn record_grid_placement<S: GridItemStyle>(
     cell_occupancy_matrix: &mut CellOccupancyMatrix,
     items: &mut Vec<GridItem>,
     node: NodeId,
     index: usize,
-    style: &Style,
+    style: S,
     parent_align_items: AlignItems,
     parent_justify_items: AlignItems,
     primary_axis: AbsoluteAxis,
diff --git a/src/compute/grid/types/grid_item.rs b/src/compute/grid/types/grid_item.rs
index b77f7de02..3df863d82 100644
--- a/src/compute/grid/types/grid_item.rs
+++ b/src/compute/grid/types/grid_item.rs
@@ -5,11 +5,11 @@ use crate::geometry::AbstractAxis;
 use crate::geometry::{Line, Point, Rect, Size};
 use crate::style::{
     AlignItems, AlignSelf, AvailableSpace, Dimension, LengthPercentageAuto, MaxTrackSizingFunction,
-    MinTrackSizingFunction, Overflow, Style,
+    MinTrackSizingFunction, Overflow,
 };
 use crate::tree::{LayoutPartialTree, LayoutPartialTreeExt, NodeId, SizingMode};
 use crate::util::{MaybeMath, MaybeResolve, ResolveOrZero};
-use crate::{BoxSizing, LengthPercentage};
+use crate::{BoxSizing, GridItemStyle, LengthPercentage};
 use core::ops::Range;
 
 /// Represents a single grid item
@@ -93,11 +93,11 @@ pub(in super::super) struct GridItem {
 
 impl GridItem {
     /// Create a new item given a concrete placement in both axes
-    pub fn new_with_placement_style_and_order(
+    pub fn new_with_placement_style_and_order<S: GridItemStyle>(
         node: NodeId,
         col_span: Line<OriginZeroLine>,
         row_span: Line<OriginZeroLine>,
-        style: &Style,
+        style: S,
         parent_align_items: AlignItems,
         parent_justify_items: AlignItems,
         source_order: u16,
@@ -107,17 +107,17 @@ impl GridItem {
             source_order,
             row: row_span,
             column: col_span,
-            overflow: style.overflow,
-            box_sizing: style.box_sizing,
-            size: style.size,
-            min_size: style.min_size,
-            max_size: style.max_size,
-            aspect_ratio: style.aspect_ratio,
-            padding: style.padding,
-            border: style.border,
-            margin: style.margin,
-            align_self: style.align_self.unwrap_or(parent_align_items),
-            justify_self: style.justify_self.unwrap_or(parent_justify_items),
+            overflow: style.overflow(),
+            box_sizing: style.box_sizing(),
+            size: style.size(),
+            min_size: style.min_size(),
+            max_size: style.max_size(),
+            aspect_ratio: style.aspect_ratio(),
+            padding: style.padding(),
+            border: style.border(),
+            margin: style.margin(),
+            align_self: style.align_self().unwrap_or(parent_align_items),
+            justify_self: style.justify_self().unwrap_or(parent_justify_items),
             baseline: None,
             baseline_shim: 0.0,
             row_indexes: Line { start: 0, end: 0 }, // Properly initialised later
diff --git a/src/compute/leaf.rs b/src/compute/leaf.rs
index 117d4284f..0c3f5a0af 100644
--- a/src/compute/leaf.rs
+++ b/src/compute/leaf.rs
@@ -1,20 +1,20 @@
 //! Computes size using styles and measure functions
 
 use crate::geometry::{Point, Size};
-use crate::style::{AvailableSpace, Display, Overflow, Position, Style};
+use crate::style::{AvailableSpace, Overflow, Position};
 use crate::tree::{CollapsibleMarginSet, RunMode};
 use crate::tree::{LayoutInput, LayoutOutput, SizingMode};
 use crate::util::debug::debug_log;
 use crate::util::sys::f32_max;
 use crate::util::MaybeMath;
 use crate::util::{MaybeResolve, ResolveOrZero};
-use crate::BoxSizing;
+use crate::{BoxSizing, CoreStyle};
 use core::unreachable;
 
 /// Compute the size of a leaf node (node with no children)
 pub fn compute_leaf_layout<MeasureFunction>(
     inputs: LayoutInput,
-    style: &Style,
+    style: &impl CoreStyle,
     measure_function: MeasureFunction,
 ) -> LayoutOutput
 where
@@ -24,12 +24,12 @@ where
 
     // Note: both horizontal and vertical percentage padding/borders are resolved against the container's inline size (i.e. width).
     // This is not a bug, but is how CSS is specified (see: https://developer.mozilla.org/en-US/docs/Web/CSS/padding#values)
-    let margin = style.margin.resolve_or_zero(parent_size.width);
-    let padding = style.padding.resolve_or_zero(parent_size.width);
-    let border = style.border.resolve_or_zero(parent_size.width);
+    let margin = style.margin().resolve_or_zero(parent_size.width);
+    let padding = style.padding().resolve_or_zero(parent_size.width);
+    let border = style.border().resolve_or_zero(parent_size.width);
     let padding_border = padding + border;
     let pb_sum = padding_border.sum_axes();
-    let box_sizing_adjustment = if style.box_sizing == BoxSizing::ContentBox { pb_sum } else { Size::ZERO };
+    let box_sizing_adjustment = if style.box_sizing() == BoxSizing::ContentBox { pb_sum } else { Size::ZERO };
 
     // Resolve node's preferred/min/max sizes (width/heights) against the available space (percentages resolve to pixel values)
     // For ContentSize mode, we pretend that the node has no size styles as these should be ignored.
@@ -41,18 +41,18 @@ where
             (node_size, node_min_size, node_max_size, None)
         }
         SizingMode::InherentSize => {
-            let aspect_ratio = style.aspect_ratio;
+            let aspect_ratio = style.aspect_ratio();
             let style_size = style
-                .size
+                .size()
                 .maybe_resolve(parent_size)
                 .maybe_apply_aspect_ratio(aspect_ratio)
                 .maybe_add(box_sizing_adjustment);
             let style_min_size = style
-                .min_size
+                .min_size()
                 .maybe_resolve(parent_size)
                 .maybe_apply_aspect_ratio(aspect_ratio)
                 .maybe_add(box_sizing_adjustment);
-            let style_max_size = style.max_size.maybe_resolve(parent_size).maybe_add(box_sizing_adjustment);
+            let style_max_size = style.max_size().maybe_resolve(parent_size).maybe_add(box_sizing_adjustment);
 
             let node_size = known_dimensions.or(style_size);
             (node_size, style_min_size, style_max_size, aspect_ratio)
@@ -62,8 +62,8 @@ where
     // Scrollbar gutters are reserved when the `overflow` property is set to `Overflow::Scroll`.
     // However, the axis are switched (transposed) because a node that scrolls vertically needs
     // *horizontal* space to be reserved for a scrollbar
-    let scrollbar_gutter = style.overflow.transpose().map(|overflow| match overflow {
-        Overflow::Scroll => style.scrollbar_width,
+    let scrollbar_gutter = style.overflow().transpose().map(|overflow| match overflow {
+        Overflow::Scroll => style.scrollbar_width(),
         _ => 0.0,
     });
     // TODO: make side configurable based on the `direction` property
@@ -71,15 +71,10 @@ where
     content_box_inset.right += scrollbar_gutter.x;
     content_box_inset.bottom += scrollbar_gutter.y;
 
-    #[cfg(feature = "block_layout")]
-    let is_block = style.display == Display::Block;
-    #[cfg(not(feature = "block_layout"))]
-    let is_block = false;
-
-    let has_styles_preventing_being_collapsed_through = !is_block
-        || style.overflow.x.is_scroll_container()
-        || style.overflow.y.is_scroll_container()
-        || style.position == Position::Absolute
+    let has_styles_preventing_being_collapsed_through = !style.is_block()
+        || style.overflow().x.is_scroll_container()
+        || style.overflow().y.is_scroll_container()
+        || style.position() == Position::Absolute
         || padding.top > 0.0
         || padding.bottom > 0.0
         || border.top > 0.0
diff --git a/src/compute/mod.rs b/src/compute/mod.rs
index 00d4a8cb9..8a23c051e 100644
--- a/src/compute/mod.rs
+++ b/src/compute/mod.rs
@@ -45,14 +45,14 @@ pub use self::flexbox::compute_flexbox_layout;
 pub use self::grid::compute_grid_layout;
 
 use crate::geometry::{Line, Point, Size};
-use crate::style::{AvailableSpace, Overflow};
+use crate::style::{AvailableSpace, CoreStyle, Overflow};
 use crate::tree::{
     Layout, LayoutInput, LayoutOutput, LayoutPartialTree, LayoutPartialTreeExt, NodeId, RoundTree, SizingMode,
 };
 use crate::util::debug::{debug_log, debug_log_node, debug_pop_node, debug_push_node};
 use crate::util::sys::round;
 use crate::util::ResolveOrZero;
-use crate::{BoxSizing, Display, MaybeMath, MaybeResolve};
+use crate::{BoxSizing, MaybeMath, MaybeResolve};
 
 /// Compute layout for the root node in the tree
 pub fn compute_root_layout(tree: &mut impl LayoutPartialTree, root: NodeId, available_space: Size<AvailableSpace>) {
@@ -61,30 +61,30 @@ pub fn compute_root_layout(tree: &mut impl LayoutPartialTree, root: NodeId, avai
     #[cfg(feature = "block_layout")]
     {
         let parent_size = available_space.into_options();
-        let style = tree.get_style(root);
+        let style = tree.get_core_container_style(root);
 
-        if style.display == Display::Block {
+        if style.is_block() {
             // Pull these out earlier to avoid borrowing issues
-            let aspect_ratio = style.aspect_ratio;
-            let margin = style.margin.resolve_or_zero(parent_size.width);
-            let padding = style.padding.resolve_or_zero(parent_size.width);
-            let border = style.border.resolve_or_zero(parent_size.width);
+            let aspect_ratio = style.aspect_ratio();
+            let margin = style.margin().resolve_or_zero(parent_size.width);
+            let padding = style.padding().resolve_or_zero(parent_size.width);
+            let border = style.border().resolve_or_zero(parent_size.width);
             let padding_border_size = (padding + border).sum_axes();
             let box_sizing_adjustment =
-                if style.box_sizing == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO };
+                if style.box_sizing() == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO };
 
             let min_size = style
-                .min_size
+                .min_size()
                 .maybe_resolve(parent_size)
                 .maybe_apply_aspect_ratio(aspect_ratio)
                 .maybe_add(box_sizing_adjustment);
             let max_size = style
-                .max_size
+                .max_size()
                 .maybe_resolve(parent_size)
                 .maybe_apply_aspect_ratio(aspect_ratio)
                 .maybe_add(box_sizing_adjustment);
             let clamped_style_size = style
-                .size
+                .size()
                 .maybe_resolve(parent_size)
                 .maybe_apply_aspect_ratio(aspect_ratio)
                 .maybe_add(box_sizing_adjustment)
@@ -122,14 +122,15 @@ pub fn compute_root_layout(tree: &mut impl LayoutPartialTree, root: NodeId, avai
         Line::FALSE,
     );
 
-    let style = tree.get_style(root);
-    let padding = style.padding.resolve_or_zero(available_space.width.into_option());
-    let border = style.border.resolve_or_zero(available_space.width.into_option());
-    let margin = style.margin.resolve_or_zero(available_space.width.into_option());
+    let style = tree.get_core_container_style(root);
+    let padding = style.padding().resolve_or_zero(available_space.width.into_option());
+    let border = style.border().resolve_or_zero(available_space.width.into_option());
+    let margin = style.margin().resolve_or_zero(available_space.width.into_option());
     let scrollbar_size = Size {
-        width: if style.overflow.y == Overflow::Scroll { style.scrollbar_width } else { 0.0 },
-        height: if style.overflow.x == Overflow::Scroll { style.scrollbar_width } else { 0.0 },
+        width: if style.overflow().y == Overflow::Scroll { style.scrollbar_width() } else { 0.0 },
+        height: if style.overflow().x == Overflow::Scroll { style.scrollbar_width() } else { 0.0 },
     };
+    drop(style);
 
     tree.set_unrounded_layout(
         root,
diff --git a/src/style/flex.rs b/src/style/flex.rs
index c24f488d8..f04f3a454 100644
--- a/src/style/flex.rs
+++ b/src/style/flex.rs
@@ -1,4 +1,70 @@
 //! Style types for Flexbox layout
+use super::{AlignContent, AlignItems, AlignSelf, CoreStyle, Dimension, JustifyContent, LengthPercentage, Style};
+use crate::geometry::Size;
+
+/// The set of styles required for a Flexbox container
+pub trait FlexboxContainerStyle: CoreStyle {
+    /// Which direction does the main axis flow in?
+    #[inline(always)]
+    fn flex_direction(&self) -> FlexDirection {
+        Style::DEFAULT.flex_direction
+    }
+    /// Should elements wrap, or stay in a single line?
+    #[inline(always)]
+    fn flex_wrap(&self) -> FlexWrap {
+        Style::DEFAULT.flex_wrap
+    }
+
+    /// How large should the gaps between items in a grid or flex container be?
+    #[inline(always)]
+    fn gap(&self) -> Size<LengthPercentage> {
+        Style::DEFAULT.gap
+    }
+
+    // Alignment properties
+
+    /// How should content contained within this item be aligned in the cross/block axis
+    #[inline(always)]
+    fn align_content(&self) -> Option<AlignContent> {
+        Style::DEFAULT.align_content
+    }
+    /// How this node's children aligned in the cross/block axis?
+    #[inline(always)]
+    fn align_items(&self) -> Option<AlignItems> {
+        Style::DEFAULT.align_items
+    }
+    /// How this node's children should be aligned in the inline axis
+    #[inline(always)]
+    fn justify_content(&self) -> Option<JustifyContent> {
+        Style::DEFAULT.justify_content
+    }
+}
+
+/// The set of styles required for a Flexbox item (child of a Flexbox container)
+pub trait FlexboxItemStyle: CoreStyle {
+    /// Sets the initial main axis size of the item
+    #[inline(always)]
+    fn flex_basis(&self) -> Dimension {
+        Style::DEFAULT.flex_basis
+    }
+    /// The relative rate at which this item grows when it is expanding to fill space
+    #[inline(always)]
+    fn flex_grow(&self) -> f32 {
+        Style::DEFAULT.flex_grow
+    }
+    /// The relative rate at which this item shrinks when it is contracting to fit into space
+    #[inline(always)]
+    fn flex_shrink(&self) -> f32 {
+        Style::DEFAULT.flex_shrink
+    }
+
+    /// How this node should be aligned in the cross/block axis
+    /// Falls back to the parents [`AlignItems`] if not set
+    #[inline(always)]
+    fn align_self(&self) -> Option<AlignSelf> {
+        Style::DEFAULT.align_self
+    }
+}
 
 use crate::geometry::AbsoluteAxis;
 
diff --git a/src/style/grid.rs b/src/style/grid.rs
index b2d2b04b4..d2597e69c 100644
--- a/src/style/grid.rs
+++ b/src/style/grid.rs
@@ -1,13 +1,124 @@
 //! Style types for CSS Grid layout
-use super::{AlignContent, LengthPercentage, Style};
+use super::{AlignContent, AlignItems, AlignSelf, CoreStyle, JustifyContent, LengthPercentage, Style};
 use crate::compute::grid::{GridCoordinate, GridLine, OriginZeroLine};
-use crate::geometry::{AbsoluteAxis, AbstractAxis};
-use crate::geometry::{Line, MinMax};
+use crate::geometry::{AbsoluteAxis, AbstractAxis, Line, MinMax, Size};
 use crate::style_helpers::*;
 use crate::util::sys::GridTrackVec;
 use core::cmp::{max, min};
 use core::convert::Infallible;
 
+/// The set of styles required for a CSS Grid container
+pub trait GridContainerStyle: CoreStyle {
+    /// Defines the track sizing functions (heights) of the grid rows
+    #[inline(always)]
+    fn grid_template_rows(&self) -> &[TrackSizingFunction] {
+        &[]
+    }
+    /// Defines the track sizing functions (widths) of the grid columns
+    #[inline(always)]
+    fn grid_template_columns(&self) -> &[TrackSizingFunction] {
+        &[]
+    }
+    /// Defines the size of implicitly created rows
+    #[inline(always)]
+    fn grid_auto_rows(&self) -> &[NonRepeatedTrackSizingFunction] {
+        &[]
+    }
+    /// Defined the size of implicitly created columns
+    #[inline(always)]
+    fn grid_auto_columns(&self) -> &[NonRepeatedTrackSizingFunction] {
+        &[]
+    }
+    /// Controls how items get placed into the grid for auto-placed items
+    #[inline(always)]
+    fn grid_auto_flow(&self) -> GridAutoFlow {
+        Style::DEFAULT.grid_auto_flow
+    }
+
+    /// How large should the gaps between items in a grid or flex container be?
+    #[inline(always)]
+    fn gap(&self) -> Size<LengthPercentage> {
+        Style::DEFAULT.gap
+    }
+
+    // Alignment properties
+
+    /// How should content contained within this item be aligned in the cross/block axis
+    #[inline(always)]
+    fn align_content(&self) -> Option<AlignContent> {
+        Style::DEFAULT.align_content
+    }
+    /// How should contained within this item be aligned in the main/inline axis
+    #[inline(always)]
+    fn justify_content(&self) -> Option<JustifyContent> {
+        Style::DEFAULT.justify_content
+    }
+    /// How this node's children aligned in the cross/block axis?
+    #[inline(always)]
+    fn align_items(&self) -> Option<AlignItems> {
+        Style::DEFAULT.align_items
+    }
+    /// How this node's children should be aligned in the inline axis
+    #[inline(always)]
+    fn justify_items(&self) -> Option<AlignItems> {
+        Style::DEFAULT.justify_items
+    }
+
+    /// Get a grid item's row or column placement depending on the axis passed
+    #[inline(always)]
+    fn grid_template_tracks(&self, axis: AbsoluteAxis) -> &[TrackSizingFunction] {
+        match axis {
+            AbsoluteAxis::Horizontal => self.grid_template_columns(),
+            AbsoluteAxis::Vertical => self.grid_template_rows(),
+        }
+    }
+
+    /// Get a grid container's align-content or justify-content alignment depending on the axis passed
+    #[inline(always)]
+    fn grid_align_content(&self, axis: AbstractAxis) -> AlignContent {
+        match axis {
+            AbstractAxis::Inline => self.justify_content().unwrap_or(AlignContent::Stretch),
+            AbstractAxis::Block => self.align_content().unwrap_or(AlignContent::Stretch),
+        }
+    }
+}
+
+/// The set of styles required for a CSS Grid item (child of a CSS Grid container)
+pub trait GridItemStyle: CoreStyle {
+    /// Defines which row in the grid the item should start and end at
+    #[inline(always)]
+    fn grid_row(&self) -> Line<GridPlacement> {
+        Style::DEFAULT.grid_row
+    }
+    /// Defines which column in the grid the item should start and end at
+    #[inline(always)]
+    fn grid_column(&self) -> Line<GridPlacement> {
+        Style::DEFAULT.grid_column
+    }
+
+    /// How this node should be aligned in the cross/block axis
+    /// Falls back to the parents [`AlignItems`] if not set
+    #[inline(always)]
+    fn align_self(&self) -> Option<AlignSelf> {
+        Style::DEFAULT.align_self
+    }
+    /// How this node should be aligned in the inline axis
+    /// Falls back to the parents [`super::JustifyItems`] if not set
+    #[inline(always)]
+    fn justify_self(&self) -> Option<AlignSelf> {
+        Style::DEFAULT.justify_self
+    }
+
+    /// Get a grid item's row or column placement depending on the axis passed
+    #[inline(always)]
+    fn grid_placement(&self, axis: AbsoluteAxis) -> Line<GridPlacement> {
+        match axis {
+            AbsoluteAxis::Horizontal => self.grid_column(),
+            AbsoluteAxis::Vertical => self.grid_row(),
+        }
+    }
+}
+
 /// Controls whether grid items are placed row-wise or column-wise. And whether the sparse or dense packing algorithm is used.
 ///
 /// The "dense" packing algorithm attempts to fill in holes earlier in the grid, if smaller items come up later. This may cause items to appear out-of-order, when doing so would fill in holes left by larger items.
@@ -600,30 +711,3 @@ impl From<MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>> for TrackSizin
         Self::Single(input)
     }
 }
-
-// Grid extensions to the Style struct
-impl Style {
-    /// Get a grid item's row or column placement depending on the axis passed
-    pub(crate) fn grid_template_tracks(&self, axis: AbsoluteAxis) -> &GridTrackVec<TrackSizingFunction> {
-        match axis {
-            AbsoluteAxis::Horizontal => &self.grid_template_columns,
-            AbsoluteAxis::Vertical => &self.grid_template_rows,
-        }
-    }
-
-    /// Get a grid item's row or column placement depending on the axis passed
-    pub(crate) fn grid_placement(&self, axis: AbsoluteAxis) -> Line<GridPlacement> {
-        match axis {
-            AbsoluteAxis::Horizontal => self.grid_column,
-            AbsoluteAxis::Vertical => self.grid_row,
-        }
-    }
-
-    /// Get a grid container's align-content or justify-content alignment depending on the axis passed
-    pub(crate) fn grid_align_content(&self, axis: AbstractAxis) -> AlignContent {
-        match axis {
-            AbstractAxis::Inline => self.justify_content.unwrap_or(AlignContent::Stretch),
-            AbstractAxis::Block => self.align_content.unwrap_or(AlignContent::Stretch),
-        }
-    }
-}
diff --git a/src/style/mod.rs b/src/style/mod.rs
index 31f4419b0..d32924879 100644
--- a/src/style/mod.rs
+++ b/src/style/mod.rs
@@ -9,7 +9,7 @@ pub use self::alignment::{AlignContent, AlignItems, AlignSelf, JustifyContent, J
 pub use self::dimension::{AvailableSpace, Dimension, LengthPercentage, LengthPercentageAuto};
 
 #[cfg(feature = "flexbox")]
-pub use self::flex::{FlexDirection, FlexWrap};
+pub use self::flex::{FlexDirection, FlexWrap, FlexboxContainerStyle, FlexboxItemStyle};
 
 #[cfg(feature = "grid")]
 mod grid;
@@ -17,8 +17,8 @@ mod grid;
 pub(crate) use self::grid::{GenericGridPlacement, OriginZeroGridPlacement};
 #[cfg(feature = "grid")]
 pub use self::grid::{
-    GridAutoFlow, GridPlacement, GridTrackRepetition, MaxTrackSizingFunction, MinTrackSizingFunction,
-    NonRepeatedTrackSizingFunction, TrackSizingFunction,
+    GridAutoFlow, GridContainerStyle, GridItemStyle, GridPlacement, GridTrackRepetition, MaxTrackSizingFunction,
+    MinTrackSizingFunction, NonRepeatedTrackSizingFunction, TrackSizingFunction,
 };
 use crate::geometry::{Point, Rect, Size};
 
@@ -29,6 +29,93 @@ use crate::style_helpers;
 #[cfg(feature = "grid")]
 use crate::util::sys::GridTrackVec;
 
+/// The core set of styles that are shared between all CSS layout nodes
+///
+/// Note that all methods come with a default implementation which simply returns the default value for that style property
+/// but this is a just a convenience to save on boilerplate for styles that your implementation doesn't support. You will need
+/// to override the default implementation for each style property that your style type actually supports.
+pub trait CoreStyle {
+    /// Which box generation mode should be used
+    #[inline(always)]
+    fn box_generation_mode(&self) -> BoxGenerationMode {
+        BoxGenerationMode::DEFAULT
+    }
+    /// Is block layout?
+    #[inline(always)]
+    fn is_block(&self) -> bool {
+        false
+    }
+    /// Which box do size styles apply to
+    #[inline(always)]
+    fn box_sizing(&self) -> BoxSizing {
+        BoxSizing::BorderBox
+    }
+
+    // Overflow properties
+    /// How children overflowing their container should affect layout
+    #[inline(always)]
+    fn overflow(&self) -> Point<Overflow> {
+        Style::DEFAULT.overflow
+    }
+    /// How much space (in points) should be reserved for the scrollbars of `Overflow::Scroll` and `Overflow::Auto` nodes.
+    #[inline(always)]
+    fn scrollbar_width(&self) -> f32 {
+        0.0
+    }
+
+    // Position properties
+    /// What should the `position` value of this struct use as a base offset?
+    #[inline(always)]
+    fn position(&self) -> Position {
+        Style::DEFAULT.position
+    }
+    /// How should the position of this element be tweaked relative to the layout defined?
+    #[inline(always)]
+    fn inset(&self) -> Rect<LengthPercentageAuto> {
+        Style::DEFAULT.inset
+    }
+
+    // Size properies
+    /// Sets the initial size of the item
+    #[inline(always)]
+    fn size(&self) -> Size<Dimension> {
+        Style::DEFAULT.size
+    }
+    /// Controls the minimum size of the item
+    #[inline(always)]
+    fn min_size(&self) -> Size<Dimension> {
+        Style::DEFAULT.min_size
+    }
+    /// Controls the maximum size of the item
+    #[inline(always)]
+    fn max_size(&self) -> Size<Dimension> {
+        Style::DEFAULT.max_size
+    }
+    /// Sets the preferred aspect ratio for the item
+    /// The ratio is calculated as width divided by height.
+    #[inline(always)]
+    fn aspect_ratio(&self) -> Option<f32> {
+        Style::DEFAULT.aspect_ratio
+    }
+
+    // Spacing Properties
+    /// How large should the margin be on each side?
+    #[inline(always)]
+    fn margin(&self) -> Rect<LengthPercentageAuto> {
+        Style::DEFAULT.margin
+    }
+    /// How large should the padding be on each side?
+    #[inline(always)]
+    fn padding(&self) -> Rect<LengthPercentage> {
+        Style::DEFAULT.padding
+    }
+    /// How large should the border be on each side?
+    #[inline(always)]
+    fn border(&self) -> Rect<LengthPercentage> {
+        Style::DEFAULT.border
+    }
+}
+
 /// Sets the layout used for the children of this node
 ///
 /// The default values depends on on which feature flags are enabled. The order of precedence is: Flex, Grid, Block, None.
@@ -44,28 +131,34 @@ pub enum Display {
     /// The children will follow the CSS Grid layout algorithm
     #[cfg(feature = "grid")]
     Grid,
-    /// The children will not be laid out, and will follow absolute positioning
+    /// The node is hidden, and it's children will also be hidden
     None,
 }
 
 impl Display {
-    /// The default of Display.
+    /// The default Display mode
     #[cfg(feature = "flexbox")]
     pub const DEFAULT: Display = Display::Flex;
 
-    /// The default of Display.
+    /// The default Display mode
     #[cfg(all(feature = "grid", not(feature = "flexbox")))]
     pub const DEFAULT: Display = Display::Grid;
 
-    /// The default of Display.
+    /// The default Display mode
     #[cfg(all(feature = "block_layout", not(feature = "flexbox"), not(feature = "grid")))]
     pub const DEFAULT: Display = Display::Block;
 
-    /// The default of Display.
+    /// The default Display mode
     #[cfg(all(not(feature = "flexbox"), not(feature = "grid"), not(feature = "block_layout")))]
     pub const DEFAULT: Display = Display::None;
 }
 
+impl Default for Display {
+    fn default() -> Self {
+        Self::DEFAULT
+    }
+}
+
 impl core::fmt::Display for Display {
     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
         match self {
@@ -80,7 +173,23 @@ impl core::fmt::Display for Display {
     }
 }
 
-impl Default for Display {
+/// An abstracted version of the CSS `display` property where any value other than "none" is represented by "normal"
+/// See: <https://www.w3.org/TR/css-display-3/#box-generation>
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+pub enum BoxGenerationMode {
+    /// The node generates a box in the regular way
+    Normal,
+    /// The node and it's descendants generate no boxes (they are hidden)
+    None,
+}
+
+impl BoxGenerationMode {
+    /// The default of BoxGenerationMode
+    pub const DEFAULT: BoxGenerationMode = BoxGenerationMode::Normal;
+}
+
+impl Default for BoxGenerationMode {
     fn default() -> Self {
         Self::DEFAULT
     }
@@ -306,11 +415,11 @@ pub struct Style {
     #[cfg(feature = "flexbox")]
     pub flex_shrink: f32,
 
-    // Grid container properties
-    /// Defines the track sizing functions (widths) of the grid rows
+    // Grid container properies
+    /// Defines the track sizing functions (heights) of the grid rows
     #[cfg(feature = "grid")]
     pub grid_template_rows: GridTrackVec<TrackSizingFunction>,
-    /// Defines the track sizing functions (heights) of the grid columns
+    /// Defines the track sizing functions (widths) of the grid columns
     #[cfg(feature = "grid")]
     pub grid_template_columns: GridTrackVec<TrackSizingFunction>,
     /// Defines the size of implicitly created rows
@@ -398,6 +507,352 @@ impl Default for Style {
     }
 }
 
+impl CoreStyle for Style {
+    #[inline(always)]
+    fn box_generation_mode(&self) -> BoxGenerationMode {
+        match self.display {
+            Display::None => BoxGenerationMode::None,
+            _ => BoxGenerationMode::Normal,
+        }
+    }
+    #[inline(always)]
+    #[cfg(feature = "block_layout")]
+    fn is_block(&self) -> bool {
+        matches!(self.display, Display::Block)
+    }
+    #[inline(always)]
+    fn box_sizing(&self) -> BoxSizing {
+        self.box_sizing
+    }
+    #[inline(always)]
+    fn overflow(&self) -> Point<Overflow> {
+        self.overflow
+    }
+    #[inline(always)]
+    fn scrollbar_width(&self) -> f32 {
+        self.scrollbar_width
+    }
+    #[inline(always)]
+    fn position(&self) -> Position {
+        self.position
+    }
+    #[inline(always)]
+    fn inset(&self) -> Rect<LengthPercentageAuto> {
+        self.inset
+    }
+    #[inline(always)]
+    fn size(&self) -> Size<Dimension> {
+        self.size
+    }
+    #[inline(always)]
+    fn min_size(&self) -> Size<Dimension> {
+        self.min_size
+    }
+    #[inline(always)]
+    fn max_size(&self) -> Size<Dimension> {
+        self.max_size
+    }
+    #[inline(always)]
+    fn aspect_ratio(&self) -> Option<f32> {
+        self.aspect_ratio
+    }
+    #[inline(always)]
+    fn margin(&self) -> Rect<LengthPercentageAuto> {
+        self.margin
+    }
+    #[inline(always)]
+    fn padding(&self) -> Rect<LengthPercentage> {
+        self.padding
+    }
+    #[inline(always)]
+    fn border(&self) -> Rect<LengthPercentage> {
+        self.border
+    }
+}
+
+impl<T: CoreStyle> CoreStyle for &'_ T {
+    #[inline(always)]
+    fn box_generation_mode(&self) -> BoxGenerationMode {
+        (*self).box_generation_mode()
+    }
+    #[inline(always)]
+    fn is_block(&self) -> bool {
+        (*self).is_block()
+    }
+    #[inline(always)]
+    fn box_sizing(&self) -> BoxSizing {
+        (*self).box_sizing()
+    }
+    #[inline(always)]
+    fn overflow(&self) -> Point<Overflow> {
+        (*self).overflow()
+    }
+    #[inline(always)]
+    fn scrollbar_width(&self) -> f32 {
+        (*self).scrollbar_width()
+    }
+    #[inline(always)]
+    fn position(&self) -> Position {
+        (*self).position()
+    }
+    #[inline(always)]
+    fn inset(&self) -> Rect<LengthPercentageAuto> {
+        (*self).inset()
+    }
+    #[inline(always)]
+    fn size(&self) -> Size<Dimension> {
+        (*self).size()
+    }
+    #[inline(always)]
+    fn min_size(&self) -> Size<Dimension> {
+        (*self).min_size()
+    }
+    #[inline(always)]
+    fn max_size(&self) -> Size<Dimension> {
+        (*self).max_size()
+    }
+    #[inline(always)]
+    fn aspect_ratio(&self) -> Option<f32> {
+        (*self).aspect_ratio()
+    }
+    #[inline(always)]
+    fn margin(&self) -> Rect<LengthPercentageAuto> {
+        (*self).margin()
+    }
+    #[inline(always)]
+    fn padding(&self) -> Rect<LengthPercentage> {
+        (*self).padding()
+    }
+    #[inline(always)]
+    fn border(&self) -> Rect<LengthPercentage> {
+        (*self).border()
+    }
+}
+
+#[cfg(feature = "flexbox")]
+impl FlexboxContainerStyle for &Style {
+    #[inline(always)]
+    fn flex_direction(&self) -> FlexDirection {
+        self.flex_direction
+    }
+    #[inline(always)]
+    fn flex_wrap(&self) -> FlexWrap {
+        self.flex_wrap
+    }
+    #[inline(always)]
+    fn gap(&self) -> Size<LengthPercentage> {
+        self.gap
+    }
+    #[inline(always)]
+    fn align_content(&self) -> Option<AlignContent> {
+        self.align_content
+    }
+    #[inline(always)]
+    fn align_items(&self) -> Option<AlignItems> {
+        self.align_items
+    }
+    #[inline(always)]
+    fn justify_content(&self) -> Option<JustifyContent> {
+        self.justify_content
+    }
+}
+
+#[cfg(feature = "flexbox")]
+impl<T: FlexboxContainerStyle> FlexboxContainerStyle for &'_ T {
+    #[inline(always)]
+    fn flex_direction(&self) -> FlexDirection {
+        (*self).flex_direction()
+    }
+    #[inline(always)]
+    fn flex_wrap(&self) -> FlexWrap {
+        (*self).flex_wrap()
+    }
+    #[inline(always)]
+    fn gap(&self) -> Size<LengthPercentage> {
+        (*self).gap()
+    }
+    #[inline(always)]
+    fn align_content(&self) -> Option<AlignContent> {
+        (*self).align_content()
+    }
+    #[inline(always)]
+    fn align_items(&self) -> Option<AlignItems> {
+        (*self).align_items()
+    }
+    #[inline(always)]
+    fn justify_content(&self) -> Option<JustifyContent> {
+        (*self).justify_content()
+    }
+}
+
+#[cfg(feature = "flexbox")]
+impl FlexboxItemStyle for Style {
+    #[inline(always)]
+    fn flex_basis(&self) -> Dimension {
+        self.flex_basis
+    }
+    #[inline(always)]
+    fn flex_grow(&self) -> f32 {
+        self.flex_grow
+    }
+    #[inline(always)]
+    fn flex_shrink(&self) -> f32 {
+        self.flex_shrink
+    }
+    #[inline(always)]
+    fn align_self(&self) -> Option<AlignSelf> {
+        self.align_self
+    }
+}
+
+#[cfg(feature = "flexbox")]
+impl<T: FlexboxItemStyle> FlexboxItemStyle for &'_ T {
+    #[inline(always)]
+    fn flex_basis(&self) -> Dimension {
+        (*self).flex_basis()
+    }
+    #[inline(always)]
+    fn flex_grow(&self) -> f32 {
+        (*self).flex_grow()
+    }
+    #[inline(always)]
+    fn flex_shrink(&self) -> f32 {
+        (*self).flex_shrink()
+    }
+    #[inline(always)]
+    fn align_self(&self) -> Option<AlignSelf> {
+        (*self).align_self()
+    }
+}
+
+#[cfg(feature = "grid")]
+impl GridContainerStyle for Style {
+    #[inline(always)]
+    fn grid_template_rows(&self) -> &[TrackSizingFunction] {
+        &self.grid_template_rows
+    }
+    #[inline(always)]
+    fn grid_template_columns(&self) -> &[TrackSizingFunction] {
+        &self.grid_template_columns
+    }
+    #[inline(always)]
+    fn grid_auto_rows(&self) -> &[NonRepeatedTrackSizingFunction] {
+        &self.grid_auto_rows
+    }
+    #[inline(always)]
+    fn grid_auto_columns(&self) -> &[NonRepeatedTrackSizingFunction] {
+        &self.grid_auto_columns
+    }
+    #[inline(always)]
+    fn grid_auto_flow(&self) -> GridAutoFlow {
+        self.grid_auto_flow
+    }
+    #[inline(always)]
+    fn gap(&self) -> Size<LengthPercentage> {
+        self.gap
+    }
+    #[inline(always)]
+    fn align_content(&self) -> Option<AlignContent> {
+        self.align_content
+    }
+    #[inline(always)]
+    fn justify_content(&self) -> Option<JustifyContent> {
+        self.justify_content
+    }
+    #[inline(always)]
+    fn align_items(&self) -> Option<AlignItems> {
+        self.align_items
+    }
+    #[inline(always)]
+    fn justify_items(&self) -> Option<AlignItems> {
+        self.justify_items
+    }
+}
+
+#[cfg(feature = "grid")]
+impl<T: GridContainerStyle> GridContainerStyle for &'_ T {
+    #[inline(always)]
+    fn grid_template_rows(&self) -> &[TrackSizingFunction] {
+        (*self).grid_template_rows()
+    }
+    #[inline(always)]
+    fn grid_template_columns(&self) -> &[TrackSizingFunction] {
+        (*self).grid_template_columns()
+    }
+    #[inline(always)]
+    fn grid_auto_rows(&self) -> &[NonRepeatedTrackSizingFunction] {
+        (*self).grid_auto_rows()
+    }
+    #[inline(always)]
+    fn grid_auto_columns(&self) -> &[NonRepeatedTrackSizingFunction] {
+        (*self).grid_auto_columns()
+    }
+    #[inline(always)]
+    fn grid_auto_flow(&self) -> GridAutoFlow {
+        (*self).grid_auto_flow()
+    }
+    #[inline(always)]
+    fn gap(&self) -> Size<LengthPercentage> {
+        (*self).gap()
+    }
+    #[inline(always)]
+    fn align_content(&self) -> Option<AlignContent> {
+        (*self).align_content()
+    }
+    #[inline(always)]
+    fn justify_content(&self) -> Option<JustifyContent> {
+        (*self).justify_content()
+    }
+    #[inline(always)]
+    fn align_items(&self) -> Option<AlignItems> {
+        (*self).align_items()
+    }
+    #[inline(always)]
+    fn justify_items(&self) -> Option<AlignItems> {
+        (*self).justify_items()
+    }
+}
+
+#[cfg(feature = "grid")]
+impl GridItemStyle for &'_ Style {
+    #[inline(always)]
+    fn grid_row(&self) -> Line<GridPlacement> {
+        self.grid_row
+    }
+    #[inline(always)]
+    fn grid_column(&self) -> Line<GridPlacement> {
+        self.grid_column
+    }
+    #[inline(always)]
+    fn align_self(&self) -> Option<AlignSelf> {
+        self.align_self
+    }
+    #[inline(always)]
+    fn justify_self(&self) -> Option<AlignSelf> {
+        self.justify_self
+    }
+}
+
+#[cfg(feature = "grid")]
+impl<T: GridItemStyle> GridItemStyle for &'_ T {
+    #[inline(always)]
+    fn grid_row(&self) -> Line<GridPlacement> {
+        (*self).grid_row()
+    }
+    #[inline(always)]
+    fn grid_column(&self) -> Line<GridPlacement> {
+        (*self).grid_column()
+    }
+    #[inline(always)]
+    fn align_self(&self) -> Option<AlignSelf> {
+        (*self).align_self()
+    }
+    #[inline(always)]
+    fn justify_self(&self) -> Option<AlignSelf> {
+        (*self).justify_self()
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::Style;
diff --git a/src/tree/mod.rs b/src/tree/mod.rs
index 7a8961f3a..ccb5b625e 100644
--- a/src/tree/mod.rs
+++ b/src/tree/mod.rs
@@ -15,6 +15,15 @@ pub use node::NodeId;
 pub(crate) use traits::LayoutPartialTreeExt;
 pub use traits::{LayoutPartialTree, PrintTree, RoundTree, TraversePartialTree, TraverseTree};
 
+#[cfg(feature = "flexbox")]
+pub use traits::LayoutFlexboxContainer;
+
+#[cfg(feature = "grid")]
+pub use traits::LayoutGridContainer;
+
+#[cfg(feature = "block_layout")]
+pub use traits::LayoutBlockContainer;
+
 #[cfg(feature = "taffy_tree")]
 mod taffy_tree;
 #[cfg(feature = "taffy_tree")]
diff --git a/src/tree/taffy_tree.rs b/src/tree/taffy_tree.rs
index 7ec8deea5..788941fdf 100644
--- a/src/tree/taffy_tree.rs
+++ b/src/tree/taffy_tree.rs
@@ -14,15 +14,15 @@ use crate::tree::{
 use crate::util::debug::{debug_log, debug_log_node};
 use crate::util::sys::{new_vec_with_capacity, ChildrenVec, Vec};
 
-#[cfg(feature = "block_layout")]
-use crate::compute::compute_block_layout;
-#[cfg(feature = "flexbox")]
-use crate::compute::compute_flexbox_layout;
-#[cfg(feature = "grid")]
-use crate::compute::compute_grid_layout;
 use crate::compute::{
     compute_cached_layout, compute_hidden_layout, compute_leaf_layout, compute_root_layout, round_layout,
 };
+#[cfg(feature = "block_layout")]
+use crate::{compute::compute_block_layout, LayoutBlockContainer};
+#[cfg(feature = "flexbox")]
+use crate::{compute::compute_flexbox_layout, LayoutFlexboxContainer};
+#[cfg(feature = "grid")]
+use crate::{compute::compute_grid_layout, LayoutGridContainer};
 
 /// The error Taffy generates on invalid operations
 pub type TaffyResult<T> = Result<T, TaffyError>;
@@ -271,9 +271,12 @@ where
     MeasureFunction:
         FnMut(Size<Option<f32>>, Size<AvailableSpace>, NodeId, Option<&mut NodeContext>, &Style) -> Size<f32>,
 {
+    type CoreContainerStyle<'a> = &'a Style where Self : 'a;
+    type CacheMut<'b> = &'b mut Cache where Self : 'b;
+
     #[inline(always)]
-    fn get_style(&self, node: NodeId) -> &Style {
-        &self.taffy.nodes[node.into()].style
+    fn get_core_container_style(&self, node_id: NodeId) -> Self::CoreContainerStyle<'_> {
+        &self.taffy.nodes[node_id.into()].style
     }
 
     #[inline(always)]
@@ -301,7 +304,7 @@ where
         //
         // If there was no cache match and a new result needs to be computed then that result will be added to the cache
         compute_cached_layout(self, node, inputs, |tree, node, inputs| {
-            let display_mode = tree.get_style(node).display;
+            let display_mode = tree.taffy.nodes[node.into()].style.display;
             let has_children = tree.child_count(node) > 0;
 
             debug_log!(display_mode);
@@ -337,6 +340,66 @@ where
     }
 }
 
+#[cfg(feature = "block_layout")]
+impl<'t, NodeContext, MeasureFunction> LayoutBlockContainer for TaffyView<'t, NodeContext, MeasureFunction>
+where
+    MeasureFunction:
+        FnMut(Size<Option<f32>>, Size<AvailableSpace>, NodeId, Option<&mut NodeContext>, &Style) -> Size<f32>,
+{
+    type BlockContainerStyle<'a> = &'a Style where Self: 'a;
+    type BlockItemStyle<'a> = &'a Style where Self: 'a;
+
+    #[inline(always)]
+    fn get_block_container_style(&self, node_id: NodeId) -> Self::BlockContainerStyle<'_> {
+        self.get_core_container_style(node_id)
+    }
+
+    #[inline(always)]
+    fn get_block_child_style(&self, child_node_id: NodeId) -> Self::BlockItemStyle<'_> {
+        self.get_core_container_style(child_node_id)
+    }
+}
+
+#[cfg(feature = "flexbox")]
+impl<'t, NodeContext, MeasureFunction> LayoutFlexboxContainer for TaffyView<'t, NodeContext, MeasureFunction>
+where
+    MeasureFunction:
+        FnMut(Size<Option<f32>>, Size<AvailableSpace>, NodeId, Option<&mut NodeContext>, &Style) -> Size<f32>,
+{
+    type FlexboxContainerStyle<'a> = &'a Style where Self: 'a;
+    type FlexboxItemStyle<'a> = &'a Style where Self: 'a;
+
+    #[inline(always)]
+    fn get_flexbox_container_style(&self, node_id: NodeId) -> Self::FlexboxContainerStyle<'_> {
+        &self.taffy.nodes[node_id.into()].style
+    }
+
+    #[inline(always)]
+    fn get_flexbox_child_style(&self, child_node_id: NodeId) -> Self::FlexboxItemStyle<'_> {
+        &self.taffy.nodes[child_node_id.into()].style
+    }
+}
+
+#[cfg(feature = "grid")]
+impl<'t, NodeContext, MeasureFunction> LayoutGridContainer for TaffyView<'t, NodeContext, MeasureFunction>
+where
+    MeasureFunction:
+        FnMut(Size<Option<f32>>, Size<AvailableSpace>, NodeId, Option<&mut NodeContext>, &Style) -> Size<f32>,
+{
+    type GridContainerStyle<'a> = &'a Style where Self: 'a;
+    type GridItemStyle<'a> = &'a Style where Self: 'a;
+
+    #[inline(always)]
+    fn get_grid_container_style(&self, node_id: NodeId) -> Self::GridContainerStyle<'_> {
+        &self.taffy.nodes[node_id.into()].style
+    }
+
+    #[inline(always)]
+    fn get_grid_child_style(&self, child_node_id: NodeId) -> Self::GridItemStyle<'_> {
+        &self.taffy.nodes[child_node_id.into()].style
+    }
+}
+
 // RoundTree impl for TaffyView
 impl<'t, NodeContext, MeasureFunction> RoundTree for TaffyView<'t, NodeContext, MeasureFunction>
 where
diff --git a/src/tree/traits.rs b/src/tree/traits.rs
index ce6688294..a549232ea 100644
--- a/src/tree/traits.rs
+++ b/src/tree/traits.rs
@@ -128,7 +128,12 @@
 //!
 use super::{Cache, Layout, LayoutInput, LayoutOutput, NodeId, RequestedAxis, RunMode, SizingMode};
 use crate::geometry::{AbsoluteAxis, Line, Size};
-use crate::style::{AvailableSpace, Style};
+use crate::style::{AvailableSpace, CoreStyle};
+#[cfg(feature = "flexbox")]
+use crate::style::{FlexboxContainerStyle, FlexboxItemStyle};
+#[cfg(feature = "grid")]
+use crate::style::{GridContainerStyle, GridItemStyle};
+use core::ops::{Deref, DerefMut};
 
 /// This trait is Taffy's abstraction for downward tree traversal.
 /// However, this trait does *not* require access to any node's other than a single container node's immediate children unless you also intend to implement `TraverseTree`.
@@ -157,14 +162,26 @@ pub trait TraverseTree: TraversePartialTree {}
 /// Note that this trait extends [`TraversePartialTree`] (not [`TraverseTree`]). Taffy's algorithm implementations have been designed such that they can be used for a laying out a single
 /// node that only has access to it's immediate children.
 pub trait LayoutPartialTree: TraversePartialTree {
-    /// Get a reference to the [`Style`] for this node.
-    fn get_style(&self, node_id: NodeId) -> &Style;
+    /// The style type representing the core container styles that all containers should have
+    /// Used when laying out the root node of a tree
+    type CoreContainerStyle<'a>: CoreStyle
+    where
+        Self: 'a;
+
+    /// A mutable reference to the cache. This is an associated type to allow for different
+    /// types of mutable reference such as mutex or refcell guards
+    type CacheMut<'b>: Deref<Target = Cache> + DerefMut
+    where
+        Self: 'b;
+
+    /// Get core style
+    fn get_core_container_style(&self, node_id: NodeId) -> Self::CoreContainerStyle<'_>;
 
     /// Set the node's unrounded layout
     fn set_unrounded_layout(&mut self, node_id: NodeId, layout: &Layout);
 
     /// Get a mutable reference to the [`Cache`] for this node.
-    fn get_cache_mut(&mut self, node_id: NodeId) -> &mut Cache;
+    fn get_cache_mut(&mut self, node_id: NodeId) -> Self::CacheMut<'_>;
 
     /// Compute the specified node's size or full layout given the specified constraints
     fn compute_child_layout(&mut self, node_id: NodeId, inputs: LayoutInput) -> LayoutOutput;
@@ -191,6 +208,64 @@ pub trait PrintTree: TraverseTree {
     fn get_final_layout(&self, node_id: NodeId) -> &Layout;
 }
 
+#[cfg(feature = "flexbox")]
+/// Extends [`LayoutPartialTree`] with getters for the styles required for Flexbox layout
+pub trait LayoutFlexboxContainer: LayoutPartialTree {
+    /// The style type representing the Flexbox container's styles
+    type FlexboxContainerStyle<'a>: FlexboxContainerStyle
+    where
+        Self: 'a;
+    /// The style type representing each Flexbox item's styles
+    type FlexboxItemStyle<'a>: FlexboxItemStyle
+    where
+        Self: 'a;
+
+    /// Get the container's styles
+    fn get_flexbox_container_style(&self, node_id: NodeId) -> Self::FlexboxContainerStyle<'_>;
+
+    /// Get the child's styles
+    fn get_flexbox_child_style(&self, child_node_id: NodeId) -> Self::FlexboxItemStyle<'_>;
+}
+
+#[cfg(feature = "grid")]
+/// Extends [`LayoutPartialTree`] with getters for the styles required for CSS Grid layout
+pub trait LayoutGridContainer: LayoutPartialTree {
+    /// The style type representing the CSS Grid container's styles
+    type GridContainerStyle<'a>: GridContainerStyle
+    where
+        Self: 'a;
+
+    /// The style type representing each CSS Grid item's styles
+    type GridItemStyle<'a>: GridItemStyle
+    where
+        Self: 'a;
+
+    /// Get the container's styles
+    fn get_grid_container_style(&self, node_id: NodeId) -> Self::GridContainerStyle<'_>;
+
+    /// Get the child's styles
+    fn get_grid_child_style(&self, child_node_id: NodeId) -> Self::GridItemStyle<'_>;
+}
+
+#[cfg(feature = "block_layout")]
+/// Extends [`LayoutPartialTree`] with getters for the styles required for CSS Block layout
+pub trait LayoutBlockContainer: LayoutPartialTree {
+    /// The style type representing the CSS Block container's styles
+    type BlockContainerStyle<'a>: CoreStyle
+    where
+        Self: 'a;
+    /// The style type representing each CSS Block item's styles
+    type BlockItemStyle<'a>: CoreStyle
+    where
+        Self: 'a;
+
+    /// Get the container's styles
+    fn get_block_container_style(&self, node_id: NodeId) -> Self::BlockContainerStyle<'_>;
+
+    /// Get the child's styles
+    fn get_block_child_style(&self, child_node_id: NodeId) -> Self::BlockItemStyle<'_>;
+}
+
 // --- PRIVATE TRAITS
 
 /// A private trait which allows us to add extra convenience methods to types which implement