HelixOverviewComponentsMapsGraphIntroductionInstallationDeriving a themeSandboxTheme builderVisual testingCortexDocsEngramBuildingTraceBuildingRecallDeclaredServicesInsightsGet in touchSource on GitHub
Foundations
ColorColors are the largest group of the library's tokens, and they make up most of
a theme. Each color token is named for its role. No token is named for how it
looks, because a name like --brand-blue-500 becomes wrong as soon as you change
the theme.Color rolesGroundsGrounds are the colors that content sits on. background is the page itself.
The surfaces above it form an ordered ladder:TokenSampleFor--helix-color-backgroundThe page background. Every other surface sits on it.--helix-color-surfaceA surface on the page, such as a card or a panel. The separation policy sets it apart with a fill, a border, or a shadow.--helix-color-surface-raisedA raised surface on the page, such as a selected tab or a hovered row. Its fill is one step down the ramp.--helix-color-surface-overlayA surface over the page, such as a menu, a popover, or a dialog. Its shadow sets it apart.--helix-color-surface-sunkenA recessed surface that holds a value, such as an input, a track, or a groove. Its fill sets it apart.Choose a surface by where the element sits relative to the page. This is the
choice people most often get wrong. See Surfaces and
separation.A field is filled with well: 5% of foreground laid over whatever the field
sits on, so its color depends on the surface under the field. A row under the
pointer or the keyboard, such as an option, is lit with hover the same way:
8% of foreground in light and 12% in dark.InksInks are the colors that go on grounds. The base ink is foreground.foreground-muted is for text that recedes and is still read, such as a
placeholder, a hint, or a caption. The engine solves it to the contrast
target.foreground-subtle recedes further. It is for marks that are not text, such
as a separator, a chevron, or a grip, and for the text of a disabled control.
It is a fixed step of the gray ramp, and the engine does not solve it, so its
contrast is not guaranteed. Use foreground-muted for text that is read.Text on a filled color takes the ink named for that fill:
foreground-on-primary on primary-surfaceforeground-on-accent on accent-surfaceforeground-on-status on the four status fillsIntentIntent colors show what something means.primary and accent are for emphasis. Each has -hover, -active, and
-subtle variants. With no brand color set, both are ink: near-black in
light mode and near-white in dark mode.info, success, warning, and danger are for status. Each has a
-surface variant, the filled ground that carries foreground-on-status,
such as a danger button or a badge in a status tone, and a -surface-hover
variant for that fill under the pointer.Rise and fallrise and fall color a value by which way it moved, such as a price. They
are success and danger unless you set them. Mainland China, Taiwan, and
Japan draw a rise in red, so a page for one of those markets swaps the two. See
Rise and fall colors.SeriesHelix has twenty-four series colors for charts. They are ordered, so a chart
with three lines uses the first three. We chose the colors so that the first
ones in the list stay easy to tell apart, however many a chart uses.SyntaxThe eight syntax roles for code are callable, comment, constant,
invalid, keyword, name, string, and tag. A full editor theme has more
roles. Helix has fewer because code in documentation only needs to be read.ScrollbarsScroll Area, Chat, and Virtual List color their scrollbars with three
tokens:--helix-scrollbar-thumb-color is the thumb at rest. It is a mid gray that
measures at least 3:1 against the page and every surface, in both modes.--helix-scrollbar-thumb-hover-color is the thumb while the pointer is over
its scroller. It is foreground-subtle.--helix-scrollbar-track-color is the track behind the thumb. It is
transparent, so the scroller's own ground shows through.Each of the three reads its own hook first, such as --scroll-area-thumb-color
or --chat-scrollbar-thumb-color, so you can recolor one scroller and leave
the rest. Set the tokens to recolor all three. Other families that scroll keep
the browser's own scrollbar.Why colors use OKLCHEvery Helix color is written in OKLCH. In sRGB, equal numeric steps do not look
equal, which makes ramps and contrast math unreliable. A ramp with even numeric
steps looks uneven. A contrast solver that works in sRGB cannot converge on a
target that it can measure.OKLCH lets the theme engine solve for a palette from your brand color. See
Deriving a theme.How the engine meets contrast targetsThe engine measures each ink against the ground it will be painted on. A brand
color that already meets the target there is used as it is. For any other color,
the engine searches for the lightness that meets the target. A pair that cannot
meet its target, such as one with a color you pinned, is reported in
violations.foreground and foreground-muted are also written in fields, so the engine
solves them against the well over the page, a card, a raised surface, a dialog,
and a sunken surface. The report names each of these grounds as the well over
its surface, such as color-well over color-surface.Both inks are also written on a lit row and on a selected row. The engine solves
them against these grounds as well:hover over each of the same five surfaces, such as color-hover over color-surfaceprimary-subtle and accent-subtle, the tints a selected row takesthe well over each tint, for a field on a selected rowforeground-subtle is the one ink the engine does not solve. The range of
readable grays has room for one quiet ink, and foreground-muted is that ink, so
foreground-subtle stays a fixed step of the stylesheet's gray ramp and its
contrast is not guaranteed. A component paints its quiet text in
foreground-muted, and keeps foreground-subtle for marks that are not text and
for the text of a disabled control.The engine can solve only the pairs it knows about, so we check two things:We compare the engine's surface model with what the components paint.We measure every pair on a rendered page.The second check finds pairs that no single CSS rule states. Only eleven rules
in the library set an ink and a ground together. Another 165 set only an ink,
and 112 set only a ground. The cascade combines them into the rest of the
pairs, and each of those pairs must still be readable.Use only semantic tokens/* Yours to read. */color:var(--helix-color-foreground-muted);/* Not yours to read, and the `tokens` rule fails the build on it. */color:var(--_helix-neutral-500);Primitive tokens such as --_helix-neutral-500 belong to helix/styles. The
underscore marks them as private. Components use only semantic tokens, such as
--helix-color-primary. A theme can change what a semantic token resolves to,
and no component depends on which step of which ramp a color comes from.