---
title: Components
description: Web components on native elements, styled only through --gw-* variables, and shipped as a package.
---

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. 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.

<Demo id="overview">
  <gw-button>Reboot</gw-button>
  <gw-button variant="neutral" appearance="filled">Plan boot</gw-button>
  <gw-chip tone="success">Online</gw-chip>
  <gw-badge label="CPU" icon="M15 13V5A3 3 0 0 0 9 5V13A5 5 0 1 0 15 13M12 4A1 1 0 0 1 13 5V8H11V5A1 1 0 0 1 12 4Z" tone="warning">71 °C</gw-badge>
  <gw-switch label="Wake on LAN" checked></gw-switch>
</Demo>

## Groups

| Group | Components |
| --- | --- |
| [Actions](/components/actions) | Button, menu button, segmented control |
| [Inputs](/components/inputs) | Input, select, checkbox, switch, setting row |
| [Display](/components/display) | Card, chip, badge, dot, alert, facts, meter |
| [Feedback](/components/feedback) | Spinner, skeleton, fade in, empty state, toast |
| [Overlays](/components/overlays) | Popover, dialog, menus |
| [Layout](/components/layout) | Page, page header, section title, stack, row, grid |
| [lit-effect](/components/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

Components and tokens ship as packages, `@timmo001/groundwork-ui` and `@timmo001/groundwork-tokens`, on npm and JSR. Each release rolls out to the apps that use them, so nothing waits on Renovate:

```sh
bun add -E @timmo001/groundwork-ui @timmo001/groundwork-tokens
```

```ts
import "@timmo001/groundwork-tokens/themes.css";
import "@timmo001/groundwork-ui";
```

Set `data-gw-theme` and `data-gw-mode` on a container, and everything inside follows. Extend a component by wrapping it, or through its variables and `::part()`. A change every app needs goes into Groundwork instead.

The packages publish once the components no longer need Lit. Until then, apps keep the copies the [sync](/sync) gave them.

## 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

Components that show an icon take it as path data in an `icon` attribute, or an `<svg slot="icon">`. [Iconography](/foundations/iconography) covers picking a set and when to show an icon on its own.

## References

- Home Assistant: [Components](https://contributors.home-assistant.io/design/components/) · [Code](https://github.com/home-assistant/frontend/tree/dev/src/components)
- Web Awesome: [Components](https://webawesome.com/docs/components/) · [Code](https://github.com/shoelace-style/webawesome/tree/next/packages/webawesome/src/components)
- Primer: [Components](https://primer.style/product/components/) · [Code](https://github.com/primer/react/tree/main/packages/react/src)
