Flexbox and Grid

Build a responsive card layout

Combine Grid, gap, width, padding, borders, and content alignment to build a reusable card list without custom component CSS.

Let’s put spacing, sizing, and layout together in one component: a responsive list of cards.

Build the smallest version first, the one that works on a phone:

<ul class="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
  <li>
    <article class="flex h-full flex-col rounded-lg border border-gray-200 p-6">
      <h2 class="text-lg font-semibold">CSS layout</h2>
      <p class="mt-2 leading-6 text-gray-600">Build resilient interfaces.</p>
      <a class="mt-auto pt-6 font-medium text-blue-700 focus-visible:outline-2"
         href="/css/">
        Read the course
      </a>
    </article>
  </li>
</ul>

The base layout is one column. Two columns appear at md, three at lg.

Who does what

The <ul> is a list because a set of cards is a collection. Grid goes on the list because the list controls where the cards sit.

Each card is a vertical Flexbox. h-full makes the article fill the height of its grid cell. flex-col stacks the heading, paragraph, and link. mt-auto on the link pushes it to the bottom. So when one card has a longer description, all the “Read the course” links still line up at the bottom, with no fixed heights anywhere.

Don’t truncate to align

Equal card bottoms are a layout goal, not a reason to cut off descriptions. Test with a two-line title, a long translated paragraph, and a card without an image. The grid row grows to fit the tallest card, and mt-auto keeps the links aligned.

The visible <a> gets keyboard focus and has a meaningful name. If the whole card must be clickable, don’t nest links inside buttons or add a click handler to the <article>. Restructure the markup so one link wraps the content or covers the card.

Choose breakpoints from the cards

md and lg are habits, not rules. Resize with real content and add a column at the width where two cards are still readable side by side. If the same card list appears in a sidebar and a main column, a container query or an auto-fit grid may fit better than viewport breakpoints.

One spacing system

Notice the spacing: gap-6 between cards, p-6 inside them, mt-2 for the small relationship between heading and paragraph. Three values, one scale. That’s what gives the layout rhythm without a one-off margin on every element.

Try this: render six cards with deliberately uneven content. Tab through the links, zoom to 200%, and squeeze the container. Remove every fixed height you find, and explain which Grid and Flex rules produce the final alignment.

Lesson completed