Compare commits
2 changed files with 0 additions and 165 deletions
|
|
@ -29,11 +29,6 @@ dashboard rather than how it's built, start at
|
||||||
- **[CSS theme variables](web-ui/css-vars.md)** — the Catppuccin
|
- **[CSS theme variables](web-ui/css-vars.md)** — the Catppuccin
|
||||||
Mocha custom properties declared once in `base.css` and the rule
|
Mocha custom properties declared once in `base.css` and the rule
|
||||||
that per-page stylesheets reference (never redeclare) them.
|
that per-page stylesheets reference (never redeclare) them.
|
||||||
- **[swarm-ui design guide](web-ui/design-guide.md)** — visual
|
|
||||||
language, motion, theming, error-UX, and component-first principles
|
|
||||||
for the swarm-level Preact app specifically (not this doc's
|
|
||||||
dashboard/agent UIs). `/components` on a running swarm-ui is the
|
|
||||||
companion living demo of every primitive it references.
|
|
||||||
|
|
||||||
## Reading paths
|
## Reading paths
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,160 +0,0 @@
|
||||||
# swarm-ui design guide
|
|
||||||
|
|
||||||
Scope: **swarm-ui only** (the swarm-level Preact app — not the per-hive
|
|
||||||
dashboard, which has its own older visual language). This doc is the
|
|
||||||
*why*: the principles behind how swarm-ui looks and behaves, and the
|
|
||||||
concrete rules that follow from them. It deliberately doesn't show what
|
|
||||||
things look like — that's `ComponentsPage` (`/components`), the living,
|
|
||||||
always-current demo of every primitive in `src/ui/`. Code can't go stale
|
|
||||||
the way a doc's screenshots can, so treat `ComponentsPage` as the source
|
|
||||||
of truth for appearance and this doc as the source of truth for intent.
|
|
||||||
Colour variables specifically are `docs/web-ui/css-vars.md`'s job, not
|
|
||||||
repeated here.
|
|
||||||
|
|
||||||
## Visual language
|
|
||||||
|
|
||||||
- **Frosted / tinted glass** where it fits — translucency over flat
|
|
||||||
panels, not a hard rule for every surface.
|
|
||||||
- **Playful use of the multiple accent colours**, without tipping into
|
|
||||||
visual noise — the base16 palette has several accent slots
|
|
||||||
(`--purple`, `--cyan`, `--pink`, …); use more than one where it adds
|
|
||||||
distinction, not decoration for its own sake.
|
|
||||||
- **Whimsy** — small, delightful touches are welcome (the per-hive
|
|
||||||
dashboard's home-page matrix-rain background, `packages/dashboard/src/
|
|
||||||
home.js`, is the reference example — currently in the dashboard
|
|
||||||
package, not swarm-ui itself, but the pattern it sets applies here
|
|
||||||
too). Whimsy still has to clear the accessibility bar below (motion,
|
|
||||||
in particular).
|
|
||||||
- **Efficient navigation** — minimize clicks/hops for a common task.
|
|
||||||
- **Avoid junk drawers.** A control belongs next to the thing it
|
|
||||||
affects, not tucked into a catch-all menu. Concrete anti-example (not
|
|
||||||
swarm-ui, but the standard to avoid): the per-hive agent terminal's
|
|
||||||
model selector living in a three-dot overflow menu instead of next to
|
|
||||||
the thing that shows the current model.
|
|
||||||
- Loosely influenced by Material Design's current (Material You)
|
|
||||||
generation, mainly for shape/elevation ideas. Colour is dynamic too —
|
|
||||||
auto-derived shades (mixing, lighter/darker) are fair game, same as
|
|
||||||
Material's own approach — but every derived colour still traces back
|
|
||||||
to the base16/stylix contract below, never an independent source.
|
|
||||||
|
|
||||||
## Motion
|
|
||||||
|
|
||||||
**General rule, not case-by-case: every non-essential CSS animation
|
|
||||||
gates on `prefers-reduced-motion` and pauses when its tab/section is
|
|
||||||
hidden.** The dashboard's matrix-rain background (`packages/dashboard/
|
|
||||||
src/home.js`/`.css`) is the existing reference implementation of this
|
|
||||||
pattern — genuinely does both today, confirmed by reading it — even
|
|
||||||
though it lives in the dashboard package rather than swarm-ui; every
|
|
||||||
future swarm-ui animation follows the same pattern, not just whimsy
|
|
||||||
pieces.
|
|
||||||
|
|
||||||
Prefer CSS-driven animation over JS-driven where possible, and avoid
|
|
||||||
jarring content swaps (layout shift, hard cuts) where a transition can
|
|
||||||
smooth them instead.
|
|
||||||
|
|
||||||
## Theming
|
|
||||||
|
|
||||||
The mechanical contract (base16 slots, semantic vars, what a page's CSS
|
|
||||||
is and isn't allowed to reference) lives in `docs/web-ui/css-vars.md` —
|
|
||||||
read that for the how. This section is the *policy* layered on top:
|
|
||||||
|
|
||||||
- **User-theming compatible by construction.** The whole point of the
|
|
||||||
base16/`colors.css` swap contract is that a user's own theme (stylix
|
|
||||||
today) takes over with zero swarm-ui code changes. Don't build
|
|
||||||
anything that assumes a specific palette's exact colours (contrast
|
|
||||||
ratios, "this accent is always purple") beyond what the semantic var
|
|
||||||
names promise.
|
|
||||||
- **stylix wins outright when it's active, no in-between state.** When
|
|
||||||
an operator has stylix supplying swarm-ui's palette, that palette is
|
|
||||||
authoritative — swarm-ui's own light/dark preference (below) doesn't
|
|
||||||
override it. This is a deliberate simplification, not a limitation to
|
|
||||||
fix: a stylix-using operator has already made their choice.
|
|
||||||
- **Light theme is accessibility, not a cosmetic extra.** Some people
|
|
||||||
need light for contrast/low-vision reasons, others need dark for
|
|
||||||
photosensitivity — there's no universally-correct default. The OS
|
|
||||||
preference (`prefers-color-scheme`) drives the default, with an
|
|
||||||
explicit user override available, same shape as motion above.
|
|
||||||
|
|
||||||
## Data freshness & refresh
|
|
||||||
|
|
||||||
The governing question for anything that shows time-sensitive data:
|
|
||||||
**would a user returning to this tab expect current data?** If yes, it
|
|
||||||
needs a refresh story; a value that's silently gone stale with no way to
|
|
||||||
tell is worse than one that's visibly stale.
|
|
||||||
|
|
||||||
- **Relative-time labels must actually tick.** A label like "fresh (5s
|
|
||||||
ago)" is computed from a stored timestamp and re-renders itself on an
|
|
||||||
interval — never a value frozen at fetch time that quietly goes stale
|
|
||||||
while still reading as current. Pauses while its tab is hidden.
|
|
||||||
- **Pages that poll get a refresh-interval control, not silent
|
|
||||||
staleness.** A small, Grafana-like "refresh every: off / 10s / 30s /
|
|
||||||
1m / …" control the operator sets per page, paused while the tab is
|
|
||||||
backgrounded and resumed on foreground.
|
|
||||||
- **A refresh must never clobber input the operator is mid-edit on.**
|
|
||||||
Any polling component's contract needs to make this the caller's
|
|
||||||
problem to opt out of correctly, not something the next adopter
|
|
||||||
discovers by shipping a bug.
|
|
||||||
|
|
||||||
## Errors
|
|
||||||
|
|
||||||
**`ApiErrorPanel` + the RFC 9457 `ProblemDetails` shape it renders is
|
|
||||||
the canonical error surface** for every API failure in swarm-ui, not
|
|
||||||
just its original callers — nobody should build a softer/quieter error
|
|
||||||
UI later. Concretely: full untruncated `detail`, a copy button, no
|
|
||||||
"something went wrong, try reloading."
|
|
||||||
|
|
||||||
This follows from **power-user-first**, one of the standing principles:
|
|
||||||
swarm-ui instances are mostly self-hosted and operators are techies, so
|
|
||||||
errors should give them what they need to actually diagnose a problem
|
|
||||||
rather than a friendly wall. Power-user-first doesn't mean
|
|
||||||
newcomer-hostile — the UI should still be self-explanatory, warn or ask
|
|
||||||
for confirmation before a destructive/dangerous action, and offer
|
|
||||||
helpful hints — it just shouldn't get in the way of someone who already
|
|
||||||
knows what they're doing.
|
|
||||||
|
|
||||||
## Empty states
|
|
||||||
|
|
||||||
Graceful fallbacks, always — a table with zero rows should show
|
|
||||||
something that represents "no rows" (a real empty-state message), not
|
|
||||||
just bare column headers floating over nothing.
|
|
||||||
|
|
||||||
## Layout & viewport
|
|
||||||
|
|
||||||
- **swarm-ui adapts to screen size and fully supports touch** — this is
|
|
||||||
not a desktop-only app. Concrete floor: every interactive control from
|
|
||||||
the shared `ui/` kit (buttons, inputs, selects) carries a minimum
|
|
||||||
touch target (`2.75em` ≈ 44px, WCAG 2.5.5) by default, so this isn't
|
|
||||||
something each page has to remember.
|
|
||||||
- **Phone is a second-class citizen, not an unsupported one.** Things
|
|
||||||
must not *break* at phone width, but don't over-invest in phone
|
|
||||||
optimization beyond that. swarm-ui is installable as a PWA, so a
|
|
||||||
phone can check status or do basic interactions.
|
|
||||||
- **A narrow viewport isn't only a phone** — a tiling-window-manager
|
|
||||||
user with swarm-ui in a narrow tile hits the same layout constraints a
|
|
||||||
phone does. Design for the constraint (narrow viewport), not the
|
|
||||||
device.
|
|
||||||
|
|
||||||
## Component-first design
|
|
||||||
|
|
||||||
Build the `src/ui/` primitive before or alongside the first real page
|
|
||||||
that needs it, not after it's been styled inline and left for later —
|
|
||||||
a page reaching for a component that's already there has ready-made
|
|
||||||
blocks to build with, instead of the next contributor duplicating
|
|
||||||
ad-hoc styling that someone then has to hunt down and consolidate.
|
|
||||||
`Panel`/`StatusChip`/`Table`/`TextField`/`SelectField`/`Button` are the
|
|
||||||
primitives that exist; `/components` always has the current, complete
|
|
||||||
list — this doc won't try to keep a duplicate inventory in sync.
|
|
||||||
|
|
||||||
**Every new `ui/` component gets a demo section on `/components` the
|
|
||||||
same day it lands** — no primitive without a place to see it. One
|
|
||||||
deliberate exception exists today: `FormField`, the internal label+
|
|
||||||
control wrapper `TextField`/`SelectField` share, isn't itself a
|
|
||||||
primitive a page reaches for directly, so it has no demo of its own —
|
|
||||||
a considered exception, not an oversight this rule missed.
|
|
||||||
|
|
||||||
## Attention
|
|
||||||
|
|
||||||
"Attention-optimized" here means the system should lead the operator's
|
|
||||||
attention to where it actually matters (an error, a state change worth
|
|
||||||
noticing) — not attention-optimized in the engagement/growth sense of
|
|
||||||
maximizing time-on-page.
|
|
||||||
Loading…
Reference in a new issue