Skip to content

Thermocline v1.2.0

Latest

Choose a tag to compare

@timlichtenberg timlichtenberg released this 25 Jul 20:51
· 4 commits to main since this release
b6ffc56

Added

  • Accretion domain colour: clay (--pt-dom-accretion #A38F7A) for Morrigan and the accretion domain, taking the module palette to seven. It keeps a minimum ΔE of 29.6 against the other six under protanopia, deuteranopia, and tritanopia (Machado 2009 at full severity, CIE76 ΔE*ab), comfortably above the 16.0 floor the check enforces on the dark surface set, and holds the same figure on the light set. The intuitive earth tones land far below that floor, burnt sienna at 4.8 and umber at 9.0 against Outgassing. An olive-green clears the domains at 21.0 and then sits 3.8 from --pt-verdant under deuteranopia, trading a collision with the modules for one with the accents, and the brand green itself reaches only 15.7 against Atmosphere under tritanopia. Clay clears both sets by the widest margin of any domain. Its two nearest neighbours in the rest of the palette are --pt-verdant at 11.39 and --pt-positive at 11.45, both under deuteranopia, close enough that a figure mixing accretion data with status colour needs a legend either way. Like Stellar and Atmosphere it is a light-toned hue, 2.8:1 on Paper, so small marks in it want a legend rather than the hue alone.
  • proteus-mpl gains DOMAINS["accretion"] and the Beamer theme gains ProteusDomAccretion, so the colour is available in figures and decks alongside the CSS token.
  • glyph_clay colorway in SVG, PNG, JPG, and PDF, completing the domain-tint matrix at seven. Same geometry as every other colorway, recoloured; the JPG sits on Void, where clay reads at 6.5:1 against 2.8:1 on paper.
  • Morrigan joins the module identity grid and the coupling diagram in the community kit, as the accretion node.
  • tokens/check_cvd.py, run in CI alongside the other token checks, measures every domain pair under all three dichromacies on both surface sets and fails when one drops below its floor. The docs asked contributors to re-run the CVD check before remapping or extending the domain colours; this is that check. It reads the hexes from tokens.css, names the metric, and lists the rejected accretion candidates with the hex each figure was measured on, so the quoted numbers can be reproduced rather than taken on trust. It also measures the domains against the accents, status colours, and ramp anchors and reports those pairs without failing on them, since the tight ones there (Tidal 3.1 from --pt-abyss, Interior 3.9 from --pt-warning) can only be closed by recolouring one set or the other. It holds the roster as well as the floors: it fails when tokens.css declares a set of domains other than the one it expects, and on either surface block it fails on a domain written in a form it cannot measure, so a colour that is removed, renamed, or moved behind a var() cannot pass as a smaller palette with a wider minimum. CROSS_PALETTE is a required minimum rather than an exact roster, so an accent that is renamed or moved behind a var() fails the check while a newly added one is measured once it is listed there. It reads the two blocks the palette ships in and nothing else, so a value set inside a media query or a cascade layer cannot stand in for the shipped one. Everywhere else in the file a domain colour is allowed only when it repeats the value one of those two blocks gives that domain. Custom properties inherit, so :root:root, :root body and an @media screen wrapper all reach the page: a component retint that restores a base colour passes, and any other colour is refused rather than measured underneath the rule that would actually win. A selector list is judged part by part, because a list such as :root, body also declares the colour on body, where a later :root block takes it back for the root element alone and leaves the visible page on the value the list gave it. An @property registration is read the same way through its initial-value, which is what an element computes wherever the property does not reach it by inheritance, so a registration declaring inherits: false cannot hand the whole page below the root a colour the palette never measured. Every initial-value in the block is read, since a repeated descriptor resolves the way a repeated declaration does, and the at-rule keyword is matched in whatever case it is written, the way CSS matches it. It also requires the light block to come after the base one, since the two select the root element at equal specificity and the block written last is the one that applies, and it refuses a nested rule rather than skipping it.
  • tokens/check_cvd_selftest.py, also run in CI, pins how the separability check reads tokens.css. It runs sixty-three cases against a scratch copy of tokens/ and asserts two things about each, the exit code and the reason printed with it, so a case cannot pass on an unrelated failure. Eighteen are valid CSS the check has to keep reading, among them the committed palette itself, a compound [data-theme="light"] .hero rule and a :root > .panel subtree that each repeat a shipped colour, that same colour repeated under @media screen, again through a :root, body list, and again as the initial value of an @property registration, a theme selector written as a selector list, with single quotes, without quotes, and with the case-insensitive i flag against a differently cased value, a final declaration with no semicolon, a byte-order mark, a priority flag, and braces inside quoted, parenthesised, and custom-property values. The other forty-five have to be caught, among them a domain behind a var(), a three-digit hex, a missing or extra token, a value shadowed later in the same block, a nested & rule, a collision hidden behind comment markers inside string values, a good value left inside a media query or cascade layer, a domain retinted through :root:root, html, :root body, a subtree, @media screen, @supports, or two nested at-rules, either palette block listed with another element and then taken back for the root alone, an @property registration whose initial value is another domain's colour, is missing altogether, or opens on a shipped colour and repeats the descriptor with an unmeasured one, a light block written before the base palette, and a collision confined to one surface set or to one dichromacy. The unmutated case also pins one measured figure per dichromacy, so a simulation matrix cannot be rewritten and still report the palette as sound. The working tree is never touched.

Changed

  • The colour-blind safety notes in docs/module-colors.md and docs/figure-conventions.md now name the difference metric and report both surface sets. The 16.36 minimum describes the dark set; on the light set, where Stellar deepens to #C8860F for contrast on paper, Interior against Stellar measures 11.44 under deuteranopia, just under the ≥ 12 target. Closing it would mean recolouring a module identity across the ecosystem, so it is documented as a known limit with the advice to label those two series in light-surface figures.