{"id":158,"date":"2026-10-17T05:01:03","date_gmt":"2026-10-17T05:01:03","guid":{"rendered":"https:\/\/webdevpuneet.com\/blog\/?p=158"},"modified":"2026-10-17T05:01:06","modified_gmt":"2026-10-17T05:01:06","slug":"css-subgrid-explained-8-real-layout-problems-it-solves","status":"publish","type":"post","link":"https:\/\/webdevpuneet.com\/blog\/css-subgrid-explained-8-real-layout-problems-it-solves\/","title":{"rendered":"CSS Subgrid Explained: 8 Real Layout Problems It Solves"},"content":{"rendered":"<p><!--\n================================================================\n  POST TITLE (paste into the WordPress title field \u2014 it becomes the page H1)\n  CSS Subgrid Explained: 8 Real Layout Problems It Solves\n\n  SEO TITLE (55 chars, target 50\u201360, max 60 \u2014 \"Optimize SEO\" panel \u2192 SEO TITLE)\n  CSS Subgrid Explained: 8 Real Layout Problems It Solves\n\n  SEO DESCRIPTION (155 chars, target 150\u2013160, max 160 \u2014 \"Optimize SEO\" panel \u2192 SEO DESCRIPTION)\n  CSS subgrid explained with 8 live demos: aligned card footers, form columns, nested 12-column layouts, full-bleed blocks, table rows and display: contents.\n\n  HOW TO PASTE: click the first line after this comment, press Ctrl+Shift+End, Ctrl+C.\n  In WordPress click \"Type \/ to choose a block\", then Ctrl+V.\n  Fallback: \u22ee menu \u2192 Code editor \u2192 paste.\n================================================================\n--><\/p>\n\n\n<p class=\"wp-block-paragraph\">Subgrid is the smallest CSS Grid feature with the biggest &#8220;oh, <em>that&#8217;s<\/em> what was wrong&#8221; moment attached to it. Every developer who has built a row of cards has hit the problem it solves: three cards, three different title lengths, and three buttons that refuse to line up. For years the answers were fixed heights, JavaScript measuring loops, or quietly accepting the mess. Subgrid fixes it with one declaration \u2014 <code>grid-template-rows: subgrid<\/code> \u2014 and the idea behind it is simpler than the name suggests: <strong>a nested grid can borrow its parent&#8217;s tracks instead of inventing its own<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The 8 demos below are all live and interactive, and most of them have a toggle that switches subgrid off so you can see the exact layout bug it was preventing. That contrast is the whole point \u2014 subgrid is hard to appreciate in isolation and obvious the moment you turn it off.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Every embed also has a <strong>Fork &amp; Edit<\/strong> button that opens the exact snippet, already running, in <a href=\"https:\/\/webdevpuneet.com\/ui-snippets\/mycode\/\">My Code<\/a> \u2014 webdevpuneet&#8217;s free in-browser editor. Change a track, delete a line, break it on purpose \u2014 that&#8217;s a faster way to build real intuition than any explanation.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The one-sentence rule<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>.parent {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  grid-template-rows: auto 1fr auto;\n}\n\n.child {\n  grid-row: span 3;            \/* claim the rows you want to share *\/\n  display: grid;\n  grid-template-rows: subgrid; \/* then adopt them instead of making new ones *\/\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Normally, a nested grid is a sealed box: it divides its <em>own<\/em> width and height into fresh tracks, and its children have no idea the outer grid exists. <code>subgrid<\/code> removes that wall for one axis. The child still has to claim a range of the parent&#8217;s tracks with <code>grid-row<\/code> or <code>grid-column<\/code> \u2014 subgrid doesn&#8217;t guess \u2014 but from then on, its children are laid out on the parent&#8217;s lines. That is the entire feature. Every demo below is one consequence of it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">1. Card Footers That Actually Line Up<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Three cards, three different title and body lengths. Toggle subgrid off and watch the buttons scatter.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/14\/1.html\" style=\"width:100%;height:480px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"Card Footer Alignment Subgrid Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.cards { grid-template-rows: auto 1fr auto; }\n\n.card {\n  grid-row: span 3;\n  display: grid;\n  grid-template-rows: subgrid;\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This is the canonical case, and worth being precise about why it works. The parent declares three row bands \u2014 title, body, footer \u2014 and every card spans all three. Because each card&#8217;s rows <em>are<\/em> the parent&#8217;s rows, the title band is sized once by the tallest title across all cards, and every card&#8217;s title occupies that same band. The <code>1fr<\/code> middle row absorbs the leftover height, which pushes every footer to the same line. With subgrid off, each card sizes its own three rows from its own content, so the buttons land wherever that card&#8217;s text happens to end.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> pricing tables, feature card rows, product grids \u2014 anywhere repeated cards need internal alignment, not just equal outer heights. <strong>Tip:<\/strong> equal card <em>heights<\/em> were never the hard part \u2014 <code>align-items: stretch<\/code> has done that since Flexbox. Subgrid solves the harder problem: equal positions for the content <em>inside<\/em> those cards.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">2. A Form Where Every Input Lines Up<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Each row is its own component with its own label. Subgrid makes them share the form&#8217;s two columns.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/14\/2.html\" style=\"width:100%;height:480px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"Form Label Alignment Subgrid Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.form { grid-template-columns: max-content 1fr; }\n\n.row {\n  grid-column: span 2;\n  display: grid;\n  grid-template-columns: subgrid;\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The label column is <code>max-content<\/code>, which means &#8220;as wide as the widest label&#8221; \u2014 but the crucial detail is <em>which<\/em> labels get measured. With subgrid, all four rows share one column, so the widest label in the entire form sets the width and every input starts at the same x position. Turn it off and each row re-declares the same <code>max-content 1fr<\/code> template, which looks identical in the CSS but measures only that one row&#8217;s label \u2014 so every input starts somewhere different. This is the classic case where you&#8217;d otherwise hardcode <code>width: 140px<\/code> on labels and hope nobody translates the page.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> settings panels, checkout forms, any form where each field is a reusable row component rather than one flat list of elements. <strong>Tip:<\/strong> this pattern is what finally makes label-beside-input layouts safe for localisation \u2014 German or Finnish labels simply widen the shared column instead of breaking a hardcoded width.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">3. A Nested Component on the Page Grid<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The purple panel is a child of the layout, not a sibling of the columns. Subgrid lets its own children snap to the page&#8217;s 12 columns anyway.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/14\/3.html\" style=\"width:100%;height:480px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"Nested Component 12 Column Subgrid Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.page  { grid-template-columns: repeat(12, 1fr); }\n\n.panel {\n  grid-column: 1 \/ 13;\n  display: grid;\n  grid-template-columns: subgrid;\n}\n.panelA { grid-column: 1 \/ 9; }    \/* real page columns, not panel columns *\/\n.panelB { grid-column: 9 \/ 13; }<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Design systems talk about &#8220;the 12-column grid&#8221; as if it applies to the whole page, but before subgrid it only ever applied to direct children of the grid container. Any component you nested \u2014 a section, a card, a wrapper div \u2014 started a brand-new coordinate system, and <code>grid-column: 9 \/ 13<\/code> inside it meant something completely different from the same declaration outside it. Subgrid makes the column lines genuinely global: the ruler at the top of this demo is a sibling of the panel, yet the panel&#8217;s children align to it exactly.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> design-system layouts where components at any depth must respect one shared column grid. <strong>Tip:<\/strong> name your lines on the parent (<code>[content-start]<\/code>, <code>[full-end]<\/code>) \u2014 named lines pass through subgrid too, which makes nested components far more readable than counting numbers.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">4. Stat Tiles With Aligned Bands<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Four KPI tiles: label, number, delta note. One label wraps to two lines and one delta is missing entirely \u2014 exactly the cases that break a flexbox row.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/14\/4.html\" style=\"width:100%;height:480px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"Stat Tile Band Alignment Subgrid Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.tiles { grid-template-rows: auto auto auto; }\n\n.tile {\n  grid-row: span 3;\n  display: grid;\n  grid-template-rows: subgrid;\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The two awkward tiles here are deliberate. One label wraps to two lines, which in a flexbox row pushes that tile&#8217;s big number down out of alignment with the other three. One tile has no delta note at all, which in flexbox simply ends short. Subgrid handles both without special-casing: the label band is as tall as the tallest label, so the wrapped one doesn&#8217;t push anything; and the missing delta leaves an empty cell in a band that still exists, so the tile keeps its shape.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> dashboards and KPI rows, where the numbers scanning cleanly across a line is most of the value of the layout. <strong>Tip:<\/strong> reach for <code>align-self<\/code> inside the bands \u2014 <code>align-self: end<\/code> on the delta pins it to the bottom of its band regardless of how tall that band grows.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">5. Full-Bleed From Inside a Nested Section<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The figure is nested inside a <code>&lt;section&gt;<\/code>, two levels below the article grid. Subgrid is what lets it still reach the outer full-bleed column lines.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/14\/5.html\" style=\"width:100%;height:480px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"Full Bleed Nested Section Subgrid Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.article {\n  grid-template-columns:\n    &#91;full-start] 1fr &#91;content-start] minmax(0, 24em) &#91;content-end] 1fr &#91;full-end];\n}\n\n.chapter {\n  grid-column: full-start \/ full-end;\n  display: grid;\n  grid-template-columns: subgrid;   \/* the named lines come with it *\/\n}\n.chapter &gt; .bleed { grid-column: full-start \/ full-end; }<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The &#8220;full-bleed inside a readable column&#8221; layout is well known, and it works fine for <em>direct<\/em> children of the article grid. The moment real content gets wrapped \u2014 in a <code>&lt;section&gt;<\/code>, a CMS block, a React component \u2014 the outer column lines become unreachable, and the usual workaround is the negative-margin hack (<code>margin-inline: calc(50% - 50vw)<\/code>) with all its scrollbar-width bugs. Subgrid passes the named lines down intact, so a figure three levels deep can still say <code>grid-column: full-start \/ full-end<\/code> and mean it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> article and documentation layouts where content is authored in nested blocks rather than one flat list. <strong>Tip:<\/strong> subgrid inherits the parent&#8217;s <em>line names<\/em> as well as its track sizes, which is what makes this readable \u2014 without names you&#8217;d be counting anonymous line numbers through every level of nesting.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">6. Table Alignment Without a Table<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Each row is an independent component \u2014 its own card, its own border, its own hover state. Subgrid keeps their columns aligned as if they were one table.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/14\/6.html\" style=\"width:100%;height:480px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"Table Column Alignment Subgrid Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.list { grid-template-columns: 2fr 1fr max-content max-content; }\n\n.row {\n  grid-column: 1 \/ -1;\n  display: grid;\n  grid-template-columns: subgrid;\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This is the layout that genuinely had no good answer before. A real <code>&lt;table&gt;<\/code> aligns columns automatically but makes styling individual rows as cards painful. A grid of flat cells aligns perfectly but leaves no element to put a border or hover state on. Subgrid gives you both: each row is a real box you can style however you like, and its cells still sit on the list&#8217;s shared columns. Toggle it off and you can see the difference clearly \u2014 every row re-declares the exact same <code>2fr 1fr max-content max-content<\/code> template, but each one now measures its own content, so nothing agrees with the header.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> data lists that need per-row interactivity \u2014 selectable rows, expandable rows, drag handles, per-row hover and focus states. <strong>Tip:<\/strong> if the data is genuinely tabular, still use a real <code>&lt;table&gt;<\/code> for the semantics and apply <code>display: grid<\/code> plus subgrid to the rows \u2014 you gain the styling freedom, but test with a screen reader, because some browsers drop the table semantics once a table element&#8217;s <code>display<\/code> changes. Adding explicit <code>role=\"table\"<\/code>, <code>role=\"row\"<\/code> and <code>role=\"cell\"<\/code> keeps the navigation intact either way.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">7. Subgrid Inherits the Parent&#8217;s Gap<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Drag the slider to change the parent&#8217;s gap, then override the child&#8217;s own gap and watch the two come apart.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/14\/7.html\" style=\"width:100%;height:480px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"Subgrid Gap Inheritance Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.parent { gap: 14px; }\n\n.child {\n  grid-template-columns: subgrid;\n  \/* no gap declared \u2014 inherits the parent's 14px *\/\n}\n\n.child.noGap { gap: 0; }   \/* override: tracks still align, spacing changes *\/<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This one catches people out, so it&#8217;s worth seeing directly. A subgrid uses its parent&#8217;s <code>gap<\/code> by default \u2014 it has to, or the tracks would stop lining up. But you <em>can<\/em> override it, and the result is subtler than it first looks: the column positions still match the parent exactly, because those are fixed by the parent&#8217;s tracks. What changes is only the space the child leaves between its own items. Dragging the slider with the override on makes this obvious \u2014 the outer spacing responds, the inner spacing doesn&#8217;t.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> understanding why your subgrid &#8220;nearly&#8221; lines up \u2014 a stray <code>gap<\/code> on the child is one of the two most common causes. <strong>Tip:<\/strong> the other common cause is padding. A subgrid&#8217;s own <code>padding<\/code> shifts its contents <em>inside<\/em> the tracks without moving the tracks, so a padded subgrid row looks misaligned even though its grid is correct.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">8. Subgrid vs display: contents<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Both make a wrapper&#8217;s children line up on the outer grid. Only one of them keeps the wrapper itself.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/14\/8.html\" style=\"width:100%;height:480px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"Subgrid vs display contents Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.rowSub      { display: grid; grid-template-columns: subgrid; }  \/* box survives *\/\n.rowContents { display: contents; }                              \/* box disappears *\/<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>display: contents<\/code> was the pre-subgrid workaround, and for pure alignment it does work: the wrapper is removed from the box tree, so its children become direct grid items of the outer grid. The cost is the wrapper itself. In this demo both rows carry <em>identical<\/em> border, background and padding declarations \u2014 the subgrid row renders them, the <code>display: contents<\/code> row silently drops every one, because there is no longer a box to paint them on. It also historically removed the element from the accessibility tree in several browsers, which made it risky on semantic elements.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> knowing which to reach for \u2014 <code>display: contents<\/code> when the wrapper is purely structural and you never need to style it; subgrid whenever the wrapper is a real visual component. <strong>Tip:<\/strong> if you&#8217;re migrating old code, a <code>display: contents<\/code> row that you keep wanting to add a hover state or border to is exactly the signal to convert it to a subgrid.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Common pitfalls<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Forgetting to span the tracks first.<\/strong> <code>grid-template-rows: subgrid<\/code> does nothing useful on its own \u2014 the element must also claim a range with <code>grid-row: span 3<\/code> (or explicit line numbers). Subgrid adopts the tracks you claimed, and claiming none means adopting none.<\/li>\n\n\n\n<li><strong>Expecting both axes at once.<\/strong> <code>subgrid<\/code> is set per axis. A child can be a subgrid for columns while defining its own rows normally \u2014 and often should be. Demo #2 subgrids only columns; demo #1 only rows.<\/li>\n\n\n\n<li><strong>A stray gap or padding on the child.<\/strong> Demo #7 shows both. The tracks still align, but the contents sit at the wrong offset inside them, which reads as &#8220;subgrid isn&#8217;t working&#8221; when the grid is actually fine.<\/li>\n\n\n\n<li><strong>Assuming it fixes unequal card heights.<\/strong> That was never the problem \u2014 <code>align-items: stretch<\/code> already did that. Subgrid aligns the content <em>inside<\/em> the cards, which is the part flexbox genuinely cannot do.<\/li>\n\n\n\n<li><strong>Reaching for <code>display: contents<\/code> out of habit.<\/strong> Demo #8 shows the cost: identical border and background declarations render on the subgrid row and vanish on the <code>contents<\/code> one.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Frequently asked questions<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Can I use subgrid in production yet?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Yes \u2014 subgrid is supported in all current major browsers (Firefox since 71, Safari since 16, Chrome and Edge since 117). It also degrades unusually gracefully: a browser that doesn&#8217;t understand <code>grid-template-rows: subgrid<\/code> ignores that one declaration and the nested element stays a normal grid, so you get the unaligned-but-functional version rather than a broken page. That makes it a reasonable progressive enhancement even where you can&#8217;t guarantee support.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">What&#8217;s the difference between subgrid and just using one big flat grid?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A flat grid aligns everything perfectly but leaves you with no wrapper elements \u2014 no per-card border, no per-row hover state, no component boundaries that match your markup. Subgrid keeps the wrappers (and therefore the components, the semantics and the styling hooks) while still sharing one set of tracks. Demo #6 is the clearest illustration of that trade-off.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Does subgrid work on both rows and columns at the same time?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Yes, but you declare each axis separately: <code>grid-template-columns: subgrid; grid-template-rows: subgrid;<\/code>. You can also mix \u2014 subgrid one axis and define your own tracks on the other, which is often what you actually want. A card row usually subgrids rows only; a form row usually subgrids columns only.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Why do my subgrid items still look misaligned?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Almost always one of three things: the child didn&#8217;t span any parent tracks (so there was nothing to adopt), the child declares its own <code>gap<\/code>, or the child has <code>padding<\/code> that offsets its contents inside otherwise-correct tracks. Demo #7 isolates the gap and padding cases specifically \u2014 they&#8217;re the ones that look most like a broken grid when the grid is fine.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Can subgrid be nested more than one level deep?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Yes. A subgrid can itself contain another subgrid, and the tracks pass all the way down as long as each level spans a range and declares <code>subgrid<\/code> for that axis. Named lines pass down through every level too, which is what keeps deeply nested layouts (demo #5) readable instead of turning into line-number arithmetic.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Every demo above ships its full HTML, CSS and JS in the <strong>HTML \/ CSS \/ JS<\/strong> tabs in its own top bar. The fastest way to internalise this is to click <strong>Fork &amp; Edit<\/strong> on demo #1, delete the <code>grid-row: span 3<\/code> line but leave <code>grid-template-rows: subgrid<\/code> in place, and watch the alignment collapse \u2014 a subgrid that spans no parent tracks has nothing to adopt. That single experiment explains more than any diagram. Once you&#8217;ve got a version you like, save it to <a href=\"https:\/\/webdevpuneet.com\/ui-snippets\/mycode\/\">My Code<\/a> and it&#8217;s yours to keep, tweak further, or drop into a real project.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Want to go deeper than a single embed? The <a href=\"https:\/\/webdevpuneet.com\/css-playground\/\">CSS Playground<\/a> is webdevpuneet&#8217;s full in-browser CSS sandbox \u2014 a real editor with instant preview, built for exactly this kind of edit-and-see learning, without the eight-tab constraint of a blog post.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Subgrid is the smallest CSS Grid feature with the biggest &#8220;oh, that&#8217;s what was wrong&#8221; moment attached to it. Every developer who has built a row of cards has hit the problem it solves: three cards, three different title lengths, and three buttons that refuse to line up. For years the answers were fixed heights, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":159,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_et_pb_use_builder":"","_et_pb_old_content":"","_et_gb_content_width":"","advanced_seo_description":"CSS subgrid explained with 8 live demos: aligned card footers, form columns, nested 12-column layouts, full-bleed blocks, table rows and display: contents.","jetpack_seo_html_title":"CSS Subgrid Explained: 8 Real Layout Problems It Solves","jetpack_seo_noindex":false,"jetpack_seo_schema_type":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"Still struggling to align elements across nested CSS layouts? CSS Subgrid can fix that.\n\nWith `grid-template-rows: subgrid` and `grid-template-columns: subgrid`, nested elements can share their parent grid tracks\u2014without hacks, fixed heights, or extra wrapper complexity.\n\nHere are 8 real-world problems CSS Subgrid solves:\n\n1. Aligning card titles, descriptions, and buttons\n2. Keeping card footers perfectly aligned\n3. Building consistent form layouts\n4. Creating nested 12-column layouts\n5. Designing full-bleed content sections\n6. Aligning elements across complex components\n7. Structuring table-like layouts with CSS Grid\n8. Understanding how `display: contents` fits into modern layouts\n\nThe best part? You can explore **8 practical demos** and see how each layout works in action.\n\nIf you're building modern, responsive interfaces, CSS Subgrid deserves a place in your toolkit.\n\n#CSS #WebDevelopment #FrontendDevelopment #CSSGrid #JavaScript\n","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[6,9],"tags":[],"class_list":["post-158","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-css","category-tutorials","et-has-post-format-content","et_post_format-et-post-format-standard"],"jetpack_publicize_connections":[],"jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/phrh5V-2y","jetpack_featured_media_url":"https:\/\/i0.wp.com\/webdevpuneet.com\/blog\/wp-content\/uploads\/2026\/10\/css-subgrid-explained-1200x630-1.jpg?fit=1200%2C630&ssl=1","_links":{"self":[{"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/posts\/158","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/comments?post=158"}],"version-history":[{"count":1,"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/posts\/158\/revisions"}],"predecessor-version":[{"id":160,"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/posts\/158\/revisions\/160"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/media\/159"}],"wp:attachment":[{"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/media?parent=158"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/categories?post=158"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/tags?post=158"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}