CodeChroma All articles
Colour Inspiration

Departures, Dyes and Data: Turning UK Rail Ephemera into CSS Palettes

CodeChroma
Departures, Dyes and Data: Turning UK Rail Ephemera into CSS Palettes

Photo: Alan Hughes , CC BY-SA 2.0, via Wikimedia Commons

Rail tickets are not designed as design artefacts. That is exactly why they work. An orange band across ticket stock, an amber dot-matrix departure board, a timetable card that has been folded into a wallet: these are colour systems built for function, not mood boards. For UK maker sites, they are a rich seam of ready-made palettes. This piece lists five found combinations, then shows a small browser tool for sampling your own.

Why rail ephemera is a good palette mine

Rail colour is functional. It has to survive cheap printing, fading, glare, rain and the general chaos of a station concourse. That tends to produce high contrast, limited inks and clear hierarchy. Those are useful qualities on the web too.

The trick is not to recreate a franchise livery or pinch a logo. Use the material as a starting point: the paper, the band, the signal colour, the dot-matrix glow. Let the palette be inspired by the system rather than copied from the brand.

Five palettes from the platform

1. Orange Band, Warm Paper

#F4EBDD paper, #E36C1F orange, #1E1B18 ink, #C62828 signal red, #6B7F8A faded blue.

This is the classic ticket-stock feeling: warm, slightly grubby paper with a strong orange accent. Use the orange for borders, small badges and card headers. Keep body text in the ink colour. Orange on paper is charming; orange as body copy is usually a contrast problem.

2. Amber Departure Board

#0B0B0B near-black, #FFB000 amber, #9A6500 dim amber, #2A2D2E cool grey.

This is the dark-mode palette. Amber on near-black is legible and atmospheric, but amber on white is not. Use it for focus states, live-status chips and small moments of glow. Do not turn the whole site into a departure board unless you enjoy that specific headache.

3. Platform Enamel

#0B5D3B deep green, #F0E6D2 cream, #141414 black, #A34A28 rust.

Heritage station signage gives you a green that feels solid without being corporate. The cream keeps it from going too austere. Rust is the surprise accent: use it sparingly for links or underlines. This palette suits editorial sections and footers that need a bit of backbone.

4. Timetable Card

#F7F3EA off-white, #203A5F navy, #4F9DB3 cyan, #C23B32 red, #6E6A63 pencil grey.

Timetable cards are full of small, practical contrasts: navy for headings, red for warnings, cyan for route lines, grey for the bits nobody reads until they need to. This is a good palette for documentation, tables and any page where information density is the point.

5. Night Sleeper

#111827 midnight, #2F6F6A muted teal, #C49A5A warm brass, #E7E1D3 paper.

A sleeper-service mood: dark, quiet, with brass details and paper-coloured text surfaces. It works well for portfolio dark themes, but keep the teal and brass for accents. Dark palettes can collapse into mud if every colour is allowed to sit at the same weight.

Sample your own ticket in the browser

You do not need a design tool to pull colours from a photo. A small canvas sketch will do. Load an image, draw it to a canvas, then sample pixels on a grid and bucket them into rough colour groups. It is not a production-grade extractor, but it is enough to find a useful starting point.

<label for='ticket'>Choose a ticket photo</label>
<input id='ticket' type='file' accept='image/*' />
<canvas id='canvas' hidden></canvas>
<div id='swatches'></div>
const input = document.querySelector('#ticket');
const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });

input.addEventListener('change', async () => {
  const file = input.files?.[0];
  if (!file) return;

  const bitmap = await createImageBitmap(file);
  const maxSide = 640;
  const scale = Math.min(1, maxSide / Math.max(bitmap.width, bitmap.height));
  canvas.width = Math.round(bitmap.width * scale);
  canvas.height = Math.round(bitmap.height * scale);
  ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height);

  const { data } = ctx.getImageData(0, 0, canvas.width, canvas.height);
  const buckets = new Map();

  for (let i = 0; i < data.length; i += 4 * 9) {
    const r = data[i], g = data[i + 1], b = data[i + 2], a = data[i + 3];
    if (a < 200) continue;
    const key = [r, g, b].map(v => Math.round(v / 32) * 32).join(',');
    buckets.set(key, (buckets.get(key) || 0) + 1);
  }

  const top = [...buckets.entries()].sort((a, b) => b[1] - a[1]).slice(0, 8);
  renderSwatches(top);
});

function renderSwatches(top) {
  const swatches = document.querySelector('#swatches');
  swatches.innerHTML = top.map(([key]) => {
    const [r, g, b] = key.split(',').map(Number);
    return `<button style='background:rgb(${r} ${g} ${b})' data-colour='${key}'>${key}</button>`;
  }).join('');
}

If you want better results, run median-cut or k-means in a Web Worker. But for a quick found-palette session, this is enough. Use your own photographs, not scans of copyrighted tickets or logos.

From found colours to accessible CSS variables

Sampling gives you colours, not roles. Assign roles next: background, surface, text, accent, focus. Then check contrast. A colour that looks great in a swatch can fail badly as body text.

:root {
  --rail-paper: #f4ebdd;
  --rail-orange: #e36c1f;
  --rail-ink: #1e1b18;
  --rail-signal: #c62828;

  --bg: var(--rail-paper);
  --text: var(--rail-ink);
  --accent: var(--rail-orange);
  --accent-strong: #a8440f;
  --on-accent: var(--rail-ink);
  --focus: var(--rail-signal);
}

.button {
  background: var(--accent);
  color: var(--on-accent);
}

.link {
  color: var(--accent-strong);
}

:focus-visible {
  outline: 2px solid var(--text);
  outline-offset: 3px;
  box-shadow: 0 0 0 4px var(--bg);
}

Notice the extra --accent-strong. The bright ticket orange is good for shapes and buttons, but it may not carry enough contrast for small link text. Darken it for text, keep the original for surfaces, and test with a proper contrast checker rather than trusting your eye after a strong coffee.

Keep the source material honest

Use rail ephemera as inspiration, not as a licence to lift trademarks. Avoid logos, franchise marks and distinctive brand assets. Keep the palette limited to three to five colours, then let typography and layout do the rest. The best found palettes feel like they have always been there, not like a ticket stuck to the screen.

All Articles

Related Articles

Blue from Woad, Red from Madder: Mixing Britain’s Dye Garden in GLSL

Laying Bricks in Code: Procedural British Materials for p5.js Sketches

Laying Bricks in Code: Procedural British Materials for p5.js Sketches

Salt, Slate and Sea Glass: Ten UK Coastal Palettes for Generative Makers

Salt, Slate and Sea Glass: Ten UK Coastal Palettes for Generative Makers