Typography, buttons and forms
Work with Foundation's type scale, button variants and form layouts, and know which styles come from base element rules versus explicit classes.
The type scale and base styles
Foundation 6 no longer applies opinionated typography to bare elements the way Foundation 5 did. You opt in with classes such as h1-through-h6, or you enable the typography component and style elements yourself.
<h1 class="h3">Visual size, semantic level</h1>
<p class="lead">A larger introductory paragraph.</p>
<p class="subheader">Slightly muted supporting line.</p>
<p class="stat">4,102</p>
<ul class="no-bullet">
<li>List without markers or padding</li>
<li>Useful when each item contains its own layout</li>
</ul>
<ul class="menu">
<li><a href="#">Inline menu item</a></li>
<li><a href="#">Second item</a></li>
</ul>
<blockquote>
We replaced our own grid with the XY grid in a week.
<cite>Platform team, Northwind</cite>
</blockquote>/* settings.scss — the typography decisions belong in Sass, not markup */
$global-font-size: 100%;
$global-lineheight: 1.6;
$body-font-family: 'Inter', system-ui, sans-serif;
$header-font-family: $body-font-family;
$header-font-weight: 600;
$header-styles: (
small: ('h1': ('font-size': 30), 'h2': ('font-size': 24), 'h3': ('font-size': 20)),
medium: ('h1': ('font-size': 40), 'h2': ('font-size': 30), 'h3': ('font-size': 24))
);
$lead-font-size: rem-calc(20);
$paragraph-margin-bottom: 1.25rem;💡
rem-calc() converts pixel values to rem using $global-font-size. Using it everywhere is what keeps the type scale correct when a user changes their browser's default font size.Buttons and button groups
<button class="button">Default</button>
<button class="button primary">Primary</button>
<button class="button secondary">Secondary</button>
<button class="button success">Success</button>
<button class="button alert">Alert</button>
<button class="button warning">Warning</button>
<button class="button hollow">Hollow</button>
<button class="button clear">Clear</button>
<button class="button tiny">Tiny</button>
<button class="button small">Small</button>
<button class="button large">Large</button>
<button class="button expanded">Full width</button>
<ul class="button-group">
<li><button class="button">Undo</button></li>
<li><button class="button">Redo</button></li>
<li><button class="button" disabled>Publish</button></li>
</ul>
<div class="input-group">
<span class="input-group-label">$</span>
<input class="input-group-field" type="number" aria-label="Amount">
<div class="input-group-button"><input type="submit" class="button" value="Apply"></div>
</div>| Class | Effect | Note |
|---|---|---|
hollow | Outline style | Inherits colour from the variant class beside it |
clear | No background or border | Use for tertiary actions |
expanded | Full width of the parent | Pairs well inside cards and panels |
disabled | Non-interactive | Use the real attribute, not a class |
dropdown + data-toggle | Splits a button from its menu | Requires foundation.dropdown to be initialised |
- Any element with the
buttonclass looks like a button, but only<button>and<a href>are reachable by keyboard. Never put it on a plaindiv. - Colour variant classes are independent of the shape classes, so
button alert hollow smallis one button with four decisions. - Foundation's default button colour is defined by
$button-backgroundand friends; changing them in settings regenerates every variant consistently.
Form layouts and states
<form class="grid-container" novalidate>
<div class="grid-x grid-margin-x">
<div class="cell medium-6">
<label for="email">Email
<span class="helper-text">We only use this for receipts.</span>
</label>
<input type="email" id="email" name="email" required aria-describedby="emailError">
<p class="help-text" id="emailHint">name@company.com</p>
<p class="form-error" id="emailError">Please enter a valid email address.</p>
</div>
<div class="cell medium-6">
<label for="plan">Plan</label>
<select id="plan" name="plan">
<option value="">Choose a plan</option>
<option value="team">Team</option>
</select>
</div>
<div class="cell">
<input type="checkbox" id="terms" name="terms" required>
<label for="terms">I accept the terms</label>
<p class="form-error">You must accept the terms to continue.</p>
</div>
<div class="cell">
<button class="button" type="submit">Create account</button>
</div>
</div>
</form>const form = document.querySelector('form[novalidate]');
form.addEventListener('submit', (event) => {
event.preventDefault();
if (!form.checkValidity()) {
// Foundation's own helper: toggles .is-invalid-input and pops the .form-error text
form.querySelectorAll(':invalid').forEach((field) => {
field.classList.add('is-invalid-input');
const error = form.querySelector(`#${field.getAttribute('aria-describedby')}`);
if (error) error.classList.add('is-visible');
});
form.querySelector(':invalid')?.focus();
return;
}
// server round-trip, then clear the states on success
});| Class | Purpose | Where it goes |
|---|---|---|
is-invalid-input | Red border and background | On the input itself |
form-error | Hidden error text | Directly after the field, linked by aria-describedby |
is-visible | Reveals the error text | Added to .form-error when failing |
help-text | Muted hint below the field | Optional; not an error |
helper-text | Inline hint inside the label | For short clarifications |
input-group | Prefix, suffix or button around a field | The label sits outside the group |
FAQ
Why is my form-error text always visible?
It is missing
form-error (with the hyphen) or the stylesheet was imported without the forms component. Foundation 6 uses .form-error and toggles .is-visible on it; the Bootstrap-era .error class does nothing.Can I restyle buttons without editing Foundation's Sass?
Yes for colours and radius via the compiled CSS since those come from custom properties in recent versions, but the size scale and padding are compiled values. For a full button redesign, override the Sass variables and rebuild — it is less fragile than fighting specificity.
Related
Project setup: Sass, Motion UI and the build pipeline Content components: cards, callouts, badges and tables
Last refreshed 2026-09-18.