tawasuyu

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 5544Rust 88Markdown 11TOML 91926 Sutichaykuna

Grid/refactor coordinate handling (#301)

* Use style helpers for creating GridPlacement::Line in gentests

* Introduce newtype system for grid line coordinate handling

* Add test for correct grid line coordinate handling

* Use i16 rather than NonZeroI16 as the backing storage for GridLine, and convert to Auto internally if zero is specified

* Update src/style/grid.rs

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

* Update src/style/grid.rs

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

* Fix typo

* Add unsigned_abs method to OriginZeroLine

* Improve estimate of grid size

* Add span method to Line<OriginZeroLine>

* Fix auto placement in negative tracks

* Update changelog with new GridLine type

Co-authored-by: Andreas Weibye <13300393+Weibye@users.noreply.github.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>

859cc921b3adb8e3b184e6fc0cf0c40b6c5a625b · ruwaq Nico Burns · (3 wata ñawpaq) · tayta 30f836428 · kay commitpi sach'ata qhaway

Semántico sayk'uy · 115

benches/generated/grid_absolute_column_end.rs

  • ukhunpi t'ikrakurqan fn compute pub

benches/generated/grid_absolute_column_start.rs

  • ukhunpi t'ikrakurqan fn compute pub

benches/generated/grid_absolute_row_end.rs

  • ukhunpi t'ikrakurqan fn compute pub

benches/generated/grid_absolute_row_start.rs

  • ukhunpi t'ikrakurqan fn compute pub

benches/generated/grid_align_items_sized_center.rs

  • ukhunpi t'ikrakurqan fn compute pub

benches/generated/grid_align_items_sized_end.rs

  • ukhunpi t'ikrakurqan fn compute pub

benches/generated/grid_align_items_sized_start.rs

  • ukhunpi t'ikrakurqan fn compute pub

benches/generated/grid_align_items_sized_stretch.rs

  • ukhunpi t'ikrakurqan fn compute pub

benches/generated/grid_justify_items_sized_center.rs

  • ukhunpi t'ikrakurqan fn compute pub

benches/generated/grid_justify_items_sized_end.rs

  • ukhunpi t'ikrakurqan fn compute pub

benches/generated/grid_justify_items_sized_start.rs

  • ukhunpi t'ikrakurqan fn compute pub

benches/generated/grid_justify_items_sized_stretch.rs

  • ukhunpi t'ikrakurqan fn compute pub

benches/generated/grid_out_of_order_items.rs

  • ukhunpi t'ikrakurqan fn compute pub

benches/generated/grid_placement_auto_negative.rs

benches/generated/grid_placement_definite_in_secondary_axis_with_fully_definite_negative.rs

scripts/gentest/src/main.rs

src/compute/grid/implicit_grid.rs

src/compute/grid/mod.rs

  • ukhunpi t'ikrakurqan fn compute pub

src/compute/grid/placement.rs

src/compute/grid/track_sizing.rs

src/compute/grid/types/cell_occupancy.rs

src/compute/grid/types/coordinates.rs

src/compute/grid/types/grid_item.rs

src/compute/grid/types/grid_track_counts.rs

src/compute/grid/types/mod.rs

src/compute/grid/util/coordinates.rs

src/compute/grid/util/mod.rs

src/compute/grid/util/test_helpers.rs

src/style/grid.rs

tests/generated/grid_absolute_column_end.rs

tests/generated/grid_absolute_column_start.rs

tests/generated/grid_absolute_row_end.rs

tests/generated/grid_absolute_row_start.rs

tests/generated/grid_align_items_sized_center.rs

tests/generated/grid_align_items_sized_end.rs

tests/generated/grid_align_items_sized_start.rs

tests/generated/grid_align_items_sized_stretch.rs

tests/generated/grid_justify_items_sized_center.rs

tests/generated/grid_justify_items_sized_end.rs

tests/generated/grid_justify_items_sized_start.rs

tests/generated/grid_justify_items_sized_stretch.rs

tests/generated/grid_out_of_order_items.rs

tests/generated/grid_placement_auto_negative.rs

tests/generated/grid_placement_definite_in_secondary_axis_with_fully_definite_negative.rs

51 qillqakuna · +807 −617

diff --git a/RELEASES.md b/RELEASES.md
index c3e92e967..af9b1a56a 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -6,12 +6,13 @@
 
 - `experimental_grid` feature named to just `grid`
 - `grid` feature enabled by default
+- `GridPlacement::Line` now stores a `GridLine` newtype wrapper around an `i16` rather than a raw `i16`. If you are using the style helpers then this change will not affect you.
+- *BREAKING:* `Position` is now renamed to `Inset` and is now in line with [CSS inset specs](https://developer.mozilla.org/en-US/docs/Web/CSS/inset)
+- *BREAKING:* `PositionType` is now renamed to `Position` and is now in line with [CSS position specs](https://developer.mozilla.org/en-US/docs/Web/CSS/position)
 
 ### Fixes
 
 - Flexbox nodes sized under a min-content constraint now size correctly (#291)
-- *BREAKING:* `Position` is now renamed to `Inset` and is now in line with [CSS inset specs](https://developer.mozilla.org/en-US/docs/Web/CSS/inset)
-- *BREAKING:* `PositionType` is now renamed to `Position` and is now in line with [CSS position specs](https://developer.mozilla.org/en-US/docs/Web/CSS/position)
 
 ### Removed
 
diff --git a/benches/generated/grid_absolute_column_end.rs b/benches/generated/grid_absolute_column_end.rs
index 06ef955eb..60cfa519e 100644
--- a/benches/generated/grid_absolute_column_end.rs
+++ b/benches/generated/grid_absolute_column_end.rs
@@ -5,10 +5,7 @@ pub fn compute() {
     let node0 = taffy
         .new_leaf(taffy::style::Style {
             position: taffy::style::Position::Absolute,
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Auto,
-                end: taffy::style::GridPlacement::Line(1i16),
-            },
+            grid_column: taffy::geometry::Line { start: taffy::style::GridPlacement::Auto, end: line(1i16) },
             inset: taffy::geometry::Rect {
                 left: taffy::style::LengthPercentageAuto::Points(4f32),
                 right: taffy::style::LengthPercentageAuto::Points(3f32),
diff --git a/benches/generated/grid_absolute_column_start.rs b/benches/generated/grid_absolute_column_start.rs
index e81125301..11362c184 100644
--- a/benches/generated/grid_absolute_column_start.rs
+++ b/benches/generated/grid_absolute_column_start.rs
@@ -5,10 +5,7 @@ pub fn compute() {
     let node0 = taffy
         .new_leaf(taffy::style::Style {
             position: taffy::style::Position::Absolute,
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_column: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
             inset: taffy::geometry::Rect {
                 left: taffy::style::LengthPercentageAuto::Points(4f32),
                 right: taffy::style::LengthPercentageAuto::Points(3f32),
diff --git a/benches/generated/grid_absolute_row_end.rs b/benches/generated/grid_absolute_row_end.rs
index 571cebfb5..2526a81fa 100644
--- a/benches/generated/grid_absolute_row_end.rs
+++ b/benches/generated/grid_absolute_row_end.rs
@@ -5,10 +5,7 @@ pub fn compute() {
     let node0 = taffy
         .new_leaf(taffy::style::Style {
             position: taffy::style::Position::Absolute,
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Auto,
-                end: taffy::style::GridPlacement::Line(1i16),
-            },
+            grid_row: taffy::geometry::Line { start: taffy::style::GridPlacement::Auto, end: line(1i16) },
             inset: taffy::geometry::Rect {
                 left: taffy::style::LengthPercentageAuto::Points(4f32),
                 right: taffy::style::LengthPercentageAuto::Points(3f32),
diff --git a/benches/generated/grid_absolute_row_start.rs b/benches/generated/grid_absolute_row_start.rs
index c3940bbef..66975aef0 100644
--- a/benches/generated/grid_absolute_row_start.rs
+++ b/benches/generated/grid_absolute_row_start.rs
@@ -5,10 +5,7 @@ pub fn compute() {
     let node0 = taffy
         .new_leaf(taffy::style::Style {
             position: taffy::style::Position::Absolute,
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
             inset: taffy::geometry::Rect {
                 left: taffy::style::LengthPercentageAuto::Points(4f32),
                 right: taffy::style::LengthPercentageAuto::Points(3f32),
diff --git a/benches/generated/grid_align_items_sized_center.rs b/benches/generated/grid_align_items_sized_center.rs
index 67f4c5d78..d7cb9e00f 100644
--- a/benches/generated/grid_align_items_sized_center.rs
+++ b/benches/generated/grid_align_items_sized_center.rs
@@ -4,14 +4,8 @@ pub fn compute() {
     let mut taffy = taffy::Taffy::new();
     let node0 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(20f32),
                 height: taffy::style::Dimension::Points(20f32),
@@ -21,14 +15,8 @@ pub fn compute() {
         .unwrap();
     let node1 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(60f32),
                 height: taffy::style::Dimension::Points(60f32),
diff --git a/benches/generated/grid_align_items_sized_end.rs b/benches/generated/grid_align_items_sized_end.rs
index 6ec0a4abc..200df8105 100644
--- a/benches/generated/grid_align_items_sized_end.rs
+++ b/benches/generated/grid_align_items_sized_end.rs
@@ -4,14 +4,8 @@ pub fn compute() {
     let mut taffy = taffy::Taffy::new();
     let node0 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(20f32),
                 height: taffy::style::Dimension::Points(20f32),
@@ -21,14 +15,8 @@ pub fn compute() {
         .unwrap();
     let node1 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(60f32),
                 height: taffy::style::Dimension::Points(60f32),
diff --git a/benches/generated/grid_align_items_sized_start.rs b/benches/generated/grid_align_items_sized_start.rs
index 16d9f0588..b1882f104 100644
--- a/benches/generated/grid_align_items_sized_start.rs
+++ b/benches/generated/grid_align_items_sized_start.rs
@@ -4,14 +4,8 @@ pub fn compute() {
     let mut taffy = taffy::Taffy::new();
     let node0 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(20f32),
                 height: taffy::style::Dimension::Points(20f32),
@@ -21,14 +15,8 @@ pub fn compute() {
         .unwrap();
     let node1 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(60f32),
                 height: taffy::style::Dimension::Points(60f32),
diff --git a/benches/generated/grid_align_items_sized_stretch.rs b/benches/generated/grid_align_items_sized_stretch.rs
index 349562e6a..96512291c 100644
--- a/benches/generated/grid_align_items_sized_stretch.rs
+++ b/benches/generated/grid_align_items_sized_stretch.rs
@@ -4,14 +4,8 @@ pub fn compute() {
     let mut taffy = taffy::Taffy::new();
     let node0 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(20f32),
                 height: taffy::style::Dimension::Points(20f32),
@@ -21,14 +15,8 @@ pub fn compute() {
         .unwrap();
     let node1 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(60f32),
                 height: taffy::style::Dimension::Points(60f32),
diff --git a/benches/generated/grid_justify_items_sized_center.rs b/benches/generated/grid_justify_items_sized_center.rs
index 1fa28e5f6..c02604a7b 100644
--- a/benches/generated/grid_justify_items_sized_center.rs
+++ b/benches/generated/grid_justify_items_sized_center.rs
@@ -4,14 +4,8 @@ pub fn compute() {
     let mut taffy = taffy::Taffy::new();
     let node0 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(20f32),
                 height: taffy::style::Dimension::Points(20f32),
@@ -21,14 +15,8 @@ pub fn compute() {
         .unwrap();
     let node1 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(60f32),
                 height: taffy::style::Dimension::Points(60f32),
diff --git a/benches/generated/grid_justify_items_sized_end.rs b/benches/generated/grid_justify_items_sized_end.rs
index 836b4ca2b..47844d4e9 100644
--- a/benches/generated/grid_justify_items_sized_end.rs
+++ b/benches/generated/grid_justify_items_sized_end.rs
@@ -4,14 +4,8 @@ pub fn compute() {
     let mut taffy = taffy::Taffy::new();
     let node0 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(20f32),
                 height: taffy::style::Dimension::Points(20f32),
@@ -21,14 +15,8 @@ pub fn compute() {
         .unwrap();
     let node1 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(60f32),
                 height: taffy::style::Dimension::Points(60f32),
diff --git a/benches/generated/grid_justify_items_sized_start.rs b/benches/generated/grid_justify_items_sized_start.rs
index 4b6ee18f5..5cd3ed913 100644
--- a/benches/generated/grid_justify_items_sized_start.rs
+++ b/benches/generated/grid_justify_items_sized_start.rs
@@ -4,14 +4,8 @@ pub fn compute() {
     let mut taffy = taffy::Taffy::new();
     let node0 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(20f32),
                 height: taffy::style::Dimension::Points(20f32),
@@ -21,14 +15,8 @@ pub fn compute() {
         .unwrap();
     let node1 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(60f32),
                 height: taffy::style::Dimension::Points(60f32),
diff --git a/benches/generated/grid_justify_items_sized_stretch.rs b/benches/generated/grid_justify_items_sized_stretch.rs
index 6a8b3feaa..c936740f7 100644
--- a/benches/generated/grid_justify_items_sized_stretch.rs
+++ b/benches/generated/grid_justify_items_sized_stretch.rs
@@ -4,14 +4,8 @@ pub fn compute() {
     let mut taffy = taffy::Taffy::new();
     let node0 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(20f32),
                 height: taffy::style::Dimension::Points(20f32),
@@ -21,14 +15,8 @@ pub fn compute() {
         .unwrap();
     let node1 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(60f32),
                 height: taffy::style::Dimension::Points(60f32),
diff --git a/benches/generated/grid_out_of_order_items.rs b/benches/generated/grid_out_of_order_items.rs
index cbb487bfc..b6bc62472 100644
--- a/benches/generated/grid_out_of_order_items.rs
+++ b/benches/generated/grid_out_of_order_items.rs
@@ -6,10 +6,7 @@ pub fn compute() {
     let node1 = taffy.new_leaf(taffy::style::Style { ..Default::default() }).unwrap();
     let node2 = taffy
         .new_leaf(taffy::style::Style {
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_column: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(35f32),
                 height: taffy::style::Dimension::Points(35f32),
@@ -21,14 +18,8 @@ pub fn compute() {
     let node4 = taffy.new_leaf(taffy::style::Style { ..Default::default() }).unwrap();
     let node5 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(20f32),
                 height: taffy::style::Dimension::Points(20f32),
@@ -39,10 +30,7 @@ pub fn compute() {
     let node6 = taffy.new_leaf(taffy::style::Style { ..Default::default() }).unwrap();
     let node7 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(10f32),
                 height: taffy::style::Dimension::Points(10f32),
diff --git a/benches/generated/grid_placement_auto_negative.rs b/benches/generated/grid_placement_auto_negative.rs
new file mode 100644
index 000000000..1898ac983
--- /dev/null
+++ b/benches/generated/grid_placement_auto_negative.rs
@@ -0,0 +1,35 @@
+pub fn compute() {
+    #[allow(unused_imports)]
+    use taffy::prelude::*;
+    let mut taffy = taffy::Taffy::new();
+    let node0 = taffy
+        .new_leaf(taffy::style::Style {
+            grid_row: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(-5i16), end: taffy::style::GridPlacement::Auto },
+            ..Default::default()
+        })
+        .unwrap();
+    let node1 = taffy.new_leaf(taffy::style::Style { ..Default::default() }).unwrap();
+    let node2 = taffy
+        .new_leaf(taffy::style::Style {
+            grid_row: taffy::geometry::Line { start: line(2i16), end: taffy::style::GridPlacement::Auto },
+            ..Default::default()
+        })
+        .unwrap();
+    let node = taffy
+        .new_with_children(
+            taffy::style::Style {
+                display: taffy::style::Display::Grid,
+                grid_template_rows: vec![points(40f32), points(40f32)],
+                grid_template_columns: vec![points(40f32), points(40f32)],
+                size: taffy::geometry::Size {
+                    width: taffy::style::Dimension::Points(120f32),
+                    height: taffy::style::Dimension::Points(120f32),
+                },
+                ..Default::default()
+            },
+            &[node0, node1, node2],
+        )
+        .unwrap();
+    taffy.compute_layout(node, taffy::geometry::Size::MAX_CONTENT).unwrap();
+}
diff --git a/benches/generated/grid_placement_definite_in_secondary_axis_with_fully_definite_negative.rs b/benches/generated/grid_placement_definite_in_secondary_axis_with_fully_definite_negative.rs
new file mode 100644
index 000000000..840997a9b
--- /dev/null
+++ b/benches/generated/grid_placement_definite_in_secondary_axis_with_fully_definite_negative.rs
@@ -0,0 +1,41 @@
+pub fn compute() {
+    #[allow(unused_imports)]
+    use taffy::prelude::*;
+    let mut taffy = taffy::Taffy::new();
+    let node0 = taffy
+        .new_leaf(taffy::style::Style {
+            grid_row: taffy::geometry::Line { start: line(2i16), end: taffy::style::GridPlacement::Auto },
+            ..Default::default()
+        })
+        .unwrap();
+    let node1 = taffy
+        .new_leaf(taffy::style::Style {
+            grid_row: taffy::geometry::Line { start: line(2i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(-4i16), end: taffy::style::GridPlacement::Auto },
+            ..Default::default()
+        })
+        .unwrap();
+    let node2 = taffy
+        .new_leaf(taffy::style::Style {
+            grid_row: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
+            ..Default::default()
+        })
+        .unwrap();
+    let node3 = taffy.new_leaf(taffy::style::Style { ..Default::default() }).unwrap();
+    let node = taffy
+        .new_with_children(
+            taffy::style::Style {
+                display: taffy::style::Display::Grid,
+                grid_template_rows: vec![points(40f32), points(40f32)],
+                grid_template_columns: vec![points(40f32), points(40f32)],
+                size: taffy::geometry::Size {
+                    width: taffy::style::Dimension::Points(120f32),
+                    height: taffy::style::Dimension::Points(120f32),
+                },
+                ..Default::default()
+            },
+            &[node0, node1, node2, node3],
+        )
+        .unwrap();
+    taffy.compute_layout(node, taffy::geometry::Size::MAX_CONTENT).unwrap();
+}
diff --git a/benches/generated/mod.rs b/benches/generated/mod.rs
index ea7234c7d..073f120a7 100644
--- a/benches/generated/mod.rs
+++ b/benches/generated/mod.rs
@@ -413,6 +413,10 @@ mod grid_percent_width_and_margin;
 #[cfg(feature = "grid")]
 mod grid_percent_width_and_padding;
 #[cfg(feature = "grid")]
+mod grid_placement_auto_negative;
+#[cfg(feature = "grid")]
+mod grid_placement_definite_in_secondary_axis_with_fully_definite_negative;
+#[cfg(feature = "grid")]
 mod grid_relayout_vertical_text;
 #[cfg(feature = "grid")]
 mod grid_size_child_fixed_tracks;
@@ -923,6 +927,10 @@ fn benchmark(c: &mut Criterion) {
             #[cfg(feature = "grid")]
             grid_percent_width_and_padding::compute();
             #[cfg(feature = "grid")]
+            grid_placement_auto_negative::compute();
+            #[cfg(feature = "grid")]
+            grid_placement_definite_in_secondary_axis_with_fully_definite_negative::compute();
+            #[cfg(feature = "grid")]
             grid_relayout_vertical_text::compute();
             #[cfg(feature = "grid")]
             grid_size_child_fixed_tracks::compute();
diff --git a/scripts/gentest/src/main.rs b/scripts/gentest/src/main.rs
index 83c847f1f..7d43e7b06 100644
--- a/scripts/gentest/src/main.rs
+++ b/scripts/gentest/src/main.rs
@@ -714,7 +714,7 @@ fn generate_grid_position(grid_position: &serde_json::Map<String, Value>) -> Tok
             }
             "line" => {
                 let value = value() as i16;
-                quote!(taffy::style::GridPlacement::Line(#value))
+                quote!(line(#value))
             }
             _ => unreachable!(),
         },
diff --git a/src/compute/grid/implicit_grid.rs b/src/compute/grid/implicit_grid.rs
index 2319b0caf..25b0ea350 100644
--- a/src/compute/grid/implicit_grid.rs
+++ b/src/compute/grid/implicit_grid.rs
@@ -1,11 +1,11 @@
 //! This module is not required for spec compliance, but is used as a performance optimisation
 //! to reduce the number of allocations required when creating a grid.
 use crate::geometry::Line;
-use crate::style::{GridPlacement, Style};
+use crate::style::{GenericGridPlacement, GridPlacement, Style};
 use core::cmp::{max, min};
 
 use super::types::TrackCounts;
-use super::util::css_grid_line_into_origin_zero_coords;
+use super::OriginZeroLine;
 
 /// Estimate the number of rows and columns in the grid
 /// This is used as a performance optimisation to pre-size vectors and reduce allocations. It also forms a necessary step
@@ -28,12 +28,12 @@ pub(crate) fn compute_grid_size_estimate<'a>(
     // Compute *track* count estimates for each axis from:
     //   - The explicit track counts
     //   - The origin-zero coordinate min and max grid line variables
-    let negative_implicit_inline_tracks = col_min.unsigned_abs();
+    let negative_implicit_inline_tracks = col_min.implied_negative_implicit_tracks();
     let explicit_inline_tracks = explicit_col_count;
-    let mut positive_implicit_inline_tracks = max(explicit_col_count, col_max.unsigned_abs()) - explicit_col_count;
-    let negative_implicit_block_tracks = row_min.unsigned_abs();
+    let mut positive_implicit_inline_tracks = col_max.implied_positive_implicit_tracks(explicit_col_count);
+    let negative_implicit_block_tracks = row_min.implied_negative_implicit_tracks();
     let explicit_block_tracks = explicit_row_count;
-    let mut positive_implicit_block_tracks = max(explicit_row_count, row_max.unsigned_abs()) - explicit_row_count;
+    let mut positive_implicit_block_tracks = row_max.implied_positive_implicit_tracks(explicit_row_count);
 
     // In each axis, adjust positive track estimate if any items have a span that does not fit within
     // the total number of tracks in the estimate
@@ -64,8 +64,9 @@ fn get_known_child_positions<'a>(
     children_iter: impl Iterator<Item = &'a Style>,
     explicit_col_count: u16,
     explicit_row_count: u16,
-) -> (i16, i16, u16, i16, i16, u16) {
-    let (mut col_min, mut col_max, mut col_max_span, mut row_min, mut row_max, mut row_max_span) = (0, 0, 0, 0, 0, 0);
+) -> (OriginZeroLine, OriginZeroLine, u16, OriginZeroLine, OriginZeroLine, u16) {
+    let (mut col_min, mut col_max, mut col_max_span) = (OriginZeroLine(0), OriginZeroLine(0), 0);
+    let (mut row_min, mut row_max, mut row_max_span) = (OriginZeroLine(0), OriginZeroLine(0), 0);
     children_iter.for_each(|child_style: &Style| {
         // Note: that the children reference the lines in between (and around) the tracks not tracks themselves,
         // and thus we must subtract 1 to get an accurate estimate of the number of tracks
@@ -89,8 +90,11 @@ fn get_known_child_positions<'a>(
 ///
 /// Values are returned in origin-zero coordinates
 #[inline]
-fn child_min_line_max_line_span(line: Line<GridPlacement>, explicit_track_count: u16) -> (i16, i16, u16) {
-    use GridPlacement::*;
+fn child_min_line_max_line_span(
+    line: Line<GridPlacement>,
+    explicit_track_count: u16,
+) -> (OriginZeroLine, OriginZeroLine, u16) {
+    use GenericGridPlacement::*;
 
     // 8.3.1. Grid Placement Conflict Handling
     // A. If the placement for a grid item contains two lines, and the start line is further end-ward than the end line, swap the two lines.
@@ -98,58 +102,55 @@ fn child_min_line_max_line_span(line: Line<GridPlacement>, explicit_track_count:
     // C. If the placement contains two spans, remove the one contributed by the end grid-placement property.
     // D. If the placement contains only a span for a named line, replace it with a span of 1.
 
-    let into_oz = |grid_line: i16| css_grid_line_into_origin_zero_coords(grid_line, explicit_track_count);
+    // Convert line into origin-zero coordinates before attempting to analyze
+    let oz_line = line.into_origin_zero(explicit_track_count);
 
-    let min = match (line.start, line.end) {
+    let min = match (oz_line.start, oz_line.end) {
         // Both tracks specified
         (Line(track1), Line(track2)) => {
-            let track1_oz = into_oz(track1);
-            let track2_oz = into_oz(track2);
             // See rules A and B above
-            if track1_oz == track2_oz {
-                track1_oz
+            if track1 == track2 {
+                track1
             } else {
-                min(track1_oz, track2_oz)
+                min(track1, track2)
             }
         }
 
         // Start track specified
-        (Line(track), Auto) => into_oz(track),
-        (Line(track), Span(_)) => into_oz(track),
+        (Line(track), Auto) => track,
+        (Line(track), Span(_)) => track,
 
         // End track specified
-        (Auto, Line(track)) => into_oz(track),
-        (Span(span), Line(track)) => into_oz(track) - (span as i16),
+        (Auto, Line(track)) => track,
+        (Span(span), Line(track)) => track - span,
 
         // Only spans or autos
-        // We ignore spans here by returning 1 which never effect the estimate as these are accounted for separately
-        (Auto | Span(_), Auto | Span(_)) => 1,
+        // We ignore spans here by returning 0 which never effect the estimate as these are accounted for separately
+        (Auto | Span(_), Auto | Span(_)) => OriginZeroLine(0),
     };
 
-    let max = match (line.start, line.end) {
+    let max = match (oz_line.start, oz_line.end) {
         // Both tracks specified
         (Line(track1), Line(track2)) => {
-            let track1_oz = into_oz(track1);
-            let track2_oz = into_oz(track2);
             // See rules A and B above
-            if track1_oz == track2_oz {
-                track1_oz + 1
+            if track1 == track2 {
+                track1 + 1
             } else {
-                max(track1_oz, track2_oz)
+                max(track1, track2)
             }
         }
 
         // Start track specified
-        (Line(track), Auto) => into_oz(track) + 1,
-        (Line(track), Span(span)) => into_oz(track) + (span as i16),
+        (Line(track), Auto) => track + 1,
+        (Line(track), Span(span)) => track + span,
 
         // End track specified
-        (Auto, Line(track)) => into_oz(track),
-        (Span(_), Line(track)) => into_oz(track),
+        (Auto, Line(track)) => track,
+        (Span(_), Line(track)) => track,
 
         // Only spans or autos
-        // We ignore spans here by returning 1 which never effect the estimate as these are accounted for separately
-        (Auto | Span(_), Auto | Span(_)) => 1,
+        // We ignore spans here by returning 0 which never effect the estimate as these are accounted for separately
+        (Auto | Span(_), Auto | Span(_)) => OriginZeroLine(0),
     };
 
     // Calculate span only for indefinitely placed items as we don't need for other items (whose required space will
@@ -167,22 +168,23 @@ fn child_min_line_max_line_span(line: Line<GridPlacement>, explicit_track_count:
 mod tests {
     mod test_child_min_max_line {
         use super::super::child_min_line_max_line_span;
+        use super::super::OriginZeroLine;
         use crate::geometry::Line;
-        use crate::style::GridPlacement::*;
+        use crate::style_helpers::*;
 
         #[test]
         fn child_min_max_line_auto() {
-            let (min_col, max_col, span) = child_min_line_max_line_span(Line { start: Line(5), end: Span(6) }, 6);
-            assert_eq!(min_col, 4);
-            assert_eq!(max_col, 10);
+            let (min_col, max_col, span) = child_min_line_max_line_span(Line { start: line(5), end: span(6) }, 6);
+            assert_eq!(min_col, OriginZeroLine(4));
+            assert_eq!(max_col, OriginZeroLine(10));
             assert_eq!(span, 1);
         }
 
         #[test]
         fn child_min_max_line_negative_track() {
-            let (min_col, max_col, span) = child_min_line_max_line_span(Line { start: Line(-5), end: Span(3) }, 6);
-            assert_eq!(min_col, 2);
-            assert_eq!(max_col, 5);
+            let (min_col, max_col, span) = child_min_line_max_line_span(Line { start: line(-5), end: span(3) }, 6);
+            assert_eq!(min_col, OriginZeroLine(2));
+            assert_eq!(max_col, OriginZeroLine(5));
             assert_eq!(span, 1);
         }
     }
@@ -190,15 +192,15 @@ mod tests {
     mod test_intial_grid_sizing {
         use super::super::compute_grid_size_estimate;
         use crate::compute::grid::util::test_helpers::*;
-        use crate::style::GridPlacement::*;
+        use crate::style_helpers::*;
 
         #[test]
         fn explicit_grid_sizing_with_children() {
             let explicit_col_count = 6;
             let explicit_row_count = 8;
             let child_styles = vec![
-                (Line(1), Span(2), Line(2), Auto).into_grid_child(),
-                (Line(-4), Auto, Line(-2), Auto).into_grid_child(),
+                (line(1), span(2), line(2), auto()).into_grid_child(),
+                (line(-4), auto(), line(-2), auto()).into_grid_child(),
             ];
             let (inline, block) =
                 compute_grid_size_estimate(explicit_col_count, explicit_row_count, child_styles.iter());
@@ -215,8 +217,8 @@ mod tests {
             let explicit_col_count = 4;
             let explicit_row_count = 4;
             let child_styles = vec![
-                (Line(-6), Span(2), Line(-8), Auto).into_grid_child(),
-                (Line(4), Auto, Line(3), Auto).into_grid_child(),
+                (line(-6), span(2), line(-8), auto()).into_grid_child(),
+                (line(4), auto(), line(3), auto()).into_grid_child(),
             ];
             let (inline, block) =
                 compute_grid_size_estimate(explicit_col_count, explicit_row_count, child_styles.iter());
diff --git a/src/compute/grid/mod.rs b/src/compute/grid/mod.rs
index 52c1aaa83..eee1dc31d 100644
--- a/src/compute/grid/mod.rs
+++ b/src/compute/grid/mod.rs
@@ -16,7 +16,8 @@ use implicit_grid::compute_grid_size_estimate;
 use placement::place_grid_items;
 use track_sizing::{determine_if_item_crosses_flexible_tracks, resolve_item_track_indexes, track_sizing_algorithm};
 use types::{CellOccupancyMatrix, GridTrack};
-use util::coordinates::css_grid_line_into_origin_zero_coords;
+
+pub(crate) use types::{GridCoordinate, GridLine, OriginZeroLine};
 
 #[cfg(feature = "debug")]
 use crate::debug::NODE_LOGGER;
@@ -285,22 +286,22 @@ pub fn compute(
         if child_style.position == Position::Absolute {
             // Convert grid-col-{start/end} into Option's of indexes into the columns vector
             // The Option is None if the style property is Auto and an unresolvable Span
-            let maybe_grid_cols = child_style.grid_column.resolve_absolutely_positioned_grid_tracks();
-            let maybe_col_indexes = maybe_grid_cols.map(|maybe_grid_line| {
-                maybe_grid_line.map(|grid_line| {
-                    let oz_line = css_grid_line_into_origin_zero_coords(grid_line, final_col_counts.explicit);
-                    final_col_counts.oz_line_to_grid_track_vec_index(oz_line) as usize
-                })
-            });
+            let maybe_col_indexes = child_style
+                .grid_column
+                .into_origin_zero(final_col_counts.explicit)
+                .resolve_absolutely_positioned_grid_tracks()
+                .map(|maybe_grid_line| {
+                    maybe_grid_line.map(|line: OriginZeroLine| line.into_track_vec_index(final_col_counts))
+                });
             // Convert grid-row-{start/end} into Option's of indexes into the row vector
             // The Option is None if the style property is Auto and an unresolvable Span
-            let maybe_grid_rows = child_style.grid_row.resolve_absolutely_positioned_grid_tracks();
-            let maybe_row_indexes = maybe_grid_rows.map(|maybe_grid_line| {
-                maybe_grid_line.map(|grid_line| {
-                    let oz_line = css_grid_line_into_origin_zero_coords(grid_line, final_row_counts.explicit);
-                    final_row_counts.oz_line_to_grid_track_vec_index(oz_line) as usize
-                })
-            });
+            let maybe_row_indexes = child_style
+                .grid_row
+                .into_origin_zero(final_col_counts.explicit)
+                .resolve_absolutely_positioned_grid_tracks()
+                .map(|maybe_grid_line| {
+                    maybe_grid_line.map(|line: OriginZeroLine| line.into_track_vec_index(final_row_counts))
+                });
 
             let grid_area = Rect {
                 top: maybe_row_indexes.start.map(|index| rows[index].offset).unwrap_or(0.0),
diff --git a/src/compute/grid/placement.rs b/src/compute/grid/placement.rs
index 0c8218848..a05deb136 100644
--- a/src/compute/grid/placement.rs
+++ b/src/compute/grid/placement.rs
@@ -1,11 +1,11 @@
 //! Implements placing items in the grid and resolving the implicit grid.
 //! https://www.w3.org/TR/css-grid-1/#placement
 use super::types::{CellOccupancyMatrix, CellOccupancyState, GridItem};
-use super::util::css_grid_line_into_origin_zero_coords;
+use super::OriginZeroLine;
 use crate::axis::{AbsoluteAxis, InBothAbsAxis};
 use crate::geometry::Line;
 use crate::node::Node;
-use crate::style::{GridAutoFlow, GridPlacement, Style};
+use crate::style::{GridAutoFlow, OriginZeroGridPlacement, Style};
 use crate::sys::Vec;
 
 /// 8.5. Grid Item Placement Algorithm
@@ -28,22 +28,23 @@ pub(super) fn place_grid_items<'a, ChildIter>(
         let explicit_row_count = cell_occupancy_matrix.track_counts(AbsoluteAxis::Vertical).explicit;
         move |(index, node, style): (usize, Node, &'a Style)| -> (_, _, _, &'a Style) {
             let origin_zero_placement = InBothAbsAxis {
-                horizontal: style.grid_column.map(|placement| {
-                    placement.map_track(|track| css_grid_line_into_origin_zero_coords(track, explicit_col_count))
-                }),
-                vertical: style.grid_row.map(|placement| {
-                    placement.map_track(|track| css_grid_line_into_origin_zero_coords(track, explicit_row_count))
-                }),
+                horizontal: style.grid_column.map(|placement| placement.into_origin_zero_placement(explicit_col_count)),
+                vertical: style.grid_row.map(|placement| placement.into_origin_zero_placement(explicit_row_count)),
             };
             (index, node, origin_zero_placement, style)
         }
     };
 
     // 1. Place children with definite positions
+    let mut idx = 0;
     children_iter()
         .filter(|(_, _, child_style)| child_style.grid_row.is_definite() && child_style.grid_column.is_definite())
         .map(map_child_style_to_origin_zero_placement)
         .for_each(|(index, child_node, child_placement, style)| {
+            idx += 1;
+            #[cfg(test)]
+            println!("Definite Item {idx}\n==============");
+
             let (row_span, col_span) = place_definite_grid_item(child_placement, primary_axis);
             record_grid_placement(
                 cell_occupancy_matrix,
@@ -59,6 +60,7 @@ pub(super) fn place_grid_items<'a, ChildIter>(
         });
 
     // 2. Place remaining children with definite secondary axis positions
+    let mut idx = 0;
     children_iter()
         .filter(|(_, _, child_style)| {
             child_style.grid_placement(secondary_axis).is_definite()
@@ -66,6 +68,10 @@ pub(super) fn place_grid_items<'a, ChildIter>(
         })
         .map(map_child_style_to_origin_zero_placement)
         .for_each(|(index, child_node, child_placement, style)| {
+            idx += 1;
+            #[cfg(test)]
+            println!("Definite Secondary Item {idx}\n==============");
+
             let (primary_span, secondary_span) =
                 place_definite_secondary_axis_item(&*cell_occupancy_matrix, child_placement, grid_auto_flow);
 
@@ -101,7 +107,10 @@ pub(super) fn place_grid_items<'a, ChildIter>(
 
     // 4. Position the remaining grid items
     // (which either have definite position only in the secondary axis or indefinite positions in both axis)
-    let mut grid_position: (u16, u16) = (0, 0);
+    let x_neg_tracks = cell_occupancy_matrix.track_counts(AbsoluteAxis::Horizontal).negative_implicit as i16;
+    let y_neg_tracks = cell_occupancy_matrix.track_counts(AbsoluteAxis::Vertical).negative_implicit as i16;
+    let grid_start_position = (OriginZeroLine(-x_neg_tracks), OriginZeroLine(-y_neg_tracks));
+    let mut grid_position = grid_start_position;
     let mut idx = 0;
     children_iter()
         .filter(|(_, _, child_style)| !child_style.grid_placement(secondary_axis).is_definite())
@@ -109,7 +118,7 @@ pub(super) fn place_grid_items<'a, ChildIter>(
         .for_each(|(index, child_node, child_placement, style)| {
             idx += 1;
             #[cfg(test)]
-            println!("\nItem {idx}\n==============");
+            println!("\nAuto Item {idx}\n==============");
 
             // Compute placement
             let (primary_span, secondary_span) = place_indefinitely_positioned_item(
@@ -132,11 +141,11 @@ pub(super) fn place_grid_items<'a, ChildIter>(
                 CellOccupancyState::AutoPlaced,
             );
 
-            // If using the "dense" placement algorithm then reset the grid position back to (0, 0) ready for the next item
+            // If using the "dense" placement algorithm then reset the grid position back to grid_start_position ready for the next item
             // Otherwise set it to the position of the current item so that the next item it placed after it.
             grid_position = match grid_auto_flow.is_dense() {
-                true => (0, 0),
-                false => (primary_span.end as u16, secondary_span.start as u16),
+                true => grid_start_position,
+                false => (primary_span.end, secondary_span.start),
             }
         });
 }
@@ -144,12 +153,12 @@ pub(super) fn place_grid_items<'a, ChildIter>(
 /// 8.5. Grid Item Placement Algorithm
 /// Place a single definitely placed item into the grid
 fn place_definite_grid_item(
-    placement: InBothAbsAxis<Line<GridPlacement>>,
+    placement: InBothAbsAxis<Line<OriginZeroGridPlacement>>,
     primary_axis: AbsoluteAxis,
-) -> (Line<i16>, Line<i16>) {
+) -> (Line<OriginZeroLine>, Line<OriginZeroLine>) {
     // Resolve spans to tracks
-    let primary_span = placement.get(primary_axis).resolve_definite_grid_tracks();
-    let secondary_span = placement.get(primary_axis.other_axis()).resolve_definite_grid_tracks();
+    let primary_span = placement.get(primary_axis).resolve_definite_grid_lines();
+    let secondary_span = placement.get(primary_axis.other_axis()).resolve_definite_grid_lines();
 
     (primary_span, secondary_span)
 }
@@ -158,21 +167,22 @@ fn place_definite_grid_item(
 /// Step 2. Place remaining children with definite secondary axis positions
 fn place_definite_secondary_axis_item(
     cell_occupancy_matrix: &CellOccupancyMatrix,
-    placement: InBothAbsAxis<Line<GridPlacement>>,
+    placement: InBothAbsAxis<Line<OriginZeroGridPlacement>>,
     auto_flow: GridAutoFlow,
-) -> (Line<i16>, Line<i16>) {
+) -> (Line<OriginZeroLine>, Line<OriginZeroLine>) {
     let primary_axis = auto_flow.primary_axis();
     let secondary_axis = primary_axis.other_axis();
 
-    let secondary_axis_placement = placement.get(secondary_axis).resolve_definite_grid_tracks();
+    let secondary_axis_placement = placement.get(secondary_axis).resolve_definite_grid_lines();
+    let primary_axis_grid_start_line = cell_occupancy_matrix.track_counts(primary_axis).implicit_start_line();
     let starting_position = match auto_flow.is_dense() {
-        true => 0,
+        true => primary_axis_grid_start_line,
         false => cell_occupancy_matrix
             .last_of_type(primary_axis, secondary_axis_placement.start, CellOccupancyState::AutoPlaced)
-            .unwrap_or(0),
+            .unwrap_or(primary_axis_grid_start_line),
     };
 
-    let mut position: i16 = starting_position;
+    let mut position: OriginZeroLine = starting_position;
     loop {
         let primary_axis_placement = placement.get(primary_axis).resolve_indefinite_grid_tracks(position);
 
@@ -194,10 +204,10 @@ fn place_definite_secondary_axis_item(
 /// Step 4. Position the remaining grid items.
 fn place_indefinitely_positioned_item(
     cell_occupancy_matrix: &CellOccupancyMatrix,
-    placement: InBothAbsAxis<Line<GridPlacement>>,
+    placement: InBothAbsAxis<Line<OriginZeroGridPlacement>>,
     auto_flow: GridAutoFlow,
-    grid_position: (u16, u16),
-) -> (Line<i16>, Line<i16>) {
+    grid_position: (OriginZeroLine, OriginZeroLine),
+) -> (Line<OriginZeroLine>, Line<OriginZeroLine>) {
     let primary_axis = auto_flow.primary_axis();
 
     let primary_placement_style = placement.get(primary_axis);
@@ -206,23 +216,24 @@ fn place_indefinitely_positioned_item(
     let primary_span = primary_placement_style.indefinite_span();
     let secondary_span = secondary_placement_style.indefinite_span();
     let has_definite_primary_axis_position = primary_placement_style.is_definite();
-    let primary_axis_length = cell_occupancy_matrix.track_counts(primary_axis).len() as i16;
+    let primary_axis_grid_start_line = cell_occupancy_matrix.track_counts(primary_axis).implicit_start_line();
+    let primary_axis_grid_end_line = cell_occupancy_matrix.track_counts(primary_axis).implicit_end_line();
+    let secondary_axis_grid_start_line =
+        cell_occupancy_matrix.track_counts(primary_axis.other_axis()).implicit_start_line();
 
-    let track_area_is_unoccupied = |primary_range, secondary_range| {
-        cell_occupancy_matrix.track_area_is_unoccupied(primary_axis, primary_range, secondary_range)
+    let line_area_is_occupied = |primary_span, secondary_span| {
+        !cell_occupancy_matrix.line_area_is_unoccupied(primary_axis, primary_span, secondary_span)
     };
-    let tracks_to_lines = |range| cell_occupancy_matrix.tracks_to_lines(primary_axis, range);
 
     let (mut primary_idx, mut secondary_idx) = grid_position;
 
     if has_definite_primary_axis_position {
-        let definite_primary_placement = primary_placement_style.resolve_definite_grid_tracks();
-        let defined_primary_idx =
-            cell_occupancy_matrix.lines_to_tracks(primary_axis.other_axis(), definite_primary_placement).start as u16;
+        let definite_primary_placement = primary_placement_style.resolve_definite_grid_lines();
+        let defined_primary_idx = definite_primary_placement.start;
 
         // Compute starting position for search
-        if defined_primary_idx < primary_idx && secondary_idx != 0 {
-            secondary_idx = 0;
+        if defined_primary_idx < primary_idx && secondary_idx != secondary_axis_grid_start_line {
+            secondary_idx = secondary_axis_grid_start_line;
             primary_idx = defined_primary_idx + 1;
         } else {
             primary_idx = defined_primary_idx;
@@ -231,43 +242,43 @@ fn place_indefinitely_positioned_item(
         // Item has fixed primary axis position: so we simply increment the secondary axis position
         // until we find a space that the item fits in
         loop {
-            let primary_range = (primary_idx as i16)..((primary_idx + primary_span) as i16);
-            let secondary_range = (secondary_idx as i16)..((secondary_idx + secondary_span) as i16);
-
-            let place_here = track_area_is_unoccupied(primary_range.clone(), secondary_range.clone());
-            if place_here {
-                let primary_span = tracks_to_lines(primary_range);
-                let secondary_span = tracks_to_lines(secondary_range);
-                return (primary_span, secondary_span);
+            let primary_span = Line { start: primary_idx, end: primary_idx + primary_span };
+            let secondary_span = Line { start: secondary_idx, end: secondary_idx + secondary_span };
+
+            // If area is occupied, increment the index and try again
+            if line_area_is_occupied(primary_span, secondary_span) {
+                secondary_idx += 1;
+                continue;
             }
 
-            secondary_idx += 1;
+            // Once we find a free space, return that position
+            return (primary_span, secondary_span);
         }
     } else {
         // Item does not have any fixed axis, so we search along the primary axis until we hit the end of the already
         // existent tracks, and then we reset the primary axis back to zero and increment the secondary axis index.
         // We continue in this vein until we find a space that the item fits in.
         loop {
-            let primary_range = (primary_idx as i16)..((primary_idx + primary_span) as i16);
-            let secondary_range = (secondary_idx as i16)..((secondary_idx + secondary_span) as i16);
-
-            // Check if item fits in it's current position, and if so place it there
-            let primary_out_of_bounds = primary_range.end > primary_axis_length;
-            let place_here =
-                !primary_out_of_bounds && track_area_is_unoccupied(primary_range.clone(), secondary_range.clone());
-            if place_here {
-                let primary_span = tracks_to_lines(primary_range);
-                let secondary_span = tracks_to_lines(secondary_range);
-                return (primary_span, secondary_span);
-            }
+            let primary_span = Line { start: primary_idx, end: primary_idx + primary_span };
+            let secondary_span = Line { start: secondary_idx, end: secondary_idx + secondary_span };
 
-            // Else check the next position
+            // If the primary index is out of bounds, then increment the secondary index and reset the primary
+            // index back to the start of the grid
+            let primary_out_of_bounds = primary_span.end > primary_axis_grid_end_line;
             if primary_out_of_bounds {
                 secondary_idx += 1;
-                primary_idx = 0;
-            } else {
+                primary_idx = primary_axis_grid_start_line;
+                continue;
+            }
+
+            // If area is occupied, increment the primary index and try again
+            if line_area_is_occupied(primary_span, secondary_span) {
                 primary_idx += 1;
+                continue;
             }
+
+            // Once we find a free space that's in bounds, return that position
+            return (primary_span, secondary_span);
         }
     }
 }
@@ -282,8 +293,8 @@ fn record_grid_placement(
     index: usize,
     style: &Style,
     primary_axis: AbsoluteAxis,
-    primary_span: Line<i16>,
-    secondary_span: Line<i16>,
+    primary_span: Line<OriginZeroLine>,
+    secondary_span: Line<OriginZeroLine>,
     placement_type: CellOccupancyState,
 ) {
     #[cfg(test)]
@@ -321,7 +332,7 @@ mod tests {
         use crate::compute::grid::util::*;
         use crate::compute::grid::CellOccupancyMatrix;
         use crate::prelude::*;
-        use crate::style::{GridAutoFlow, GridPlacement::*};
+        use crate::style::GridAutoFlow;
         use slotmap::SlotMap;
 
         use super::super::place_grid_items;
@@ -355,7 +366,7 @@ mod tests {
             {
                 assert_eq!(item.node, *node);
                 let actual_placement = (item.column.start, item.column.end, item.row.start, item.row.end);
-                assert_eq!(actual_placement, *expected_placement, "Item {idx} (0-indexed)");
+                assert_eq!(actual_placement, (*expected_placement).into_oz(), "Item {idx} (0-indexed)");
             }
 
             // Assert that the correct number of implicit rows have been generated
@@ -374,10 +385,10 @@ mod tests {
                 let mut sm = SlotMap::new();
                 vec![
                     // node, style (grid coords), expected_placement (oz coords)
-                    (1, sm.insert(()), (Line(1), Auto, Line(1), Auto).into_grid_child(), (0, 1, 0, 1)),
-                    (2, sm.insert(()), (Line(-4), Auto, Line(-3), Auto).into_grid_child(), (-1, 0, 0, 1)),
-                    (3, sm.insert(()), (Line(-3), Auto, Line(-4), Auto).into_grid_child(), (0, 1, -1, 0)),
-                    (4, sm.insert(()), (Line(3), Span(2), Line(5), Auto).into_grid_child(), (2, 4, 4, 5)),
+                    (1, sm.insert(()), (line(1), auto(), line(1), auto()).into_grid_child(), (0, 1, 0, 1)),
+                    (2, sm.insert(()), (line(-4), auto(), line(-3), auto()).into_grid_child(), (-1, 0, 0, 1)),
+                    (3, sm.insert(()), (line(-3), auto(), line(-4), auto()).into_grid_child(), (0, 1, -1, 0)),
+                    (4, sm.insert(()), (line(3), span(2), line(5), auto()).into_grid_child(), (2, 4, 4, 5)),
                 ]
             };
             let expected_cols = TrackCounts { negative_implicit: 1, explicit: 2, positive_implicit: 2 };
@@ -385,6 +396,26 @@ mod tests {
             placement_test_runner(explicit_col_count, explicit_row_count, children, expected_cols, expected_rows, flow);
         }
 
+        #[test]
+        fn test_placement_spanning_origin() {
+            let flow = GridAutoFlow::Row;
+            let explicit_col_count = 2;
+            let explicit_row_count = 2;
+            let children = {
+                let mut sm = SlotMap::new();
+                vec![
+                    // node, style (grid coords), expected_placement (oz coords)
+                    (1, sm.insert(()), (line(-1), line(-1), line(-1), line(-1)).into_grid_child(), (2, 3, 2, 3)),
+                    (2, sm.insert(()), (line(-1), span(2), line(-1), span(2)).into_grid_child(), (2, 4, 2, 4)),
+                    (3, sm.insert(()), (line(-4), line(-4), line(-4), line(-4)).into_grid_child(), (-1, 0, -1, 0)),
+                    (4, sm.insert(()), (line(-4), span(2), line(-4), span(2)).into_grid_child(), (-1, 1, -1, 1)),
+                ]
+            };
+            let expected_cols = TrackCounts { negative_implicit: 1, explicit: 2, positive_implicit: 2 };
+            let expected_rows = TrackCounts { negative_implicit: 1, explicit: 2, positive_implicit: 2 };
+            placement_test_runner(explicit_col_count, explicit_row_count, children, expected_cols, expected_rows, flow);
+        }
+
         #[test]
         fn test_only_auto_placement_row_flow() {
             let flow = GridAutoFlow::Row;
@@ -392,7 +423,7 @@ mod tests {
             let explicit_row_count = 2;
             let children = {
                 let mut sm = SlotMap::new();
-                let auto_child = (Auto, Auto, Auto, Auto).into_grid_child();
+                let auto_child = (auto(), auto(), auto(), auto()).into_grid_child();
                 vec![
                     // output order, node, style (grid coords), expected_placement (oz coords)
                     (1, sm.insert(()), auto_child.clone(), (0, 1, 0, 1)),
@@ -417,7 +448,7 @@ mod tests {
             let explicit_row_count = 2;
             let children = {
                 let mut sm = SlotMap::new();
-                let auto_child = (Auto, Auto, Auto, Auto).into_grid_child();
+                let auto_child = (auto(), auto(), auto(), auto()).into_grid_child();
                 vec![
                     // output order, node, style (grid coords), expected_placement (oz coords)
                     (1, sm.insert(()), auto_child.clone(), (0, 1, 0, 1)),
@@ -444,7 +475,7 @@ mod tests {
                 let mut sm = SlotMap::new();
                 vec![
                     // output order, node, style (grid coords), expected_placement (oz coords)
-                    (1, sm.insert(()), (Span(5), Auto, Auto, Auto).into_grid_child(), (0, 5, 0, 1)),
+                    (1, sm.insert(()), (span(5), auto(), auto(), auto()).into_grid_child(), (0, 5, 0, 1)),
                 ]
             };
             let expected_cols = TrackCounts { negative_implicit: 0, explicit: 2, positive_implicit: 3 };
@@ -461,10 +492,10 @@ mod tests {
                 let mut sm = SlotMap::new();
                 vec![
                     // output order, node, style (grid coords), expected_placement (oz coords)
-                    (1, sm.insert(()), (Span(2), Auto, Line(1), Auto).into_grid_child(), (0, 2, 0, 1)),
-                    (2, sm.insert(()), (Auto, Auto, Line(2), Auto).into_grid_child(), (0, 1, 1, 2)),
-                    (3, sm.insert(()), (Auto, Auto, Line(1), Auto).into_grid_child(), (2, 3, 0, 1)),
-                    (4, sm.insert(()), (Auto, Auto, Line(4), Auto).into_grid_child(), (0, 1, 3, 4)),
+                    (1, sm.insert(()), (span(2), auto(), line(1), auto()).into_grid_child(), (0, 2, 0, 1)),
+                    (2, sm.insert(()), (auto(), auto(), line(2), auto()).into_grid_child(), (0, 1, 1, 2)),
+                    (3, sm.insert(()), (auto(), auto(), line(1), auto()).into_grid_child(), (2, 3, 0, 1)),
+                    (4, sm.insert(()), (auto(), auto(), line(4), auto()).into_grid_child(), (0, 1, 3, 4)),
                 ]
             };
             let expected_cols = TrackCounts { negative_implicit: 0, explicit: 2, positive_implicit: 1 };
@@ -481,9 +512,9 @@ mod tests {
                 let mut sm = SlotMap::new();
                 vec![
                     // output order, node, style (grid coords), expected_placement (oz coords)
-                    (2, sm.insert(()), (Auto, Auto, Line(2), Auto).into_grid_child(), (0, 1, 1, 2)),
-                    (1, sm.insert(()), (Line(-4), Auto, Line(2), Auto).into_grid_child(), (-1, 0, 1, 2)),
-                    (3, sm.insert(()), (Auto, Auto, Line(1), Auto).into_grid_child(), (0, 1, 0, 1)),
+                    (2, sm.insert(()), (auto(), auto(), line(2), auto()).into_grid_child(), (0, 1, 1, 2)),
+                    (1, sm.insert(()), (line(-4), auto(), line(2), auto()).into_grid_child(), (-1, 0, 1, 2)),
+                    (3, sm.insert(()), (auto(), auto(), line(1), auto()).into_grid_child(), (-1, 0, 0, 1)),
                 ]
             };
             let expected_cols = TrackCounts { negative_implicit: 1, explicit: 2, positive_implicit: 0 };
@@ -500,9 +531,9 @@ mod tests {
                 let mut sm = SlotMap::new();
                 vec![
                     // output order, node, style (grid coords), expected_placement (oz coords)
-                    (1, sm.insert(()), (Line(2), Auto, Line(1), Auto).into_grid_child(), (1, 2, 0, 1)), // Definitely positioned in column 2
-                    (2, sm.insert(()), (Span(2), Auto, Auto, Auto).into_grid_child(), (2, 4, 0, 1)), // Spans 2 columns, so positioned after item 1
-                    (3, sm.insert(()), (Auto, Auto, Auto, Auto).into_grid_child(), (0, 1, 0, 1)), // Spans 1 column, so should be positioned before item 1
+                    (1, sm.insert(()), (line(2), auto(), line(1), auto()).into_grid_child(), (1, 2, 0, 1)), // Definitely positioned in column 2
+                    (2, sm.insert(()), (span(2), auto(), auto(), auto()).into_grid_child(), (2, 4, 0, 1)), // Spans 2 columns, so positioned after item 1
+                    (3, sm.insert(()), (auto(), auto(), auto(), auto()).into_grid_child(), (0, 1, 0, 1)), // Spans 1 column, so should be positioned before item 1
                 ]
             };
             let expected_cols = TrackCounts { negative_implicit: 0, explicit: 4, positive_implicit: 0 };
@@ -519,14 +550,33 @@ mod tests {
                 let mut sm = SlotMap::new();
                 vec![
                     // output order, node, style (grid coords), expected_placement (oz coords)
-                    (1, sm.insert(()), (Auto, Span(3), Auto, Auto).into_grid_child(), (0, 3, 0, 1)), // Width 3
-                    (2, sm.insert(()), (Auto, Span(3), Auto, Auto).into_grid_child(), (0, 3, 1, 2)), // Width 3 (wraps to next row)
-                    (3, sm.insert(()), (Auto, Span(1), Auto, Auto).into_grid_child(), (3, 4, 1, 2)), // Width 1 (uses second row as we're already on it)
+                    (1, sm.insert(()), (auto(), span(3), auto(), auto()).into_grid_child(), (0, 3, 0, 1)), // Width 3
+                    (2, sm.insert(()), (auto(), span(3), auto(), auto()).into_grid_child(), (0, 3, 1, 2)), // Width 3 (wraps to next row)
+                    (3, sm.insert(()), (auto(), span(1), auto(), auto()).into_grid_child(), (3, 4, 1, 2)), // Width 1 (uses second row as we're already on it)
                 ]
             };
             let expected_cols = TrackCounts { negative_implicit: 0, explicit: 4, positive_implicit: 0 };
             let expected_rows = TrackCounts { negative_implicit: 0, explicit: 4, positive_implicit: 0 };
             placement_test_runner(explicit_col_count, explicit_row_count, children, expected_cols, expected_rows, flow);
         }
+
+        #[test]
+        fn test_auto_placement_in_negative_tracks() {
+            let flow = GridAutoFlow::RowDense;
+            let explicit_col_count = 2;
+            let explicit_row_count = 2;
+            let children = {
+                let mut sm = SlotMap::new();
+                vec![
+                    // output order, node, style (grid coords), expected_placement (oz coords)
+                    (1, sm.insert(()), (line(-5), auto(), line(1), auto()).into_grid_child(), (-2, -1, 0, 1)), // Row 1. Definitely positioned in column -2
+                    (2, sm.insert(()), (auto(), auto(), line(2), auto()).into_grid_child(), (-2, -1, 1, 2)), // Row 2. Auto positioned in column -2
+                    (3, sm.insert(()), (auto(), auto(), auto(), auto()).into_grid_child(), (-1, 0, 0, 1)), // Row 1. Auto positioned in column -1
+                ]
+            };
+            let expected_cols = TrackCounts { negative_implicit: 2, explicit: 2, positive_implicit: 0 };
+            let expected_rows = TrackCounts { negative_implicit: 0, explicit: 2, positive_implicit: 0 };
+            placement_test_runner(explicit_col_count, explicit_row_count, children, expected_cols, expected_rows, flow);
+        }
     }
 }
diff --git a/src/compute/grid/track_sizing.rs b/src/compute/grid/track_sizing.rs
index a2ca95531..d001ae218 100644
--- a/src/compute/grid/track_sizing.rs
+++ b/src/compute/grid/track_sizing.rs
@@ -136,8 +136,8 @@ pub(super) fn compute_alignment_gutter_adjustment(
 /// Convert origin-zero coordinates track placement in grid track vector indexes
 pub(super) fn resolve_item_track_indexes(items: &mut [GridItem], column_counts: TrackCounts, row_counts: TrackCounts) {
     for item in items {
-        item.column_indexes = item.column.map(|oz_index| column_counts.oz_line_to_grid_track_vec_index(oz_index));
-        item.row_indexes = item.row.map(|oz_index| row_counts.oz_line_to_grid_track_vec_index(oz_index));
+        item.column_indexes = item.column.map(|line| line.into_track_vec_index(column_counts) as u16);
+        item.row_indexes = item.row.map(|line| line.into_track_vec_index(row_counts) as u16);
     }
 }
 
diff --git a/src/compute/grid/types/cell_occupancy.rs b/src/compute/grid/types/cell_occupancy.rs
index 8fa9450d2..35b3d5cef 100644
--- a/src/compute/grid/types/cell_occupancy.rs
+++ b/src/compute/grid/types/cell_occupancy.rs
@@ -1,6 +1,7 @@
 //! Contains CellOccupancyMatrix used to track occupied cells during grid placement
 use super::TrackCounts;
 use crate::axis::AbsoluteAxis;
+use crate::compute::grid::OriginZeroLine;
 use crate::geometry::Line;
 use crate::sys::Vec;
 use core::cmp::{max, min};
@@ -140,8 +141,8 @@ impl CellOccupancyMatrix {
     pub fn mark_area_as(
         &mut self,
         primary_axis: AbsoluteAxis,
-        primary_span: Line<i16>,
-        secondary_span: Line<i16>,
+        primary_span: Line<OriginZeroLine>,
+        secondary_span: Line<OriginZeroLine>,
         value: CellOccupancyState,
     ) {
         let (row_span, column_span) = match primary_axis {
@@ -168,25 +169,13 @@ impl CellOccupancyMatrix {
         }
     }
 
-    /// Converts a span of grid lines in OriginZero coordinates into a range a tracks suitable
-    /// for indexing into this CellOccupancyMatrix
-    pub fn lines_to_tracks(&self, axis: AbsoluteAxis, span: Line<i16>) -> Range<i16> {
-        self.track_counts(axis).oz_line_range_to_track_range(span)
-    }
-
-    /// Converts a range a tracks indexes from this CellOccupancyMatrix into a span of grid lines
-    /// in OriginZero coordinates
-    pub fn tracks_to_lines(&self, axis: AbsoluteAxis, span: Range<i16>) -> Line<i16> {
-        self.track_counts(axis).track_range_to_oz_line_range(span)
-    }
-
     /// Determines whether a grid area specified by the bounding grid lines in OriginZero coordinates
     /// is entirely unnocupied. Returns true if all grid cells within the grid area are unnocupied, else false.
     pub fn line_area_is_unoccupied(
         &self,
         primary_axis: AbsoluteAxis,
-        primary_span: Line<i16>,
-        secondary_span: Line<i16>,
+        primary_span: Line<OriginZeroLine>,
+        secondary_span: Line<OriginZeroLine>,
     ) -> bool {
         let primary_range = self.track_counts(primary_axis).oz_line_range_to_track_range(primary_span);
         let secondary_range = self.track_counts(primary_axis.other_axis()).oz_line_range_to_track_range(secondary_span);
@@ -240,9 +229,14 @@ impl CellOccupancyMatrix {
     /// Given an axis and a track index
     /// Search backwards from the end of the track and find the last grid cell matching the specified state (if any)
     /// Return the index of that cell or None.
-    pub fn last_of_type(&self, track_type: AbsoluteAxis, track_index: i16, kind: CellOccupancyState) -> Option<i16> {
+    pub fn last_of_type(
+        &self,
+        track_type: AbsoluteAxis,
+        start_at: OriginZeroLine,
+        kind: CellOccupancyState,
+    ) -> Option<OriginZeroLine> {
         let track_counts = self.track_counts(track_type.other_axis());
-        let track_computed_index = track_counts.oz_line_to_next_track(track_index);
+        let track_computed_index = track_counts.oz_line_to_next_track(start_at);
 
         let maybe_index = match track_type {
             AbsoluteAxis::Horizontal => {
diff --git a/src/compute/grid/types/coordinates.rs b/src/compute/grid/types/coordinates.rs
new file mode 100644
index 000000000..62525a8b6
--- /dev/null
+++ b/src/compute/grid/types/coordinates.rs
@@ -0,0 +1,129 @@
+//! Taffy uses two coordinate systems to refer to grid lines (the gaps/gutters between rows/columns):
+use super::super::types::TrackCounts;
+use crate::geometry::Line;
+use core::cmp::{max, Ordering};
+use core::ops::{Add, AddAssign, Sub};
+
+/// Represents a grid line position in "CSS Grid Line" coordinates
+///
+/// "CSS Grid Line" coordinates are those used in grid-row/grid-column in the CSS grid spec:
+///   - The line at left hand (or top) edge of the explicit grid is line 1
+///     (and counts up from there)
+///   - The line at the right hand (or bottom) edge of the explicit grid is -1
+///     (and counts down from there)
+///   - 0 is not a valid index
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[repr(transparent)]
+pub struct GridLine(i16);
+
+impl From<i16> for GridLine {
+    fn from(value: i16) -> Self {
+        Self(value)
+    }
+}
+
+impl GridLine {
+    /// Returns the underlying i16
+    pub fn as_i16(self) -> i16 {
+        self.0
+    }
+
+    /// Convert into OriginZero coordinates using the specified explicit track count
+    pub(crate) fn into_origin_zero_line(self, explicit_track_count: u16) -> OriginZeroLine {
+        let explicit_line_count = explicit_track_count + 1;
+        let oz_line = match self.0.cmp(&0) {
+            Ordering::Greater => self.0 - 1,
+            Ordering::Less => self.0 + explicit_line_count as i16,
+            Ordering::Equal => panic!("Grid line of zero is invalid"),
+        };
+        OriginZeroLine(oz_line)
+    }
+}
+
+/// Represents a grid line position in "OriginZero" coordinates
+///
+/// "OriginZero" coordinates are a normalized form:
+///   - The line at left hand (or top) edge of the explicit grid is line 0
+///   - The next line to the right (or down) is 1, and so on
+///   - The next line to the left (or up) is -1, and so on
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+#[repr(transparent)]
+pub struct OriginZeroLine(pub i16);
+
+// Add and Sub with Self
+impl Add<OriginZeroLine> for OriginZeroLine {
+    type Output = Self;
+    fn add(self, rhs: OriginZeroLine) -> Self::Output {
+        OriginZeroLine(self.0 + rhs.0)
+    }
+}
+impl Sub<OriginZeroLine> for OriginZeroLine {
+    type Output = Self;
+    fn sub(self, rhs: OriginZeroLine) -> Self::Output {
+        OriginZeroLine(self.0 - rhs.0)
+    }
+}
+
+// Add and Sub with u16
+impl Add<u16> for OriginZeroLine {
+    type Output = Self;
+    fn add(self, rhs: u16) -> Self::Output {
+        OriginZeroLine(self.0 + rhs as i16)
+    }
+}
+impl AddAssign<u16> for OriginZeroLine {
+    fn add_assign(&mut self, rhs: u16) {
+        self.0 += rhs as i16;
+    }
+}
+impl Sub<u16> for OriginZeroLine {
+    type Output = Self;
+    fn sub(self, rhs: u16) -> Self::Output {
+        OriginZeroLine(self.0 - rhs as i16)
+    }
+}
+
+impl OriginZeroLine {
+    /// Converts a grid line in OriginZero coordinates into the index of that same grid line in the GridTrackVec.
+    pub(crate) fn into_track_vec_index(self, track_counts: TrackCounts) -> usize {
+        assert!(
+            self.0 >= -(track_counts.negative_implicit as i16),
+            "OriginZero grid line cannot be less than the number of negative grid lines"
+        );
+        assert!(
+            self.0 <= (track_counts.explicit + track_counts.positive_implicit) as i16,
+            "OriginZero grid line cannot be more than the number of positive grid lines"
+        );
+        2 * ((self.0 + track_counts.negative_implicit as i16) as usize)
+    }
+
+    /// The minimum number of negative implicit track there must be if a grid item starts at this line.
+    pub(crate) fn implied_negative_implicit_tracks(self) -> u16 {
+        if self.0 < 0 {
+            self.0.unsigned_abs()
+        } else {
+            0
+        }
+    }
+
+    /// The minimum number of positive implicit track there must be if a grid item end at this line.
+    pub(crate) fn implied_positive_implicit_tracks(self, explicit_track_count: u16) -> u16 {
+        if self.0 > explicit_track_count as i16 {
+            self.0 as u16 - explicit_track_count
+        } else {
+            0
+        }
+    }
+}
+
+impl Line<OriginZeroLine> {
+    /// The number of tracks between the start and end lines
+    pub(crate) fn span(self) -> u16 {
+        max(self.end.0 - self.start.0, 0) as u16
+    }
+}
+
+/// A trait for the different coordinates used to define grid lines.
+pub trait GridCoordinate: Copy {}
+impl GridCoordinate for GridLine {}
+impl GridCoordinate for OriginZeroLine {}
diff --git a/src/compute/grid/types/grid_item.rs b/src/compute/grid/types/grid_item.rs
index 57fa54eb6..b38d84b15 100644
--- a/src/compute/grid/types/grid_item.rs
+++ b/src/compute/grid/types/grid_item.rs
@@ -2,6 +2,7 @@
 use super::GridTrack;
 use crate::axis::AbstractAxis;
 use crate::compute::compute_node_layout;
+use crate::compute::grid::OriginZeroLine;
 use crate::geometry::{Line, Rect, Size};
 use crate::layout::{RunMode, SizingMode};
 use crate::node::Node;
@@ -9,7 +10,6 @@ use crate::prelude::LayoutTree;
 use crate::resolve::MaybeResolve;
 use crate::style::{AvailableSpace, LengthPercentageAuto, MaxTrackSizingFunction, MinTrackSizingFunction, Style};
 use crate::style_helpers::*;
-use core::cmp::max;
 use core::ops::Range;
 
 /// Represents a single grid item
@@ -26,10 +26,10 @@ pub(in super::super) struct GridItem {
 
     /// The item's definite row-start and row-end, as resolved by the placement algorithm
     /// (in origin-zero coordinates)
-    pub row: Line<i16>,
+    pub row: Line<OriginZeroLine>,
     /// The items definite column-start and column-end, as resolved by the placement algorithm
     /// (in origin-zero coordinates)
-    pub column: Line<i16>,
+    pub column: Line<OriginZeroLine>,
 
     /// The item's margin style
     pub margin: Rect<LengthPercentageAuto>,
@@ -61,8 +61,8 @@ impl GridItem {
     /// Create a new item given a concrete placement in both axes
     pub fn new_with_placement_style_and_order(
         node: Node,
-        col_span: Line<i16>,
-        row_span: Line<i16>,
+        col_span: Line<OriginZeroLine>,
+        row_span: Line<OriginZeroLine>,
         style: &Style,
         source_order: u16,
     ) -> Self {
@@ -84,7 +84,7 @@ impl GridItem {
     }
 
     /// This item's placement in the specified axis in OriginZero coordinates
-    pub fn placement(&self, axis: AbstractAxis) -> Line<i16> {
+    pub fn placement(&self, axis: AbstractAxis) -> Line<OriginZeroLine> {
         match axis {
             AbstractAxis::Block => self.row,
             AbstractAxis::Inline => self.column,
@@ -110,8 +110,8 @@ impl GridItem {
     /// Returns the number of tracks that this item spans in the specified axis
     pub fn span(&self, axis: AbstractAxis) -> u16 {
         match axis {
-            AbstractAxis::Block => max(self.row.end - self.row.start, 0) as u16,
-            AbstractAxis::Inline => max(self.column.end - self.column.start, 0) as u16,
+            AbstractAxis::Block => self.row.span(),
+            AbstractAxis::Inline => self.column.span(),
         }
     }
 
diff --git a/src/compute/grid/types/grid_track_counts.rs b/src/compute/grid/types/grid_track_counts.rs
index efd6745ad..8ab8ae309 100644
--- a/src/compute/grid/types/grid_track_counts.rs
+++ b/src/compute/grid/types/grid_track_counts.rs
@@ -32,7 +32,7 @@
 //!       - Index 1 is the leftmost track of the implict grid. Index 3 is the second leftmost track, etc.
 //!       - Index 0 is the leftmost grid line. Index 2 is the second leftmost line, etc.
 //!
-use crate::geometry::Line;
+use crate::{compute::grid::OriginZeroLine, geometry::Line};
 use core::ops::Range;
 
 /// Stores the number of tracks in a given dimension.
@@ -57,35 +57,30 @@ impl TrackCounts {
     pub fn len(&self) -> usize {
         (self.negative_implicit + self.explicit + self.positive_implicit) as usize
     }
-}
 
-/// Conversion functions between OriginZero coordinates and GridTrackVec indexes
-impl TrackCounts {
-    /// Converts a grid line in OriginZero coordinates into the index of that same grid line in the GridTrackVec.
-    pub fn oz_line_to_grid_track_vec_index(&self, index: i16) -> u16 {
-        assert!(
-            index >= -(self.negative_implicit as i16),
-            "OriginZero grid line cannot be less than the number of negative grid lines"
-        );
-        assert!(
-            index <= (self.explicit + self.positive_implicit) as i16,
-            "OriginZero grid line cannot be more than the number of positive grid lines"
-        );
-        2 * ((index + self.negative_implicit as i16) as u16)
+    /// The OriginZeroLine representing the start of the implicit grid
+    pub fn implicit_start_line(&self) -> OriginZeroLine {
+        OriginZeroLine(-(self.negative_implicit as i16))
+    }
+
+    /// The OriginZeroLine representing the end of the implicit grid
+    pub fn implicit_end_line(&self) -> OriginZeroLine {
+        OriginZeroLine((self.explicit + self.positive_implicit) as i16)
     }
 }
 
 /// Conversion functions between OriginZero coordinates and CellOccupancyMatrix track indexes
+#[allow(dead_code)]
 impl TrackCounts {
     /// Converts a grid line in OriginZero coordinates into the track immediately
     /// following that grid line as an index into the CellOccupancyMatrix.
-    pub fn oz_line_to_next_track(&self, index: i16) -> i16 {
-        index + (self.negative_implicit as i16)
+    pub fn oz_line_to_next_track(&self, index: OriginZeroLine) -> i16 {
+        index.0 + (self.negative_implicit as i16)
     }
 
     /// Converts start and end grid lines in OriginZero coordinates into a range of tracks
     /// as indexes into the CellOccupancyMatrix
-    pub fn oz_line_range_to_track_range(&self, input: Line<i16>) -> Range<i16> {
+    pub fn oz_line_range_to_track_range(&self, input: Line<OriginZeroLine>) -> Range<i16> {
         let start = self.oz_line_to_next_track(input.start);
         let end = self.oz_line_to_next_track(input.end); // Don't subtract 1 as output range is exclusive
         start..end
@@ -93,13 +88,13 @@ impl TrackCounts {
 
     /// Converts a track as an index into the CellOccupancyMatrix into the grid line immediately
     /// preceeding that track in OriginZero coordinates.
-    pub fn track_to_prev_oz_line(&self, index: u16) -> i16 {
-        (index as i16) - (self.negative_implicit as i16)
+    pub fn track_to_prev_oz_line(&self, index: u16) -> OriginZeroLine {
+        OriginZeroLine((index as i16) - (self.negative_implicit as i16))
     }
 
     /// Converts a range of tracks as indexes into the CellOccupancyMatrix into
     /// start and end grid lines in OriginZero coordinates
-    pub fn track_range_to_oz_line_range(&self, input: Range<i16>) -> Line<i16> {
+    pub fn track_range_to_oz_line_range(&self, input: Range<i16>) -> Line<OriginZeroLine> {
         let start = self.track_to_prev_oz_line(input.start as u16);
         let end = self.track_to_prev_oz_line(input.end as u16); // Don't add 1 as input range is exclusive
         Line { start, end }
diff --git a/src/compute/grid/types/mod.rs b/src/compute/grid/types/mod.rs
index 1260c9bfd..c9105fa1c 100644
--- a/src/compute/grid/types/mod.rs
+++ b/src/compute/grid/types/mod.rs
@@ -1,11 +1,13 @@
 //! Structs and enums that are used within the grid module
 mod cell_occupancy;
+mod coordinates;
 mod grid_item;
 mod grid_track;
 mod grid_track_counts;
 
 // Publish only locally in the grid module
 pub(super) use cell_occupancy::{CellOccupancyMatrix, CellOccupancyState};
+pub(crate) use coordinates::{GridCoordinate, GridLine, OriginZeroLine};
 pub(super) use grid_item::GridItem;
 pub(super) use grid_track::GridTrack;
 pub(super) use grid_track_counts::TrackCounts;
diff --git a/src/compute/grid/util/coordinates.rs b/src/compute/grid/util/coordinates.rs
deleted file mode 100644
index 6e8de2086..000000000
--- a/src/compute/grid/util/coordinates.rs
+++ /dev/null
@@ -1,36 +0,0 @@
-//! Taffy uses two coordinate systems to refer to grid lines (the gaps/gutters between rows/columns):
-//!
-//!   "CSS Grid Line" coordinates are those used in grid-row/grid-column in the CSS grid spec:
-//!     - The line at left hand (or top) edge of the explicit grid is line 1
-//!       (and counts up from there)
-//!     - The line at the right hand (or bottom) edge of the explicit grid in -1
-//!       (and counts down from there)
-//!     - 0 is not a valid index
-//!
-//!   "OriginZero" coordinates are a normalized form:
-//!     - The line at left hand (or top) edge of the explicit grid is line 0
-//!     - The next line to the right (or down) is 1, and so on
-//!     - The next line to the left (or up) is -1, and so on
-//!
-use core::cmp::Ordering;
-
-/// Convert from CSS Grid Line coordinates to our custom OriginZero coordinates
-pub(crate) fn css_grid_line_into_origin_zero_coords(grid_line: i16, explicit_track_count: u16) -> i16 {
-    let explicit_line_count = explicit_track_count + 1;
-    match grid_line.cmp(&0) {
-        Ordering::Greater => grid_line - 1,
-        Ordering::Less => grid_line + explicit_line_count as i16,
-        Ordering::Equal => panic!("Grid line of zero is invalid"),
-    }
-}
-
-#[allow(dead_code)]
-/// Convert from OriginZero coordinates to CSS Grid Line coordinates
-pub(crate) fn origin_zero_coords_into_css_grid_line(origin_zero_line: i16, explicit_track_count: u16) -> i16 {
-    let explicit_line_count = explicit_track_count + 1;
-    if origin_zero_line >= 0 {
-        origin_zero_line + 1
-    } else {
-        -(origin_zero_line + explicit_line_count as i16)
-    }
-}
diff --git a/src/compute/grid/util/mod.rs b/src/compute/grid/util/mod.rs
index 2e5108a81..ea6a8ed2c 100644
--- a/src/compute/grid/util/mod.rs
+++ b/src/compute/grid/util/mod.rs
@@ -1,11 +1,6 @@
 //! Utility functions used within the grid module
-pub(super) mod coordinates;
-
-pub(super) use coordinates::css_grid_line_into_origin_zero_coords;
-#[allow(unused_imports)]
-pub(super) use coordinates::origin_zero_coords_into_css_grid_line;
 
 #[cfg(test)]
 pub(super) mod test_helpers;
 #[cfg(test)]
-pub(super) use test_helpers::{CreateChildTestNode, CreateParentTestNode};
+pub(super) use test_helpers::{CreateChildTestNode, CreateExpectedPlacement, CreateParentTestNode};
diff --git a/src/compute/grid/util/test_helpers.rs b/src/compute/grid/util/test_helpers.rs
index 5e43032c4..e129d88c5 100644
--- a/src/compute/grid/util/test_helpers.rs
+++ b/src/compute/grid/util/test_helpers.rs
@@ -1,4 +1,5 @@
 //! Helpers for use in unit tests within the grid module
+use super::super::OriginZeroLine;
 use crate::prelude::*;
 use crate::style::{Dimension, GridPlacement, Style};
 
@@ -29,3 +30,12 @@ impl CreateChildTestNode for (GridPlacement, GridPlacement, GridPlacement, GridP
         }
     }
 }
+
+pub(crate) trait CreateExpectedPlacement {
+    fn into_oz(self) -> (OriginZeroLine, OriginZeroLine, OriginZeroLine, OriginZeroLine);
+}
+impl CreateExpectedPlacement for (i16, i16, i16, i16) {
+    fn into_oz(self) -> (OriginZeroLine, OriginZeroLine, OriginZeroLine, OriginZeroLine) {
+        (OriginZeroLine(self.0), OriginZeroLine(self.1), OriginZeroLine(self.2), OriginZeroLine(self.3))
+    }
+}
diff --git a/src/style/grid.rs b/src/style/grid.rs
index 37a9f8afc..e06f49cdc 100644
--- a/src/style/grid.rs
+++ b/src/style/grid.rs
@@ -1,6 +1,7 @@
 //! Style types for CSS Grid layout
 use super::{AlignContent, LengthPercentage, Style};
 use crate::axis::{AbsoluteAxis, AbstractAxis};
+use crate::compute::grid::{GridCoordinate, GridLine, OriginZeroLine};
 use crate::geometry::{Line, MinMax};
 use crate::style::AvailableSpace;
 use crate::style_helpers::*;
@@ -53,30 +54,39 @@ impl GridAutoFlow {
     }
 }
 
-/// A grid line placement specification. Used for grid-[row/column]-[start/end]. Named tracks are not implemented.
+/// A grid line placement specification which is generic over the coordinate system that it uses to define
+/// grid line positions.
 ///
-/// Defaults to [`GridLine::Auto`]
+/// GenericGridPlacement<GridLine> is aliased as GridPlacement and is exposed to users of Taffy to define styles.
+/// GenericGridPlacement<OriginZeroLine> is aliased as OriginZeroGridPlacement and is used internally for placement computations.
 ///
-/// [Specification](https://www.w3.org/TR/css3-grid-layout/#typedef-grid-row-start-grid-line)
+/// See [`crate::compute::grid::type::coordinates`] for documentation on the different coordinate systems.
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
-pub enum GridPlacement {
+pub enum GenericGridPlacement<LineType: GridCoordinate> {
     /// Place item according to the auto-placement algorithm, and the parent's grid_auto_flow property
     Auto,
     /// Place item at specified line (column or row) index
-    Line(i16),
+    Line(LineType),
     /// Item should span specified number of tracks (columns or rows)
     Span(u16),
 }
+
+/// A grid line placement using the normalized OriginZero coordinates to specify line positions.
+pub(crate) type OriginZeroGridPlacement = GenericGridPlacement<OriginZeroLine>;
+
+/// A grid line placement specification. Used for grid-[row/column]-[start/end]. Named tracks are not implemented.
+///
+/// Defaults to [`GridLine::Auto`]
+///
+/// [Specification](https://www.w3.org/TR/css3-grid-layout/#typedef-grid-row-start-grid-line)
+pub type GridPlacement = GenericGridPlacement<GridLine>;
 impl TaffyAuto for GridPlacement {
     const AUTO: Self = Self::Auto;
 }
 impl TaffyGridLine for GridPlacement {
     fn from_line_index(index: i16) -> Self {
-        match index {
-            0 => GridPlacement::Auto,
-            _ => GridPlacement::Line(index),
-        }
+        GridPlacement::Line(GridLine::from(index))
     }
 }
 impl TaffyGridLine for Line<GridPlacement> {
@@ -103,41 +113,73 @@ impl Default for GridPlacement {
 
 impl GridPlacement {
     /// Apply a mapping function if the [`GridPlacement`] is a `Track`. Otherwise return `self` unmodified.
-    pub fn map_track(&self, map_fn: impl FnOnce(i16) -> i16) -> Self {
-        use GridPlacement::*;
-        match *self {
-            Auto => Auto,
-            Span(span) => Span(span),
-            Line(track) => Line(map_fn(track)),
+    pub fn into_origin_zero_placement(self, explicit_track_count: u16) -> OriginZeroGridPlacement {
+        match self {
+            Self::Auto => OriginZeroGridPlacement::Auto,
+            Self::Span(span) => OriginZeroGridPlacement::Span(span),
+            // Grid line zero is an invalid index, so it gets treated as Auto
+            // See: https://developer.mozilla.org/en-US/docs/Web/CSS/grid-row-start#values
+            Self::Line(line) => match line.as_i16() {
+                0 => OriginZeroGridPlacement::Auto,
+                _ => OriginZeroGridPlacement::Line(line.into_origin_zero_line(explicit_track_count)),
+            },
         }
     }
 }
 
-impl Line<GridPlacement> {
+impl<T: GridCoordinate> Line<GenericGridPlacement<T>> {
     #[inline]
     /// Whether the track position is definite in this axis (or the item will need auto placement)
     /// The track position is definite if least one of the start and end positions is a track index
     pub fn is_definite(&self) -> bool {
-        use GridPlacement::*;
-        matches!((self.start, self.end), (Line(_), _) | (_, Line(_)))
+        matches!((self.start, self.end), (GenericGridPlacement::Line(_), _) | (_, GenericGridPlacement::Line(_)))
+    }
+
+    /// Resolves the span for an indefinite placement (a placement that does not consist of two `Track`s).
+    /// Panics if called on a definite placement
+    pub fn indefinite_span(&self) -> u16 {
+        use GenericGridPlacement as GP;
+        match (self.start, self.end) {
+            (GP::Line(_), GP::Auto) => 1,
+            (GP::Auto, GP::Line(_)) => 1,
+            (GP::Auto, GP::Auto) => 1,
+            (GP::Line(_), GP::Span(span)) => span,
+            (GP::Span(span), GP::Line(_)) => span,
+            (GP::Span(span), GP::Auto) => span,
+            (GP::Auto, GP::Span(span)) => span,
+            (GP::Span(span), GP::Span(_)) => span,
+            (GP::Line(_), GP::Line(_)) => panic!("indefinite_span should only be called on indefinite grid tracks"),
+        }
+    }
+}
+
+impl Line<GridPlacement> {
+    /// Apply a mapping function if the [`GridPlacement`] is a `Track`. Otherwise return `self` unmodified.
+    pub fn into_origin_zero(&self, explicit_track_count: u16) -> Line<OriginZeroGridPlacement> {
+        Line {
+            start: self.start.into_origin_zero_placement(explicit_track_count),
+            end: self.end.into_origin_zero_placement(explicit_track_count),
+        }
     }
+}
 
+impl Line<OriginZeroGridPlacement> {
     /// If at least one of the of the start and end positions is a track index then the other end can be resolved
     /// into a track index purely based on the information contained with the placement specification
-    pub fn resolve_definite_grid_tracks(&self) -> Line<i16> {
-        use GridPlacement as GP;
+    pub fn resolve_definite_grid_lines(&self) -> Line<OriginZeroLine> {
+        use OriginZeroGridPlacement as GP;
         match (self.start, self.end) {
-            (GP::Line(track1), GP::Line(track2)) if track1 != 0 && track2 != 0 => {
-                if track1 == track2 {
-                    Line { start: track1, end: track1 + 1 }
+            (GP::Line(line1), GP::Line(line2)) => {
+                if line1 == line2 {
+                    Line { start: line1, end: line1 + 1 }
                 } else {
-                    Line { start: min(track1, track2), end: max(track1, track2) }
+                    Line { start: min(line1, line2), end: max(line1, line2) }
                 }
             }
-            (GP::Line(track), GP::Span(span)) => Line { start: track, end: track + span as i16 },
-            (GP::Line(track), GP::Auto | GP::Line(0)) => Line { start: track, end: track + 1_i16 },
-            (GP::Span(span), GP::Line(track)) => Line { start: track - span as i16, end: track },
-            (GP::Auto | GP::Line(0), GP::Line(track)) => Line { start: track - 1_i16, end: track },
+            (GP::Line(line), GP::Span(span)) => Line { start: line, end: line + span },
+            (GP::Line(line), GP::Auto) => Line { start: line, end: line + 1 },
+            (GP::Span(span), GP::Line(line)) => Line { start: line - span, end: line },
+            (GP::Auto, GP::Line(line)) => Line { start: line - 1, end: line },
             _ => panic!("resolve_definite_grid_tracks should only be called on definite grid tracks"),
         }
     }
@@ -151,53 +193,36 @@ impl Line<GridPlacement> {
     ///
     /// When finally positioning the item, a value of None means that the item's grid area is bounded by the grid
     /// container's border box on that side.
-    pub fn resolve_absolutely_positioned_grid_tracks(&self) -> Line<Option<i16>> {
-        use GridPlacement as GP;
+    pub fn resolve_absolutely_positioned_grid_tracks(&self) -> Line<Option<OriginZeroLine>> {
+        use OriginZeroGridPlacement as GP;
         match (self.start, self.end) {
-            (GP::Line(track1), GP::Line(track2)) if track1 != 0 && track2 != 0 => {
+            (GP::Line(track1), GP::Line(track2)) => {
                 if track1 == track2 {
                     Line { start: Some(track1), end: Some(track1 + 1) }
                 } else {
                     Line { start: Some(min(track1, track2)), end: Some(max(track1, track2)) }
                 }
             }
-            (GP::Line(track), GP::Span(span)) => Line { start: Some(track), end: Some(track + span as i16) },
-            (GP::Line(track), GP::Auto | GP::Line(0)) => Line { start: Some(track), end: None },
-            (GP::Span(span), GP::Line(track)) => Line { start: Some(track - span as i16), end: Some(track) },
-            (GP::Auto | GP::Line(0), GP::Line(track)) => Line { start: None, end: Some(track) },
+            (GP::Line(track), GP::Span(span)) => Line { start: Some(track), end: Some(track + span) },
+            (GP::Line(track), GP::Auto) => Line { start: Some(track), end: None },
+            (GP::Span(span), GP::Line(track)) => Line { start: Some(track - span), end: Some(track) },
+            (GP::Auto, GP::Line(track)) => Line { start: None, end: Some(track) },
             _ => Line { start: None, end: None },
         }
     }
 
     /// If neither of the start and end positions is a track index then the other end can be resolved
     /// into a track index if a definite start position is supplied externally
-    pub fn resolve_indefinite_grid_tracks(&self, start: i16) -> Line<i16> {
-        use GridPlacement as GP;
+    pub fn resolve_indefinite_grid_tracks(&self, start: OriginZeroLine) -> Line<OriginZeroLine> {
+        use OriginZeroGridPlacement as GP;
         match (self.start, self.end) {
             (GP::Auto, GP::Auto) => Line { start, end: start + 1 },
-            (GP::Span(span), GP::Auto) => Line { start, end: start + span as i16 },
-            (GP::Auto, GP::Span(span)) => Line { start, end: start + span as i16 },
-            (GP::Span(span), GP::Span(_)) => Line { start, end: start + span as i16 },
+            (GP::Span(span), GP::Auto) => Line { start, end: start + span },
+            (GP::Auto, GP::Span(span)) => Line { start, end: start + span },
+            (GP::Span(span), GP::Span(_)) => Line { start, end: start + span },
             _ => panic!("resolve_indefinite_grid_tracks should only be called on indefinite grid tracks"),
         }
     }
-
-    /// Resolves the span for an indefinite placement (a placement that does not consist of two `Track`s).
-    /// Panics if called on a definite placement
-    pub fn indefinite_span(&self) -> u16 {
-        use GridPlacement as GP;
-        match (self.start, self.end) {
-            (GP::Line(_), GP::Auto) => 1,
-            (GP::Auto, GP::Line(_)) => 1,
-            (GP::Auto, GP::Auto) => 1,
-            (GP::Line(_), GP::Span(span)) => span,
-            (GP::Span(span), GP::Line(_)) => span,
-            (GP::Span(span), GP::Auto) => span,
-            (GP::Auto, GP::Span(span)) => span,
-            (GP::Span(span), GP::Span(_)) => span,
-            (GP::Line(_), GP::Line(_)) => panic!("indefinite_span should only be called on indefinite grid tracks"),
-        }
-    }
 }
 
 /// Represents the start and end points of a GridItem within a given axis
diff --git a/src/style/mod.rs b/src/style/mod.rs
index 6d5639a3b..a424bd702 100644
--- a/src/style/mod.rs
+++ b/src/style/mod.rs
@@ -10,6 +10,8 @@ pub use self::flex::{FlexDirection, FlexWrap};
 #[cfg(feature = "grid")]
 mod grid;
 #[cfg(feature = "grid")]
+pub(crate) use self::grid::{GenericGridPlacement, OriginZeroGridPlacement};
+#[cfg(feature = "grid")]
 pub use self::grid::{
     GridAutoFlow, GridPlacement, GridTrackRepetition, MaxTrackSizingFunction, MinTrackSizingFunction,
     NonRepeatedTrackSizingFunction, TrackSizingFunction,
diff --git a/src/style_helpers.rs b/src/style_helpers.rs
index e2899e580..7d4d16fcf 100644
--- a/src/style_helpers.rs
+++ b/src/style_helpers.rs
@@ -16,7 +16,10 @@ pub fn repeat(
     TrackSizingFunction::AutoRepeat(repetition_kind, track_list)
 }
 
-/// Returns a GridPlacement::Line
+/// Specifies a grid line to place a grid item between in CSS Grid Line coordinates:
+///  - Positive indicies count upwards from the start (top or left) of the explicit grid
+///  - Negative indicies count downwards from the end (bottom or right) of the explicit grid
+///  - ZERO IS INVALID index, and will be treated as a GridPlacement::Auto.
 pub fn line<T: TaffyGridLine>(index: i16) -> T {
     T::from_line_index(index)
 }
diff --git a/test_fixtures/grid_placement_auto_negative.html b/test_fixtures/grid_placement_auto_negative.html
new file mode 100644
index 000000000..2491d5b76
--- /dev/null
+++ b/test_fixtures/grid_placement_auto_negative.html
@@ -0,0 +1,19 @@
+<!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="height: 120px; width: 120px; display: grid; grid-template-columns: 40px 40px;grid-template-rows: 40px 40px;">
+  <div style="grid-row: 1;grid-column: -5;"></div>
+  <div></div>
+  <div style="grid-row: 2"></div>
+</div>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/test_fixtures/grid_placement_definite_in_secondary_axis_with_fully_definite_negative.html b/test_fixtures/grid_placement_definite_in_secondary_axis_with_fully_definite_negative.html
new file mode 100644
index 000000000..5f20f47c4
--- /dev/null
+++ b/test_fixtures/grid_placement_definite_in_secondary_axis_with_fully_definite_negative.html
@@ -0,0 +1,19 @@
+<!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="height: 120px; width: 120px; display: grid; grid-template-columns: 40px 40px;grid-template-rows: 40px 40px;">
+  <div style="grid-row: 2"></div>
+  <div style="grid-row: 2;grid-column: -4;"></div>
+  <div style="grid-row: 1;"></div>
+</div>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/tests/generated/grid_absolute_column_end.rs b/tests/generated/grid_absolute_column_end.rs
index 68340985e..fcb7dac1b 100644
--- a/tests/generated/grid_absolute_column_end.rs
+++ b/tests/generated/grid_absolute_column_end.rs
@@ -7,10 +7,7 @@ fn grid_absolute_column_end() {
     let node0 = taffy
         .new_leaf(taffy::style::Style {
             position: taffy::style::Position::Absolute,
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Auto,
-                end: taffy::style::GridPlacement::Line(1i16),
-            },
+            grid_column: taffy::geometry::Line { start: taffy::style::GridPlacement::Auto, end: line(1i16) },
             inset: taffy::geometry::Rect {
                 left: taffy::style::LengthPercentageAuto::Points(4f32),
                 right: taffy::style::LengthPercentageAuto::Points(3f32),
diff --git a/tests/generated/grid_absolute_column_start.rs b/tests/generated/grid_absolute_column_start.rs
index f6fd1c750..356c3838c 100644
--- a/tests/generated/grid_absolute_column_start.rs
+++ b/tests/generated/grid_absolute_column_start.rs
@@ -7,10 +7,7 @@ fn grid_absolute_column_start() {
     let node0 = taffy
         .new_leaf(taffy::style::Style {
             position: taffy::style::Position::Absolute,
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_column: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
             inset: taffy::geometry::Rect {
                 left: taffy::style::LengthPercentageAuto::Points(4f32),
                 right: taffy::style::LengthPercentageAuto::Points(3f32),
diff --git a/tests/generated/grid_absolute_row_end.rs b/tests/generated/grid_absolute_row_end.rs
index 542b310a3..c69c1a1d7 100644
--- a/tests/generated/grid_absolute_row_end.rs
+++ b/tests/generated/grid_absolute_row_end.rs
@@ -7,10 +7,7 @@ fn grid_absolute_row_end() {
     let node0 = taffy
         .new_leaf(taffy::style::Style {
             position: taffy::style::Position::Absolute,
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Auto,
-                end: taffy::style::GridPlacement::Line(1i16),
-            },
+            grid_row: taffy::geometry::Line { start: taffy::style::GridPlacement::Auto, end: line(1i16) },
             inset: taffy::geometry::Rect {
                 left: taffy::style::LengthPercentageAuto::Points(4f32),
                 right: taffy::style::LengthPercentageAuto::Points(3f32),
diff --git a/tests/generated/grid_absolute_row_start.rs b/tests/generated/grid_absolute_row_start.rs
index 6d407e9f7..96dbd90bd 100644
--- a/tests/generated/grid_absolute_row_start.rs
+++ b/tests/generated/grid_absolute_row_start.rs
@@ -7,10 +7,7 @@ fn grid_absolute_row_start() {
     let node0 = taffy
         .new_leaf(taffy::style::Style {
             position: taffy::style::Position::Absolute,
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
             inset: taffy::geometry::Rect {
                 left: taffy::style::LengthPercentageAuto::Points(4f32),
                 right: taffy::style::LengthPercentageAuto::Points(3f32),
diff --git a/tests/generated/grid_align_items_sized_center.rs b/tests/generated/grid_align_items_sized_center.rs
index 6980f62d4..cef789834 100644
--- a/tests/generated/grid_align_items_sized_center.rs
+++ b/tests/generated/grid_align_items_sized_center.rs
@@ -6,14 +6,8 @@ fn grid_align_items_sized_center() {
     let mut taffy = taffy::Taffy::new();
     let node0 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(20f32),
                 height: taffy::style::Dimension::Points(20f32),
@@ -23,14 +17,8 @@ fn grid_align_items_sized_center() {
         .unwrap();
     let node1 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(60f32),
                 height: taffy::style::Dimension::Points(60f32),
diff --git a/tests/generated/grid_align_items_sized_end.rs b/tests/generated/grid_align_items_sized_end.rs
index e2a4eb4d2..8c003bd90 100644
--- a/tests/generated/grid_align_items_sized_end.rs
+++ b/tests/generated/grid_align_items_sized_end.rs
@@ -6,14 +6,8 @@ fn grid_align_items_sized_end() {
     let mut taffy = taffy::Taffy::new();
     let node0 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(20f32),
                 height: taffy::style::Dimension::Points(20f32),
@@ -23,14 +17,8 @@ fn grid_align_items_sized_end() {
         .unwrap();
     let node1 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(60f32),
                 height: taffy::style::Dimension::Points(60f32),
diff --git a/tests/generated/grid_align_items_sized_start.rs b/tests/generated/grid_align_items_sized_start.rs
index 780adb24f..8b98ea38a 100644
--- a/tests/generated/grid_align_items_sized_start.rs
+++ b/tests/generated/grid_align_items_sized_start.rs
@@ -6,14 +6,8 @@ fn grid_align_items_sized_start() {
     let mut taffy = taffy::Taffy::new();
     let node0 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(20f32),
                 height: taffy::style::Dimension::Points(20f32),
@@ -23,14 +17,8 @@ fn grid_align_items_sized_start() {
         .unwrap();
     let node1 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(60f32),
                 height: taffy::style::Dimension::Points(60f32),
diff --git a/tests/generated/grid_align_items_sized_stretch.rs b/tests/generated/grid_align_items_sized_stretch.rs
index eaca1d8a7..be7989810 100644
--- a/tests/generated/grid_align_items_sized_stretch.rs
+++ b/tests/generated/grid_align_items_sized_stretch.rs
@@ -6,14 +6,8 @@ fn grid_align_items_sized_stretch() {
     let mut taffy = taffy::Taffy::new();
     let node0 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(20f32),
                 height: taffy::style::Dimension::Points(20f32),
@@ -23,14 +17,8 @@ fn grid_align_items_sized_stretch() {
         .unwrap();
     let node1 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(60f32),
                 height: taffy::style::Dimension::Points(60f32),
diff --git a/tests/generated/grid_justify_items_sized_center.rs b/tests/generated/grid_justify_items_sized_center.rs
index 4347c5ba1..57c788e9a 100644
--- a/tests/generated/grid_justify_items_sized_center.rs
+++ b/tests/generated/grid_justify_items_sized_center.rs
@@ -6,14 +6,8 @@ fn grid_justify_items_sized_center() {
     let mut taffy = taffy::Taffy::new();
     let node0 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(20f32),
                 height: taffy::style::Dimension::Points(20f32),
@@ -23,14 +17,8 @@ fn grid_justify_items_sized_center() {
         .unwrap();
     let node1 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(60f32),
                 height: taffy::style::Dimension::Points(60f32),
diff --git a/tests/generated/grid_justify_items_sized_end.rs b/tests/generated/grid_justify_items_sized_end.rs
index 34bae63c9..8e0b10969 100644
--- a/tests/generated/grid_justify_items_sized_end.rs
+++ b/tests/generated/grid_justify_items_sized_end.rs
@@ -6,14 +6,8 @@ fn grid_justify_items_sized_end() {
     let mut taffy = taffy::Taffy::new();
     let node0 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(20f32),
                 height: taffy::style::Dimension::Points(20f32),
@@ -23,14 +17,8 @@ fn grid_justify_items_sized_end() {
         .unwrap();
     let node1 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(60f32),
                 height: taffy::style::Dimension::Points(60f32),
diff --git a/tests/generated/grid_justify_items_sized_start.rs b/tests/generated/grid_justify_items_sized_start.rs
index ba45d0999..d72826b3d 100644
--- a/tests/generated/grid_justify_items_sized_start.rs
+++ b/tests/generated/grid_justify_items_sized_start.rs
@@ -6,14 +6,8 @@ fn grid_justify_items_sized_start() {
     let mut taffy = taffy::Taffy::new();
     let node0 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(20f32),
                 height: taffy::style::Dimension::Points(20f32),
@@ -23,14 +17,8 @@ fn grid_justify_items_sized_start() {
         .unwrap();
     let node1 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(60f32),
                 height: taffy::style::Dimension::Points(60f32),
diff --git a/tests/generated/grid_justify_items_sized_stretch.rs b/tests/generated/grid_justify_items_sized_stretch.rs
index 872d3b7b9..d254e90a9 100644
--- a/tests/generated/grid_justify_items_sized_stretch.rs
+++ b/tests/generated/grid_justify_items_sized_stretch.rs
@@ -6,14 +6,8 @@ fn grid_justify_items_sized_stretch() {
     let mut taffy = taffy::Taffy::new();
     let node0 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(20f32),
                 height: taffy::style::Dimension::Points(20f32),
@@ -23,14 +17,8 @@ fn grid_justify_items_sized_stretch() {
         .unwrap();
     let node1 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(3i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(3i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(60f32),
                 height: taffy::style::Dimension::Points(60f32),
diff --git a/tests/generated/grid_out_of_order_items.rs b/tests/generated/grid_out_of_order_items.rs
index 166ab6e5a..257b0cb5f 100644
--- a/tests/generated/grid_out_of_order_items.rs
+++ b/tests/generated/grid_out_of_order_items.rs
@@ -8,10 +8,7 @@ fn grid_out_of_order_items() {
     let node1 = taffy.new_leaf(taffy::style::Style { ..Default::default() }).unwrap();
     let node2 = taffy
         .new_leaf(taffy::style::Style {
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_column: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(35f32),
                 height: taffy::style::Dimension::Points(35f32),
@@ -23,14 +20,8 @@ fn grid_out_of_order_items() {
     let node4 = taffy.new_leaf(taffy::style::Style { ..Default::default() }).unwrap();
     let node5 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
-            grid_column: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(20f32),
                 height: taffy::style::Dimension::Points(20f32),
@@ -41,10 +32,7 @@ fn grid_out_of_order_items() {
     let node6 = taffy.new_leaf(taffy::style::Style { ..Default::default() }).unwrap();
     let node7 = taffy
         .new_leaf(taffy::style::Style {
-            grid_row: taffy::geometry::Line {
-                start: taffy::style::GridPlacement::Line(1i16),
-                end: taffy::style::GridPlacement::Auto,
-            },
+            grid_row: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
             size: taffy::geometry::Size {
                 width: taffy::style::Dimension::Points(10f32),
                 height: taffy::style::Dimension::Points(10f32),
diff --git a/tests/generated/grid_placement_auto_negative.rs b/tests/generated/grid_placement_auto_negative.rs
new file mode 100644
index 000000000..d0323261d
--- /dev/null
+++ b/tests/generated/grid_placement_auto_negative.rs
@@ -0,0 +1,60 @@
+#[test]
+fn grid_placement_auto_negative() {
+    use slotmap::Key;
+    #[allow(unused_imports)]
+    use taffy::{layout::Layout, prelude::*};
+    let mut taffy = taffy::Taffy::new();
+    let node0 = taffy
+        .new_leaf(taffy::style::Style {
+            grid_row: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(-5i16), end: taffy::style::GridPlacement::Auto },
+            ..Default::default()
+        })
+        .unwrap();
+    let node1 = taffy.new_leaf(taffy::style::Style { ..Default::default() }).unwrap();
+    let node2 = taffy
+        .new_leaf(taffy::style::Style {
+            grid_row: taffy::geometry::Line { start: line(2i16), end: taffy::style::GridPlacement::Auto },
+            ..Default::default()
+        })
+        .unwrap();
+    let node = taffy
+        .new_with_children(
+            taffy::style::Style {
+                display: taffy::style::Display::Grid,
+                grid_template_rows: vec![points(40f32), points(40f32)],
+                grid_template_columns: vec![points(40f32), points(40f32)],
+                size: taffy::geometry::Size {
+                    width: taffy::style::Dimension::Points(120f32),
+                    height: taffy::style::Dimension::Points(120f32),
+                },
+                ..Default::default()
+            },
+            &[node0, node1, node2],
+        )
+        .unwrap();
+    taffy.compute_layout(node, taffy::geometry::Size::MAX_CONTENT).unwrap();
+    println!("\nComputed tree:");
+    taffy::debug::print_tree(&taffy, node);
+    println!();
+    let Layout { size, location, .. } = taffy.layout(node).unwrap();
+    assert_eq!(size.width, 120f32, "width of node {:?}. Expected {}. Actual {}", node.data(), 120f32, size.width);
+    assert_eq!(size.height, 120f32, "height of node {:?}. Expected {}. Actual {}", node.data(), 120f32, size.height);
+    assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node.data(), 0f32, location.x);
+    assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node.data(), 0f32, location.y);
+    let Layout { size, location, .. } = taffy.layout(node0).unwrap();
+    assert_eq!(size.width, 20f32, "width of node {:?}. Expected {}. Actual {}", node0.data(), 20f32, size.width);
+    assert_eq!(size.height, 40f32, "height of node {:?}. Expected {}. Actual {}", node0.data(), 40f32, size.height);
+    assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node0.data(), 0f32, location.x);
+    assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node0.data(), 0f32, location.y);
+    let Layout { size, location, .. } = taffy.layout(node1).unwrap();
+    assert_eq!(size.width, 20f32, "width of node {:?}. Expected {}. Actual {}", node1.data(), 20f32, size.width);
+    assert_eq!(size.height, 40f32, "height of node {:?}. Expected {}. Actual {}", node1.data(), 40f32, size.height);
+    assert_eq!(location.x, 20f32, "x of node {:?}. Expected {}. Actual {}", node1.data(), 20f32, location.x);
+    assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node1.data(), 0f32, location.y);
+    let Layout { size, location, .. } = taffy.layout(node2).unwrap();
+    assert_eq!(size.width, 20f32, "width of node {:?}. Expected {}. Actual {}", node2.data(), 20f32, size.width);
+    assert_eq!(size.height, 40f32, "height of node {:?}. Expected {}. Actual {}", node2.data(), 40f32, size.height);
+    assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node2.data(), 0f32, location.x);
+    assert_eq!(location.y, 40f32, "y of node {:?}. Expected {}. Actual {}", node2.data(), 40f32, location.y);
+}
diff --git a/tests/generated/grid_placement_definite_in_secondary_axis_with_fully_definite_negative.rs b/tests/generated/grid_placement_definite_in_secondary_axis_with_fully_definite_negative.rs
new file mode 100644
index 000000000..7159c01d8
--- /dev/null
+++ b/tests/generated/grid_placement_definite_in_secondary_axis_with_fully_definite_negative.rs
@@ -0,0 +1,71 @@
+#[test]
+fn grid_placement_definite_in_secondary_axis_with_fully_definite_negative() {
+    use slotmap::Key;
+    #[allow(unused_imports)]
+    use taffy::{layout::Layout, prelude::*};
+    let mut taffy = taffy::Taffy::new();
+    let node0 = taffy
+        .new_leaf(taffy::style::Style {
+            grid_row: taffy::geometry::Line { start: line(2i16), end: taffy::style::GridPlacement::Auto },
+            ..Default::default()
+        })
+        .unwrap();
+    let node1 = taffy
+        .new_leaf(taffy::style::Style {
+            grid_row: taffy::geometry::Line { start: line(2i16), end: taffy::style::GridPlacement::Auto },
+            grid_column: taffy::geometry::Line { start: line(-4i16), end: taffy::style::GridPlacement::Auto },
+            ..Default::default()
+        })
+        .unwrap();
+    let node2 = taffy
+        .new_leaf(taffy::style::Style {
+            grid_row: taffy::geometry::Line { start: line(1i16), end: taffy::style::GridPlacement::Auto },
+            ..Default::default()
+        })
+        .unwrap();
+    let node3 = taffy.new_leaf(taffy::style::Style { ..Default::default() }).unwrap();
+    let node = taffy
+        .new_with_children(
+            taffy::style::Style {
+                display: taffy::style::Display::Grid,
+                grid_template_rows: vec![points(40f32), points(40f32)],
+                grid_template_columns: vec![points(40f32), points(40f32)],
+                size: taffy::geometry::Size {
+                    width: taffy::style::Dimension::Points(120f32),
+                    height: taffy::style::Dimension::Points(120f32),
+                },
+                ..Default::default()
+            },
+            &[node0, node1, node2, node3],
+        )
+        .unwrap();
+    taffy.compute_layout(node, taffy::geometry::Size::MAX_CONTENT).unwrap();
+    println!("\nComputed tree:");
+    taffy::debug::print_tree(&taffy, node);
+    println!();
+    let Layout { size, location, .. } = taffy.layout(node).unwrap();
+    assert_eq!(size.width, 120f32, "width of node {:?}. Expected {}. Actual {}", node.data(), 120f32, size.width);
+    assert_eq!(size.height, 120f32, "height of node {:?}. Expected {}. Actual {}", node.data(), 120f32, size.height);
+    assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node.data(), 0f32, location.x);
+    assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node.data(), 0f32, location.y);
+    let Layout { size, location, .. } = taffy.layout(node0).unwrap();
+    assert_eq!(size.width, 40f32, "width of node {:?}. Expected {}. Actual {}", node0.data(), 40f32, size.width);
+    assert_eq!(size.height, 40f32, "height of node {:?}. Expected {}. Actual {}", node0.data(), 40f32, size.height);
+    assert_eq!(location.x, 40f32, "x of node {:?}. Expected {}. Actual {}", node0.data(), 40f32, location.x);
+    assert_eq!(location.y, 40f32, "y of node {:?}. Expected {}. Actual {}", node0.data(), 40f32, location.y);
+    let Layout { size, location, .. } = taffy.layout(node1).unwrap();
+    assert_eq!(size.width, 40f32, "width of node {:?}. Expected {}. Actual {}", node1.data(), 40f32, size.width);
+    assert_eq!(size.height, 40f32, "height of node {:?}. Expected {}. Actual {}", node1.data(), 40f32, size.height);
+    assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node1.data(), 0f32, location.x);
+    assert_eq!(location.y, 40f32, "y of node {:?}. Expected {}. Actual {}", node1.data(), 40f32, location.y);
+    let Layout { size, location, .. } = taffy.layout(node2).unwrap();
+    assert_eq!(size.width, 40f32, "width of node {:?}. Expected {}. Actual {}", node2.data(), 40f32, size.width);
+    assert_eq!(size.height, 40f32, "height of node {:?}. Expected {}. Actual {}", node2.data(), 40f32, size.height);
+    assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node2.data(), 0f32, location.x);
+    assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node2.data(), 0f32, location.y);
+    let Layout { size, location, .. } = taffy.layout(node3).unwrap();
+    assert_eq!(size.width, 40f32, "width of node {:?}. Expected {}. Actual {}", node3.data(), 40f32, size.width);
+    assert_eq!(size.height, 40f32, "height of node {:?}. Expected {}. Actual {}", node3.data(), 40f32, size.height);
+    assert_eq!(location.x, 40f32, "x of node {:?}. Expected {}. Actual {}", node3.data(), 40f32, location.x);
+    assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node3.data(), 0f32, location.y);
+}
diff --git a/tests/generated/mod.rs b/tests/generated/mod.rs
index 30442ab48..0a7dd7489 100644
--- a/tests/generated/mod.rs
+++ b/tests/generated/mod.rs
@@ -412,6 +412,10 @@ mod grid_percent_width_and_margin;
 #[cfg(feature = "grid")]
 mod grid_percent_width_and_padding;
 #[cfg(feature = "grid")]
+mod grid_placement_auto_negative;
+#[cfg(feature = "grid")]
+mod grid_placement_definite_in_secondary_axis_with_fully_definite_negative;
+#[cfg(feature = "grid")]
 mod grid_relayout_vertical_text;
 #[cfg(feature = "grid")]
 mod grid_size_child_fixed_tracks;