docs(specs): add M3-a shared Theme design
First of three M3 cycles (Theme -> shell 2a -> Title). Palette.gd consts as the single source of truth; game_theme.tres for flat-bordered UI; shaders for the three signature surfaces. EB Garamond replaces proprietary Georgia. Proof is a human-eyeballed showcase harness. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
196
docs/superpowers/specs/2026-07-10-shared-theme-design.md
Normal file
196
docs/superpowers/specs/2026-07-10-shared-theme-design.md
Normal file
@@ -0,0 +1,196 @@
|
||||
# Shared Theme — design
|
||||
|
||||
**Milestone:** M3 (Visual foundation & shell), item **M3-a** — the first of three
|
||||
cycles (Theme → Main Window shell 2a → Title). See `docs/roadmap.md` and
|
||||
`CLAUDE.md` §16.
|
||||
|
||||
**§2 side:** n/a — pure presentation. Owns no game state, makes no AI/network
|
||||
call. It is the visual system every later screen inherits.
|
||||
|
||||
**Depends on:** nothing (foundation). **Unblocks:** every M3+ screen.
|
||||
|
||||
---
|
||||
|
||||
## 1. Goal
|
||||
|
||||
Deliver the one shared visual system the mockups describe (`/mockups/README.md`,
|
||||
"Global Design System") as reusable Godot resources, so every screen built after
|
||||
this pulls the same palette, fonts, styleboxes, and signature surfaces — "the
|
||||
look is one system." Nothing renders a real screen yet; this is the toolbox the
|
||||
screens are built from.
|
||||
|
||||
Because a Theme has no behavior, its **proof is a showcase harness** a human
|
||||
eyeballs in the editor — the same "live-proven / human-confirmed" bar M0–M2 held.
|
||||
|
||||
## 2. The core architectural split
|
||||
|
||||
A Godot `StyleBox` **cannot run a shader** — the engine draws styleboxes itself.
|
||||
So the deliverable splits in two, each half using the right tool:
|
||||
|
||||
| Concern | Mechanism | Covers |
|
||||
|---|---|---|
|
||||
| **Crisp bordered UI** | `game_theme.tres` (StyleBoxFlat + fonts) | buttons/CTA, tabs active/inactive, chips/tags, item tiles, cards, insets, dark panels; all three font families at their sizes |
|
||||
| **The 3 signature surfaces** | `ShaderMaterial` on `Panel`/`ColorRect`, wrapped as drop-in mini-scenes | parchment sheet, dark diagonal-stripe bay, radial vignette overlay |
|
||||
| **Code-driven color** | `palette.gd` consts | rarity bars, HP/MP/stamina gradients, disposition/turn-order colour coding |
|
||||
|
||||
The "procedural shaders" decision applies to the **three big backdrops only**. A
|
||||
chip or button is flat-bordered and fits `StyleBoxFlat` natively; a shader on
|
||||
every chip would be wasted work.
|
||||
|
||||
`palette.gd` is the **single source of truth** for every hex value. The
|
||||
`game_theme.tres` styleboxes are authored to those same values; the shaders take
|
||||
them as uniforms. **One palette, three consumers.**
|
||||
|
||||
## 3. Fonts (redistribution constraint)
|
||||
|
||||
Godot exports must **bundle** their fonts. The mock's three families become:
|
||||
|
||||
| Mock role | Bundled face | Licence | Notes |
|
||||
|---|---|---|---|
|
||||
| Georgia (prose / headings / logo) | **EB Garamond** (Regular, Italic, Bold) | OFL | Redistributable. Georgia is Microsoft-proprietary and **cannot** ship. EB Garamond is a warm old-style serif — the "aged tome" DM-prose voice. Its strong italic carries the narrative-body *italic* the mock uses. |
|
||||
| Architects Daughter (hand-inked accent) | **Architects Daughter** (Regular) | OFL | Ships as-is; the mock already used the Google face. Nameplates, big stat numbers, DM-header labels. |
|
||||
| Monospace (UI chrome) | **JetBrains Mono** (Regular) | OFL | Default pick (clean, wide, reads well UPPERCASE with letter-spacing); swappable for another OFL mono without design change. Labels, keybinds, numeric readouts, tags. |
|
||||
|
||||
## 4. File layout
|
||||
|
||||
```
|
||||
client/
|
||||
scripts/theme/
|
||||
palette.gd # const Color tokens — the source of truth
|
||||
theme_keys.gd # const StringNames for type-variations, so scripts never
|
||||
# hardcode Theme string keys
|
||||
assets/theme/
|
||||
fonts/
|
||||
EBGaramond-Regular.ttf EBGaramond-Italic.ttf EBGaramond-Bold.ttf
|
||||
ArchitectsDaughter-Regular.ttf
|
||||
JetBrainsMono-Regular.ttf
|
||||
shaders/
|
||||
parchment.gdshader # vertical gradient #ece2ca -> #e4d8bd
|
||||
dark_bay.gdshader # 135deg repeating stripe #26221c/#221e18 + radial vignette
|
||||
vignette.gdshader # standalone dim/blur-edge overlay (pause, dialogue)
|
||||
game_theme.tres # the Theme resource — fonts, sizes, StyleBoxFlat variations
|
||||
scenes/theme/
|
||||
surfaces/
|
||||
ParchmentPanel.tscn # Panel + parchment material (drop-in backdrop)
|
||||
DarkBay.tscn # Panel + dark_bay material
|
||||
VignetteOverlay.tscn # ColorRect + vignette material
|
||||
theme_showcase.tscn # the proof: every token, stylebox, font, surface, one screen
|
||||
theme_showcase.gd
|
||||
tests/unit/
|
||||
test_palette.gd
|
||||
test_theme_resource.gd
|
||||
```
|
||||
|
||||
Follows existing conventions: `scripts/<domain>/`, harness scenes under
|
||||
`scenes/`, `tests/unit/test_*.gd`.
|
||||
|
||||
## 5. Contents
|
||||
|
||||
### 5.1 `palette.gd`
|
||||
|
||||
Every hex from the README's "Color tokens" section as `const Color`, grouped by
|
||||
role:
|
||||
|
||||
- **Dark backgrounds** — near-black stage, bay stripe pair, translucent panels,
|
||||
the dark-border ladder (`#6b6152`…`#1a1512`).
|
||||
- **Parchment** — sheet gradient stops, card fills + insets, parchment borders.
|
||||
- **Ink** — heading / body / muted / label / gold-accent inks.
|
||||
- **Text-on-dark** — bone/cream primaries, secondaries, muted monos.
|
||||
- **Accents** — blood-red set, gold/brass set, steel blue-teal set, green set.
|
||||
- **Stat bars** — HP / MP / stamina gradient endpoints + track/border.
|
||||
- **Rarity** — the five rarity colours as an ordered array.
|
||||
|
||||
Plus the small helpers the code-driven cases need:
|
||||
|
||||
- `rarity_color(rarity) -> Color`
|
||||
- stat-bar gradient stops for HP / MP / stamina (returned as a pair, or two
|
||||
named consts each).
|
||||
|
||||
This is the file the whole game reads from for any colour a Theme can't express.
|
||||
|
||||
### 5.2 `theme_keys.gd`
|
||||
|
||||
`const StringName`s for every type-variation name (`PrimaryCTA`, `Tab`,
|
||||
`TabActive`, `Chip`, `ItemTile`, `ParchmentCard`, `DarkPanel`, …), so scripts
|
||||
that set `theme_type_variation` reference a constant, never a raw string that can
|
||||
typo silently.
|
||||
|
||||
### 5.3 `game_theme.tres`
|
||||
|
||||
The six reusable pieces from the README, each authored as a Godot **type
|
||||
variation** (applied via `theme_type_variation` on any Control):
|
||||
|
||||
- **PrimaryCTA** — blood-red CTA button (gradient-look `StyleBoxFlat`, `#8f3a34`
|
||||
border, cream text, brightened hover).
|
||||
- **Tab** / **TabActive** — inactive (parchment `#efe6cf` + `#cbb684` border,
|
||||
muted text) vs active (solid `#8f3a34`, cream text).
|
||||
- **Chip** — mono-uppercase tag base; the *semantic* colour (red/gold/grey) is
|
||||
set by the consuming script from `palette.gd`, since it varies per use.
|
||||
- **ItemTile** — rounded square, 2px border (rarity colour applied by script),
|
||||
plus a recessed dashed **empty** variant.
|
||||
- **ParchmentCard** — `#f1e8d2` fill, `#bfa878` border, and a darker inset
|
||||
variant.
|
||||
- **DarkPanel** — translucent `rgba(18,15,11,.7)`.
|
||||
|
||||
Plus the three font families wired into the Theme's default / heading / mono
|
||||
font roles at the README's sizes and letter-spacing.
|
||||
|
||||
### 5.4 The three surface scenes
|
||||
|
||||
Each wraps one shader as a **one-line drop-in**: later screens instance
|
||||
`DarkBay.tscn` / `ParchmentPanel.tscn` / `VignetteOverlay.tscn` instead of
|
||||
rebuilding a backdrop. Shader uniforms default to the `palette.gd` values (baked
|
||||
into the `.tscn`/material), so the surfaces match the tokens without a runtime
|
||||
dependency on the script.
|
||||
|
||||
- `parchment.gdshader` — vertical gradient `#ece2ca → #e4d8bd`.
|
||||
- `dark_bay.gdshader` — 135° repeating stripe `#26221c` / `#221e18` (16px bands)
|
||||
plus the radial vignette overlay.
|
||||
- `vignette.gdshader` — standalone edge-dim overlay for the pause/dialogue dim.
|
||||
|
||||
## 6. The proof — `theme_showcase.tscn`
|
||||
|
||||
A single scrollable screen rendering, for a human to eyeball in the editor:
|
||||
|
||||
- the full palette as labelled swatches,
|
||||
- every stylebox type-variation (CTA, both tabs, chips in each semantic colour,
|
||||
filled + empty item tiles, parchment card + inset, dark panel),
|
||||
- all three fonts at their real sizes (heading, prose body italic, hand-inked
|
||||
accent, mono chrome),
|
||||
- the rarity ladder,
|
||||
- a sample HP / MP / stamina bar,
|
||||
- the three shader surfaces side by side.
|
||||
|
||||
Pure presentation — no AI, no network, no game state. This is what confirms "the
|
||||
look is one system" before any real screen is built.
|
||||
|
||||
## 7. Testing
|
||||
|
||||
- **`test_palette.gd`** — asserts the documented tokens exist and are `Color`;
|
||||
`rarity_color()` maps each rarity to the right colour and handles out-of-range
|
||||
safely; the stat-bar stop helpers return the documented values.
|
||||
- **`test_theme_resource.gd`** — `game_theme.tres` loads without error, and every
|
||||
named type-variation in `theme_keys.gd` resolves on the Theme (no dangling
|
||||
key).
|
||||
|
||||
**Visual correctness is not GUT-testable** — it is an explicit **human-confirm**
|
||||
step against `theme_showcase.tscn`, exactly as M2's live model proofs were
|
||||
human-confirmed. The spec is not "done" until a human has opened the showcase and
|
||||
signed off that it reads as the mockups' system.
|
||||
|
||||
## 8. Explicitly out of scope (this cycle)
|
||||
|
||||
- Any real screen (the Main Window shell 2a and Title are the next two M3
|
||||
cycles).
|
||||
- Real art of any kind — portraits, key art, iso/battlefield renders, item/ability
|
||||
icons remain the mocks' dashed **art slots**, filled later.
|
||||
- Responsive/stretch tuning beyond the project's existing 1920×1080 canvas
|
||||
assumption (`canvas_items` stretch is a shell-cycle concern, not a Theme one).
|
||||
- Baked-PNG texture variants — the three surfaces are shader-only this cycle.
|
||||
- Animation (ember particles, pulse/`cturn` keyframes) — those belong to the
|
||||
screens that use them, not the Theme.
|
||||
|
||||
## 9. Reconciliations touched
|
||||
|
||||
None. The two flagged reconciliations (damage determinism → M5, class names →
|
||||
M4) do not touch presentation. This cycle introduces no charter conflict.
|
||||
Reference in New Issue
Block a user