diff --git a/.gitignore b/.gitignore index 140f8cf..7454bdb 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.so +.superpowers/ diff --git a/docs/superpowers/specs/2026-06-06-lcars-card-redesign-design.md b/docs/superpowers/specs/2026-06-06-lcars-card-redesign-design.md new file mode 100644 index 0000000..48686ac --- /dev/null +++ b/docs/superpowers/specs/2026-06-06-lcars-card-redesign-design.md @@ -0,0 +1,175 @@ +# LCARS Card Redesign + +**Date:** 2026-06-06 +**Scope:** `~/src/blockninja/themes/lcars` theme plugin +**Status:** Design + +## Problem + +The current LCARS theme has the outer page frame (elbows, sidebar, header) reading correctly, but its **in-content blocks are generic**: + +- `lcars_panel` renders as a thin coloured left-border with a small pill and title bar — visually closer to a Bootstrap callout than to LCARS. +- There is no way for adjacent panels to **share frame chrome** (the defining "L wrapping multiple readouts" pattern of canonical LCARS). +- There is no small-tile equivalent for dashboard-style numeric readouts. +- Existing CSS hardcodes `font-family: 'Antonio', sans-serif`, which now violates the project-wide font-token rule introduced in `themes/CLAUDE.md` (must go through `var(--font-heading|body|mono, )`). + +This spec redesigns the panel system so cards read as LCARS and so that grouped cards visually share a single L-frame. + +## Visual outcome + +The brainstorming session locked the following geometry (mockups in `.superpowers/brainstorm/3782075-1780722894/content/08-wider-bigger-curve.html`): + +- **Outer page corners** of every framed block are rounded; the **inner L bend** stays a sharp 90° (the corner block has `border-top-left-radius` on its outer corner only). +- **Column 1** (rail / corner) is **5.5rem** wide. +- **Top bar / top corner row** is **4rem** tall; **bottom bar / bottom corner row** is **3rem** tall — deliberate asymmetric chrome weight, top heavier than bottom. +- **Outer top corners** use **1.75rem** radius; **outer bottom corners** use **1.25rem** radius. +- **Grid gap** between cells is **4px**, producing the slim dark separators between LCARS colour segments. +- Identifier text in the corner blocks uses `padding: 0 1.25rem 0.85rem 1rem` (top, right, bottom, left) so it sits clear of all four edges. + +## Three block additions / changes + +### 1. `lcars_panel` — redesigned (existing key) + +A single self-contained framed panel. **Replaces the current thin-left-border implementation.** + +**Schema (`schemas/lcars_panel.schema.json`):** + +| Property | Type | Default | Notes | +|---|---|---|---| +| `frame` | enum `"elbow" \| "strip"` | `"elbow"` | Renders mode A or mode B from the brainstorm | +| `title` | text | `""` | Goes in the top bar | +| `top_label` | text | `""` | Corner-block identifier (e.g. `RM-47-A`); placeholder shows an LCARSy default | +| `bottom_label` | text | `""` | Bottom corner identifier (e.g. `28-301`) | +| `top_meta` | text | `""` | Right-aligned bar-top text (e.g. `STARDATE 47634.4`) | +| `bottom_meta` | text | `""` | Right-aligned bar-bot text | +| `status` | enum `online \| standby \| alert \| offline` | none | Optional status dot in bar-top; reuses existing `.lcars-status-*` classes | +| `accent_color` | enum `primary \| secondary \| accent \| muted` | `primary` | Picks which theme token drives the top L colour | +| `bottom_accent_color` | enum (same) | `secondary` | Bottom L colour — defaults differ from top so the panel reads as two-tone | +| `content` | richtext | `""` | Panel body | + +**Render targets:** + +- `frame="elbow"`: 2-column / 3-row CSS grid as locked in the mockup (see § Geometry). +- `frame="strip"`: a single segmented colour bar header + bordered body. No corner blocks, no rail; the colour band carries the labels. + +### 2. `lcars_rail` — new compound block + +Multiple sibling content cells share a single L-frame. The defining "how cards interact with each other" answer. + +**Architecture choice:** the rail is **not** a true container (`HasInternalSlot: true`). The CMS's `RenderSlot` pipeline returns all children as one concatenated HTML blob with no per-child wrappers, which makes it impossible to pair each child with its own rail segment in a single CSS grid. Instead, the rail is a compound block whose cells are defined inline in its own schema — each cell has the rail-segment colour/label AND the cell content together. This keeps v1 implementable and avoids inventing a per-child-wrapper extension to the SDK. A future iteration can add a true container variant if needed. + +**Schema (`schemas/lcars_rail.schema.json`):** + +| Property | Type | Default | Notes | +|---|---|---|---| +| `title` | text | `""` | Top-bar title | +| `top_label` / `bottom_label` | text | `""` | Same role as on `lcars_panel` | +| `top_accent_color` / `bottom_accent_color` | enum | `primary` / `primary` | Colour the top and bottom corner blocks | +| `cells` | array of `{label, color, title, content}` | required, min 1 | Each entry renders one rail segment + one paired body cell. `color` enum maps to theme tokens; default rotates `mauve → gold → blue → mauve…` when user leaves it blank. `content` is richtext. | +| `rail_side` | enum `left \| right` | `left` | Flips the L for right-handed layouts (schema field reserved; CSS implements `"left"` only) | + +### 3. `lcars_readout` — new tile block + +A small dashboard tile for numeric/status displays. Designed for grids of mini-readouts. + +**Schema (`schemas/lcars_readout.schema.json`):** + +| Property | Type | Default | Notes | +|---|---|---|---| +| `label` | text | required | Small uppercase label above the value | +| `value` | text | required | Big numeric/text value | +| `unit` | text | `""` | Smaller unit suffix (`%`, `LY`, etc.) | +| `accent_color` | enum | `primary` | Colours the top border and the value text | +| `pulse` | bool | `false` | If true, the value gets the existing `lcars-pulse` animation | + +**Render target:** a `
` with a 4px coloured top border, `padding: 0.5rem 0.75rem`, label in muted small caps, big value, optional unit. Multiple readouts work in any standard grid (CSS grid, columns block, etc.) the user already has. + +## Geometry (locked) + +```css +.lcars-elbow { + display: grid; + grid-template-columns: 5.5rem 1fr; + grid-template-rows: 4rem 1fr 3rem; + gap: 4px; + background: hsl(var(--background)); +} +.lcars-elbow__tl { grid-area: 1/1; border-top-left-radius: 1.75rem; } +.lcars-elbow__bar-t { grid-area: 1/2; border-top-right-radius: 1.75rem; } +.lcars-elbow__rail { grid-area: 2/1; } +.lcars-elbow__body { grid-area: 2/2; background: hsl(var(--background)); } +.lcars-elbow__bl { grid-area: 3/1; border-bottom-left-radius: 1.25rem; } +.lcars-elbow__bar-b { grid-area: 3/2; border-bottom-right-radius: 1.25rem; } + +/* Identifier text inside the corner blocks */ +.lcars-elbow__tl, .lcars-elbow__bl { + display: flex; justify-content: flex-end; + font: 800 0.75rem/1 var(--font-heading, 'Antonio', sans-serif); + letter-spacing: 0.08em; + color: hsl(var(--background)); +} +.lcars-elbow__tl { align-items: flex-end; padding: 0 1.25rem 0.85rem 1rem; } +.lcars-elbow__bl { align-items: flex-start; padding: 0.75rem 1.25rem 0 1rem; } +``` + +The rail block reuses the same column/gap rules but the row template becomes `4rem repeat(N, var(--lcars-rail-row, 3.5rem)) 3rem`, with one paired (rail-segment, body-cell) per `cells[]` entry. Segment and cell are placed in the same row via explicit `grid-row` in the pongo `{% for %}` loop — no reliance on auto-flow. + +## Refinements bundled in this spec + +These are small but in-scope because the changes touch the same files: + +1. **Font tokens.** All `font-family: 'Antonio', sans-serif` in `assets/style.css` rewritten as `font-family: var(--font-heading, 'Antonio', sans-serif)`. No bundled fonts change. `fonts.json` shipped as `[]` (per CLAUDE.md guidance) and a new `RECOMMENDED_FONTS.md` instructs admins to assign Antonio (or similar geometric sans) to `--font-heading`. +2. **Palette expansion in `presets.json`.** Add the canonical LCARS roles beyond `primary/secondary/accent`: the file gains entries for `peach`, `mauve`, `gold`, `blue`, `red` so panel accent-colour pickers map to actual theme tokens rather than the generic three. Existing token names stay; the new ones are additive. +3. **Outer page-frame refinement.** The existing page-template elbow widths and radii are re-tuned to the same 5.5rem column / 1.75rem outer radius vocabulary so the page outer chrome and the in-content panel chrome share visual language. Adds optional `stardate` and `identifier` slots on the existing `lcars_header` block (already partly there — just makes the chrome richer). +4. **Sidebar identifier codes.** `lcars_sidebar` items gain an optional `code` field (e.g. `SCI 01`) rendered as small right-aligned text inside each button, so the sidebar visually rhymes with the new rail block. +5. **`required_icon_packs` declaration.** `plugin.mod` gets `required_icon_packs = ["lucide"]` (forward-declared per CLAUDE.md). No icons are required for the new blocks today; this is staged for future status-indicator iconography. +6. **`RECOMMENDED_ICONS.md`** added with one line (Lucide) for parity with `RECOMMENDED_FONTS.md`. + +## Out of scope + +- `rail_side: "right"` rendering — schema field accepted, CSS only implements `"left"`. +- SVG-based frames or arbitrarily large inner-bend curves — the locked design uses CSS border-radius and a sharp inner bend. +- Icon rendering inside panels — staged via `required_icon_packs` but no panel uses an icon yet. +- Touch / mobile rail-collapse behaviour — existing responsive rules in `style.css` already collapse the page sidebar at 768px; the new blocks inherit the same breakpoint via a single block of rules at the bottom of the stylesheet. +- Changes to `gotham` or any other theme. + +## File-by-file impact + +| File | Change | +|---|---| +| `blocks.go` | Add `LCARSRailMeta`, `LCARSReadoutMeta`. Update `LCARSPanelMeta` description. New `headerDefaults` / `panelDefaults` updated. | +| `register.go` | Register the two new blocks. Update `lcars_panel` registration to use the new template (`blocks/panel.html` rewritten). Schema load remains in place. | +| `schemas/lcars_panel.schema.json` | Rewritten per § 1 above. | +| `schemas/lcars_rail.schema.json` | **NEW.** | +| `schemas/lcars_readout.schema.json` | **NEW.** | +| `schemas/lcars_sidebar.schema.json` | Add optional `code` field on each item. | +| `templates/blocks/panel.html` | Rewritten to render `frame="elbow"` and `frame="strip"` per the locked geometry. | +| `templates/blocks/rail.html` | **NEW.** | +| `templates/blocks/readout.html` | **NEW.** | +| `templates/blocks/sidebar.html` | Render the new optional `code` field. | +| `templates/blocks/header.html` | Minor refinement so it shares the new 5.5rem / 1.75rem vocabulary with panel chrome. | +| `templates/default.html` | Re-tune outer-frame widths/radii to match. | +| `assets/style.css` | Replace `'Antonio', sans-serif` with `var(--font-heading, 'Antonio', sans-serif)` throughout. Add `.lcars-elbow*`, `.lcars-strip*`, `.lcars-rail*`, `.lcars-readout*` rule blocks. Re-tune `.lcars-elbow-*` outer-frame rules to new dimensions. Delete the old `.lcars-panel*` rules. | +| `presets.json` | Add `peach / mauve / gold / blue / red` colour roles to each preset; keep `primary / secondary / accent / background / foreground` etc. as the canonical tokens. | +| `plugin.mod` | Add `required_icon_packs = ["lucide"]`. Bump `version` via `make bump-minor` after implementation (this is a feature). | +| `fonts.json` | Set to `[]`. (Currently lists bundled woff2s; per new CLAUDE.md rule fonts should be admin-assigned.) | +| `RECOMMENDED_FONTS.md` | **NEW.** Antonio for heading; system sans for body; system mono for mono. | +| `RECOMMENDED_ICONS.md` | **NEW.** Lucide pack. | + +## Validation + +Before commit: + +- `make` — compiles the `.so`. +- `cd ~/src/blockninja/check-safety && go run . ~/src/blockninja/themes/lcars` — passes the import-boundary and schema-vs-block-key checks. +- `make rebuild` against a dev CMS instance — visually confirm an `lcars_panel` with `frame="elbow"`, an `lcars_rail` with three `lcars_readout` children, and the existing page chrome all read as one consistent LCARS frame. + +## Decisions made during brainstorming (for reference) + +1. **Inner L bend is sharp, outer page corners are rounded.** Confirmed against the user's `Lcars_wallpaper.svg.png` reference and an explicit AskUserQuestion choice ("Curve at the page corner (outside the panel)"). +2. **Top bar 4rem, bottom bar 3rem, asymmetric.** Authentic LCARS chrome is top-heavy. +3. **Curve ratio matches v1 mockups, not v6.** 1.75rem top / 1.25rem bottom on a 5.5rem column 1 — visible but not dominating the corner block. +4. **`lcars_rail` is a separate block, not a panel variant.** It exists as its own block (not just an `lcars_panel` mode) because it has fundamentally different content shape: a list of paired (segment, body-cell) entries, vs. the panel's single body. One mode-switch would have over-loaded the schema with mutually-exclusive fields. +5. **`lcars_rail` is a compound schema-driven block, not a `HasInternalSlot` container.** Investigated during spec self-review: pongo blocks have no per-child wrapper hook in `RenderSlot`, so children of a true container come back as one concatenated HTML blob — incompatible with the goal of pairing each child with its own rail segment in a single CSS grid. Inline `cells[]` in the schema sidesteps this. (`themes/CLAUDE.md` lists an `AllowedChildren []string` field on BlockMeta which does not exist in `core/blocks/types.go` — the actual field is `HasInternalSlot bool`; CLAUDE.md should be corrected separately.) +6. **`lcars_readout` is a separate block, not a `lcars_panel` mode.** Readouts are meant to be tiled; panels are meant to stand alone. Conflating them would have forced one of those uses to dominate. +7. **Font tokens, not bundled font, going forward.** Aligns with the project-wide rule introduced in `themes/CLAUDE.md` during this session.