Deployment
healthy- Requests
- 18,402
- p99
- 42 ms
- Errors
- 0.02%
a colour system
Most colour systems are a list of hex values somebody chose. This one is the output of a compiler: every step of every ramp was solved against a perceptual contrast target, in every palette, and checked before it shipped. Link one file and you have all of it — light and dark included.
This swatch is #bf8253 — the accent family's solid step. It was not chosen. Its hue came from measuring an existing palette, its lightness was solved from a contrast target, and its chroma is 62% of the most sRGB can show at that lightness and hue.
oklch(0.6584 0.0985 57.71)
Pick the one that matches you. Each is complete — there is no step two.
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@noctua-world/colors/system/css/palette/ochre-balanced.css">npm install @noctua-world/colors
/* then, in your CSS */
@import "@noctua-world/colors/palette/ochre-balanced.css";@import "tailwindcss";
@import "@noctua-world/colors/tailwind/palette/ochre-balanced.css";
<!-- then -->
<div class="bg-surface text-fg border-border dark:bg-surface-subtle">cargo add noctua-colors-tokens --features ochre_balanced
use noctua_colors_tokens::ochre_balanced::light::accent;
let hex = accent::SOLID.hex;That is one palette, complete: the neutral ramp, every semantic name, and both modes — 29 KB over the wire. The file carrying all 39 is 741 KB, which is why the small one exists. Use it only if your users switch palettes at runtime.
Every family in every theme, in both modes. Tap any swatch to copy it — as hex, OKLCH, a CSS variable, a Rust path or a Tailwind class.
Roles are canonical; the numbers are aliases for interop. 2 gamuts emitted per token.
Roles are canonical; the numbers are aliases for interop. 2 gamuts emitted per token.
Separate from the twelve functional roles and shared by both modes. Interfaces need far finer gray resolution than twelve steps, and they need it concentrated where surfaces actually live — just below white and just above black — rather than spread evenly.
The cool and warm ramps are the same lightnesses at a different temperature — step placement depends only on the count and the density, so gray-7 and gray-cool-7 differ in tint and nothing else, and one can be swapped for the other without moving any contrast.
--nc-gray-*--nc-gray-cool-*--nc-gray-warm-*Every name an application can code against. Ten of these are families with a hue of their own, because their meanings have to be told apart without a legend; the rest are aliases onto one of those, because a family costs a full ramp in every theme, mode and gamut and an alias costs one line.
Colour alone cannot separate ten meanings for a dichromat — measured, and published in system/reports/colour-vision.md. Pair a status colour with an icon or a label.
349 contexts
accent · 30 contextsaccenthighlightnewexperimentalalphabetapreviewcanarynightlyprereleaselaunchedfeaturedpinnedstarredfavoritebookmarkedrecommendedpopulartrendingtoplimitedexclusivepremiumpropilotprototypeearlyaccessadminownermoderatordanger · 36 contextsdangererrornegativerejecteddeclinedcriticalfatalseverefailedfailingcanceledabortedterminatedwithdrawnrevokedsuspendedbannedforbiddenunauthorizedunreachableoutageunsupportedinvalidchargebackvoidedrefundedundeliveredunavailablebouncedspamhighunsafeinsecurecompromisedbreachedvulnerablewarning · 31 contextswarningdelayedcautionminorunresolvedconflictdegradedunstableunhealthydeprecatedoutdateddowngradedexpiringoverdueunpaidbackorderedoverflowstaleslowsuboptimaldecreasedfallingmediumunsigneduntrustednoncompliantunlicensedofftrackbehindpostponedrescheduledsuccess · 67 contextssuccesspositivefinishedcompletedoneresolvedrestoredmergedsolvedapprovedacceptedenabledvisiblepublicsharedexternalauthorizedhealthystableoperationalsupportedupdatedupgradedmigratedreleasedrestockeddeployedbuilttestedpassinginstalledsynceduploadeddownloadedverifiedvalidatedvalidconfirmedrenewedpaidsettledcaptureddeliveredavailablepreordercreatedpublishedduplicatedcheckedoptimalincreasedrisingsteadypeaklowsafesecureencryptedsignedtrustedpatchedcompliantcertifiedlicensedontrackaheadconcludedinfo · 26 contextsinfonormaldefaultnoticehinttipopenreopenedansweredduplicateneedsinfomaintenancefreetrialquotedinvoicedbillededitedmodifiedrenamedmovedimportedexportedfinalmemberreturningurgent · 20 contextsurgentalertmajorstalledblockedtimeoutexpiredretriedescalateddisputedflaggedreportedrequiredmandatoryquarantinedisolatedthrottledratelimitedoverloadedcongestedwaiting · 22 contextswaitingpendingqueuedscheduledstandbyidlesubmittedresubmittedreviewappealedreservedbookedoccupiedinvitedsnoozedunreadunseenonholdupcomingprovisionaltentativewaitlistactive · 17 contextsactiverunningbusyreadyresumedonlineconnectedliverealtimeassignedclaimedjoinedfocusedselectedfreshcachedongoingprogress · 29 contextsprogresssentreceivedloadinginitializingstartingstoppingretryingprocessingrecoveringreconnectingmigratingstageddeployingbuildingtestinginstallingupdatingsyncinguploadingdownloadingshippedtransitdispatchedpackingorderedreturnedonboardingoffboardingneutral · 71 contextsinactivedraftarchivedpausedsubtlesilentdebugdisabledstoppedskippedpartialcloseddismissedwontfixignoredunansweredlockedunlockedfrozenprivateinternalrestrictedreadonlyeditableofflinedisconnecteddormantlapsedlegacyobsoleteretireddiscontinuedunreleasedrevertedrollbackuninstalledunsyncedunverifiedunconfirmedvacantunclaimedunassignedunselectedreadseenrepliedforwardedunchangedunpublisheddeletedemptytruncatedcollapsedexpandedfilledfulloptionalinheritedcustomsystemautomaticmanualoverriddenindeterminateuncheckedsandboxedanonymousguestdeactivatedsilencedHues spread around the wheel rather than along a path, at equal perceptual intervals rather than equal angles — a fixed rotation through the greens changes appearance far less than the same rotation through the blues. Each set also spans a range of lightness, because hue is precisely the axis a dichromat is missing, and lightness is what is left.
Six is what a generated set can keep apart under all three dichromacies. A set marked labelled goes past that deliberately and says so: its legend has to name every series, and the measured margins are published in system/reports/colour-vision.md rather than assumed away.
chart123456chart-alt123456chart-wide123456789101112chart-trio123A traffic light, read in order. Stops are spaced by perceptual distance along the hue path rather than evenly in the parameter, and lightness descends by equal steps of stop index — because lightness is the only axis left when hue is unavailable, and only the index guarantees the step is even.
level012345678910magnitudelowerlowmediumhighhigherReal alpha, not a hex solved to composite over one backdrop. Each stop is color-mix(in oklab, <token> N%, transparent), which is premultiplied — so it is the token at that opacity and composites correctly over anything. Shown here over two different surfaces for that reason.
No contrast gate can audit these: contrast is a property of two opaque colours, and a wash has none until it is composited.
surfacesurface-raisedA palette is judged in use, not as a grid of squares. Everything below is built from the semantic tokens and follows whichever theme and mode is selected above.
Three contrast targets were not met on the accent family.
success and danger sit close under deuteranopia.
29 files written to system/.
Generated artifacts are committed, so every consumption path works with no build step: submodule, subtree, sparse checkout, a plain copy, or a raw URL.
<!-- three files: the dense grays, the semantic contract, and
the default theme's values -->
<link rel="stylesheet" href="system/css/ramp.css">
<link rel="stylesheet" href="system/css/contexts.css">
<link rel="stylesheet" href="system/css/ochre-balanced.css">
<!-- or index.css: all of the above plus every other theme, and a name
that survives a theme being renamed -->
.card {
background: var(--nc-color-surface-raised);
color: var(--nc-color-fg);
border: 1px solid var(--nc-color-border);
}@import "tailwindcss";
@import "../noctua-colors/system/tailwind/theme.css";
<!-- then -->
<div class="bg-surface text-fg border-border">noctua-colors-tokens = { path = "../noctua-colors/system/rust" }
use noctua_colors_tokens::balanced::dark::accent;
let hex = accent::SOLID.hex;import { palette } from "../noctua-colors/system/ts/index.js";
const solid = palette.themes.balanced.light.families.accent.steps[8];
solid.renditions[0].hex;import "."
Rectangle {
color: NoctuaDark.surface
border.color: NoctuaDark.border
}Nothing here was picked by eye. A colour's saturation is stored as a fraction of the most a display can actually show at that lightness and hue, so the same token is richer on a wide-gamut screen without being redefined. And a step's lightness is solved from a contrast target using APCA, which — unlike WCAG 2.x — models polarity, and so does not rate a too-weak dark-mode pair as better than a comfortable light-mode one.
Every build runs 48,441 checks across every palette, mode and pair, and fails on a regression. Colour-vision simulation runs too — and where a pair genuinely cannot be told apart, that is published rather than hidden.
There are 39 palettes, and every one of them went through the same gates.