web3icons

Don't rely on color alone in crypto interfaces

How WCAG 1.4.1 applies to red and green price changes, chart lines, status dots and form errors, and which signs, labels and patterns fix them.

5 min read

Crypto interfaces speak in red and green. Prices go up in green and down in red, positions are in profit or loss, transactions succeed or fail, a dot next to the wallet button says connected or not. For people with red-green color vision deficiency, the most common form of color blindness, many of those signals look nearly identical. They also vanish on a grayscale e-ink screen, in bright sunlight, and through a screen reader.

WCAG's rule for this is short, and the fixes are mostly small: a sign, an arrow, a word, a pattern. This guide goes through the places crypto products rely on color and shows what to add.

What 1.4.1 says

1.4.1 Use of Color is Level A: color must not be the only visual means of conveying information, indicating an action, prompting a response, or distinguishing a visual element.

It doesn't ban color. Green for gains is a useful, fast cue, and you should keep it. The rule says color can't be the only cue. If you made the page grayscale, everything that was communicated by color must still be communicated by something else.

It's also not a contrast rule. Contrast between text and background is covered by 1.4.3, and contrast of icons and controls by 1.4.11, both explained in color contrast ratio explained. Two colors can each have perfect contrast against the background and still be indistinguishable from each other. A typical green #16A34A and red #DC2626 have a contrast ratio of only 1.47:1 against each other, which means that in grayscale they're close to the same shade.

Price changes

The classic case is a percentage change rendered only in color: "2.41%" in green, "1.80%" in red. Add a sign and, if you like, an arrow:

Color only With a second cue
2.41% in green ▲ +2.41% in green
1.80% in red ▼ −1.80% in red

The sign alone is enough to pass. Arrows help when scanning long columns. Make sure the screen reader hears the meaning too. Arrow characters may be read out by their Unicode names, something like "black up-pointing triangle", and minus signs can be read inconsistently depending on punctuation settings. One approach shows the symbols visually and gives screen readers a word:

type Props = { change: number }

export function PriceChange({ change }: Props) {
  const value = `${Math.abs(change).toFixed(2)}%`
  const up = change > 0
  const down = change < 0

  return (
    <span className={up ? 'change up' : down ? 'change down' : 'change'}>
      <span aria-hidden="true">
        {up ? '▲ +' : down ? '▼ −' : ''}
        {value}
      </span>
      <span className="visually-hidden">
        {up ? `up ${value}` : down ? `down ${value}` : `unchanged, ${value}`}
      </span>
    </span>
  )
}

visually-hidden is the usual utility class that keeps text available to assistive technology while hiding it from view. Making token amounts and prices accessible covers the rest of how numbers are read aloud.

Color meaning is also cultural. In some markets, including mainland China, red signals a rise and green a fall, and trading apps often let users swap the two. Signs and arrows mean the same thing everywhere, which is another reason not to rely on color.

Charts

Charts are where color carries the most weight and where adding a second cue takes the most thought.

Multi-line charts. A portfolio chart with five assets in five colors, matched to a color-only legend, fails for many people. Label lines directly at their ends, vary the stroke with dash patterns (stroke-dasharray), or add distinct markers such as circles, squares and triangles. Letting users highlight one line on hover or focus also helps.

Candlesticks. Rising and falling candles are usually distinguished by color alone in default themes. A long-standing alternative draws rising candles hollow and falling candles filled, which survives grayscale. Offer it as a chart setting if not as the default.

Donuts and allocation bars. Put labels with percentages on or next to segments instead of relying on a legend, and separate segments with a thin gap so neighbors don't merge.

Accessible price charts goes further into text alternatives and data tables for charts.

Status dots and badges

A colored dot is the most compact status indicator, and the easiest to misread.

  • Transaction lists. A yellow, green or red dot for pending, confirmed and failed. Add the word ("Pending", "Confirmed", "Failed"), or at least use three different shapes, such as a clock, a check and a cross. The design of those states is covered in transaction state design.
  • Wallet connection. A green dot on the wallet button meaning "connected". The button text usually changes from "Connect" to an address, which already carries the information; if it doesn't, it needs to.
  • Network or RPC health. A dot next to the network name meaning "degraded". Pair it with a short label or an icon, and put the explanation in text people can reach with the keyboard, not just in a hover tooltip.
  • Risk levels. Health factors, liquidation risk and price impact often move from green through amber to red. Show the number and a word ("Low", "High") alongside the color band.

Form errors and selected states

An amount field that turns its border red when the amount exceeds the balance fails 1.4.1 if nothing else changes. Add a message in text, connect it to the field, and mark the field as invalid:

<label for="amount">Amount</label>
<input id="amount" inputmode="decimal" aria-invalid="true" aria-describedby="amount-error">
<p id="amount-error">Insufficient ETH balance. You have 0.42 ETH.</p>

Selection has the same problem in reverse. Chain filter chips, timeframe tabs ("1D", "1W", "1M") and selected rows in a token list often show selection only with a fill color. Add a check icon, a heavier border, an underline or bold text, and expose the state in code with aria-pressed or aria-selected so screen readers hear it too.

Links inside body text follow a similar logic. Underline them. A documented WCAG technique accepts links distinguished by color alone when the link color has 3:1 contrast with the surrounding text and the link gets another cue, such as an underline, on hover and focus. The underline is simpler.

How to test

The quickest test is to remove color. In devtools, add filter: grayscale(1) to the html element and look at the page. Anything that stops making sense depends on color alone.

For a closer look, Chromium devtools can emulate vision deficiencies in the Rendering panel, including protanopia, deuteranopia, tritanopia and achromatopsia. Check the screens where color does the most work: the markets table, the portfolio chart, the transaction history and any form with validation. If every gain, loss, status and selection still reads correctly, you're done.

More in Accessibility

All guides