Skip to content

Use flex body layout to avoid CLS in customized themes#3931

Closed
guaca wants to merge 1 commit into
mainfrom
feature/fix-cls-body-grid
Closed

Use flex body layout to avoid CLS in customized themes#3931
guaca wants to merge 1 commit into
mainfrom
feature/fix-cls-body-grid

Conversation

@guaca

@guaca guaca commented Jun 10, 2026

Copy link
Copy Markdown

PR Summary:

Replaces Dawn's body-level grid layout with a flex column layout so the main content-for-layout area grows by selector instead of relying on its position as the third direct child of <body>. This makes the layout more resilient for customized themes and prevents severe CLS when merchants add, remove, or reorder body-level elements.

Why are these changes introduced?

Dawn currently lays out the page with:

body {
  display: grid;
  grid-template-rows: auto auto 1fr auto;
}

This layout assumes that the page structure always maps cleanly to four body-level rows, with content-for-layout occupying the 1fr row. In practice, many merchant themes are customized from Dawn and may add, remove, or move direct children of <body> such as announcement bars, submenu containers, app-injected elements, drawers, or other custom markup.

When content-for-layout is not the element that receives the 1fr grid row, a different body child can be expanded during page load. For example, an announcement bar or submenu can temporarily take the available viewport height before the main content finishes rendering. Once the intended content loads and the layout recalculates, the page shifts significantly, causing poor CLS.

Our team has fixed this issue several times while working directly with merchants. The recurring fix has been to move away from the body grid row-index assumption and use the same flexbox-based approach used by Horizon.

What approach did you take?

This change updates the body layout from CSS Grid to Flexbox:

body {
  display: flex;
  flex-direction: column;
  min-height: 100%;
}

It then explicitly makes the semantic main content container responsible for filling the available page height:

.content-for-layout {
  flex: 1;
  display: flex;
  flex-direction: column;
}

.content-for-layout > .shopify-section:last-child {
  flex-grow: 1;
}

This removes the dependency on content-for-layout being a specific direct child of <body>. Instead, the element that should grow is targeted directly by class.

Expected impact

  • Reduces or eliminates CLS caused by the wrong body child receiving the 1fr row during page load.
  • Makes Dawn more resilient to common merchant customizations that alter body-level markup.
  • Aligns Dawn's page-height strategy with Horizon's flexbox approach.
  • Expected to have no visible impact for stores using the default Dawn structure, aside from improved layout stability.
  • Improves the upgrade path for customized merchant themes by avoiding a fragile child-index-based layout assumption.

Visual impact on existing themes

No intentional visual change is expected for unmodified Dawn layouts.

Other considerations

This change intentionally avoids relying on DOM child order for page-height behavior. The content-for-layout element remains the source of truth for the expanding content region.

The added .content-for-layout > .shopify-section:last-child { flex-grow: 1; } rule preserves the ability for the last rendered section inside the main content area to fill remaining vertical space when the page content is shorter than the viewport.

Decision log

# Decision Alternatives Rationale Downsides
1 Replace body grid with body flex column. Keep the current grid and adjust row placement. Flexbox better matches the desired behavior: stack body children vertically and let the intended main content area grow explicitly. Slightly changes the underlying page layout primitive, so regression testing should cover common templates and short pages.
2 Target .content-for-layout directly for growth. Continue depending on body child position. Directly targets the semantic layout region that should fill available space, making the layout resilient to merchant customizations. Custom CSS that relied on the body grid behavior may need review.
3 Grow the last Shopify section inside content-for-layout. Only set flex: 1 on content-for-layout. Preserves full-height behavior for short content and aligns with the flex layout approach used in Horizon. Last section can receive remaining vertical space, which should be validated across common templates.

Testing steps/scenarios

  1. Use a Dawn-derived theme where a direct body child is inserted before content-for-layout, causing content-for-layout to no longer be the third relevant body child.
  2. Load the page using the current production grid layout.
  3. Capture CLS in Chrome DevTools Performance or Lighthouse.
  4. Apply this branch.
  5. Repeat the measurement and compare CLS

Expected results

Before:

image image

After:

No CLS issues are observed
image

Demo links

Checklist

Replace the body grid layout with a flex column layout and make .content-for-layout grow explicitly. This avoids relying on the main content being the Dawn themes to expand the wrong element during page load and trigger layout shifts.
@guaca guaca closed this Jun 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant