The device console is a default-grey tkinter window with no hierarchy: no grouping, no state colour, no type scale, the privilege warning buried in a monospace paragraph, "Çık" duplicated at two sizes, and an output area that looks editable. Every measurement is in the delivered work; the point here is that the fix is a system, not a repaint. Adds PRODUCT.md (product truth: who uses this, from where, under what constraints), docs/DESIGN.md (the direction contract and the shared status vocabulary) and web/static/tokens.css (the values both surfaces read). The vocabulary is the load-bearing part. Four severities -- ok / warn / danger / info -- plus one action accent, and one Turkish word per device stage (KURULDU / KAYITLI / HAZIR / HATA / YOK) used identically on the device and in the central panel. A fifth hue is a bug, and colour is never the only carrier: every badge ships its word, so the fleet list stays readable in greyscale. The ground is light on purpose. The console runs in workshops and dealer service areas in daylight; a near-black ops console loses its own body text there and its state colours collapse into equal-brightness smudges. No webfont: the fleet installs offline from a committed pool, so a font CDN is a broken page on every device.
4.2 KiB
Blueforce Field OS — Design System
Two operator surfaces, one visual language: the device console (Python/tkinter,
ships on each field PC) and the central panel (Python stdlib http.server,
browser). Both describe the same fleet from opposite ends, so they share a status
vocabulary and a token set. Palette values live in web/static/tokens.css; the
console reads the same values through bfos/ui_theme.py.
Direction contract
THESIS. The device's state is the subject, and it must be legible from a step back — on a workshop laptop, in daylight, with someone waiting. This surface refuses the category default it would otherwise inherit: a near-black "ops console" with grey body text, six hues fighting for meaning, and status buried in a log. Instead: a light ground, dark text with real contrast, and saturation spent only where it carries a state.
OWN-WORLD. Light neutral ground (--bg-app #f6f7f8) with white raised panels
and hairline borders; one dark region, the app header, which anchors the frame.
One action accent (--accent #1d4ed8) marking the action the operator came to
take and the selected row, nothing else. Four state severities — ok / warn /
danger / info — each with a strong and a soft form, so a badge or a row can be
built from one palette without inventing a second. Data (ids, counts, exit codes,
timestamps) set in monospace so columns align; UI text in the platform face.
Buttons and inputs share one height; rows share one height; a 4px space rhythm.
STORY. The operator sees at a glance whether the device (or the fleet) is ready, what stage it reached, and what the one next action is. A technician understands an install's outcome without reading the log; an operator scanning 700 devices finds the ones that need attention without opening them.
FIRST VIEWPORT. Header band (dark, 56px) carrying the product name, the target identity, and privilege level on the right — privilege is never a buried sentence. Below it, a two-column body: left, the fleet/device list with a state badge per row, sorted so non-ready devices surface first, with a filter bar and a summary strip of counts by state (the one large figure per view). Right, the selected device's detail: state, target id, hostname, wave, and the channel verification list. The primary action sits in the header, not below the fold.
FORM. Operate surface. The world is a control room board: fixed regions, legible readouts, one accent, state as signal. No decorative motion; the only transition is a state change and a row selection.
FINISH. unreviewed and undocumented is unfinished; this build ends with the finish review, the verdict, DESIGN.md, and every shipping raster carrying its provenance.
Status vocabulary
One word per state, Turkish, used identically in both surfaces.
| State | Token | Meaning |
|---|---|---|
KURULDU |
--state-provisioned |
Provisioned offline, not yet enrolled |
KAYITLI |
--state-enrolled |
Enrolled, channels not all verified |
HAZIR |
--state-ready |
Every channel verified; the only shippable state |
HATA |
--state-failed |
A check failed; the device needs a person |
YOK |
--state-absent |
Not reachable / never seen |
Colour is never the only carrier: every badge ships its word, and the fleet table sorts non-ready first, so the list is readable in greyscale.
Privilege
Read-only and full privilege are visually distinct states, not a log line: the header states the current level, and the console disables rather than hides write operations when unprivileged, with the reason on the control.
Rules
- Saturation carries meaning. A colour is a state or the action accent; it is never decoration, and a fifth hue is a bug.
- Badges carry words. Greyscale must not lose the state.
- One loud figure per view. Everything else is quiet and aligned.
- No webfont. The fleet installs offline; the UI face is whatever the platform ships and the data face is monospace.
- Destructive actions look different and ask twice. Approval and the root gate are design, not an afterthought.
- The log is evidence, not the interface. Outcomes surface as state and structure first; raw output stays available, collapsed.