Design system
Open the prototype

LOVE design system

The design system for LOVE, the LSST Operations and Visualization Environment: the web interface observers and engineers use to monitor and control the Rubin Observatory control system. This system replaces the original internal-tool styling with one consistent, accessible visual language, in a dark theme for night operations and a light theme for daytime work.

Principles

  1. State is never color alone. Every status carries an icon shape and a text label as well as a color. Someone with deuteranopia, someone on a washed-out control-room monitor, and someone reading a printed log all get the same meaning.
  2. One meaning per color. Cyan (accent) means "you can interact with this." Green, violet, yellow, orange and magenta-red mean system states, and nothing else. Chart series use their own palette, and the force map uses its own diverging scale.
  3. Dark first, light equal. Dark is the default because operators work at night with dark-adapted eyes. Light is designed and checked separately, not produced by inverting dark.
  4. Numbers are the content. Telemetry is set in IBM Plex Mono with tabular figures, so values stay aligned and don't jitter as they update.
  5. Hide complexity until asked. Panels lead with a summary state; raw data and engineering detail sit one step deeper (semantic zoom, "raw data", detail cards).

Color

Surfaces and text

Role Dark Light Rule
bg-page #0A0E14 #F2F5F8 App background
bg-surface #111821 #FFFFFF Panels; every contrast check is made against this
bg-raised #18212C #F6F8FA Inset groups, table headers
text-primary #E9EEF4 (15.3:1) #0F161E (18.2:1) Values, titles
text-secondary #AEB9C6 (9.0:1) #455262 (8.0:1) Labels
text-muted #8592A1 (5.6:1) #5F6C7B (5.4:1) Units, timestamps, the faintest text allowed
border-control #5A6878 (3.1:1) #8693A2 (3.1:1) Any control's outline

The neutrals are a cool blue-gray rather than the old teal-gray. That keeps the night-sky character while letting status colors read cleanly against it.

Accent: interaction only

accent is cyan (#56C2F0 dark, #056E9F light), carried over from the original LOVE identity. It marks links, toggles, focus rings and the selected item. It never means a state, which is why the "in progress" status is violet rather than blue.

A solid accent fill means a command. Only a CommandButton, which sends something to the telescope or a subsystem, is filled cyan. Buttons that only change the LOVE interface (save a view, add a panel, open raw data) are neutral. So an operator can tell from color, glyph and label alone whether a click will move hardware.

Status: the core of the system

Status Dark signal Light signal / ink Icon LOVE states
ok #4DD991 #2CBF6E / #0B6A3A ✓ in circle ENABLED, IN POSITION, DONE, CONFIGURED, TRACKING, STATIONARY
active #A08CFF #7457E8 / #5336C8 rotating arc MOVING, SLEWING, HOMING, RUNNING, INTEGRATING, RAISING, LOWERING
warning #F9E04B #F0BC00 / #6E5300 triangle ! Watcher WARNING, UNCONFIGURED, near a limit
serious #FF8B42 #D9580F / #9E3F08 diamond ! Watcher SERIOUS, CONFIGURE FAILED, LOAD FAILED
critical #F2558E #B01049 / #A50E44 octagon × FAULT, FAILED, Watcher CRITICAL, breakaway
neutral #97A3B1 #7D8896 / #45505E pause / minus / dashed ring / ? STANDBY, DISABLED, PARKED, NOT IN POSITION, OFFLINE, UNKNOWN

What changed from the original, and why:

  • STANDBY is no longer yellow. Standby is a normal resting state, not a problem. Yellow is reserved for things that need attention, so a screen in a healthy state is calm.
  • Fault is magenta-red, not orange. Under deuteranopia, the old green and orange collapse into the same olive. Pushing critical toward magenta, and making it darker than green, keeps the two apart.
  • Each tone has three tokens. status-* is the signal color for dots, marks and drawings. status-*-text is the ink for text and icons inside a badge. status-*-tint is the badge or banner background. In light mode, signal yellow and green fall below 3:1 on white, so badge text and icons always use the dark ink, and marks drawn in those colors get a 1px ink outline.

Validation. The six status colors, including accent, were run through the data-viz palette validator as an all-pairs set, simulating protanopia and deuteranopia with the Machado 2009 model:

  • Dark theme: worst CVD ΔE 8.2 (target ≥ 8). Worst normal-vision ΔE 16.0 (floor ≥ 15).
  • Light theme: worst CVD ΔE 8.0. Worst normal-vision ΔE 15.9.

All text pairs meet WCAG AA at 4.5:1.

Data visualization

  • Diverging (div-neg-4 … div-mid … div-pos-4). Blue ↔ gray ↔ orange, for signed values. The M1M3 force map uses it, centered on 0 N. Blue/orange is the pairing that survives every common color-vision deficiency. The midpoint is gray so that "zero" reads as "nothing." This replaces the old rainbow scale, which had no natural midpoint and put false boundaries in the data.
  • Sequential (seq-100 … seq-800). A single cyan hue for one-sided magnitudes such as temperature, pressure and wind.
  • Categorical (series-1 … series-8). Fixed order, never cycled. Charts with 2 or more series always show a legend, and series with up to 4 lines also get direct labels.
  • Status colors are never used as series colors.

Diagrams and the light path

Telescope, dome and instrument drawings use diagram-structure for fills and diagram-outline for outlines, with optics in diagram-optic. The light itself is beam, a warm starlight glow drawn through every element the light currently reaches. It stops at the first closed cover, unaligned mirror or shut shutter, so "where is the light blocked?" is answered at a glance.

Typography

IBM Plex Sans is the interface face. It has open apertures and clearly different I, l and 1, it's legible at 12px on a dim screen, and its technical, engineered character suits an observatory. IBM Plex Mono is used for every live value. Both are free on Google Fonts.

  • 12px is the minimum size for any text. The original used 13px gray-on-navy labels at under 3:1.
  • Panel titles are sentence case at 16/22 semibold. All-caps is reserved for the overline section label and for badges.
  • Long state names wrap or abbreviate in the layout. They are never cut off mid-word like "NOTINPOSI…".

Shape and space

  • Spacing: a 4px base (space-1 4 → space-6 32). Panels pad 16, groups separate by 24, panels separate by 32.
  • Radius:
  • radius-sm 4 for badges, inputs and buttons
  • radius-md 8 for inset groups
  • radius-lg 12 for panels
  • radius-full for actuator dots, hardpoints and toggles
  • One badge style. The original mixed three: pill, bracketed bar and plain text. There is now one badge: a tinted rectangle with radius-sm, an icon and an uppercase label.

Interaction

  • Focus. Every control shows a 2px focus-ring on :focus-visible. Map elements such as actuators and hardpoints are keyboard reachable with arrow keys.
  • Two kinds of button that never look alike:
UI action (Button) Command (CommandButton)
Affects Only the LOVE interface The telescope or a subsystem (CSC)
Fill Neutral (bg-raised), outline or ghost Solid accent
Glyph None, or a UI icon Transmit glyph (dot sending waves)
Label Verb Verb + target CSC in mono (Slew · ATMCS)
Feedback Immediate Sending… → ACKNOWLEDGED → DONE, or a failure message with the CSC's reason
  • Destructive and safety commands (STOP, lower mirror to static support) are CommandButtons in status-critical. They ask for confirmation, and they are always visible, never in an overflow menu. Routine commands stay one click, so operators aren't trained to click through dialogs.
  • Commands without permission are disabled with a lock glyph, and the reason is shown next to them.
  • Hover is a wash, never a color change of the data. Hover uses bg-hover.
  • Menus: an icon means "this does something". Action rows carry an icon. Information rows (status, alarms, the signed-in user) and lists of places to go don't. Choices show a check. See Menu.

Theme

  • Dark is the primary theme, and this system opens in it.
  • Automatic is the default in the app. It is dark from sunset to sunrise at Cerro Pachón and light during the day, computed from the sun's altitude at the summit rather than the viewer's clock or operating-system setting. People can pick Light or Dark instead. The choice is per device, and it persists across views and reloads.
  • Shift T switches between light and dark, and turns Automatic off.

Limits and verdicts

A condition with limits always shows four things together:

  • the value, in mono
  • a LimitGauge, with hatched warning and closure zones
  • the limits in words
  • a status badge: WITHIN LIMITS, NEAR LIMIT or CLOSE

Above the rows, a Verdict sums them up in one sentence that names the reasons. Limits in the prototype are illustrative unless marked "documented".

Alarms

  • Raising. Views raise alarms for their own conditions: wind past the closure limit, an actuator in fault, a script that stopped its queue, the dome blocking the beam.
  • The banner. The most important unacknowledged alarm sits in an AlarmBanner under the toolbar on every view. The bell shows how many alarms are unacknowledged, colored by the highest severity.
  • Acknowledging is a command to the Watcher, with the same permission rules as any other command.
  • Latching. Alarms latch: they stay until someone acknowledges them, and come back if the condition returns.

Scripts and queues

  • One queue per telescope. ScriptQueue:1 runs MainTel and ScriptQueue:2 runs AuxTel. Every command in the queue view names which one it goes to.
  • A failed script stops the queue on purpose. The view leads with a critical notice, highlights the error and traceback, and offers three ways forward: requeue, edit and requeue, or resume past it.
  • Reordering, adding, removing and requeueing are commands, and they all appear in the command log. Terminate is a critical CommandButton that asks for confirmation; Stop script is the gentle default.

Charts and maps

  • Linked hover. Small multiples share one time axis. Hovering one chart shows the same moment on all of them, and the arrow keys do the same for keyboard users.
  • Data and its limits. Warning and closure zones are drawn on charts as shaded bands, with a dashed line labelled "Close N".
  • Sky map orientation. "Looking up" (east on the left, like the all-sky camera) is the default, and "From above" (east on the right) matches the dome plan. AuxTel is series-1 (ring), MainTel series-2 (filled disc). Queued targets are diamonds, outlined in status-critical when below the pointing limit.

View editor

  • Two levels of layout. Views are composed of components on a 12-column grid. Inside a component, panels sit in columns that can be resized, reordered and hidden, and illustrations can be scaled down to a legible minimum. See ArrangePanel.
  • Save thumbnail has three states:
  • blocked when nothing has changed
  • ready when there are changes; saving the thumbnail also saves the view
  • alert (status-serious) when the view was saved without an up-to-date thumbnail
  • Previews and displays. "Layout size" previews a desk monitor (1920 px) or the control-room wall (3840 px at 1.5×) at true layout width. The control-room wall is a per-device display profile, not a separate layout.

Color

Every color has a dark and a light value. Ratios are contrast against --bg-surface in that theme.

Bg

  • --bg-page
    Dark #0A0E141.1:1
    Light #F2F5F81.1:1

    App background behind panels. Dark is the primary theme for night operations.

  • --bg-surface
    Dark #1118211.0:1
    Light #FFFFFF1.0:1

    Panel (widget) background. The surface all contrast checks are made against.

  • --bg-raised
    Dark #18212C1.1:1
    Light #F6F8FA1.1:1

    Inset groups inside a panel: table headers, detail cards, control strips.

  • --bg-input
    Dark #0D12191.1:1
    Light #FFFFFF1.0:1

    Text fields, selects and numeric inputs. Always paired with border-control.

  • --bg-hover
    Dark #212C391.3:1
    Light #EBEFF41.2:1

    Hover wash for rows, list items and ghost buttons.

  • --bg-selected
    Dark #13303F1.3:1
    Light #E1F0F81.2:1

    Selected row / selected item background (accent tint).

Border

  • --border-subtle
    Dark #222C381.3:1
    Light #E3E8EE1.2:1

    Dividers inside a panel, table row rules, chart gridlines.

  • --border-default
    Dark #2B36431.5:1
    Light #D9E0E81.3:1

    Panel outline and section separators.

  • --border-control
    Dark #5A68783.1:1
    Light #8693A23.1:1

    Borders of interactive controls (inputs, secondary buttons, toggles). Meets 3:1 against bg-surface.

Text

  • --text-primary
    Dark #E9EEF415.3:1
    Light #0F161E18.2:1

    Values, titles and body text. 15:1 dark / 18:1 light on bg-surface.

  • --text-secondary
    Dark #AEB9C69.0:1
    Light #4552628.0:1

    Field labels, secondary descriptions. ≥7:1 on bg-surface.

  • --text-muted
    Dark #8592A15.6:1
    Light #5F6C7B5.4:1

    Units, timestamps, axis ticks, placeholder text. ≥4.5:1 on every surface; never use anything fainter for text.

  • --text-on-accent
    Dark #0710181.1:1
    Light #FFFFFF1.0:1

    Text and icons on accent-filled buttons.

Accent

  • --accent
    Dark #56C2F08.8:1
    Light #056E9F5.6:1

    Interactive color only: primary buttons, links, focus, selection, toggles. Never encodes a system state.

  • --accent-hover
    Dark #7FD2F510.6:1
    Light #04587F7.8:1

    Hover/pressed state of accent fills and links.

Focus

  • --focus-ring
    Dark #7FD2F510.6:1
    Light #056E9F5.6:1

    2px keyboard focus outline, offset 2px. Every interactive element shows it on :focus-visible.

Status

  • --status-ok
    Dark #4DD9919.9:1
    Light #2CBF6E2.4:1

    Nominal / ready: ENABLED, IN POSITION, DONE, CONFIGURED, TRACKING, STATIONARY. Icon: check in circle. Signal color for marks, dots and diagram fills; always paired with icon + label.

  • --status-ok-text
    Dark #4DD9919.9:1
    Light #0B6A3A6.7:1

    Text and icon color inside a ok badge or message.

  • --status-ok-tint
    Dark #1933311.3:1
    Light #E3F6EA1.1:1

    Background of a ok badge, banner or highlighted row.

  • --status-active
    Dark #A08CFF6.5:1
    Light #7457E84.9:1

    In progress: MOVING, SLEWING, HOMING, RUNNING, INTEGRATING, RAISING, LOWERING. Icon: rotating arc. Violet, so it never reads as the cyan accent. Signal color for marks, dots and diagram fills; always paired with icon + label.

  • --status-active-text
    Dark #A08CFF6.5:1
    Light #5336C87.7:1

    Text and icon color inside a active badge or message.

  • --status-active-tint
    Dark #2528401.2:1
    Light #EEEAFD1.2:1

    Background of a active badge, banner or highlighted row.

  • --status-warning
    Dark #F9E04B13.4:1
    Light #F0BC001.8:1

    Needs attention: Watcher WARNING, UNCONFIGURED, approaching a limit. Icon: triangle with ! Signal color for marks, dots and diagram fills; always paired with icon + label.

  • --status-warning-text
    Dark #F9E04B13.4:1
    Light #6E53007.2:1

    Text and icon color inside a warning badge or message.

  • --status-warning-tint
    Dark #3134271.4:1
    Light #FCF2CF1.1:1

    Background of a warning badge, banner or highlighted row.

  • --status-serious
    Dark #FF8B427.7:1
    Light #D9580F3.9:1

    Watcher SERIOUS, CONFIGURE FAILED, LOAD FAILED. Icon: diamond with ! Signal color for marks, dots and diagram fills; always paired with icon + label.

  • --status-serious-text
    Dark #FF8B427.7:1
    Light #9E3F086.6:1

    Text and icon color inside a serious badge or message.

  • --status-serious-tint
    Dark #3228261.2:1
    Light #FCE8DC1.2:1

    Background of a serious badge, banner or highlighted row.

  • --status-critical
    Dark #F2558E5.5:1
    Light #B010497.0:1

    FAULT, FAILED, Watcher CRITICAL, hardpoint breakaway. Icon: octagon with ×. Magenta-red so it separates from ok-green under deuteranopia. Signal color for marks, dots and diagram fills; always paired with icon + label.

  • --status-critical-text
    Dark #F2558E5.5:1
    Light #A50E447.6:1

    Text and icon color inside a critical badge or message.

  • --status-critical-tint
    Dark #3021301.2:1
    Light #FBE3EA1.2:1

    Background of a critical badge, banner or highlighted row.

  • --status-neutral
    Dark #97A3B17.0:1
    Light #7D88963.6:1

    Idle states that are not problems: STANDBY, DISABLED, PARKED, NOT IN POSITION, OFFLINE, UNKNOWN. Each has its own icon (pause, minus, dashed ring, ?). Signal color for marks, dots and diagram fills; always paired with icon + label.

  • --status-neutral-text
    Dark #97A3B17.0:1
    Light #45505E8.2:1

    Text and icon color inside a neutral badge or message.

  • --status-neutral-tint
    Dark #242B351.3:1
    Light #ECEFF31.2:1

    Background of a neutral badge, banner or highlighted row.

Div

  • --div-neg-4
    Dark #7DC2FF9.4:1
    Light #0058A87.1:1

    Diverging scale, negative arm (blue). Step 4 = largest magnitude. Used for signed values such as M1M3 actuator force (−).

  • --div-neg-3
    Dark #58A2F26.7:1
    Light #287CCF4.3:1

    Diverging scale, negative arm (blue). Step 4 = largest magnitude. Used for signed values such as M1M3 actuator force (−).

  • --div-neg-2
    Dark #3F83CB4.5:1
    Light #64A2E72.7:1

    Diverging scale, negative arm (blue). Step 4 = largest magnitude. Used for signed values such as M1M3 actuator force (−).

  • --div-neg-1
    Dark #35669A3.0:1
    Light #A4C8F11.7:1

    Diverging scale, negative arm (blue). Step 4 = largest magnitude. Used for signed values such as M1M3 actuator force (−).

  • --div-mid
    Dark #43484E1.9:1
    Light #E5E8EC1.2:1

    Diverging scale midpoint: zero. Neutral gray, never a hue.

  • --div-pos-1
    Dark #8F51282.9:1
    Light #EAB89B1.8:1

    Diverging scale, positive arm (orange). Step 4 = largest magnitude.

  • --div-pos-2
    Dark #BB672C4.3:1
    Light #D988542.8:1

    Diverging scale, positive arm (orange). Step 4 = largest magnitude.

  • --div-pos-3
    Dark #E183446.4:1
    Light #BD5B004.5:1

    Diverging scale, positive arm (orange). Step 4 = largest magnitude.

  • --div-pos-4
    Dark #FFA56C9.2:1
    Light #9536007.5:1

    Diverging scale, positive arm (orange). Step 4 = largest magnitude.

Seq

  • --seq-100
    Dark #DBF3FF15.5:1
    Light #DBF3FF1.1:1

    Sequential cyan ramp for one-sided magnitude (temperature, pressure, wind). In dark mode map low values to high step numbers so small values recede into the surface.

  • --seq-200
    Dark #AFE0F712.6:1
    Light #AFE0F71.4:1

    Sequential cyan ramp for one-sided magnitude (temperature, pressure, wind). In dark mode map low values to high step numbers so small values recede into the surface.

  • --seq-300
    Dark #7DCAEB9.8:1
    Light #7DCAEB1.8:1

    Sequential cyan ramp for one-sided magnitude (temperature, pressure, wind). In dark mode map low values to high step numbers so small values recede into the surface.

  • --seq-400
    Dark #4DB2DB7.4:1
    Light #4DB2DB2.4:1

    Sequential cyan ramp for one-sided magnitude (temperature, pressure, wind). In dark mode map low values to high step numbers so small values recede into the surface.

  • --seq-500
    Dark #1096C25.2:1
    Light #1096C23.4:1

    Sequential cyan ramp for one-sided magnitude (temperature, pressure, wind). In dark mode map low values to high step numbers so small values recede into the surface.

  • --seq-600
    Dark #007AA33.7:1
    Light #007AA34.9:1

    Sequential cyan ramp for one-sided magnitude (temperature, pressure, wind). In dark mode map low values to high step numbers so small values recede into the surface.

  • --seq-700
    Dark #005F802.5:1
    Light #005F807.1:1

    Sequential cyan ramp for one-sided magnitude (temperature, pressure, wind). In dark mode map low values to high step numbers so small values recede into the surface.

  • --seq-800
    Dark #00445E1.7:1
    Light #00445E10.6:1

    Sequential cyan ramp for one-sided magnitude (temperature, pressure, wind). In dark mode map low values to high step numbers so small values recede into the surface.

Series

  • --series-1
    Dark #3987E54.9:1
    Light #2A78D64.4:1

    Categorical chart series, assigned in fixed order 1→8, never cycled. Lines and scatter beyond 3 series need direct labels.

  • --series-2
    Dark #D959264.6:1
    Light #EB68343.2:1

    Categorical chart series, assigned in fixed order 1→8, never cycled. Lines and scatter beyond 3 series need direct labels.

  • --series-3
    Dark #199E705.2:1
    Light #1BAF7A2.8:1

    Categorical chart series, assigned in fixed order 1→8, never cycled. Lines and scatter beyond 3 series need direct labels.

  • --series-4
    Dark #C985005.8:1
    Light #EDA1002.2:1

    Categorical chart series, assigned in fixed order 1→8, never cycled. Lines and scatter beyond 3 series need direct labels.

  • --series-5
    Dark #D551814.5:1
    Light #E87BA42.7:1

    Categorical chart series, assigned in fixed order 1→8, never cycled. Lines and scatter beyond 3 series need direct labels.

  • --series-6
    Dark #0083003.6:1
    Light #0083004.9:1

    Categorical chart series, assigned in fixed order 1→8, never cycled. Lines and scatter beyond 3 series need direct labels.

  • --series-7
    Dark #9085E95.7:1
    Light #6250D65.8:1

    Categorical chart series, assigned in fixed order 1→8, never cycled. Lines and scatter beyond 3 series need direct labels.

  • --series-8
    Dark #E667675.5:1
    Light #E349484.0:1

    Categorical chart series, assigned in fixed order 1→8, never cycled. Lines and scatter beyond 3 series need direct labels.

Diagram

  • --diagram-structure
    Dark #1C26321.2:1
    Light #E4EAF01.2:1

    Fill of mechanical structure in telescope, dome and instrument drawings.

  • --diagram-outline
    Dark #3E4B5B2.0:1
    Light #AEB9C62.0:1

    Outline of structure in drawings.

  • --diagram-optic
    Dark #6FA9C97.0:1
    Light #3F7FA34.4:1

    Optical elements (mirrors, filters, gratings) when idle.

Beam

  • --beam
    Dark #FFE7A814.7:1
    Light #C98A002.9:1

    The light path. Drawn as a glow through every element light currently reaches; stops where light is blocked.

Typography

Interface · "IBM Plex Sans"

  • Auxiliary Telescope
    display28px/34px · 600

    View titles.

  • Dome and mount
    title16px/22px · 600

    Panel titles. Sentence case, never all caps.

  • MOUNT
    overline12px/16px · 600

    Section labels inside a panel. The only uppercase style besides badges.

  • Move dome to home position
    body14px/20px · 400

    Default text, field labels.

  • Time to limit
    label12px/16px · 500

    Secondary labels, units, captions. 12px is the minimum size for any text.

  • IN POSITION
    badge12px/16px · 600

    Status badge text.

Telemetry · "IBM Plex Mono"

  • 183.402°
    value14px/20px · 500

    Live values in tables and summaries. Tabular figures so columns align and numbers don't jitter.

  • −12.88 N
    value-lg24px/30px · 500

    Headline readouts (selected actuator, current azimuth).

Spacing and shape

Spacing

  • space-14px

    Icon-to-label gap.

  • space-28px

    Gap between related controls; badge horizontal padding.

  • space-312px

    Row padding in tables and lists.

  • space-416px

    Panel inner padding.

  • space-524px

    Gap between groups inside a panel.

  • space-632px

    Gap between panels.

Radius

  • radius-sm4px

    Badges, inputs, buttons, table cells.

  • radius-md8px

    Inset groups and cards inside panels.

  • radius-lg12px

    Panels.

  • radius-full999px

    Toggles, actuator dots, hardpoint chips.

Components

Live previews. Most of them respond to clicks and the keyboard.

AlarmBanner

Status

The newest unacknowledged alarm, under the toolbar on every view. Acknowledging is a command to the Watcher

An AlarmBanner shows the most important unacknowledged alarm on every view, directly under the toolbar.

  • Which alarm. Alarms raised by the current view come first, then the highest severity (critical > serious > warning), then the newest. "N more" opens the full list in the notifications menu.
  • Acknowledging is a command. Acknowledge is a CommandButton with the target Watcher. A read-only user sees why they can't acknowledge.
  • Alarms latch. An alarm stays until someone acknowledges it, even after its condition clears; the banner then adds "condition cleared". If an acknowledged alarm's condition comes back, it is raised again.
  • Navigation. The "Open" button (for example "Open Environment") is a UI button that goes to the view that raised the alarm. It is hidden when you're already there.
  • Tint. The background is the severity's tint mixed 70% into bg-surface. Use role="alert".

StatusBadge

Status

One badge for every state: tint + icon shape + label

The single way to show a system state: a tinted rectangle with an icon shape and an uppercase label.

  • Pick the tone by meaning, not by word. STANDBY and PARKED are neutral, because idle isn't a problem. Yellow is only for things that need attention.
  • The icon shape carries the meaning for color-blind readers and grayscale prints: ✓ ok, arc in progress, triangle warning, diamond serious, octagon critical, and pause, minus, dashed ring or ? for idle and unknown states.
  • Never truncate the label. Give the badge its natural width. If space is short, let the row wrap.
  • Text and icon use status-*-text and the background uses status-*-tint. The badge has role="status" so screen readers announce changes.

Verdict

Status

One sentence that answers: is it safe, and if not, why

A Verdict answers one question in one sentence, such as "is it safe to observe?", and names the reason when the answer isn't a plain yes.

  • Three states. ok (safe), warning (safe, with conditions to watch) and critical (conditions call for closing). Each state has its status icon, a tinted icon disc and a border in its signal color.
  • The reason is required for warning and critical. It lists the conditions by name, joined in plain English: "Dew point margin and humidity are near the closure limit."
  • It describes, it doesn't decide. The Environment view says the decision to open or close stays with the observers. A Verdict never sends a command by itself.
  • Announce changes politely. Use role="status", so a screen reader reads the new verdict when it changes.

Button

Controls

UI actions: change the view, never the telescope

UI buttons change only what you see in LOVE: views, panels, filters, layout, raw data. They never reach the observatory, so they never use the accent fill.

  • Neutral (bg-raised fill, border-default) is the main UI action in a panel: Save view, Apply.
  • Outline (border-control, 3:1) is for secondary UI actions.
  • Ghost (text-secondary) is for low-emphasis actions such as Raw data or Expand.
  • Icon-only buttons carry an aria-label and the same three styles.
  • If a button sends anything to the telescope or a subsystem, it is a CommandButton, not a Button.

CommandButton

Controls

Commands to the observatory: accent fill, transmit glyph, named target, visible lifecycle. Click them.

A CommandButton sends a command to the telescope or one of its subsystems. It is the only control with a solid accent fill, so anything that fills cyan reaches the observatory.

It is told apart from a UI button in four ways, so it never relies on color alone:

  1. Solid accent fill. UI buttons are never filled with accent.
  2. The transmit glyph (a dot sending waves) before the label.
  3. The target segment. The CSC that receives the command is named at the right end in mono (ATMCS, ATDome, ScriptQueue:2). Icon-only commands put the target in their aria-label and tooltip.
  4. A visible lifecycle. While sending, the button shows Sending… and blocks double-sends. A badge beside it then shows ACKNOWLEDGED (active) and finishes as DONE with the duration, or as a critical message giving the CSC's reason. The button itself never turns a status color.

Rules:

  • Without permission, the button is disabled with a lock glyph, and the reason sits next to it rather than only in a tooltip.
  • STOP is a CommandButton in status-critical with the octagon glyph and the target ALL. It is always visible and asks for confirmation.
  • Confirmation is for safety-critical commands only (STOP, raise or lower mirror, open dome). Routine commands like Slew stay one click, so operators aren't trained to click through dialogs.

ForceMap

Data

M1M3 actuators on the diverging scale, faults by shape

The M1M3 actuator map, colored on the diverging blue ↔ gray ↔ orange scale and centered on 0 N. It replaces the rainbow scale, which had no zero point and isn't color-blind safe.

  • Faulted actuators keep their position but change shape. They become a hollow ring with an × in status-critical and leave the color scale altogether, so a fault can never be mistaken for a large force.
  • Selection is the focus-ring outline. Every actuator is keyboard-focusable, and its detail card shows applied force, commanded force and following error in value-lg.
  • The legend always shows numeric end points and the zero. It never shows "±Infinity"; with no data, the map shows an empty state instead.

LightPath

Data

The beam token stops where light is blocked

The drawing language for the telescope, dome and instrument views: diagram-structure fills, diagram-outline outlines and diagram-optic optics. The light itself is drawn in beam.

  • The beam is drawn through every element the light reaches, and ends at the first thing blocking it. The blocking element is outlined in a status color that matches its badge.
  • Every drawing has a list of the same elements beside it, each with its badge. Screen readers and color-blind readers get the state from the list, not from the picture.

LimitGauge

Data

A value against its warning and closure zones. Hatching keeps the zones apart without color

A LimitGauge shows a value against its warning and closure zones, so a glance answers "how close are we?" as well as "are we over?"

  • Track. The track is bg-raised. The warning zone is status-warning-tint with 135° hatching, and the closure zone is status-critical-tint with 45° hatching. The two hatch angles keep the zones distinct without relying on color.
  • Marker. The current value is a 4px text-primary marker with a 2px bg-surface halo.
  • Direction. The zones sit at the high end for upper limits (humidity, wind) and at the low end for lower limits (dew point margin).
  • Layout. Always pair the gauge with the number, the limits in words ("Warn ≥ 80% · Close ≥ 90%") and a status badge (WITHIN LIMITS, NEAR LIMIT, CLOSE). The gauge itself is aria-hidden. In narrow containers, drop the gauge before dropping the words.

WindRose

Data

Share of time per direction and speed band. One hue, light to dark, area-true rings

A WindRose shows how much of the time the wind blew from each of 16 directions, stacked by speed band.

  • Color. One hue, from the sequential scale. In light mode the bands run seq-300 (slowest) to seq-800 (fastest); in dark mode they run the other way, seq-700 to seq-100, so faster wind is always more prominent. Never use a rainbow, and never use status colors for speed.
  • Geometry.
  • The radius grows with the square root of the share, so wedge area is true to the data.
  • Wedges have a 1.5px bg-surface gap.
  • The share of calm time is printed in the centre.
  • Overlays. The current wind direction is a text-primary line with an arrowhead. The dome slit (series-1) and the telescope (series-2) are drawn on the rim, so you can see where the wind hits relative to the pointing.
  • Legend and access. Always show a legend with km/h units, and hover tooltips with the direction, speed band and share. Give a text summary in <desc>.

ScriptQueueItem

Lists

Waiting scripts: a drag handle that also works with arrow keys, the reason, the estimate and the load state

A ScriptQueueItem is one script waiting in a ScriptQueue.

  • What the row shows. From left to right:
  • its position
  • a drag handle
  • the path, with the folder in text-muted and the file name in text-primary, in mono
  • why it's running
  • the estimate
  • its load state: CONFIGURED (neutral), LOADING (active), CONFIGURE FAILED (serious)
  • Reordering is a command. Dragging the handle, or pressing ↑ and ↓ on it, sends move to the ScriptQueue. The move is announced in a live region and logged.
  • Expanding a row shows its configuration and command buttons (Move to top, Remove). Those are commands, so they are never hidden in a menu.
  • Failures. A configure failure gets a critical-tinted border, and the error is shown as text, not only in a tooltip.

Menu

Navigation

Actions carry an icon; information and destinations do not; choices show a check

A Menu is a popover list opened from a bar button. It follows one rule: an icon means the row does something.

  • Actions have a 16px icon in text-secondary. Examples: Add log entry, Emergency contacts, LOVE configuration file, Switch user.
  • Information rows have no icon. Examples: the signed-in user, alarms, status rows, recent commands.
  • Destinations have no icon either. Examples: the list of views in the breadcrumb menu.
  • Choices (theme, display) are menuitemradio. They show a check in the icon slot when selected, and a short state on the right ("Dark now", "Now: Desk").
  • Keyboard. ↑ ↓ move between items, Home and End jump to the first and last, and Esc or Tab closes the menu and returns focus to its button.
  • Keep menus short. Keyboard shortcuts live in their own dialog, not repeated in every menu.

ArrangePanel

Editor

Edit mode: each panel gets a strip to move or hide it, columns resize at their divider, illustrations at their corner

ArrangePanel covers the view editor's controls for arranging the panels inside a component. They appear only in edit mode, and panel contents are inert while editing.

  • Panel strip. Each panel gets a strip with:
  • a grip, for drag, or ↑ ↓ and Shift ← → on the keyboard
  • move up and down
  • move to the previous or next column
  • hide

Hidden panels are listed in the arrange bar as "Show" buttons named after each panel. The last visible panel can't be hidden. - Columns resize at the line between them, by drag or ← → on the focused separator, in steps of 1/12, with at least 2/12 per column. "Add column" allows up to 4 columns, and an empty column offers "Remove column". - Illustrations resize from a round handle at their bottom-right corner, by drag, the arrow keys, or Home to reset. Each has a minimum size so it stays legible: about 340px for the dome plan and 260px for the actuator maps. Shrinking an illustration gives its freed space to the information next to it. - History and saving. Every change goes through undo and redo, and is saved with the view. - Responsive layout. Panels respond to their own width (container queries), not the window's, so a narrow column reflows its contents.

TelemetryPanel

Panels

Panel chrome, status rows, mono values and a limit gauge

The panel every LOVE widget sits in: a bg-surface card with a sentence-case title, a "Raw data" escape hatch, and overline group labels each followed by a summary badge.

  • Labels and values. Labels sit on the left in text-secondary. Values sit on the right in mono with tabular figures, and a target follows as → value in text-muted.
  • Limit gauges. Forbidden zones are hatched in status-warning, so they read without color. The current position is a solid tick, and the commanded target is an active diamond.