---
title: Badge Soup
slug: badge-soup
kind: anti-pattern
summary: Every attribute rendered as its own coloured pill, so the row is a wall of chips and colour has stopped separating anything.
problem: >-
  A row carries six badges in five hues at the same saturation, and two of the
  hues mean two different things each. Under a squint test the table is
  confetti; the badge that means "a human must act" looks exactly like the one
  that means "annual plan".
family: [scan]
principles: [progressive-disclosure, consistency]
status: stable
visibility: public
related:
  - title: Color, Icons, and Contrast
    url: /cookbook/color-icons-and-contrast
    summary: The authority on the colour budget this violates, with the contrast floors.
  - title: Lifecycle Status
    url: /patterns/lifecycle-status
    summary: The usual fix — one named state instead of six independent chips.
---

## How you get here

A badge is the cheapest way to put a fact on a row, and it looks deliberate. So
every attribute that someone wants visible becomes a badge, and every badge
wants its own colour so it can be told apart from the others.

The tipping point is unremarkable. Three badges is a row. Six is soup, and it
happens one reasonable request at a time.

## How to recognise it

- More than about three chips on one row or card.
- Two badges that mean different kinds of thing — a lifecycle state and a
  billing plan — rendered identically.
- The same hue used for two meanings anywhere in the product. Purple for
  "draft" in one place and "re-opened" in another is the documented example
  from our own ticket page.
- Every badge at the same saturation, so nothing is louder than anything else.
- Squinting turns the list into confetti rather than showing you where to look.

## Why it fails

Colour is **preattentive**: the eye spends it before you read a word. That makes
it powerful and strictly limited. The budget is roughly three semantic colours
plus neutrals, each with exactly one meaning.

Six hues do not give six channels of information. They give **zero**, because
the reader cannot hold the mapping, so they stop trying and read the text
instead — at which point every badge is a slower version of a word.

There is a second failure underneath. Most of what becomes a badge is not a
status at all. "Annual plan" and "EU region" are attributes. Rendering an
attribute with the same visual weight as "needs a human" asserts they are the
same kind of fact, and they are not.

## The way out

1. **Decide what the row is for**, and keep the one or two facts that change
   what someone does. The rest belong on the record.
2. **Neutral by default.** Grey chips for everything informational. That alone
   fixes most of it.
3. **Spend colour on exceptions only**, at most three meanings — amber for
   needs-a-human, rose for broken, emerald for a positive delta — and never let
   a hue carry two meanings.
4. **Encode roles with structure, not hue.** Position, indentation and grouping
   show what things are; colour shows what needs attention.
5. **Back every colour with a word or an icon**, so the row still reads in
   greyscale.

## The test

Blur your eyes, or shrink the browser to 25%. A good list shows a few points of
colour where attention is needed and grey everywhere else. Badge soup looks
like a bag of sweets, and in that state the urgent row is genuinely no easier to
find than the routine one — which is the whole cost.
