Entry 04Palette design4 min read

The stability field is background

The stability field is the largest painted surface on a windgram — and it is not the data. It is the layer everything a pilot actually came to read is drawn on top of: the usable-lift line, cloud base, the wind barbs, the isotherm labels, the markers. That one fact decides the palette. This entry records what the shipped eight-class ramp promises, what it deliberately does not, and the one measured defect in its lineage that a validator caught where every reviewing eye had signed off.

Every number here is reproducible: colour difference is ΔE, Euclidean distance in the OKLab colour space scaled by 100, with colour-vision deficiency simulated by the Machado–Oliveira–Fernandes (2009) matrices at full severity. The shipped palette’s figures compute their values at build time from the package export, so the article cannot drift from what renders.

What a background field owes its chart

Figure-ground contrast comes first. Every point of saturation a field palette spends on itself is taken from the legibility of the content above it, so the whole ramp lives in a pale register — roughly thirty points of lightness, all of it near the paper. Salience still works, because salience is relative: with the field quiet, the warm unstable classes are the loudest thing on the chart without spending any ink mass, and the classes that dominate an ordinary summer field — conditional instability, stable air — recede instead of shouting. A field earns attention for its exceptions by being quiet about its rule.

The register has a price, and it is paid openly: thirty points of lightness across eight classes cannot also carry a grayscale-survivable ordering, so none is claimed. Class identity rides hue; class order rides the channels the chart already has — the key names every cell in plain words, the cursor readouts state the class at any point, and the field’s own geometry orders it (stable air sits where stable air sits). The palest cells stand near 2:1 against the surface because receding into the page is the design, not an accident.

A background palette and its lineage

A quiet field spends its lightness budget on the chart content drawn above it; the key and readouts carry class order.

The windgram default strip runs #d95f52, #de8f3a, #c67eb6, #aeaad9, #d7b29b, #768bb9, #9aa19d, #b3b9b6 on its design surface #fffdf8; its luminance-matched grays are #858585, #a1a1a1, #969696, #afafaf, #b9b9b9, #8b8b8b, #9f9f9f, #b8b8b8, which do not darken in class order, so the grayscale row loses the sequence. The canadarasp strip runs #ff3d3d, #ff7800, #ff96ff, #ccbfff, #facab1, #8080e6, #cccccc, #999999 on its design surface #8080e6; its luminance-matched grays are #898989, #9f9f9f, #bcbcbc, #c7c7c7, #d4d4d4, #8b8b8b, #cccccc, #999999, which do not darken in class order, so the grayscale row loses the sequence.

The default stability ramp and the historical canadarasp palette as swatch strips, each above its luminance-matched grayscale row Eight stability classes from very unstable to strong inversion, left to right. The windgram default strip runs #d95f52, #de8f3a, #c67eb6, #aeaad9, #d7b29b, #768bb9, #9aa19d, #b3b9b6 on its design surface #fffdf8; its luminance-matched grays are #858585, #a1a1a1, #969696, #afafaf, #b9b9b9, #8b8b8b, #9f9f9f, #b8b8b8, which do not darken in class order, so the grayscale row loses the sequence. The canadarasp strip runs #ff3d3d, #ff7800, #ff96ff, #ccbfff, #facab1, #8080e6, #cccccc, #999999 on its design surface #8080e6; its luminance-matched grays are #898989, #9f9f9f, #bcbcbc, #c7c7c7, #d4d4d4, #8b8b8b, #cccccc, #999999, which do not darken in class order, so the grayscale row loses the sequence.In the canadarasp strip the stable swatch matches the design surface exactly, in colour and in grayscale.VeryunstableUnstableConditionalstrongConditionalNearneutralStableInversionStronginversionwindgram defaultcolourgrayscaleVery unstable: #d95f52 on #fffdf8Unstable: #de8f3a on #fffdf8Conditional · strong: #c67eb6 on #fffdf8Conditional: #aeaad9 on #fffdf8Near neutral: #d7b29b on #fffdf8Stable: #768bb9 on #fffdf8Inversion: #9aa19d on #fffdf8Strong inversion: #b3b9b6 on #fffdf8Very unstable as grayscale: #858585Unstable as grayscale: #a1a1a1Conditional · strong as grayscale: #969696Conditional as grayscale: #afafafNear neutral as grayscale: #b9b9b9Stable as grayscale: #8b8b8bInversion as grayscale: #9f9f9fStrong inversion as grayscale: #b8b8b8#d95f52#de8f3a#c67eb6#aeaad9#d7b29b#768bb9#9aa19d#b3b9b6canadaraspcolourgrayscaleVery unstable: #ff3d3d on #8080e6Unstable: #ff7800 on #8080e6Conditional · strong: #ff96ff on #8080e6Conditional: #ccbfff on #8080e6Near neutral: #facab1 on #8080e6Stable: #8080e6 on #8080e6Inversion: #cccccc on #8080e6Strong inversion: #999999 on #8080e6Very unstable as grayscale: #898989Unstable as grayscale: #9f9f9fConditional · strong as grayscale: #bcbcbcConditional as grayscale: #c7c7c7Near neutral as grayscale: #d4d4d4Stable as grayscale: #8b8b8bInversion as grayscale: #ccccccStrong inversion as grayscale: #999999#ff3d3d#ff7800#ff96ff#ccbfff#facab1#8080e6#cccccc#999999
The shipped ramp comes from the package export; the canadarasp row records its cited lineage. Neither grayscale row preserves class order, so hue, the key, readouts, and field geometry carry that meaning.Units sRGB colours and luminance-matched grayscale

The lineage, and the pivot that does not transfer

The hues descend from canadarasp, the project these windgrams gratefully descend from: warm reds and oranges for instability, pink and lavender for the conditional middle, a tan pivot, blue for stable, greys for inversion. Two details from its source are worth keeping on the record. It contours nine lapse-rate boundaries where this project draws eight classes — but the extra −0.5 °C per 1,000 ft boundary paints the same colour on both sides, so there were eight visible bands all along, and their boundaries are exactly the classes this pipeline publishes. And its colormap file states its design’s pivot in six words: “matching the background is stable” — stable air disappears into canadarasp’s blue page, and everything painted is signal. That pivot is brilliant on its own page and does not transfer: these charts sit on paper, so this palette keeps stable air pale rather than invisible, and owes no debt to a background it does not have.

Measured, not eyeballed

The ramp’s direct ancestor — the palette this project’s site first rendered, tuned by eye in exactly this register — carried a defect nobody saw: its two conditional purples sat at adjacent ΔE 6.4, and 2.8 under deuteranopia, where roughly 6 is the floor for telling adjacent fills apart and 2.8 is one colour. The most common form of colour-vision deficiency erased the boundary between conditional-strong and conditional instability — a boundary the chart exists to draw. The defect lived below the threshold of design-by-eye and above the threshold of measurement, which is the process lesson: once a palette is measurable, “looks right” stops being the acceptance test.

The shipped default, STABILITY_TOKEN_DEFAULTS, is that palette hardened in place — five of eight values unchanged. The failing purples are pulled apart (ΔE 2.8 to 7.0 under deuteranopia), the grey tail is internally light-ordered from stable through strong inversion, and every adjacent pair now clears the 6.0 floor on every simulated axis (worst pair: 7.0 deuteranopic, 6.8 tritanopic). The repairs were found the same way the defect was — a validator over OKLab with CVD simulation — and are exactly as re-runnable.

Adjacent boundaries in the pale register

The shipped field stays pale behind chart content while each adjacent class clears the colour-vision floor.

windgram default: adjacent pairs 12.2, 17.9, 11.1, 11.2, 19.5, 10.0, 7.8 (worst 7.8 normal, 7.0 deuteranopia, 7.8 protanopia). canadarasp: adjacent pairs 11.3, 26.4, 11.8, 13.6, 30.1, 24.9, 16.2 (worst 11.3 normal, 1.8 deuteranopia, 7.3 protanopia).

Adjacent-pair colour difference of the default stability ramp and the historical canadarasp palette, under normal vision and simulated deuteranopia Seven adjacent class pairs, from very unstable through strong inversion, left to right.windgram default: adjacent pairs 12.2, 17.9, 11.1, 11.2, 19.5, 10.0, 7.8 (worst 7.8 normal, 7.0 deuteranopia, 7.8 protanopia). canadarasp: adjacent pairs 11.3, 26.4, 11.8, 13.6, 30.1, 24.9, 16.2 (worst 11.3 normal, 1.8 deuteranopia, 7.3 protanopia). The validator's adjacent-pair target of 8 is marked.adjacent ΔEwindgram defaultcanadaraspdeuteranopia windgram default — Very unstable to Unstable: ΔE 12.2 normal, 9.0 deuteranopia, 12.8 protanopia windgram default — Very unstable to Unstable under deuteranopia: ΔE 9.0 windgram default — Unstable to Conditional · strong: ΔE 17.9 normal, 17.3 deuteranopia, 18.6 protanopia windgram default — Unstable to Conditional · strong under deuteranopia: ΔE 17.3 windgram default — Conditional · strong to Conditional: ΔE 11.1 normal, 7.0 deuteranopia, 10.6 protanopia windgram default — Conditional · strong to Conditional under deuteranopia: ΔE 7.0 windgram default — Conditional to Near neutral: ΔE 11.2 normal, 11.7 deuteranopia, 10.4 protanopia windgram default — Conditional to Near neutral under deuteranopia: ΔE 11.7 windgram default — Near neutral to Stable: ΔE 19.5 normal, 20.4 deuteranopia, 16.3 protanopia windgram default — Near neutral to Stable under deuteranopia: ΔE 20.4 windgram default — Stable to Inversion: ΔE 10.0 normal, 10.5 deuteranopia, 9.3 protanopia windgram default — Stable to Inversion under deuteranopia: ΔE 10.5 windgram default — Inversion to Strong inversion: ΔE 7.8 normal, 7.8 deuteranopia, 7.8 protanopia windgram default — Inversion to Strong inversion under deuteranopia: ΔE 7.8 7.8 canadarasp — Very unstable to Unstable: ΔE 11.3 normal, 7.0 deuteranopia, 11.8 protanopia canadarasp — Very unstable to Unstable under deuteranopia: ΔE 7.0 11.3 canadarasp — Unstable to Conditional · strong: ΔE 26.4 normal, 25.1 deuteranopia, 28.0 protanopia canadarasp — Unstable to Conditional · strong under deuteranopia: ΔE 25.1 canadarasp — Conditional · strong to Conditional: ΔE 11.8 normal, 1.8 deuteranopia, 7.3 protanopia canadarasp — Conditional · strong to Conditional under deuteranopia: ΔE 1.8 1.8 canadarasp — Conditional to Near neutral: ΔE 13.6 normal, 14.2 deuteranopia, 12.5 protanopia canadarasp — Conditional to Near neutral under deuteranopia: ΔE 14.2 canadarasp — Near neutral to Stable: ΔE 30.1 normal, 32.0 deuteranopia, 27.4 protanopia canadarasp — Near neutral to Stable under deuteranopia: ΔE 32.0 canadarasp — Stable to Inversion: ΔE 24.9 normal, 26.2 deuteranopia, 24.3 protanopia canadarasp — Stable to Inversion under deuteranopia: ΔE 26.2 canadarasp — Inversion to Strong inversion: ΔE 16.2 normal, 16.2 deuteranopia, 16.2 protanopia canadarasp — Inversion to Strong inversion under deuteranopia: ΔE 16.2 ΔE 8 · adjacent-pair targetVeryunstableUnstableConditionalstrongConditionalNearneutralStableInversionStronginversion
The shipped default repairs its ancestor's deuteranopia-blind purple boundary while staying in a pale background register. Bars show normal-vision ΔE; dots show simulated deuteranopia.Units OKLab ΔE ×100

One look

The package ships this palette and no theme catalogue. Anything a consumer legitimately wants different is a token: the eight --wg-stab-* custom properties restyle the field without forking the renderer, which is precisely how the palette’s own ancestor was built and tuned. Defaults are argued on claims anyone can re-run — figure-ground first, worst-reader floors on the boundaries the register can honestly hold, and the residue documented rather than pretended away.