Menu


Error summary


A box at the top of a form after a failed send, listing every mistake as a link that moves focus to its field.

Customize the preview


Choices
Show
View

There is a problem

Fix these so we can send your details:

Options


Error summary options
ChoiceValuesDefaultWhat it does
sizesmbk-error-summary--size-<value>mdsmmd
Small sits closer to its edges with a smaller heading, for a dialog or a narrow form.

Partssmbk-error-summary__iconsmbk-error-summary__contentsmbk-error-summary__textsmbk-error-summary__headingsmbk-error-summary__introsmbk-error-summary__listsmbk-error-summary__itemsmbk-error-summary__link

Examples


Usage

Every mistake as a link to its field, worded exactly as the message beside it.

There is a problem

Fix these so we can send your details:

Sizes

Medium for a page’s form; small for a dialog or a narrow form.

There is a problem

Fix these so we can send your details:

There is a problem

Fix these so we can send your details:

A problem with no field

A problem with the whole form is words alone, not a link.

There is a problem

Focus

The ring it draws round itself when the page moves focus to it.

There is a problem

Fix these so we can send your details:

After a failed send

Send the form empty: the summary appears and takes focus, and each link moves focus to its field.

Delivery speed

In a narrow space

Long mistakes wrap; the icon stays on the heading’s line.

There is a problem

Fix these so we can send your details:

Guidelines


Purpose

Lists every mistake in a form at its top, after the form was sent with mistakes, so a reader sees at once how many things need fixing and can go straight to each. Each mistake is a link to its field. The message beside each field still says what is wrong there; the summary gathers those messages in one place.

When to use

  • At the top of a form, after it was sent with one mistake or more.
  • For a long form, or one that runs over more than one screen, where a reader cannot see every field's message at once.

When not to use

  • Before the form is sent. Check fields as they are left, if at all, and show the message beside the field.
  • For a problem the reader cannot fix, such as a server that is down. Use an alert or an empty state.
  • For a single short field, such as a search box. Its own message is enough.

Usage rules

Do

  • Show it only after the form was sent with mistakes, and take it away when they are fixed and the form is sent.
  • List the mistakes in the order of the fields.
  • Use the same words in the summary and beside the field.

Don't

  • Show a summary with no message beside each field.
  • Move focus to it while someone is still filling in the form.
  • List problems the reader cannot fix.

There is a problem

Do

Say what is wrong and how to fix it, in the words the field shows.

There is a problem

Don't

A bare “Invalid” tells the reader something is wrong but not what to do.

Anatomy

  • The block, a box on the danger color's subtle surface, inside a hairline edge, with small container corners.
  • The icon part, the danger icon, on the heading's first line.
  • The content part, holding the text and the list.
  • The text part: the heading part ("There is a problem") over the optional intro part, a line such as "Fix these so we can send your details:".
  • The list part, one item part per mistake, in the order the fields come in the form.
  • The link part, inside an item whose mistake belongs to a field: the mistake's words, underlined, in the danger text color. An item for a problem with the whole form holds its words alone.

Behavior

The summary takes focus as it appears, so the page scrolls to it and a screen reader reads its heading. Send the form again with mistakes still in it and the summary takes focus again: give it a focusKey that changes with each send, such as a count of sends. A summary that shows only after the page reloads takes focus as the page loads.

Pressing a mistake's link brings the field's label into view and moves focus to the field. For a group of choices, such as radio buttons, focus moves to the first choice marked invalid, or the first choice, and the group's question comes into view. Without script, the link goes to the field's id, as any link to a part of a page does.

While it has focus, the summary draws the focus ring round itself, whether focus came from a press, the keyboard or the page.

Accessibility


What it does for you

  • The summary is a group named by its heading, and the page moves focus to it, so a screen reader reads "There is a problem" without a live region. It is not a Tab stop: Tab from the summary reaches its first link.
  • Each mistake with a field is a link, so it can be reached with Tab and listed with a screen reader's list of links.
  • The heading's level follows the page's outline: 2 by default.
  • The words carry the meaning; the danger color only backs them up.

What you must do

  • Word each mistake exactly as the message beside its field, and say how to fix it: "Enter a date in the future", never "Invalid date".
  • Give each field the id its mistake names. Give a group of choices its id on the group (the field group's fieldset), and its choices are reached through it.
  • Set the heading's level with headingLevel to follow the page's outline.
  • Give it a focusKey that changes with each send, so it takes focus again when the form is sent with mistakes still in it.

How to test

  • Send the form with mistakes: focus moves to the summary and a screen reader reads its heading.
  • Tab from the summary to the first link, then press Enter: focus moves to the field, with its label in view.
  • With a screen reader's list of links, each mistake appears as a link.
  • With text at 200% and the page 320 pixels wide, the words and links stay whole.
  • In a forced-color mode, the summary's focus ring and the links are still visible.

Keyboard

Error summary keys
KeyWhat it does
Tab
Moves focus from the summary to its first link, then on through the links.
Enter
Follows the focused link, moving focus to its field.

Source


The files the error summary is made of. Its folder on GitHub (opens in a new tab) holds them with its wrapper and its document.

Stylesheet
error-summary.css
/* smbk-error-summary — Error summary. Figma "Error summary" 7110:412 (Size × State) and "Error summary item"
 * 7110:350 (Type). A box on the danger family's subtle surface, inside a hairline of its edge colour, at the small
 * container corners: the danger icon, then the heading over an optional intro line, then the mistakes, each a link
 * in the danger text colour, underlined, that moves focus to its field. It is focused by the page after a failed
 * send, and draws the focus ring round itself whenever it has focus, from a click as from the keyboard. */

.smbk-error-summary {
  /* Always the danger family (the tone recipe). */
  --smbk-tone-surface: var(--smbk-color-feedback-danger-surface-subtle);
  --smbk-tone-border: var(--smbk-color-feedback-danger-border);
  --smbk-tone-text: var(--smbk-color-feedback-danger-text-on-subtle);
  --smbk-tone-icon: var(--smbk-color-feedback-danger-icon);
  box-sizing: border-box;
  display: flex;
  align-items: flex-start;
  gap: var(--smbk-space-inline-lg);
  padding: var(--smbk-space-inset-xl);
  background: var(--smbk-tone-surface);
  border: var(--smbk-border-width-hairline) solid var(--smbk-tone-border);
  border-radius: var(--smbk-radius-container-sm);
  color: var(--smbk-tone-text);
}

/* The page moves focus here; the ring shows for that focus too, not only for the keyboard's. */
.smbk-error-summary:focus {
  outline: var(--smbk-focus-ring-width) solid var(--smbk-color-border-focus);
  outline-offset: var(--smbk-focus-ring-offset);
}

/* The icon sits in a box one heading line tall, so it is centred on the heading's first line at either size. */
.smbk-error-summary__icon {
  flex: none;
  display: flex;
  align-items: center;
  height: 1lh;
  font-family: var(--smbk-typography-subtitle-font-family);
  font-size: var(--smbk-typography-subtitle-font-size);
  line-height: var(--smbk-typography-subtitle-line-height);
  color: var(--smbk-tone-icon);
}

.smbk-error-summary__icon .smbk-icon {
  font-size: var(--smbk-size-icon-md);
  color: inherit;
}

.smbk-error-summary__content {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--smbk-space-stack-md);
}

.smbk-error-summary__text {
  display: flex;
  flex-direction: column;
  gap: var(--smbk-space-text-label);
}

.smbk-error-summary__heading {
  margin: 0;
  color: var(--smbk-tone-text);
  font-family: var(--smbk-typography-subtitle-font-family);
  font-size: var(--smbk-typography-subtitle-font-size);
  font-weight: var(--smbk-typography-subtitle-font-weight);
  line-height: var(--smbk-typography-subtitle-line-height);
  letter-spacing: var(--smbk-typography-subtitle-letter-spacing);
}

.smbk-error-summary__intro {
  margin: 0;
  color: var(--smbk-color-text-default);
  font-family: var(--smbk-typography-body-md-font-family);
  font-size: var(--smbk-typography-body-md-font-size);
  font-weight: var(--smbk-typography-body-md-font-weight);
  line-height: var(--smbk-typography-body-md-line-height);
  letter-spacing: var(--smbk-typography-body-md-letter-spacing);
}

.smbk-error-summary__list {
  display: flex;
  flex-direction: column;
  gap: var(--smbk-space-stack-md);
  margin: 0;
  padding: 0;
  list-style: none;
}

.smbk-error-summary__item {
  margin: 0;
  color: var(--smbk-tone-text);
  font-family: var(--smbk-typography-body-md-font-family);
  font-size: var(--smbk-typography-body-md-font-size);
  font-weight: var(--smbk-typography-body-md-font-weight);
  line-height: var(--smbk-typography-body-md-line-height);
  letter-spacing: var(--smbk-typography-body-md-letter-spacing);
}

/* The link keeps the danger text colour and its underline at rest, on hover and when pressed: the summary's colour
   is what ties it to the fields' messages. */
.smbk-error-summary__link {
  color: inherit;
  text-decoration: underline;
  border-radius: var(--smbk-radius-control-sm);
  -webkit-tap-highlight-color: transparent;
}

.smbk-error-summary__link:focus-visible {
  outline: var(--smbk-focus-ring-width) solid var(--smbk-color-border-focus);
  outline-offset: var(--smbk-focus-ring-offset);
}

/* ---- small: closer to its edges, the heading in Label LG ---- */

.smbk-error-summary--size-sm {
  padding: var(--smbk-space-inset-lg);
}

.smbk-error-summary--size-sm .smbk-error-summary__icon {
  font-family: var(--smbk-typography-label-lg-font-family);
  font-size: var(--smbk-typography-label-lg-font-size);
  line-height: var(--smbk-typography-label-lg-line-height);
}

.smbk-error-summary--size-sm .smbk-error-summary__heading {
  font-family: var(--smbk-typography-label-lg-font-family);
  font-size: var(--smbk-typography-label-lg-font-size);
  font-weight: var(--smbk-typography-label-lg-font-weight);
  line-height: var(--smbk-typography-label-lg-line-height);
  letter-spacing: var(--smbk-typography-label-lg-letter-spacing);
}

@media (forced-colors: active) {
  .smbk-error-summary:focus,
  .smbk-error-summary__link:focus-visible {
    outline-color: Highlight;
  }
}

/* ---- touch: each link takes an invisible press area ----
   On a touch screen an invisible area, centred on each link, takes the press out to the comfortable target in each
   direction it falls short of it, and between two links only halfway into the list's gap, so the links stay one
   press each. The links, their focus ring and their places stay as drawn. */

@media (pointer: coarse) {
  .smbk-error-summary__link {
    position: relative;
  }

  .smbk-error-summary__link::after {
    content: '';
    position: absolute;
    inset: min(0%, (100% - var(--smbk-size-target-comfortable)) / 2);
  }

  /* Halfway between two links: half the list's gap and half of what the line keeps round the link's own box. */
  .smbk-error-summary__item:has(.smbk-error-summary__link) + .smbk-error-summary__item .smbk-error-summary__link::after {
    inset-block-start: max(min(0%, (100% - var(--smbk-size-target-comfortable)) / 2), (var(--smbk-space-stack-md) + 1lh - 100%) / -2);
  }

  .smbk-error-summary__item:has(+ .smbk-error-summary__item .smbk-error-summary__link) .smbk-error-summary__link::after {
    inset-block-end: max(min(0%, (100% - var(--smbk-size-target-comfortable)) / 2), (var(--smbk-space-stack-md) + 1lh - 100%) / -2);
  }
}
Tokens it reads
Error summary tokens
Token and CSSValuePreviewFoundation
color.feedback.danger.surface-subtle--smbk-color-feedback-danger-surface-subtlelight: #fee5e1dark: #4b130echanges with themeColor
color.feedback.danger.border--smbk-color-feedback-danger-border#f34336Color
color.feedback.danger.text-on-subtle--smbk-color-feedback-danger-text-on-subtlelight: #a51d17dark: #febdb2changes with themeColor
color.feedback.danger.icon--smbk-color-feedback-danger-iconlight: #cc0d0ddark: #fe7d6dchanges with themeColor
space.inline.lg--smbk-space-inline-lgcomfortable: 12pxcompact: 8pxchanges with densitySpacing
space.inset.xl--smbk-space-inset-xlcomfortable: 16pxcompact: 12pxchanges with densitySpacing
border-width.hairline--smbk-border-width-hairline1pxBorder width
radius.container.sm--smbk-radius-container-sm8pxRadius
focus.ring.width--smbk-focus-ring-width2pxFocus
color.border.focus--smbk-color-border-focuslight: #cc0d0ddark: #f34336changes with themeColor
focus.ring.offset--smbk-focus-ring-offset2pxFocus
typography.subtitle--smbk-typography-subtitle-font-family--smbk-typography-subtitle-font-size--smbk-typography-subtitle-font-weight--smbk-typography-subtitle-line-height--smbk-typography-subtitle-letter-spacing--smbk-typography-subtitle-text-transformfontFamily: Geist, Geist Fallback, system-ui, sans-seriffontSize: 20pxfontWeight: 400lineHeight: 1.4letterSpacing: 0px

The quick brown fox

Typography
size.icon.md--smbk-size-icon-md20pxSize
space.stack.md--smbk-space-stack-mdcomfortable: 12pxcompact: 8pxchanges with densitySpacing
space.text.label--smbk-space-text-label0pxSpacing
color.text.default--smbk-color-text-defaultlight: #232527dark: #e7ebf0changes with themeColor
typography.body-md--smbk-typography-body-md-font-family--smbk-typography-body-md-font-size--smbk-typography-body-md-font-weight--smbk-typography-body-md-line-height--smbk-typography-body-md-letter-spacing--smbk-typography-body-md-text-transformfontFamily: Geist, Geist Fallback, system-ui, sans-seriffontSize: 16pxfontWeight: 400lineHeight: 1.5letterSpacing: 0px

The quick brown fox

Typography
radius.control.sm--smbk-radius-control-sm4pxRadius
space.inset.lg--smbk-space-inset-lgcomfortable: 12pxcompact: 8pxchanges with densitySpacing
typography.label-lg--smbk-typography-label-lg-font-family--smbk-typography-label-lg-font-size--smbk-typography-label-lg-font-weight--smbk-typography-label-lg-line-height--smbk-typography-label-lg-letter-spacing--smbk-typography-label-lg-text-transformfontFamily: Geist, Geist Fallback, system-ui, sans-seriffontSize: 16pxfontWeight: 500lineHeight: 1.4letterSpacing: 0px

The quick brown fox

Typography
size.target.comfortable--smbk-size-target-comfortable44pxSize
Manifest
error-summary.manifest.json
{
  "name": "error-summary",
  "block": "smbk-error-summary",
  "summary": "A box at the top of a form after a failed send, listing every mistake as a link that moves focus to its field.",
  "adapt": "css",
  "axes": {
    "size": {
      "values": [
        "md",
        "sm"
      ],
      "default": "md",
      "description": "Small sits closer to its edges with a smaller heading, for a dialog or a narrow form."
    }
  },
  "flags": {},
  "parts": [
    "icon",
    "content",
    "text",
    "heading",
    "intro",
    "list",
    "item",
    "link"
  ],
  "figmaNodeId": "7110:412"
}