Content components: cards, callouts, badges and tables

Compose cards, messaging, badges and data tables, and use the grid-aware helpers that keep mixed content aligned.

Cards and their containers

<div class="grid-x grid-margin-x grid-margin-y">
  <div class="cell medium-6 large-4">
    <div class="card">
      <div class="card-divider">Quarterly report</div>
      <img src="/img/report.webp" alt="" width="640" height="360">
      <div class="card-section">
        <h3 class="h6">Revenue up 12%</h3>
        <p>Driven by the self-serve tier in the EMEA region.</p>
      </div>
    </div>
  </div>

  <div class="cell medium-6 large-4">
    <div class="card">
      <div class="card-section">
        <span class="label primary">New</span>
        <p class="card-text">Every card is a block; the section is what carries padding.</p>
      </div>
    </div>
  </div>
</div>
ElementRoleNote
.cardThe frame and borderDoes not stretch to its neighbour automatically
.card-dividerA heading bandUsually the first child
.card-sectionPadded content regionRepeatable; each one is a separate block
.card-imageEdge-to-edge imageRemoves the section padding around the image
💡
Equal-height cards are not a card feature. They come from the grid: grid-margin-y on the row plus a flex column on the cell. Without the grid doing the stretching, one card will always be shorter than its neighbour.

Callouts, badges and labels

<div class="callout">
  <h3 class="h6">Neutral note</h3>
  <p>Default callout with a light background.</p>
</div>

<div class="callout primary">
  <p>Informational, using the primary colour.</p>
</div>

<div class="callout alert" data-closable>
  <p>Your session expires in five minutes.</p>
  <button class="close-button" aria-label="Dismiss warning" type="button" data-close>
    <span aria-hidden="true">&times;</span>
  </button>
</div>

<div class="callout small success">
  <p>Saved.</p>
</div>

<p>
  <span class="label primary">Draft</span>
  <span class="badge info">14</span>
  <span class="badge warning">Overdue</span>
</p>

<ul class="menu">
  <li><span class="label">Build</span> <span class="badge secondary">passing</span></li>
</ul>
// data-closable on the callout plus data-close on the button is the whole wiring.
// The plugin is foundation.abide-free: it lives in foundation.core and needs
// no options, so $(document).foundation() is enough.

// To remove it from the DOM instead of just hiding it:
$('.callout[data-closable]').on('closed.zf.callout', function () {
  $(this).remove();
});
ClassPurposeCommon mistake
callout + colourMessage blockUsing colour alone to convey severity
callout smallTighter paddingTrying to size the text with it
data-closableAdds the close animationForgetting data-close on the button
labelInline status chipUsing a label for a count — that is a badge
badgeSmall numeric or status tokenLeaving the number without context for screen readers

Give every count badge a hidden word or an aria-label. A screen reader reaching a bare "14" next to a menu item has no way to tell whether it is a count, a price or a page number.

Tables and responsive wrappers

<table class="hover stack">
  <caption class="show-for-sr">Invoices issued in September</caption>
  <thead>
    <tr><th scope="col">Invoice</th><th scope="col" class="text-right">Amount</th><th scope="col">Status</th></tr>
  </thead>
  <tbody>
    <tr>
      <td>#1041</td>
      <td class="text-right">82.00</td>
      <td><span class="label success">Paid</span></td>
    </tr>
    <tr>
      <td>#1042</td>
      <td class="text-right">140.00</td>
      <td><span class="label warning">Pending</span></td>
    </tr>
  </tbody>
</table>

<!-- For a table that must not stack, scroll it instead -->
<div class="table-scroll">
  <table class="hover">
    <thead><tr><th>Endpoint</th><th>Method</th><th>Latency</th></tr></thead>
    <tbody><tr><td>/v1/orders</td><td>GET</td><td>42 ms</td></tr></tbody>
  </table>
</div>
  • stack turns each row into a labelled block on small screens. It needs td elements in a consistent order, because the labels come from the header row's text.
  • table-scroll wraps the table in a horizontally scrolling box, which is the right answer when the columns must stay comparable.
  • class="text-right" on numeric cells is a readability decision, not decoration — decimal points line up and scanner-reading is far easier.
  • The caption is hidden with show-for-sr rather than omitted: the table still needs an accessible name.

FAQ

Why does my stacked table show no labels?
The stack class reads the text from the thead cells and renders it with a pseudo-element. If the header is missing, the cells are empty, or a table is nested, the labels disappear. Use a real thead with th cells.
Should badges be buttons?
No. A badge communicates a value; if it is clickable it should be a link or a button styled with the badge class. Making a span respond to clicks loses keyboard access entirely.

Typography, buttons and forms Building a complete page with Foundation

Last refreshed 2026-09-18.