Files
cimtechniques-service-suite/docs/DESIGN-SYSTEM.md
2026-06-11 16:13:51 -04:00

7.0 KiB

CIM Service Suite — Design System ("Instrument")

Scope: this is the binding visual standard for the whole suite. Every current and future module follows it so the unified app looks like one product. The authoritative specification — every color token, type style, geometry value, and component rule — is Instrument Design Spec.md. This document is the working guide: where the implementation lives, how to adopt it in a module, and the rules that keep it coherent. If a new module needs something the system doesn't cover, extend the theme/kit packages — never introduce per-module colors, fonts, or one-off styling.

The mockups (Instrument Suite (standalone).html) are a visual reference ONLY — never pixel-measure or scrape values from them; the spec's tables are the source of truth. Rollout history: docs/superpowers/specs/2026-06-10-instrument-design-system-rollout-design.md (six phases, all merged — per-phase records in docs/BACKLOG.md BL-DS1..BL-DS-P6).

Implementation map

cim_suite/core/ui/theme/      The theme layer — the only place colors/fonts/QSS live
  tokens.py                   Two theme dicts (light/dark) keyed by spec token names,
                              + SEVERITY/STALENESS/CHART maps, Space/Radius/Metrics
  fonts.py                    Bundled IBM Plex Sans + IBM Plex Mono (OFL), loaded at startup
  type_styles.py              The spec's named type styles as QFont factories
                              (letter-spacing/weight included — QSS can't express tracking)
  stylesheet.py               build_qss(...) — the entire QSS, generated from tokens
  manager.py                  current() accessor + themeChanged signal + persistence
  __init__.py                 apply_theme(app, theme=None) — fonts + QSS + Fusion base

cim_suite/core/ui/kit/        The component kit (spec §5) — built once, adopted per module
  delegate.py                 InstrumentDelegate: cell kinds, §1.2 status tags, edge bars,
                              alarm-row tint, hover edit affordance, styled editor,
                              validation, write feedback (mark_pending/resolve), QUIET_ROLE
  settings_list.py            SettingsList (§5.5): groups, hints, RO chips, toggles, choices
  summary_strip.py            SummaryStrip (§5.4 footer) + the shared EDIT_HINT constant
  units_header.py             UnitsHeaderView: microcaps header + mono units line
  tab_widget.py               InstrumentTabWidget: §5.3 underline tabs
  toggle_switch.py            ToggleSwitch (§5.4 boolean columns)
  connection_chip.py          ConnectionChip (§5.2 toolbar pill)
  sidebar.py                  Sidebar (§5.7 device list)
  activity_log.py             ActivityLogCard (§5.8)
  icons.py                    currentColor-tinted SVG icons

cim_suite/core/ui/chrome/     Window chrome (spec §4): frameless TitleBar, AccentStrip,
                              StatusFooter, ChromeDialog + message_box confirms

apply_theme(app, theme=None) is called once by the shell; it loads the saved theme choice when theme is omitted. Theme choice persists in the JSON config under ui.json. Theme switching: set_theme(app, name) regenerates and reapplies the QSS; anything that paints token colors in code must read theme.current() at paint time (delegates do this) or repaint on manager.signals.themeChanged.

Rules (enforced)

  • No hard-coded hex and no setStyleSheet outside cim_suite/core/ui/theme/tests/core/test_design_discipline.py fails the build otherwise.
  • Fonts come from type_styles, never ad-hoc QFont(...) — microcaps tracking and the Plex families only exist there (Qt ignores text-transform/tracking in QSS, so microcaps text is .upper() + the style's font, in code).
  • Status is never color alone (§1.2): the kit's status tags pair the dot shape with a text label; alarm rows pair the tint with the edge bar.
  • One primary action per toolbar (§5.1): the connect/disconnect verb gets objectName="PrimaryAction"; everything else is secondary/quiet.
  • Single-click inline editing (§5.6) via TableTab/the kit delegate; Qt.ItemIsEditable is the single source of editability. When repopulating a table from the model, run under the tab's _loading guard so programmatic fills don't fire phantom edits (write-storm protection — regression-tested).
  • Geometry/spacing from Space/Radius/Metrics tokens, not magic numbers.

Component hooks

Intent How
Highlighted toolbar action tool button objectName="PrimaryAction"
Brand marks in the toolbar QLabel objectName="BrandTitle" / "BrandTitleAccent"
Filled accent button button.setProperty("variant", "primary")
Footer readouts chrome.StatusFooter — declarative add_dot/add_field
Confirm/info dialogs chrome.message_box submodule — message_box.question/information/warning (chrome-framed, breadcrumb titles)
Grid summary footer TableTab.enable_summary(kit.EDIT_HINT) + summary.set_summary([...])

For new modules — adoption checklist

The worked examples are the three shipped modules; cim_suite/modules/da07/ui/ is the most recent full adoption and cim_suite/modules/iomodbus/ui/ shows the sidebar + activity log. Copy this checklist into the module's adoption plan:

  • Toolbar per spec §5.2: ← Suite (emit suiteRequested; the shell wires it), brand labels + kit.ConnectionChip (the module feeds COM/SIM source labels), rare/risky commands behind a confirm-guarded menu (menu.setToolTipsVisible(True) — QMenu hides tooltips by default), primary Connect/Disconnect far right
  • Grid screens subclass TableTab; column kinds set on the delegate (NUMERIC/IDENTIFIER/STATUS/TOGGLE), units rows via set_column_units, header help via set_column_help (tooltips — never ⓘ markers)
  • Status columns use §1.2 status tags (STATUS_ROLE), full-row alarm treatment via ALARM_ROW_ROLE for alarm-class states only
  • Summary strip with kit.EDIT_HINT on every editable grid
  • Station/settings screens use kit.SettingsList (§5.5) with curated groups; read-only governed by the wire/protocol, never invented; toggles/choices only where the value's meaning is verified (VB6-fidelity rule)
  • Write feedback (§5.6): delegate.mark_pending on edit; resolve on the protocol's confirmation (echo or re-read)
  • No setStyleSheet, no hex, no ad-hoc fonts in module code (the guard test enforces it); custom paints read theme.current() at paint time
  • Spreadsheet export wired per docs/EXPORT.md
  • Status shown by color and text; exactly one primary action per surface

Fonts & licensing

IBM Plex Sans and IBM Plex Mono are bundled under the SIL Open Font License (cim_suite/core/ui/theme/fonts/OFL-IBMPlex.txt) and registered at startup via QFontDatabase; if the assets are missing, the theme degrades to system fonts rather than failing.