PRIZM 4.0DSTA
Viewing C3 design language
RC3 · Components

Platform roster

A list of platforms with quick-glance link, autonomy, and battery per row. The active platform is unmistakable — Ember-marked left edge and leading dot. Essential for group / swarm command surfaces.

Live

Two roster postures. Click a row to make it active — the Ember marker shifts. Each row carries signal bars, identifier, class tag, autonomy rung, battery percent, and link label. Battery and link colours use semantic tokens; identity stays on the Ember edge.

Group — 3 platforms
ECHELON BRAVO
Swarm — 7 platforms, mixed state
PERIMETER WATCH

Anatomy

Vertical list. Each row is a single mono line of state. The active row carries the Ember marker on the left edge; everything else stays neutral so the active context reads unambiguously.

Active marker

Left-edge Ember bar + Ember leading dot mark the active platform. Honours invariant 3 — the active context is unmistakable at a glance.

Signal bars

Four-bar reading coloured by link status. Greyed bars indicate empty slots. Matches the sibling comms / health strip's bar treatment.

Identifier + class

Mono identifier with optional class tag (UGV / UAV / USV / UUV). Class reads in fg-subtle so it doesn't compete with the ID.

Autonomy + battery + link

Right-aligned cluster: current autonomy rung (mono caps), battery percentage with semantic colour, and link label (LINK / DEGRADED / LOST) in semantic tone.

Props

PropTypeDefaultDescription
platformsRosterEntry[]Platforms to render, in display order.
activeIdstringThe id of the currently active platform. Marked with Ember left-edge + dot.
onSelect(id: string) => voidFires when the operator selects a row. When omitted, rows are non-interactive (status only) and render without focus / hover affordances.
labelstringOptional roster label rendered above the list — e.g. "ECHELON BRAVO".
fillHeightbooleanfalseOpt into fit-and-scroll. Root takes parent's full height; the label header stays anchored; the list scrolls within the roster frame. Use when the fleet might be longer than the column can show.
classNamestringForwarded to the root container.

Types

RosterEntry

Per-platform row state. Drives the visual cells. Leave lost platforms in the array (with `link: "lost"`) rather than removing them — the operator's spatial map stays stable.

ts
interface RosterEntry {
  id: string;                    // operator-visible identifier, e.g. "UGV-04"
  link: "good" | "degraded" | "lost";
  signal?: 0 | 1 | 2 | 3 | 4;    // bars; defaults from link status when omitted
  battery?: number;              // 0-100, colour-coded
  autonomy?: string;             // e.g. "MANUAL", "SUPERVISED"
  klass?: string;                // class tag, e.g. "UGV", "UAV"
}

Wiring

The roster is read-only state plus a single selection callback. The consumer holds the active-platform state machine; the roster renders it.

tsx
<PlatformRoster
  label="ECHELON BRAVO"
  platforms={fleet.map(toRosterEntry)}
  activeId={activePlatformId}
  onSelect={(id) => commandContext.focus(id)}
/>

Behavioural rule

Invariant Three

Active context unambiguous

The same command word means different things at different platforms. The roster's Ember-marked active row anchors the operator's spatial sense of who am I addressing right now — no row hover state, no near-miss colour, no ambiguity.

Read invariant

Accessibility

Listbox roleThe root carries `role="listbox"` with an `aria-label` (defaults to `Platform roster`). Each row is `role="option"` with `aria-selected` reflecting the active state.
Interactive vs statusWhen `onSelect` is passed, rows render as buttons with hover / focus affordances and keyboard activation. When omitted, rows are static status-only items — no focus ring, no hover.
Decorative visualsSignal bars, the Ember left-edge, and the leading dot are `aria-hidden`. Row text — ID, autonomy, battery percentage, link label — carries the meaning.
Colour and meaningLink state (LINK / DEGRADED / LOST) and battery percentage are paired with explicit text. Operators with colour-vision differences still read state.

JavaFX

Ships in the PRIZM JavaFX library for thick-client C3 apps as Rc3PlatformRoster (extends VBox). Run the gallery to see it natively.

java
import design.prizm.fx.rc3.Rc3PlatformRoster;

Rc3PlatformRoster()
MemberTypeDefaultDescription
LinkStatusenumGOOD / DEGRADED / LOST — signal-bar colour + link label.
RosterEntryrecord(String id, LinkStatus link, Integer signal, Integer battery, String autonomy, String klass)Optional fields may be null; signal falls back to a status default.
setPlatforms(List<RosterEntry>) → voidDisplay order.
setActiveId(String) → voidEmber-marked active row (left bar + dot).
setOnSelect(Consumer<String>) → voidMakes rows interactive.
setLabel(String) → voidOptional roster header.
setFillHeight(boolean) → voidfalseFit-and-scroll — needs a height-constrained parent.

Honours invariant 3 (active context unambiguous). Mirrors components/rc3/platform-roster.tsx.

Usage

Use whenever the operator commands more than one platform. The roster's first responsibility is to make the active context unambiguous — if no platform is active, pass an explicit activeId that the consumer's state machine can verify. Leave lost platforms in the list with link: "lost" rather than removing them — the operator's spatial map of the fleet stays stable.