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>
ClassEffectNote
hollowOutline styleInherits colour from the variant class beside it
clearNo background or borderUse for tertiary actions
expandedFull width of the parentPairs well inside cards and panels
disabledNon-interactiveUse the real attribute, not a class
dropdown + data-toggleSplits a button from its menuRequires foundation.dropdown to be initialised
  • Any element with the button class looks like a button, but only <button> and <a href> are reachable by keyboard. Never put it on a plain div.
  • Colour variant classes are independent of the shape classes, so button alert hollow small is one button with four decisions.
  • Foundation's default button colour is defined by $button-background and 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">[email protected]</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
});
ClassPurposeWhere it goes
is-invalid-inputRed border and backgroundOn the input itself
form-errorHidden error textDirectly after the field, linked by aria-describedby
is-visibleReveals the error textAdded to .form-error when failing
help-textMuted hint below the fieldOptional; not an error
helper-textInline hint inside the labelFor short clarifications
input-groupPrefix, suffix or button around a fieldThe 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.

Project setup: Sass, Motion UI and the build pipeline Content components: cards, callouts, badges and tables

Last refreshed 2026-09-18.