Skip to content

feat(grid, col): resolve breakpoints from config - #31518

Open
brandyscarney wants to merge 10 commits into
FW-7285-2-configfrom
FW-7285-3-grid-col
Open

brandyscarney wants to merge 10 commits into
FW-7285-2-configfrom
FW-7285-3-grid-col

Conversation

@brandyscarney

@brandyscarney brandyscarney commented Oct 1, 2026 •

Copy link
Copy Markdown
Member

Issue number: internal


What is the current behavior?

The previous PR in this stack added the screenBreakpoints config option and the @utils/breakpoints helpers, but nothing consumes them yet.

ion-grid gets its responsive padding and fixed widths purely from @media queries, with the thresholds compiled into the CSS. ion-col only expresses per-breakpoint values through breakpoint-suffixed properties (size-xs through size-xl, plus the order-* and offset-* equivalents). Neither reads the configured breakpoints, and neither can target xxl.

ion-grid also declared its own ION_GRID_BREAKPOINTS / IonGridBreakpoint (carrying a TODO(FW-7285)) rather than using the shared type.

What is the new behavior?

  • ion-grid resolves the active breakpoint in JavaScript and reflects it on the host as a screen-breakpoint attribute. It subscribes via onBreakpointChange in connectedCallback and unsubscribes in disconnectedCallback, so it re-renders when the screen crosses a configured threshold.
  • The grid Sass now emits every breakpoint rule twice: a @media copy scoped to :host(:not([screen-breakpoint])), and an attribute copy :host([screen-breakpoint="md"]). The attribute copy is what the config drives; the @media copy is the baseline before the component hydrates and when JavaScript never runs. This split is necessary because CSS media queries cannot read custom properties, so a var() cannot be used to change a breakpoint.
  • ion-col accepts an object of breakpoint values for size, order and offset: col.size = { xs: 12, md: 6 }. This is resolved against the configured breakpoints. xxl is added to this object. An empty string or null at a breakpoint resets that column to the default flex layout.
  • All suffixed properties are deprecated, but not removed. They still work and log a deprecation warning. If both forms are set for the same property, the object form wins and the suffixed ones are ignored with a warning. They will be removed in a future major release.
  • ION_GRID_BREAKPOINTS / IonGridBreakpoint are replaced by the shared ScreenBreakpoint.

Does this introduce a breaking change?

  • Yes
  • No

The size, order, and offset prop types have been widened from string | undefined to BreakpointMap<string | number | null> | number | string | undefined. The suffixed properties continue to work as before.

BREAKING.md has been updated under 10.x with migration guidance for these deprecations. The push/pull entry has also been moved from the Grid section to Col, where the properties are defined.

Other information

Preview

@brandyscarney
brandyscarney added this pull request to stack #31503 October 1, 2026 17:24
@vercel

vercel Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
ionic-framework Ready Ready Preview Oct 5, 2026 10:42pm UTC

Request Review

@github-actions github-actions Bot added the package: core @ionic/core package label Oct 1, 2026
@brandyscarney
brandyscarney marked this pull request as ready for review October 2, 2026 14:06
@brandyscarney
brandyscarney requested a review from a team as a code owner October 2, 2026 14:06
@brandyscarney
brandyscarney requested review from thetaPC and removed request for a team October 2, 2026 14:06

@ShaneK ShaneK left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks really good overall! Just a couple of things, mainly the grid and col holding onto a stale breakpoint after being removed and re-added, plus the deprecation warning firing once per column. The rest are small.

@Prop() fixed = false;

connectedCallback() {
this.unsubscribeBreakpoint = onBreakpointChange(() => forceUpdate(this));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
this.unsubscribeBreakpoint = onBreakpointChange(() => forceUpdate(this));
this.unsubscribeBreakpoint = onBreakpointChange(() => forceUpdate(this));
forceUpdate(this);

If a grid is removed and re-added after the screen crosses a breakpoint, it keeps the old screen-breakpoint, since reconnecting only re-subscribes. A fixed grid removed at 800px and re-added at 1300px stays 720px wide instead of 1140px, and a size-md="6" col taken from 1000px down to 600px the same way keeps its half width.

Before this the padding and widths came straight from media queries, so they couldn't go stale, and now nothing corrects them until the next breakpoint crossing. I think that could bite things like Vue's KeepAlive or Angular's detached views. Forcing an update after subscribing fixes both cases and the grid tests still pass with it. The col would need the same change.

@brandyscarney brandyscarney Oct 5, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: aaa5d7e

I also noticed there were no tests for screen-breakpoint on ion-col and added them: 61b4b9f

Comment thread core/src/components/col/col.tsx Outdated
// TODO(FW-7557): Remove these in v11.
// Keep track of which deprecation warnings have been printed so they are
// not repeated on every re-render or screen resize.
private hasWarnedDeprecatedProps = false;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since these flags are per instance, every column with a suffixed prop logs its own warning, so a grid with five size-md columns prints five of them. Most existing apps use the suffixed props, so I think anything with a few dozen columns is going to flood the console. Moving both flags to module scope gets it down to one per page, though the "warns that they are deprecated" spec would then need a way to reset them between tests.

Separately, was the plain HTML case considered? Once these are removed in v11 there's no way to set responsive sizes in markup, since the object form only works as a property.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a separate file for the deprecation warnings & now print the actual code that should be written: f4742cc

Separately, was the plain HTML case considered? Once these are removed in v11 there's no way to set responsive sizes in markup, since the object form only works as a property.

Honestly, no. I was just thinking that JS was required. Created FW-7822 to look into supporting this.

Comment thread BREAKING.md
Comment thread core/src/components/col/col.tsx Outdated
Comment thread core/src/components/grid/grid.tsx Outdated

This branch was successfully deployed

1 active deployment
Preview — 61b4b9f9 Deployed Oct 5, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

package: core @ionic/core package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants