Using Materialize CSS Grid as a Layout Contract for Responsive Card Rows
Learn why treating Materialize’s 12‑column grid as a layout contract—letting it own width and gutters while keeping styling inside cards—prevents uneven card rows on mobile and simplifies responsive design.
15 Feb 2026, 04:31 UTC

The problem: cards that look fine on desktop but break on phone
You have a row of three feature cards. On a wide screen they sit side‑by‑side, each with a title, paragraph and button. When you shrink the viewport to a phone width the cards stack, but the middle card ends up taller than the others, leaving uneven white space and a horizontal scrollbar. The cause isn’t the cards themselves; it’s that you added custom margins and widths to the card elements, fighting the framework’s grid.
Thesis
Treat Materialize’s 12‑column grid as the layout contract, not as decorative helper classes. Let the grid own width and gutters; keep all styling (padding, borders, background) inside the card component.
Anatomy of the grid in one pass
.container– centers content and applies responsive max‑widths (e.g., 960 px on large screens)..row– creates the gutter system by applying negative left/right margins equal to the column padding..col– supplies horizontal padding and the width fraction. Breakpoint prefixess,m,lmodify the width at small, medium and large widths.
The math works only when .col elements are direct children of a .row. Any extra wrapper around a column breaks the negative‑margin/gutter cancellation.
Worked example: three cards
<div class='container'>
<div class='row'>
<div class='col s12 m6 l4'>
<div class='card'>
<div class='card-content'>
<span class='card-title'>Feature A</span>
<p>Short description.</p>
</div>
<div class='card-action'>
<a href='#'>Learn more</a>
</div>
</div>
</div>
<div class='col s12 m6 l4'>
<div class='card'>
<div class='card-content'>
<span class='card-title'>Feature B</span>
<p>Short description.</p>
</div>
<div class='card-action'>
<a href='#'>Learn more</a>
</div>
</div>
</div>
<div class='col s12 m6 l4'>
<div class='card'>
<div class='card-content'>
<span class='card-title'>Feature C</span>
<p>Short description.</p>
</div>
<div class='card-action'>
<a href='#'>Learn more</a>
</div>
</div>
</div>
</div>
</div>
Behavior:
- Small (
s): each column iss12→ full‑width cards stacked vertically. - Medium (
m): first two columns arem6(50 % each) → they sit side‑by‑side; the third column wraps to a new row asm6but now occupies the full width of that row. - Large (
l): each column isl4(33.3 % each) → all three cards fit on one row.
All spacing, borders and backgrounds stay inside the .card element, so the grid’s width calculations remain unchanged.
Engineering rule: keep the grid pure
- Only
.colmay be a direct child of.row. If you need a wrapper for a background or border, place it inside the column. - Remove any ad‑hoc
margin,paddingorwidthrules from column children. Move those styles to the card component. - If you must override gutter size, do it via Sass variables (
$grid-col-spacing) rather than editing.rowdirectly.
Trade‑offs and limitations
- The grid controls width and gutters only. Equal‑height cards still require flexbox (
display:flexon the row) or a utility likeequal-heightfrom a custom stylesheet. - With a
m6layout you can get an “orphan” card on medium widths (two cards on the first row, one on the second). This is expected behavior; if you need a strict three‑column pattern at all sizes, choose a different breakpoint combination or use CSS grid. - Arbitrary column counts (e.g., 5 columns) or non‑12‑column designs fight the framework; consider CSS grid or flexbox for those cases.
- Visibility classes (
hide-on-small-only, etc.) only toggledisplay; hidden markup remains in the DOM and must not be relied upon for security boundaries.
Actionable audit
- Open the page in Chrome DevTools.
- Select the
.rowthat holds your cards. In the Styles pane verify it has negative left/right margins (e.g.,margin-left: -0.75rem; margin-right: -0.75rem;). - Confirm each direct child of that row has a class beginning with
coland no other width/margin rules. - Resize the viewport to 320 px, 768 px and 1200 px. Observe the card layout: full‑width → two‑up → three‑up, with no horizontal scrollbar.
- If you see unexpected gaps or overflow, search the CSS for rules targeting
.row> * or.col> * and move them inside the card component.
After the audit, recompile your Sass (if you use a build step) and verify the generated .container max‑widths match your design tokens. This keeps the layout contract clear and makes future upgrades painless.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.