Locks the geometry, schemas, and architecture decisions from the brainstorming session: redesigned lcars_panel (elbow/strip frames), new lcars_rail compound block, new lcars_readout tile, plus bundled font-token + palette + outer-frame refinements. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
13 KiB
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_panelrenders 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 inthemes/CLAUDE.md(must go throughvar(--font-heading|body|mono, <fallback>)).
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-radiuson 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 <div> 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)
.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:
- Font tokens. All
font-family: 'Antonio', sans-serifinassets/style.cssrewritten asfont-family: var(--font-heading, 'Antonio', sans-serif). No bundled fonts change.fonts.jsonshipped as[](per CLAUDE.md guidance) and a newRECOMMENDED_FONTS.mdinstructs admins to assign Antonio (or similar geometric sans) to--font-heading. - Palette expansion in
presets.json. Add the canonical LCARS roles beyondprimary/secondary/accent: the file gains entries forpeach,mauve,gold,blue,redso panel accent-colour pickers map to actual theme tokens rather than the generic three. Existing token names stay; the new ones are additive. - 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
stardateandidentifierslots on the existinglcars_headerblock (already partly there — just makes the chrome richer). - Sidebar identifier codes.
lcars_sidebaritems gain an optionalcodefield (e.g.SCI 01) rendered as small right-aligned text inside each button, so the sidebar visually rhymes with the new rail block. required_icon_packsdeclaration.plugin.modgetsrequired_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.RECOMMENDED_ICONS.mdadded with one line (Lucide) for parity withRECOMMENDED_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_packsbut no panel uses an icon yet. - Touch / mobile rail-collapse behaviour — existing responsive rules in
style.cssalready 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
gothamor 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 rebuildagainst a dev CMS instance — visually confirm anlcars_panelwithframe="elbow", anlcars_railwith threelcars_readoutchildren, and the existing page chrome all read as one consistent LCARS frame.
Decisions made during brainstorming (for reference)
- Inner L bend is sharp, outer page corners are rounded. Confirmed against the user's
Lcars_wallpaper.svg.pngreference and an explicit AskUserQuestion choice ("Curve at the page corner (outside the panel)"). - Top bar 4rem, bottom bar 3rem, asymmetric. Authentic LCARS chrome is top-heavy.
- 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.
lcars_railis a separate block, not a panel variant. It exists as its own block (not just anlcars_panelmode) 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.lcars_railis a compound schema-driven block, not aHasInternalSlotcontainer. Investigated during spec self-review: pongo blocks have no per-child wrapper hook inRenderSlot, 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. Inlinecells[]in the schema sidesteps this. (themes/CLAUDE.mdlists anAllowedChildren []stringfield on BlockMeta which does not exist incore/blocks/types.go— the actual field isHasInternalSlot bool; CLAUDE.md should be corrected separately.)lcars_readoutis a separate block, not alcars_panelmode. Readouts are meant to be tiled; panels are meant to stand alone. Conflating them would have forced one of those uses to dominate.- Font tokens, not bundled font, going forward. Aligns with the project-wide rule introduced in
themes/CLAUDE.mdduring this session.