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>| Element | Role | Note |
|---|---|---|
.card | The frame and border | Does not stretch to its neighbour automatically |
.card-divider | A heading band | Usually the first child |
.card-section | Padded content region | Repeatable; each one is a separate block |
.card-image | Edge-to-edge image | Removes 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">×</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();
});| Class | Purpose | Common mistake |
|---|---|---|
callout + colour | Message block | Using colour alone to convey severity |
callout small | Tighter padding | Trying to size the text with it |
data-closable | Adds the close animation | Forgetting data-close on the button |
label | Inline status chip | Using a label for a count — that is a badge |
badge | Small numeric or status token | Leaving 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>stackturns each row into a labelled block on small screens. It needstdelements in a consistent order, because the labels come from the header row's text.table-scrollwraps 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
captionis hidden withshow-for-srrather 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.Related
Typography, buttons and forms Building a complete page with Foundation
Last refreshed 2026-09-18.