Home / Primitives / Save bar

Save bar

A sticky bottom strip that slides up the moment a form has unsaved changes. Carries a dirty-state label on the left and Discard + Save on the right.

Stable .p-save-bar Sticky · safe-area aware

Default

Add .dirty when the form has unsaved changes; remove it on save / discard.

Unsaved changes
3 fields edited
Dirty form · save bar slides up .p-save-bar.dirty

With save status

Use the summary slot for autosave timestamps or validation hints.

Saving as draft…
Last autosaved 12 seconds ago
Autosave + publish

Parts

PropertyValueNotes
positionsticky; bottom: 0Lives inside the form / panel that scrolls.
padding12 16 (+ safe-area)Adds env(safe-area-inset-bottom) on iOS.
border-top1 px --border-strongReads as a divider, not a card.
shadow0 -6 16 -8Soft lift to suggest "above the content."
transformtranslateY(110%) → 0Slide-up on .dirty; 240 ms ease-out.
labelflex 1Title + optional summary line.
actionsflex noneDiscard (ghost) + Save (primary). Save sits right.

Do & don't

Do

Show the bar the instant the form goes dirty. The operator should never wonder whether their edits are tracked.

Don't

Hide the bar behind a fixed footer or bottom nav. It needs to sit above the page chrome.

Do

Summarise what changed in the summary slot — "3 fields edited", "Quantity adjusted". Operators trust what they can see.

Don't

Use destructive styling for Discard. It's a soft action that loses unsaved work, not a delete.

Do

Disable Save while validation is failing — but keep the bar visible so the operator knows changes are pending.

Don't

Auto-save and show the bar at the same time. Pick one mental model per surface.

Accessibility

The bar appears and disappears — screen readers need to know.

  • Wrap the bar in role="region" aria-label="Unsaved changes" so it lands in landmark navigation.
  • Move keyboard focus to the bar (or its primary button) when the form first goes dirty, only if the operator triggered the dirty state via the keyboard.
  • Discard should require a confirm step on irreversible edits (use alert dialog).
  • Respect prefers-reduced-motion: the slide-up reduces to a fade in the global motion rule.

Code

<!-- Sticky save bar — toggle .dirty when the form has unsaved changes -->
<div class="p-save-bar dirty" role="region" aria-label="Unsaved changes">
  <div class="label">
    <div class="title">Unsaved changes</div>
    <div class="summary">3 fields edited</div>
  </div>
  <div class="actions">
    <button class="btn btn-ghost" type="button">Discard</button>
    <button class="btn btn-primary" type="submit">Save changes</button>
  </div>
</div>

Related

For a soft confirmation that a save just happened, see Record saved banner. For the whole-form chrome, see Form shell.