Every color in NebulaKit comes from one engine in the stylesheet. Components pass a few props and the engine turns them into backgrounds, text, lines and interaction states that stay consistent across every hue, tone and theme. This page explains how it is designed.
How to pick colors for your app is covered on Tones + variants, what the engine costs at runtime on Performance.
Every component draws its colors through Box, so the same props give the same colors in every component.
A nested element always shows its own colors, never the ones of its parent.
No color is computed in JavaScript. The stylesheet resolves every color.
Every element carries its theme itself, so a menu or a dialog rendered at the end of the page keeps the theme of the place it was opened from.
Each theme has its own palette of eight colors in five tones, plus neutral and max. Neutral is the page itself, max is its opposite.
Saturation and lightness are set per tone and shared by every color. Each color adds small shifts of its own, so a tone reads equally strong in every hue.
Yellow, green and cyan hues look lighter than others at the same lightness, which shows most on pale tones. Those tones correct for it, so pale amber sits next to pale blue without standing out.
The light theme runs from a white page outwards, the dark theme from a black page. The high tone sits in the middle and looks alike in both.
The system theme follows the operating system through a media query, so it is right from the first paint of the server HTML, before any script runs.
A region in the opposite theme, such as a dark panel in a light app, is a flipped theme set through StylingIsland, never a stronger tone. Everything inside it sits on that theme's side of the scale and keeps its interaction states.
The brand set on NebkitProvider or StylingIsland is the color of every component that sets no color of its own.
A tone is a distance from the page. Hover, press and selection move the color further away, darker in light and lighter in dark, with steps that grow with the tone.
Max already sits at the far end of the scale, so its states step back toward the page. Controls side by side share one tone, so they always move together.
Steps are measured in perceptual lightness, so a step looks the same size next to black as next to white. Both themes share one set of rules and differ only in a few deliberate choices.
A hovered button keeps its color and only gets darker or lighter.
Text on a filled surface is either black or white, chosen by the lightness of the fill. There is no gray in between, even right at the switching point.
On a tone's own color every hue carries the same text. A lifted, sunken or selected surface can switch it where its own lightness calls for it.
A solid fill is the opaque palette color. A soft fill is a see-through tint of it, so it takes on whatever sits behind it.
Bright hues tint more strongly than dark ones at the same opacity. Each color is evened out, so soft red reads as strong as soft blue.
Colored text without a fill goes through the same states as a solid fill, step for step.
A surface kind is a recipe, not a strength. base is the tone's own color. raised is lifted between its hover and press colors and keeps stepping that way. raised-adaptive is lifted the same way, its states step in a direction tuned per tone. sunken is a well that stays close to the page on every tone. muted recedes toward the page until it is selected.
A selected surface rests on its own selected color and has its own hover, press and focus colors from there.
An element never takes its tone or surface from its parent, only from its own props and theme, so the same props give the same colors wherever it is placed.
An edge bounds a surface. It stands out away from the page in both themes, so its strength stays the same in light and dark.
A divider separates parts inside one surface and is lighter than that surface. In light it reads as a gap, in dark as a line closer to the eye, where a darker one would read as a crack. On the white page nothing is lighter, so there it steps darker.
A rule stands alone, like HorizontalRule. It ignores any fill and steps evenly from the page by tone.
In forced colors modes, such as Windows High Contrast, the browser drops backgrounds and uses the system colors. Every drawn surface keeps a thin outline there, so components whose shape comes from their fill stay visible.
Keyboard focus draws a full ring in the system highlight color, so it never looks like the outlines around it.