diff --git a/Userland/Libraries/LibWeb/CSS/ComputedValues.h b/Userland/Libraries/LibWeb/CSS/ComputedValues.h
index 65e43bf8fa4..7fc77ee7d72 100644
--- a/Userland/Libraries/LibWeb/CSS/ComputedValues.h
+++ b/Userland/Libraries/LibWeb/CSS/ComputedValues.h
@@ -64,8 +64,8 @@ public:
     static CSS::Size height() { return CSS::Size::make_auto(); }
     static CSS::Size min_height() { return CSS::Size::make_auto(); }
     static CSS::Size max_height() { return CSS::Size::make_none(); }
-    static CSS::ExplicitTrackSizing grid_template_columns() { return CSS::ExplicitTrackSizing::make_auto(); }
-    static CSS::ExplicitTrackSizing grid_template_rows() { return CSS::ExplicitTrackSizing::make_auto(); }
+    static CSS::GridTrackSizeList grid_template_columns() { return CSS::GridTrackSizeList::make_auto(); }
+    static CSS::GridTrackSizeList grid_template_rows() { return CSS::GridTrackSizeList::make_auto(); }
     static CSS::GridTrackPlacement grid_column_end() { return CSS::GridTrackPlacement::make_auto(); }
     static CSS::GridTrackPlacement grid_column_start() { return CSS::GridTrackPlacement::make_auto(); }
     static CSS::GridTrackPlacement grid_row_end() { return CSS::GridTrackPlacement::make_auto(); }
@@ -182,8 +182,8 @@ public:
     CSS::Size const& min_height() const { return m_noninherited.min_height; }
     CSS::Size const& max_height() const { return m_noninherited.max_height; }
     Variant<CSS::VerticalAlign, CSS::LengthPercentage> const& vertical_align() const { return m_noninherited.vertical_align; }
-    CSS::ExplicitTrackSizing const& grid_template_columns() const { return m_noninherited.grid_template_columns; }
-    CSS::ExplicitTrackSizing const& grid_template_rows() const { return m_noninherited.grid_template_rows; }
+    CSS::GridTrackSizeList const& grid_template_columns() const { return m_noninherited.grid_template_columns; }
+    CSS::GridTrackSizeList const& grid_template_rows() const { return m_noninherited.grid_template_rows; }
     CSS::GridTrackPlacement const& grid_column_end() const { return m_noninherited.grid_column_end; }
     CSS::GridTrackPlacement const& grid_column_start() const { return m_noninherited.grid_column_start; }
     CSS::GridTrackPlacement const& grid_row_end() const { return m_noninherited.grid_row_end; }
@@ -304,8 +304,8 @@ protected:
         CSS::BoxSizing box_sizing { InitialValues::box_sizing() };
         CSS::ContentData content;
         Variant<CSS::VerticalAlign, CSS::LengthPercentage> vertical_align { InitialValues::vertical_align() };
-        CSS::ExplicitTrackSizing grid_template_columns;
-        CSS::ExplicitTrackSizing grid_template_rows;
+        CSS::GridTrackSizeList grid_template_columns;
+        CSS::GridTrackSizeList grid_template_rows;
         CSS::GridTrackPlacement grid_column_end { InitialValues::grid_column_end() };
         CSS::GridTrackPlacement grid_column_start { InitialValues::grid_column_start() };
         CSS::GridTrackPlacement grid_row_end { InitialValues::grid_row_end() };
@@ -382,8 +382,8 @@ public:
     void set_box_sizing(CSS::BoxSizing value) { m_noninherited.box_sizing = value; }
     void set_vertical_align(Variant<CSS::VerticalAlign, CSS::LengthPercentage> value) { m_noninherited.vertical_align = move(value); }
     void set_visibility(CSS::Visibility value) { m_inherited.visibility = value; }
-    void set_grid_template_columns(CSS::ExplicitTrackSizing value) { m_noninherited.grid_template_columns = move(value); }
-    void set_grid_template_rows(CSS::ExplicitTrackSizing value) { m_noninherited.grid_template_rows = move(value); }
+    void set_grid_template_columns(CSS::GridTrackSizeList value) { m_noninherited.grid_template_columns = move(value); }
+    void set_grid_template_rows(CSS::GridTrackSizeList value) { m_noninherited.grid_template_rows = move(value); }
     void set_grid_column_end(CSS::GridTrackPlacement value) { m_noninherited.grid_column_end = value; }
     void set_grid_column_start(CSS::GridTrackPlacement value) { m_noninherited.grid_column_start = value; }
     void set_grid_row_end(CSS::GridTrackPlacement value) { m_noninherited.grid_row_end = value; }
diff --git a/Userland/Libraries/LibWeb/CSS/GridTrackSize.cpp b/Userland/Libraries/LibWeb/CSS/GridTrackSize.cpp
index 0097d1e3d64..850788387c6 100644
--- a/Userland/Libraries/LibWeb/CSS/GridTrackSize.cpp
+++ b/Userland/Libraries/LibWeb/CSS/GridTrackSize.cpp
@@ -12,34 +12,39 @@
 
 namespace Web::CSS {
 
-GridTrackSize::GridTrackSize(Length length)
+GridSize::GridSize(Length length)
     : m_type(Type::Length)
     , m_length(length)
 {
 }
 
-GridTrackSize::GridTrackSize(Percentage percentage)
+GridSize::GridSize(Percentage percentage)
     : m_type(Type::Percentage)
     , m_length { Length::make_px(0) }
     , m_percentage(percentage)
 {
 }
 
-GridTrackSize::GridTrackSize(float flexible_length)
+GridSize::GridSize(float flexible_length)
     : m_type(Type::FlexibleLength)
     , m_length { Length::make_px(0) }
     , m_flexible_length(flexible_length)
 {
 }
 
-GridTrackSize::~GridTrackSize() = default;
-
-GridTrackSize GridTrackSize::make_auto()
+GridSize::GridSize()
+    : m_length { Length::make_auto() }
 {
-    return GridTrackSize(CSS::Length::make_auto());
 }
 
-String GridTrackSize::to_string() const
+GridSize::~GridSize() = default;
+
+GridSize GridSize::make_auto()
+{
+    return GridSize(CSS::Length::make_auto());
+}
+
+String GridSize::to_string() const
 {
     switch (m_type) {
     case Type::Length:
@@ -52,78 +57,123 @@ String GridTrackSize::to_string() const
     VERIFY_NOT_REACHED();
 }
 
-Length GridTrackSize::length() const
+Length GridSize::length() const
 {
     return m_length;
 }
 
-MetaGridTrackSize::MetaGridTrackSize(GridTrackSize grid_track_size)
-    : m_min_grid_track_size(grid_track_size)
-    , m_max_grid_track_size(grid_track_size)
+GridMinMax::GridMinMax(GridSize min_grid_size, GridSize max_grid_size)
+    : m_min_grid_size(min_grid_size)
+    , m_max_grid_size(max_grid_size)
 {
 }
 
-MetaGridTrackSize::MetaGridTrackSize(GridTrackSize min_grid_track_size, GridTrackSize max_grid_track_size)
-    : m_min_grid_track_size(min_grid_track_size)
-    , m_max_grid_track_size(max_grid_track_size)
-    , m_is_min_max(true)
+String GridMinMax::to_string() const
 {
+    StringBuilder builder;
+    builder.append("minmax("sv);
+    builder.appendff("{}", m_min_grid_size.to_string());
+    builder.append(", "sv);
+    builder.appendff("{}", m_max_grid_size.to_string());
+    builder.append(")"sv);
+    return builder.to_string();
 }
 
-String MetaGridTrackSize::to_string() const
-{
-    if (m_is_min_max) {
-        StringBuilder builder;
-        builder.append("minmax("sv);
-        builder.appendff("{}", m_min_grid_track_size.to_string());
-        builder.append(", "sv);
-        builder.appendff("{}", m_max_grid_track_size.to_string());
-        builder.append(")"sv);
-        return builder.to_string();
-    }
-    return String::formatted("{}", m_min_grid_track_size.to_string());
-}
-
-ExplicitTrackSizing::ExplicitTrackSizing()
-{
-}
-
-ExplicitTrackSizing::ExplicitTrackSizing(Vector<CSS::MetaGridTrackSize> meta_grid_track_sizes)
-    : m_meta_grid_track_sizes(meta_grid_track_sizes)
-{
-}
-
-ExplicitTrackSizing::ExplicitTrackSizing(Vector<CSS::MetaGridTrackSize> meta_grid_track_sizes, int repeat_count)
-    : m_meta_grid_track_sizes(meta_grid_track_sizes)
-    , m_is_repeat(true)
+GridRepeat::GridRepeat(GridTrackSizeList grid_track_size_list, int repeat_count)
+    : m_type(Type::Default)
+    , m_grid_track_size_list(grid_track_size_list)
     , m_repeat_count(repeat_count)
 {
 }
 
-ExplicitTrackSizing::ExplicitTrackSizing(Vector<CSS::MetaGridTrackSize> meta_grid_track_sizes, Type type)
-    : m_meta_grid_track_sizes(meta_grid_track_sizes)
-    , m_is_auto_fill(type == Type::AutoFill)
-    , m_is_auto_fit(type == Type::AutoFit)
+GridRepeat::GridRepeat(GridTrackSizeList grid_track_size_list, Type type)
+    : m_type(type)
+    , m_grid_track_size_list(grid_track_size_list)
 {
 }
 
-String ExplicitTrackSizing::to_string() const
+GridRepeat::GridRepeat()
+{
+}
+
+String GridRepeat::to_string() const
 {
     StringBuilder builder;
-    if (m_is_repeat) {
-        builder.append("repeat("sv);
-        builder.append(m_repeat_count);
-        builder.append(", "sv);
+    builder.append("repeat("sv);
+    switch (m_type) {
+    case Type::AutoFit:
+        builder.append("auto-fill"sv);
+        break;
+    case Type::AutoFill:
+        builder.append("auto-fit"sv);
+        break;
+    case Type::Default:
+        builder.appendff("{}", m_repeat_count);
+        break;
+    default:
+        VERIFY_NOT_REACHED();
     }
-    for (int _ = 0; _ < m_repeat_count; ++_) {
-        for (size_t y = 0; y < m_meta_grid_track_sizes.size(); ++y) {
-            builder.append(m_meta_grid_track_sizes[y].to_string());
-            if (y != m_meta_grid_track_sizes.size() - 1)
-                builder.append(" "sv);
-        }
+    builder.append(", "sv);
+    builder.appendff("{}", m_grid_track_size_list.to_string());
+    builder.append(")"sv);
+    return builder.to_string();
+}
+
+ExplicitGridTrack::ExplicitGridTrack(CSS::GridMinMax grid_minmax)
+    : m_type(Type::MinMax)
+    , m_grid_minmax(grid_minmax)
+{
+}
+
+ExplicitGridTrack::ExplicitGridTrack(CSS::GridRepeat grid_repeat)
+    : m_type(Type::Repeat)
+    , m_grid_repeat(grid_repeat)
+{
+}
+
+ExplicitGridTrack::ExplicitGridTrack(CSS::GridSize grid_size)
+    : m_type(Type::Default)
+    , m_grid_size(grid_size)
+{
+}
+
+String ExplicitGridTrack::to_string() const
+{
+    switch (m_type) {
+    case Type::MinMax:
+        return m_grid_minmax.to_string();
+    case Type::Repeat:
+        return m_grid_repeat.to_string();
+    case Type::Default:
+        return m_grid_size.to_string();
+    default:
+        VERIFY_NOT_REACHED();
+    }
+}
+
+GridTrackSizeList::GridTrackSizeList(Vector<CSS::ExplicitGridTrack> track_list)
+    : m_track_list(track_list)
+{
+}
+
+GridTrackSizeList::GridTrackSizeList()
+    : m_track_list({})
+{
+}
+
+GridTrackSizeList GridTrackSizeList::make_auto()
+{
+    return GridTrackSizeList();
+}
+
+String GridTrackSizeList::to_string() const
+{
+    StringBuilder builder;
+    for (size_t i = 0; i < m_track_list.size(); ++i) {
+        builder.append(m_track_list[i].to_string());
+        if (i < m_track_list.size() - 1)
+            builder.append(" "sv);
     }
-    if (m_is_repeat)
-        builder.append(")"sv);
     return builder.to_string();
 }
 
diff --git a/Userland/Libraries/LibWeb/CSS/GridTrackSize.h b/Userland/Libraries/LibWeb/CSS/GridTrackSize.h
index 7fc5f8e2659..6a269ae568f 100644
--- a/Userland/Libraries/LibWeb/CSS/GridTrackSize.h
+++ b/Userland/Libraries/LibWeb/CSS/GridTrackSize.h
@@ -12,7 +12,7 @@
 
 namespace Web::CSS {
 
-class GridTrackSize {
+class GridSize {
 public:
     enum class Type {
         Length,
@@ -21,12 +21,13 @@ public:
         // TODO: Max-Content
     };
 
-    GridTrackSize(Length);
-    GridTrackSize(Percentage);
-    GridTrackSize(float);
-    ~GridTrackSize();
+    GridSize(Length);
+    GridSize(Percentage);
+    GridSize(float);
+    GridSize();
+    ~GridSize();
 
-    static GridTrackSize make_auto();
+    static GridSize make_auto();
 
     Type type() const { return m_type; }
 
@@ -52,7 +53,7 @@ public:
     }
 
     String to_string() const;
-    bool operator==(GridTrackSize const& other) const
+    bool operator==(GridSize const& other) const
     {
         return m_type == other.type()
             && m_length == other.length()
@@ -69,63 +70,133 @@ private:
     float m_flexible_length { 0 };
 };
 
-class MetaGridTrackSize {
+class GridMinMax {
 public:
-    MetaGridTrackSize(CSS::GridTrackSize);
-    MetaGridTrackSize(CSS::GridTrackSize min_grid_track_size, CSS::GridTrackSize max_grid_track_size);
+    GridMinMax(CSS::GridSize min_grid_size, CSS::GridSize max_grid_size);
+    GridMinMax() = default;
 
-    bool is_min_max() const { return m_is_min_max; }
-
-    GridTrackSize grid_track_size() const& { return m_min_grid_track_size; }
-    GridTrackSize min_grid_track_size() const& { return m_min_grid_track_size; }
-    GridTrackSize max_grid_track_size() const& { return m_max_grid_track_size; }
+    GridSize min_grid_size() const& { return m_min_grid_size; }
+    GridSize max_grid_size() const& { return m_max_grid_size; }
 
     String to_string() const;
-    bool operator==(MetaGridTrackSize const& other) const
+    bool operator==(GridMinMax const& other) const
     {
-        return m_min_grid_track_size == other.min_grid_track_size()
-            && m_max_grid_track_size == other.max_grid_track_size();
+        return m_min_grid_size == other.min_grid_size()
+            && m_max_grid_size == other.max_grid_size();
     }
 
 private:
-    GridTrackSize m_min_grid_track_size;
-    GridTrackSize m_max_grid_track_size;
-    bool m_is_min_max { false };
+    GridSize m_min_grid_size;
+    GridSize m_max_grid_size;
 };
 
-class ExplicitTrackSizing {
+class GridTrackSizeList {
+public:
+    GridTrackSizeList(Vector<CSS::ExplicitGridTrack> track_list);
+    GridTrackSizeList();
+
+    static GridTrackSizeList make_auto();
+
+    Vector<CSS::ExplicitGridTrack> track_list() const { return m_track_list; }
+    String to_string() const;
+    bool operator==(GridTrackSizeList const& other) const
+    {
+        return m_track_list == other.track_list();
+    }
+
+private:
+    Vector<CSS::ExplicitGridTrack> m_track_list;
+};
+
+class GridRepeat {
 public:
     enum class Type {
         AutoFit,
         AutoFill,
+        Default,
     };
+    GridRepeat(GridTrackSizeList, int repeat_count);
+    GridRepeat(GridTrackSizeList, Type);
+    GridRepeat();
 
-    ExplicitTrackSizing();
-    ExplicitTrackSizing(Vector<CSS::MetaGridTrackSize>);
-    ExplicitTrackSizing(Vector<CSS::MetaGridTrackSize>, int repeat_count);
-    ExplicitTrackSizing(Vector<CSS::MetaGridTrackSize>, Type);
-
-    static ExplicitTrackSizing make_auto() { return ExplicitTrackSizing(); };
-
-    bool is_repeat() const { return m_is_repeat; }
-    bool is_auto_fill() const { return m_is_auto_fill; }
-    bool is_auto_fit() const { return m_is_auto_fit; }
-    int repeat_count() const { return m_repeat_count; }
-
-    Vector<CSS::MetaGridTrackSize> meta_grid_track_sizes() const& { return m_meta_grid_track_sizes; }
+    bool is_auto_fill() const { return m_type == Type::AutoFill; }
+    bool is_auto_fit() const { return m_type == Type::AutoFit; }
+    bool is_default() const { return m_type == Type::Default; }
+    int repeat_count() const
+    {
+        VERIFY(is_default());
+        return m_repeat_count;
+    }
+    GridTrackSizeList grid_track_size_list() const& { return m_grid_track_size_list; }
+    Type type() const& { return m_type; }
 
     String to_string() const;
-    bool operator==(ExplicitTrackSizing const& other) const
+    bool operator==(GridRepeat const& other) const
     {
-        return m_meta_grid_track_sizes == other.meta_grid_track_sizes();
+        if (m_type != other.type())
+            return false;
+        if (m_type == Type::Default && m_repeat_count != other.repeat_count())
+            return false;
+        return m_grid_track_size_list == other.grid_track_size_list();
     }
 
 private:
-    Vector<CSS::MetaGridTrackSize> m_meta_grid_track_sizes;
-    bool m_is_repeat { false };
-    bool m_is_auto_fill { false };
-    bool m_is_auto_fit { false };
+    Type m_type;
+    GridTrackSizeList m_grid_track_size_list;
     int m_repeat_count { 0 };
 };
 
+class ExplicitGridTrack {
+public:
+    enum class Type {
+        MinMax,
+        Repeat,
+        Default,
+    };
+    ExplicitGridTrack(CSS::GridRepeat);
+    ExplicitGridTrack(CSS::GridMinMax);
+    ExplicitGridTrack(CSS::GridSize);
+
+    bool is_repeat() const { return m_type == Type::Repeat; }
+    GridRepeat repeat() const
+    {
+        VERIFY(is_repeat());
+        return m_grid_repeat;
+    }
+
+    bool is_minmax() const { return m_type == Type::MinMax; }
+    GridMinMax minmax() const
+    {
+        VERIFY(is_minmax());
+        return m_grid_minmax;
+    }
+
+    bool is_default() const { return m_type == Type::Default; }
+    GridSize grid_size() const
+    {
+        VERIFY(is_default());
+        return m_grid_size;
+    }
+
+    Type type() const { return m_type; }
+
+    String to_string() const;
+    bool operator==(ExplicitGridTrack const& other) const
+    {
+        if (is_repeat() && other.is_repeat())
+            return m_grid_repeat == other.repeat();
+        if (is_minmax() && other.is_minmax())
+            return m_grid_minmax == other.minmax();
+        if (is_default() && other.is_default())
+            return m_grid_size == other.grid_size();
+        return false;
+    }
+
+private:
+    Type m_type;
+    GridRepeat m_grid_repeat;
+    GridMinMax m_grid_minmax;
+    GridSize m_grid_size;
+};
+
 }
diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
index f43ac3eeee5..ef47008ed91 100644
--- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
+++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
@@ -5407,221 +5407,267 @@ RefPtr<StyleValue> Parser::parse_as_css_value(PropertyID property_id)
     return parsed_value.release_value();
 }
 
+Optional<CSS::GridSize> Parser::parse_grid_size(ComponentValue const& component_value)
+{
+    // FIXME: Parse calc here if necessary
+    if (component_value.is_function())
+        return {};
+    auto token = component_value.token();
+    if (token.is(Token::Type::Dimension) && token.dimension_unit().equals_ignoring_case("fr"sv)) {
+        float numeric_value = token.dimension_value();
+        if (numeric_value)
+            return GridSize(numeric_value);
+    }
+    if (token.is(Token::Type::Ident) && token.ident().equals_ignoring_case("auto"sv))
+        return GridSize::make_auto();
+    auto dimension = parse_dimension(token);
+    if (!dimension.has_value())
+        return {};
+    if (dimension->is_length())
+        return GridSize(dimension->length());
+    else if (dimension->is_percentage())
+        return GridSize(dimension->percentage());
+    return {};
+}
+
+Optional<CSS::GridMinMax> Parser::parse_min_max(Vector<ComponentValue> const& component_values)
+{
+    // https://www.w3.org/TR/css-grid-2/#valdef-grid-template-columns-minmax
+    // 'minmax(min, max)'
+    // Defines a size range greater than or equal to min and less than or equal to max. If the max is
+    // less than the min, then the max will be floored by the min (essentially yielding minmax(min,
+    // min)). As a maximum, a <flex> value sets the track’s flex factor; it is invalid as a minimum.
+    auto function_tokens = TokenStream(component_values);
+    auto comma_separated_list = parse_a_comma_separated_list_of_component_values(function_tokens);
+    if (comma_separated_list.size() != 2)
+        return {};
+
+    TokenStream part_one_tokens { comma_separated_list[0] };
+    part_one_tokens.skip_whitespace();
+    if (!part_one_tokens.has_next_token())
+        return {};
+    auto current_token = part_one_tokens.next_token();
+    auto min_grid_size = parse_grid_size(current_token);
+
+    TokenStream part_two_tokens { comma_separated_list[1] };
+    part_two_tokens.skip_whitespace();
+    if (!part_two_tokens.has_next_token())
+        return {};
+    current_token = part_two_tokens.next_token();
+    auto max_grid_size = parse_grid_size(current_token);
+
+    if (min_grid_size.has_value() && max_grid_size.has_value()) {
+        // https://www.w3.org/TR/css-grid-2/#valdef-grid-template-columns-minmax
+        // As a maximum, a <flex> value sets the track’s flex factor; it is invalid as a minimum.
+        if (min_grid_size.value().is_flexible_length())
+            return {};
+        return CSS::GridMinMax(min_grid_size.value(), max_grid_size.value());
+    }
+    return {};
+}
+
+Optional<CSS::GridRepeat> Parser::parse_repeat(Vector<ComponentValue> const& component_values)
+{
+    // https://www.w3.org/TR/css-grid-2/#repeat-syntax
+    // 7.2.3.1. Syntax of repeat()
+    // The generic form of the repeat() syntax is, approximately,
+    // repeat( [ <integer [1,∞]> | auto-fill | auto-fit ] , <track-list> )
+    auto is_auto_fill = false;
+    auto is_auto_fit = false;
+    auto function_tokens = TokenStream(component_values);
+    auto comma_separated_list = parse_a_comma_separated_list_of_component_values(function_tokens);
+    if (comma_separated_list.size() != 2)
+        return {};
+    // The first argument specifies the number of repetitions.
+    TokenStream part_one_tokens { comma_separated_list[0] };
+    part_one_tokens.skip_whitespace();
+    if (!part_one_tokens.has_next_token())
+        return {};
+    auto current_token = part_one_tokens.next_token().token();
+
+    auto repeat_count = 0;
+    if (current_token.is(Token::Type::Number) && current_token.number().is_integer() && current_token.number_value() > 0)
+        repeat_count = current_token.number_value();
+    else if (current_token.is(Token::Type::Ident) && current_token.ident().equals_ignoring_case("auto-fill"sv))
+        is_auto_fill = true;
+    else if (current_token.is(Token::Type::Ident) && current_token.ident().equals_ignoring_case("auto-fit"sv))
+        is_auto_fit = true;
+
+    // The second argument is a track list, which is repeated that number of times.
+    TokenStream part_two_tokens { comma_separated_list[1] };
+    part_two_tokens.skip_whitespace();
+    if (!part_two_tokens.has_next_token())
+        return {};
+
+    Vector<CSS::ExplicitGridTrack> repeat_params;
+    while (part_two_tokens.has_next_token()) {
+        auto token = part_two_tokens.next_token();
+        if (token.is_block()) {
+            part_two_tokens.skip_whitespace();
+        } else {
+            auto track_sizing_function = parse_track_sizing_function(token);
+            if (!track_sizing_function.has_value())
+                return {};
+            // However, there are some restrictions:
+            // The repeat() notation can’t be nested.
+            if (track_sizing_function.value().is_repeat())
+                return {};
+            // Automatic repetitions (auto-fill or auto-fit) cannot be combined with intrinsic or flexible sizes.
+            if (track_sizing_function.value().is_default() && track_sizing_function.value().grid_size().is_flexible_length() && (is_auto_fill || is_auto_fit))
+                return {};
+            repeat_params.append(track_sizing_function.value());
+            part_two_tokens.skip_whitespace();
+        }
+    }
+
+    // Thus the precise syntax of the repeat() notation has several forms:
+    // <track-repeat> = repeat( [ <integer [1,∞]> ] , [ <line-names>? <track-size> ]+ <line-names>? )
+    // <auto-repeat>  = repeat( [ auto-fill | auto-fit ] , [ <line-names>? <fixed-size> ]+ <line-names>? )
+    // <fixed-repeat> = repeat( [ <integer [1,∞]> ] , [ <line-names>? <fixed-size> ]+ <line-names>? )
+    // <name-repeat>  = repeat( [ <integer [1,∞]> | auto-fill ], <line-names>+)
+
+    // The <track-repeat> variant can represent the repetition of any <track-size>, but is limited to a
+    // fixed number of repetitions.
+
+    // The <auto-repeat> variant can repeat automatically to fill a space, but requires definite track
+    // sizes so that the number of repetitions can be calculated. It can only appear once in the track
+    // list, but the same track list can also contain <fixed-repeat>s.
+
+    // The <name-repeat> variant is for adding line names to subgrids. It can only be used with the
+    // subgrid keyword and cannot specify track sizes, only line names.
+
+    // If a repeat() function that is not a <name-repeat> ends up placing two <line-names> adjacent to
+    // each other, the name lists are merged. For example, repeat(2, [a] 1fr [b]) is equivalent to [a]
+    // 1fr [b a] 1fr [b].
+    if (is_auto_fill)
+        return CSS::GridRepeat(CSS::GridTrackSizeList(repeat_params), CSS::GridRepeat::Type::AutoFill);
+    else if (is_auto_fit)
+        return CSS::GridRepeat(CSS::GridTrackSizeList(repeat_params), CSS::GridRepeat::Type::AutoFit);
+    else
+        return CSS::GridRepeat(CSS::GridTrackSizeList(repeat_params), repeat_count);
+}
+
+Optional<CSS::ExplicitGridTrack> Parser::parse_track_sizing_function(ComponentValue const& token)
+{
+    if (token.is_function()) {
+        auto const& function_token = token.function();
+        if (function_token.name().equals_ignoring_case("repeat"sv)) {
+            auto maybe_repeat = parse_repeat(function_token.values());
+            if (maybe_repeat.has_value())
+                return CSS::ExplicitGridTrack(maybe_repeat.value());
+            else
+                return {};
+        } else if (function_token.name().equals_ignoring_case("minmax"sv)) {
+            auto maybe_min_max_value = parse_min_max(function_token.values());
+            if (maybe_min_max_value.has_value())
+                return CSS::ExplicitGridTrack(maybe_min_max_value.value());
+            else
+                return {};
+        }
+        return {};
+    } else if (token.is(Token::Type::Ident) && token.token().ident().equals_ignoring_case("auto"sv)) {
+        return CSS::ExplicitGridTrack(GridSize(Length::make_auto()));
+    } else if (token.is_block()) {
+        return {};
+    } else {
+        auto grid_size = parse_grid_size(token);
+        if (!grid_size.has_value())
+            return {};
+        return CSS::ExplicitGridTrack(grid_size.value());
+    }
+}
+
 RefPtr<StyleValue> Parser::parse_grid_track_sizes(Vector<ComponentValue> const& component_values)
 {
-    auto parse_grid_track_size = [&](ComponentValue const& component_value) -> Optional<GridTrackSize> {
-        // FIXME: Parse calc here if necessary
-        if (component_value.is_function())
-            return {};
-        auto token = component_value.token();
-        if (token.is(Token::Type::Dimension) && token.dimension_unit().equals_ignoring_case("fr"sv)) {
-            float numeric_value = token.dimension_value();
-            if (numeric_value)
-                return GridTrackSize(numeric_value);
+    Vector<CSS::ExplicitGridTrack> track_list;
+    TokenStream tokens { component_values };
+    while (tokens.has_next_token()) {
+        auto token = tokens.next_token();
+        if (token.is_block()) {
+
+        } else {
+            auto track_sizing_function = parse_track_sizing_function(token);
+            if (!track_sizing_function.has_value())
+                return GridTrackSizeStyleValue::make_auto();
+            // FIXME: Handle multiple repeat values (should combine them here, or remove
+            // any other ones if the first one is auto-fill, etc.)
+            track_list.append(track_sizing_function.value());
         }
-        if (token.is(Token::Type::Ident) && token.ident().equals_ignoring_case("auto"sv))
-            return GridTrackSize::make_auto();
-        auto dimension = parse_dimension(token);
-        if (!dimension.has_value())
-            return {};
-        if (dimension->is_length())
-            return GridTrackSize(dimension->length());
-        else if (dimension->is_percentage())
-            return GridTrackSize(dimension->percentage());
-        return {};
-    };
-
-    auto parse_min_max = [&](Vector<ComponentValue> const& component_values) -> Optional<MetaGridTrackSize> {
-        // https://www.w3.org/TR/css-grid-2/#valdef-grid-template-columns-minmax
-        // 'minmax(min, max)'
-        // Defines a size range greater than or equal to min and less than or equal to max. If the max is
-        // less than the min, then the max will be floored by the min (essentially yielding minmax(min,
-        // min)). As a maximum, a <flex> value sets the track’s flex factor; it is invalid as a minimum.
-        auto function_tokens = TokenStream(component_values);
-        auto comma_separated_list = parse_a_comma_separated_list_of_component_values(function_tokens);
-        if (comma_separated_list.size() != 2)
-            return MetaGridTrackSize(GridTrackSize::make_auto());
-
-        TokenStream part_one_tokens { comma_separated_list[0] };
-        part_one_tokens.skip_whitespace();
-        if (!part_one_tokens.has_next_token())
-            return MetaGridTrackSize(GridTrackSize::make_auto());
-        auto current_token = part_one_tokens.next_token();
-        auto min_grid_track_size = parse_grid_track_size(current_token);
-
-        TokenStream part_two_tokens { comma_separated_list[1] };
-        part_two_tokens.skip_whitespace();
-        if (!part_two_tokens.has_next_token())
-            return MetaGridTrackSize(GridTrackSize::make_auto());
-        current_token = part_two_tokens.next_token();
-        auto max_grid_track_size = parse_grid_track_size(current_token);
-
-        if (min_grid_track_size.has_value() && max_grid_track_size.has_value()) {
-            // https://www.w3.org/TR/css-grid-2/#valdef-grid-template-columns-minmax
-            // As a maximum, a <flex> value sets the track’s flex factor; it is invalid as a minimum.
-            if (min_grid_track_size.value().is_flexible_length())
-                return {};
-            return MetaGridTrackSize(min_grid_track_size.value(), max_grid_track_size.value());
-        }
-        return MetaGridTrackSize(GridTrackSize::make_auto(), GridTrackSize::make_auto());
-    };
-
-    Vector<CSS::MetaGridTrackSize> params;
-    for (auto const& component_value : component_values) {
-        if (component_value.is_function()) {
-            auto const& function_token = component_value.function();
-            // https://www.w3.org/TR/css-grid-2/#repeat-syntax
-            // 7.2.3.1. Syntax of repeat()
-            // The generic form of the repeat() syntax is, approximately,
-            // repeat( [ <integer [1,∞]> | auto-fill | auto-fit ] , <track-list> )
-            if (function_token.name().equals_ignoring_case("repeat"sv)) {
-                auto is_auto_fill = false;
-                auto is_auto_fit = false;
-                auto function_tokens = TokenStream(function_token.values());
-                auto comma_separated_list = parse_a_comma_separated_list_of_component_values(function_tokens);
-                if (comma_separated_list.size() != 2)
-                    continue;
-                // The first argument specifies the number of repetitions.
-                TokenStream part_one_tokens { comma_separated_list[0] };
-                part_one_tokens.skip_whitespace();
-                if (!part_one_tokens.has_next_token())
-                    continue;
-                auto current_token = part_one_tokens.next_token().token();
-
-                auto repeat_count = 0;
-                if (current_token.is(Token::Type::Number) && current_token.number().is_integer() && current_token.number_value() > 0)
-                    repeat_count = current_token.number_value();
-                else if (current_token.is(Token::Type::Ident) && current_token.ident().equals_ignoring_case("auto-fill"sv))
-                    is_auto_fill = true;
-                else if (current_token.is(Token::Type::Ident) && current_token.ident().equals_ignoring_case("auto-fit"sv))
-                    is_auto_fit = true;
-
-                // The second argument is a track list, which is repeated that number of times.
-                TokenStream part_two_tokens { comma_separated_list[1] };
-                part_two_tokens.skip_whitespace();
-                if (!part_two_tokens.has_next_token())
-                    continue;
-
-                Vector<CSS::MetaGridTrackSize> repeat_params;
-                while (true) {
-                    part_two_tokens.skip_whitespace();
-                    auto current_component_value = part_two_tokens.next_token();
-                    if (current_component_value.is_function()) {
-                        // However, there are some restrictions:
-                        // The repeat() notation can’t be nested.
-                        if (current_component_value.function().name().equals_ignoring_case("repeat"sv))
-                            return GridTrackSizeStyleValue::create({});
-                        if (current_component_value.function().name().equals_ignoring_case("minmax"sv)) {
-                            auto maybe_min_max_value = parse_min_max(current_component_value.function().values());
-                            if (maybe_min_max_value.has_value())
-                                repeat_params.append(maybe_min_max_value.value());
-                            else
-                                return GridTrackSizeStyleValue::create({});
-                        }
-                        // FIXME: Implement other function values possible within repeat
-                    } else if (current_component_value.is_block()) {
-                        // FIXME: Implement things like grid-template-columns: repeat(1, [col-start] 8);
-                    } else {
-                        auto grid_track_size = parse_grid_track_size(current_component_value);
-                        if (!grid_track_size.has_value())
-                            return GridTrackSizeStyleValue::create({});
-                        // Automatic repetitions (auto-fill or auto-fit) cannot be combined with intrinsic or flexible sizes.
-                        if (grid_track_size.value().is_flexible_length() && (is_auto_fill || is_auto_fit))
-                            return GridTrackSizeStyleValue::create({});
-                        repeat_params.append(grid_track_size.value());
-                    }
-                    part_two_tokens.skip_whitespace();
-                    if (!part_two_tokens.has_next_token())
-                        break;
-                }
-
-                // Thus the precise syntax of the repeat() notation has several forms:
-                // <track-repeat> = repeat( [ <integer [1,∞]> ] , [ <line-names>? <track-size> ]+ <line-names>? )
-                // <auto-repeat>  = repeat( [ auto-fill | auto-fit ] , [ <line-names>? <fixed-size> ]+ <line-names>? )
-                // <fixed-repeat> = repeat( [ <integer [1,∞]> ] , [ <line-names>? <fixed-size> ]+ <line-names>? )
-                // <name-repeat>  = repeat( [ <integer [1,∞]> | auto-fill ], <line-names>+)
-
-                // The <track-repeat> variant can represent the repetition of any <track-size>, but is limited to a
-                // fixed number of repetitions.
-
-                // The <auto-repeat> variant can repeat automatically to fill a space, but requires definite track
-                // sizes so that the number of repetitions can be calculated. It can only appear once in the track
-                // list, but the same track list can also contain <fixed-repeat>s.
-
-                // The <name-repeat> variant is for adding line names to subgrids. It can only be used with the
-                // subgrid keyword and cannot specify track sizes, only line names.
-
-                // If a repeat() function that is not a <name-repeat> ends up placing two <line-names> adjacent to
-                // each other, the name lists are merged. For example, repeat(2, [a] 1fr [b]) is equivalent to [a]
-                // 1fr [b a] 1fr [b].
-                if (is_auto_fill)
-                    return GridTrackSizeStyleValue::create(CSS::ExplicitTrackSizing(repeat_params, CSS::ExplicitTrackSizing::Type::AutoFill));
-                if (is_auto_fit)
-                    return GridTrackSizeStyleValue::create(CSS::ExplicitTrackSizing(repeat_params, CSS::ExplicitTrackSizing::Type::AutoFit));
-                return GridTrackSizeStyleValue::create(CSS::ExplicitTrackSizing(repeat_params, repeat_count));
-            } else if (function_token.name().equals_ignoring_case("minmax"sv)) {
-                auto maybe_min_max_value = parse_min_max(function_token.values());
-                if (maybe_min_max_value.has_value())
-                    params.append(maybe_min_max_value.value());
-                else
-                    return GridTrackSizeStyleValue::create({});
-            }
-            continue;
-        }
-        if (component_value.is_block()) {
-            params.append(GridTrackSize(Length::make_auto()));
-            continue;
-        }
-        if (component_value.is(Token::Type::Ident) && component_value.token().ident().equals_ignoring_case("auto"sv)) {
-            params.append(GridTrackSize(Length::make_auto()));
-            continue;
-        }
-        auto grid_track_size = parse_grid_track_size(component_value);
-        if (!grid_track_size.has_value())
-            return GridTrackSizeStyleValue::create({});
-        params.append(grid_track_size.value());
     }
-    return GridTrackSizeStyleValue::create(params);
+    return GridTrackSizeStyleValue::create(CSS::GridTrackSizeList(track_list));
 }
 
 RefPtr<StyleValue> Parser::parse_grid_track_placement(Vector<ComponentValue> const& component_values)
 {
+    // https://www.w3.org/TR/css-grid-2/#line-placement
+    // Line-based Placement: the grid-row-start, grid-column-start, grid-row-end, and grid-column-end properties
+    // <grid-line> =
+    //     auto |
+    //     <custom-ident> |
+    //     [ <integer> && <custom-ident>? ] |
+    //     [ span && [ <integer> || <custom-ident> ] ]
+    auto is_auto = [](Token token) -> bool {
+        if (token.is(Token::Type::Ident) && token.ident().equals_ignoring_case("auto"sv))
+            return true;
+        return false;
+    };
+    auto is_span = [](Token token) -> bool {
+        if (token.is(Token::Type::Ident) && token.ident().equals_ignoring_case("span"sv))
+            return true;
+        return false;
+    };
+    auto is_valid_integer = [](Token token) -> bool {
+        // An <integer> value of zero makes the declaration invalid.
+        if (token.is(Token::Type::Number) && token.number().is_integer() && token.number_value() != 0)
+            return true;
+        return false;
+    };
     auto tokens = TokenStream { component_values };
+    tokens.skip_whitespace();
     auto current_token = tokens.next_token().token();
 
     if (!tokens.has_next_token()) {
-        if (current_token.is(Token::Type::Ident) && current_token.ident().equals_ignoring_case("auto"sv))
+        if (is_auto(current_token))
             return GridTrackPlacementStyleValue::create(CSS::GridTrackPlacement());
-        // https://drafts.csswg.org/css-grid/#grid-placement-span-int
-        // If the <integer> is omitted, it defaults to 1.
-        if (current_token.is(Token::Type::Ident) && current_token.ident().equals_ignoring_case("span"sv))
+        if (is_span(current_token))
             return GridTrackPlacementStyleValue::create(CSS::GridTrackPlacement(1, true));
-        // https://drafts.csswg.org/css-grid/#grid-placement-int
-        // [ <integer [−∞,−1]> | <integer [1,∞]> ] && <custom-ident>?
-        // An <integer> value of zero makes the declaration invalid.
-        if (current_token.is(Token::Type::Number) && current_token.number().is_integer() && current_token.number_value() != 0)
+        if (is_valid_integer(current_token))
             return GridTrackPlacementStyleValue::create(CSS::GridTrackPlacement(static_cast<int>(current_token.number_value())));
         return {};
     }
 
-    auto is_span = false;
-    if (current_token.is(Token::Type::Ident) && current_token.ident().equals_ignoring_case("span"sv)) {
-        is_span = true;
+    auto span_value = false;
+    auto span_or_position_value = 0;
+    while (true) {
+        if (is_auto(current_token))
+            return {};
+        if (is_span(current_token)) {
+            if (span_value == false)
+                span_value = true;
+            else
+                return {};
+        }
+        if (is_valid_integer(current_token)) {
+            if (span_or_position_value == 0)
+                span_or_position_value = static_cast<int>(current_token.number_value());
+            else
+                return {};
+        }
         tokens.skip_whitespace();
-        current_token = tokens.next_token().token();
+        if (tokens.has_next_token())
+            current_token = tokens.next_token().token();
+        else
+            break;
     }
 
-    // https://drafts.csswg.org/css-grid/#grid-placement-int
-    // [ <integer [−∞,−1]> | <integer [1,∞]> ] && <custom-ident>?
-    // An <integer> value of zero makes the declaration invalid.
-    if (current_token.is(Token::Type::Number) && current_token.number().is_integer() && current_token.number_value() != 0 && !tokens.has_next_token()) {
-        // https://drafts.csswg.org/css-grid/#grid-placement-span-int
-        // Negative integers or zero are invalid.
-        if (is_span && static_cast<int>(current_token.number_value()) < 1)
-            return GridTrackPlacementStyleValue::create(CSS::GridTrackPlacement(1, is_span));
-        return GridTrackPlacementStyleValue::create(CSS::GridTrackPlacement(static_cast<int>(current_token.number_value()), is_span));
-    }
-    return {};
+    // Negative integers or zero are invalid.
+    if (span_value && span_or_position_value < 1)
+        return {};
+
+    // If the <integer> is omitted, it defaults to 1.
+    if (span_or_position_value == 0)
+        span_or_position_value = 1;
+    return GridTrackPlacementStyleValue::create(CSS::GridTrackPlacement(span_or_position_value, span_value));
 }
 
 RefPtr<StyleValue> Parser::parse_grid_track_placement_shorthand_value(Vector<ComponentValue> const& component_values)
diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h
index 896127cb2fb..7d31d8ad935 100644
--- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h
+++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h
@@ -250,6 +250,10 @@ private:
     Optional<Ratio> parse_ratio(TokenStream<ComponentValue>&);
     Optional<UnicodeRange> parse_unicode_range(TokenStream<ComponentValue>&);
     Optional<UnicodeRange> parse_unicode_range(StringView);
+    Optional<GridSize> parse_grid_size(ComponentValue const&);
+    Optional<GridMinMax> parse_min_max(Vector<ComponentValue> const&);
+    Optional<GridRepeat> parse_repeat(Vector<ComponentValue> const&);
+    Optional<ExplicitGridTrack> parse_track_sizing_function(ComponentValue const&);
 
     enum class AllowedDataUrlType {
         None,
diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp
index 96ebd559e7b..58bb482f931 100644
--- a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp
+++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp
@@ -710,16 +710,16 @@ Optional<CSS::FontVariant> StyleProperties::font_variant() const
     return value_id_to_font_variant(value->to_identifier());
 }
 
-CSS::ExplicitTrackSizing StyleProperties::grid_template_columns() const
+CSS::GridTrackSizeList StyleProperties::grid_template_columns() const
 {
     auto value = property(CSS::PropertyID::GridTemplateColumns);
-    return value->as_explicit_track_sizing().explicit_track_sizing();
+    return value->as_grid_track_size_list().grid_track_size_list();
 }
 
-CSS::ExplicitTrackSizing StyleProperties::grid_template_rows() const
+CSS::GridTrackSizeList StyleProperties::grid_template_rows() const
 {
     auto value = property(CSS::PropertyID::GridTemplateRows);
-    return value->as_explicit_track_sizing().explicit_track_sizing();
+    return value->as_grid_track_size_list().grid_track_size_list();
 }
 
 CSS::GridTrackPlacement StyleProperties::grid_column_end() const
diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.h b/Userland/Libraries/LibWeb/CSS/StyleProperties.h
index 33be1134275..55f51fd020a 100644
--- a/Userland/Libraries/LibWeb/CSS/StyleProperties.h
+++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.h
@@ -85,8 +85,8 @@ public:
     Optional<CSS::PointerEvents> pointer_events() const;
     Variant<CSS::VerticalAlign, CSS::LengthPercentage> vertical_align() const;
     Optional<CSS::FontVariant> font_variant() const;
-    CSS::ExplicitTrackSizing grid_template_columns() const;
-    CSS::ExplicitTrackSizing grid_template_rows() const;
+    CSS::GridTrackSizeList grid_template_columns() const;
+    CSS::GridTrackSizeList grid_template_rows() const;
     CSS::GridTrackPlacement grid_column_end() const;
     CSS::GridTrackPlacement grid_column_start() const;
     CSS::GridTrackPlacement grid_row_end() const;
diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp
index 009850fca36..869cab95c54 100644
--- a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp
+++ b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp
@@ -168,9 +168,9 @@ LengthStyleValue const& StyleValue::as_length() const
     return static_cast<LengthStyleValue const&>(*this);
 }
 
-GridTrackSizeStyleValue const& StyleValue::as_explicit_track_sizing() const
+GridTrackSizeStyleValue const& StyleValue::as_grid_track_size_list() const
 {
-    VERIFY(is_explicit_track_sizing());
+    VERIFY(is_grid_track_size_list());
     return static_cast<GridTrackSizeStyleValue const&>(*this);
 }
 
@@ -1418,15 +1418,15 @@ bool GridTrackPlacementStyleValue::equals(StyleValue const& other) const
 
 String GridTrackSizeStyleValue::to_string() const
 {
-    return m_explicit_track_sizing.to_string();
+    return m_grid_track_size_list.to_string();
 }
 
 bool GridTrackSizeStyleValue::equals(StyleValue const& other) const
 {
     if (type() != other.type())
         return false;
-    auto const& typed_other = other.as_explicit_track_sizing();
-    return m_explicit_track_sizing == typed_other.explicit_track_sizing();
+    auto const& typed_other = other.as_grid_track_size_list();
+    return m_grid_track_size_list == typed_other.grid_track_size_list();
 }
 
 String IdentifierStyleValue::to_string() const
@@ -2145,9 +2145,14 @@ NonnullRefPtr<GridTrackPlacementStyleValue> GridTrackPlacementStyleValue::create
     return adopt_ref(*new GridTrackPlacementStyleValue(grid_track_placement));
 }
 
-NonnullRefPtr<GridTrackSizeStyleValue> GridTrackSizeStyleValue::create(CSS::ExplicitTrackSizing explicit_track_sizing)
+NonnullRefPtr<GridTrackSizeStyleValue> GridTrackSizeStyleValue::create(CSS::GridTrackSizeList grid_track_size_list)
 {
-    return adopt_ref(*new GridTrackSizeStyleValue(explicit_track_sizing));
+    return adopt_ref(*new GridTrackSizeStyleValue(grid_track_size_list));
+}
+
+NonnullRefPtr<GridTrackSizeStyleValue> GridTrackSizeStyleValue::make_auto()
+{
+    return adopt_ref(*new GridTrackSizeStyleValue(CSS::GridTrackSizeList()));
 }
 
 NonnullRefPtr<RectStyleValue> RectStyleValue::create(EdgeRect rect)
diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValue.h
index 44255447530..991b57a4cfe 100644
--- a/Userland/Libraries/LibWeb/CSS/StyleValue.h
+++ b/Userland/Libraries/LibWeb/CSS/StyleValue.h
@@ -174,7 +174,6 @@ public:
         Calculated,
         Color,
         Content,
-        ExplicitTrackSizing,
         FilterValueList,
         Flex,
         FlexFlow,
@@ -182,6 +181,7 @@ public:
         Frequency,
         GridTrackPlacement,
         GridTrackPlacementShorthand,
+        GridTrackSizeList,
         Identifier,
         Image,
         Inherit,
@@ -226,7 +226,7 @@ public:
     bool is_frequency() const { return type() == Type::Frequency; }
     bool is_grid_track_placement() const { return type() == Type::GridTrackPlacement; }
     bool is_grid_track_placement_shorthand() const { return type() == Type::GridTrackPlacementShorthand; }
-    bool is_explicit_track_sizing() const { return type() == Type::ExplicitTrackSizing; }
+    bool is_grid_track_size_list() const { return type() == Type::GridTrackSizeList; }
     bool is_identifier() const { return type() == Type::Identifier; }
     bool is_image() const { return type() == Type::Image; }
     bool is_inherit() const { return type() == Type::Inherit; }
@@ -269,7 +269,7 @@ public:
     FrequencyStyleValue const& as_frequency() const;
     GridTrackPlacementShorthandStyleValue const& as_grid_track_placement_shorthand() const;
     GridTrackPlacementStyleValue const& as_grid_track_placement() const;
-    GridTrackSizeStyleValue const& as_explicit_track_sizing() const;
+    GridTrackSizeStyleValue const& as_grid_track_size_list() const;
     IdentifierStyleValue const& as_identifier() const;
     ImageStyleValue const& as_image() const;
     InheritStyleValue const& as_inherit() const;
@@ -310,7 +310,7 @@ public:
     FrequencyStyleValue& as_frequency() { return const_cast<FrequencyStyleValue&>(const_cast<StyleValue const&>(*this).as_frequency()); }
     GridTrackPlacementShorthandStyleValue& as_grid_track_placement_shorthand() { return const_cast<GridTrackPlacementShorthandStyleValue&>(const_cast<StyleValue const&>(*this).as_grid_track_placement_shorthand()); }
     GridTrackPlacementStyleValue& as_grid_track_placement() { return const_cast<GridTrackPlacementStyleValue&>(const_cast<StyleValue const&>(*this).as_grid_track_placement()); }
-    GridTrackSizeStyleValue& as_explicit_track_sizing() { return const_cast<GridTrackSizeStyleValue&>(const_cast<StyleValue const&>(*this).as_explicit_track_sizing()); }
+    GridTrackSizeStyleValue& as_grid_track_size_list() { return const_cast<GridTrackSizeStyleValue&>(const_cast<StyleValue const&>(*this).as_grid_track_size_list()); }
     IdentifierStyleValue& as_identifier() { return const_cast<IdentifierStyleValue&>(const_cast<StyleValue const&>(*this).as_identifier()); }
     ImageStyleValue& as_image() { return const_cast<ImageStyleValue&>(const_cast<StyleValue const&>(*this).as_image()); }
     InheritStyleValue& as_inherit() { return const_cast<InheritStyleValue&>(const_cast<StyleValue const&>(*this).as_inherit()); }
@@ -1048,21 +1048,24 @@ private:
 
 class GridTrackSizeStyleValue final : public StyleValue {
 public:
-    static NonnullRefPtr<GridTrackSizeStyleValue> create(CSS::ExplicitTrackSizing explicit_track_sizing);
+    static NonnullRefPtr<GridTrackSizeStyleValue> create(CSS::GridTrackSizeList grid_track_size_list);
     virtual ~GridTrackSizeStyleValue() override = default;
 
-    CSS::ExplicitTrackSizing explicit_track_sizing() const { return m_explicit_track_sizing; }
+    static NonnullRefPtr<GridTrackSizeStyleValue> make_auto();
+
+    CSS::GridTrackSizeList grid_track_size_list() const { return m_grid_track_size_list; }
+
     virtual String to_string() const override;
     virtual bool equals(StyleValue const& other) const override;
 
 private:
-    explicit GridTrackSizeStyleValue(CSS::ExplicitTrackSizing explicit_track_sizing)
-        : StyleValue(Type::ExplicitTrackSizing)
-        , m_explicit_track_sizing(explicit_track_sizing)
+    explicit GridTrackSizeStyleValue(CSS::GridTrackSizeList grid_track_size_list)
+        : StyleValue(Type::GridTrackSizeList)
+        , m_grid_track_size_list(grid_track_size_list)
     {
     }
 
-    CSS::ExplicitTrackSizing m_explicit_track_sizing;
+    CSS::GridTrackSizeList m_grid_track_size_list;
 };
 
 class IdentifierStyleValue final : public StyleValue {
diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h
index d726199b088..e39ecebdee2 100644
--- a/Userland/Libraries/LibWeb/Forward.h
+++ b/Userland/Libraries/LibWeb/Forward.h
@@ -50,7 +50,7 @@ class CSSStyleSheet;
 class CSSSupportsRule;
 class Display;
 class ElementInlineCSSStyleDeclaration;
-class ExplicitTrackSizing;
+class ExplicitGridTrack;
 class FilterValueListStyleValue;
 class FlexFlowStyleValue;
 class FlexStyleValue;
@@ -59,10 +59,13 @@ class FontStyleValue;
 class Frequency;
 class FrequencyPercentage;
 class FrequencyStyleValue;
+class GridMinMax;
+class GridRepeat;
+class GridSize;
 class GridTrackPlacement;
 class GridTrackPlacementShorthandStyleValue;
 class GridTrackPlacementStyleValue;
-class GridTrackSize;
+class GridTrackSizeList;
 class GridTrackSizeStyleValue;
 class IdentifierStyleValue;
 class ImageStyleValue;
@@ -79,7 +82,6 @@ class MediaList;
 class MediaQuery;
 class MediaQueryList;
 class MediaQueryListEvent;
-class MetaGridTrackSize;
 class Number;
 class NumericStyleValue;
 class OverflowStyleValue;
diff --git a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp
index f946b0e2e8a..de7075bd859 100644
--- a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp
+++ b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp
@@ -20,6 +20,8 @@ GridFormattingContext::~GridFormattingContext() = default;
 void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const& available_space)
 {
     auto& box_state = m_state.get_mutable(box);
+    auto grid_template_columns = box.computed_values().grid_template_columns();
+    auto grid_template_rows = box.computed_values().grid_template_rows();
     auto should_skip_is_anonymous_text_run = [&](Box& child_box) -> bool {
         if (child_box.is_anonymous() && !child_box.first_child_of_type<BlockContainer>()) {
             bool contains_only_white_space = true;
@@ -36,16 +38,16 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const
         return false;
     };
 
-    auto resolve_definite_track_size = [&](CSS::GridTrackSize const& grid_track_size) -> float {
-        VERIFY(grid_track_size.is_definite());
-        switch (grid_track_size.type()) {
-        case CSS::GridTrackSize::Type::Length:
-            if (grid_track_size.length().is_auto())
+    auto resolve_definite_track_size = [&](CSS::GridSize const& grid_size) -> float {
+        VERIFY(grid_size.is_definite());
+        switch (grid_size.type()) {
+        case CSS::GridSize::Type::Length:
+            if (grid_size.length().is_auto())
                 break;
-            return grid_track_size.length().to_px(box);
+            return grid_size.length().to_px(box);
             break;
-        case CSS::GridTrackSize::Type::Percentage:
-            return grid_track_size.percentage().as_fraction() * box_state.content_width();
+        case CSS::GridSize::Type::Percentage:
+            return grid_size.percentage().as_fraction() * box_state.content_width();
             break;
         default:
             VERIFY_NOT_REACHED();
@@ -75,8 +77,20 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const
         boxes_to_place.append(child_box);
         return IterationDecision::Continue;
     });
-    auto column_repeat_count = box.computed_values().grid_template_columns().is_repeat() ? box.computed_values().grid_template_columns().repeat_count() : 1;
-    auto row_repeat_count = box.computed_values().grid_template_rows().is_repeat() ? box.computed_values().grid_template_rows().repeat_count() : 1;
+    auto column_count = 0;
+    for (auto const& explicit_grid_track : grid_template_columns.track_list()) {
+        if (explicit_grid_track.is_repeat() && explicit_grid_track.repeat().is_default())
+            column_count += explicit_grid_track.repeat().repeat_count() * explicit_grid_track.repeat().grid_track_size_list().track_list().size();
+        else
+            column_count += 1;
+    }
+    auto row_count = 0;
+    for (auto const& explicit_grid_track : grid_template_rows.track_list()) {
+        if (explicit_grid_track.is_repeat() && explicit_grid_track.repeat().is_default())
+            row_count += explicit_grid_track.repeat().repeat_count() * explicit_grid_track.repeat().grid_track_size_list().track_list().size();
+        else
+            row_count += 1;
+    }
 
     // https://www.w3.org/TR/css-grid-2/#auto-repeat
     // 7.2.3.2. Repeat-to-fill: auto-fill and auto-fit repetitions
@@ -85,7 +99,9 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const
     // the span is already fulfilled).
 
     // Otherwise on a standalone axis, when auto-fill is given as the repetition number
-    if (box.computed_values().grid_template_columns().is_auto_fill() || box.computed_values().grid_template_columns().is_auto_fit()) {
+    if (grid_template_columns.track_list().size() == 1
+        && grid_template_columns.track_list().first().is_repeat()
+        && (grid_template_columns.track_list().first().repeat().is_auto_fill() || grid_template_columns.track_list().first().repeat().is_auto_fit())) {
         // If the grid container has a definite size or max size in the relevant axis, then the number of
         // repetitions is the largest possible positive integer that does not cause the grid to overflow the
         // content box of its grid container
@@ -95,21 +111,28 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const
         // function otherwise, flooring the max track sizing function by the min track sizing function if both
         // are definite, and taking gap into account)
         // FIXME: take gap into account
-        for (auto& meta_grid_track : box.computed_values().grid_template_columns().meta_grid_track_sizes()) {
-            if (meta_grid_track.max_grid_track_size().is_definite() && !meta_grid_track.min_grid_track_size().is_definite())
-                sum_of_grid_track_sizes += resolve_definite_track_size(meta_grid_track.max_grid_track_size());
-            else if (meta_grid_track.min_grid_track_size().is_definite() && !meta_grid_track.max_grid_track_size().is_definite())
-                sum_of_grid_track_sizes += resolve_definite_track_size(meta_grid_track.min_grid_track_size());
-            else if (meta_grid_track.min_grid_track_size().is_definite() && meta_grid_track.max_grid_track_size().is_definite())
-                sum_of_grid_track_sizes += min(resolve_definite_track_size(meta_grid_track.min_grid_track_size()), resolve_definite_track_size(meta_grid_track.max_grid_track_size()));
+        for (auto& explicit_grid_track : grid_template_columns.track_list().first().repeat().grid_track_size_list().track_list()) {
+            auto track_sizing_function = explicit_grid_track;
+            if (track_sizing_function.is_minmax()) {
+                if (track_sizing_function.minmax().max_grid_size().is_definite() && !track_sizing_function.minmax().min_grid_size().is_definite())
+                    sum_of_grid_track_sizes += resolve_definite_track_size(track_sizing_function.minmax().max_grid_size());
+                else if (track_sizing_function.minmax().min_grid_size().is_definite() && !track_sizing_function.minmax().max_grid_size().is_definite())
+                    sum_of_grid_track_sizes += resolve_definite_track_size(track_sizing_function.minmax().min_grid_size());
+                else if (track_sizing_function.minmax().min_grid_size().is_definite() && track_sizing_function.minmax().max_grid_size().is_definite())
+                    sum_of_grid_track_sizes += min(resolve_definite_track_size(track_sizing_function.minmax().min_grid_size()), resolve_definite_track_size(track_sizing_function.minmax().max_grid_size()));
+            } else {
+                sum_of_grid_track_sizes += min(resolve_definite_track_size(track_sizing_function.grid_size()), resolve_definite_track_size(track_sizing_function.grid_size()));
+            }
         }
-        column_repeat_count = max(1, static_cast<int>(get_free_space_x(box) / sum_of_grid_track_sizes));
+        column_count = max(1, static_cast<int>(get_free_space_x(box) / sum_of_grid_track_sizes));
 
         // For the purpose of finding the number of auto-repeated tracks in a standalone axis, the UA must
         // floor the track size to a UA-specified value to avoid division by zero. It is suggested that this
         // floor be 1px.
     }
-    if (box.computed_values().grid_template_rows().is_auto_fill() || box.computed_values().grid_template_rows().is_auto_fit()) {
+    if (grid_template_rows.track_list().size() == 1
+        && grid_template_rows.track_list().first().is_repeat()
+        && (grid_template_rows.track_list().first().repeat().is_auto_fill() || grid_template_rows.track_list().first().repeat().is_auto_fit())) {
         // If the grid container has a definite size or max size in the relevant axis, then the number of
         // repetitions is the largest possible positive integer that does not cause the grid to overflow the
         // content box of its grid container
@@ -119,15 +142,20 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const
         // function otherwise, flooring the max track sizing function by the min track sizing function if both
         // are definite, and taking gap into account)
         // FIXME: take gap into account
-        for (auto& meta_grid_track : box.computed_values().grid_template_rows().meta_grid_track_sizes()) {
-            if (meta_grid_track.max_grid_track_size().is_definite() && !meta_grid_track.min_grid_track_size().is_definite())
-                sum_of_grid_track_sizes += resolve_definite_track_size(meta_grid_track.max_grid_track_size());
-            else if (meta_grid_track.min_grid_track_size().is_definite() && !meta_grid_track.max_grid_track_size().is_definite())
-                sum_of_grid_track_sizes += resolve_definite_track_size(meta_grid_track.min_grid_track_size());
-            else if (meta_grid_track.min_grid_track_size().is_definite() && meta_grid_track.max_grid_track_size().is_definite())
-                sum_of_grid_track_sizes += min(resolve_definite_track_size(meta_grid_track.min_grid_track_size()), resolve_definite_track_size(meta_grid_track.max_grid_track_size()));
+        for (auto& explicit_grid_track : grid_template_rows.track_list().first().repeat().grid_track_size_list().track_list()) {
+            auto track_sizing_function = explicit_grid_track;
+            if (track_sizing_function.is_minmax()) {
+                if (track_sizing_function.minmax().max_grid_size().is_definite() && !track_sizing_function.minmax().min_grid_size().is_definite())
+                    sum_of_grid_track_sizes += resolve_definite_track_size(track_sizing_function.minmax().max_grid_size());
+                else if (track_sizing_function.minmax().min_grid_size().is_definite() && !track_sizing_function.minmax().max_grid_size().is_definite())
+                    sum_of_grid_track_sizes += resolve_definite_track_size(track_sizing_function.minmax().min_grid_size());
+                else if (track_sizing_function.minmax().min_grid_size().is_definite() && track_sizing_function.minmax().max_grid_size().is_definite())
+                    sum_of_grid_track_sizes += min(resolve_definite_track_size(track_sizing_function.minmax().min_grid_size()), resolve_definite_track_size(track_sizing_function.minmax().max_grid_size()));
+            } else {
+                sum_of_grid_track_sizes += min(resolve_definite_track_size(track_sizing_function.grid_size()), resolve_definite_track_size(track_sizing_function.grid_size()));
+            }
         }
-        row_repeat_count = max(1, static_cast<int>(get_free_space_y(box) / sum_of_grid_track_sizes));
+        row_count = max(1, static_cast<int>(get_free_space_y(box) / sum_of_grid_track_sizes));
 
         // The auto-fit keyword behaves the same as auto-fill, except that after grid item placement any
         // empty repeated tracks are collapsed. An empty track is one with no in-flow grid items placed into
@@ -140,7 +168,7 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const
         // floor the track size to a UA-specified value to avoid division by zero. It is suggested that this
         // floor be 1px.
     }
-    auto occupation_grid = OccupationGrid(column_repeat_count * box.computed_values().grid_template_columns().meta_grid_track_sizes().size(), row_repeat_count * box.computed_values().grid_template_rows().meta_grid_track_sizes().size());
+    auto occupation_grid = OccupationGrid(column_count, row_count);
 
     // https://drafts.csswg.org/css-grid/#auto-placement-algo
     // 8.5. Grid Item Placement Algorithm
@@ -594,19 +622,67 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const
     // - A flexible sizing function (<flex>).
 
     // The grid sizing algorithm defines how to resolve these sizing constraints into used track sizes.
-    for (int x = 0; x < column_repeat_count; ++x) {
-        for (auto& meta_grid_track_size : box.computed_values().grid_template_columns().meta_grid_track_sizes())
-            m_grid_columns.append({ meta_grid_track_size.min_grid_track_size(), meta_grid_track_size.max_grid_track_size() });
+    for (auto const& track_in_list : grid_template_columns.track_list()) {
+        auto repeat_count = (track_in_list.is_repeat() && track_in_list.repeat().is_default()) ? track_in_list.repeat().repeat_count() : 1;
+        if (track_in_list.is_repeat()) {
+            if (track_in_list.repeat().is_auto_fill() || track_in_list.repeat().is_auto_fit())
+                repeat_count = column_count;
+        }
+        for (auto _ = 0; _ < repeat_count; _++) {
+            switch (track_in_list.type()) {
+            case CSS::ExplicitGridTrack::Type::MinMax:
+                m_grid_columns.append({ track_in_list.minmax().min_grid_size(), track_in_list.minmax().max_grid_size() });
+                break;
+            case CSS::ExplicitGridTrack::Type::Repeat:
+                for (auto& explicit_grid_track : track_in_list.repeat().grid_track_size_list().track_list()) {
+                    auto track_sizing_function = explicit_grid_track;
+                    if (track_sizing_function.is_minmax())
+                        m_grid_columns.append({ track_sizing_function.minmax().min_grid_size(), track_sizing_function.minmax().max_grid_size() });
+                    else
+                        m_grid_columns.append({ track_sizing_function.grid_size(), track_sizing_function.grid_size() });
+                }
+                break;
+            case CSS::ExplicitGridTrack::Type::Default:
+                m_grid_columns.append({ track_in_list.grid_size(), track_in_list.grid_size() });
+                break;
+            default:
+                VERIFY_NOT_REACHED();
+            }
+        }
     }
-    for (int x = 0; x < row_repeat_count; ++x) {
-        for (auto& meta_grid_track_size : box.computed_values().grid_template_rows().meta_grid_track_sizes())
-            m_grid_rows.append({ meta_grid_track_size.min_grid_track_size(), meta_grid_track_size.max_grid_track_size() });
+    for (auto const& track_in_list : grid_template_rows.track_list()) {
+        auto repeat_count = (track_in_list.is_repeat() && track_in_list.repeat().is_default()) ? track_in_list.repeat().repeat_count() : 1;
+        if (track_in_list.is_repeat()) {
+            if (track_in_list.repeat().is_auto_fill() || track_in_list.repeat().is_auto_fit())
+                repeat_count = row_count;
+        }
+        for (auto _ = 0; _ < repeat_count; _++) {
+            switch (track_in_list.type()) {
+            case CSS::ExplicitGridTrack::Type::MinMax:
+                m_grid_rows.append({ track_in_list.minmax().min_grid_size(), track_in_list.minmax().max_grid_size() });
+                break;
+            case CSS::ExplicitGridTrack::Type::Repeat:
+                for (auto& explicit_grid_track : track_in_list.repeat().grid_track_size_list().track_list()) {
+                    auto track_sizing_function = explicit_grid_track;
+                    if (track_sizing_function.is_minmax())
+                        m_grid_rows.append({ track_sizing_function.minmax().min_grid_size(), track_sizing_function.minmax().max_grid_size() });
+                    else
+                        m_grid_rows.append({ track_sizing_function.grid_size(), track_sizing_function.grid_size() });
+                }
+                break;
+            case CSS::ExplicitGridTrack::Type::Default:
+                m_grid_rows.append({ track_in_list.grid_size(), track_in_list.grid_size() });
+                break;
+            default:
+                VERIFY_NOT_REACHED();
+            }
+        }
     }
 
     for (int column_index = m_grid_columns.size(); column_index < occupation_grid.column_count(); column_index++)
-        m_grid_columns.append({ CSS::GridTrackSize::make_auto(), CSS::GridTrackSize::make_auto() });
+        m_grid_columns.append({ CSS::GridSize::make_auto(), CSS::GridSize::make_auto() });
     for (int row_index = m_grid_rows.size(); row_index < occupation_grid.row_count(); row_index++)
-        m_grid_rows.append({ CSS::GridTrackSize::make_auto(), CSS::GridTrackSize::make_auto() });
+        m_grid_rows.append({ CSS::GridSize::make_auto(), CSS::GridSize::make_auto() });
 
     // https://www.w3.org/TR/css-grid-2/#algo-overview
     // 12.1. Grid Sizing Algorithm
@@ -695,16 +771,16 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const
         switch (grid_column.min_track_sizing_function.type()) {
         // - A fixed sizing function
         // Resolve to an absolute length and use that size as the track’s initial base size.
-        case CSS::GridTrackSize::Type::Length:
+        case CSS::GridSize::Type::Length:
             if (!grid_column.min_track_sizing_function.length().is_auto())
                 grid_column.base_size = grid_column.min_track_sizing_function.length().to_px(box);
             break;
-        case CSS::GridTrackSize::Type::Percentage:
+        case CSS::GridSize::Type::Percentage:
             grid_column.base_size = grid_column.min_track_sizing_function.percentage().as_fraction() * box_state.content_width();
             break;
         // - An intrinsic sizing function
         // Use an initial base size of zero.
-        case CSS::GridTrackSize::Type::FlexibleLength:
+        case CSS::GridSize::Type::FlexibleLength:
             break;
         default:
             VERIFY_NOT_REACHED();
@@ -714,7 +790,7 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const
         switch (grid_column.max_track_sizing_function.type()) {
         // - A fixed sizing function
         // Resolve to an absolute length and use that size as the track’s initial growth limit.
-        case CSS::GridTrackSize::Type::Length:
+        case CSS::GridSize::Type::Length:
             if (!grid_column.max_track_sizing_function.length().is_auto())
                 grid_column.growth_limit = grid_column.max_track_sizing_function.length().to_px(box);
             else
@@ -722,12 +798,12 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const
                 // Use an initial growth limit of infinity.
                 grid_column.growth_limit = -1;
             break;
-        case CSS::GridTrackSize::Type::Percentage:
+        case CSS::GridSize::Type::Percentage:
             grid_column.growth_limit = grid_column.max_track_sizing_function.percentage().as_fraction() * box_state.content_width();
             break;
         // - A flexible sizing function
         // Use an initial growth limit of infinity.
-        case CSS::GridTrackSize::Type::FlexibleLength:
+        case CSS::GridSize::Type::FlexibleLength:
             grid_column.growth_limit = -1;
             break;
         default:
@@ -746,16 +822,16 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const
         switch (grid_row.min_track_sizing_function.type()) {
         // - A fixed sizing function
         // Resolve to an absolute length and use that size as the track’s initial base size.
-        case CSS::GridTrackSize::Type::Length:
+        case CSS::GridSize::Type::Length:
             if (!grid_row.min_track_sizing_function.length().is_auto())
                 grid_row.base_size = grid_row.min_track_sizing_function.length().to_px(box);
             break;
-        case CSS::GridTrackSize::Type::Percentage:
+        case CSS::GridSize::Type::Percentage:
             grid_row.base_size = grid_row.min_track_sizing_function.percentage().as_fraction() * box_state.content_height();
             break;
         // - An intrinsic sizing function
         // Use an initial base size of zero.
-        case CSS::GridTrackSize::Type::FlexibleLength:
+        case CSS::GridSize::Type::FlexibleLength:
             break;
         default:
             VERIFY_NOT_REACHED();
@@ -765,7 +841,7 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const
         switch (grid_row.max_track_sizing_function.type()) {
         // - A fixed sizing function
         // Resolve to an absolute length and use that size as the track’s initial growth limit.
-        case CSS::GridTrackSize::Type::Length:
+        case CSS::GridSize::Type::Length:
             if (!grid_row.max_track_sizing_function.length().is_auto())
                 grid_row.growth_limit = grid_row.max_track_sizing_function.length().to_px(box);
             else
@@ -773,12 +849,12 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const
                 // Use an initial growth limit of infinity.
                 grid_row.growth_limit = -1;
             break;
-        case CSS::GridTrackSize::Type::Percentage:
+        case CSS::GridSize::Type::Percentage:
             grid_row.growth_limit = grid_row.max_track_sizing_function.percentage().as_fraction() * box_state.content_height();
             break;
         // - A flexible sizing function
         // Use an initial growth limit of infinity.
-        case CSS::GridTrackSize::Type::FlexibleLength:
+        case CSS::GridSize::Type::FlexibleLength:
             grid_row.growth_limit = -1;
             break;
         default:
@@ -940,7 +1016,9 @@ void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const
     // The auto-fit keyword behaves the same as auto-fill, except that after grid item placement any
     // empty repeated tracks are collapsed. An empty track is one with no in-flow grid items placed into
     // or spanning across it. (This can result in all tracks being collapsed, if they’re all empty.)
-    if (box.computed_values().grid_template_columns().is_auto_fit()) {
+    if (grid_template_columns.track_list().size() == 1
+        && grid_template_columns.track_list().first().is_repeat()
+        && grid_template_columns.track_list().first().repeat().is_auto_fit()) {
         auto idx = 0;
         for (auto& grid_column : m_grid_columns) {
             // A collapsed track is treated as having a fixed track sizing function of 0px, and the gutters on
diff --git a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.h b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.h
index 85a14b207bf..2d43271e681 100644
--- a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.h
+++ b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.h
@@ -27,8 +27,8 @@ private:
     bool is_auto_positioned_track(CSS::GridTrackPlacement const&, CSS::GridTrackPlacement const&) const;
 
     struct GridTrackSizeConstraints {
-        CSS::GridTrackSize min_track_sizing_function;
-        CSS::GridTrackSize max_track_sizing_function;
+        CSS::GridSize min_track_sizing_function;
+        CSS::GridSize max_track_sizing_function;
         float base_size { 0 };
         float growth_limit { 0 };
         float space_to_distribute { 0 };