Brandbook
PortQueue's dashboard and docs share one calm, muted visual system. The goal is a content-first surface: a dominant neutral, a single desaturated green accent, one text colour, and low-contrast semantic colours used only where they carry meaning.
All colours come from one set of design tokens in
@portqueue/ui (src/theme/tokens.ts). The dashboard
reads them as CSS variables (src/theme/theme.css, switched between light and dark by its
ThemeProvider); this docs site mirrors them onto Docusaurus's Infima variables. A token change
is made in tokens.ts and mirrored into both stylesheets.
Core palette
Semantic palette
Muted and desaturated to match the core palette — never saturated/pure red, orange or
blue. Use them only when the context genuinely means it; never decoratively. Always pair
with --text (dark) for on-colour text, never white.
Iconography
Icons are real SVGs from @ant-design/icons (v6) —
never emoji. Emoji render as a different picture on every platform, carry colours that
aren't in the palette, and can't inherit the theme, so they are not used anywhere in the
dashboard or these docs.
- One curated set.
@portqueue/uire-exports the icons the design system uses fromsrc/icons.tsunder intent-named aliases —RetryIcon,PauseIcon,RemoveIcon,SearchIcon— and components import from there, never from@ant-design/iconsdirectly. Adding an icon means adding a line to that file, which keeps the bundle to what's actually used and stops the same idea being drawn two ways. The docs site is the one exception: it has no dependency on@portqueue/uiand imports the icon package directly. - One icon per action. The dashboard's action icons live in one map
(
constants/labels.tsx), so Retry is the same glyph in a row, a bulk menu and a detail header. - Icons inherit. Every icon is sized in
emand filled withcurrentColor, so it takes the size and colour of the text beside it. Colour an icon only where the colour carries the same meaning the palette gives it — semantic tones on alerts and toasts,--dangeron a destructive action. - Decorative by default. An icon beside a text label is
aria-hidden; an icon-only control carries the name inaria-label(and usually a tooltip).
Feedback & affordances
A control that changes nothing visible has not told the user anything. Two patterns cover most
of it, and both live in @portqueue/ui so every surface behaves the same way.
- An action confirms itself.
CopyButtonswaps its icon for a green tick for one second after a successful copy, and saysCouldn't copy — press ⌘Cwhen the clipboard API is missing (any insecure origin) or refuses. Never let a copy look identical before and after. - The confirmation is announced, not just drawn. The state also lands in a
role="status"region — a colour change is no feedback at all to a screen-reader user. - Clipped content shows it's clipped. A table wider than its card sits in
ScrollShadow, which fades whichever edge has more content behind it: right-only at the start, both mid-scroll, left-only at the end, and neither when everything fits. A bareoverflow-x: autoreads as a truncated table rather than a scrollable one.