Files
VeillaxandClaude Sonnet 5.5 cf4d5b3ce2 Turn the status page into a full status page generator
Components can be put in groups and given public names. Public incidents carry
posts written for customers (the internal timeline stays internal), the
components they affect, and a page for each incident plus paged history.
Scheduled maintenance has its own window, shows affected components as under
maintenance, and is announced when scheduled, started, ended or canceled.

Branding (title, logo, accent checked for contrast, contact link, footer) and a
custom domain that serves only the status page. Visitors follow by RSS, by
confirmed email, or by webhook that must answer a test request. Admin UI for
all of it, tests, README and design notes.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-27 17:05:38 -06:00

71 lines
5.6 KiB
Markdown

# Sandbar design language
Sandbar's pages follow the Lightwell design language, as Hammerhead does. The references are `docs/design.md` in the Lightwell repository and in the Hammerhead repository. This page records how that language is applied here. When they disagree, Lightwell's document wins.
## What is shared
* **Tokens.** Colors, spacing (4px scale), radius (6px and 10px), type sizes (12 to 28px), the single shadow and the dark theme are the Lightwell values, copied from Hammerhead. The accent is Lightwell's teal, `#1f5c5c`, with white text on it.
* **Principles.** Quiet neutrals with one accent, plain and specific wording, one way to do each thing, calm density, and WCAG 2.1 AA.
* **Voice.** No emoji, no em or en dashes, no exclamation marks, sentence case, buttons that name the result, and errors that say what happened and what to do next. Errors from the server are capitalized in one place (`bad` in `api.go`).
* **Status carries text.** A status is a badge with a label ("Firing", "Down", "Resolved"), never a colored dot on its own.
## Where it lives
| File | What it holds |
| --- | --- |
| `internal/sandbar/web/index.html` | The app. The token block sits between `tokens:begin` and `tokens:end`; raw colors are allowed only there. |
| `internal/sandbar/web/status.html` | The public status page: home, history and incident pages in one file. Same token block, repeated. |
Both are single files with no build step. Change a token in both, and in Hammerhead when the language changes.
## Components
The helpers at the top of the script in `index.html` are the same as Hammerhead's: `card`, `pageHeader`, `field`, `chk`, `badge`, `emptyState`, `modal`, `dialogForm`, `confirmDialog`, `ask`, `toast`, `skeleton`. Feature code uses these and does not style one-off. Sandbar adds three, built only from tokens:
| Helper | Use |
| --- | --- |
| `table(head, rows)` | A table inside a flush card, with column headers |
| `rowList(addLabel, make, initial)` | A list the person adds to and removes from, such as people in a rotation or steps in a policy |
| `timeline(events)` | Dated entries for an alert or an incident |
## Status page bars and charts
* **Uptime bars** on the public page are one bar per UTC day, 90 of them. Green is no failures, amber is at least 99% passing, red is below that, grey is no data. Color is never the only signal: the row carries a text status badge and the uptime percentage, each bar's tooltip says the date and the numbers in words, and the whole strip has an `aria-label` summary.
* **Component states** are badges with text: Operational (success), Degraded performance and Partial outage (warning), Major outage (danger), Under maintenance (accent). A group heading shows the worst state of its components.
* **Incident posts** are a short list with a rule on the left: the status in bold, the time, then the text, newest first. Maintenance windows use the same card with a Scheduled, In progress or Completed badge.
* **Branding** changes one token only, `--accent`, which the page sets from the saved color. The admin form refuses colors that give white text less than 4.5 to 1, so the contrast rule survives any choice.
* **Analytics charts** are plain bars made of tokens, with two series (visitors in the accent, page views in a lighter mix of it), a legend with text, a native tooltip per bar, and an `aria-label` that gives the totals. There are no chart libraries.
* **Top lists** are tables with a proportional bar behind the name, so the numbers stay readable without the bar.
## Icons
Lucide style, outline only, stroke 1.75, round caps, `currentColor`. Sizes are 16, 18 and 22 pixels. They are inline in the `ICONS` table. An icon goes with a text label, and icon-only buttons carry an `aria-label`.
Approved names: `sandbar`, `alerts`, `incidents`, `oncall`, `monitors`, `websites`, `topics`, `channels`, `status`, `users`, `account`, `out`, `menu`, `close`, `add`, `check`, `back`, `light`, `dark`, `alert`. Add a name here and in `ICONS` together.
## Patterns
* **One primary action per view.** It sits in the page header ("Declare incident", "Add monitor").
* **List pages** show the page header, a filter or tab row where there is one, then the table in a card with no second title.
* **Detail pages** (an incident) use a back link, the title with its status badge, then cards, most important first.
* **Dialogs and forms** end with Cancel and then the primary action, at the bottom right. Labels sit above fields, and rules such as "At least 10 characters" are hints below them.
* **Destructive actions** always use `confirmDialog`. The title names the object, the body says what is lost and whether it can be undone.
* **Dates** have the month written out ("Nov 2, 2026"), with `fmtDate` and `fmtTime`.
* **Empty states** say why the list is empty and what will appear there.
* **Secrets** such as a channel's address are never sent back to the browser. The form says it is stored encrypted and hidden.
## Layout
* Content is capped at 1120px. Below 860px the sidebar becomes a slide-in drawer with a top bar. Phones keep a 16px side gutter.
* On touch screens controls are at least 44px high and inputs are 16px so the page does not zoom.
* Motion is minimal and turns off under `prefers-reduced-motion`. The theme follows the system and can be switched from the sidebar.
* The public status page is a single column, 720px wide, and has no navigation.
## Checking a change
```
node scripts/lint-design.mjs
```
It fails on em and en dashes, emoji, and raw colors outside the token block, in the pages, the docs and the Go source. Then check the page in light and dark, at phone and desktop width, using only the keyboard.