Wes Ellis./ a personal notebook
Technology. Stories. Side projects.
A few things worth writing down.
← Back to Web Design

Web Design

Borrowing a Palette from a Reading App

Part 3 of the thread The design notebook

THE SHORT VERSION4 points
  • Six colors pulled from a screenshot of a Reeder-style RSS app: two ambers, two near-blacks, a warm white and a warm grey.
  • The rule that makes it work is restraint: amber on headers, links and metadata only, never on body text.
  • I ran the numbers. Plain amber on charcoal is 6.75:1, which already passes AA. The lighter #F7B95C gets it to 8.12:1 and clears AAA.
  • The deep amber is 4.41:1 on charcoal, just short of AA for body-size text. Borders and hover states only.

While I was trying out directions for this blog, one of the experiments started with a screenshot of a dark RSS reader in the Reeder mold. Near-black background, soft grey text, and little flashes of amber on the parts you actually click. That kind of screen looks calm, like a place you'd happily read for an hour.

So I sampled it. Six colors, eyedropper straight off the screenshot, and then a lot of fiddling with what goes where.

This isn't what the site uses now, for what it's worth. The notebook you're reading ended up on a cream, blackberry and teal palette instead. But the amber set taught me a few things worth writing down.

The six swatches

Name Hex Job
Amber #F0A33C Headers, links
Deep amber #C97F1E Hover states, borders
Charcoal #2B2B2B Cards and surfaces
Near-black #161616 Page background
Warm white #F5F3EF Headlines
Muted grey #B8B5AE Body copy

Two things stand out once they're in a table. There are really only two "colors" here, amber and a warm neutral, and everything else is a lightness step. And the light neutrals aren't really neutral. The charcoal and near-black are straight greys, but the warm white and the body grey both lean slightly yellow, which is a big part of why the amber sits in them so comfortably.

As custom properties:

:root {
  --amber: #F0A33C;         /* headers, links */
  --amber-deep: #C97F1E;    /* hover, borders */
  --amber-small: #F7B95C;   /* amber text at 14px and below */
  --charcoal: #2B2B2B;      /* cards, surfaces */
  --near-black: #161616;    /* page background */
  --warm-white: #F5F3EF;    /* headlines */
  --muted-grey: #B8B5AE;    /* body copy */
}

body { background: var(--near-black); color: var(--muted-grey); }
h1, h2, h3 { color: var(--warm-white); }
a { color: var(--amber); }
a:hover { color: var(--amber-deep); }
.card { background: var(--charcoal); border: 1px solid var(--amber-deep); }
.meta { color: var(--amber-small); font-size: 13px; }

Rule one: amber is for chrome, not for reading

The whole palette hangs on this. Amber goes on the chrome and the metadata: headings, links, dates, tags, the little labels. It never goes on body text.

Put paragraphs in amber and the page stops being a calm reading room and starts being a jack-o'-lantern. Orange on black is a very loud combination, and it only reads as elegant when there's not much of it. A few amber words per screen feels intentional. A few hundred feels like October.

Rule two: lighten the amber when it gets small

Small text in a mid-tone color on a dark background tends to look thinner and dimmer than the same color at headline size. So the plan was to swap in a lighter amber, #F7B95C, for anything at 14px and below: dates, tags, captions.

I wanted to know whether that was a real accessibility fix or just a feel thing, so I ran the WCAG contrast formula over the key pairs with a few lines of Python (relative luminance of each color, then (lighter + 0.05) / (darker + 0.05)):

Text Background Ratio WCAG result
Amber #F0A33C Charcoal #2B2B2B 6.75:1 AA at any size, AAA for large text only
Light amber #F7B95C Charcoal #2B2B2B 8.12:1 AAA at any size
Amber #F0A33C Near-black #161616 8.63:1 AAA
Deep amber #C97F1E Charcoal #2B2B2B 4.41:1 Fails AA for normal text, passes for large text and UI parts
Muted grey #B8B5AE Near-black #161616 8.84:1 AAA
Muted grey #B8B5AE Charcoal #2B2B2B 6.92:1 AA (just under AAA)
Warm white #F5F3EF Near-black #161616 16.33:1 AAA, with lots to spare

So, honestly: the plain amber was never failing. At 6.75:1 on charcoal it clears the 4.5:1 AA bar comfortably. What the lighter amber does is push small text past the 7:1 AAA line, and give it a bit more visual weight at sizes where thin strokes wash out. It's a real improvement, just not a rescue.

Heads up

The one pair that doesn't hold up is deep amber on charcoal, at 4.41:1. That's fine for a border or a hover underline (non-text UI only needs 3:1), but don't let it creep into small text on a card. It's exactly the kind of color that ends up on a "last updated" line by accident.

Rule three: don't mix warm grey with neutral grey

The body grey, #B8B5AE, is warm. It has a little yellow in it, same as the whites. If a plain neutral grey like #999 sneaks in somewhere (a default placeholder color, a borrowed component, a forgotten color: #999 on a caption), the two sit side by side and one of them looks dirty. Usually the warm one looks muddy next to the neutral, or the neutral looks cold and blue next to the warm.

For the record, #999 would have been accessible too: 6.35:1 on near-black, 4.97:1 on charcoal. The problem isn't contrast. It's that two slightly different greys read as a mistake instead of a choice.

The fix is boring and effective: define the greys once as custom properties and never type a grey hex anywhere else.

Tip

When you sample a palette from a screenshot, check the "neutrals" in a color picker before you trust them. They often aren't, and that faint tint can be doing half the work.

Where it ended up

In my notes, not on the site. This blog went with the cream, blackberry and teal palette instead, which is its own story. The amber set is still a solid dark reading palette, though, and the three rules travel well: keep the loud color on the chrome, give small text a lighter tint, and never let two different greys share a screen.