ScreenshotNeo

BlogHow-to

How to Create a 12-Column CSS Grid Layout

Build a flexible 12-column CSS Grid, place content with lines and spans, and adapt the layout to smaller screens.

By the ScreenshotNeo team4 October 20267 min read

To create a 12-column CSS Grid, make the container a grid and define twelve equal tracks:

.container {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 1rem;
}

Place each child by grid line number or by span. For example, grid-column: 1 / 13 covers all twelve tracks: line 1 begins the first track, and line 13 follows the last. The grid is flexible, but the spans you assign determine how regions relate to one another. [MDN: CSS grid layout]

1. Build a basic 12-column page

Here is a complete example with a header and footer spanning the page, a three-column sidebar, and a nine-column main area:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Twelve-column CSS Grid</title>
  <style>
    * {
      box-sizing: border-box;
    }

    body {
      margin: 0;
      font-family: system-ui, sans-serif;
    }

    .layout {
      display: grid;
      grid-template-columns: repeat(12, 1fr);
      gap: 1.25rem;
      max-width: 72rem;
      margin-inline: auto;
      padding: 1.25rem;
    }

    .layout > * {
      padding: 1rem;
      background: #eef2f6;
    }

    header,
    footer {
      grid-column: 1 / 13;
    }

    aside {
      grid-column: 1 / 4;
    }

    main {
      grid-column: 4 / 13;
    }

    @media (max-width: 48rem) {
      .layout {
        grid-template-columns: repeat(12, minmax(0, 1fr));
        gap: 0.75rem;
      }

      aside {
        grid-column: 1 / 5;
      }

      main {
        grid-column: 5 / 13;
      }
    }

    @media (max-width: 36rem) {
      header,
      aside,
      main,
      footer {
        grid-column: 1 / 13;
      }
    }
  </style>
</head>
<body>
  <div class="layout">
    <header>Site header</header>
    <aside>Related links</aside>
    <main>Main article</main>
    <footer>Site footer</footer>
  </div>
</body>
</html>

The example uses a three-to-nine track split. That is a starting arrangement, not a rule: change the spans and gap to fit your content. The max width and padding keep the page content from stretching indefinitely and touching the viewport edge.

2. Understand tracks, lines, gaps, and spans

grid-template-columns: repeat(12, 1fr) creates twelve flexible column tracks. Each 1fr track receives an equal share of the available track space after gaps are accounted for. The tracks sit between numbered grid lines, so twelve columns have thirteen boundary lines.

CSS Effect When to use it
grid-column: 1 / 13 Start at line 1 and end at line 13; span all twelve columns. Full-width regions.
grid-column: 1 / 4 Span columns 1 through 3. A left region covering three tracks.
grid-column: 4 / 13 Span columns 4 through 12. A main region beside a three-track sidebar.
grid-column: span 6 Request a span of six tracks; auto-placement chooses the start. Repeated cards or items that flow in the grid.
gap: 1rem Sets the spacing between rows and columns. Consistent gutters. Use column-gap and row-gap if they need different values.

Grid gaps are part of the layout calculation. With twelve tracks and eleven column gaps, the available width for the tracks is the grid’s inner width minus those gaps. If the overall layout feels too wide, reduce the gap or container width as well as adjusting spans.

Use spans when the start position is flexible

For an item whose exact starting line is unimportant, use a span and let auto-placement position it:

.card {
  grid-column: span 4;
}

Three such cards can fit across twelve tracks when the container is wide enough. If an item needs a fixed position, specify both start and end lines instead.

Name the grid lines for repeated patterns

Named lines can make a layout easier to read when several rules refer to the same boundaries:

.wrapper {
  display: grid;
  grid-template-columns: repeat(12, [col-start] 1fr);
  gap: 1.25rem;
}

.content {
  grid-column: col-start 4 / span 9;
}

The repeated name is indexed, so col-start 4 refers to the fourth occurrence. Use names if they clarify the layout; numbered lines are often simpler for a small grid. [MDN: common grid layouts]

3. Make the grid responsive

A twelve-column grid can remain twelve columns on tablet and desktop while items change their spans. At a narrower width, you can either change those spans or redefine the grid. Pick breakpoints based on when your content stops fitting, not on a device label.

Keep twelve tracks and change item spans

.layout {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 1rem;
}

.sidebar {
  grid-column: 1 / 4;
}

.content {
  grid-column: 4 / 13;
}

@media (max-width: 48rem) {
  .sidebar {
    grid-column: 1 / 5;
  }

  .content {
    grid-column: 5 / 13;
  }
}

@media (max-width: 36rem) {
  .sidebar,
  .content {
    grid-column: 1 / 13;
  }
}

minmax(0, 1fr) allows a track to shrink below the automatic minimum contributed by its contents. This is useful when long text, a wide image, or a code block would otherwise force a column wider than the grid. It does not make the content itself fit; also constrain or wrap that content where needed.

Redefine the number of tracks at smaller widths

If twelve tracks are awkward to reason about on a narrow screen, switch to fewer tracks at a breakpoint:

.layout {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 1rem;
}

@media (max-width: 36rem) {
  .layout {
    grid-template-columns: 1fr;
  }

  .layout > * {
    grid-column: auto;
  }
}

Reset explicit column placement when changing the grid definition. Otherwise, rules such as grid-column: 4 / 13 still refer to those line numbers and can create implicit tracks beyond the new one-column grid.

Use auto-fitting columns for content-driven cards

A fixed twelve-track system is useful when page regions must align predictably. For a collection of cards where the column count should follow the available width, use an auto-fitting grid instead:

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 1rem;
}

This asks the browser to fit as many columns as possible while keeping each track at least 230px, then share leftover space. Tune the minimum to the card’s content and available container width. auto-fit collapses empty tracks after placement; auto-fill keeps the empty tracks reserved. [MDN: basic concepts of grid layout]

4. Common problems and fixes

Symptom Likely cause Fix
Content overflows a narrow track. A grid item’s automatic minimum size or a wide child prevents shrinking. Try minmax(0, 1fr) on tracks; add min-width: 0 to the item; constrain images with max-width: 100%; make long code wrap or scroll.
An item appears beyond the twelfth column. Its start/end line is outside the defined grid, or explicit placement was left over after changing the number of tracks. Check the line numbers and reset placement to auto or a valid span at the relevant breakpoint.
Columns are not the widths expected. Gaps, container padding, borders, and content minimums all affect the available track width. Inspect the container’s inner width and gap values. Use minmax(0, 1fr) if content minimums are preventing equal shrinkage.
Items wrap into unexpected rows. Auto-placement follows source order and available track space; fixed positions or spans may leave insufficient room. Verify each item’s span and start line. Use explicit placement for regions that must stay aligned.
Mobile layout remains two-column. The desktop placement rules still apply at the smaller width. Add a breakpoint that changes the spans or resets items to full width, then check it at the narrowest supported viewport.
Cards do not become equal height. Grid track sizing and child alignment may differ from the intended card styling. Use align-items: stretch on the grid (the default) and make each card’s internal layout handle its own content spacing.

5. Practical checks for performance and reliability

A twelve-track CSS grid is a browser layout feature; the declarations above do not require a JavaScript layout library. Keep the grid rules focused, and avoid adding DOM measurement code for a layout CSS can express. The main reliability work is checking real content at the widths your design supports.

  • Test the narrowest viewport, the breakpoint boundaries, and a wide desktop width.
  • Use long headings, unbroken URLs, large images, and code samples to expose overflow.
  • Keep DOM order meaningful for keyboard and screen-reader users; visual placement does not change source order.
  • Check that the chosen gap and container padding leave enough width for readable text.

There is no CSS Grid service cost or API call involved in this implementation. The practical tradeoff is maintainability: fixed line placement is predictable, while content-driven auto-fit layouts can need fewer breakpoint rules when the number of columns should vary.

6. Inspect the rendered layout

When a layout looks different from the intended grid, inspect its computed grid tracks and the affected item placement in your browser’s developer tools. Compare the viewport width, container inner width, gap, and the active media-query rules. For a repeatable visual check, capture the page at the same URL and viewport after each change.

For capture checks, ScreenshotNeo is a website screenshot API and MCP server. Its API can return a screenshot or PDF from one GET request, and its options include viewport presets, custom viewport sizes, full-page capture, and wait conditions. See the ScreenshotNeo API docs for parameters.

7. Or skip the browser setup

To capture a page after adjusting the grid, make one request. Replace the URL and API key with your own values. The examples save the response bytes as an image; check the response status before using this in production.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://stripe.com \
  -o shot.webp

Python

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://stripe.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(({ writeFile }) =>
  writeFile('shot.webp', Buffer.from(await res.arrayBuffer()))
);

ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots per month with no card, and paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.

8. Frequently asked questions

Does a twelve-column grid require twelve child elements?

No. The columns are tracks in the parent grid. Each child can span one or more tracks, and some tracks can remain unused.

Can I use fractional spans such as 2.5 columns?

Grid placement uses track lines and integer spans. To get a different proportion, define different track sizes or use nested grids rather than placing an item on a fractional line.

Should every page use twelve columns?

No. Use twelve tracks when predictable alignment and reusable spans help the design. Use auto-fitting tracks when the number of columns should respond to available space and content size.