tawasuyu

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 231Rust 10TOML 3CSS 1348 Definitions

Document entire crate (#96)

* Deny missing docs :3

* Module level docs

* Document geometry.rs

* Document layout.rs

* Document prelude.rs

* Document lib.rs

* Document node.rs

* Document number.rs

* Partial docs for style.rs

* Warn that padding broken :(

* Add docs to forest.rs

* Fix typo

Co-authored-by: Andreas Weibye <13300393+Weibye@users.noreply.github.com>

* Fix docs for geometry.rs

* Typo fix

Co-authored-by: TimJentzsch <tim-jentzsch@gmx.de>

* Typo fix

Co-authored-by: TimJentzsch <tim-jentzsch@gmx.de>

* Revert non-exhaustive annotation

Co-authored-by: TimJentzsch <tim-jentzsch@gmx.de>

* Rename unclear method names on Rect

* Clarify wording of mark_dirty field

Co-authored-by: TimJentzsch <tim-jentzsch@gmx.de>

* Clarify wording of mark_dirty field

Co-authored-by: TimJentzsch <tim-jentzsch@gmx.de>

* Use `parent` as the argument name over `node` in methods on `Forest`

* index -> child_index

* Fix incorrect comment

* More docs for style.rs

* Fix merge error

* Document JustifyContent

* Finish documenting style.rs

* Small improvements to style.rs docs

Co-authored-by: Andreas Weibye <13300393+Weibye@users.noreply.github.com>

* Typo fix

Co-authored-by: Andreas Weibye <13300393+Weibye@users.noreply.github.com>

* Document sys.rs

* Document more of forest.rs

* Docs tweaks

Co-authored-by: TimJentzsch <tim-jentzsch@gmx.de>

* Typos

Co-authored-by: TimJentzsch <tim-jentzsch@gmx.de>

* Caching notes

* Complete internal documentation

* Cargo fmt

Co-authored-by: Andreas Weibye <13300393+Weibye@users.noreply.github.com>
Co-authored-by: TimJentzsch <tim-jentzsch@gmx.de>

de9123e02b894f0fc6309998feccc31683fd4c2b · by Alice Cecile · (4 years ago) · parent 0d46716fe · browse the tree at this commit

Semantic delta · 40

src/flexbox.rs

src/forest.rs

src/geometry.rs

src/lib.rs

src/node.rs

10 files · +560 −101

diff --git a/src/flexbox.rs b/src/flexbox.rs
index 0f7a5a4fb..14d196b93 100644
--- a/src/flexbox.rs
+++ b/src/flexbox.rs
@@ -1,3 +1,6 @@
+//! Computes the [flexbox](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) layout algorithm on a [`Forest`](crate::forest::Forest) according to the [spec](https://www.w3.org/TR/css-flexbox-1/)
+//!
+//! Note that some minor steps appear to be missing: see https://github.com/DioxusLabs/sprawl/issues for more information.
 use core::f32;
 
 use crate::flexbox::Number::{Defined, Undefined};
@@ -11,65 +14,104 @@ use crate::style::{AlignContent, AlignSelf, Dimension, Display, FlexWrap, Justif
 use crate::style::{FlexDirection, Style};
 use crate::sys::{abs, round, ChildrenVec, Vec};
 
+/// The computed results of a [`flexbox`](crate::flexbox) calculation
 #[derive(Debug, Clone)]
 pub struct ComputeResult {
+    /// The computed size
     pub size: Size<f32>,
 }
 
+/// The intermediate results of a flexbox calculation for a single item
 struct FlexItem {
+    /// The identifier for the associated [`Node`](crate::node::Node)
     node: NodeId,
 
+    /// The base size of this item
     size: Size<Number>,
+    /// The minimum allowable size of this item
     min_size: Size<Number>,
+    /// The maximum allowable size of this item
     max_size: Size<Number>,
 
+    /// The final offset of this item
     position: Rect<Number>,
+    /// The margin of this item
     margin: Rect<f32>,
+    /// The padding of this item
     padding: Rect<f32>,
+    /// The border of this item
     border: Rect<f32>,
 
+    /// The default size of this item
     flex_basis: f32,
+    /// The default size of this item, minus padding and border
     inner_flex_basis: f32,
+    /// The amount by which this item has deviated from its target size
     violation: f32,
+    /// Is the size of this item locked
     frozen: bool,
 
+    /// The proposed inner size of this item
     hypothetical_inner_size: Size<f32>,
+    /// The proposed outer size of this item
     hypothetical_outer_size: Size<f32>,
+    /// The size that this item wants to be
     target_size: Size<f32>,
+    /// The size that this item wants to be, plus any padding and border
     outer_target_size: Size<f32>,
 
+    /// The position of the bottom edge of this item
     baseline: f32,
 
-    // temporary values for holding offset in the main / cross direction.
-    // offset is the relative position from the item's natural flow position based on
-    // relative position values, alignment, and justification. Does not include margin/padding/border.
+    /// A temporary values for holding offset in the main / cross direction.
+
+    /// A temporary value for the main offset
+    ///
+    /// Offset is the relative position from the item's natural flow position based on
+    /// relative position values, alignment, and justification. Does not include margin/padding/border.
     offset_main: f32,
+    /// A temporary value for the cross offset
     offset_cross: f32,
 }
 
+/// A line of [`FlexItem`] used for intermediate computation
 struct FlexLine<'a> {
+    /// The slice of items to
     items: &'a mut [FlexItem],
+    /// The dimensions of the cross-axis
     cross_size: f32,
+    /// The relative offset of the cross-axis
     offset_cross: f32,
 }
 
-/// Constant values that can be reused for the flexbox algorithm.
+/// Values that can be cached during the flexbox algorithm
 struct AlgoConstants {
+    /// The direction of the current segment being layed out
     dir: FlexDirection,
+    /// Is this segment a row
     is_row: bool,
+    /// Is this segment a column
     is_column: bool,
+    /// Is the wrap direction inverted
     is_wrap_reverse: bool,
 
+    /// The margin of this section
     margin: Rect<f32>,
+    /// The border of this section
     border: Rect<f32>,
+    /// The padding of this section
     padding_border: Rect<f32>,
 
+    /// The size of the internal node
     node_inner_size: Size<Number>,
+    /// The size of the surrounding container
     container_size: Size<f32>,
+    /// The size of the internal container
     inner_container_size: Size<f32>,
 }
 
 impl Forest {
+    /// Computes the layout of this [`Forest`] according to the flexbox algorithm
     pub(crate) fn compute(&mut self, root: NodeId, size: Size<Number>) {
         let style = self.nodes[root].style;
         let has_root_min_max = style.min_size.width.is_defined()
@@ -78,9 +120,9 @@ impl Forest {
             || style.max_size.height.is_defined();
 
         let result = if has_root_min_max {
-            let first_pass = self.compute_internal(root, style.size.resolve(size), size, false, true);
+            let first_pass = self.compute_preliminary(root, style.size.resolve(size), size, false, true);
 
-            self.compute_internal(
+            self.compute_preliminary(
                 root,
                 Size {
                     width: first_pass
@@ -101,7 +143,7 @@ impl Forest {
                 true,
             )
         } else {
-            self.compute_internal(root, style.size.resolve(size), size, true, true)
+            self.compute_preliminary(root, style.size.resolve(size), size, true, true)
         };
 
         self.nodes[root].layout = Layout { order: 0, size: result.size, location: Point::zero() };
@@ -109,6 +151,7 @@ impl Forest {
         Self::round_layout(&mut self.nodes, &self.children, root, 0.0, 0.0);
     }
 
+    /// Rounds the calculated [`NodeData`] according to the spec
     fn round_layout(nodes: &mut [NodeData], children: &[ChildrenVec<NodeId>], root: NodeId, abs_x: f32, abs_y: f32) {
         let layout = &mut nodes[root].layout;
         let abs_x = abs_x + layout.location.x;
@@ -125,6 +168,7 @@ impl Forest {
         }
     }
 
+    /// Saves intermediate results to a [`Cache`]
     fn cache(&mut self, node: NodeId, main_size: bool) -> &mut Option<Cache> {
         if main_size {
             &mut self.nodes[node].main_size_layout_cache
@@ -190,8 +234,8 @@ impl Forest {
         };
 
         let node_inner_size = Size {
-            width: node_size.width - padding_border.horizontal(),
-            height: node_size.height - padding_border.vertical(),
+            width: node_size.width - padding_border.horizontal_axis_sum(),
+            height: node_size.height - padding_border.vertical_axis_sum(),
         };
 
         let container_size = Size::zero();
@@ -269,10 +313,10 @@ impl Forest {
         constants: &AlgoConstants,
     ) -> Size<Number> {
         Size {
-            width: node_size.width.or_else(parent_size.width - constants.margin.horizontal())
-                - constants.padding_border.horizontal(),
-            height: node_size.height.or_else(parent_size.height - constants.margin.vertical())
-                - constants.padding_border.vertical(),
+            width: node_size.width.or_else(parent_size.width - constants.margin.horizontal_axis_sum())
+                - constants.padding_border.horizontal_axis_sum(),
+            height: node_size.height.or_else(parent_size.height - constants.margin.vertical_axis_sum())
+                - constants.padding_border.vertical_axis_sum(),
         }
     }
 
@@ -380,7 +424,7 @@ impl Forest {
             };
 
             child.flex_basis = self
-                .compute_internal(
+                .compute_preliminary(
                     child.node,
                     Size {
                         width: width.maybe_max(child.min_size.width).maybe_min(child.max_size.width),
@@ -400,15 +444,16 @@ impl Forest {
         // used min and max main sizes (and flooring the content box size at zero).
 
         for child in flex_items {
-            child.inner_flex_basis =
-                child.flex_basis - child.padding.main(constants.dir) - child.border.main(constants.dir);
+            child.inner_flex_basis = child.flex_basis
+                - child.padding.main_axis_sum(constants.dir)
+                - child.border.main_axis_sum(constants.dir);
 
             // TODO - not really spec abiding but needs to be done somewhere. probably somewhere else though.
             // The following logic was developed not from the spec but by trail and error looking into how
             // webkit handled various scenarios. Can probably be solved better by passing in
             // min-content max-content constraints from the top
             let min_main = self
-                .compute_internal(child.node, Size::undefined(), available_space, false, false)
+                .compute_preliminary(child.node, Size::undefined(), available_space, false, false)
                 .size
                 .main(constants.dir)
                 .maybe_max(child.min_size.main(constants.dir))
@@ -422,7 +467,7 @@ impl Forest {
 
             child.hypothetical_outer_size.set_main(
                 constants.dir,
-                child.hypothetical_inner_size.main(constants.dir) + child.margin.main(constants.dir),
+                child.hypothetical_inner_size.main(constants.dir) + child.margin.main_axis_sum(constants.dir),
             );
         }
     }
@@ -517,7 +562,7 @@ impl Forest {
             if constants.node_inner_size.main(constants.dir).is_undefined() && constants.is_row {
                 child.target_size.set_main(
                     constants.dir,
-                    self.compute_internal(
+                    self.compute_preliminary(
                         child.node,
                         Size {
                             width: child.size.width.maybe_max(child.min_size.width).maybe_min(child.max_size.width),
@@ -539,9 +584,10 @@ impl Forest {
             // TODO this should really only be set inside the if-statement below but
             // that causes the target_main_size to never be set for some items
 
-            child
-                .outer_target_size
-                .set_main(constants.dir, child.target_size.main(constants.dir) + child.margin.main(constants.dir));
+            child.outer_target_size.set_main(
+                constants.dir,
+                child.target_size.main(constants.dir) + child.margin.main_axis_sum(constants.dir),
+            );
 
             let child_style = &self.nodes[child.node].style;
             if (child_style.flex_grow == 0.0 && child_style.flex_shrink == 0.0)
@@ -560,7 +606,7 @@ impl Forest {
             .items
             .iter()
             .map(|child| {
-                child.margin.main(constants.dir)
+                child.margin.main_axis_sum(constants.dir)
                     + if child.frozen { child.target_size.main(constants.dir) } else { child.flex_basis }
             })
             .sum();
@@ -587,7 +633,7 @@ impl Forest {
                 .items
                 .iter()
                 .map(|child| {
-                    child.margin.main(constants.dir)
+                    child.margin.main_axis_sum(constants.dir)
                         + if child.frozen { child.target_size.main(constants.dir) } else { child.flex_basis }
                 })
                 .sum();
@@ -668,7 +714,7 @@ impl Forest {
                 // min-content max-content constraints from the top. Need to figure out correct thing to do here as
                 // just piling on more conditionals.
                 let min_main = if constants.is_row && self.nodes[child.node].measure.is_none() {
-                    self.compute_internal(child.node, Size::undefined(), available_space, false, false)
+                    self.compute_preliminary(child.node, Size::undefined(), available_space, false, false)
                         .size
                         .width
                         .maybe_min(child.size.width)
@@ -682,9 +728,10 @@ impl Forest {
                 let clamped = child.target_size.main(constants.dir).maybe_min(max_main).maybe_max(min_main).max(0.0);
                 child.violation = clamped - child.target_size.main(constants.dir);
                 child.target_size.set_main(constants.dir, clamped);
-                child
-                    .outer_target_size
-                    .set_main(constants.dir, child.target_size.main(constants.dir) + child.margin.main(constants.dir));
+                child.outer_target_size.set_main(
+                    constants.dir,
+                    child.target_size.main(constants.dir) + child.margin.main_axis_sum(constants.dir),
+                );
 
                 acc + child.violation
             });
@@ -732,7 +779,7 @@ impl Forest {
 
             child.hypothetical_inner_size.set_cross(
                 constants.dir,
-                self.compute_internal(
+                self.compute_preliminary(
                     child.node,
                     Size {
                         width: if constants.is_row { child.target_size.width.into() } else { child_cross },
@@ -761,7 +808,7 @@ impl Forest {
 
             child.hypothetical_outer_size.set_cross(
                 constants.dir,
-                child.hypothetical_inner_size.cross(constants.dir) + child.margin.cross(constants.dir),
+                child.hypothetical_inner_size.cross(constants.dir) + child.margin.cross_axis_sum(constants.dir),
             );
         }
     }
@@ -775,6 +822,7 @@ impl Forest {
         flex_lines: &mut [FlexLine],
         constants: &AlgoConstants,
     ) {
+        /// Recursively caluculates the baseline for children
         fn calc_baseline(db: &Forest, node: NodeId, layout: &Layout) -> f32 {
             if db.children[node].is_empty() {
                 layout.size.height
@@ -786,7 +834,7 @@ impl Forest {
 
         for line in flex_lines {
             for child in line.items.iter_mut() {
-                let result = self.compute_internal(
+                let result = self.compute_preliminary(
                     child.node,
                     Size {
                         width: if constants.is_row {
@@ -855,7 +903,7 @@ impl Forest {
     ) {
         if flex_lines.len() == 1 && node_size.cross(constants.dir).is_defined() {
             flex_lines[0].cross_size =
-                (node_size.cross(constants.dir) - constants.padding_border.cross(constants.dir)).or_else(0.0);
+                (node_size.cross(constants.dir) - constants.padding_border.cross_axis_sum(constants.dir)).or_else(0.0);
         } else {
             for line in flex_lines.iter_mut() {
                 //    1. Collect all the flex items whose inline-axis is parallel to the main-axis, whose
@@ -910,7 +958,7 @@ impl Forest {
         {
             let total_cross: f32 = flex_lines.iter().map(|line| line.cross_size).sum();
             let inner_cross =
-                (node_size.cross(constants.dir) - constants.padding_border.cross(constants.dir)).or_else(0.0);
+                (node_size.cross(constants.dir) - constants.padding_border.cross_axis_sum(constants.dir)).or_else(0.0);
 
             if total_cross < inner_cross {
                 let remaining = inner_cross - total_cross;
@@ -945,7 +993,7 @@ impl Forest {
                         && child_style.cross_margin_end(constants.dir) != Dimension::Auto
                         && child_style.cross_size(constants.dir) == Dimension::Auto
                     {
-                        (line_cross_size - child.margin.cross(constants.dir))
+                        (line_cross_size - child.margin.cross_axis_sum(constants.dir))
                             .maybe_max(child.min_size.cross(constants.dir))
                             .maybe_min(child.max_size.cross(constants.dir))
                     } else {
@@ -955,7 +1003,7 @@ impl Forest {
 
                 child.outer_target_size.set_cross(
                     constants.dir,
-                    child.target_size.cross(constants.dir) + child.margin.cross(constants.dir),
+                    child.target_size.cross(constants.dir) + child.margin.cross_axis_sum(constants.dir),
                 );
             }
         }
@@ -1209,12 +1257,14 @@ impl Forest {
 
         constants.container_size.set_cross(
             constants.dir,
-            node_size.cross(constants.dir).or_else(total_cross_size + constants.padding_border.cross(constants.dir)),
+            node_size
+                .cross(constants.dir)
+                .or_else(total_cross_size + constants.padding_border.cross_axis_sum(constants.dir)),
         );
 
         constants.inner_container_size.set_cross(
             constants.dir,
-            constants.container_size.cross(constants.dir) - constants.padding_border.cross(constants.dir),
+            constants.container_size.cross(constants.dir) - constants.padding_border.cross_axis_sum(constants.dir),
         );
 
         total_cross_size
@@ -1296,7 +1346,7 @@ impl Forest {
             let line_offset_cross = line.offset_cross;
 
             let layout_item = |child: &mut FlexItem| {
-                let result = self.compute_internal(
+                let result = self.compute_preliminary(
                     child.node,
                     child.target_size.map(|s| s.into()),
                     constants.container_size.map(|s| s.into()),
@@ -1327,7 +1377,7 @@ impl Forest {
                 };
 
                 total_offset_main +=
-                    child.offset_main + child.margin.main(constants.dir) + result.size.main(constants.dir);
+                    child.offset_main + child.margin.main_axis_sum(constants.dir) + result.size.main(constants.dir);
             };
 
             if constants.dir.is_reverse() {
@@ -1399,7 +1449,7 @@ impl Forest {
                     Undefined
                 });
 
-            let result = self.compute_internal(
+            let result = self.compute_preliminary(
                 child,
                 Size { width, height },
                 Size { width: container_width, height: container_height },
@@ -1493,7 +1543,8 @@ impl Forest {
         }
     }
 
-    fn compute_internal(
+    /// Compute a preliminary size for an item
+    fn compute_preliminary(
         &mut self,
         node: NodeId,
         node_size: Size<Number>,
@@ -1530,8 +1581,8 @@ impl Forest {
 
             return ComputeResult {
                 size: Size {
-                    width: node_size.width.or_else(0.0) + constants.padding_border.horizontal(),
-                    height: node_size.height.or_else(0.0) + constants.padding_border.vertical(),
+                    width: node_size.width.or_else(0.0) + constants.padding_border.horizontal_axis_sum(),
+                    height: node_size.height.or_else(0.0) + constants.padding_border.vertical_axis_sum(),
                 },
             };
         }
@@ -1576,7 +1627,7 @@ impl Forest {
                     acc.max(length)
                 });
 
-                let size = longest_line + constants.padding_border.main(constants.dir);
+                let size = longest_line + constants.padding_border.main_axis_sum(constants.dir);
                 match available_space.main(constants.dir) {
                     Defined(val) if flex_lines.len() > 1 && size < val => val,
                     _ => size,
@@ -1586,7 +1637,7 @@ impl Forest {
 
         constants.inner_container_size.set_main(
             constants.dir,
-            constants.container_size.main(constants.dir) - constants.padding_border.main(constants.dir),
+            constants.container_size.main(constants.dir) - constants.padding_border.main_axis_sum(constants.dir),
         );
 
         // 9.4. Cross Size Determination
@@ -1657,6 +1708,9 @@ impl Forest {
         // Before returning we perform absolute layout on all absolutely positioned children
         self.perform_absolute_layout_on_absolute_children(node, &constants);
 
+        /// Lay out all hidden nodes recursively
+        ///
+        /// Each hidden node has zero size and is placed at the origin
         fn hidden_layout(nodes: &mut [NodeData], children: &[ChildrenVec<NodeId>], node: NodeId, order: u32) {
             nodes[node].layout = Layout { order, size: Size::zero(), location: Point::zero() };
 
diff --git a/src/forest.rs b/src/forest.rs
index f2aa6ebe3..6d983cf08 100644
--- a/src/forest.rs
+++ b/src/forest.rs
@@ -1,4 +1,4 @@
-//! Forest - ECS like datastructure for storing node trees.
+//! Forest - a struct-of-arrays data structure for storing node trees.
 //!
 //! Backing datastructure for `Sprawl` structs.
 use crate::geometry::Size;
@@ -8,16 +8,26 @@ use crate::number::Number;
 use crate::style::Style;
 use crate::sys::{new_vec_with_capacity, ChildrenVec, ParentsVec, Vec};
 
+/// Layout information for a given [`Node`](crate::node::Node)
+///
+/// Stored in a [`Forest`].
 pub(crate) struct NodeData {
+    /// The layout strategy used by this node
     pub(crate) style: Style,
+    /// The mapping from the Size<Number> (in units) to Size<f32> (in points) for this node
     pub(crate) measure: Option<MeasureFunc>,
+    /// The results of the layout computation
     pub(crate) layout: Layout,
+    /// The primary cached results of the layout computation
     pub(crate) main_size_layout_cache: Option<Cache>,
+    /// Secondary cached results of the layout computation
     pub(crate) other_layout_cache: Option<Cache>,
+    /// Does this node's layout need to be recomputed?
     pub(crate) is_dirty: bool,
 }
 
 impl NodeData {
+    /// Create the data for a new leaf node
     #[must_use]
     fn new_leaf(style: Style, measure: MeasureFunc) -> Self {
         Self {
@@ -30,6 +40,8 @@ impl NodeData {
         }
     }
 
+    /// Create the data for a new node
+    // TODO: why is this different from new_leaf?
     #[must_use]
     fn new(style: Style) -> Self {
         Self {
@@ -53,15 +65,24 @@ impl NodeData {
     }
 }
 
+/// A collection of UI layout trees used to store [`NodeData`] associated with specific [`Nodes`](crate::node::Node)
 pub(crate) struct Forest {
+    /// The [`NodeData`] for each node stored in this forest
     pub(crate) nodes: Vec<NodeData>,
+    /// The children of each node
+    ///
+    /// The indexes in the outer vector correspond to the position of the parent [`NodeData`]
     pub(crate) children: Vec<ChildrenVec<NodeId>>,
+    /// The parents of each node
+    ///
+    /// The indexes in the outer vector correspond to the position of the child [`NodeData`]
     pub(crate) parents: Vec<ParentsVec<NodeId>>,
 }
 
 impl Forest {
+    /// Creates a new [`Forest`] that can store up to `capacity` [`Nodes`](crate::node::Node) before needing to be expanded
     #[must_use]
-    pub fn with_capacity(capacity: usize) -> Self {
+    pub(crate) fn with_capacity(capacity: usize) -> Self {
         Self {
             nodes: new_vec_with_capacity(capacity),
             children: new_vec_with_capacity(capacity),
@@ -69,7 +90,8 @@ impl Forest {
         }
     }
 
-    pub fn new_leaf(&mut self, style: Style, measure: MeasureFunc) -> NodeId {
+    /// Adds a new unattached leaf node to the forest, and returns the [`NodeId`] of the new node
+    pub(crate) fn new_leaf(&mut self, style: Style, measure: MeasureFunc) -> NodeId {
         let id = self.nodes.len();
         self.nodes.push(NodeData::new_leaf(style, measure));
         self.children.push(new_vec_with_capacity(0));
@@ -77,7 +99,8 @@ impl Forest {
         id
     }
 
-    pub fn new_node(&mut self, style: Style, children: ChildrenVec<NodeId>) -> NodeId {
+    /// Adds a new unparented node to the forest with the associated children attached, and returns the [`NodeId`] of the new node
+    pub(crate) fn new_node(&mut self, style: Style, children: ChildrenVec<NodeId>) -> NodeId {
         let id = self.nodes.len();
         for child in &children {
             self.parents[*child].push(id);
@@ -88,20 +111,27 @@ impl Forest {
         id
     }
 
-    pub fn add_child(&mut self, node: NodeId, child: NodeId) {
-        self.parents[child].push(node);
-        self.children[node].push(child);
-        self.mark_dirty(node)
+    /// Adds a `child` node to the `parent` node
+    pub(crate) fn add_child(&mut self, parent: NodeId, child: NodeId) {
+        self.parents[child].push(parent);
+        self.children[parent].push(child);
+        self.mark_dirty(parent)
     }
 
-    pub fn clear(&mut self) {
+    /// Removes all nodes and resets the data structure
+    ///
+    /// The capacity is retained.
+    pub(crate) fn clear(&mut self) {
         self.nodes.clear();
         self.children.clear();
         self.parents.clear();
     }
 
-    /// Removes a node and swaps with the last node.
-    pub fn swap_remove(&mut self, node: NodeId) -> Option<NodeId> {
+    /// Removes the specified `node`
+    ///
+    /// The last existing node is moved to its previous position, in order to ensure compactness.
+    /// Returns the previous [`NodeId`] of the moved node, if one was moved.
+    pub(crate) fn swap_remove(&mut self, node: NodeId) -> Option<NodeId> {
         self.nodes.swap_remove(node);
 
         // Now the last element is swapped in at index `node`.
@@ -169,21 +199,31 @@ impl Forest {
         }
     }
 
-    pub fn remove_child(&mut self, node: NodeId, child: NodeId) -> NodeId {
-        let index = self.children[node].iter().position(|n| *n == child).unwrap();
-        self.remove_child_at_index(node, index)
+    /// Breaks the link between the `parent` node and the `child` node
+    ///
+    /// The `child`'s data is not removed.
+    pub(crate) fn remove_child(&mut self, parent: NodeId, child: NodeId) -> NodeId {
+        let index = self.children[parent].iter().position(|n| *n == child).unwrap();
+        self.remove_child_at_index(parent, index)
     }
 
-    pub fn remove_child_at_index(&mut self, node: NodeId, index: usize) -> NodeId {
-        let child = self.children[node].remove(index);
-        self.parents[child].retain(|p| *p != node);
-        self.mark_dirty(node);
+    /// Breaks the link between the `parent` node and the n-th child node
+    ///
+    /// The child's data is not removed.
+    pub(crate) fn remove_child_at_index(&mut self, parent: NodeId, child_index: usize) -> NodeId {
+        let child = self.children[parent].remove(child_index);
+        self.parents[child].retain(|p| *p != parent);
+        self.mark_dirty(parent);
         child
     }
 
-    pub fn mark_dirty(&mut self, node: NodeId) {
-        // Performs a recursive depth-first search up the tree until the root node is reached
-        // WARNING: this will stack-overflow if the tree contains a cycle
+    /// Marks the `node` as needing layout recalculation
+    ///
+    /// Any cached layout information is cleared.
+    pub(crate) fn mark_dirty(&mut self, node: NodeId) {
+        /// Performs a recursive depth-first search up the tree until the root node is reached
+        ///
+        ///  WARNING: this will stack-overflow if the tree contains a cycle
         fn mark_dirty_recursive(nodes: &mut Vec<NodeData>, parents: &[ParentsVec<NodeId>], node_id: NodeId) {
             nodes[node_id].mark_dirty();
 
@@ -195,7 +235,9 @@ impl Forest {
         mark_dirty_recursive(&mut self.nodes, &self.parents, node);
     }
 
-    pub fn compute_layout(&mut self, node: NodeId, size: Size<Number>) {
+    /// Computes the layout of the `node` and its children
+    pub(crate) fn compute_layout(&mut self, node: NodeId, size: Size<Number>) {
+        // TODO: It's not clear why this method is distinct
         self.compute(node, size)
     }
 }
diff --git a/src/geometry.rs b/src/geometry.rs
index 8f54463a0..55555948c 100644
--- a/src/geometry.rs
+++ b/src/geometry.rs
@@ -1,25 +1,51 @@
+//! Geometric primitives useful for layout
+
 use core::ops::Add;
 
 use crate::number::Number;
 use crate::style::{Dimension, FlexDirection};
 
+/// An axis-aligned UI rectangle
 #[derive(Debug, Copy, Clone, PartialEq)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 #[cfg_attr(feature = "serde", serde(default))]
 pub struct Rect<T> {
+    /// This can represent either the x-coordinate of the starting edge,
+    /// or the amount of padding on the starting side.
+    ///
+    /// The starting edge is the left edge when working with LTR text,
+    /// and the right edge when working with RTL text.
     pub start: T,
+    /// This can represent either the x-coordinate of the ending edge,
+    /// or the amount of padding on the ending side.
+    ///
+    /// The ending edge is the right edge when working with LTR text,
+    /// and the left edge when working with RTL text.
     pub end: T,
+    /// This can represent either the y-coordinate of the top edge,
+    /// or the amount of padding on the top side.
     pub top: T,
+    /// This can represent either the y-coordinate of the bottom edge,
+    /// or the amount of padding on the bottom side.
     pub bottom: T,
 }
 
 impl<T> Rect<T> {
+    /// Applies the function `f` to all four sides of the [`Rect`]
+    ///
+    /// This is used to transform a `Rect<T>` into a `Rect<R>`.
     pub(crate) fn map<R, F>(self, f: F) -> Rect<R>
     where
         F: Fn(T) -> R,
     {
         Rect { start: f(self.start), end: f(self.end), top: f(self.top), bottom: f(self.bottom) }
     }
+
+    /// Applies the function `f` to all four sides of the rect
+    ///
+    /// When applied to the left and right sides, the width is used
+    /// as the second parameter of `f`.
+    /// When applied to the top or bottom sides, the height is used instead.
     pub(crate) fn zip_size<R, F, U>(self, size: Size<U>, f: F) -> Rect<R>
     where
         F: Fn(T, U) -> R,
@@ -38,27 +64,47 @@ impl<T> Rect<T>
 where
     T: Add<Output = T> + Copy + Clone,
 {
-    pub(crate) fn horizontal(&self) -> T {
+    /// The sum of [`Rect.start`](Rect) and [`Rect.end`](Rect)
+    ///
+    /// This is typically used when computing total padding.
+    ///
+    /// **NOTE:** this is *not* the width of the rectangle.
+    pub(crate) fn horizontal_axis_sum(&self) -> T {
         self.start + self.end
     }
 
-    pub(crate) fn vertical(&self) -> T {
+    /// The sum of [`Rect.top`](Rect) and [`Rect.bottom`](Rect)
+    ///
+    /// This is typically used when computing total padding.
+    ///
+    /// **NOTE:** this is *not* the height of the rectangle.
+    pub(crate) fn vertical_axis_sum(&self) -> T {
         self.top + self.bottom
     }
 
-    pub(crate) fn main(&self, direction: FlexDirection) -> T {
+    /// The sum of the two fields of the [`Rect`] representing the main axis.
+    ///
+    /// This is typically used when computing total padding.
+    ///
+    /// If the [`FlexDirection`] is [`FlexDirection::Row`] or [`FlexDirection::RowReverse`], this is [`Rect::horizontal`].
+    /// Otherwise, this is [`Rect::vertical`].
+    pub(crate) fn main_axis_sum(&self, direction: FlexDirection) -> T {
         if direction.is_row() {
-            self.horizontal()
+            self.horizontal_axis_sum()
         } else {
-            self.vertical()
+            self.vertical_axis_sum()
         }
     }
 
-    pub(crate) fn cross(&self, direction: FlexDirection) -> T {
+    /// The sum of the two fields of the [`Rect`] representing the cross axis.
+    ///
+    /// If the [`FlexDirection`] is [`FlexDirection::Row`] or [`FlexDirection::RowReverse`], this is [`Rect::vertical`].
+    /// Otherwise, this is [`Rect::horizontal`].
+    pub(crate) fn cross_axis_sum(&self, direction: FlexDirection) -> T {
         if direction.is_row() {
-            self.vertical()
+            self.vertical_axis_sum()
         } else {
-            self.horizontal()
+            self.horizontal_axis_sum()
         }
     }
 }
@@ -67,6 +113,7 @@ impl<T> Rect<T>
 where
     T: Copy + Clone,
 {
+    /// The `start` or `top` value of the [`Rect`], from the perspective of the main layout axis
     pub(crate) fn main_start(&self, direction: FlexDirection) -> T {
         if direction.is_row() {
             self.start
@@ -75,6 +122,7 @@ where
         }
     }
 
+    /// The `end` or `bottom` value of the [`Rect`], from the perspective of the main layout axis
     pub(crate) fn main_end(&self, direction: FlexDirection) -> T {
         if direction.is_row() {
             self.end
@@ -83,6 +131,7 @@ where
         }
     }
 
+    /// The `start` or `top` value of the [`Rect`], from the perspective of the cross layout axis
     pub(crate) fn cross_start(&self, direction: FlexDirection) -> T {
         if direction.is_row() {
             self.top
@@ -91,6 +140,7 @@ where
         }
     }
 
+    /// The `end` or `bottom` value of the [`Rect`], from the perspective of the main layout axis
     pub(crate) fn cross_end(&self, direction: FlexDirection) -> T {
         if direction.is_row() {
             self.bottom
@@ -100,15 +150,19 @@ where
     }
 }
 
+/// The width and height of a [`Rect`]
 #[derive(Debug, Copy, Clone, PartialEq)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 #[cfg_attr(feature = "serde", serde(default))]
 pub struct Size<T> {
+    /// The x extent of the rectangle
     pub width: T,
+    /// The y extent of the rectangle
     pub height: T,
 }
 
 impl Size<()> {
+    /// Generates a `Size<Number>` with undefined width and height
     #[must_use]
     pub fn undefined() -> Size<Number> {
         Size { width: Number::Undefined, height: Number::Undefined }
@@ -116,6 +170,9 @@ impl Size<()> {
 }
 
 impl<T> Size<T> {
+    /// Applies the function `f` to both the width and height
+    ///
+    /// This is used to transform a `Rect<T>` into a `Rect<R>`.
     pub fn map<R, F>(self, f: F) -> Size<R>
     where
         F: Fn(T) -> R,
@@ -123,6 +180,9 @@ impl<T> Size<T> {
         Size { width: f(self.width), height: f(self.height) }
     }
 
+    /// Sets the extent of the main layout axis
+    ///
+    /// Whether this is the width or height depends on the `direction` provided
     pub(crate) fn set_main(&mut self, direction: FlexDirection, value: T) {
         if direction.is_row() {
             self.width = value
@@ -131,6 +191,9 @@ impl<T> Size<T> {
         }
     }
 
+    /// Sets the extent of the cross layout axis
+    ///
+    /// Whether this is the width or height depends on the `direction` provided
     pub(crate) fn set_cross(&mut self, direction: FlexDirection, value: T) {
         if direction.is_row() {
             self.height = value
@@ -139,6 +202,9 @@ impl<T> Size<T> {
         }
     }
 
+    /// Gets the extent of the main layout axis
+    ///
+    /// Whether this is the width or height depends on the `direction` provided
     pub(crate) fn main(self, direction: FlexDirection) -> T {
         if direction.is_row() {
             self.width
@@ -147,6 +213,9 @@ impl<T> Size<T> {
         }
     }
 
+    /// Gets the extent of the cross layout axis
+    ///
+    /// Whether this is the width or height depends on the `direction` provided
     pub(crate) fn cross(self, direction: FlexDirection) -> T {
         if direction.is_row() {
             self.height
@@ -157,6 +226,7 @@ impl<T> Size<T> {
 }
 
 impl Size<f32> {
+    /// A [`Size`] with zero width and height
     #[must_use]
     pub fn zero() -> Self {
         Self { width: 0.0, height: 0.0 }
@@ -164,18 +234,25 @@ impl Size<f32> {
 }
 
 impl Size<Dimension> {
+    /// Converts any `parent`-relative values for size into an absolute size
     pub(crate) fn resolve(&self, parent: Size<Number>) -> Size<Number> {
         Size { width: self.width.resolve(parent.width), height: self.height.resolve(parent.height) }
     }
 }
 
+/// A 2-dimensional coordinate.
+///
+/// When used in association with a [`Rect`], represents the bottom-left corner.
 #[derive(Debug, Copy, Clone, PartialEq)]
 pub struct Point<T> {
+    /// The x-coordinate
     pub x: T,
+    /// The y-coordinate
     pub y: T,
 }
 
 impl Point<f32> {
+    /// A [`Point`] with values (0,0), representing the origin
     #[must_use]
     pub fn zero() -> Self {
         Self { x: 0.0, y: 0.0 }
diff --git a/src/layout.rs b/src/layout.rs
index 0f77546ff..b09b859f4 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -1,3 +1,5 @@
+//! Final and cached data structures that represent the high-level UI layout
+
 use crate::flexbox::ComputeResult;
 use crate::geometry::{Point, Size};
 use crate::number::Number;
@@ -10,24 +12,30 @@ pub struct Layout {
     /// Nodes with a higher order should be rendered on top of those with a lower order.
     /// This is effectively a topological sort of each tree.
     pub order: u32,
-    // The width and height of the node
+    /// The width and height of the node
     pub size: Size<f32>,
-    // The bottom-left corner of the node
+    /// The bottom-left corner of the node
     pub location: Point<f32>,
 }
 
 impl Layout {
+    /// Creates a new [`Layout`] struct with zero size positioned at the origin
     #[must_use]
     pub(crate) fn new() -> Self {
         Self { order: 0, size: Size::zero(), location: Point::zero() }
     }
 }
 
+/// Cached intermediate layout results
 #[derive(Debug, Clone)]
 pub(crate) struct Cache {
+    /// The initial cached size of the node itself
     pub(crate) node_size: Size<Number>,
+    /// The initial cached size of the parent's node
     pub(crate) parent_size: Size<Number>,
+    /// Whether or not layout should be recomputed
     pub(crate) perform_layout: bool,
 
+    /// The stored result of the layout calculations
     pub(crate) result: ComputeResult,
 }
diff --git a/src/lib.rs b/src/lib.rs
index 0b3335851..ed5ec0143 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,8 @@
+#![doc = include_str!("../README.md")]
 #![cfg_attr(not(feature = "std"), no_std)]
 #![deny(unsafe_code)]
+#![warn(missing_docs)]
+#![warn(clippy::missing_docs_in_private_items)]
 
 #[cfg(all(not(feature = "std"), feature = "alloc"))]
 extern crate alloc;
@@ -17,18 +20,19 @@ pub mod style;
 
 mod flexbox;
 mod forest;
-mod sys;
-
 #[cfg(all(not(feature = "alloc"), not(feature = "std")))]
 mod indexmap;
+mod sys;
+
+pub use crate::node::Sprawl;
 
 #[cfg(feature = "std")]
 use core::fmt::{Display, Formatter, Result};
 
-pub use crate::node::Sprawl;
-
+/// An error that can occur when performing layout
 #[derive(Debug)]
 pub enum Error {
+    /// The [`Node`](node::Node) was invalid
     InvalidNode(node::Node),
 }
 
@@ -45,7 +49,7 @@ impl Display for Error {
 impl std::error::Error for Error {
     fn description(&self) -> &str {
         match *self {
-            Error::InvalidNode(_) => "The node is not part of the sprawl instance",
+            Error::InvalidNode(_) => "The node is not part of the Sprawl instance",
         }
     }
 }
diff --git a/src/node.rs b/src/node.rs
index 299ca8c64..477ea4878 100644
--- a/src/node.rs
+++ b/src/node.rs
@@ -1,3 +1,6 @@
+//! UI [`Node`] types and related data structures.
+//!
+//! Layouts are composed of multiple nodes, which live in a forest-like data structure.
 use crate::forest::Forest;
 use crate::geometry::Size;
 use crate::layout::Layout;
@@ -9,8 +12,11 @@ use crate::sys::{new_map_with_capacity, ChildrenVec, Map, Vec};
 use crate::Error;
 use core::sync::atomic::{AtomicUsize, Ordering};
 
+/// A function that can be applied to a `Size<Number>` to obtain a `Size<f32>`
 pub enum MeasureFunc {
+    /// Stores an unboxed function
     Raw(fn(Size<Number>) -> Size<f32>),
+    /// Stores a boxed function
     #[cfg(any(feature = "std", feature = "alloc"))]
     Boxed(Box<dyn Fn(Size<Number>) -> Size<f32>>),
 }
@@ -18,18 +24,27 @@ pub enum MeasureFunc {
 /// Global sprawl instance id allocator.
 static INSTANCE_ALLOCATOR: Allocator = Allocator::new();
 
+/// An [`Id`]-containing identifier
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 #[cfg_attr(not(any(feature = "std", feature = "alloc")), derive(hash32_derive::Hash32))]
 pub struct Node {
+    /// The id of the forest that this node lives with
     instance: Id,
+    /// The identifier of this particular node within the tree
     local: Id,
 }
 
+/// A forest of UI [`Nodes`](`Node`), suitable for UI layout
 pub struct Sprawl {
+    /// The ID of the root node
     id: Id,
-    nodes: Allocator,
+    /// A monotonically-increasing index that tracks the [`Id`] of the next node
+    allocator: Allocator,
+    /// A map from Node -> NodeId
     nodes_to_ids: Map<Node, NodeId>,
+    /// A map from NodeId -> Node
     ids_to_nodes: Map<NodeId, Node>,
+    /// An efficient data structure that stores the node trees
     forest: Forest,
 }
 
@@ -40,32 +55,38 @@ impl Default for Sprawl {
 }
 
 impl Sprawl {
+    /// Creates a new [`Sprawl`]
+    ///
+    /// The default capacity of a [`Sprawl`] is 16 nodes.
     #[must_use]
     pub fn new() -> Self {
         Default::default()
     }
 
+    /// Creates a new [`Sprawl`] that can store `capacity` nodes before reallocation
     pub fn with_capacity(capacity: usize) -> Self {
         Self {
             id: INSTANCE_ALLOCATOR.allocate(),
-            nodes: Allocator::new(),
+            allocator: Allocator::new(),
             nodes_to_ids: new_map_with_capacity(capacity),
             ids_to_nodes: new_map_with_capacity(capacity),
             forest: Forest::with_capacity(capacity),
         }
     }
 
+    /// Allocates memory for a new node, and returns a matching generated [`Node`]
     fn allocate_node(&mut self) -> Node {
-        let local = self.nodes.allocate();
+        let local = self.allocator.allocate();
         Node { instance: self.id, local }
     }
 
+    /// Stores a new node in the tree
     fn add_node(&mut self, node: Node, id: NodeId) {
         let _ = self.nodes_to_ids.insert(node, id);
         let _ = self.ids_to_nodes.insert(id, node);
     }
 
-    // Find node in the forest.
+    /// Returns the `NodeId` of the provided node within the forest
     fn find_node(&self, node: Node) -> Result<NodeId, Error> {
         match self.nodes_to_ids.get(&node) {
             Some(id) => Ok(*id),
@@ -73,6 +94,7 @@ impl Sprawl {
         }
     }
 
+    /// Adds a new leaf node, which does not have any children
     pub fn new_leaf(&mut self, style: Style, measure: MeasureFunc) -> Result<Node, Error> {
         let node = self.allocate_node();
         let id = self.forest.new_leaf(style, measure);
@@ -80,6 +102,7 @@ impl Sprawl {
         Ok(node)
     }
 
+    /// Adds a new node, which may have any number of `children`
     pub fn new_node(&mut self, style: Style, children: &[Node]) -> Result<Node, Error> {
         let node = self.allocate_node();
         let children =
@@ -89,16 +112,18 @@ impl Sprawl {
         Ok(node)
     }
 
-    /// Removes all nodes.
+    /// Removes all nodes
     ///
-    /// All associated nodes will be invalid.
+    /// All associated [`Id`] will be rendered invalid.
     pub fn clear(&mut self) {
         self.nodes_to_ids.clear();
         self.ids_to_nodes.clear();
         self.forest.clear();
     }
 
-    /// Remove nodes.
+    /// Remove a specific [`Node`] from the tree
+    ///
+    /// Its [`Id`] is marked as invalid.
     pub fn remove(&mut self, node: Node) {
         let id = if let Ok(id) = self.find_node(node) { id } else { return };
 
@@ -112,6 +137,7 @@ impl Sprawl {
         }
     }
 
+    /// Sets the [`MeasureFunc`] of the associated node
     pub fn set_measure(&mut self, node: Node, measure: Option<MeasureFunc>) -> Result<(), Error> {
         let id = self.find_node(node)?;
         self.forest.nodes[id].measure = measure;
@@ -119,6 +145,7 @@ impl Sprawl {
         Ok(())
     }
 
+    /// Adds a `child` [`Node`] under the supplied `parent`
     pub fn add_child(&mut self, parent: Node, child: Node) -> Result<(), Error> {
         let node_id = self.find_node(parent)?;
         let child_id = self.find_node(child)?;
@@ -127,6 +154,7 @@ impl Sprawl {
         Ok(())
     }
 
+    /// Directly sets the `children` of the supplied `parent`
     pub fn set_children(&mut self, parent: Node, children: &[Node]) -> Result<(), Error> {
         let node_id = self.find_node(parent)?;
         let children_id = children.iter().map(|child| self.find_node(*child)).collect::<Result<ChildrenVec<_>, _>>()?;
@@ -146,6 +174,9 @@ impl Sprawl {
         Ok(())
     }
 
+    /// Removes the `child` of the parent `node`
+    ///
+    /// The child is not removed from the forest entirely, it is simply no longer attached to its previous parent.
     pub fn remove_child(&mut self, parent: Node, child: Node) -> Result<Node, Error> {
         let node_id = self.find_node(parent)?;
         let child_id = self.find_node(child)?;
@@ -154,6 +185,9 @@ impl Sprawl {
         Ok(self.ids_to_nodes[&prev_id])
     }
 
+    /// Removes the child at the given `index` from the `parent`
+    ///
+    /// The child is not removed from the forest entirely, it is simply no longer attached to its previous parent.
     pub fn remove_child_at_index(&mut self, parent: Node, child_index: usize) -> Result<Node, Error> {
         let node_id = self.find_node(parent)?;
         // TODO: index check
@@ -162,6 +196,9 @@ impl Sprawl {
         Ok(self.ids_to_nodes[&prev_id])
     }
 
+    /// Replaces the child at the given `child_index` from the `parent` node with the new `child` node
+    ///
+    /// The child is not removed from the forest entirely, it is simply no longer attached to its previous parent.
     pub fn replace_child_at_index(&mut self, parent: Node, child_index: usize, new_child: Node) -> Result<Node, Error> {
         let node_id = self.find_node(parent)?;
         let child_id = self.find_node(new_child)?;
@@ -176,21 +213,25 @@ impl Sprawl {
         Ok(self.ids_to_nodes[&old_child])
     }
 
-    pub fn children(&self, parent: Node) -> Result<Vec<Node>, Error> {
-        let id = self.find_node(parent)?;
-        Ok(self.forest.children[id].iter().map(|child| self.ids_to_nodes[child]).collect())
-    }
-
+    /// Returns the child [`Node`] of the parent `node` at the provided `child_index`
     pub fn child_at_index(&self, parent: Node, child_index: usize) -> Result<Node, Error> {
         let id = self.find_node(parent)?;
         Ok(self.ids_to_nodes[&self.forest.children[id][child_index]])
     }
 
+    /// Returns the number of children of the `parent` [`Node`]
     pub fn child_count(&self, parent: Node) -> Result<usize, Error> {
         let id = self.find_node(parent)?;
         Ok(self.forest.children[id].len())
     }
 
+    /// Returns a list of children that belong to the [`Parent`]
+    pub fn children(&self, parent: Node) -> Result<Vec<Node>, Error> {
+        let id = self.find_node(parent)?;
+        Ok(self.forest.children[id].iter().map(|child| self.ids_to_nodes[child]).collect())
+    }
+
+    /// Sets the [`Style`] of the provided `node`
     pub fn set_style(&mut self, node: Node, style: Style) -> Result<(), Error> {
         let id = self.find_node(node)?;
         self.forest.nodes[id].style = style;
@@ -198,6 +239,7 @@ impl Sprawl {
         Ok(())
     }
 
+    /// Gets the [`Style`] of the provided `node`
     pub fn style(&self, node: Node) -> Result<&Style, Error> {
         let id = self.find_node(node)?;
         Ok(&self.forest.nodes[id].style)
@@ -209,17 +251,20 @@ impl Sprawl {
         Ok(&self.forest.nodes[id].layout)
     }
 
+    /// Marks the layout computation of this node and its children as outdated
     pub fn mark_dirty(&mut self, node: Node) -> Result<(), Error> {
         let id = self.find_node(node)?;
         self.forest.mark_dirty(id);
         Ok(())
     }
 
+    /// Indicates whether the layout of this node (and its children) need to be recomputed
     pub fn dirty(&self, node: Node) -> Result<bool, Error> {
         let id = self.find_node(node)?;
         Ok(self.forest.nodes[id].is_dirty)
     }
 
+    /// Updates the stored layout of the provided `node` and its children
     pub fn compute_layout(&mut self, node: Node, size: Size<Number>) -> Result<(), Error> {
         let id = self.find_node(node)?;
         self.forest.compute_layout(id, size);
@@ -230,21 +275,26 @@ impl Sprawl {
 /// Internal node id.
 pub(crate) type NodeId = usize;
 
+/// The identifier of a [`Node`]
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 #[cfg_attr(not(any(feature = "std", feature = "alloc")), derive(hash32_derive::Hash32))]
 pub(crate) struct Id(usize);
 
+/// An bump-allocator index that tracks how many [`Nodes`](Node) have been allocated in a [`Sprawl`].
 pub(crate) struct Allocator {
-    new_id: AtomicUsize,
+    /// The last reserved [`NodeId`]
+    last_id: AtomicUsize,
 }
 
 impl Allocator {
+    /// Creates a fresh [`Allocator`]
     #[must_use]
     pub const fn new() -> Self {
-        Self { new_id: AtomicUsize::new(0) }
+        Self { last_id: AtomicUsize::new(0) }
     }
 
+    /// Allocates space for one more [`Node`]
     pub fn allocate(&self) -> Id {
-        Id(self.new_id.fetch_add(1, Ordering::Relaxed))
+        Id(self.last_id.fetch_add(1, Ordering::Relaxed))
     }
 }
diff --git a/src/number.rs b/src/number.rs
index 744e8c13d..48dd11ea7 100644
--- a/src/number.rs
+++ b/src/number.rs
@@ -1,13 +1,25 @@
+//! Contains the [`Number`] type and associated methods
+
 use core::ops::{Add, Div, Mul, Sub};
 
+/// A [`f32`] that may be undefined
+///
+/// NOTE: this should *really* be an [`Option<f32>`],
+/// but we're optimizing for backwards compatibility for now.
 #[derive(Copy, Clone, PartialEq, Debug)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 pub enum Number {
+    /// Pretend this is [`Some(f32)`]
+    ///
+    /// WARNING: the contained value may be NaN
     Defined(f32),
+    /// Pretend this is [`None`]
     Undefined,
 }
 
+/// An extension method used for [`Number`]
 pub trait OrElse<T> {
+    /// Returns the contained value if it is defined, otherwise returns `other`
     fn or_else(self, other: T) -> T;
 }
 
@@ -36,6 +48,7 @@ impl OrElse<Self> for Number {
 }
 
 impl Number {
+    /// Is the number defined?
     pub fn is_defined(self) -> bool {
         match self {
             Number::Defined(_) => true,
@@ -43,6 +56,7 @@ impl Number {
         }
     }
 
+    /// Is the number undefined?
     pub fn is_undefined(self) -> bool {
         match self {
             Number::Defined(_) => false,
@@ -51,8 +65,15 @@ impl Number {
     }
 }
 
+/// An extension trait for [`Number`]
 pub trait MinMax<In, Out> {
+    /// Returns the minimum of self and rhs
+    ///
+    /// If either either value is invalid, returns the other value.
     fn maybe_min(self, rhs: In) -> Out;
+    /// Returns the maximum of self and rhs
+    ///
+    /// If either either value is invalid, returns the other value.
     fn maybe_max(self, rhs: In) -> Out;
 }
 
diff --git a/src/prelude.rs b/src/prelude.rs
index abc9c55ee..153f727c7 100644
--- a/src/prelude.rs
+++ b/src/prelude.rs
@@ -1,3 +1,5 @@
+//! Commonly used types
+
 pub use crate::{
     geometry::{Rect, Size},
     layout::Layout,
diff --git a/src/style.rs b/src/style.rs
index 436173793..ff95ab96d 100644
--- a/src/style.rs
+++ b/src/style.rs
@@ -1,13 +1,25 @@
+//! A representation of [CSS layout properties](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) in Rust, used for flexbox layout
+
 use crate::geometry::{Rect, Size};
 use crate::number::Number;
 
+/// How [`Nodes`](crate::node::Node) are aligned relative to the cross axis
+///
+/// The default behavior is [`AlignItems::Stretch`].
+///
+/// [Specification](https://www.w3.org/TR/css-flexbox-1/#align-items-property)
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 pub enum AlignItems {
+    /// Items are packed toward the start of the cross axis
     FlexStart,
+    /// Items are packed toward the end of the cross axis
     FlexEnd,
+    /// Items are packed along the center of the cross axis
     Center,
+    /// Items are aligned such as their baselines align
     Baseline,
+    /// Stretch to fill the container
     Stretch,
 }
 
@@ -17,14 +29,27 @@ impl Default for AlignItems {
     }
 }
 
+/// Overrides the inherited [`AlignItems`] behavior for this node.
+///
+/// The behavior of any child nodes will be controlled by this node's [`AlignItems`] value.
+///
+/// The default behavior is [`AlignSelf::Auto`].
+///
+/// [Specification](https://www.w3.org/TR/css-flexbox-1/#align-items-property)
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 pub enum AlignSelf {
+    /// Inherits the [`AlignItems`] behavior of the parent
     Auto,
+    /// Items are packed toward the start of the cross axis
     FlexStart,
+    /// Items are packed toward the end of the cross axis
     FlexEnd,
+    /// Items are packed along the center of the cross axis
     Center,
+    /// Items are aligned such as their baselines align
     Baseline,
+    /// Distribute items evenly, but stretch them to fill the container
     Stretch,
 }
 
@@ -34,14 +59,26 @@ impl Default for AlignSelf {
     }
 }
 
+/// Sets the distribution of space between and around content items along the cross-axis
+///
+/// The default value is [`AlignContent::Stretch`].
+///
+/// [Specification](https://www.w3.org/TR/css-flexbox-1/#align-content-property)
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 pub enum AlignContent {
+    /// Items are packed toward the start of the cross axis
     FlexStart,
+    /// Items are packed toward the end of the cross axis
     FlexEnd,
+    /// Items are packed along the center of the cross axis
     Center,
+    /// Distribute items evenly, but stretch them to fill the container
     Stretch,
+    /// Distribute items evenly, such that the first and last item are aligned with the edges
     SpaceBetween,
+    /// Distribute items evenly,
+    /// such that the space between items is the same as the space between the first and last item and the edges
     SpaceAround,
 }
 
@@ -51,11 +88,16 @@ impl Default for AlignContent {
     }
 }
 
+/// Sets the layout used for the children of this node
+///
+/// [`Display::Flex`] is the default value.
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 pub enum Display {
+    /// The children will follow the flexbox layout algorithm
     #[cfg_attr(feature = "serde", serde(rename = "flex"))]
     Flex,
+    /// The children will not be laid out, and will follow absolute positioning
     #[cfg_attr(feature = "serde", serde(rename = "none"))]
     None,
 }
@@ -66,12 +108,35 @@ impl Default for Display {
     }
 }
 
+/// The direction of the flexbox layout main axis.
+///
+/// There are always two perpendicular layout axes: main (or primary) and cross (or secondary).
+/// Adding items will cause them to be positioned adjacent to each other along the main axis.
+/// By varying this value throughout your tree, you can create complex axis-aligned layouts.
+///
+/// Items are always aligned relative to the cross axis, and justified relative to the main axis.
+///
+/// The default behavior is [`FlexDirection::Row`].
+///
+/// [Specification](https://www.w3.org/TR/css-flexbox-1/#flex-direction-property)
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 pub enum FlexDirection {
+    /// Defines +x as the main axis
+    ///
+    /// Items will be added from left to right in a row.
     Row,
+    /// Defines +y as the main axis
+    ///
+    /// Items will be added from top to bottom in a column.
     Column,
+    /// Defines -x as the main axis
+    ///
+    /// Items will be added from right to left in a row.
     RowReverse,
+    /// Defines -y as the main axis
+    ///
+    /// Items will be added from bottom to top in a column.
     ColumnReverse,
 }
 
@@ -83,29 +148,45 @@ impl Default for FlexDirection {
 
 impl FlexDirection {
     #[inline]
+    /// Is the direction [`FlexDirection::Row`] or [`FlexDirection::RowReverse`]?
     pub(crate) fn is_row(self) -> bool {
         matches!(self, Self::Row | Self::RowReverse)
     }
 
     #[inline]
+    /// Is the direction [`FlexDirection::Column`] or [`FlexDirection::ColumnReverse`]?
     pub(crate) fn is_column(self) -> bool {
         matches!(self, Self::Column | Self::ColumnReverse)
     }
 
     #[inline]
+    /// Is the direction [`FlexDirection::RowReverse`] or [`FlexDirection::ColumnReverse`]?
     pub(crate) fn is_reverse(self) -> bool {
         matches!(self, Self::RowReverse | Self::ColumnReverse)
     }
 }
 
+/// Sets the distribution of space between and around content items along the main-axis
+///
+/// The default value is [`JustifyContent::FlexStart`].
+///
+/// [Specification](https://www.w3.org/TR/css-flexbox-1/#justify-content-property)
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 pub enum JustifyContent {
+    /// Items are packed toward the start of the main axis
     FlexStart,
+    /// Items are packed toward the end of the main axis
     FlexEnd,
+    /// Items are packed along the center of the main axis
     Center,
+    /// Distribute items evenly, such that the first and last item are aligned with the edges
     SpaceBetween,
+    /// Distribute items evenly,
+    /// such that the space between items is twice same as the space between the first and last item and the edges
     SpaceAround,
+    /// Distribute items evenly,
+    /// such that the space between items is the same as the space between the first and last item and the edges
     SpaceEvenly,
 }
 
@@ -115,10 +196,26 @@ impl Default for JustifyContent {
     }
 }
 
+/// The positioning strategy for this item.
+///
+/// This controls both how the origin is determined for the [`Style::position`] field,
+/// and whether or not the item will be controlled by flexbox's layout algorithm.
+///
+/// WARNING: this enum follows the behavior of [CSS's `position` property](https://developer.mozilla.org/en-US/docs/Web/CSS/position),
+/// which can be unintuitive.
+///
+/// [`PositionType::Relative`] is the default value, in contrast to the default behavior in CSS.
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 pub enum PositionType {
+    /// The offset is computed relative to the final position given by the layout algorithm.
+    /// Offsets do not affect the position of any other items; they are effectively a correction factor applied at the end.
     Relative,
+    /// The offset is computed relative to this item's closest positioned ancestor, if any.
+    /// Otherwise, it is placed relative to the origin.
+    /// No space is created for the item in the page layout, and its size will not be altered.
+    ///
+    /// WARNING: to opt-out of layouting entirely, you must use [`Display::None`] instead on your [`Style`] object.
     Absolute,
 }
 
@@ -128,11 +225,19 @@ impl Default for PositionType {
     }
 }
 
+/// Controls whether flex items are forced onto one line or can wrap onto multiple lines.
+///
+/// Defaults to [`FlexWrap::NoWrap`]
+///
+/// [Specification](https://www.w3.org/TR/css-flexbox-1/#flex-wrap-property)
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 pub enum FlexWrap {
+    /// Items will not wrap and stay on a single line
     NoWrap,
+    /// Items will wrap according to this item's [`FlexDirection`]
     Wrap,
+    /// Items will wrap in the oppposite direction to this item's [`FlexDirection`]
     WrapReverse,
 }
 
@@ -142,12 +247,22 @@ impl Default for FlexWrap {
     }
 }
 
+/// A unit of linear measurement
+///
+/// This is commonly combined with [`Rect`], [`Point`](crate::geometry::Point) and [`Size<T>`].
+/// The default value is [`Dimension::Undefined`].
 #[derive(Copy, Clone, PartialEq, Debug)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 pub enum Dimension {
+    /// The dimension is not given
     Undefined,
+    /// The dimension should be automatically computed
     Auto,
+    /// The dimension is stored in [points](https://en.wikipedia.org/wiki/Point_(typography))
+    ///
+    /// Each point is about 0.353 mm in size.
     Points(f32),
+    /// The dimension is stored in percentage relative to the parent item.
     Percent(f32),
 }
 
@@ -158,6 +273,7 @@ impl Default for Dimension {
 }
 
 impl Dimension {
+    /// Converts the given [`Dimension`] into a concrete value of points
     pub(crate) fn resolve(self, parent_dim: Number) -> Number {
         match self {
             Dimension::Points(points) => Number::Defined(points),
@@ -166,6 +282,7 @@ impl Dimension {
         }
     }
 
+    /// Is this value defined?
     pub(crate) fn is_defined(self) -> bool {
         matches!(self, Dimension::Points(_) | Dimension::Percent(_))
     }
@@ -183,28 +300,68 @@ impl Default for Size<Dimension> {
     }
 }
 
+/// The flexbox layout information for a single [`Node`](crate::node::Node).
+///
+/// The most important idea in flexbox is the notion of a "main" and "cross" axis, which are always perpendicular to each other.
+/// The orientation of these axes are controlled via the [`FlexDirection`] field of this struct.
+///
+/// This struct follows the [CSS equivalent](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox) directly;
+/// information about the behavior on the web should transfer directly.
+///
+/// Detailed information about the exact behavior of each of these fields
+/// can be found on [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS) by searching for the field name.
+/// The distinction between margin, padding and border is explained well in
+/// this [introduction to the box model](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Introduction_to_the_CSS_box_model).
+///
+/// If the behavior does not match the flexbox layout algorithm on the web, please file a bug!
 #[derive(Copy, Clone, PartialEq, Debug)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 #[cfg_attr(feature = "serde", serde(default))]
 pub struct Style {
+    /// What layout strategy should be used?
     pub display: Display,
+    /// What should the `position` value of this struct use as a base offset?
     pub position_type: PositionType,
+    /// Which direction does the main axis flow in?
     pub flex_direction: FlexDirection,
+    /// Should elements wrap, or stay in a single line?
     pub flex_wrap: FlexWrap,
+    /// How should items be aligned relative to the cross axis?
     pub align_items: AlignItems,
+    /// Should this item violate the cross axis alignment specified by its parent's [`AlignItems`]?
     pub align_self: AlignSelf,
+    /// How should content contained within this item be aligned relative to the cross axis?
     pub align_content: AlignContent,
+    /// How should items be aligned relative to the main axis?
     pub justify_content: JustifyContent,
+    /// How should the position of this element be tweaked relative to the layout defined?
     pub position: Rect<Dimension>,
+    /// How large should the margin be on each side?
     pub margin: Rect<Dimension>,
+    /// How large should the padding be on each side?
     pub padding: Rect<Dimension>,
+    /// How large should the border be on each side?
     pub border: Rect<Dimension>,
+    /// The relative rate at which this item grows when it is expanding to fill space
+    ///
+    /// 1.0 is the default value, and this value must be positive.
     pub flex_grow: f32,
+    /// The relative rate at which this item shrinks when it is contracting to fit into space
+    ///
+    /// 1.0 is the default value, and this value must be positive.
     pub flex_shrink: f32,
+    /// Sets the initial main axis size of the item
     pub flex_basis: Dimension,
+    /// Sets the initial size of the item
+    // TODO: why does this exist as distinct from flex_basis? How do they interact?
     pub size: Size<Dimension>,
+    /// Controls the minimum size of the item
     pub min_size: Size<Dimension>,
+    /// Controls the maximum size of the item
     pub max_size: Size<Dimension>,
+    /// Sets the preferred aspect ratio for the item
+    ///
+    /// The ratio is calculated as width divided by height.
     pub aspect_ratio: Number,
 }
 
@@ -235,6 +392,7 @@ impl Default for Style {
 }
 
 impl Style {
+    /// If the `direction` is row-oriented, the min width. Otherwise the min height
     pub(crate) fn min_main_size(&self, direction: FlexDirection) -> Dimension {
         if direction.is_row() {
             self.min_size.width
@@ -243,6 +401,7 @@ impl Style {
         }
     }
 
+    /// If the `direction` is row-oriented, the max width. Otherwise the max height
     pub(crate) fn max_main_size(&self, direction: FlexDirection) -> Dimension {
         if direction.is_row() {
             self.max_size.width
@@ -251,6 +410,7 @@ impl Style {
         }
     }
 
+    /// If the `direction` is row-oriented, the margin start. Otherwise the margin top
     pub(crate) fn main_margin_start(&self, direction: FlexDirection) -> Dimension {
         if direction.is_row() {
             self.margin.start
@@ -259,6 +419,7 @@ impl Style {
         }
     }
 
+    /// If the `direction` is row-oriented, the margin end. Otherwise the margin bottom
     pub(crate) fn main_margin_end(&self, direction: FlexDirection) -> Dimension {
         if direction.is_row() {
             self.margin.end
@@ -267,6 +428,7 @@ impl Style {
         }
     }
 
+    /// If the `direction` is row-oriented, the height. Otherwise the width
     pub(crate) fn cross_size(&self, direction: FlexDirection) -> Dimension {
         if direction.is_row() {
             self.size.height
@@ -275,6 +437,7 @@ impl Style {
         }
     }
 
+    /// If the `direction` is row-oriented, the min height. Otherwise the min width
     pub(crate) fn min_cross_size(&self, direction: FlexDirection) -> Dimension {
         if direction.is_row() {
             self.min_size.height
@@ -283,6 +446,7 @@ impl Style {
         }
     }
 
+    /// If the `direction` is row-oriented, the max height. Otherwise the max width
     pub(crate) fn max_cross_size(&self, direction: FlexDirection) -> Dimension {
         if direction.is_row() {
             self.max_size.height
@@ -291,6 +455,7 @@ impl Style {
         }
     }
 
+    /// If the `direction` is row-oriented, the margin top. Otherwise the margin start
     pub(crate) fn cross_margin_start(&self, direction: FlexDirection) -> Dimension {
         if direction.is_row() {
             self.margin.top
@@ -299,6 +464,7 @@ impl Style {
         }
     }
 
+    /// If the `direction` is row-oriented, the margin bottom. Otherwise the margin end
     pub(crate) fn cross_margin_end(&self, direction: FlexDirection) -> Dimension {
         if direction.is_row() {
             self.margin.bottom
@@ -307,6 +473,7 @@ impl Style {
         }
     }
 
+    /// Computes the final alignment of this item based on the parent's [`AlignItems`] and this item's [`AlignSelf`]
     pub(crate) fn align_self(&self, parent: &Style) -> AlignSelf {
         if self.align_self == AlignSelf::Auto {
             match parent.align_items {
diff --git a/src/sys.rs b/src/sys.rs
index d1ed6134d..1c54af08c 100644
--- a/src/sys.rs
+++ b/src/sys.rs
@@ -1,3 +1,5 @@
+//! Allocator-flexible data types
+
 // When std is enabled, prefer those types
 #[cfg(feature = "std")]
 pub use self::std::*;
@@ -10,14 +12,21 @@ pub use self::alloc::*;
 #[cfg(all(not(feature = "alloc"), not(feature = "std")))]
 pub use self::core::*;
 
+/// For when `std` is enabled
 #[cfg(feature = "std")]
 mod std {
+    /// An allocation-backend agnostic [`Box`] type
     pub type Box<A> = std::boxed::Box<A>;
+    /// An allocation-backend agnostic map type
     pub type Map<K, V> = std::collections::HashMap<K, V>;
+    /// An allocation-backend agnostic vector type
     pub type Vec<A> = std::vec::Vec<A>;
+    /// A vector of child nodes
     pub type ChildrenVec<A> = std::vec::Vec<A>;
+    /// A vector of parent nodes
     pub type ParentsVec<A> = std::vec::Vec<A>;
 
+    /// Creates a new map with the capacity for the specified number of items before it must be resized
     #[must_use]
     pub fn new_map_with_capacity<K, V>(capacity: usize) -> Map<K, V>
     where
@@ -26,68 +35,88 @@ mod std {
         Map::with_capacity(capacity)
     }
 
+    /// Creates a new vector with the capacity for the specified number of items before it must be resized    
     #[must_use]
     pub fn new_vec_with_capacity<A>(capacity: usize) -> Vec<A> {
         Vec::with_capacity(capacity)
     }
 
-    #[inline]
+    /// Rounds to the nearest whole number
     #[must_use]
     pub fn round(value: f32) -> f32 {
         value.round()
     }
 
-    #[inline]
+    /// Computes the absolute value
     #[must_use]
     pub fn abs(value: f32) -> f32 {
         value.abs()
     }
 }
 
+/// For when `alloc` but not `std` is enabled
 #[cfg(all(feature = "alloc", not(feature = "std")))]
 mod alloc {
     extern crate alloc;
 
+    /// An allocation-backend agnostic `Box` type
     pub type Box<A> = alloc::boxed::Box<A>;
+    /// An allocation-backend agnostic map type
     pub type Map<K, V> = hashbrown::HashMap<K, V>;
+    /// An allocation-backend agnostic vector type
     pub type Vec<A> = alloc::vec::Vec<A>;
+    /// A vector of child nodes
     pub type ChildrenVec<A> = alloc::vec::Vec<A>;
+    /// A vector of parent nodes
     pub type ParentsVec<A> = alloc::vec::Vec<A>;
 
+    /// Creates a new map with the capacity for the specified number of items before it must be resized
     #[must_use]
     pub fn new_map_with_capacity<K, V>(capacity: usize) -> Map<K, V> {
         Map::with_capacity(capacity)
     }
 
+    /// Creates a new vector with the capacity for the specified number of items before it must be resized
     #[must_use]
     pub fn new_vec_with_capacity<A>(capacity: usize) -> Vec<A> {
         Vec::with_capacity(capacity)
     }
 
-    #[inline]
+    /// Rounds to the nearest whole number
     #[must_use]
     pub fn round(value: f32) -> f32 {
         num_traits::float::FloatCore::round(value)
     }
 
-    #[inline]
+    /// Computes the absolute value
     #[must_use]
     pub fn abs(value: f32) -> f32 {
         num_traits::float::FloatCore::abs(value)
     }
 }
 
+/// For when neither `alloc` nor `std` is enabled
 #[cfg(all(not(feature = "alloc"), not(feature = "std")))]
 mod core {
+    /// The maximum number of nodes in the forest
     pub const MAX_NODE_COUNT: usize = 256;
+    /// The maximum number of children of any given node
     pub const MAX_CHILD_COUNT: usize = 16;
+    /// The maximum number of parents for each child
     pub const MAX_PARENTS_COUNT: usize = 1;
 
+    /// An allocation-backend agnostic map type
     pub type Map<K, V> = crate::indexmap::FnvIndexMap<K, V, MAX_NODE_COUNT>;
+    /// An allocation-backend agnostic vector type
     pub type Vec<A> = arrayvec::ArrayVec<A, MAX_NODE_COUNT>;
+    /// A vector of child nodes, whose length cannot exceed [`MAX_CHILD_COUNT`]
     pub type ChildrenVec<A> = arrayvec::ArrayVec<A, MAX_CHILD_COUNT>;
+    /// A vector of parent nodes, whose length cannot exceed [`MAX_PARENTS_COUNT`]
     pub type ParentsVec<A> = arrayvec::ArrayVec<A, MAX_PARENTS_COUNT>;
 
+    /// Creates a new map with the capacity for the specified number of items
+    ///
+    /// This map cannot be resized.
     #[must_use]
     pub fn new_map_with_capacity<K, V>(_capacity: usize) -> Map<K, V>
     where
@@ -96,17 +125,22 @@ mod core {
         Map::new()
     }
 
+    /// Creates a new map with the capacity for the specified number of items before it must be resized
+    ///
+    /// This vector cannot be resized.
     #[must_use]
     pub fn new_vec_with_capacity<A, const CAP: usize>(_capacity: usize) -> arrayvec::ArrayVec<A, CAP> {
         arrayvec::ArrayVec::new()
     }
 
+    /// Rounds to the nearest whole number
     #[inline]
     #[must_use]
     pub fn round(value: f32) -> f32 {
         num_traits::float::FloatCore::round(value)
     }
 
+    /// Computes the absolute value
     #[inline]
     #[must_use]
     pub fn abs(value: f32) -> f32 {