HelixOverviewComponentsMapsGraphIntroductionInstallationDeriving a themeSandboxTheme builderVisual testingCortexDocsEngramBuildingTraceBuildingRecallDeclaredServicesInsightsGet in touchSource on GitHub
Foundations
Surfaces and separationSeparate each surface from what is behind it with exactly one of four
things. The four are a fill, an edge, a lift, and space. A design language
leads with one of them. Helix uses an edge for surfaces on the page and a lift
for surfaces above it. A card has a hairline border and no shadow. A menu has a
shadow and no hairline border. The surface ladder below sets the ground color
for each place a surface can sit.The four ways to separate a surfaceFillA ground color different from what is behind itEdgeA borderLiftA shadowSpaceNothing at all. The gap separates itWhen a design system uses two or three of these without a plan, a card can end
up with a border, a shadow, and a tint, each added by someone solving the same
problem again.Helix sets the choice with two pairs of policy tokens:TokenValueFor--helix-border-surfacevar(--helix-border-thin)The border of a surface in the page flow. Set it to zero to separate surfaces with shadows.--helix-shadow-surfacevar(--helix-shadow-none)The shadow of a surface in the page flow. Set it to none to separate surfaces with borders.--helix-border-floating0The border of a surface that floats over content.--helix-shadow-floatingvar(--helix-shadow-lg)The shadow of a surface that floats over content.To make every component shadow-first, set the border pair to zero and give the
shadow pair a value. Reverse them to go border-first. Every Helix surface reads
both pairs, and this is checked. A surface that reads only one pair is easy to
miss. If it reads the shadow token and sets border: none directly, a theme can
change it in one direction and not in the other.The surface ladderA surface's ground color token names where the surface sits: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.raised goes one step down the ramp. A selected tab's pill, a hovered row,
and an option under the pointer all have the page on every side. Only their
fill separates them, and on a white page a fill has to be a step darker to be
seen. overlay works the other way. Its shadow and what it covers separate it,
so on a white page it is white.Using the page color inside a component--helix-color-background is the page color. Do not use it inside a
component./* Wrong. Claims the component is sitting on the page. */
.marker{background:var(--helix-color-background);}Using the page color inside a component assumes the component sits on the page.
That is right only when the component is placed directly on the page.Timeline's marker had this mistake. It was painted the page color to hide the
rail behind it, so it came out darker than any surface other than the page. On a
card, it showed as a black disc. The sparkline's ring terminus had the same
mistake and looked the same.Both looked correct for months, because components are usually built on the
page. The mistake shows on a card, a drawer, a toast, a panel, or a photograph,
and components are not built on any of those.Put the color behind a hook that defaults to the page color. When the component
sits on another surface, the hook can be set to match. Helix requires this of
its own components, and the same applies to anything you build with it.ContrastThe theme engine measures each ink against the ground it is actually painted
on. The ladder records which pairs occur. For example, foreground on
surface-overlay is a real pair, and foreground-on-primary on surface is
not. A field's fill, well, is translucent, so the text in a field is measured
against the well over each surface. A lit row's fill, hover, is measured the
same way. See Color.