gioser

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 5364Rust 86Markdown 12TOML 91841 Definiciones

Basic test generation working

18777ef665cbb1f16d912e69eb8c17ba1ef9ba4f · por Emil Sjölander · (hace 7 años) · padre 494b1b4fd · ver el árbol en este commit

Delta semántico · 57

examples/basic.rs

  • cambió por dentro fn main

scripts/gentest/src/main.rs

scripts/gentest/test_helper.js

src/algo.rs

src/style.rs

tests/generated.rs

tests/lib.rs

tests/manual.rs

15 archivos · +2303 −1589

diff --git a/.gitignore b/.gitignore
index 693699042..8144c2f5e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,6 @@
 /target
+/scripts/gentest/target
+
 **/*.rs.bk
 Cargo.lock
+.DS_Store
diff --git a/examples/basic.rs b/examples/basic.rs
index 7289695d7..244bfca1c 100644
--- a/examples/basic.rs
+++ b/examples/basic.rs
@@ -6,13 +6,11 @@ fn main() {
         height: stretch::style::Dimension::Points(100.0),
         justify_content: stretch::style::JustifyContent::Center,
 
-        children: vec![
-            stretch::style::Node {
-                width: stretch::style::Dimension::Percent(0.5),
+        children: vec![stretch::style::Node {
+            width: stretch::style::Dimension::Percent(0.5),
 
-                ..Default::default()
-            },
-        ],
+            ..Default::default()
+        }],
 
         ..Default::default()
     };
@@ -21,4 +19,3 @@ fn main() {
 
     println!("{:#?}", layout);
 }
-
diff --git a/scripts/gentest/Cargo.toml b/scripts/gentest/Cargo.toml
new file mode 100644
index 000000000..fe97b86b3
--- /dev/null
+++ b/scripts/gentest/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "gentest"
+version = "0.1.0"
+authors = ["Emil Sjölander <emil@visly.app>"]
+
+[dependencies]
+selenium-rs = "0.1.0"
+json = "0.11.13"
diff --git a/scripts/gentest/selenium.jar b/scripts/gentest/selenium.jar
new file mode 100755
index 000000000..8410e9565
Binary files /dev/null and b/scripts/gentest/selenium.jar differ
diff --git a/scripts/gentest/src/main.rs b/scripts/gentest/src/main.rs
new file mode 100644
index 000000000..f2a5f76c3
--- /dev/null
+++ b/scripts/gentest/src/main.rs
@@ -0,0 +1,364 @@
+extern crate json;
+
+extern crate selenium_rs;
+use selenium_rs::element::Element;
+use selenium_rs::webdriver::{Browser, Selector, WebDriver};
+
+use std::process::Command;
+use std::path::Path;
+use std::fs;
+use std::{thread, time};
+
+fn main() {
+    // std::env::current_exe should be <path/to/repo>/scripts/gentest/target/debug/gentest
+    // move up five times to get to repo root
+    let root_dir = std::env::current_exe().unwrap();
+    let repo_root = root_dir.parent()
+        .and_then(Path::parent)
+        .and_then(Path::parent)
+        .and_then(Path::parent)
+        .and_then(Path::parent)
+        .unwrap();
+
+    let fixtures_root = repo_root.join("test_fixtures");
+
+    let mut selenium = Command::new("java")
+        .arg("-jar")
+        .arg("selenium.jar")
+        .current_dir(&format!("{}/scripts/gentest", repo_root.display()))
+        .spawn()
+        .expect("failed to execute selenium, most likely missing java");
+
+    // Wait for a bit until selenium is up and running...
+    let ten_millis = time::Duration::from_millis(2000);
+    thread::sleep(ten_millis);
+
+    let mut driver = WebDriver::new(Browser::Chrome);
+    driver.start_session();
+
+    let mut src = String::new();
+    src.push_str("#[cfg(test)]\n");
+    src.push_str("mod generated {\n");
+
+    let fixtures = fs::read_dir(fixtures_root).unwrap();
+    for fixture in fixtures {
+        let fixture_path = fixture.unwrap().path();
+        let name = fixture_path.file_stem().unwrap().to_str().unwrap();
+
+        driver.navigate(&format!("file://{}", fixture_path.display()));
+        let root_element = driver.query_element(Selector::CSS, "#test-root").unwrap();
+        src.push_str(&format!("{}\n", generate_test(&name, root_element)));
+    }
+    
+    src.push_str("}\n");
+    fs::write(repo_root.join("tests").join("generated.rs"), src);
+
+    Command::new("cargo")
+        .arg("fmt")
+        .current_dir(repo_root)
+        .status();
+
+    driver.delete_session();
+    selenium.kill();
+}
+
+fn generate_test(name: &str, root: Element) -> String {
+    let description_string = root.get_property("__stretch_description__").unwrap();
+    let description = json::parse(&description_string).unwrap();
+
+    let mut src = String::new();
+    src.push_str("#[test]\n");
+    src.push_str(&format!("fn {}() {{\n", name));
+    
+    src.push_str("let layout = stretch::compute(\n");
+    src.push_str(&format!("&{}", &generate_node(&description)));
+    src.push_str(");\n\n");
+
+    src.push_str(&generate_assertions("layout".to_string(), &description));
+    src.push_str("}\n");
+    src
+}
+
+fn generate_assertions(prefix: String, node: &json::JsonValue) -> String {
+    let layout = &node["layout"];
+    let mut src = String::new();
+
+    src.push_str(&format!("assert_eq!({}.width, {:.1});\n", prefix, layout["width"].as_f32().unwrap()));
+    src.push_str(&format!("assert_eq!({}.height, {:.1});\n", prefix, layout["height"].as_f32().unwrap()));
+    src.push_str(&format!("assert_eq!({}.x, {:.1});\n", prefix, layout["x"].as_f32().unwrap()));
+    src.push_str(&format!("assert_eq!({}.y, {:.1});\n\n", prefix, layout["y"].as_f32().unwrap()));
+
+    match node["children"] {
+        json::JsonValue::Array(ref value) => {
+            for i in 0..value.len() {
+                let child = &value[i];
+                src.push_str(&generate_assertions(format!("{}.children[{}]", prefix, i), child));
+            }
+        },
+        _ => (),
+    };
+    src
+}
+
+fn generate_node(node: &json::JsonValue) -> String {
+    let mut src = String::new();
+    src.push_str("stretch::style::Node {\n");
+
+    let style = &node["style"];
+
+    match style["position"] {
+        json::JsonValue::String(ref value) => {
+            match value.as_ref() {
+                "absolute" => src.push_str("position: stretch::style::Position::Absolute,\n"),
+                _ => (),
+            };
+        },
+        _ => (),
+    };
+
+    match style["direction"] {
+        json::JsonValue::String(ref value) => {
+            match value.as_ref() {
+                "rtl" => src.push_str("direction: stretch::style::Direction::RTL,\n"),
+                "ltr" => src.push_str("direction: stretch::style::Direction::LTR,\n"),
+                _ => (),
+            };
+        },
+        _ => (),
+    };
+
+    match style["flexDirection"] {
+        json::JsonValue::String(ref value) => {
+            match value.as_ref() {
+                "row-reverse" => src.push_str("flex_direction: stretch::style::FlexDirection::RowReverse,\n"),
+                "column" => src.push_str("flex_direction: stretch::style::FlexDirection::Column,\n"),
+                "column-reverse" => src.push_str("flex_direction: stretch::style::FlexDirection::ColumnReverse,\n"),
+                _ => (),
+            };
+        },
+        _ => (),
+    };
+
+    match style["wrap"] {
+        json::JsonValue::String(ref value) => {
+            match value.as_ref() {
+                "wrap" => src.push_str("wrap: stretch::style::Wrap::Wrap,\n"),
+                "wrap-reverse" => src.push_str("wrap: stretch::style::Wrap::WrapReverse,\n"),
+                _ => (),
+            };
+        },
+        _ => (),
+    };
+
+    match style["overflow"] {
+        json::JsonValue::String(ref value) => {
+            match value.as_ref() {
+                "hidden" => src.push_str("overflow: stretch::style::Overflow::Hidden,\n"),
+                "scroll" => src.push_str("overflow: stretch::style::Overflow::Scroll,\n"),
+                _ => (),
+            };
+        },
+        _ => (),
+    };
+
+    match style["alignItems"] {
+        json::JsonValue::String(ref value) => {
+            match value.as_ref() {
+                "flex-start" => src.push_str("align_items: stretch::style::AlignItems::FlexStart,\n"),
+                "flex-end" => src.push_str("align_items: stretch::style::AlignItems::FlexEnd,\n"),
+                "center" => src.push_str("align_items: stretch::style::AlignItems::Center,\n"),
+                "baseline" => src.push_str("align_items: stretch::style::AlignItems::Baseline,\n"),
+                _ => (),
+            };
+        },
+        _ => (),
+    };
+
+    match style["alignSelf"] {
+        json::JsonValue::String(ref value) => {
+            match value.as_ref() {
+                "flex-start" => src.push_str("align_self: stretch::style::AlignSelf::FlexStart,\n"),
+                "flex-end" => src.push_str("align_self: stretch::style::AlignSelf::FlexEnd,\n"),
+                "center" => src.push_str("align_self: stretch::style::AlignSelf::Center,\n"),
+                "baseline" => src.push_str("align_self: stretch::style::AlignSelf::Baseline,\n"),
+                "stretch" => src.push_str("align_self: stretch::style::AlignSelf::Stretch,\n"),
+                _ => (),
+            };
+        },
+        _ => (),
+    };
+
+    match style["alignContent"] {
+        json::JsonValue::String(ref value) => {
+            match value.as_ref() {
+                "flex-start" => src.push_str("align_content: stretch::style::AlignContent::FlexStart,\n"),
+                "flex-end" => src.push_str("align_content: stretch::style::AlignContent::FlexEnd,\n"),
+                "center" => src.push_str("align_content: stretch::style::AlignContent::Center,\n"),
+                "space-between" => src.push_str("align_content: stretch::style::AlignContent::SpaceBetween,\n"),
+                "space-around" => src.push_str("align_content: stretch::style::AlignContent::SpaceAround,\n"),
+                _ => (),
+            };
+        },
+        _ => (),
+    };
+
+    match style["justifyContent"] {
+        json::JsonValue::String(ref value) => {
+            match value.as_ref() {
+                "flex-end" => src.push_str("justify_content: stretch::style::JustifyContent::FlexEnd,\n"),
+                "center" => src.push_str("justify_content: stretch::style::JustifyContent::Center,\n"),
+                "space-between" => src.push_str("justify_content: stretch::style::JustifyContent::SpaceBetween,\n"),
+                "space-around" => src.push_str("justify_content: stretch::style::JustifyContent::SpaceAround,\n"),
+                "space-evenly" => src.push_str("justify_content: stretch::style::JustifyContent::SpaceEvenly,\n"),
+                _ => (),
+            };
+        },
+        _ => (),
+    };
+
+    match style["flexGrow"] {
+        json::JsonValue::Number(value) => src.push_str(&format!("flex_grow: {:.1},\n", value)),
+        _ => (),
+    };
+
+    match style["flexShrink"] {
+        json::JsonValue::Number(value) => src.push_str(&format!("flex_shrink: {:.1},\n", value)),
+        _ => (),
+    };
+
+    match style["flexBasis"] {
+        json::JsonValue::Object(ref value) => src.push_str(&format!("flex_basis: {},\n", generate_dimension(value))),
+        _ => (),
+    };
+
+    match style["width"] {
+        json::JsonValue::Object(ref value) => src.push_str(&format!("width: {},\n", generate_dimension(value))),
+        _ => (),
+    };
+
+    match style["minWidth"] {
+        json::JsonValue::Object(ref value) => src.push_str(&format!("min_width: {},\n", generate_dimension(value))),
+        _ => (),
+    };
+
+    match style["maxWidth"] {
+        json::JsonValue::Object(ref value) => src.push_str(&format!("max_width: {},\n", generate_dimension(value))),
+        _ => (),
+    };
+
+    match style["height"] {
+        json::JsonValue::Object(ref value) => src.push_str(&format!("height: {},\n", generate_dimension(value))),
+        _ => (),
+    };
+
+    match style["minHeight"] {
+        json::JsonValue::Object(ref value) => src.push_str(&format!("min_height: {},\n", generate_dimension(value))),
+        _ => (),
+    };
+
+    match style["maxHeight"] {
+        json::JsonValue::Object(ref value) => src.push_str(&format!("max_height: {},\n", generate_dimension(value))),
+        _ => (),
+    };
+
+    match style["margin"] {
+        json::JsonValue::Object(ref value) => src.push_str(&format!("margin: {},\n", generate_edges(value))),
+        _ => (),
+    };
+
+    match style["padding"] {
+        json::JsonValue::Object(ref value) => src.push_str(&format!("padding: {},\n", generate_edges(value))),
+        _ => (),
+    };
+
+    match style["border"] {
+        json::JsonValue::Object(ref value) => src.push_str(&format!("border: {},\n", generate_edges(value))),
+        _ => (),
+    };
+
+    match style["start"] {
+        json::JsonValue::Object(ref value) => src.push_str(&format!("start: {},\n", generate_dimension(value))),
+        _ => (),
+    };
+
+    match style["end"] {
+        json::JsonValue::Object(ref value) => src.push_str(&format!("end: {},\n", generate_dimension(value))),
+        _ => (),
+    };
+
+    match style["top"] {
+        json::JsonValue::Object(ref value) => src.push_str(&format!("top: {},\n", generate_dimension(value))),
+        _ => (),
+    };
+
+    match style["bottom"] {
+        json::JsonValue::Object(ref value) => src.push_str(&format!("bottom: {},\n", generate_dimension(value))),
+        _ => (),
+    };
+
+    match node["children"] {
+        json::JsonValue::Array(ref value) => {
+            src.push_str("children: vec![\n");
+            value.iter().for_each(|child| {
+                src.push_str(&format!("{},\n", generate_node(child)));
+            });
+            src.push_str("],\n");
+        },
+        _ => (),
+    };
+
+    src.push_str("..Default::default()\n");
+    src.push_str("}\n");
+    src
+}
+
+fn generate_dimension(dimen: &json::object::Object) -> String {
+    let unit = dimen.get("unit").unwrap();
+    let value = || dimen.get("value").unwrap().as_f32().unwrap();
+
+    if unit == "auto" { format!("stretch::style::Dimension::Auto") }
+    else if unit == "points" { format!("stretch::style::Dimension::Points({:.1})", value()) }
+    else if unit == "percent" { format!("stretch::style::Dimension::Percent({:.1})", value()) }
+    else { panic!() }
+}
+
+fn generate_edges(dimen: &json::object::Object) -> String {
+    let mut src = String::new();
+    src.push_str("stretch::style::Edges {\n");
+
+    match dimen.get("start") {
+        Some(value) => match value {
+            json::JsonValue::Object(ref value) => src.push_str(&format!("start: {},\n", generate_dimension(value))),
+            _ => (),
+        },
+        None => (),
+    };
+
+    match dimen.get("end") {
+        Some(value) => match value {
+            json::JsonValue::Object(ref value) => src.push_str(&format!("end: {},\n", generate_dimension(value))),
+            _ => (),
+        },
+        None => (),
+    };
+
+    match dimen.get("top") {
+        Some(value) => match value {
+            json::JsonValue::Object(ref value) => src.push_str(&format!("top: {},\n", generate_dimension(value))),
+            _ => (),
+        },
+        None => (),
+    };
+
+    match dimen.get("bottom") {
+        Some(value) => match value {
+            json::JsonValue::Object(ref value) => src.push_str(&format!("bottom: {},\n", generate_dimension(value))),
+            _ => (),
+        },
+        None => (),
+    };
+
+    src.push_str("..Default::default()\n");
+    src.push_str("}\n");
+    src
+}
diff --git a/scripts/gentest/test_base_style.css b/scripts/gentest/test_base_style.css
new file mode 100644
index 000000000..16f4a1375
--- /dev/null
+++ b/scripts/gentest/test_base_style.css
@@ -0,0 +1,17 @@
+body {
+  padding: 0;
+  margin: 0;
+}
+
+div, span, img {
+  box-sizing: border-box;
+  position: relative;
+  border: 0 solid black;
+  margin: 0;
+  padding: 0;
+  display: flex;
+}
+
+body > * {
+  position: absolute;
+}
\ No newline at end of file
diff --git a/scripts/gentest/test_helper.js b/scripts/gentest/test_helper.js
new file mode 100644
index 000000000..270c9e3f7
--- /dev/null
+++ b/scripts/gentest/test_helper.js
@@ -0,0 +1,129 @@
+function define_element_prop(property, getter) {
+  if (!(property in Element.prototype)) {
+    Object.defineProperty(Element.prototype, property, {
+      get: function() {
+        return getter(this);
+      }
+    });
+  }
+}
+
+define_element_prop("__stretch_description__", (e) => {
+  return JSON.stringify(describeElement(e));
+});
+
+function parseDimension(input) {
+  if (input.endsWith("px")) {
+    return {
+      unit: 'points', 
+      value: Number(input.replace('px',''))
+    };
+  } else if (input.endsWith("%")) {
+    return {
+      unit: 'percent', 
+      value: Number(input.replace('%','')) / 100
+    };
+  } else {
+    return input == "auto" ? {unit: "auto"} : undefined;
+  }
+}
+
+function parseNumber(input) {
+  if (input === "" || isNaN(input)) {
+    return undefined;
+  } else {
+    return Number(input);
+  }
+}
+
+function parseEnum(input) {
+  if (input) {
+    return input;
+  } else {
+    return undefined;
+  }
+}
+
+function parseEdges(edges) {
+  var start = parseDimension(edges.start);
+  var end = parseDimension(edges.end);
+  var top = parseDimension(edges.top);
+  var bottom = parseDimension(edges.bottom);
+  
+  if (start === undefined && end === undefined && top === undefined && bottom === undefined) {
+    return undefined;
+  }
+
+  return {
+    start: start,
+    end: end,
+    top: top,
+    bottom: bottom
+  };
+}
+
+function describeElement(e) {
+  return {
+    style: {
+      position: parseEnum(e.style.position),
+      direction: parseEnum(e.style.direction),
+      flexDirection: parseEnum(e.style.flexDirection),
+
+      wrap: parseEnum(e.style.wrap),
+      overflow: parseEnum(e.style.overflow),
+
+      alignItems: parseEnum(e.style.alignItems),
+      alignSelf: parseEnum(e.style.alignSelf),
+      alignContent: parseEnum(e.style.alignContent),
+      
+      justifyContent: parseEnum(e.style.justifyContent),
+
+      flexGrow: parseNumber(e.style.flexGrow),
+      flexShrink: parseNumber(e.style.flexShrink),
+      flexBasis: parseDimension(e.style.flexBasis),
+
+      width: parseDimension(e.style.width),
+      minWidth: parseDimension(e.style.minWidth),
+      maxWidth: parseDimension(e.style.maxWidth),
+
+      height: parseDimension(e.style.height),
+      minHeight: parseDimension(e.style.minHeight),
+      maxHeight: parseDimension(e.style.maxHeight),
+
+      margin: parseEdges({
+        start: e.style.marginLeft,
+        end: e.style.marginRight,
+        top: e.style.marginTop,
+        bottom: e.style.marginBottom,
+      }),
+
+      padding: parseEdges({
+        start: e.style.paddingLeft,
+        end: e.style.paddingRight,
+        top: e.style.paddingTop,
+        bottom: e.style.paddingBottom,
+      }),
+
+      border: parseEdges({
+        start: e.style.borderLeft,
+        end: e.style.borderRight,
+        top: e.style.borderTop,
+        bottom: e.style.borderBottom,
+      }),
+
+      start: parseDimension(e.style.left),
+      end: parseDimension(e.style.right),
+      top: parseDimension(e.style.top),
+      bottom: parseDimension(e.style.bottom),
+    },
+
+    layout: {
+      width: e.offsetWidth,
+      height: e.offsetHeight,
+      x: e.offsetLeft,
+      y: e.offsetTop,
+    },
+
+    children: Array.from(e.children).map(c => describeElement(c)),
+  }
+}
diff --git a/src/algo.rs b/src/algo.rs
index 4fc059131..2e8b9f0ba 100644
--- a/src/algo.rs
+++ b/src/algo.rs
@@ -1,781 +1,913 @@
 use std::f32;
 
-use style;
 use layout;
+use style;
 
 #[derive(Debug, Copy, Clone)]
 struct FlexSize {
-  main: f32,
-  cross: f32,
+    main: f32,
+    cross: f32,
 }
 
 #[derive(Debug, Copy, Clone)]
 struct SizeConstraint {
-  min: f32,
-  max: f32,
+    min: f32,
+    max: f32,
 }
 
 impl SizeConstraint {
-  fn exactly(size: f32) -> SizeConstraint {
-    SizeConstraint {
-      min: size,
-      max: size,
+    fn exactly(size: f32) -> SizeConstraint {
+        SizeConstraint {
+            min: size,
+            max: size,
+        }
     }
-  }
 
-  fn undefined() -> SizeConstraint {
-    SizeConstraint {
-      min: f32::NEG_INFINITY,
-      max: f32::INFINITY,
+    fn undefined() -> SizeConstraint {
+        SizeConstraint {
+            min: f32::NEG_INFINITY,
+            max: f32::INFINITY,
+        }
     }
-  }
 
-  fn at_least(size: f32) -> SizeConstraint {
-    SizeConstraint {
-      min: size,
-      max: f32::NEG_INFINITY,
+    fn at_least(size: f32) -> SizeConstraint {
+        SizeConstraint {
+            min: size,
+            max: f32::NEG_INFINITY,
+        }
     }
-  }
 
-  fn at_most(size: f32) -> SizeConstraint {
-    SizeConstraint {
-      min: f32::NEG_INFINITY,
-      max: size,
+    fn at_most(size: f32) -> SizeConstraint {
+        SizeConstraint {
+            min: f32::NEG_INFINITY,
+            max: size,
+        }
     }
-  }
 
-  fn between(min: f32, max: f32) -> SizeConstraint {
-    SizeConstraint {
-      min: min,
-      max: max,
+    fn between(min: f32, max: f32) -> SizeConstraint {
+        SizeConstraint { min: min, max: max }
     }
-  }
 }
 
 struct FlexItem<'a> {
-  node: &'a style::Node,
-
-  // temporary values for main size determination
-  flex_basis: f32,
-  hypothetical_inner_main_size: f32,
-  hypothetical_outer_main_size: f32, 
-  target_main_size: f32,
-  violation: f32,
-  frozen: bool,
-
-  // temporary values for cross size determination
-  hypothetical_inner_cross_size: f32,
-  hypothetical_outer_cross_size: f32, 
-  target_cross_size: f32,
-
-  main_margin_start: f32,
-  main_margin_end: f32,
-  cross_margin_start: f32,
-  cross_margin_end: 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 unclude margin/padding/border.
-  offset_main: f32,
-  offset_cross: f32,
+    node: &'a style::Node,
+
+    // temporary values for main size determination
+    flex_basis: f32,
+    hypothetical_inner_main_size: f32,
+    hypothetical_outer_main_size: f32,
+    target_main_size: f32,
+    violation: f32,
+    frozen: bool,
+
+    // temporary values for cross size determination
+    hypothetical_inner_cross_size: f32,
+    hypothetical_outer_cross_size: f32,
+    target_cross_size: f32,
+
+    main_margin_start: f32,
+    main_margin_end: f32,
+    cross_margin_start: f32,
+    cross_margin_end: 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 unclude margin/padding/border.
+    offset_main: f32,
+    offset_cross: f32,
 }
 
 struct ComputeResult {
-  size: FlexSize,
-  children: Vec<layout::Node>,
+    size: FlexSize,
+    children: Vec<layout::Node>,
 }
 
 struct FlexLine<'a> {
-  items: Vec<FlexItem<'a>>,
-  cross_size: f32,
-  offset_cross: f32,
+    items: Vec<FlexItem<'a>>,
+    cross_size: f32,
+    offset_cross: f32,
 }
 
 pub fn compute(root: &style::Node) -> layout::Node {
-  let result = compute_internal(root, SizeConstraint::undefined(), SizeConstraint::undefined());
-
-  layout::Node {
-    width: if root.flex_direction.is_row() { result.size.main } else { result.size.cross },
-    height: if root.flex_direction.is_column() { result.size.main } else { result.size.cross },
-    x: 0.0,
-    y: 0.0,
-    children: result.children,
-  }
+    let result = compute_internal(
+        root,
+        SizeConstraint::undefined(),
+        SizeConstraint::undefined(),
+    );
+
+    layout::Node {
+        width: if root.flex_direction.is_row() {
+            result.size.main
+        } else {
+            result.size.cross
+        },
+        height: if root.flex_direction.is_column() {
+            result.size.main
+        } else {
+            result.size.cross
+        },
+        x: 0.0,
+        y: 0.0,
+        children: result.children,
+    }
 }
 
-fn compute_internal(node: &style::Node, main: SizeConstraint, cross: SizeConstraint) -> ComputeResult {
-  // 9.2. Line Length Determination
+fn compute_internal(
+    node: &style::Node,
+    main: SizeConstraint,
+    cross: SizeConstraint,
+) -> ComputeResult {
+    // 9.2. Line Length Determination
+
+    // 1. Generate anonymous flex items as described in §4 Flex Items.
+
+    let mut flex_items: Vec<FlexItem> = node
+        .children
+        .iter()
+        .map(|child| FlexItem {
+            node: child,
+
+            flex_basis: 0.0,
+            hypothetical_inner_main_size: 0.0,
+            hypothetical_outer_main_size: 0.0,
+            target_main_size: 0.0,
+            violation: 0.0,
+            frozen: false,
+
+            hypothetical_inner_cross_size: 0.0,
+            hypothetical_outer_cross_size: 0.0,
+            target_cross_size: 0.0,
+
+            main_margin_start: 0.0,
+            main_margin_end: 0.0,
+            cross_margin_start: 0.0,
+            cross_margin_end: 0.0,
+
+            offset_main: 0.0,
+            offset_cross: 0.0,
+        }).collect();
+
+    // 2. Determine the available main and cross space for the flex items.
+    //    For each dimension, if that dimension of the flex container’s content box
+    //    is a definite size, use that; if that dimension of the flex container is
+    //    being sized under a min or max-content constraint, the available space in
+    //    that dimension is that constraint; otherwise, subtract the flex container’s
+    //    margin, border, and padding from the space available to the flex container
+    //    in that dimension and use that value. This might result in an infinite value.
+
+    let avaliable_main = node.main_size().resolve(main.max, {
+        main.max
+            - node.main_margin_start().resolve(main.max, 0.0)
+            - node.main_margin_end().resolve(main.max, 0.0)
+    }) - node.main_padding_start().resolve(main.max, 0.0)
+        - node.main_padding_end().resolve(main.max, 0.0)
+        - node.main_border_start().resolve(main.max, 0.0)
+        - node.main_border_end().resolve(main.max, 0.0);
+
+    let avaliable_cross = node.cross_size().resolve(cross.max, {
+        cross.max
+            - node.cross_margin_start().resolve(cross.max, 0.0)
+            - node.cross_margin_end().resolve(cross.max, 0.0)
+    }) - node.cross_padding_start().resolve(cross.max, 0.0)
+        - node.cross_padding_end().resolve(cross.max, 0.0)
+        - node.cross_border_start().resolve(cross.max, 0.0)
+        - node.cross_border_end().resolve(cross.max, 0.0);
+
+    // 3. Determine the flex base size and hypothetical main size of each item:
+    for child in &mut flex_items {
+        // A. If the item has a definite used flex basis, that’s the flex base size.
+
+        let flex_basis = child.node.flex_basis.resolve(avaliable_main, f32::INFINITY);
+        if flex_basis.is_finite() {
+            child.flex_basis = flex_basis;
+            continue;
+        }
 
-  // 1. Generate anonymous flex items as described in §4 Flex Items.
+        // B. If the flex item has an intrinsic aspect ratio,
+        //    a used flex basis of content, and a definite cross size,
+        //    then the flex base size is calculated from its inner
+        //    cross size and the flex item’s intrinsic aspect ratio.
 
-  let mut flex_items: Vec<FlexItem> = node.children.iter().map(|child| { 
-    FlexItem { 
-      node: child, 
+        if let Some(ratio) = child.node.aspect_ratio {
+            let cross_size = node.cross_size().resolve(avaliable_cross, f32::INFINITY);
 
-      flex_basis: 0.0, 
-      hypothetical_inner_main_size: 0.0,
-      hypothetical_outer_main_size: 0.0, 
-      target_main_size: 0.0,
-      violation: 0.0,
-      frozen: false,
+            if cross_size.is_finite() && child.node.flex_basis == style::Dimension::Auto {
+                child.flex_basis = ratio * cross_size;
+                continue;
+            }
+        }
 
-      hypothetical_inner_cross_size: 0.0,
-      hypothetical_outer_cross_size: 0.0, 
-      target_cross_size: 0.0,
+        // C. If the used flex basis is content or depends on its available space,
+        //    and the flex container is being sized under a min-content or max-content
+        //    constraint (e.g. when performing automatic table layout [CSS21]),
+        //    size the item under that constraint. The flex base size is the item’s
+        //    resulting main size.
 
-      main_margin_start: 0.0,
-      main_margin_end: 0.0,
-      cross_margin_start: 0.0,
-      cross_margin_end: 0.0,
+        if main.min.is_finite() || main.max.is_finite() {
+            let size = compute_internal(child.node, main, cross).size;
+            child.flex_basis = size.main;
+            continue;
+        }
 
-      offset_main: 0.0,
-      offset_cross: 0.0,
-    }
-  }).collect();
-
-  // 2. Determine the available main and cross space for the flex items. 
-  //    For each dimension, if that dimension of the flex container’s content box
-  //    is a definite size, use that; if that dimension of the flex container is 
-  //    being sized under a min or max-content constraint, the available space in 
-  //    that dimension is that constraint; otherwise, subtract the flex container’s 
-  //    margin, border, and padding from the space available to the flex container 
-  //    in that dimension and use that value. This might result in an infinite value.
-
-  let avaliable_main = node.main_size().resolve(main.max, {
-    main.max 
-        - node.main_margin_start().resolve(main.max, 0.0) 
-        - node.main_margin_end().resolve(main.max, 0.0) 
-  }) 
-  - node.main_padding_start().resolve(main.max, 0.0) 
-  - node.main_padding_end().resolve(main.max, 0.0) 
-  - node.main_border_start().resolve(main.max, 0.0)
-  - node.main_border_end().resolve(main.max, 0.0);
-
-  let avaliable_cross = node.cross_size().resolve(cross.max, {
-    cross.max 
-        - node.cross_margin_start().resolve(cross.max, 0.0) 
-        - node.cross_margin_end().resolve(cross.max, 0.0) 
-  })
-  - node.cross_padding_start().resolve(cross.max, 0.0) 
-  - node.cross_padding_end().resolve(cross.max, 0.0) 
-  - node.cross_border_start().resolve(cross.max, 0.0)
-  - node.cross_border_end().resolve(cross.max, 0.0);
-  
-  // 3. Determine the flex base size and hypothetical main size of each item:
-  for child in &mut flex_items {
-    // A. If the item has a definite used flex basis, that’s the flex base size.
-
-    let flex_basis = child.node.flex_basis.resolve(avaliable_main, f32::INFINITY);
-    if flex_basis.is_finite() {
-      child.flex_basis = flex_basis;
-      continue;
+        // D. Otherwise, if the used flex basis is content or depends on its
+        //    available space, the available main size is infinite, and the flex item’s
+        //    inline axis is parallel to the main axis, lay the item out using the rules
+        //    for a box in an orthogonal flow [CSS3-WRITING-MODES]. The flex base size
+        //    is the item’s max-content main size.
+
+        if avaliable_main.is_infinite() {
+            let size = compute_internal(
+                child.node,
+                SizeConstraint::undefined(),
+                SizeConstraint::undefined(),
+            ).size;
+            child.flex_basis = size.main;
+            continue;
+        }
+
+        // E. Otherwise, size the item into the available space using its used flex basis
+        //    in place of its main size, treating a value of content as max-content.
+        //    If a cross size is needed to determine the main size (e.g. when the
+        //    flex item’s main size is in its block axis) and the flex item’s cross size
+        //    is auto and not definite, in this calculation use fit-content as the
+        //    flex item’s cross size. The flex base size is the item’s resulting main size.
+
+        let size = compute_internal(
+            child.node,
+            SizeConstraint::at_most(avaliable_main),
+            SizeConstraint::at_most(avaliable_cross),
+        ).size;
+        child.flex_basis = size.main;
     }
 
-    // B. If the flex item has an intrinsic aspect ratio,
-    //    a used flex basis of content, and a definite cross size,
-    //    then the flex base size is calculated from its inner
-    //    cross size and the flex item’s intrinsic aspect ratio.
+    // The hypothetical main size is the item’s flex base size clamped according to its
+    // used min and max main sizes (and flooring the content box size at zero).
 
-    if let Some(ratio) = child.node.aspect_ratio {
-      let cross_size = node.cross_size().resolve(avaliable_cross, f32::INFINITY);
+    for child in &mut flex_items {
+        child.hypothetical_inner_main_size = child
+            .flex_basis
+            .max(child.node.min_main_size().resolve(avaliable_main, f32::MIN))
+            .min(child.node.max_main_size().resolve(avaliable_main, f32::MAX))
+            .max(0.0);
 
-      if cross_size.is_finite() && child.node.flex_basis == style::Dimension::Auto {
-        child.flex_basis = ratio * cross_size;
-        continue;
-      }
+        child.hypothetical_outer_main_size = child.hypothetical_inner_main_size
+            + child.node.main_margin_start().resolve(avaliable_main, 0.0)
+            + child.node.main_margin_end().resolve(avaliable_main, 0.0);
     }
 
-    // C. If the used flex basis is content or depends on its available space, 
-    //    and the flex container is being sized under a min-content or max-content 
-    //    constraint (e.g. when performing automatic table layout [CSS21]),
-    //    size the item under that constraint. The flex base size is the item’s 
-    //    resulting main size.
+    // 9.3. Main Size Determination
+
+    // 5. Collect flex items into flex lines:
+    //    - If the flex container is single-line, collect all the flex items into
+    //      a single flex line.
+    //    - Otherwise, starting from the first uncollected item, collect consecutive
+    //      items one by one until the first time that the next collected item would
+    //      not fit into the flex container’s inner main size (or until a forced break
+    //      is encountered, see §10 Fragmenting Flex Layout). If the very first
+    //      uncollected item wouldn’t fit, collect just it into the line.
+    //
+    //      For this step, the size of a flex item is its outer hypothetical main size. (Note: This can be negative.)
+    //      Repeat until all flex items have been collected into flex lines
+    //
+    //      Note that the "collect as many" line will collect zero-sized flex items onto
+    //      the end of the previous line even if the last non-zero item exactly "filled up" the line.
+
+    let mut flex_lines = {
+        let mut lines: Vec<FlexLine> = vec![];
+        let mut line_length = 0.0;
+
+        if node.wrap == style::Wrap::NoWrap {
+            lines.push(FlexLine {
+                items: flex_items,
+                cross_size: 0.0,
+                offset_cross: 0.0,
+            });
+        } else {
+            let mut line = FlexLine {
+                items: vec![],
+                cross_size: 0.0,
+                offset_cross: 0.0,
+            };
+
+            for child in flex_items {
+                line_length += child.hypothetical_outer_main_size;
+
+                if line_length > avaliable_main && line.items.len() > 0 {
+                    line_length = child.hypothetical_outer_main_size;
+                    lines.push(line);
+                    line = FlexLine {
+                        items: vec![],
+                        cross_size: 0.0,
+                        offset_cross: 0.0,
+                    };
+                }
+
+                line.items.push(child);
+            }
+
+            lines.push(line);
+        }
 
-    if main.min.is_finite() || main.max.is_finite() {
-      let size = compute_internal(child.node, main, cross).size;
-      child.flex_basis = size.main;
-      continue;
-    }
+        lines
+    };
 
-    // D. Otherwise, if the used flex basis is content or depends on its
-    //    available space, the available main size is infinite, and the flex item’s
-    //    inline axis is parallel to the main axis, lay the item out using the rules
-    //    for a box in an orthogonal flow [CSS3-WRITING-MODES]. The flex base size
-    //    is the item’s max-content main size.
+    // 6. Resolve the flexible lengths of all the flex items to find their used main size.
+    //    See §9.7 Resolving Flexible Lengths.
+    //
+    // 9.7. Resolving Flexible Lengths
 
-    if avaliable_main.is_infinite() {
-      let size = compute_internal(child.node, SizeConstraint::undefined(), SizeConstraint::undefined()).size;
-      child.flex_basis = size.main;
-      continue;
+    for line in &mut flex_lines {
+        // 1. Determine the used flex factor. Sum the outer hypothetical main sizes of all
+        //    items on the line. If the sum is less than the flex container’s inner main size,
+        //    use the flex grow factor for the rest of this algorithm; otherwise, use the
+        //    flex shrink factor.
+
+        let used_flex_factor: f32 = line
+            .items
+            .iter()
+            .map(|child| child.hypothetical_outer_main_size)
+            .sum();
+        let growing = used_flex_factor < avaliable_main;
+        let shrinking = !growing;
+
+        // 2. Size inflexible items. Freeze, setting its target main size to its hypothetical main size
+        //    - Any item that has a flex factor of zero
+        //    - If using the flex grow factor: any item that has a flex base size
+        //      greater than its hypothetical main size
+        //    - If using the flex shrink factor: any item that has a flex base size
+        //      smaller than its hypothetical main size
+
+        for child in line.items.iter_mut() {
+            child.target_main_size = child.hypothetical_inner_main_size;
+
+            if (child.node.flex_grow == 0.0 && child.node.flex_shrink == 0.0)
+                || (growing && child.flex_basis > child.hypothetical_inner_main_size)
+                || (shrinking && child.flex_basis < child.hypothetical_inner_main_size)
+            {
+                child.frozen = true;
+            }
+        }
+
+        // 3. Calculate initial free space. Sum the outer sizes of all items on the line,
+        //    and subtract this from the flex container’s inner main size. For frozen items,
+        //    use their outer target main size; for other items, use their outer flex base size.
+
+        let used_space: f32 = line
+            .items
+            .iter()
+            .map(|child| {
+                child.node.main_margin_start().resolve(avaliable_main, 0.0)
+                    + child.node.main_margin_end().resolve(avaliable_main, 0.0)
+                    + if child.frozen {
+                        child.target_main_size
+                    } else {
+                        child.flex_basis
+                    }
+            }).sum();
+
+        let initial_free_space = avaliable_main - used_space;
+
+        // 4. Loop
+
+        loop {
+            // a. Check for flexible items. If all the flex items on the line are frozen,
+            //    free space has been distributed; exit this loop.
+
+            let mut frozen: Vec<&mut FlexItem> = vec![];
+            let mut unfrozen: Vec<&mut FlexItem> = vec![];
+
+            for child in line.items.iter_mut() {
+                if child.frozen {
+                    frozen.push(child);
+                } else {
+                    unfrozen.push(child);
+                }
+            }
+
+            if unfrozen.len() == 0 {
+                break;
+            }
+
+            // b. Calculate the remaining free space as for initial free space, above.
+            //    If the sum of the unfrozen flex items’ flex factors is less than one,
+            //    multiply the initial free space by this sum. If the magnitude of this
+            //    value is less than the magnitude of the remaining free space, use this
+            //    as the remaining free space.
+
+            let used_space: f32 = Iterator::chain(frozen.iter(), unfrozen.iter())
+                .map(|child| {
+                    child.node.main_margin_start().resolve(avaliable_main, 0.0)
+                        + child.node.main_margin_end().resolve(avaliable_main, 0.0)
+                        + if child.frozen {
+                            child.target_main_size
+                        } else {
+                            child.flex_basis
+                        }
+                }).sum();
+
+            let sum_flex_grow: f32 = unfrozen.iter().map(|item| item.node.flex_grow).sum();
+            let sum_flex_shrink: f32 = unfrozen.iter().map(|item| item.node.flex_shrink).sum();
+
+            let free_space = if growing && sum_flex_grow < 1.0 {
+                (initial_free_space * sum_flex_grow).min(avaliable_main - used_space)
+            } else if shrinking && sum_flex_shrink < 1.0 {
+                (initial_free_space * sum_flex_shrink).max(avaliable_main - used_space)
+            } else {
+                avaliable_main - used_space
+            };
+
+            // c. Distribute free space proportional to the flex factors.
+            //    - If the remaining free space is zero
+            //        Do Nothing
+            //    - If using the flex grow factor
+            //        Find the ratio of the item’s flex grow factor to the sum of the
+            //        flex grow factors of all unfrozen items on the line. Set the item’s
+            //        target main size to its flex base size plus a fraction of the remaining
+            //        free space proportional to the ratio.
+            //    - If using the flex shrink factor
+            //        For every unfrozen item on the line, multiply its flex shrink factor by
+            //        its inner flex base size, and note this as its scaled flex shrink factor.
+            //        Find the ratio of the item’s scaled flex shrink factor to the sum of the
+            //        scaled flex shrink factors of all unfrozen items on the line. Set the item’s
+            //        target main size to its flex base size minus a fraction of the absolute value
+            //        of the remaining free space proportional to the ratio. Note this may result
+            //        in a negative inner main size; it will be corrected in the next step.
+            //    - Otherwise
+            //        Do Nothing
+
+            if free_space > 0.0 && growing {
+                for child in &mut unfrozen {
+                    child.target_main_size =
+                        child.flex_basis + free_space * (child.node.flex_grow / sum_flex_grow);
+                }
+            } else if free_space < 0.0 && shrinking {
+                for child in &mut unfrozen {
+                    child.target_main_size =
+                        child.flex_basis + free_space * (child.node.flex_shrink / sum_flex_shrink);
+                }
+            }
+
+            // d. Fix min/max violations. Clamp each non-frozen item’s target main size by its
+            //    used min and max main sizes and floor its content-box size at zero. If the
+            //    item’s target main size was made smaller by this, it’s a max violation.
+            //    If the item’s target main size was made larger by this, it’s a min violation.
+
+            let mut total_violation = 0.0;
+            for child in &mut unfrozen {
+                let max = child.node.max_main_size().resolve(avaliable_main, f32::MAX);
+                let min = child
+                    .node
+                    .min_main_size()
+                    .resolve(avaliable_main, f32::MIN)
+                    .max(0.0);
+
+                let clamped = if child.target_main_size > max {
+                    max
+                } else if child.target_main_size < min {
+                    min
+                } else {
+                    child.target_main_size
+                };
+
+                child.violation = clamped - child.target_main_size;
+                total_violation += child.violation;
+                child.target_main_size = clamped;
+            }
+
+            // e. Freeze over-flexed items. The total violation is the sum of the adjustments
+            //    from the previous step ∑(clamped size - unclamped size). If the total violation is:
+            //    - Zero
+            //        Freeze all items.
+            //    - Positive
+            //        Freeze all the items with min violations.
+            //    - Negative
+            //        Freeze all the items with max violations.
+
+            if total_violation > 0.0 {
+                for child in &mut unfrozen {
+                    child.frozen = child.violation < 0.0;
+                }
+            } else if total_violation < 0.0 {
+                for child in &mut unfrozen {
+                    child.frozen = child.violation > 0.0;
+                }
+            } else {
+                for child in &mut unfrozen {
+                    child.frozen = true;
+                }
+            }
+
+            // f. Return to the start of this loop.
+        }
     }
 
-    // E. Otherwise, size the item into the available space using its used flex basis
-    //    in place of its main size, treating a value of content as max-content. 
-    //    If a cross size is needed to determine the main size (e.g. when the
-    //    flex item’s main size is in its block axis) and the flex item’s cross size 
-    //    is auto and not definite, in this calculation use fit-content as the
-    //    flex item’s cross size. The flex base size is the item’s resulting main size.
-
-    let size = compute_internal(child.node, SizeConstraint::at_most(avaliable_main), SizeConstraint::at_most(avaliable_cross)).size;
-    child.flex_basis = size.main;
-  }
-
-  // The hypothetical main size is the item’s flex base size clamped according to its
-  // used min and max main sizes (and flooring the content box size at zero).
-
-  for child in &mut flex_items {
-    child.hypothetical_inner_main_size = child.flex_basis
-        .max(child.node.min_main_size().resolve(avaliable_main, f32::MIN))
-        .min(child.node.max_main_size().resolve(avaliable_main, f32::MAX))
-        .max(0.0);
-
-    child.hypothetical_outer_main_size = child.hypothetical_inner_main_size
-        + child.node.main_margin_start().resolve(avaliable_main, 0.0) 
-        + child.node.main_margin_end().resolve(avaliable_main, 0.0);
-  }
-
-  // 9.3. Main Size Determination
-
-  // 5. Collect flex items into flex lines:
-  //    - If the flex container is single-line, collect all the flex items into
-  //      a single flex line.
-  //    - Otherwise, starting from the first uncollected item, collect consecutive 
-  //      items one by one until the first time that the next collected item would 
-  //      not fit into the flex container’s inner main size (or until a forced break 
-  //      is encountered, see §10 Fragmenting Flex Layout). If the very first 
-  //      uncollected item wouldn’t fit, collect just it into the line.
-  //
-  //      For this step, the size of a flex item is its outer hypothetical main size. (Note: This can be negative.)
-  //      Repeat until all flex items have been collected into flex lines
-  //
-  //      Note that the "collect as many" line will collect zero-sized flex items onto
-  //      the end of the previous line even if the last non-zero item exactly "filled up" the line.
-
-  let mut flex_lines = {
-    let mut lines: Vec<FlexLine> = vec![];
-    let mut line_length = 0.0;
-
-    if node.wrap == style::Wrap::NoWrap {
-      lines.push(FlexLine { items: flex_items, cross_size: 0.0, offset_cross: 0.0 });
+    // Not part of the spec from what i can see but seems correct
+    let container_main_size = if avaliable_main.is_finite() {
+        avaliable_main
     } else {
-      let mut line = FlexLine { items: vec![], cross_size: 0.0, offset_cross: 0.0 };
+        let mut longest_line = f32::MIN;
 
-      for child in flex_items {
-        line_length += child.hypothetical_outer_main_size;
+        for line in &flex_lines {
+            let length: f32 = line.items.iter().map(|item| item.target_main_size).sum();
 
-        if line_length > avaliable_main && line.items.len() > 0 {
-          line_length = child.hypothetical_outer_main_size;
-          lines.push(line);
-          line = FlexLine { items: vec![], cross_size: 0.0, offset_cross: 0.0 };
+            longest_line = if length > longest_line {
+                length
+            } else {
+                longest_line
+            };
         }
 
-        line.items.push(child);
-      }
-
-      lines.push(line);
-    }
+        longest_line
+    }.min(main.max)
+    .max(main.min)
+    .max(0.0);
 
-    lines
-  };
-
-  // 6. Resolve the flexible lengths of all the flex items to find their used main size.
-  //    See §9.7 Resolving Flexible Lengths.
-  //
-  // 9.7. Resolving Flexible Lengths
-
-  for line in &mut flex_lines {
-    // 1. Determine the used flex factor. Sum the outer hypothetical main sizes of all 
-    //    items on the line. If the sum is less than the flex container’s inner main size, 
-    //    use the flex grow factor for the rest of this algorithm; otherwise, use the
-    //    flex shrink factor.
-
-    let used_flex_factor: f32 = line.items.iter().map(|child| child.hypothetical_outer_main_size).sum();
-    let growing = used_flex_factor < avaliable_main;
-    let shrinking = !growing;
-
-    // 2. Size inflexible items. Freeze, setting its target main size to its hypothetical main size
-    //    - Any item that has a flex factor of zero
-    //    - If using the flex grow factor: any item that has a flex base size
-    //      greater than its hypothetical main size
-    //    - If using the flex shrink factor: any item that has a flex base size
-    //      smaller than its hypothetical main size
-
-    for child in line.items.iter_mut() {
-      child.target_main_size = child.hypothetical_inner_main_size;
-      
-      if (child.node.flex_grow == 0.0 && child.node.flex_shrink == 0.0) ||
-         (growing && child.flex_basis > child.hypothetical_inner_main_size) ||
-         (shrinking && child.flex_basis < child.hypothetical_inner_main_size) {
-        child.frozen = true;
-      }
-    }
+    // 9.4. Cross Size Determination
 
-    // 3. Calculate initial free space. Sum the outer sizes of all items on the line,
-    //    and subtract this from the flex container’s inner main size. For frozen items,
-    //    use their outer target main size; for other items, use their outer flex base size.
+    // 7. Determine the hypothetical cross size of each item by performing layout with the
+    //    used main size and the available space, treating auto as fit-content.
 
-    let used_space: f32 = line.items.iter().map(|child| {
-      child.node.main_margin_start().resolve(avaliable_main, 0.0) +
-      child.node.main_margin_end().resolve(avaliable_main, 0.0) + 
-      if child.frozen {
-        child.target_main_size
-      } else {
-        child.flex_basis
-      }
-    }).sum();
+    for line in &mut flex_lines {
+        for child in &mut line.items {
+            let size = compute_internal(
+                child.node,
+                SizeConstraint::exactly(child.target_main_size),
+                SizeConstraint::between(cross.min, avaliable_cross),
+            ).size;
+
+            child.hypothetical_inner_cross_size = size
+                .cross
+                .max(
+                    child
+                        .node
+                        .min_cross_size()
+                        .resolve(avaliable_cross, f32::MIN),
+                ).min(
+                    child
+                        .node
+                        .max_cross_size()
+                        .resolve(avaliable_cross, f32::MAX),
+                ).max(0.0);
+
+            child.hypothetical_outer_cross_size = child.hypothetical_inner_cross_size
+                + node.cross_margin_start().resolve(avaliable_cross, 0.0)
+                + node.cross_margin_end().resolve(avaliable_cross, 0.0);
+        }
+    }
 
-    let initial_free_space = avaliable_main - used_space;
+    // 8. Calculate the cross size of each flex line.
+    //    If the flex container is single-line and has a definite cross size, the cross size
+    //    of the flex line is the flex container’s inner cross size. Otherwise, for each flex line:
+    //
+    //    1. Collect all the flex items whose inline-axis is parallel to the main-axis, whose
+    //       align-self is baseline, and whose cross-axis margins are both non-auto. Find the
+    //       largest of the distances between each item’s baseline and its hypothetical outer
+    //       cross-start edge, and the largest of the distances between each item’s baseline
+    //       and its hypothetical outer cross-end edge, and sum these two values.
+    //    2. Among all the items not collected by the previous step, find the largest
+    //       outer hypothetical cross size.
+    //    3. The used cross-size of the flex line is the largest of the numbers found in the
+    //       previous two steps and zero.
+    //
+    //    If the flex container is single-line, then clamp the line’s cross-size to be within
+    //    the container’s computed min and max cross sizes. Note that if CSS 2.1’s definition
+    //    of min/max-width/height applied more generally, this behavior would fall out automatically.
+
+    if flex_lines.len() == 1 && avaliable_cross.is_finite() {
+        flex_lines[0].cross_size = avaliable_cross.min(cross.max).max(cross.min);
+    } else {
+        for line in &mut flex_lines {
+            // TODO handle baseline (1)
+
+            line.cross_size = line
+                .items
+                .iter()
+                .map(|child| child.hypothetical_outer_cross_size)
+                .fold(0.0, |acc, x| acc.max(x));
+        }
+    }
 
-    // 4. Loop
+    // 9. Handle 'align-content: stretch'. If the flex container has a definite cross size,
+    //    align-content is stretch, and the sum of the flex lines' cross sizes is less than
+    //    the flex container’s inner cross size, increase the cross size of each flex line
+    //    by equal amounts such that the sum of their cross sizes exactly equals the
+    //    flex container’s inner cross size.
 
-    loop {
-      // a. Check for flexible items. If all the flex items on the line are frozen,
-      //    free space has been distributed; exit this loop.
+    if node.align_content == style::AlignContent::Stretch && avaliable_cross.is_finite() {
+        let total_cross: f32 = flex_lines.iter().map(|line| line.cross_size).sum();
 
-      let mut frozen: Vec<&mut FlexItem> = vec![];
-      let mut unfrozen: Vec<&mut FlexItem> = vec![];
+        if total_cross < avaliable_cross {
+            let remaining = avaliable_cross - total_cross;
+            let addition = remaining / flex_lines.len() as f32;
 
-      for child in line.items.iter_mut() {
-        if child.frozen {
-          frozen.push(child);
-        } else {
-          unfrozen.push(child);
-        }
-      }
-
-      if unfrozen.len() == 0 {
-        break;
-      }
-
-      // b. Calculate the remaining free space as for initial free space, above.
-      //    If the sum of the unfrozen flex items’ flex factors is less than one,
-      //    multiply the initial free space by this sum. If the magnitude of this
-      //    value is less than the magnitude of the remaining free space, use this
-      //    as the remaining free space.
-
-      let used_space: f32 = Iterator::chain(frozen.iter(), unfrozen.iter()).map(|child| {
-        child.node.main_margin_start().resolve(avaliable_main, 0.0) +
-        child.node.main_margin_end().resolve(avaliable_main, 0.0) + 
-        if child.frozen {
-          child.target_main_size
-        } else {
-          child.flex_basis
-        }
-      }).sum();
-
-      let sum_flex_grow: f32 = unfrozen.iter().map(|item| { item.node.flex_grow }).sum();
-      let sum_flex_shrink: f32 = unfrozen.iter().map(|item| { item.node.flex_shrink }).sum();
-
-      let free_space = if growing && sum_flex_grow < 1.0 {
-        (initial_free_space * sum_flex_grow).min(avaliable_main - used_space)
-      } else if shrinking && sum_flex_shrink < 1.0 {
-        (initial_free_space * sum_flex_shrink).max(avaliable_main - used_space)
-      } else {
-        avaliable_main - used_space
-      };
-
-      // c. Distribute free space proportional to the flex factors.
-      //    - If the remaining free space is zero
-      //        Do Nothing
-      //    - If using the flex grow factor
-      //        Find the ratio of the item’s flex grow factor to the sum of the
-      //        flex grow factors of all unfrozen items on the line. Set the item’s
-      //        target main size to its flex base size plus a fraction of the remaining
-      //        free space proportional to the ratio.
-      //    - If using the flex shrink factor
-      //        For every unfrozen item on the line, multiply its flex shrink factor by
-      //        its inner flex base size, and note this as its scaled flex shrink factor.
-      //        Find the ratio of the item’s scaled flex shrink factor to the sum of the
-      //        scaled flex shrink factors of all unfrozen items on the line. Set the item’s
-      //        target main size to its flex base size minus a fraction of the absolute value
-      //        of the remaining free space proportional to the ratio. Note this may result
-      //        in a negative inner main size; it will be corrected in the next step.
-      //    - Otherwise
-      //        Do Nothing
-
-      if free_space > 0.0 && growing {
-        for child in &mut unfrozen {
-          child.target_main_size = child.flex_basis + free_space * (child.node.flex_grow / sum_flex_grow);
+            for line in &mut flex_lines {
+                line.cross_size += addition;
+            }
         }
-      } else if free_space < 0.0 && shrinking {
-        for child in &mut unfrozen {
-          child.target_main_size = child.flex_basis + free_space * (child.node.flex_shrink / sum_flex_shrink);
-        }
-      }
-
-      // d. Fix min/max violations. Clamp each non-frozen item’s target main size by its
-      //    used min and max main sizes and floor its content-box size at zero. If the
-      //    item’s target main size was made smaller by this, it’s a max violation.
-      //    If the item’s target main size was made larger by this, it’s a min violation.
-
-      let mut total_violation = 0.0;
-      for child in &mut unfrozen {
-        let max = child.node.max_main_size().resolve(avaliable_main, f32::MAX);
-        let min = child.node.min_main_size().resolve(avaliable_main, f32::MIN).max(0.0);
-
-        let clamped = if child.target_main_size > max {
-          max
-        } else if child.target_main_size < min {
-          min
-        } else {
-          child.target_main_size
-        };
+    }
 
-        child.violation = clamped - child.target_main_size;
-        total_violation += child.violation;
-        child.target_main_size = clamped;
-      }
-
-      // e. Freeze over-flexed items. The total violation is the sum of the adjustments
-      //    from the previous step ∑(clamped size - unclamped size). If the total violation is:
-      //    - Zero
-      //        Freeze all items.
-      //    - Positive
-      //        Freeze all the items with min violations.
-      //    - Negative
-      //        Freeze all the items with max violations.
-
-      if total_violation > 0.0 {
-        for child in &mut unfrozen {
-          child.frozen = child.violation < 0.0;
-        }
-      } else if total_violation < 0.0 {
-        for child in &mut unfrozen {
-          child.frozen = child.violation > 0.0;
-        }
-      } else {
-        for child in &mut unfrozen {
-          child.frozen = true;
-        }
-      }
+    // 10. Collapse visibility:collapse items. If any flex items have visibility: collapse,
+    //     note the cross size of the line they’re in as the item’s strut size, and restart
+    //     layout from the beginning.
+    //
+    //     In this second layout round, when collecting items into lines, treat the collapsed
+    //     items as having zero main size. For the rest of the algorithm following that step,
+    //     ignore the collapsed items entirely (as if they were display:none) except that after
+    //     calculating the cross size of the lines, if any line’s cross size is less than the
+    //     largest strut size among all the collapsed items in the line, set its cross size to
+    //     that strut size.
+    //
+    //     Skip this step in the second layout round.
+
+    // TODO implement once (if ever) we support visibility:collapse
+
+    // 11. Determine the used cross size of each flex item. If a flex item has align-self: stretch,
+    //     its computed cross size property is auto, and neither of its cross-axis margins are auto,
+    //     the used outer cross size is the used cross size of its flex line, clamped according to
+    //     the item’s used min and max cross sizes. Otherwise, the used cross size is the item’s
+    //     hypothetical cross size.
+    //
+    //     If the flex item has align-self: stretch, redo layout for its contents, treating this
+    //     used size as its definite cross size so that percentage-sized children can be resolved.
+    //
+    //     Note that this step does not affect the main size of the flex item, even if it has an
+    //     intrinsic aspect ratio.
 
-      // f. Return to the start of this loop.
-    }
-  }
-
-  // Not part of the spec from what i can see but seems correct
-  let container_main_size = if avaliable_main.is_finite() {
-    avaliable_main
-  } else {
-    let mut longest_line = f32::MIN;
-
-    for line in &flex_lines {
-      let length: f32 = line.items.iter().map(|item| {
-        item.target_main_size
-      }).sum();
-
-      longest_line = if length > longest_line {
-        length
-      } else {
-        longest_line
-      };
+    for line in &mut flex_lines {
+        for child in &mut line.items {
+            let is_stretch = child.node.align_self(node) == style::AlignSelf::Stretch;
+
+            if is_stretch
+                && child.node.cross_margin_start() != style::Dimension::Auto
+                && child.node.cross_margin_end() != style::Dimension::Auto
+                && child.node.cross_size() == style::Dimension::Auto
+            {
+                child.target_cross_size = line
+                    .cross_size
+                    .min(
+                        child
+                            .node
+                            .max_cross_size()
+                            .resolve(avaliable_cross, f32::MAX),
+                    ).max(
+                        child
+                            .node
+                            .min_cross_size()
+                            .resolve(avaliable_cross, f32::MIN),
+                    );
+            } else {
+                child.target_cross_size = child.hypothetical_inner_cross_size;
+            }
+
+            if is_stretch {
+                // TODO use result somehow
+                compute_internal(
+                    child.node,
+                    SizeConstraint::exactly(child.target_main_size),
+                    SizeConstraint::exactly(child.target_cross_size),
+                );
+            }
+        }
     }
 
-    longest_line
-  }.min(main.max).max(main.min).max(0.0);
+    // 9.5. Main-Axis Alignment
 
-  // 9.4. Cross Size Determination
+    // 12. Distribute any remaining free space. For each flex line:
+    //     1. If the remaining free space is positive and at least one main-axis margin on this
+    //        line is auto, distribute the free space equally among these margins. Otherwise,
+    //        set all auto margins to zero.
+    //     2. Align the items along the main-axis per justify-content.
 
-  // 7. Determine the hypothetical cross size of each item by performing layout with the
-  //    used main size and the available space, treating auto as fit-content.
+    for line in &mut flex_lines {
+        let used_space: f32 = line.items.iter().map(|child| child.target_main_size).sum();
+        let free_space = avaliable_main - used_space;
+
+        if free_space > 0.0 {
+            let mut num_auto_margins = 0;
+
+            for child in &mut line.items {
+                if child.node.main_margin_start() == style::Dimension::Auto {
+                    num_auto_margins += 1;
+                }
+                if child.node.main_margin_end() == style::Dimension::Auto {
+                    num_auto_margins += 1;
+                }
+            }
+
+            if num_auto_margins > 0 {
+                let margin = free_space / num_auto_margins as f32;
+
+                for child in &mut line.items {
+                    if child.node.main_margin_start() == style::Dimension::Auto {
+                        child.main_margin_start = margin;
+                    }
+                    if child.node.main_margin_end() == style::Dimension::Auto {
+                        child.main_margin_end = margin;
+                    }
+                }
+            } else {
+                let num_items = line.items.len();
+                let mut is_first = true;
+
+                for child in &mut line.items {
+                    child.main_margin_start = child
+                        .node
+                        .main_margin_start()
+                        .resolve(container_main_size, 0.0);
+                    child.main_margin_end = child
+                        .node
+                        .main_margin_end()
+                        .resolve(container_main_size, 0.0);
+
+                    child.offset_main = match node.justify_content {
+                        style::JustifyContent::FlexStart => 0.0,
+                        style::JustifyContent::Center => if is_first {
+                            free_space / 2.0
+                        } else {
+                            0.0
+                        },
+                        style::JustifyContent::FlexEnd => if is_first {
+                            free_space
+                        } else {
+                            0.0
+                        },
+                        style::JustifyContent::SpaceBetween => if is_first {
+                            0.0
+                        } else {
+                            free_space / (num_items - 1) as f32
+                        },
+                        style::JustifyContent::SpaceAround => if is_first {
+                            (free_space / num_items as f32) / 2.0
+                        } else {
+                            free_space / num_items as f32
+                        },
+                        style::JustifyContent::SpaceEvenly => free_space / (num_items + 1) as f32,
+                    };
+
+                    is_first = false;
+                }
+            }
+        }
+    }
 
-  for line in &mut flex_lines {
-    for child in &mut line.items {
-      let size = compute_internal(
-        child.node,
-        SizeConstraint::exactly(child.target_main_size),
-        SizeConstraint::between(cross.min, avaliable_cross),
-      ).size;
+    // 9.6. Cross-Axis Alignment
 
-      child.hypothetical_inner_cross_size = size.cross
-          .max(child.node.min_cross_size().resolve(avaliable_cross, f32::MIN))
-          .min(child.node.max_cross_size().resolve(avaliable_cross, f32::MAX))
-          .max(0.0);
+    // 13. Resolve cross-axis auto margins. If a flex item has auto cross-axis margins:
+    //     - If its outer cross size (treating those auto margins as zero) is less than the
+    //       cross size of its flex line, distribute the difference in those sizes equally
+    //       to the auto margins.
+    //     - Otherwise, if the block-start or inline-start margin (whichever is in the cross axis)
+    //       is auto, set it to zero. Set the opposite margin so that the outer cross size of the
+    //       item equals the cross size of its flex line.
 
-      child.hypothetical_outer_cross_size = child.hypothetical_inner_cross_size
-          + node.cross_margin_start().resolve(avaliable_cross, 0.0) 
-          + node.cross_margin_end().resolve(avaliable_cross, 0.0);
-    }
-  }
-
-  // 8. Calculate the cross size of each flex line.
-  //    If the flex container is single-line and has a definite cross size, the cross size
-  //    of the flex line is the flex container’s inner cross size. Otherwise, for each flex line:
-  //
-  //    1. Collect all the flex items whose inline-axis is parallel to the main-axis, whose
-  //       align-self is baseline, and whose cross-axis margins are both non-auto. Find the 
-  //       largest of the distances between each item’s baseline and its hypothetical outer
-  //       cross-start edge, and the largest of the distances between each item’s baseline
-  //       and its hypothetical outer cross-end edge, and sum these two values.
-  //    2. Among all the items not collected by the previous step, find the largest
-  //       outer hypothetical cross size.
-  //    3. The used cross-size of the flex line is the largest of the numbers found in the
-  //       previous two steps and zero.
-  //
-  //    If the flex container is single-line, then clamp the line’s cross-size to be within
-  //    the container’s computed min and max cross sizes. Note that if CSS 2.1’s definition
-  //    of min/max-width/height applied more generally, this behavior would fall out automatically.
-
-  if flex_lines.len() == 1 && avaliable_cross.is_finite() {
-    flex_lines[0].cross_size = avaliable_cross.min(cross.max).max(cross.min);
-  } else {
     for line in &mut flex_lines {
-      // TODO handle baseline (1)
-
-      line.cross_size = line.items.iter().map(|child| {
-        child.hypothetical_outer_cross_size
-      }).fold(0.0, |acc, x| acc.max(x));
+        for child in &mut line.items {
+            if child.target_cross_size < line.cross_size {
+                let free_space = line.cross_size - child.target_cross_size;
+
+                if child.node.cross_margin_start() == style::Dimension::Auto
+                    && child.node.cross_margin_end() == style::Dimension::Auto
+                {
+                    child.cross_margin_start = free_space / 2.0;
+                    child.cross_margin_end = free_space / 2.0;
+                } else if child.node.cross_margin_start() == style::Dimension::Auto {
+                    child.cross_margin_start = free_space;
+                } else if child.node.cross_margin_end() == style::Dimension::Auto {
+                    child.cross_margin_end = free_space;
+                } else {
+                    child.cross_margin_start = child
+                        .node
+                        .cross_margin_start()
+                        .resolve(line.cross_size, 0.0);
+                    child.cross_margin_end =
+                        child.node.cross_margin_end().resolve(line.cross_size, 0.0);
+
+                    // 14. Align all flex items along the cross-axis per align-self, if neither of the item’s
+                    //     cross-axis margins are auto.
+
+                    child.offset_cross = match child.node.align_self(node) {
+                        style::AlignSelf::Auto => 0.0, // Should never happen
+                        style::AlignSelf::FlexStart => 0.0,
+                        style::AlignSelf::FlexEnd => free_space,
+                        style::AlignSelf::Center => free_space / 2.0,
+                        style::AlignSelf::Baseline => free_space / 2.0, // Treat as center for now until we have baseline support
+                        style::AlignSelf::Stretch => 0.0,
+                    };
+                }
+            }
+        }
     }
-  }
 
-  // 9. Handle 'align-content: stretch'. If the flex container has a definite cross size,
-  //    align-content is stretch, and the sum of the flex lines' cross sizes is less than
-  //    the flex container’s inner cross size, increase the cross size of each flex line
-  //    by equal amounts such that the sum of their cross sizes exactly equals the
-  //    flex container’s inner cross size.
+    // 15. Determine the flex container’s used cross size:
+    //     - If the cross size property is a definite size, use that, clamped by the used
+    //       min and max cross sizes of the flex container.
+    //     - Otherwise, use the sum of the flex lines' cross sizes, clamped by the used
+    //       min and max cross sizes of the flex container.
 
-  if node.align_content == style::AlignContent::Stretch && avaliable_cross.is_finite() {
-    let total_cross: f32 = flex_lines.iter().map(|line| line.cross_size).sum();
+    let total_cross_size = flex_lines.iter().map(|line| line.cross_size).sum();
+    let container_cross_size = if avaliable_cross.is_finite() {
+        avaliable_cross
+    } else {
+        total_cross_size
+    }.min(cross.max)
+    .max(cross.min);;
 
-    if total_cross < avaliable_cross {
-      let remaining = avaliable_cross - total_cross;
-      let addition = remaining / flex_lines.len() as f32;
+    // 16. Align all flex lines per align-content.
 
-      for line in &mut flex_lines {
-        line.cross_size += addition;
-      }
-    }
-  }
-
-  // 10. Collapse visibility:collapse items. If any flex items have visibility: collapse,
-  //     note the cross size of the line they’re in as the item’s strut size, and restart
-  //     layout from the beginning.
-  //
-  //     In this second layout round, when collecting items into lines, treat the collapsed
-  //     items as having zero main size. For the rest of the algorithm following that step,
-  //     ignore the collapsed items entirely (as if they were display:none) except that after
-  //     calculating the cross size of the lines, if any line’s cross size is less than the
-  //     largest strut size among all the collapsed items in the line, set its cross size to
-  //     that strut size.
-  //
-  //     Skip this step in the second layout round.
-
-  // TODO implement once (if ever) we support visibility:collapse
-
-  // 11. Determine the used cross size of each flex item. If a flex item has align-self: stretch,
-  //     its computed cross size property is auto, and neither of its cross-axis margins are auto,
-  //     the used outer cross size is the used cross size of its flex line, clamped according to
-  //     the item’s used min and max cross sizes. Otherwise, the used cross size is the item’s
-  //     hypothetical cross size.
-  //
-  //     If the flex item has align-self: stretch, redo layout for its contents, treating this
-  //     used size as its definite cross size so that percentage-sized children can be resolved.
-  //
-  //     Note that this step does not affect the main size of the flex item, even if it has an
-  //     intrinsic aspect ratio.
-
-  for line in &mut flex_lines {
-    for child in &mut line.items {
-      let is_stretch = child.node.align_self(node) == style::AlignSelf::Stretch;
-
-      if is_stretch && 
-         child.node.cross_margin_start() != style::Dimension::Auto && 
-         child.node.cross_margin_end() != style::Dimension::Auto &&
-         child.node.cross_size() == style::Dimension::Auto {
-        child.target_cross_size = line.cross_size
-            .min(child.node.max_cross_size().resolve(avaliable_cross, f32::MAX))
-            .max(child.node.min_cross_size().resolve(avaliable_cross, f32::MIN));
-      } else {
-        child.target_cross_size = child.hypothetical_inner_cross_size;
-      }
-
-      if is_stretch {
-        // TODO use result somehow
-        compute_internal(child.node, SizeConstraint::exactly(child.target_main_size), SizeConstraint::exactly(child.target_cross_size));
-      }
-    }
-  }
+    let free_space = container_cross_size - total_cross_size;
+    let num_lines = flex_lines.len();
+    let mut is_first = true;
 
-  // 9.5. Main-Axis Alignment
-
-  // 12. Distribute any remaining free space. For each flex line:
-  //     1. If the remaining free space is positive and at least one main-axis margin on this
-  //        line is auto, distribute the free space equally among these margins. Otherwise, 
-  //        set all auto margins to zero.
-  //     2. Align the items along the main-axis per justify-content.
+    for line in &mut flex_lines {
+        line.offset_cross = match node.align_content {
+            style::AlignContent::FlexStart => 0.0,
+            style::AlignContent::FlexEnd => if is_first {
+                free_space
+            } else {
+                0.0
+            },
+            style::AlignContent::Center => if is_first {
+                free_space / 2.0
+            } else {
+                0.0
+            },
+            style::AlignContent::Stretch => 0.0,
+            style::AlignContent::SpaceBetween => if is_first {
+                0.0
+            } else {
+                free_space / (num_lines - 1) as f32
+            },
+            style::AlignContent::SpaceAround => if is_first {
+                (free_space / num_lines as f32) / 2.0
+            } else {
+                free_space / num_lines as f32
+            },
+        };
+        is_first = false;
+    }
 
-  for line in &mut flex_lines {
-    let used_space: f32 = line.items.iter().map(|child| child.target_main_size).sum();
-    let free_space = avaliable_main - used_space;
+    let mut children: Vec<layout::Node> = vec![];
 
-    if free_space > 0.0 {
-      let mut num_auto_margins = 0;
+    let mut total_offset_cross = node
+        .cross_padding_start()
+        .resolve(container_cross_size, 0.0)
+        + node.cross_border_start().resolve(container_cross_size, 0.0);
 
-      for child in &mut line.items {
-        if child.node.main_margin_start() == style::Dimension::Auto {
-          num_auto_margins += 1;
-        }
-        if child.node.main_margin_end() == style::Dimension::Auto {
-          num_auto_margins += 1;
-        }
-      }
+    for line in &mut flex_lines {
+        let mut total_offset_main = node.main_padding_start().resolve(container_main_size, 0.0)
+            + node.main_border_start().resolve(container_main_size, 0.0);
 
-      if num_auto_margins > 0 {
-        let margin = free_space / num_auto_margins as f32;
-        
         for child in &mut line.items {
-          if child.node.main_margin_start() == style::Dimension::Auto {
-            child.main_margin_start = margin;
-          }
-          if child.node.main_margin_end() == style::Dimension::Auto {
-            child.main_margin_end = margin;
-          }
+            let result = compute_internal(
+                child.node,
+                SizeConstraint::exactly(child.target_main_size),
+                SizeConstraint::exactly(child.target_cross_size),
+            );
+
+            let offset_main = { total_offset_main + child.offset_main + child.main_margin_start };
+
+            let offset_cross = {
+                total_offset_cross
+                    + child.offset_cross
+                    + line.offset_cross
+                    + child.cross_margin_start
+            };
+
+            children.push(layout::Node {
+                width: if child.node.flex_direction.is_row() {
+                    result.size.main
+                } else {
+                    result.size.cross
+                },
+                height: if child.node.flex_direction.is_column() {
+                    result.size.main
+                } else {
+                    result.size.cross
+                },
+                x: if child.node.flex_direction.is_row() {
+                    offset_main
+                } else {
+                    offset_cross
+                },
+                y: if child.node.flex_direction.is_column() {
+                    offset_main
+                } else {
+                    offset_cross
+                },
+                children: result.children,
+            });
+
+            total_offset_main = offset_main + result.size.main + child.main_margin_end;
         }
-      } else {
-        let num_items = line.items.len();
-        let mut is_first = true;
 
-        for child in &mut line.items {
-          child.main_margin_start = child.node.main_margin_start().resolve(container_main_size, 0.0);
-          child.main_margin_end = child.node.main_margin_end().resolve(container_main_size, 0.0);
-
-          child.offset_main = match node.justify_content {
-            style::JustifyContent::FlexStart => 0.0,
-            style::JustifyContent::Center => if is_first { free_space / 2.0 } else { 0.0 },
-            style::JustifyContent::FlexEnd => if is_first { free_space } else { 0.0 },
-            style::JustifyContent::SpaceBetween => if is_first { 0.0 } else { free_space / (num_items - 1) as f32 },
-            style::JustifyContent::SpaceAround => if is_first { (free_space / num_items as f32) / 2.0 } else { free_space / num_items as f32 },
-            style::JustifyContent::SpaceEvenly => free_space / (num_items + 1) as f32,
-          };
-
-          is_first = false;
-        }
-      }
-    }
-  }
-
-  // 9.6. Cross-Axis Alignment
-
-  // 13. Resolve cross-axis auto margins. If a flex item has auto cross-axis margins:
-  //     - If its outer cross size (treating those auto margins as zero) is less than the
-  //       cross size of its flex line, distribute the difference in those sizes equally
-  //       to the auto margins.
-  //     - Otherwise, if the block-start or inline-start margin (whichever is in the cross axis)
-  //       is auto, set it to zero. Set the opposite margin so that the outer cross size of the
-  //       item equals the cross size of its flex line.
-
-  for line in &mut flex_lines {
-    for child in &mut line.items {
-      if child.target_cross_size < line.cross_size {
-        let free_space = line.cross_size - child.target_cross_size;
-
-        if child.node.cross_margin_start() == style::Dimension::Auto && child.node.cross_margin_end() == style::Dimension::Auto {
-          child.cross_margin_start = free_space / 2.0;
-          child.cross_margin_end = free_space / 2.0;
-        } else if child.node.cross_margin_start() == style::Dimension::Auto {
-          child.cross_margin_start = free_space;
-        } else if child.node.cross_margin_end() == style::Dimension::Auto {
-          child.cross_margin_end = free_space;
-        } else {
-          child.cross_margin_start = child.node.cross_margin_start().resolve(line.cross_size, 0.0);
-          child.cross_margin_end = child.node.cross_margin_end().resolve(line.cross_size, 0.0);
-
-          // 14. Align all flex items along the cross-axis per align-self, if neither of the item’s
-          //     cross-axis margins are auto.
-
-          child.offset_cross = match child.node.align_self(node) {
-            style::AlignSelf::Auto => 0.0, // Should never happen
-            style::AlignSelf::FlexStart => 0.0,
-            style::AlignSelf::FlexEnd => free_space, 
-            style::AlignSelf::Center => free_space / 2.0, 
-            style::AlignSelf::Baseline => free_space / 2.0, // Treat as center for now until we have baseline support
-            style::AlignSelf::Stretch => 0.0,
-          };
-        }
-      }
+        total_offset_cross += line.offset_cross + line.cross_size;
     }
-  }
-
-  // 15. Determine the flex container’s used cross size:
-  //     - If the cross size property is a definite size, use that, clamped by the used
-  //       min and max cross sizes of the flex container.
-  //     - Otherwise, use the sum of the flex lines' cross sizes, clamped by the used
-  //       min and max cross sizes of the flex container.
-
-  let total_cross_size = flex_lines.iter().map(|line| line.cross_size).sum();
-  let container_cross_size = if avaliable_cross.is_finite() {
-    avaliable_cross
-  } else {
-    total_cross_size
-  }.min(cross.max).max(cross.min);;
-
-  // 16. Align all flex lines per align-content.
-
-  let free_space = container_cross_size - total_cross_size;
-  let num_lines = flex_lines.len();
-  let mut is_first = true;
-
-  for line in &mut flex_lines {
-    line.offset_cross = match node.align_content {
-      style::AlignContent::FlexStart => 0.0,
-      style::AlignContent::FlexEnd => if is_first { free_space } else { 0.0 },
-      style::AlignContent::Center => if is_first { free_space / 2.0 } else { 0.0 },
-      style::AlignContent::Stretch => 0.0,
-      style::AlignContent::SpaceBetween => if is_first { 0.0 } else { free_space / (num_lines - 1) as f32 },
-      style::AlignContent::SpaceAround => if is_first { (free_space / num_lines as f32) / 2.0 } else { free_space / num_lines as f32 },
-    };
-    is_first = false;
-  }
-
-  let mut children: Vec<layout::Node> = vec![];
-
-  let mut total_offset_cross =
-      node.cross_padding_start().resolve(container_cross_size, 0.0) + 
-      node.cross_border_start().resolve(container_cross_size, 0.0);
-
-  for line in &mut flex_lines {
-    let mut total_offset_main =
-        node.main_padding_start().resolve(container_main_size, 0.0) + 
-        node.main_border_start().resolve(container_main_size, 0.0);
-        
-    for child in &mut line.items {
-      let result = compute_internal(
-        child.node, 
-        SizeConstraint::exactly(child.target_main_size), 
-        SizeConstraint::exactly(child.target_cross_size));
-
-      let offset_main = {
-        total_offset_main +
-        child.offset_main +
-        child.main_margin_start
-      };
-
-      let offset_cross = {
-        total_offset_cross +
-        child.offset_cross + 
-        line.offset_cross +
-        child.cross_margin_start
-      };
-
-      children.push(layout::Node {
-        width: if child.node.flex_direction.is_row() { result.size.main } else { result.size.cross },
-        height: if child.node.flex_direction.is_column() { result.size.main } else { result.size.cross },
-        x: if child.node.flex_direction.is_row() { offset_main } else {offset_cross },
-        y: if child.node.flex_direction.is_column() { offset_main } else {offset_cross },
-        children: result.children,
-      });
 
-      total_offset_main = offset_main + result.size.main + child.main_margin_end;
+    ComputeResult {
+        size: FlexSize {
+            main: container_main_size,
+            cross: container_cross_size,
+        },
+        children: children,
     }
-
-    total_offset_cross += line.offset_cross + line.cross_size;
-  }
-
-  ComputeResult {
-    size: FlexSize {
-      main: container_main_size,
-      cross: container_cross_size,
-    },
-    children: children,
-  }
 }
diff --git a/src/layout.rs b/src/layout.rs
index c9906be2c..3fe8fa74e 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -1,16 +1,16 @@
 #[derive(Debug)]
 pub struct Edges {
-  pub left: f32,
-  pub right: f32,
-  pub top: f32,
-  pub bottom: f32,
+    pub left: f32,
+    pub right: f32,
+    pub top: f32,
+    pub bottom: f32,
 }
 
 #[derive(Debug)]
 pub struct Node {
-  pub width: f32,
-  pub height: f32,
-  pub x: f32,
-  pub y: f32,
-  pub children: Vec<Node>,
-}
\ No newline at end of file
+    pub width: f32,
+    pub height: f32,
+    pub x: f32,
+    pub y: f32,
+    pub children: Vec<Node>,
+}
diff --git a/src/lib.rs b/src/lib.rs
index 87fba59fa..36c851903 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,5 @@
-pub mod style;
 pub mod layout;
+pub mod style;
 
 mod algo;
-pub use algo::compute;
\ No newline at end of file
+pub use algo::compute;
diff --git a/src/style.rs b/src/style.rs
index a48822566..a18d0be46 100644
--- a/src/style.rs
+++ b/src/style.rs
@@ -1,396 +1,418 @@
 #[derive(Copy, Clone, PartialEq, Debug)]
 pub enum AlignItems {
-  FlexStart,
-  FlexEnd,
-  Center,
-  Baseline,
-  Stretch,
+    FlexStart,
+    FlexEnd,
+    Center,
+    Baseline,
+    Stretch,
 }
 
 impl Default for AlignItems {
-  fn default() -> AlignItems { AlignItems::Stretch }
+    fn default() -> AlignItems {
+        AlignItems::Stretch
+    }
 }
 
 #[derive(Copy, Clone, PartialEq, Debug)]
 pub enum AlignSelf {
-  Auto,
-  FlexStart,
-  FlexEnd,
-  Center,
-  Baseline,
-  Stretch,
+    Auto,
+    FlexStart,
+    FlexEnd,
+    Center,
+    Baseline,
+    Stretch,
 }
 
 impl Default for AlignSelf {
-  fn default() -> AlignSelf { AlignSelf::Auto }
+    fn default() -> AlignSelf {
+        AlignSelf::Auto
+    }
 }
 
 #[derive(Copy, Clone, PartialEq, Debug)]
 pub enum AlignContent {
-  FlexStart,
-  FlexEnd,
-  Center,
-  Stretch,
-  SpaceBetween,
-  SpaceAround,
+    FlexStart,
+    FlexEnd,
+    Center,
+    Stretch,
+    SpaceBetween,
+    SpaceAround,
 }
 
 impl Default for AlignContent {
-  fn default() -> AlignContent { AlignContent::Stretch }
+    fn default() -> AlignContent {
+        AlignContent::Stretch
+    }
 }
 
 #[derive(Copy, Clone, PartialEq, Debug)]
 pub enum Direction {
-  Inherit,
-  LTR,
-  RTL,
+    Inherit,
+    LTR,
+    RTL,
 }
 
 impl Default for Direction {
-  fn default() -> Direction { Direction::Inherit }
+    fn default() -> Direction {
+        Direction::Inherit
+    }
 }
 
 #[derive(Copy, Clone, PartialEq, Debug)]
 pub enum Display {
-  Flex,
-  None,
+    Flex,
+    None,
 }
 
 impl Default for Display {
-  fn default() -> Display { Display::Flex }
+    fn default() -> Display {
+        Display::Flex
+    }
 }
 
 #[derive(Copy, Clone, PartialEq, Debug)]
 pub enum FlexDirection {
-  Row,
-  Column,
-  RowReverse,
-  ColumnReverse,
+    Row,
+    Column,
+    RowReverse,
+    ColumnReverse,
 }
 
 impl Default for FlexDirection {
-  fn default() -> FlexDirection { FlexDirection::Row }
+    fn default() -> FlexDirection {
+        FlexDirection::Row
+    }
 }
 
 impl FlexDirection {
-  pub(crate) fn is_row(self) -> bool {
-    self == FlexDirection::Row || self == FlexDirection::RowReverse
-  }
+    pub(crate) fn is_row(self) -> bool {
+        self == FlexDirection::Row || self == FlexDirection::RowReverse
+    }
 
-  pub(crate) fn is_column(self) -> bool {
-    self == FlexDirection::Column || self == FlexDirection::ColumnReverse
-  }
+    pub(crate) fn is_column(self) -> bool {
+        self == FlexDirection::Column || self == FlexDirection::ColumnReverse
+    }
 }
 
 #[derive(Copy, Clone, PartialEq, Debug)]
 pub enum JustifyContent {
-  FlexStart,
-  Center,
-  FlexEnd,
-  SpaceBetween,
-  SpaceAround,
-  SpaceEvenly,
+    FlexStart,
+    FlexEnd,
+    Center,
+    SpaceBetween,
+    SpaceAround,
+    SpaceEvenly,
 }
 
 impl Default for JustifyContent {
-  fn default() -> JustifyContent { JustifyContent::FlexStart }
+    fn default() -> JustifyContent {
+        JustifyContent::FlexStart
+    }
 }
 
 #[derive(Copy, Clone, PartialEq, Debug)]
 pub enum Overflow {
-  Visible,
-  Hidden,
-  Scroll,
+    Visible,
+    Hidden,
+    Scroll,
 }
 
 impl Default for Overflow {
-  fn default() -> Overflow { Overflow::Visible }
+    fn default() -> Overflow {
+        Overflow::Visible
+    }
 }
 
 #[derive(Copy, Clone, PartialEq, Debug)]
 pub enum Position {
-  Relative,
-  Absolute,
+    Relative,
+    Absolute,
 }
 
 impl Default for Position {
-  fn default() -> Position { Position::Relative }
+    fn default() -> Position {
+        Position::Relative
+    }
 }
 
 #[derive(Copy, Clone, PartialEq, Debug)]
 pub enum Wrap {
-  NoWrap,
-  Wrap,
-  WrapReverse,
+    NoWrap,
+    Wrap,
+    WrapReverse,
 }
 
 impl Default for Wrap {
-  fn default() -> Wrap { Wrap::NoWrap }
+    fn default() -> Wrap {
+        Wrap::NoWrap
+    }
 }
 
 #[derive(Copy, Clone, PartialEq, Debug)]
 pub enum Dimension {
-  Auto,
-  Points(f32),
-  Percent(f32),
+    Auto,
+    Points(f32),
+    Percent(f32),
 }
 
 impl Default for Dimension {
-  fn default() -> Dimension { Dimension::Auto }
+    fn default() -> Dimension {
+        Dimension::Auto
+    }
 }
 
 impl Dimension {
-  pub(crate) fn resolve(&self, parent_size: f32, auto_size: f32) -> f32 {
-    match self {
-      Dimension::Points(points) => *points,
-      Dimension::Percent(percent) if parent_size.is_finite() => percent * parent_size,
-      _ => auto_size,
+    pub(crate) fn resolve(&self, parent_size: f32, auto_size: f32) -> f32 {
+        match self {
+            Dimension::Points(points) => *points,
+            Dimension::Percent(percent) if parent_size.is_finite() => percent * parent_size,
+            _ => auto_size,
+        }
     }
-  }
 }
 
 #[derive(Debug)]
 pub struct Edges {
-  pub start: Dimension,
-  pub end: Dimension,
-  pub top: Dimension,
-  pub bottom: Dimension,
+    pub start: Dimension,
+    pub end: Dimension,
+    pub top: Dimension,
+    pub bottom: Dimension,
 }
 
 impl Default for Edges {
-  fn default() -> Edges {
-    Edges {
-      start: Dimension::Points(0.0),
-      end: Dimension::Points(0.0),
-      top: Dimension::Points(0.0),
-      bottom: Dimension::Points(0.0),
-    }
-  }
+    fn default() -> Edges {
+        Edges {
+            start: Dimension::Points(0.0),
+            end: Dimension::Points(0.0),
+            top: Dimension::Points(0.0),
+            bottom: Dimension::Points(0.0),
+        }
+    }
 }
 
 #[derive(Debug)]
 pub struct Node {
-  pub position: Position,
-  pub direction: Direction,
-  pub flex_direction: FlexDirection,
-  
-  pub wrap: Wrap,
-  pub overflow: Overflow,
-
-  pub align_items: AlignItems,
-  pub align_self: AlignSelf,
-  pub align_content: AlignContent,
-  
-  pub justify_content: JustifyContent,
-
-  pub margin: Edges,
-  pub padding: Edges,
-  pub border: Edges,
-
-  pub flex_grow: f32,
-  pub flex_shrink: f32,
-  pub flex_basis: Dimension,
-
-  pub left: Dimension,
-  pub right: Dimension,
-  pub top: Dimension,
-  pub bottom: Dimension,
-
-  pub width: Dimension,
-  pub min_width: Dimension,
-  pub max_width: Dimension,
-
-  pub height: Dimension,
-  pub min_height: Dimension,
-  pub max_height: Dimension,
-
-  pub aspect_ratio: Option<f32>,
-
-  pub children: Vec<Node>,
+    pub position: Position,
+    pub direction: Direction,
+    pub flex_direction: FlexDirection,
+
+    pub wrap: Wrap,
+    pub overflow: Overflow,
+
+    pub align_items: AlignItems,
+    pub align_self: AlignSelf,
+    pub align_content: AlignContent,
+
+    pub justify_content: JustifyContent,
+
+    pub margin: Edges,
+    pub padding: Edges,
+    pub border: Edges,
+
+    pub flex_grow: f32,
+    pub flex_shrink: f32,
+    pub flex_basis: Dimension,
+
+    pub start: Dimension,
+    pub end: Dimension,
+    pub top: Dimension,
+    pub bottom: Dimension,
+
+    pub width: Dimension,
+    pub min_width: Dimension,
+    pub max_width: Dimension,
+
+    pub height: Dimension,
+    pub min_height: Dimension,
+    pub max_height: Dimension,
+
+    pub aspect_ratio: Option<f32>,
+
+    pub children: Vec<Node>,
 }
 
 impl Default for Node {
-  fn default() -> Node {
-    Node {
-      position: Default::default(),
-      direction: Default::default(),
-      flex_direction: Default::default(),
+    fn default() -> Node {
+        Node {
+            position: Default::default(),
+            direction: Default::default(),
+            flex_direction: Default::default(),
 
-      wrap: Default::default(),
-      overflow: Default::default(),
+            wrap: Default::default(),
+            overflow: Default::default(),
 
-      align_items: Default::default(),
-      align_self: Default::default(),
-      align_content: Default::default(),
+            align_items: Default::default(),
+            align_self: Default::default(),
+            align_content: Default::default(),
 
-      justify_content: Default::default(),
+            justify_content: Default::default(),
 
-      margin: Default::default(),
-      padding: Default::default(),
-      border: Default::default(),
+            margin: Default::default(),
+            padding: Default::default(),
+            border: Default::default(),
 
-      flex_grow: 0.0,
-      flex_shrink: 1.0,
-      flex_basis: Default::default(),
+            flex_grow: 0.0,
+            flex_shrink: 1.0,
+            flex_basis: Default::default(),
 
-      left: Default::default(),
-      right: Default::default(),
-      top: Default::default(),
-      bottom: Default::default(),
+            start: Default::default(),
+            end: Default::default(),
+            top: Default::default(),
+            bottom: Default::default(),
 
-      width: Default::default(),
-      min_width: Default::default(),
-      max_width: Default::default(),
+            width: Default::default(),
+            min_width: Default::default(),
+            max_width: Default::default(),
 
-      height: Default::default(),
-      min_height: Default::default(),
-      max_height: Default::default(),
+            height: Default::default(),
+            min_height: Default::default(),
+            max_height: Default::default(),
 
-      aspect_ratio: Default::default(),
+            aspect_ratio: Default::default(),
 
-      children: vec![],
+            children: vec![],
+        }
     }
-  }
 }
 
 impl Node {
-  pub(crate) fn main_size(&self) -> Dimension {
-    match self.flex_direction {
-      FlexDirection::Row | FlexDirection::RowReverse => self.width,
-      FlexDirection::Column | FlexDirection::ColumnReverse => self.height,
+    pub(crate) fn main_size(&self) -> Dimension {
+        match self.flex_direction {
+            FlexDirection::Row | FlexDirection::RowReverse => self.width,
+            FlexDirection::Column | FlexDirection::ColumnReverse => self.height,
+        }
     }
-  }
 
-  pub(crate) fn min_main_size(&self) -> Dimension {
-    match self.flex_direction {
-      FlexDirection::Row | FlexDirection::RowReverse => self.min_width,
-      FlexDirection::Column | FlexDirection::ColumnReverse => self.min_height,
+    pub(crate) fn min_main_size(&self) -> Dimension {
+        match self.flex_direction {
+            FlexDirection::Row | FlexDirection::RowReverse => self.min_width,
+            FlexDirection::Column | FlexDirection::ColumnReverse => self.min_height,
+        }
     }
-  }
 
-  pub(crate) fn max_main_size(&self) -> Dimension {
-    match self.flex_direction {
-      FlexDirection::Row | FlexDirection::RowReverse => self.max_width,
-      FlexDirection::Column | FlexDirection::ColumnReverse => self.max_height,
+    pub(crate) fn max_main_size(&self) -> Dimension {
+        match self.flex_direction {
+            FlexDirection::Row | FlexDirection::RowReverse => self.max_width,
+            FlexDirection::Column | FlexDirection::ColumnReverse => self.max_height,
+        }
     }
-  }
 
-  pub(crate) fn main_margin_start(&self) -> Dimension {
-    match self.flex_direction {
-      FlexDirection::Row | FlexDirection::RowReverse => self.margin.start,
-      FlexDirection::Column | FlexDirection::ColumnReverse => self.margin.top,
+    pub(crate) fn main_margin_start(&self) -> Dimension {
+        match self.flex_direction {
+            FlexDirection::Row | FlexDirection::RowReverse => self.margin.start,
+            FlexDirection::Column | FlexDirection::ColumnReverse => self.margin.top,
+        }
     }
-  }
 
-  pub(crate) fn main_margin_end(&self) -> Dimension {
-    match self.flex_direction {
-      FlexDirection::Row | FlexDirection::RowReverse => self.margin.end,
-      FlexDirection::Column | FlexDirection::ColumnReverse => self.margin.bottom,
+    pub(crate) fn main_margin_end(&self) -> Dimension {
+        match self.flex_direction {
+            FlexDirection::Row | FlexDirection::RowReverse => self.margin.end,
+            FlexDirection::Column | FlexDirection::ColumnReverse => self.margin.bottom,
+        }
     }
-  }
 
-  pub(crate) fn main_padding_start(&self) -> Dimension {
-    match self.flex_direction {
-      FlexDirection::Row | FlexDirection::RowReverse => self.padding.start,
-      FlexDirection::Column | FlexDirection::ColumnReverse => self.padding.top,
+    pub(crate) fn main_padding_start(&self) -> Dimension {
+        match self.flex_direction {
+            FlexDirection::Row | FlexDirection::RowReverse => self.padding.start,
+            FlexDirection::Column | FlexDirection::ColumnReverse => self.padding.top,
+        }
     }
-  }
 
-  pub(crate) fn main_padding_end(&self) -> Dimension {
-    match self.flex_direction {
-      FlexDirection::Row | FlexDirection::RowReverse => self.padding.end,
-      FlexDirection::Column | FlexDirection::ColumnReverse => self.padding.bottom,
+    pub(crate) fn main_padding_end(&self) -> Dimension {
+        match self.flex_direction {
+            FlexDirection::Row | FlexDirection::RowReverse => self.padding.end,
+            FlexDirection::Column | FlexDirection::ColumnReverse => self.padding.bottom,
+        }
     }
-  }
 
-  pub(crate) fn main_border_start(&self) -> Dimension {
-    match self.flex_direction {
-      FlexDirection::Row | FlexDirection::RowReverse => self.border.start,
-      FlexDirection::Column | FlexDirection::ColumnReverse => self.border.top,
+    pub(crate) fn main_border_start(&self) -> Dimension {
+        match self.flex_direction {
+            FlexDirection::Row | FlexDirection::RowReverse => self.border.start,
+            FlexDirection::Column | FlexDirection::ColumnReverse => self.border.top,
+        }
     }
-  }
 
-  pub(crate) fn main_border_end(&self) -> Dimension {
-    match self.flex_direction {
-      FlexDirection::Row | FlexDirection::RowReverse => self.border.end,
-      FlexDirection::Column | FlexDirection::ColumnReverse => self.border.bottom,
+    pub(crate) fn main_border_end(&self) -> Dimension {
+        match self.flex_direction {
+            FlexDirection::Row | FlexDirection::RowReverse => self.border.end,
+            FlexDirection::Column | FlexDirection::ColumnReverse => self.border.bottom,
+        }
     }
-  }
 
-  pub(crate) fn cross_size(&self) -> Dimension {
-    match self.flex_direction {
-      FlexDirection::Row | FlexDirection::RowReverse => self.height,
-      FlexDirection::Column | FlexDirection::ColumnReverse => self.width,
+    pub(crate) fn cross_size(&self) -> Dimension {
+        match self.flex_direction {
+            FlexDirection::Row | FlexDirection::RowReverse => self.height,
+            FlexDirection::Column | FlexDirection::ColumnReverse => self.width,
+        }
     }
-  }
 
-  pub(crate) fn min_cross_size(&self) -> Dimension {
-    match self.flex_direction {
-      FlexDirection::Row | FlexDirection::RowReverse => self.min_height,
-      FlexDirection::Column | FlexDirection::ColumnReverse => self.min_width,
+    pub(crate) fn min_cross_size(&self) -> Dimension {
+        match self.flex_direction {
+            FlexDirection::Row | FlexDirection::RowReverse => self.min_height,
+            FlexDirection::Column | FlexDirection::ColumnReverse => self.min_width,
+        }
     }
-  }
 
-  pub(crate) fn max_cross_size(&self) -> Dimension {
-    match self.flex_direction {
-      FlexDirection::Row | FlexDirection::RowReverse => self.max_height,
-      FlexDirection::Column | FlexDirection::ColumnReverse => self.max_width,
+    pub(crate) fn max_cross_size(&self) -> Dimension {
+        match self.flex_direction {
+            FlexDirection::Row | FlexDirection::RowReverse => self.max_height,
+            FlexDirection::Column | FlexDirection::ColumnReverse => self.max_width,
+        }
     }
-  }
 
-  pub(crate) fn cross_margin_start(&self) -> Dimension {
-    match self.flex_direction {
-      FlexDirection::Row | FlexDirection::RowReverse => self.margin.top,
-      FlexDirection::Column | FlexDirection::ColumnReverse => self.margin.start,
+    pub(crate) fn cross_margin_start(&self) -> Dimension {
+        match self.flex_direction {
+            FlexDirection::Row | FlexDirection::RowReverse => self.margin.top,
+            FlexDirection::Column | FlexDirection::ColumnReverse => self.margin.start,
+        }
     }
-  }
 
-  pub(crate) fn cross_margin_end(&self) -> Dimension {
-    match self.flex_direction {
-      FlexDirection::Row | FlexDirection::RowReverse => self.margin.bottom,
-      FlexDirection::Column | FlexDirection::ColumnReverse => self.margin.end,
+    pub(crate) fn cross_margin_end(&self) -> Dimension {
+        match self.flex_direction {
+            FlexDirection::Row | FlexDirection::RowReverse => self.margin.bottom,
+            FlexDirection::Column | FlexDirection::ColumnReverse => self.margin.end,
+        }
     }
-  }
 
-  pub(crate) fn cross_padding_start(&self) -> Dimension {
-    match self.flex_direction {
-      FlexDirection::Row | FlexDirection::RowReverse => self.padding.top,
-      FlexDirection::Column | FlexDirection::ColumnReverse => self.padding.start,
+    pub(crate) fn cross_padding_start(&self) -> Dimension {
+        match self.flex_direction {
+            FlexDirection::Row | FlexDirection::RowReverse => self.padding.top,
+            FlexDirection::Column | FlexDirection::ColumnReverse => self.padding.start,
+        }
     }
-  }
 
-  pub(crate) fn cross_padding_end(&self) -> Dimension {
-    match self.flex_direction {
-      FlexDirection::Row | FlexDirection::RowReverse => self.padding.bottom,
-      FlexDirection::Column | FlexDirection::ColumnReverse => self.padding.end,
+    pub(crate) fn cross_padding_end(&self) -> Dimension {
+        match self.flex_direction {
+            FlexDirection::Row | FlexDirection::RowReverse => self.padding.bottom,
+            FlexDirection::Column | FlexDirection::ColumnReverse => self.padding.end,
+        }
     }
-  }
 
-  pub(crate) fn cross_border_start(&self) -> Dimension {
-    match self.flex_direction {
-      FlexDirection::Row | FlexDirection::RowReverse => self.border.top,
-      FlexDirection::Column | FlexDirection::ColumnReverse => self.border.start,
+    pub(crate) fn cross_border_start(&self) -> Dimension {
+        match self.flex_direction {
+            FlexDirection::Row | FlexDirection::RowReverse => self.border.top,
+            FlexDirection::Column | FlexDirection::ColumnReverse => self.border.start,
+        }
     }
-  }
 
-  pub(crate) fn cross_border_end(&self) -> Dimension {
-    match self.flex_direction {
-      FlexDirection::Row | FlexDirection::RowReverse => self.border.bottom,
-      FlexDirection::Column | FlexDirection::ColumnReverse => self.border.end,
+    pub(crate) fn cross_border_end(&self) -> Dimension {
+        match self.flex_direction {
+            FlexDirection::Row | FlexDirection::RowReverse => self.border.bottom,
+            FlexDirection::Column | FlexDirection::ColumnReverse => self.border.end,
+        }
     }
-  }
 
-  pub(crate) fn align_self(&self, parent: &Node) -> AlignSelf {
-    if self.align_self == AlignSelf::Auto {
-      match parent.align_items {
-        AlignItems::FlexStart => AlignSelf::FlexStart,
-        AlignItems::FlexEnd => AlignSelf::FlexEnd,
-        AlignItems::Center => AlignSelf::Center,
-        AlignItems::Baseline => AlignSelf::Baseline,
-        AlignItems::Stretch => AlignSelf::Stretch,
-      }
-    } else {
-      self.align_self
+    pub(crate) fn align_self(&self, parent: &Node) -> AlignSelf {
+        if self.align_self == AlignSelf::Auto {
+            match parent.align_items {
+                AlignItems::FlexStart => AlignSelf::FlexStart,
+                AlignItems::FlexEnd => AlignSelf::FlexEnd,
+                AlignItems::Center => AlignSelf::Center,
+                AlignItems::Baseline => AlignSelf::Baseline,
+                AlignItems::Stretch => AlignSelf::Stretch,
+            }
+        } else {
+            self.align_self
+        }
     }
-  }
 }
diff --git a/test_fixtures/sample.html b/test_fixtures/sample.html
new file mode 100644
index 000000000..618f23c19
--- /dev/null
+++ b/test_fixtures/sample.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <script src="../scripts/gentest/test_helper.js"></script>
+  <link rel="stylesheet" type="text/css" href="../scripts/gentest/test_base_style.css">
+  <title>
+    Test description
+  </title>
+<head/>
+<body>
+
+<div id="test-root" style="width: 100px; height: 100px;">
+  <div style="width: 50px;"></div>
+</div>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/tests/generated.rs b/tests/generated.rs
new file mode 100644
index 000000000..8d6c9c10e
--- /dev/null
+++ b/tests/generated.rs
@@ -0,0 +1,27 @@
+#[cfg(test)]
+mod generated {
+    #[test]
+    fn sample() {
+        let layout = stretch::compute(&stretch::style::Node {
+            width: stretch::style::Dimension::Points(100.0),
+            height: stretch::style::Dimension::Points(100.0),
+            children: vec![stretch::style::Node {
+                width: stretch::style::Dimension::Points(50.0),
+                children: vec![],
+                ..Default::default()
+            }],
+            ..Default::default()
+        });
+
+        assert_eq!(layout.width, 100.0);
+        assert_eq!(layout.height, 100.0);
+        assert_eq!(layout.x, 0.0);
+        assert_eq!(layout.y, 0.0);
+
+        assert_eq!(layout.children[0].width, 50.0);
+        assert_eq!(layout.children[0].height, 100.0);
+        assert_eq!(layout.children[0].x, 0.0);
+        assert_eq!(layout.children[0].y, 0.0);
+    }
+
+}
diff --git a/tests/lib.rs b/tests/manual.rs
similarity index 98%
rename from tests/lib.rs
rename to tests/manual.rs
index 7d02c7323..6a4fa0e3a 100644
--- a/tests/lib.rs
+++ b/tests/manual.rs
@@ -18,14 +18,12 @@ mod tests {
             width: stretch::style::Dimension::Points(100.0),
             height: stretch::style::Dimension::Points(100.0),
 
-            children: vec![
-                stretch::style::Node {
-                    width: stretch::style::Dimension::Percent(0.5),
+            children: vec![stretch::style::Node {
+                width: stretch::style::Dimension::Percent(0.5),
+
+                ..Default::default()
+            }],
 
-                    ..Default::default()
-                },
-            ],
-          
             ..Default::default()
         });
 
@@ -51,7 +49,7 @@ mod tests {
                     ..Default::default()
                 },
             ],
-          
+
             ..Default::default()
         });
 
@@ -85,7 +83,7 @@ mod tests {
                     ..Default::default()
                 },
             ],
-          
+
             ..Default::default()
         });
 
@@ -120,7 +118,7 @@ mod tests {
                     ..Default::default()
                 },
             ],
-          
+
             ..Default::default()
         });
 
@@ -157,7 +155,7 @@ mod tests {
                     ..Default::default()
                 },
             ],
-          
+
             ..Default::default()
         });
 
@@ -196,7 +194,7 @@ mod tests {
                     ..Default::default()
                 },
             ],
-          
+
             ..Default::default()
         });
 
@@ -235,7 +233,7 @@ mod tests {
                     ..Default::default()
                 },
             ],
-          
+
             ..Default::default()
         });
 
@@ -271,7 +269,7 @@ mod tests {
                     ..Default::default()
                 },
             ],
-          
+
             ..Default::default()
         });
 
@@ -307,7 +305,7 @@ mod tests {
                     ..Default::default()
                 },
             ],
-          
+
             ..Default::default()
         });
 
@@ -343,7 +341,7 @@ mod tests {
                     ..Default::default()
                 },
             ],
-          
+
             ..Default::default()
         });
 
@@ -379,7 +377,7 @@ mod tests {
                     ..Default::default()
                 },
             ],
-          
+
             ..Default::default()
         });
 
@@ -415,7 +413,7 @@ mod tests {
                     ..Default::default()
                 },
             ],
-          
+
             ..Default::default()
         });
 
@@ -452,7 +450,7 @@ mod tests {
                     ..Default::default()
                 },
             ],
-          
+
             ..Default::default()
         });
 
@@ -486,7 +484,7 @@ mod tests {
                     ..Default::default()
                 },
             ],
-          
+
             ..Default::default()
         });
 
@@ -541,7 +539,7 @@ mod tests {
                     ..Default::default()
                 },
             ],
-          
+
             ..Default::default()
         });
 
@@ -607,7 +605,7 @@ mod tests {
                     ..Default::default()
                 },
             ],
-          
+
             ..Default::default()
         });