The xSwarm Style Guide

The single source for colour and material, what "slightly skeuomorphic" means in tokens, and the two rules an audit enforces so this stays true.

Chad, 2026-09-17: “I asked for a stunning, slightly skeumophic, award-winning design for the tickets and the web interface. and I got a 1px line red box. That was a serious UI fail.”

It was, and the cause was not taste. It was that there was no style guide, so there was nothing for anyone — human or agent — to build to. Measured across packages/web/src the day this was written:

Svelte components with no <style> block at all 16 of 37
hard-coded colour literals outside theme.css 1,909
files carrying them 82
design tokens already defined and bypassed 68

A component with no styles renders as raw markup — a thin bordered box. That is the red box. It was not designed badly; it was not designed.

One source, and it already exists

packages/web/src/styles/theme.css is the only place a colour or a material value is defined. Nothing else declares one. Not a component, not a page, not a second stylesheet.

Every semantic value is a light-dark() pair, because the two modes are not the same design:

--color-surface: light-dark(rgb(249 250 251), rgb(39 39 42));
--shadow-raised: light-dark(0 2px 4px rgba(17 24 39 / 0.08), 0 2px 6px rgba(0 0 0 / 0.5));

Write var(--color-surface). Never #f9fafb, never rgb(249 250 251), and never a dark: prefix — the pair is the definition, so there is no second ruleset to keep in sync and no way for the two modes to drift apart.

If a value you need does not exist, add it to theme.css. That is a smaller change than inlining it, and it is the only version of the change that helps the next component.

What “slightly skeuomorphic” means here

Material honesty, not ornament. A raised surface catches light along its top edge and casts a shadow below. A pressed one inverts both. That is the entire vocabulary, and it is four tokens:

.card {
  background: var(--color-surface);
  background-image: var(--surface-raised);   /* a <3% top lift, not a gradient you can see */
  box-shadow: var(--shadow-raised), var(--highlight-top);
  border-radius: var(--radius-md);
}
.card:active { box-shadow: var(--shadow-pressed); }

--highlight-top is the token that matters. A 1px inner light line along the top edge is what makes a surface look lifted rather than merely outlined — it is the difference between this and the flat box that prompted the guide.

Three elevations only: --shadow-resting, --shadow-raised, --shadow-floating. A fourth is decoration. One radius scale, so a card and the button inside it cannot disagree by a pixel.

Restraint is the “slightly”. Bevels under 1px of light, gradients under 3%, no inner glows on text, no drop shadows on type. If a surface’s depth is the first thing you notice about a screen, it is too much.

Components own their styles

From CLAUDE.md, and it is load-bearing rather than stylistic:

  • Self-contained. All styles in the component’s own <style> block. No external CSS file per component.
  • Generic, not content-specific. Chart, not TokenChart. Variants via props, not new files.
  • One file per component. Card variant="tiny", never CardTiny.svelte.

A component with no <style> block is not “using the defaults”. There are no defaults. It is unstyled, and it will look like the red box.

What is enforced, and where

A rule nothing checks is a wish. CLAUDE.md has said “semantic colors only” and “single source” for a long time, and the count above is 1,909 — so this guide ships with an audit rather than hoping.

quality/ui-audit.mjs reports two things, and the quality gate ratchets both: the number can hold or improve, never regress.

  1. unstyled-component — a .svelte component with no <style> block.
  2. hard-coded-colour — a hex, rgb(), rgba() or hsl() literal anywhere under packages/web/src except theme.css.

The refactor stage runs the whole suite, so a ticket that adds either one is caught inside the ticket that added it — which is the only moment the person who wrote it still remembers why.

Neither number has to reach zero to be useful. Both are debt with a direction, and the ratchet is what makes the direction real.