HelixOverviewComponentsMapsGraphIntroductionInstallationDeriving a themeSandboxTheme builderVisual testingCortexDocsEngramBuildingTraceBuildingRecallDeclaredServicesInsightsGet in touchSource on GitHub
Essentials
Overlays and layeringThese components are overlays, and they all work the same way:DialogDrawerPopoverMenuSelectComboboxTooltipMegamenuSpeed DialCommandTourKnowing how they work helps you avoid most overlay problems.Overlays render in the top layerEvery overlay uses the popover API, so the browser draws it in the top layer.
The top layer is above everything on the page, and these do not affect it:z-indexoverflow: hiddentransforma stacking context in an ancestorAs a result, a menu inside a scrolling panel is not clipped, and a tooltip
inside a transformed card is not trapped in its stacking context. You do not
configure a portal or pass appendTo, because the browser handles the layering.Do not raise an overlay with z-index. It has no effect on a top-layer
element. If you want to use it, something else is wrong.How overlays are positionedOverlays are placed with @floating-ui/dom, which does the following:It flips a panel to the other side of its anchor when there is no room below.It moves a panel placed beside its anchor below or above when there is no room
on either side.It shifts a panel to stay on screen.It sizes a panel to the available space.CSS anchor positioning is the platform's own way to do this. We do not use it
yet, because it is not available in every browser the rest of Helix supports.
See Browser support.While an overlay is open, its position is recomputed on scroll and resize. The
recomputing stops when the overlay closes.How overlays closeLight dismiss comes from the platform: click outside the overlay, or press
Escape. Helix makes light dismiss work correctly across shadow boundaries.document.activeElement stops at the outermost host. Focus inside a shadow tree
is reported as the element that contains the tree. A simple check for whether
focus is still inside the overlay never matches, so the overlay would close as
soon as anything inside it is focused. Every way of closing an overlay in Helix
checks focus by walking through the shadow roots.Nested overlays work because lifecycle events do not bubble.
helix-before-open and helix-before-close do not bubble and are not composed,
so a menu that closes inside a dialog does not close the dialog. Escape closes
the innermost overlay and leaves the others open. The overlay that closes
cancels the key press, so a Dialog or Drawer around it stays open until you
press Escape again.Where focus goesA modal overlay keeps Tab inside itself while it is open, and it returns focus
to the element that opened it. Dialog does this, and so does Drawer unless
you set modal="false". Set dismissible on either to draw a close control in
the corner by the title. The page behind a modal Dialog or Drawer does not
scroll while it is open.Opening a Popover moves focus to the first control in its panel, or to the
panel where it holds none, and closing it from inside gives focus back to the
trigger, however it closes, setting open included. It does not trap focus
unless you set modal, which keeps Tab in the panel until it closes and hides
the rest of the page from screen readers meanwhile. When focus moves out of its
trigger and its panel, the popover closes.Press F8 to move focus to the Toast stack, and press it again to go back.
Set hotkey on the stack to use another key. The stack's name says which key
it is.The fields with a panel behave the same way:Select and Combobox close their list when Tab moves focus out.Date Input, Time Input, and Color Input close their panel when focus
leaves the field, and Escape returns focus to the field.Opening one of those panels from its trigger, or with ArrowDown, moves
focus into the panel.Inside one of those panels, Tab moves between the panel's controls and
does not leave the panel.A tooltip never takes focus. A tooltip only describes a control, so it
cannot contain a button. For something with a button in it, use a Popover.Name the tooltip in its trigger's aria-describedby. Place the tooltip
beside its trigger, name the trigger with for, and give the trigger an
aria-describedby with the tooltip's id. The description is then in the
markup your server sends, so a screen reader meets it at rest, in reading mode
too:<helix-buttonaria-describedby="save-tip"id="save">Save</helix-button><helix-tooltipcontent="Save your work"for="save"id="save-tip"></helix-tooltip>Without the attribute, a tooltip links itself. A Helix trigger is described as
soon as it has rendered. A plain element that for names is described the
first time you point at it or focus it, so the link is never written into
markup your server sent. In the for form, the tooltip itself is out of the
accessibility tree while its bubble is down, so you meet its words once, as the
description.
The pointer can move onto a tooltip's bubble and the bubble stays, so a reader
can read it under a magnifier. Set hoverable="false" to hide it once the
pointer reaches it.Keep an overlay openAn overlay sends helix-before-close before it closes. Cancel the event to keep
the overlay open:dialog.addEventListener('helix-before-close',(event)=>{if(unsaved){event.preventDefault()}})Canceling covers Escape, a backdrop click, and the close button, because all
three send the same request. See State and control.Entry and exit animationOverlays animate in with @starting-style. They animate out with a transition
on the overlay and display properties, which allows an exit animation on an
element the browser is about to remove from the top layer. Every overlay
responds to prefers-reduced-motion.Dialog and Drawer also say where they are in the journey:
:state(entering) holds from the moment they open until their entrance has
finished, and :state(exiting) from the moment they close until their exit has
finished. Select on them to draw an entrance or an exit of your own.Swiping an overlay awayA touch or a pen swipes a Drawer toward the edge it came from, and a Toast
message toward the end of the line. A swipe past halfway, or a quick one, closes
it through the same cancelable request as its close control. A Drawer with
snap-points comes to rest at the nearest point, such as half open, and
:state(snapped) holds while it rests short of the whole panel. Moving focus
with Tab to a control past the edge of the window opens it the rest of the
way, and snapTo() moves it from script. In a
Lightbox, a swipe across the viewer shows the next or the previous item. A
mouse never swipes, because dragging with a mouse selects text. A Lightbox
with max-zoom also zooms: pinch the item, or double click or double tap it,
and drag it about once zoomed. It swipes only while the item is at its own
size. With controls it also draws zoom-out and zoom-in controls, and once a
reader stops zooming it says the new scale, such as "Zoomed to 150%".Choose an overlayComponentUse it forDialogA task that blocks the pageDrawerThe same, from an edge, usually largerPopoverA surface anchored to a control, possibly interactiveTooltipA description of a control. Never interactiveMenuA list of commandsSelect, ComboboxA value chosen from a list, in a formCommandA palette over the whole applicationMegamenuNavigation, anchored to a headerA Dialog used for a tooltip's job traps focus for no reason. A Tooltip used
for a popover's job silently drops the button you put in it.