Gotchas and troubleshooting
Every entry on this page cost this project a defect: ISS-001 shipped a whole wave before anyone noticed the page was not themed, and ISS-005 shipped overlays whose items had no styling at all. The difference between a documented edge and an undocumented one is whether a host loses an afternoon to it.
This page is maintained from the issues catalogue in .work/issues/. Each entry is named for the symptom, says how to tell it is that, gives the fix, and points at the issue it came from - the issue IDs below are the repository’s, where the measurement lives. A new issue that a host could meet should add an entry here.
Theming
Section titled “Theming”The components are themed and the page is not
Section titled “The components are themed and the page is not”Symptom. In dark mode the ghost and link buttons are white text on a white page, and the host’s own body text is black on a dark design.
Check. getComputedStyle(document.body).backgroundColor is transparent while the components are dark.
Fix. Put class="hui-surface" on <body> (or a wrapper). Home-UI deliberately does not style <body>; the class is the opt-in.
Source. ISS-001.
A theme set in script flashes the wrong one
Section titled “A theme set in script flashes the wrong one”Symptom. The page paints light, then flips to dark once the module runs.
Check. The theme is applied in JavaScript rather than present in the server’s HTML.
Fix. Stamp class="light" / class="dark" on <html> from the server, before first paint. Use hui-theme-toggle only if the reader should choose in the browser.
Source. UI-CONTRACT-002.
Styling
Section titled “Styling”The page flashes raw content before the components appear
Section titled “The page flashes raw content before the components appear”Symptom. For a moment the page shows every option of a select at once, every tab panel stacked, and then it snaps into shape when the script runs.
Check. The elements are still unknown: customElements.get('hui-select') is undefined while the raw content is on screen.
Fix. Nothing - the stylesheet handles it, as long as it is loaded in <head> before the module. Until an element is defined the sheet hides it (hui-select:not(:defined)) and reserves the height of the 2rem controls, so the page does not jump either. Do not copy that rule into a host sheet with a different list: reveal is by :defined, and an element left out of the list simply flashes as before.
If the module never arrives - blocked, failed, JavaScript off - everything is revealed after three seconds, unstyled but readable, rather than hidden for good. A host that wants a different fallback can override animation-delay on those selectors.
Source. ISS-020.
A document stylesheet cannot reach inside a component
Section titled “A document stylesheet cannot reach inside a component”Symptom. A rule like hui-select .item { … } does nothing.
Check. The element has a shadow root (element.shadowRoot is non-null).
Fix. Use the --hui-* token or the ::part() hook on the component’s own page. Tokens inherit through the shadow boundary; rules do not.
Source. UI-CONTRACT-001.
My menu and select items look like browser buttons
Section titled “My menu and select items look like browser buttons”Symptom. A dropdown or select panel is themed but its items are grey system buttons.
Check. The items are host-rendered children ([role="menuitem"], [role="option"]) and nothing styles them.
Fix. The library styles these from the class sheet, scoped by element. If they are still unstyled, the item markup is outside the component or uses a different role. ::slotted() only matches direct children, which is why the styling lives in the sheet - that is ISS-005.
Source. ISS-005.
A focus ring is drawn inside a composite rather than around it
Section titled “A focus ring is drawn inside a composite rather than around it”Symptom. A search field shows the ring around the borderless input, inside the control’s own border, instead of around the control.
Check. The component wraps an input with an icon or a clear button and the ring is on the input.
Fix. Not a host concern; raise it against the component. The rule is that the ring belongs to the control, not the focusable element inside it - ISS-006.
Source. ISS-006.
Setting display on an item the component hides breaks filtering
Section titled “Setting display on an item the component hides breaks filtering”Symptom. A combobox or command palette shows every option regardless of the query.
Check. A host rule sets display on an option the component filters with the hidden property.
Fix. Do not set display on components’ overlay items; their own rule shows and hides them.
Source. ISS-005, class sheet.
A field’s description and error run together on one line
Section titled “A field’s description and error run together on one line”Symptom. With hui-field, the help text and the validation error read as a single sentence.
Check. The description and error are rendered without the data-description and data-error hooks.
Fix. Mark them with those attributes so the sheet styles them as separate, differently-marked lines (the error by colour, position and an icon). That was ISS-002.
Source. ISS-002.
A control submits nothing
Section titled “A control submits nothing”Symptom. The form posts and the field is absent from the request body.
Check. document.querySelector('hui-input').name is empty.
Fix. Add a name. A control with no name submits nothing, exactly like a native input.
Source. COMPAT-CONTRACT-001.
An unchecked box or switch contributes nothing
Section titled “An unchecked box or switch contributes nothing”Symptom. Only checked controls appear in the request body.
Check. Expected: this is native behaviour.
Fix. Read the field’s presence, not its value, for a boolean.
Source. COMPAT-CONTRACT-001.
A value set before the element upgrades is lost
Section titled “A value set before the element upgrades is lost”Symptom. A script sets value on load and it stays empty.
Check. The assignment runs before the class upgrades the element.
Fix. Render the value as an attribute from the server. Set properties only after upgrade.
Source. Server-side mechanics.
A swap leaves the page inert with nothing to click
Section titled “A swap leaves the page inert with nothing to click”Symptom. After an hx-swap, the page will not respond.
Check. An overlay was open when its region was swapped, and an element is still in the top layer.
Fix. The library closes an overlay on disconnect (ISS-007); do not move an open overlay between parents by hand, and do not remove it without letting it disconnect.
Source. ISS-007.
A control re-created by a swap fires its event twice
Section titled “A control re-created by a swap fires its event twice”Symptom. One interaction produces two events.
Check. A listener was added on an ancestor without cleanup, in addition to the component’s own.
Fix. Components clean up their own listeners; a host listener on a persistent ancestor should be added once, not per swap.
Source. COMPAT-CONTRACT-002.
Overlays
Section titled “Overlays”A popover closes as soon as it opens
Section titled “A popover closes as soon as it opens”Symptom. Clicking a trigger flashes the panel open and shut, every time.
Check. The panel opens during the same pointer gesture that opened it.
Fix. The library delays or defers the open so the platform does not light-dismiss it (ISS-007, which reproduced in Firefox). If a host opens a popover of its own in a pointerdown, do the same.
Source. ISS-007.
An alert dialog cannot be dismissed
Section titled “An alert dialog cannot be dismissed”Symptom. Escape does nothing.
Check. Expected: an alert dialog (alert) is not dismissible. hui-dismiss is cancellable, so a host may also refuse it.
Fix. Provide an explicit action inside the dialog.
Source. UI-CONTRACT-004.
A tooltip’s rich mode and delay have no effect
Section titled “A tooltip’s rich mode and delay have no effect”Symptom. <hui-tooltip rich> looks exactly like a plain tooltip, and a custom delay is ignored.
Check. The panel is interactive content but closes on blur, and the delay is 400 ms whatever you set.
Fix. Not yet; rich and delay are class fields rather than reactive properties, so the attributes do nothing today (ISS-009, open). Until it is fixed, treat a rich tooltip as unsupported and use hui-popover for interactive content.
Source. ISS-009.
Accessibility
Section titled “Accessibility”A horizontal radio group is announced as vertical
Section titled “A horizontal radio group is announced as vertical”Symptom. A screen reader says a group is vertical though it is laid out in a row.
Check. aria-orientation is vertical while the attribute is orientation="horizontal".
Fix. Not yet; the attribute reaches the layout but not aria-orientation (ISS-010, open). Keep the group vertical until it is fixed.
Source. ISS-010.
A dialog has no name when announced
Section titled “A dialog has no name when announced”Symptom. A screen reader opens a dialog with no title, though one is on screen.
Check. The native <dialog> has no aria-labelledby or aria-describedby.
Fix. Not yet; the title and description slots are visual only (ISS-011, open). Give the host a labelled dialog where you can.
Source. ISS-011.
Browsers
Section titled “Browsers”The element that triggered a dialog is not restored on close
Section titled “The element that triggered a dialog is not restored on close”Symptom. Focus returns to <body> after closing a click-opened dialog in WebKit.
Check. WebKit does not focus a <button> when it is clicked, so the platform’s own restore target was <body>.
Fix. Handled by the library: the trigger is recorded on open and focus is restored through the shared helper. Use the slotted trigger rather than opening from unrelated script.
Source. ISS-007.
Focus appears to escape a modal dialog to <body>
Section titled “Focus appears to escape a modal dialog to <body>”Symptom. Tabbing out of a modal spends a stop on <body>.
Check. Chromium includes <body> in a modal <dialog>’s own tab cycle.
Fix. Expected; it is not an escape.
Source. TASK-017.
Have you hit a sharp edge that is not here? If its cause is in the library rather than in the host’s Go template, it belongs in the issues catalogue and then, once fixed, on this page.