HelixOverviewComponentsMapsGraphIntroductionInstallationDeriving a themeSandboxTheme builderVisual testingCortexDocsEngramBuildingTraceBuildingRecallDeclaredServicesInsightsGet in touchSource on GitHub
Theming
Visual axesA component's visual options come from a registry of axes. Each axis has a
fixed set of values. You cannot add a value of your own, and a component can
accept a subset of an axis's values.AxisValuesemphasisghost, muted, outlined, solid, subtlematerialfrosted, glass, transparentnestingheading, peer, subordinateorientationauto, horizontal, verticalseparationelevated, filled, outlined, plainshapearc, circle, diamond, dots, pill, pin, ring, rounded, square, textsizesm, md, lg, fulltoneThe status and intent words: primary, accent, neutral, danger, and so onwidthauto, full<helix-buttonemphasis="solid"tone="primary">Save</helix-button><helix-cardseparation="outlined">…</helix-card><helix-drawermaterial="glass">…</helix-drawer>Why there is no appearance attributeHelix once used one attribute, appearance, for at least four different
settings across both tiers:where a surface ends, such as outlined or plain on a Cardhow much ink a control uses, such as ghost or solid on a Buttonwhich widget is rendered, such as segmented or underlined on Tabsin one case, a tone filed in the wrong placeoutlined appeared in two of those groups with different meanings. On a Card,
it set where the surface ends. On a Button, it set how much ink the button uses.
With one attribute for both, you could not set one without also setting the
other.Helix does not accept appearance. A component that adds it back fails the
build.When a property needs two axesIf two values can apply at the same time, they belong on two axes.Card is the example. separation sets where its surface ends, and material
sets what fills it. A card can be outlined and glass at once, so the two values
sit on two axes:<helix-cardmaterial="glass"separation="outlined">…</helix-card>If you are adding a property and cannot decide which axis it belongs on, it is
usually two properties.Use variant for a choice between widgets<helix-tabsvariant="segmented">…</helix-tabs><helix-time-pickervariant="wheels">…</helix-time-picker>variant has no registry, because its values belong to one component. For
example:segmented or underlinedlist or wheelsgrip or lineA variant is a choice between two widgets. That choice changes structure
wherever it is made, so variant can appear on either tier.Do not use variant for anything an axis already covers. That is the
problem appearance had.Axes on both tiersStyled axes do not appear on the headless tier, because the headless tier has
nothing to paint and no box to size. The exceptions are the axes that are not
about paint:tone sets what a thing means, and that meaning holds without a stylesheet.shape sets what is rendered. A meter at ring renders an arc where it would
otherwise render a bar, so the markup differs before any CSS rule is written.nesting sets what depth means. A tree at subordinate ranks its rows. A
tree at heading makes each branch the name of the list under it. A file
explorer does neither. All of this holds for a tree with no styles.orientation is on both tiers for the same reason as shape.
aria-orientation is markup, and the toolbar, the splitter, the divider, and
the table column all write it with nothing painted.size is the opposite case. Pictures and regions take a size. Controls do
not, because they take their height from their region through data-density.
So size appears only where something is painted.When a family takes neutraltone is the one axis whose values the table above does not list in full.
Families have decided one at a time whether to accept neutral. Three of them
reached the same rule in their own declarations, so the rule is stated here.A family that reports a status accepts only the status values.Callout and
Toast both state this. A callout reports a status and never a brand role, so
it accepts four of the seven tones. There is no neutral status. Accepting a
value the family cannot draw is worse than leaving the value out.A family whose resting state has no tone does not need neutral.Text
gives this reason for having no default value. A value that matches no rule
would fall back to the inherited ink. It would look like it works and change
nothing. Leaving the attribute off already gives the resting state.Use neutral for something that has a tone but reports no status. Examples
are a gray tag beside six colored ones, or a button that is not the primary
action. Announcement accepts neutral as a value you write, so you can set
the untoned look by name. Leaving the attribute off would give the same look
without a name. That is the only reason to accept this value.What each shape drawsSeveral shape values sound alike and draw different geometries:ValueWhat it drawsarcAn open curve, a gauge that stops short of a full turncircleA full circle, with the whole edge at radii-fulldiamondA square turned on its point, with its corners at the middles of the box's sides, like a flowchart's decision stepdotsSeparate marks, with nothing joining thempillA rectangle whose short ends are half-circles, the round of radii-full on a box wider than it is tallpinA teardrop that marks a place with its tip, such as where a journey ends on a mapringA closed curve, the whole turn drawn as a bandroundedCorners cut to radii-md, still a rectanglesquareSharp corners, the shape rounded softenstextThe shape of a line of text, as wide and as tall as one linesquare has sharp corners, and rounded has corners cut to radii-md.
They were once one word. An avatar drawing radii-md called its shape square,
and a QR code drawing sharp modules also called its shape square, on the same
registered axis. A check can confirm only that a value is in the registry, so no
check could catch this. The table above keeps it from happening again.What each separation drawsseparation sets where a box ends. Nearly every family leaves out elevated,
and this section explains why.ValueWhat it drawselevatedA surface lifted off the page, with a shadow under itfilledA ground of its own, and no edgeoutlinedAn edge over the ground it already hasplainNeither, so the box ends where its content doesMost families leave out elevated because of what the value means. A shadow
shows that the box floats above the page. That is true of a card picked up off a
grid and of an announcement laid over the content. It is false of:a row in a feeda groove a thumb sits ina checkboxa bar across a videoA family that accepts only the other three values needs no reason for it. A
family that accepts elevated has made a choice, and Card and Announcement
both accept it.What each nesting value doesThe three nesting values are three ways to show the same depth in a tree.
Each suits a different kind of tree.ValueThe tree it suitsWhat movesheadingA documentation sidebar, where a branch names the list it holds and is not a pageThe branch, which takes the rail heading. Its children keep the row they hadpeerA file explorer, where every row is the same kind of thingNothing. Only the indent shows depthsubordinateSide navigation, a categorization, a facet listThe nested row, one step down the type scale and no furtherheading and subordinate work differently. subordinate depends on depth and
makes the child quieter. heading depends on whether a row has children and
turns the parent into a heading. As a result:Under heading, a branch at any depth is a heading.Under subordinate, the step down stops after one level.Both leave a top-level leaf beside a branch alone, for opposite reasons.What each emphasis drawsemphasis sets how prominent a control is. Its values form one ladder, and one
value changes meaning partway along.ValueWhat it drawssolidThe tone as a full ground, with the ink flipped to read on itsubtleA wash of the tone and no edge, the quietest thing that still has a surfaceoutlinedAn edge in the toneghostNothing at rest, the tone carried by the ink alonemutedQuieter ink, where there is no surface to quietoutlined is the value that changes meaning. On a control, it adds an edge
and removes the fill, which makes it quieter than solid. On a chart mark, the
fill stays under the edge, because the area shows a value and removing the fill
would hide that value. In both cases, outlined sets how much ink the element
uses, based on what the element needs the ink for.muted is for something whose only ink is its letterforms. Only text takes
it, because everything else has a surface to make quieter first. Leaving it off
other families is intentional.What each material drawsEach material value sets two declarations together: a translucent ground and a
backdrop filter. The three values set that pair at different strengths. They
work only as a pair. A translucent ground without a backdrop filter looks
smeared, and a filter behind an opaque ground cannot be seen. With no
material, a surface is opaque.ValueWhat it drawsglassA translucent ground and the backdrop filter that makes it readfrostedThe same pair turned up, a heavier ground under a heavier blurtransparentNeither, so the page shows through structure and spacingUse frosted for a surface that has to stay readable over a busy page. A theme
sets how strong each pair is. The three values choose which pair a surface uses.What each orientation doesA component reads orientation as a value, because it changes more than paint.
It turns:the drag axisthe arrow keysaria-orientationA version done only in CSS would look stacked while behaving side by side, and
the arrow keys would move the wrong way.ValueWhat it doeshorizontalLays out along the inline axis and sets aria-orientation to matchverticalLays out along the block axis and sets aria-orientation to matchautoPicks a direction from the room the component has, and turns again when that changesauto uses the component's own width, never the viewport's. A splitter in a
300px panel of a 1400px window has no room for two panes side by side. A media
query would give it the desktop layout. A ResizeObserver on the host measures
the width the splitter actually has.What each size drawssize applies to pictures and regions, and never to controls. That rule sets
the scope of the axis. One of the four values is a different kind of step.ValueWhat it drawssmThe small stepmdThe middle step, and the resting state for a family that has onelgThe large stepfullThe whole region, less the same inset every other step already leavesfull is one more step on the size axis. A takeover dialog is the same
setting taken further, so it belongs on this axis. A separate fullscreen
boolean would be one component's own way to set a value the registry already
covers.What each tone meansThe table at the top describes tone in words and names four of its values.
tone has seven fixed values:ValueWhat it meansprimaryThe main action, or the brand speaking in its own voiceaccentThe second brand voice, for a thing that must not read as primaryneutralA tone that reports no statusinfoWorth knowing, with nothing to do about itsuccessFinished, or correctwarningMay go wrong, or needs attention before it doesdangerDestructive, or already wrongThe first three are intents, and the last four are statuses. A family that
accepts a subset is choosing between these two groups. The section on neutral
above says when to accept it.What each width value doeswidth sets a policy. Two buttons placed together can have different widths.
By the same test that keeps height off each control, width belongs on each
component, separate from size.ValueWhat it drawsautoAs wide as its content. This is the resting behavior, written outfullAll the inline space its parent gives itA width that takes a number is a different property with the same name. A
canvas and a table column take a length, so they are not on this axis.