# Elbow: a working grammar for LCARS-style panels

Draft 0.4 · 2026-10-30 · drafted by Claude, not yet edited by Dan

"Elbow" is a working name. It is inspired by LCARS, the Star Trek interface designed by
Michael Okuda. It is not LCARS and does not use that name. Paramount owns the look and has
enforced it before (two apps were pulled in 2011).

**What changed in 0.4:**
- New: the map (rule 16), rows that open in place, and the pop-up rule (rule 17).
- The data screen gains a number band, a status line and a polar plot (rule 14).
- The research behind all of this is in `research/REFERENCES.md`.

**What changed in 0.3:**
- Phones now use the PADD layout: a side rail, a C-shaped frame with status in the bottom
  bar, and nested menus you go into one level at a time (rule 12).
- Big-button chips now say what they count (rule 4).

**What changed in 0.2:**
- Nesting now goes four levels deep.
- Colors are families of tints.
- Every number on a segment is an address you can type.
- New: big buttons with cost rings, the split frame, and the bracket viewport (after the TNG
  science stations).
- New: a rule for charts.
- New: themes.

## Why this exists

LCARS was built to look advanced on a TV budget. Its numbers mean nothing, its colors mean
nothing, and you can't tell a button from a label. The open-source LCARS projects (CSS
frameworks, Home Assistant themes) are skins: they copy the shapes and leave out the rules.

This spec keeps the shapes and gives each one a job.

## Where it fits

It fits ambient control panels: wall tablets, home dashboards, kiosks and status screens.
These are places where people glance from across the room, tap one big thing, or watch some
numbers move.

It's a poor fit for long reading and forms with many fields. Put a plain content area inside
the field for those (rule 2).

## The rules

### 1. The frame is navigation
- The **rail** is the vertical bar on the left. Each rail segment is one top-level section,
  and a filler segment takes up the rest.
- The **header bar** says where you are: the section name, the address (rule 6) and the
  condition (rule 7).
- The **elbow** is home, and it shows one live value, such as the clock.
- The current segment is lighter and has a **black notch** on its inner edge. It also has
  `aria-current`. The notch matters because tints alone also vary for rhythm (rule 5).

### 2. The field holds content; the frame never does
The black area inside the frame is the field. Readouts, controls, charts and text go there.

### 3. Thickness means depth
Each level of navigation is a frame drawn inside its parent, and each level is thinner. This
lets people see how deep they are without reading anything.

| Depth | Example | Side | Bar | Corner radius | Tint |
|---|---|---|---|---|---|
| 0 | Root frame | 150px | 30px | 64px | Frame 1 |
| 1 | Systems | 132px | 16px | 56px | Frame 2 |
| 2 | Climate | 116px | 10px | 46px | Frame 3 |
| 3 | Upstairs | 100px | 6px | 36px | Frame 4 |

- Every frame goes from thick to thin at each turn. A frame never keeps the same thickness
  around a corner. This is the one older LCARS rule kept as written.
- Four levels is the limit. Content at depth 3 is a leaf: no more frames.
- Ancestor frames stay visible. They are the breadcrumb.
- `Esc` goes up one level, back to a thicker frame. Tapping the open item again also closes it.
- A **group frame** (`.panel`) holds content, never navigation. It always uses the thinnest
  stroke.
- Segments get narrower with depth, but touch targets stay at least 48px tall.

### 4. Shape tells you what you can do
| Shape | Meaning | Clickable? |
|---|---|---|
| Segment attached to a frame | Navigation | Yes |
| Pill, floating in the field | Action | Yes |
| Big button (rounded block, 110px or taller) | One of the panel's main actions | Yes |
| Square-ended block | Readout or label | Never |
| Bracket | A viewport around a visualization | Its contents may be |

- **Toggles** are pills. A toggle is filled when on and outlined when off, and its text says
  ON or OFF.
- **Big buttons** are for the few things the panel exists to do. Use at most six on a screen.
  Each one has a **chip**: a black circle with a live number inside and a colored ring
  around it.
  - The number is how many things the button will change, and the line under the name says
    what they are ("Turns off 2 lights, locks 1 door"). A number never appears without its
    noun.
  - When there is nothing to change, the chip shows a check, the button dims, and the line
    reads "Already set".
  - The ring is what pressing costs:

    | Ring | Cost |
    |---|---|
    | White | Happens now and can be undone. A tap is enough. |
    | Gold | Hard to undo. Press and hold; a sweep fills the button. Releasing early cancels. |
    | Red | Reaches a person, such as a text or a call. Hold longer (2s). |

  - Holding works with Space or Enter as well as touch.
- Hover makes a shape brighter, and press makes it darker. Keyboard focus shows a 3px ring in
  the Data color, offset 3px.

### 5. Color: five families, many tints
The family carries the meaning, and a tint within a family only tells neighbors apart. This
gives the panel the show's busy, many-colored look without colors that lie.

| Family | Tints (Amber theme) | Used for |
|---|---|---|
| Frame | amber `#F2A541`, apricot `#FFB86B`, sand `#E8C48E`, clay `#D49A6A` | Rail, bars, elbows, navigation, group frames |
| Action | lavender `#B9A3FF`, lilac `#D5C7FF`, violet `#9B84EE` | Pills, big buttons |
| Data | ice `#8EC5FF`, frost `#BFE0FF`, steel `#5FB3D9` | Readouts, chart marks, spec sheets |
| Caution | gold `#FFD23F`, `#FFE07A` | Needs attention soon; hold-to-confirm rings |
| Alert | red `#FF4D3D`, `#FF7766` | Needs attention now; rings that reach a person |

- Color is never the only signal. Every role also has a shape or a word.
- Text on a colored shape is black. Colored text sits on black. Every pair must meet 4.5:1
  contrast. All tints above do; the lowest is condition-red clay, at 4.8:1.
- Chart axes and gridlines use neutral greys (`#8A8A8A` labels, `#262626` lines), never a
  family color.

### 6. Every number is real
A number on screen has to be one of three things:
- **An address.** A segment's number is its position, and the numbers add up down the tree.
  `5.2.1` is Systems › Climate › Upstairs. Typing 5, 2, 1 goes there, and the header shows
  the address.
- **A live value**, such as temperature, time, a count, or a big button's chip.
- **An ID** that a person will type or say somewhere else. The sensor log uses addresses as
  IDs, so every row points to a real place in the panel.

Decorative numbers are not allowed.

### 7. Condition is the whole frame
- The system has one condition at a time: **Normal**, **Caution** or **Alert**. All four
  Frame tints swap to the Caution or Alert family together.
- The header says the condition and its cause in words, for example
  `CONDITION RED · HAIL WARNING`.
- Only the system sets the condition. On entering Alert, the header blinks three times,
  except with `prefers-reduced-motion`.

### 8. Two typefaces, two jobs
- **Frame face:** condensed, all caps (the reference uses Saira Extra Condensed: its C and G sit flush with the other capitals, where Antonio's overshoot). Set it by capital height (`font-size-adjust: cap-height 0.75`). It is for labels, pill
  text, readout values and spec sheets. Keep labels to three words or fewer, at 13px or larger.
- **Content face:** a highly legible face in sentence case (the reference uses Atkinson
  Hyperlegible). It is for anything longer than a label, at 16px or larger.
- Never set a sentence in the frame face. A KEY = VALUE line is not a sentence.

### 9. Gaps are the grid
Use black gaps: 4px between frame segments and 8px between field items. Use no borders and no
shadows, except chart gridlines.

### 10. Motion is for state
Animate only when something changes: switching sections, opening a level, a condition change,
a hold sweep, or a new log row. Nothing moves when idle. Sound is optional and off by default.

### 11. Keyboard and text input
| Input | Effect |
|---|---|
| Digits | Type an address. The first digit picks the section, and later digits (within 1.2s) walk down the tree. |
| `Esc` | Go up one level, or home from the top |
| `←` `→` | Step through a focused chart one sample at a time |
| `Tab` | Move through the rail, then the header, then the field |

A text field is a black well over a Frame-colored bar, which turns Action-colored on focus.
Errors are Caution readouts under the field, in words.

### 12. Small screens: the PADD layout
This follows handheld PADDs, especially Lower Decks (see `research/REFERENCES.md`).
- **The sidebar stays a sidebar.** Under 700px wide, the rail narrows to 64px and keeps its
  numbered segments. There is no bottom tab bar; the show never has one.
- **The frame closes into a C.** The rail sweeps through a bottom-left elbow into a thin bottom
  bar that carries status, such as the condition. A bottom bar holds status, IDs or the title,
  never navigation.
- **The title sits flush right** on a thinner top bar (18px).
- **Nested menus go inside.** Only the current level is drawn, as one thin frame inside the
  field.
  - The levels above become thin bars above it, each thinner than the last, labeled with its
    address and name. Tap one to climb back out.
  - Items are rounded rows attached to the frame. Each row shows its number, its name and a
    live value in words ("67°F", "Locked", "3 rooms"), never a bare count.
- Secondary labels may hide. Primary labels may not.

### 13. The split frame: summary over detail
This comes from the TNG science stations. A frame's side rail breaks into two elbows with a
pair of thin bars running between them.

- **Above the bars:** what the thing is, as a spec sheet. A title like
  `POWER SCAN = LAST 24 H`, then `KEY = VALUE` lines.
- **Below the bars:** what it looks like, as a chart in a bracket.
- **Top segments** pick the subject (`4.1 Power`, `4.2 Outside`…).
- **Bottom segments** pick the range or the view (`24 H`, `12 H`, `6 H`).
- **The junction**, the striped block on the bars, is the cursor. It slides along the bars to
  show which moment you are reading. In the show it is decoration; here it is the scrubber.

### 14. Data and charts
- **One series per chart.** For two measures, use two charts or two subjects. Never use two
  y-axes.
- **Marks** use the Data family. The current or selected mark uses a lighter tint. Bars start
  at zero and have 4px rounded tops; lines are 2px.
- **The spec sheet is the tooltip.** Hovering, tapping or arrowing over a chart updates the
  `READING =` line and moves the junction. There is no floating box.
- **The bracket** frames the chart:
  - The ID goes top-left (`4.1-24H`), and the sample count goes bottom-right
    (`24 / 24 samples`).
  - The scale note goes bottom-left (`Scale = 1.5 kWh / line`).
  - A Data-colored scale bar with ticks stands in for the y-axis.
- **Scales** use whole steps, an even number of them, so the middle line lands on a step.
- **Parts of a whole** go in a composition list: one row per part, with a label, a percentage
  and a bar. "Other" is always last.
- **Meters** are segmented: 20 blocks of 5% each, and they turn Caution under a threshold.
- **The data layout** follows the show: a big title flush right, then a **band** of the actual
  sample values (the selected one in white), then KEY = VALUE lines, then the picture in a
  bracket, then **one status line**, bottom right, in words, about what the system is doing now
  ("Heating engaged · 2° to set point"). The band doubles as a readable table of the chart.
- **Polar plots** are for anything with a direction, such as wind: rings for magnitude, eight
  labeled spokes, the hours as a trail of dots, and an arrow to the selected reading.
- **Live logs** (the LCARS cascade, made real) show the newest row first, eight rows at most.
  Flags are Caution chips with a word.

### 15. Themes change tints, never roles
A theme replaces the tints inside each family. It can't move a role to a different family or
add a family.

The reference ships two themes:
- **Amber**, the default.
- **Science**, the pink-and-blue TNG lab look: Frame tints rose, peach, mauve and dusty rose;
  Action tints mint.

The Data, Caution and Alert families are the same in every theme, so charts and warnings read
the same everywhere.

### 16. Maps
When the things on a panel have a place, the panel shows a map, our version of a starship's
cutaway.
- **Floor plans:**
  - Plans are drawn in thin Frame-colored outline.
  - Unselectable spaces are dashed grey.
  - The selected room is filled with Frame 1, and its labels turn black.
- **Each room** shows its name and one live value.
- **Doors** sit on the walls: Data-colored when locked, Caution when open, and labeled in words
  when open.
- **Going into a room:** the plan shrinks to a **thumbnail** beside the room's detail, with the
  room still filled. Tapping the thumbnail goes back to the whole floor. This is the TNG
  schematic move: the overview stays as context.
- **Addresses:**
  - Floors are segments in the map frame (`8.1`, `8.2`…).
  - A room in the map shows its Systems address (`5.2.2.2`), so there is one address per
    thing.
- **Cameras** are Action-colored rings on the plan. Tapping one opens the pop-up (rule 17).

### 17. Open in place, go inside, or pop up
- **One level down opens in place.** A row opens a small box directly under it, joined by a
  connector line from the row's node. Only one row is open at a time.
- **Two or more levels down goes inside** (rule 3, and rule 12 on phones).
- **Only live video pops up.**
  - It floats over the content in a black box with four Action-colored corner brackets, a
    title (`CAM 3 · BACK YARD`) and a Close pill.
  - `Esc` closes it.
  - Menus, confirmations and forms never pop up. They live in the frame, and confirmations
    use hold-to-confirm (rule 4).

## Conformance checklist
- [ ] Every rail segment is a section, and every section has a segment.
- [ ] Nothing in a frame is content.
- [ ] Every nested frame is thinner than its parent, nesting stops at depth 3, and the current
  segment at each level is notched.
- [ ] Every clickable thing is a segment, pill or big button, and all of them are clickable.
- [ ] Every big button has a chip with a live number and a ring that matches its cost, and
  every gold or red ring requires a hold.
- [ ] Every color comes from its role's family, and color always has a shape or word beside it.
- [ ] Every number is an address, a live value or an ID, and typed addresses work.
- [ ] The condition shows across the whole frame family and in words in the header.
- [ ] Every chart has one series, a bracket with ID and count, a spec sheet reading, and
  arrow-key stepping.
- [ ] Every text pair has at least 4.5:1 contrast, and every touch target is at least 48px.
- [ ] The layout works at 375px wide.

## Open questions
- **Dense tables** beyond the eight-row log: is row striping in the Data family enough?
- Should a big button's **chip number** be allowed to be a hotkey instead of a count?
- **Name:** Elbow, Sweep or Bridge?

## Reference
- The demo is `demo/index.html`.
  - URL hash opens a place: `#systems/2/1/1` is the Bedroom and `#data/2` is the outdoor
    scan.
  - `?theme=science` switches the theme.
- Research notes and sources are in the BTF4E notebook post.
