Skip to content
GroundworkGroundwork
Esc
↑↓navigate↵open⌘Jpreview
Colour mode
On this page

Components

Lit components on native elements, styled only through --gw-* variables, and kept current in each app by the sync.

The web components are Lit elements built on native elements: a gw-button is a <button>, a gw-dialog is a <dialog>, a gw-segmented is a set of radio buttons. Keyboard use, focus and forms come from the browser, and Home Assistant’s frontend is the reference for the rest. They’re styled only through --gw-* variables, so each follows whichever theme and mode surrounds it.

Every demo on these pages has a theme and mode picker. Pick one and the components follow it.

RebootPlan bootOnline71 °C

Groups

Group Components
Actions Button, menu button, segmented control
Inputs Input, select, checkbox, switch, setting row
Display Card, chip, badge, dot, alert, facts, meter
Feedback Spinner, skeleton, empty state, toast
Overlays Popover, dialog, menus
Layout Page, page header, section title, stack, row, grid
lit-effect AtomController, the registry context and a typed router

The data group (table, virtual list, time series chart and JSON view) comes next.

Taking them into an app

The sync brings components into an app and keeps them current. Add the ones an app uses once, and mise run sync updates them after every groundwork change:

bun packages/sync/src/cli.ts add --app ../my-app --to src/ui gw-button gw-popover themes-css

Each component needs ui-base (its shared styles and the icon() helper), which the sync brings along. Load themes.css and set data-gw-theme and data-gw-mode on a container, and everything inside follows. Extend a component by wrapping it or setting variables and ::part(), never by editing it in the app, since the sync flags those edits.

Fields stay in light DOM

Password managers and <label for> can’t reach into a shadow root, and a form only submits the controls it can see. So gw-input, gw-select, gw-checkbox and gw-switch render their native control in light DOM, and adopt their styles into whichever document or shadow root they’re in. Pages with credentials keep the whole form out of shadow roots.

Icons

icon(path, label?) draws any SVG path data, so each app picks its set: MDI (as Home Assistant uses), Lucide, Tabler or Phosphor. Components that show an icon take it as path data in an icon attribute, or an <svg slot="icon">.

Last updated on