Skip to content

M2 Cycle 8 — UI Scale Pass, Bible Live Filter & Neon Restyle

M2 Cycle 8 — UI Scale Pass, Bible Live Filter & Neon Restyle

Section titled “M2 Cycle 8 — UI Scale Pass, Bible Live Filter & Neon Restyle”

Date: 2026-06-23
Status: Approved

Three tightly related improvements to make the game easier to read mid-play and the design bible feel like part of the same project.

  1. Game UI scale pass — bump HUD and weapon-panel constants so elements are readable at a glance
  2. Bible live filter — mark in-game content with live: true in seed data; default the bible to show only live items
  3. Bible neon restyle — Orbitron + JetBrains Mono, neon glow accents matching the game aesthetic

Files modified: ui/hud.gd, ui/weapon_panel.gd
No logic changes, no new tests. All changes are constant and font-size values.

Element Current New
HP bar backing size 240×40px 340×52px
BAR_W (usable fill) 234px 334px
BAR_H (fill height) 32px 40px
HP label font 15px 18px
HP fill inset position (19,16) (21,18) — 3px inset maintained
Level label font 18px Orbitron 22px
Level label x-offset 264px 348px (right of new 340px bar)
Level label size 80×40px 100×52px
Timer font 28px Orbitron 38px
Timer label width 160px 220px
Timer label x vp.x/2 - 80 vp.x/2 - 110
Kills font 18px 22px
Kills label width 144px 180px
Kills label x vp.x - 160 vp.x - 196
Element Current New
SLOT_W 120px 160px
SLOT_H 70px 90px
SLOT_GAP 12px 16px
Name label font 13px 16px
Name label size (SLOT_W-16)×20 (SLOT_W-16)×24
Stat label font 12px 15px
Stat label y-offset 28px 34px
Stat label size (SLOT_W-44)×18 (SLOT_W-50)×20
Arc radius 24.0 32.0
Arc position x x + SLOT_W - 30 x + SLOT_W - 38
Arc position y y + SLOT_H/2 y + SLOT_H/2 (unchanged)
Arc line width 3.0 4.0

Files modified: tools/design-bible/src/seed.js, tools/design-bible/src/app.js, tools/design-bible/src/views/list.js

Live items — add live: true to these 13 entries in seed.js

Section titled “Live items — add live: true to these 13 entries in seed.js”

Weapons (2): pulse, nova

Enemies (1): swarmer

Mods/upgrades (8): damage, fire-rate, move-speed, pickup, max-hp (stat mods), overcharge, catalyst, lingering (transformative mods)

Elements (2): fire, lightning

Reactions (1): fire-lightning (id: fire-lightning, the Plasma reaction)

All other entries have no live field (falsy = not yet in-game).

// Before:
weapon('pulse', 'Pulse', 'projectile', 'lightning', 1.0, 0.6, { tags: ['projectile','homing'] }),
// After:
weapon('pulse', 'Pulse', 'projectile', 'lightning', 1.0, 0.6, { tags: ['projectile','homing'], live: true }),

The weapon() / mod() / enemy() / el() / rx() helper functions all use ...extra or spread — the live field passes through automatically to the output object.

For elements (using the el() helper that doesn’t take an extra spread), add live after construction:

const elements = [
Object.assign(el('fire', 'Fire', '#ff6a4d', 'burn'), { live: true }),
el('cold', 'Cold', '#6cc8ff', 'chill'),
Object.assign(el('lightning', 'Lightning', '#ffe34d', 'shock', 0.15), { live: true }),
// ... rest unchanged
];

For reactions (using rx() which also has no spread), same Object.assign pattern on the fire-lightning entry.

  1. Add liveOnly: true to the initial ui state object:
const ui = { activeKey: 'elements', activeId: null, query: '', viewMode: 'detail', liveOnly: true };
  1. Update filtered() to respect the flag:
function filtered() {
const entries = model.list(ui.activeKey);
return ui.liveOnly ? entries.filter(e => e.live) : entries;
}
  1. Add a live-toggle button in renderActions() (alongside existing Export/Import buttons):
const liveBtn = mk(ui.liveOnly ? '◈ Live' : '◇ All', () => { ui.liveOnly = !ui.liveOnly; render(); });
liveBtn.title = ui.liveOnly ? 'Showing live items only — click to show all' : 'Showing all items — click to show live only';
liveBtn.dataset.live = ui.liveOnly ? 'true' : 'false';
actionsEl.prepend(liveBtn);

The CSS [data-live="true"] selector will give the active state a gold glow (see Section 3).

In the list-item rendering loop, add a badge after the name when e.live:

const label = document.createElement('span');
label.textContent = (e.live ? '' : '') + (e.name ?? e.id);
label.style.cursor = 'pointer';
if (e.live) label.classList.add('live-label');

When liveOnly is true, the badge is redundant (everything shown is live), but it’s still visible and reinforces the state. When showing all, it cleanly marks which items exist in-game.

The live filter is a UI-only state. The existing “Export JSON” action writes the full model.bible (all entries) to bible.json regardless of the filter. The live fields will be present in the exported JSON — Godot’s ContentLoader.validate ignores unknown fields, so no loader changes are needed now. If needed in a future cycle, the Godot side can read live to show in-game indicators.


Files modified: tools/design-bible/index.html, tools/design-bible/css/style.css

In <head>, before the stylesheet link:

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@700&family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet">

Body font:

body { font: 14px/1.5 'JetBrains Mono', monospace; }

Topbar:

#topbar {
background: linear-gradient(180deg, #14141f 0%, #0a0a12 100%);
border-bottom: 1px solid #1e1e38;
}
.brand {
font-family: 'Orbitron', sans-serif;
font-size: 15px;
letter-spacing: 0.08em;
color: var(--accent);
text-shadow: 0 0 12px rgba(108,200,255,0.5);
}

Nav and list items — active state:

.navitem.active, .listitem.active {
background: var(--panel);
color: var(--accent);
border-left: 2px solid var(--accent);
padding-left: 6px; /* compensate so text doesn't shift */
box-shadow: inset 0 0 10px rgba(108,200,255,0.06);
}

Buttons:

button {
font-family: 'JetBrains Mono', monospace;
font-size: 12px;
transition: border-color 0.15s, box-shadow 0.15s;
}
button:hover {
border-color: var(--accent);
box-shadow: 0 0 6px rgba(108,200,255,0.3);
}

Live toggle button active state:

button[data-live="true"] {
border-color: rgba(255,227,77,0.6);
color: #ffe34d;
box-shadow: 0 0 8px rgba(255,227,77,0.35);
}

Inputs/selects:

input:focus, select:focus {
outline: none;
border-color: var(--accent);
box-shadow: 0 0 5px rgba(108,200,255,0.25);
}

Live label in list:

.live-label { color: #ffe34d; }

Metrics panel:

.metrics { border: 1px solid #1e1e38; }
.metrics .m b { text-shadow: 0 0 6px rgba(108,200,255,0.4); }

Reaction matrix hover:

.cell:hover { background: rgba(108,200,255,0.08); color: var(--accent); }

No layout changes — the 3-column grid, topbar structure, and panel dimensions are unchanged.


  • Game HUD/panel: boot smoke only (godot --headless --path . --quit-after 300) — render-only changes, no new GUT tests needed. Existing 150 tests must still pass.
  • Bible filter: cd tools/design-bible && node --test — existing 31 node tests must still pass. No new tests needed (the filter is UI state in app.js, not in the pure-logic core).
  • Bible restyle: visual verification in browser only — cd tools/design-bible && python3 -m http.server 8080.

File Change type
ui/hud.gd Constants/sizes only
ui/weapon_panel.gd Constants/sizes only
tools/design-bible/src/seed.js Add live: true to 13 entries
tools/design-bible/src/app.js liveOnly state + filtered() + toggle button
tools/design-bible/src/views/list.js badge on live items
tools/design-bible/index.html Google Fonts link
tools/design-bible/css/style.css Neon typography + glow styles