MeridiansMeridians

Frontend Convention — UI naming, composition, and visual vocabulary

Source path: knowledge-base/knowledge/conventions/FRONTEND_CONVENTION.md

# Frontend Convention — UI naming, composition, and visual vocabulary

> How code names are *formed* in this codebase (companion to [LANGUAGE.md](../vocabulary/index.md), which defines the *domain* vocabulary). This is the present convention — match it when adding or renaming code.

## The formula: `Domain + Role`

Every component / surface name = a **domain** term (from LANGUAGE.md) + a **role** word for its physical place/shape. Reads as "what it's about" + "where it is". e.g. `ScenePanel`, `StageBar`, `ForceTimeline`, `DecisionView`.

### Role lexicon (precise)
| Role | Means | Notes |
|---|---|---|
| **Shell** | a **top-level page frame** that hosts a whole surface | one per surface: `WorkspaceShell` (domain workspace), `GatewayShell` (instance home + auth — a misnomer, see Platform naming), `ScenarioShell` (live game table), `PortalShell` (the meridians.global site). `MobileWorkspace` is the phone variant of `WorkspaceShell`. |
| **Rail** | thin **vertical** strip of items/icons | left story rail; inspector icon rail |
| **Bar** | thin **horizontal** strip of controls | top of a region (`StageBar`, `RunBar`, `FuturesBar`) |
| **Panel** | a **large rectangular region** of content | resizable / dockable; the inspector/sidebar bodies. When a concept has **both** (e.g. Readings): **View** = the stage surface, **Panel** = the inspector mirror of the same thing. |
| **View** | a **full center surface** the Stage swaps between | keep the `…View` suffix; `BoardView`, `ReadingsView`, `NetworkView`, `DecisionView`, … |
| **Stage** | the **center surface** that hosts one View at a time | switched by `StageBar` → `state.stageView` |
| **Timeline** | a horizontal **time-ordered** band | scenes, forces |
| **Modal** | overlay dialog | `BranchModal`, `ScenarioModal`, `ApiKeyModal` |
| **Popover** | small **anchored** overlay | |
| **Chart** | one visualization | |
| **Dashboard** | a full **analytics** surface | |
| **Detail** | inspector body for **one entity** | panel content (`CharacterDetail`, `SceneDetail`, …) |
| **Slide** | one deck slide | |
| **Palette** | floating action menu | `StagePalette` |

### Domain lexicon (draw names from LANGUAGE.md, not synonyms)
`Domain · Scene · Arc · Branch · Thread · Belief · Stance · Stream · Merge · Priors · Capture · Scenario · Explore · Readings · Variables · Phase Reasoning Graph / Phase Graph (PRG) · Causal Reasoning Graph / Causal Graph (CRG) · Decision · Mind (the stance/machinery cluster) · Signals (the Capture cluster) · Force (Fate/World/System) · Network · World · System · Board · Substrate · Perspective · Member · Agent · Invitation`.

Retired aliases — do **not** use as code names: `Driver` (→ Capture / Prior), `Queue` (→ Priors), `Series` (→ Domain), `Mode` (→ Phase, for the PRG), `Control` (→ Mind), `canvas` (→ Stage), `Playback` (→ Scenario), `Conviction` (the game → **Scenario**; `ConvictionShell`→`ScenarioShell`, `useConviction`→`useScenario`, etc.). **Exception:** the in-game *currency* you pay to commit stays **`conviction`** (`Seat.conviction`, `ConvictionEconomy`, `CONVICTION_*`, `evidenceFromConviction`) — you still "pay conviction" inside the Scenario game.

The load-bearing four are **`*Shell` · `*View` · `*Panel` · `*Detail`** — get those right and the rest
follow. This file owns the convention; agent briefs and skills should link here rather than restate it.

## Platform & gateway naming (shipped — see [GATEWAY.md](../operations/GATEWAY.md))

The platform layer adds terms that **collide with shipped ones** if we're not deliberate. These decisions
are settled and in code:

- **The `GatewayShell` trap → the meridians.global site is `PortalShell`, NOT `GatewayShell`.** The
  `GatewayShell` is the **instance home console** (spine + stage: Games / Domains / Extraction /
  Invitations) — a slight misnomer, but it's shipped and stays. The **hosted gateway** at
  `meridians.global` (landing · account · Admin console) is a *different* frame →
  **`PortalShell`** (`src/features/gateway/landing/PortalShell.tsx`; cosmos + glass). Never call the
  meridians.global site "GatewayShell".
- **"Director" spans two layers — keep the word, know the layer.** *Platform Director* = the Supabase
  account owner (owns products, pays). *In-instance `director`* = the top role in the shipped 5-tier
  access ladder (Guest→Viewer→Contributor→**Manager**→Director). They **coincide** (the owner *is* their
  instance's director) but are separate systems: platform Director lives in `directors` (Supabase);
  in-instance director lives in `instanceMembers` (the master's registry). Say **"platform account"** vs
  **"in-instance member / people"** when the layer matters.
- **"Admin" (platform) vs "Manager" (in-instance role) — no crossing.** **Admin** = the platform operator
  (`platform_admins` / the gateway `/admin`; us). **Manager** = the ladder role just below director (the
  director's deputy inside one instance), *renamed from "admin"* so the word is free for the platform.
  There is **no** in-instance "admin" and **no** platform "manager". The `MemberRole` literal is
  `'manager'` (`src/types/domain/access.ts`); the platform table is `platform_admins`.
- **Products: `License` and `Hosted`** (settled). *License* = the BYOK Electron app (cheap flat). *Hosted*
  = the managed Fly VM (pricier). Avoid "Managed" (ambiguous) and "tier"/"seat" (there are no tiers — one
  License plan; the tunnel is a BYOK ngrok key). A director's products are **per-subscription**
  (`hasLicense` / `hasHosted`), not a field — both can be true.
- **"Instance" = one running host.** The Electron app (License) or a Fly VM (Hosted). Its DB row is
  `instances` (Hosted only). Don't call it a "tenant" (control-plane word we don't use) or a "server".
- **"Domain" is triple-overloaded — disambiguate every time.** (1) *domain* = the product knowledge
  structure (LANGUAGE.md — the thing users build). (2) *domain name* = the DNS host an instance runs on
  (`<slug>.meridians.global` / a custom domain). (3) *`directors`/account domain* = platform data. In
  gateway code + copy, say **"domain name"** or **"hosting domain"** for DNS, never bare "domain".
- **Entitlement predicates: `hasLicense(director)` / `hasHosted(director)`** — read the `subscriptions`
  rows; never a `directors.product` column (it doesn't exist).
- **Suffixes carry over:** the gateway's frames are `PortalShell` + `*View` / `*Panel` / `*Modal` as
  usual (e.g. `FleetConsole` is a Dashboard; `SubdomainClaim` / `CustomDomainSetup` are onboarding
  panels; `LicenseStatusChip` a chip). Same lexicon, new surface.

## What's consistent (leave alone)

- **Casing:** PascalCase components, kebab-case lib, domain-foldered prompts.
- **File families:** `…-graph.ts` (`world` / `system` / `network` / `phase-graph`); `…-export.ts`; the `futures-engine` / `-state` / `-remap` trio; the `game-theory*` family; `*Slide` / `*Modal` / `*Chart` / `*Detail` suffixes.
- **`stageView` literals track their UI labels.** The union in [`src/types/domain/view-state.ts`](../../../src/types/domain/view-state.ts) is the source of truth, and most members now read as the user sees them (`'opinion'`, `'notes'`, `'streams'`, `'cycles'`, `'projections'`, `'runs'`, `'operations'`) so code and screen use one word. Two kinds of exception, both deliberate:
  - **Legacy literals kept for persisted state** — `'mode'` is labelled **Phase** / **Phase Graph** in the interface, and `'present'` labels as **Present**. Renaming them would strip stored `viewState`, so they stay; only the *label* moved.
  - **Retired deep-link values are mapped at the boundary, never re-persisted** — `useDomainDeepLinks` translates old URLs (`?view=sources`→`cycles`, `schedule`→`operations`, `loop`/`stories`→`inbox`) so old links keep working without the retired literal re-entering state.

  Adding a mode: name the literal after its label, and if you rename an existing one, add the old value to the deep-link mapping rather than leaving a dead URL.
- **Pentad archetypes** are 32 Domain work forms over exact formed/latent force profiles. Use the canonical
  names from `pentad-archetype.ts`; do not concatenate force labels or reuse Scene Sign names. The same
  five-bit coordinate has two altitudes: a Sign names Scene movement, an archetype names whole-work
  composition. A counterform reverses all five bits and is a semantic contrast, never a quality verdict.
Open on GitHub

Raw Markdown source