Alert
Inline messages from role="status" and role="alert" elements with a data-variant, optionally dismissible.
On this page
A message with role="status" or role="alert" (or an <output>) becomes an alert when it has a data-variant: info, success, warning or danger. No class is needed. Without data-variant the element keeps its default look, so ordinary status regions such as "Autosaved at 14:32" are left alone.
Load css/extras/alert.min.css, or the whole set from css/pico.extras.min.css. See the overview.
Variants
Use role="status" for information that can wait for the screen reader to finish what it is saying, and role="alert" for something the user must hear now, like a failed payment.
<div role="status" data-variant="info">
<strong>Tip.</strong> You can drag files onto the table to upload them.
</div>
<div role="status" data-variant="success">
<strong>Saved.</strong> Your changes are live.
</div>
<div role="status" data-variant="warning">
<strong>Heads up.</strong> Your trial ends in 3 days.
<a href="#billing">Choose a plan</a>.
</div>
<div role="alert" data-variant="danger">
<strong>Payment failed.</strong> Your card was declined. Try another payment method.
</div>Dismissible
Add a native button with data-dismiss as a direct child, and name it with aria-label. The CSS draws the close glyph and makes room for it. Any other button inside an alert stays an ordinary button, like "Retry upload" below.
Dismissing needs a few lines of script. Hide the alert with hidden and not remove(), so it can be shown again with hidden = false.
<div role="status" data-variant="warning">
<strong>Heads up.</strong> Your export will take a few minutes to finish.
<button type="button" data-dismiss aria-label="Dismiss: export notice"></button>
</div>
<div role="alert" data-variant="danger">
<strong>Sync failed.</strong> We could not reach your Stripe account. Check your API key
in <a href="#integrations">Integrations</a> and try again.
<button type="button" data-dismiss aria-label="Dismiss: sync failure"></button>
</div>
<div role="alert" data-variant="danger">
<strong>Upload failed.</strong> The connection dropped.
<button type="button">Retry upload</button>
</div>
<script>
document.addEventListener("click", (event) => {
const button = event.target.closest("[data-variant] > button[data-dismiss]");
if (!button) return;
const alert = button.parentElement;
const heading = alert.closest("section, article, main")?.querySelector("h1, h2, h3");
alert.hidden = true;
if (heading) {
heading.tabIndex = -1;
heading.focus();
}
});
</script>The script moves focus to the nearest heading after a dismiss, so keyboard focus does not fall back to the top of the page. Do not move focus into an alert when it appears, and do not auto-dismiss it.
Heading and list
A heading inside an alert is set to the body font size. The first and last child lose their outer margins, so lists and paragraphs sit inside the box.
<div role="alert" data-variant="danger">
<h3>We could not save the invoice</h3>
<p>Fix these fields and submit again:</p>
<ul>
<li><a href="#vat">VAT number</a> is not valid for the selected country.</li>
<li><a href="#due">Due date</a> must be after the issue date.</li>
</ul>
</div>
<div role="status" data-variant="info">
<h3>Scheduled maintenance</h3>
<p>
Saturday 02:00 to 03:00 UTC. The dashboard stays read-only during that hour, and API
requests keep working.
</p>
</div>Output
<output> has an implicit role="status", so it reads as a result computed from input. Keep controls out of it.
<form oninput="seats.value = Number(plan.value) * 12" onsubmit="return false">
<label for="plan">Seats</label>
<input id="plan" name="plan" type="number" value="4" min="1" />
<output name="seats" for="plan" data-variant="success"
><strong>Total:</strong> 48 seats per year</output
>
</form>Announcing a message
A live region is announced only when its content changes. An alert that is already on the page at load is not announced. To announce a message, keep an empty container with its role and data-variant on the page and fill in the text later. If the message is a pop-up notification instead of part of the page, use a toast.
Accessibility
- Pick
role="alert"only for urgent messages. It interrupts the screen reader. - Color is not the only signal: write the outcome in the text ("Saved.", "Payment failed.").
- The dismiss button needs an
aria-label. Say what it dismisses, like "Dismiss: export notice". - Links inherit the alert color and are underlined, so they stay readable on the tinted background.
- Motion (a short fade in) only runs under
prefers-reduced-motion:.no-preference
Customization
Each variant sets --pico-alert-accent, and the tinted background is 12% of it mixed into the page background. Set the variable on one alert to change its color. The warning accent has its own token, because Pico CSS never had a warning color.
--pico-alert-warning-accent- Accent color of the warning variant.
--pico-alert-accent- Accent color, used for the border, the tint and the title. Set per variant, success and danger reuse the form valid and invalid border colors.
--pico-alert-background-color- Background of the alert.
--pico-card-border-radius- Corner radius of the alert.
--pico-alert-color- Text color.