/* * Blueforce Field OS — design tokens * --------------------------------------------------------------------------- * One source for both operator surfaces: * - the device console (Python/tkinter, bfos/ui_theme.py reads these values) * - the central panel (stdlib http.server, web/templates/*) * * The surfaces share a status vocabulary because they describe the same fleet * from two ends. A colour here means one thing everywhere: a device state, a * severity, or an action weight. It never decorates. * * WHY LIGHT, NOT DARK * The console is used in workshops, dealer service areas and customer sites -- * bright overhead light, daylight from a roller door, a 14" laptop panel. A * near-black console loses its own body text there and its state colours * collapse into equal-brightness smudges. So the ground is light, the text is * dark, and saturation is spent only where it carries meaning: state. * * WHAT THE COLOURS HAVE TO CARRY * The catalogue is 48 operations, 13 of which write. The state machine has * three stages and a device can also be failed or absent. That is four * severities plus one action colour, and no more -- a fifth hue would make the * distinction something an operator has to read instead of see. */ :root { /* -- Ground and structure -------------------------------------------- */ --bg-app: #f6f7f8; /* window ground */ --bg-panel: #ffffff; /* raised surfaces, cards, table body */ --bg-sunken: #eef0f2; /* wells: log output, code, inactive rows */ --bg-header: #1d2530; /* app header; the one dark region */ --border: #d5d9de; /* hairline between regions */ --border-strong: #b6bcc4; /* input borders, table rules */ /* -- Text ------------------------------------------------------------- */ --text: #1a1f26; /* body */ --text-muted: #5c6570; /* secondary lines, labels */ --text-faint: #8b939c; /* timestamps, hints, placeholders */ --text-on-dark: #e8ebee; /* header text */ --text-on-accent: #ffffff; /* -- Action ----------------------------------------------------------- */ /* One accent. It marks the action the operator came to take and the selection in a list; nothing else. */ --accent: #1d4ed8; --accent-hover: #1a43bd; --accent-active: #16389e; --accent-soft: #e8eeff; /* selected row, focus ring fill */ --focus-ring: #1d4ed8; /* -- State: the fleet vocabulary -------------------------------------- */ /* Four severities. Each has a strong form for text and a soft form for fills, so a badge can be built from either without a second palette. */ --ok: #15803d; --ok-soft: #e6f4ea; --warn: #b45309; --warn-soft: #fdf3e3; --danger: #b91c1c; --danger-soft: #fdecec; --info: #1d4ed8; --info-soft: #e8eeff; --idle: #5c6570; /* not yet acted on: not a failure */ --idle-soft: #eef0f2; /* -- Device state machine --------------------------------------------- */ /* PROVISIONED_OFFLINE -> ENROLLED -> READY, and the failure/absence that sit outside the sequence. Kept as named tokens so no surface invents a fifth stage or repaints a stage by accident. */ --state-provisioned: var(--info); --state-enrolled: var(--warn); --state-ready: var(--ok); --state-failed: var(--danger); --state-absent: var(--idle); /* -- Type ------------------------------------------------------------- */ /* Workhorse faces only: this is an Operate surface, and the value of a webfont here is negative -- the fleet installs offline from a committed pool, so a font CDN is a broken page on every device. The UI face is whatever the platform ships; the data face is monospace so device ids, counts and exit codes line up in a column. */ --font-ui: system-ui, -apple-system, "Segoe UI", Roboto, "Noto Sans", "DejaVu Sans", sans-serif; --font-data: ui-monospace, "Cascadia Mono", "JetBrains Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace; --size-xs: 12px; /* timestamps, table meta */ --size-sm: 13px; /* labels, table body */ --size-md: 15px; /* body, inputs, buttons */ --size-lg: 18px; /* card titles */ --size-xl: 22px; /* view titles */ --size-2xl: 30px; /* the one headline figure per view */ --leading-tight: 1.25; --leading-normal: 1.5; /* -- Space ------------------------------------------------------------ */ /* A 4px rhythm. Named, so a component never hand-picks 7px. */ --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px; --sp-5: 24px; --sp-6: 32px; --sp-7: 48px; --radius: 6px; --radius-lg: 10px; /* -- Elevation -------------------------------------------------------- */ /* Two levels. More would separate regions that are not separate. */ --shadow-sm: 0 1px 2px rgba(16, 24, 40, .06); --shadow-md: 0 4px 12px rgba(16, 24, 40, .10); /* -- Metrics ---------------------------------------------------------- */ --row-h: 44px; /* fleet table row: comfortable at arm's length */ --control-h: 36px; /* buttons and inputs share one height */ --header-h: 56px; --sidebar-w: 220px; }