Blog
All articles Guides

Success, Error, Warning: Semantic UI Colors Done Right

By the Paleta team·8 August 2026·~8 min read

Semantic colors are colors with a job, not a hue you happen to like. Success, warning, error and info are four roles a color system has to fill, and each role needs its own background-and-text pair, not one saturated swatch that only looks right on your monitor. Search "semantic colors ui" and you will find plenty of mood boards; you will find far fewer palettes where the error state still reads at 4.5:1 on a phone screen in direct sun.

This guide gives you four working pairs with real hex codes and their measured contrast ratios, points out exactly where the classic amber warning color quietly breaks the rule, and walks through turning the set into tokens instead of four hex values buried in a stylesheet.

What "semantic" means here

A semantic color is named for what it communicates, not for what it looks like. --color-error describes a role a designer can reuse on a banner, a form field or a toast; #B91C1C describes one specific red and nothing else. Swap that red for a different shade next year and every component referencing the role updates on its own, because nothing was ever tied to the hex value directly. That is the same idea behind design tokens in general; if you have not set one up yet, this primer on design tokens covers the two-layer system that makes role-based naming like this work.

The four conventions: success, warning, error, info

Four roles have become close to a universal pattern across web and native apps: green for success, amber or orange for warning, red for error, blue for neutral information. None of it is a law of physics; some systems drop blue for gray, or skip "info" entirely, but breaking the convention without a real reason makes people re-learn something every other app already taught them.

  • Success: a task finished, a form saved, a payment went through. Usually green.
  • Warning: something needs attention before it turns into a real problem. Usually amber or orange.
  • Error: something failed and needs fixing right now. Usually red.
  • Info: neutral, non-urgent context. Usually blue.

These four are one piece of a bigger system. See this guide to building an accessible palette for how status colors sit next to your brand colors without clashing.

Every status needs two colors, not one

A single hex code cannot do both jobs a status color has to do. You need a solid pair, a strong background with light or dark text, for buttons and filled badges, and a soft pair, a tinted background with a darker version of the same hue for text, for banners and inline alerts that should not shout. Both pairs have to clear WCAG 4.5:1 minimum for normal text (SC 1.4.3); icons and borders that carry meaning only need 3:1, the threshold for non-text contrast (SC 1.4.11).

Here is a set that measures out cleanly, checked against pure white and pure black:

success   solid  #1E7B34 on #FFFFFF  -> 5.33:1
success   soft   #1E4620 on #E6F4EA  -> 9.47:1
warning   solid  #B45309 on #FFFFFF  -> 5.02:1
warning   soft   #78350F on #FEF3C7  -> 8.15:1
error     solid  #B91C1C on #FFFFFF  -> 6.47:1
error     soft   #7F1D1D on #FEE2E2  -> 8.20:1
info      solid  #1D4ED8 on #FFFFFF  -> 6.70:1
info      soft   #1E3A8A on #DBEAFE  -> 8.49:1

Warning is the one that trips people up. The amber most people reach for first, something close to #F59E0B, only hits 2.15:1 against white text, nowhere near AA. Push the same hue behind black text instead and it jumps to 9.78:1, which is why most warning badges you have seen use dark text on a bright amber fill rather than white like the other three roles. The pair above, #B45309 with white, does clear 4.5:1, but only by leaning the hue toward brown; that is the real cost of keeping white text on a warning color.

Do not take these numbers on faith. Drop each pair into the contrast checker with your own brand hue swapped in, and watch the ratio update live as you nudge the lightness.

Solid vs. soft: picking the right pair for the job

Two pairs per status is a rule, but nobody tells you which one to grab on a Tuesday afternoon when a due badge, a form field and a full-page banner are all asking for the same color. Reach for the solid pair — saturated background, near-white text — when the color sits on something the user can act on: a button, a single-count badge, a toast that needs to grab attention and then get out of the way. Reach for the soft pair — tinted background, a darker version of the same hue for text — for anything that has to sit next to real body copy for more than a couple of seconds: inline banners, table rows, form hints, badges in a dense list.

A failed-payment screen makes the split obvious. The banner explaining what went wrong should use the soft error pair, calm enough to read next to the rest of the page; the "Try again" button underneath it should use the solid pair, because that button is the one thing you actually want the eye to land on. Give both elements the same saturated fill and you get two things shouting at once — and in most usability sessions, people click the button anyway, so the banner's extra loudness bought nothing.

Info without the blue: does it need its own hue?

Info is the odd one out of the four. Success, warning and error each map to something that already happened; info is just supporting context, and forcing it into a fourth saturated hue is not automatic. If your primary buttons and links already use blue, an info badge in the same blue competes with real calls to action on the same screen — a status label and a "Learn more" link start looking like the same kind of thing. A neutral gray role sidesteps that: #475569 on white clears 7.58:1 for the solid pair, and #1E293B on #F1F5F9 clears 13.35:1 for the soft one, both comfortably past the 4.5:1 floor. Keep blue for info only when it needs to hold its own next to warning and error on a dense dashboard, or when your primary color already lives somewhere far from blue.

Build the pairs as tokens, not eight hex values in a stylesheet

Once the eight colors are locked, do not paste them into components as literal hex. Name them by role instead, so a component only ever asks for "the success text color," never a specific value:

:root{
  --status-success-bg:#E6F4EA; --status-success-text:#1E4620;
  --status-warning-bg:#FEF3C7; --status-warning-text:#78350F;
  --status-error-bg:#FEE2E2;   --status-error-text:#7F1D1D;
  --status-info-bg:#DBEAFE;    --status-info-text:#1E3A8A;
}

The token generator builds this kind of role-based system for you and exports it as CSS variables, Figma Tokens or a Style Dictionary file, so the pairing survives the handoff from design to code instead of getting redrawn from a screenshot six weeks later.

Dark-mode semantic colors: same tokens, different values

The token layer is exactly why dark mode should not mean redrawing eight colors from a blank canvas. Reusing the light-mode values as-is is the actual trap: #7F1D1D on #FEE2E2 was tuned for a white page, and dropped onto a near-black surface it either sinks into the background or turns into a dim, muddy red with almost no separation from the card around it. A dark surface needs a lighter, less saturated color for text and icons, and a darker, still-tinted background for the soft pair — the same role, a different value underneath it.

Override the same four role names inside a dark media query instead of inventing a second set of variables, and every component that already asks for --status-success-text gets the right value automatically, no matter which theme is active:

@media (prefers-color-scheme: dark){
  :root{
    --status-success-bg:#0F2A1A; --status-success-text:#86EFAC;
    --status-warning-bg:#332107; --status-warning-text:#FDE68A;
    --status-error-bg:#3A1414;   --status-error-text:#FCA5A5;
    --status-info-bg:#14213D;    --status-info-text:#93C5FD;
  }
}

Every one of those four pairs clears 8.5:1 or better against its own background — success 10.95:1, warning 12.39:1, error 8.58:1, info 8.86:1 — with more headroom than the light-mode set, because dark surfaces punish a thin margin more than light ones do. For status text or icons sitting straight on the app background rather than inside a tinted badge, a plain, unbadged color works too: #4ADE80 on a #16181D surface still measures 10.19:1. This guide to building a dark-mode palette covers choosing that surface color in the first place, including why pure black causes its own contrast problems.

See them where they actually live: alerts, badges, toasts

A contrast ratio in a spreadsheet does not tell you whether a badge looks cramped next to real body copy, or whether an amber banner disappears against your card background. Paste the four pairs into the UI preview and check them as real alerts, badges and buttons before anyone writes a line of component code; it catches a clash far faster than reviewing it in a pull request.

Four alert rows in success (green), warning (amber), error (red) and info (blue), each with a distinct icon shape next to two lines of placeholder text

Color alone is still not enough

WCAG Use of Color rule (SC 1.4.1) exists because roughly one in twelve men cannot reliably tell red from green, and a status system built on hue alone fails for all of them at the same moment. Every success, warning and error state needs a second signal that does not depend on color, a checkmark, a triangle, an X, a distinct icon shape, plus the word itself whenever there is room for it. This guide to designing for color blindness covers the one habit that fixes most of this in a single move: pair icon shape with color every time, no exceptions for "just this one badge."

Anti-patterns that quietly break the system

Color alone is the anti-pattern everyone brings up first, and rightly so, but four more show up just as often once a status system reaches real components.

  • Borrowing the brand's signature color for the error state because it is on-brand. Users already know red means stop; making them relearn that on your product for no real gain is a cost with no upside.
  • Signaling an invalid field with a border-color change alone. A 2px border technically clears the 3:1 non-text minimum, but a thin line is easy to miss entirely — pair it with inline text and an icon, every time.
  • Using the solid pair as ordinary paragraph text. Saturated color next to real body copy reads as a wall of noise; save the solid pair for buttons and single-line badges, and let the soft pair's darker text carry anything longer.
  • Shipping a dark theme with the light-mode hex values pasted in unchanged. A pair built for a white background rarely holds up on a near-black one — check it on its own, the same way you checked the light version.
  • Sprinkling literal hex values across components instead of the token roles. The day someone decides the amber is too orange, updating one token beats searching the codebase for eight different values.

Building your four statuses from scratch

If you are starting a status system today rather than patching an old one, the order below avoids most of the rework later.

  1. Pick the hue before the exact shade. Green, amber, red and blue for the four roles, unless there is a real reason to deviate — people bring that convention with them from every other product they use.
  2. Build the solid pair first. Push the hue's lightness down (or up, for warning) against white or black until the ratio clears 4.5:1 in the contrast checker; warning usually needs the extra nudge, as the table above shows.
  3. Build the soft pair from that same hue, not a different one: a light tint for the background, a much darker shade of the identical hue for the text, checked again against 4.5:1.
  4. Name both pairs as roles — a -bg and a -text token per status — before either one touches a component, so nothing gets hardcoded on day one.
  5. Add the dark-mode override for all eight values under the same role names, rather than a second, differently-named set of variables.
  6. Drop every pair into the UI preview as a real button, badge and banner, and confirm each one carries a non-color signal — icon, shape or label — before calling the system done.

Start with the four pairs above, test them against your own brand hues in the contrast checker, and read the plain-English WCAG guide if 4.5:1 and 3:1 still feel like arbitrary numbers; they stop feeling that way the first time you watch a pair fail and fix it yourself.

Ready to try it? Every tool on Paleta runs free in your browser — no sign-up, nothing uploaded.

Explore the tools →