Alerts are the most-styled element in a documentation set and the easiest to get subtly wrong. Out of the box each of the five types is drawn from a different Tailwind ramp — gray, green, sky, yellow, red — which is five hues, most of which your brand probably does not contain. A page carrying a green tip and a sky-blue info reads as somebody else’s design system leaking through yours.
Everything here is one file under tailwind/, imported into layer(components). You cannot add a type and you do not touch the markup. Alerts and alert theming are free on every plan: the tokens are ordinary custom properties and the override is an ordinary stylesheet in your repository.
Writing the alerts themselves — the ::: fence, the five types, custom titles and nesting — is on Alerts. This page is about coloring them.
The five types are a closed set
note, tip, info, warning, danger. Registered five times in the markdown instance and carried as the same closed union in the editor’s schema.
There is no extension point. A sixth type is not a configuration change; it means editing the markdown instance, the ProseMirror schema and the serializer. Design within five.
Two authoring facts matter even when you only came here to change colors, because both affect what your CSS is styling. The text after the fence type becomes the heading — :::tip Custom Heading renders that string inside span.description, and a bare :::tip falls back to the capitalized type name. And alerts nest by adding colons; the editor writes the outer fence as 3 + the deepest nesting inside it, so an alert containing an alert is written with ::::.
The token contract, and which two of the five slots are decoration
Twenty-five names: five types × five slots, all declared on :root, with a full prefers-color-scheme: dark block restating every one.
| Slot | Read by anything? | Light default (note / tip / info / warning / danger) | Dark default |
|---|---|---|---|
--alert-<t>-border | Yes | gray-300 / green-600 / sky-500 / yellow-600 / red-700 | -500 (note: gray-500) |
--alert-<t>-bg | Yes | gray-50 / green-50 / sky-50 / yellow-50 / red-50 | -950 (note: gray-800) |
--alert-<t>-text | Yes | gray-950 / green-950 / sky-950 / yellow-950 / red-950 | -200 |
--alert-<t>-code-bg | No | gray-100 / green-100 / sky-100 / yellow-100 / red-100 | -900 (note: gray-700) |
--alert-<t>-code-border | No | gray-300 / green-300 / sky-300 / yellow-300 / red-300 | -700 (note: gray-700) |
How an override wins without !important
Leed declares @layer theme, base, components, utilities; before any import, and the build injects Leed’s own stylesheet immediately after the @import "tailwindcss" line at the top of your entry file. So everything you import afterwards comes later — and a file of yours imported into layer(components) beats Leed’s alert component on plain source order at identical specificity.
/* tailwind/site.config.css */
@import "./partials/alerts.css" layer(components);Both selectors are aside.alert.alert-<type> or aside.alert.alert-<type> > .heading > i. Identical specificity, later in the same layer, so yours wins.
Never build an alert ground with color-mix()
Your palette needs semantic status tokens, and they belong at site level
The pattern is two tokens per status: a saturated one for the rule, and a -soft one for the ground. Name them to mirror whatever your accent pair is already called, so there is nothing new to learn.
/* tailwind/site/theme.css — light */
:root {
--error: var(--color-red-700);
--error-soft: var(--color-red-50);
--warning: var(--color-amber-600);
--warning-soft: var(--color-amber-50);
--success: var(--color-emerald-700);
--success-soft: var(--color-emerald-50);
}
@media (prefers-color-scheme: dark) {
:root {
--error: var(--color-red-400);
--error-soft: var(--color-red-950);
--warning: var(--color-amber-400);
--warning-soft: var(--color-amber-950);
--success: var(--color-emerald-400);
--success-soft: var(--color-emerald-950);
}
}Note the inversion: in dark mode the saturated rung moves lighter (700 → 400) and the ground drops to the darkest rung (50 → 950). A status color that stays put across schemes is either invisible on one ground or shouting on the other.
Why site level and not a documentation-only file. Alerts render on every markdown page type, not only in the documentation set — and three separate consumers want the same two pairs:
| Consumer | What it uses them for |
|---|---|
| This alert override | The warning and danger registers |
| Your custom code theme | .hljs-addition and .hljs-deletion diff tints — see Custom Documentation Themes |
| Your Mermaid palette | errorBkgColor / errorTextColor, gantt critical and done tasks — see Theming Diagrams |
A docs set should not be the only thing on your site that knows what “error” looks like. Put them beside the rest of the palette, on the Site Token Contract’s :root.
Three statuses, not five — and the diagram palette is why
You need error, success and warning as tokens, but they are not equal citizens, and it is worth knowing that here rather than discovering it two pages later.
A Mermaid palette has slots for error and for success and none for warning. errorBkgColor and errorTextColor take the soft/saturated error pair; gantt’s critBkgColor and critBorderColor take the same pair for critical tasks; doneTaskBkgColor and doneTaskBorderColor take the success pair for completed ones. One further literal belongs to the error token rather than to any chart ramp, because it means danger boundary rather than series 4: the Cynefin diagram’s cliffColor, which otherwise defaults to a dark red of its own.
Do not invent a warning slot in a diagram palette. --warning exists for alerts and stays there. A palette that repurposes it produces a diagram convention no reader can decode, because Mermaid never draws anything that means “caution”.
One measurement constrains the warning pair, and this page is where the color gets chosen, so it belongs here: amber-600 (#e17100) measures 3.20:1 against a white canvas. That clears WCAG 1.4.11’s 3:1 for a graphical object — a rule, a border, an icon — and fails the 4.5:1 required for body text. Use it as a border color. Do not use it as text on a light ground.
Status colors are the two pairs every consumer shares. The categorical colors a chart needs — twelve series that must all be distinguishable from each other at once — are a separate problem with a separate method, and they are not these tokens. If you are picking those twelve as well, Diagram Color Scales is the arithmetic for deriving them from your own brand, and it explains why the status pairs cannot be stretched into a ramp.
Replacing the per-type icon
This is the part that surprises everyone: the markdown plugin emits no glyph. It writes a literal placeholder into every alert:
<aside class="alert alert-warning">
<div class="heading">
<i class="fa-icon"></i>
<span class="description">WARNING</span>
</div>
<div class="content">
…
</div>
</aside>The icon comes entirely from CSS. So the override is a CSS rule, imported later in the same layer:
/* tailwind/partials/alerts.css */
aside.alert.alert-note > .heading > i { @apply fa-light fa-note-sticky; }
aside.alert.alert-info > .heading > i { @apply fa-light fa-circle-info; }
aside.alert.alert-tip > .heading > i { @apply fa-regular fa-lightbulb; }
aside.alert.alert-warning > .heading > i { @apply fa-regular fa-triangle-exclamation; }
aside.alert.alert-danger > .heading > i { @apply fa-solid fa-octagon-exclamation; }The reason this replaces rather than stacks is worth stating, because a reader who assumes @apply is additive will write defensive nonsense to undo the default. Font Awesome Pro 7’s utilities do not carry glyph rules. A style utility (fa-light, fa-regular, fa-solid) sets --fa-style, a weight. A glyph utility (fa-circle-info) sets --fa, a codepoint. Both are plain custom properties, and a later declaration of a custom property simply wins. There is nothing to unset, and content: none will only break the pseudo-element.
Font Awesome is on every Leed site with no setup, which is what makes an icon override a one-line change — see Fonts and Webfonts.
Leed’s defaults, and why you will want to change them
| Type | Leed default | Selector to override |
|---|---|---|
note | fa-solid fa-circle-info | aside.alert.alert-note > .heading > i |
tip | fa-solid fa-rocket | aside.alert.alert-tip > .heading > i |
info | fa-regular fa-lightbulb | aside.alert.alert-info > .heading > i |
warning | fa-solid fa-triangle-exclamation | aside.alert.alert-warning > .heading > i |
danger | fa-solid fa-skull-crossbones | aside.alert.alert-danger > .heading > i |
A rocket and a skull are jokes, and they are the only illustration on the page. A reference set whose danger means this overwrites your live site wants something soberer. The lightbulb is also on the wrong type — it is the universal “here is a better way”, which is tip, while info is the one type that should carry the circle-i everybody already reads. And info is the lone fa-regular in an otherwise fa-solid set, so the weight variation says nothing at all.
How many registers do you need?
Five types do not need five hues, and this is a design decision with a real trade-off rather than a rule.
Three registers. Pithy collapses to neutral (note, info), accent (tip, warning) and error (danger). The argument: its brand owns exactly one accent, and a sixth color costs more than the icon already buys.
Four registers. Leed’s own documentation collapses note with info on one neutral and keeps tip, warning and danger distinct. The argument is about what the pages contain: these are operating instructions, and several hundred of them describe actions that overwrite a live site, delete revisions or bill money. A reader skimming for what will hurt me has to be able to find it by color alone. Putting tip and warning on the same hue would paint “use ⌘K to jump between pages” and “publishing replaces the live site” identically, on pages where both appear.
Both are defensible. The thing that makes either safe is the accessibility note: encode severity in icon weight as well as hue. A ladder of fa-light → fa-regular → fa-solid still reads for a reader with a color-vision deficiency, and it survives two types sharing a hue.
What not to restyle
The default box is py-3 pl-4 pr-8 mt-6 border-l-6 shadow-sm rounded-lg, wrapping a > div.heading (with > i and > .description) and a div.content.
| Element | Leed’s treatment | Change it when |
|---|---|---|
aside.alert | 6px left rule, 8px radius, soft shadow, asymmetric padding | Your site does not use shadows or rounded panels — otherwise leave it |
> div.heading | flex row, vertically centered | Almost never |
> .heading > i | mr-2.5 text-xl | To resize the glyph, or to change it (above) |
> .heading > .description | uppercase, bold, small | Your brand does not use uppercase labels |
div.content | top margin; links inherit the alert’s own color and are underlined | Almost never — inherited link color is what keeps an alert readable |
Structural overrides are supported, and one of the two reference sites makes several. But they are only worth making when the default fights your brand, and the general principle is worth stating plainly: change what conflicts, not what merely differs from the reference implementation you copied from. leed.ai’s own override touches color and icons only, because its site already uses shadows and rounded panels everywhere; Pithy’s does more because Pithy’s does not.
Verifying it
Render all five types on one page and read them, in both color schemes. That is not a metaphor — this page is that test. Everything above renders live, so if your override is wrong, the five examples near the top of this page are where you will see it.
Three things to check specifically:
- Both schemes. If your file has no dark block because every value is a brand token that already swaps, confirm that assumption rather than trusting it. Flip the scheme with your browser’s rendering panel; Dark Mode has the per-browser instructions.
- A nested alert. The inner one inherits
div.content’s link styling and sits on the outer one’s ground; the combination is worth looking at once. - An alert containing a code block. The code block’s own surfaces come from your color theme, not from the alert tokens, so the two have to agree without either knowing about the other.