Design charter

The direction for every Nistor surface, a calm operator console, in numbered rules that reviews cite.

Google Cloud console's structure, Nistor's identity. This page fixes the direction for every Nistor surface (the shell, its plugins, Nistor Cloud, the website and the extensions). Reviews cite it by rule number; a screen that breaks a rule needs a change to this page first, not an exception in code.

The rules and decisions below were accepted on 17 October 2026. The tokens that implement them live in one generated package, @nistor-io/tokens: web/tokens/README.md says how to change a value, regenerate and sync the website and Nistor Cloud. Branding (the mark, the lockups, the loader) is in docs/brand.md.

The eight rules#

  1. One accent, with a job. Indigo marks the primary action, links, the current nav item, selection and focus. Nothing else is indigo. Why: when the accent means "you can act here" or "you are here", the eye finds the next step without reading.
  2. Status colour is for status only. Red, amber, green and blue appear as an icon plus its text, in a status badge, a banner or a chart series that means a state. Never as a card fill, a card border, a left rail or a section tint. Why: an operator scans for red; every decorative red makes the real incident harder to see, and colour alone fails colour-blind users.
  3. Calm surfaces. Page #F4F5F9, content white, 1px hairlines. Shadows only on menus, tooltips, dialogs and the snackbar. No card inside a card. Why: elevation then means "this floats above the page and will go away", and the data, not the chrome, carries the screen.
  4. A short type scale. 22px page title, 16px section, 14px body, 13px inside dense tables, 12px floor. Sentence case everywhere; uppercase only for rare overlines. Monospace only for identifiers and code. Why: five sizes are enough for a clear hierarchy, and monospace that only ever means "identifier" becomes a signal.
  5. One page anatomy. Breadcrumb → title row (resource icon, title, one status, a toolbar of icon + label text actions) → underline tabs → content. Four templates: List, Detail, Form, Dashboard. Why: a user who has learned one page has learned them all, and new screens are assembled, not designed.
  6. Navigation you can read. Top bar: lockup, context picker, the only search field, help, notifications, account. Side nav: labeled, grouped, open by default, collapsible to a rail, current item as a tinted pill. Why: icon-only navigation makes people memorise; labels and groups let them recognise.
  7. Forms that explain themselves. Label, helper text, inline validation with an icon, primary disabled until valid, "Learn more ↗" for external docs. Why: the form answers the question at the field, so nobody submits to find out what was wrong.
  8. Feedback in fixed places. Tonal banner at the top of the content, snackbar bottom left, designed empty states with one next action, skeletons while loading. One date format. Why: when feedback always lands in the same place, users notice it; when it moves, they miss it.

Decisions#

# Decision Rule it sets Why
D1 Component foundation Nistor's own components in @nistor-io/ui, built on React Aria Components (react-aria-components, Apache-2.0) for keyboard, focus and ARIA behaviour. Radix is the fallback only if the P2 spike shows a blocker (bundle > 60 kB gzip for the primitives in use, or a Module Federation sharing problem). No MUI, no Material Web. Accessible behaviour is solved once by a maintained library, while the look stays ours and needs no theme fight.
D2 Neutrals A neutral ramp tuned to the brand: ink #0B0C12, ground #F4F5F9, slight indigo bias. Google's greys (#202124, #3C4043, #5F6368, #DADCE0, #F8F9FA) leave the shell, the website and the extensions. Greys borrowed from Google make Nistor look like a copy; greys from the mark's own ink make it look like one product.
D3 Button shape Pills (border-radius: 999px). Heights 32 (small), 36 (default), 40 (large). One filled primary per view. A distinct shape reads as "button" at a glance, and one filled primary leaves no doubt about the main action.
D4 Base size and density Body 14px (from 13px). Floor 12px. Compact density only inside tables (13px text, 36px rows); comfortable elsewhere. Ships in P1 behind the visual baseline of P0. 13px body is tiring to read all day; tables keep their density where scanning many rows matters.
D5 Navigation groups Overview on top, then Observe (Incidents, Lineage, Data trust), Operate (Scheduler, Processing, Dashboards, Notifications), Catalog (Assets, Glossary, Contracts), Cost (FinOps, Recommendations), AI (Assistant, Agent governance), Admin (IAM, Connections, Plugins, GitOps, Webhooks, Settings). Plugins choose a group; they never append loose icons. Groups named after the job let the nav grow with plugins without turning into a wall of icons.
D6 Nistor Cloud stack Shared tokens from P1. Shared components once P2 ships @nistor-io/ui as typed ESM; cloud-ui shrinks to cloud-only widgets. One set of tokens and components keeps Nistor Cloud and the product from drifting into two looks.

Settled rules#

  • Brand accent vs UI accent. #6366F1 is the mark's accent (logo, illustrations, display type ≥ 24px). #4F46E5 is the UI accent (buttons, links, selection, focus). Dark theme: fill #818CF8 with ink text, text accent #A5B4FC. Why: #6366F1 is 4.47:1 on white, under the 4.5:1 AA minimum for text; the mark keeps its colour, the interface stays readable.
  • Names. Nistor, Nistor Cloud, Nistor Enterprise are product names. nistor.cloud is a domain only. Lowercase nistor exists only as the wordmark. Why: one spelling per product keeps the docs, the UI and the marketing talking about the same things.

Token reference#

The light and dark values of the main roles, as @nistor-io/tokens publishes them (web/tokens/source/roles.json is the source of truth; web/tokens/README.md covers changing one). Use these names when you discuss a colour and in new code; the older shell names still resolve as aliases until their screens move.

primary #4F46E5 / hover #4338CA / pressed #3730A3 · primary-container #E0E7FF on #312E81
ink #0B0C12 · ink-variant #4A4F5E · ink-muted #5F6473 · outline #7A7F8C · outline-variant #DCDFE6
page #F4F5F9 · surface #FFFFFF · surface-container #EEF0F6
error #C5221F · warning #9A5B00 · success #1C7D4E · info #1B6CCE (containers: #FCE8E6 #FEF3E0 #E3F3EA #E3EEFB)
dark: page #0B0C12 · surface #14161E · container #1B1E28 · ink #EAEBF2 · ink-variant #A9AEBD
      outline #8D92A0 · outline-variant #2A2E3A · primary fill #818CF8 (ink text) · primary text #A5B4FC
type: 22/28/500 · 16/24/600 · 14/20/600 · 14/20/400 · 13/18/400 · 12/16/400 · mono 12.5/18
space 4 8 12 16 24 32 48 · radius 4 8 12 999 · Inter + JetBrains Mono

Edit this page on GitLab

↑ ↓ to moveEnter to openEsc to close