← Back to Responsive

Responsive pattern

Responsive feature grid

A card grid that finds useful column widths automatically and stays readable on small screens.

Live example

See the pattern in isolation

The preview uses the same locked-down composition policy as the Agate IDE.

PreviewSandboxed

Canonical source

Copy it, change it, keep it plain

These exact files power the preview and the IDE handoff.

Source

HTML

<section class="feature-collection" aria-labelledby="feature-grid-title">
  <header>
    <p>Three useful qualities</p>
    <h1 id="feature-grid-title">A grid that finds its own columns.</h1>
  </header>

  <ul class="feature-grid">
    <li>
      <span aria-hidden="true">01</span>
      <h2>Readable</h2>
      <p>Each card has enough room for a short, comfortable line.</p>
    </li>
    <li>
      <span aria-hidden="true">02</span>
      <h2>Flexible</h2>
      <p>Columns appear only when the container can support them.</p>
    </li>
    <li>
      <span aria-hidden="true">03</span>
      <h2>Direct</h2>
      <p>The list stays meaningful before any layout styles are applied.</p>
    </li>
  </ul>
</section>
Source

CSS

* {
  box-sizing: border-box;
}

body {
  min-height: 100vh;
  margin: 0;
  padding: clamp(1.25rem, 6vw, 4rem);
  background: #f2e3de;
  color: #2b151d;
  font-family: system-ui, sans-serif;
}

.feature-collection {
  width: min(100%, 68rem);
  margin-inline: auto;
}

.feature-collection > header {
  margin-bottom: 2rem;
}

.feature-collection > header p {
  margin: 0 0 0.75rem;
  color: #8e2f50;
  font-size: 0.78rem;
  font-weight: 900;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.feature-collection > header h1 {
  max-width: 15ch;
  margin: 0;
  font-size: clamp(2.2rem, 7vw, 4.5rem);
  line-height: 1;
}

.feature-grid {
  display: grid;
  margin: 0;
  padding: 0;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
  gap: 1rem;
  list-style: none;
}

.feature-grid li {
  min-height: 15rem;
  padding: 1.5rem;
  border: 4px solid #2b151d;
  background: #f7efe6;
}

.feature-grid li:nth-child(2) {
  background: #d76572;
}

.feature-grid li:nth-child(3) {
  background: #d94f9d;
}

.feature-grid span {
  display: grid;
  width: 2.5rem;
  height: 2.5rem;
  margin-bottom: 2.5rem;
  place-items: center;
  border: 2px solid #2b151d;
  background: #b0436a;
  color: #f7efe6;
  font-size: 0.78rem;
  font-weight: 900;
}

.feature-grid h2 {
  margin: 0 0 0.6rem;
  font-size: 1.4rem;
}

.feature-grid p {
  margin: 0;
  color: #2b151d;
  line-height: 1.55;
}

How it works

A small pattern with visible decisions

repeat(auto-fit, minmax()) creates as many useful columns as the available width can hold. Cards fall into fewer columns naturally without a list of device breakpoints.

The list remains semantic, and every card can grow with its content instead of depending on a fixed height.

Make it yours

Open both files in the workspace.

Agate will protect different saved work before loading this example.