docs(android): build contract for admin theming & navigation parity (M12)

The website merged runtime admin theming, brand assets and nav overrides to
main (website#126 / docs#109). The app reads exactly one field of it --
brand.accent -- and renders a hardcoded APP_MENU, so an admin who re-skins the
site and restructures the header sees none of it on the phone.

Adds docs/android/THEMING_AND_NAV.md as the design of record for M12, and the
PLAN.md §9 entry that anchors it. Plan only: no app code, no backend work.
Everything consumed is already live on website/main.

The points that shaped it:

- The app's ui/theme/Color.kt palette is already, value for value, the
  runic-gateway preset -- M5 was drawn from the same theme.css the preset was
  later extracted from. So "an untouched instance is unchanged" carries over as
  a testable ColorScheme equality assertion, not an approximation.
- Radii apply as a ratio against that baseline, not as literal dp. The app's
  Shapes came from the M5 mockup and genuinely differ (medium 12dp vs
  --radius-card 10px); a literal mapping would restyle the untouched app the
  day this ships, and copying the app's scale into the server would be a second
  source of truth.
- Fonts are bundled, not downloadable: the Play Store font provider makes a
  de-Googled device fall back silently. Seven families join the bundled Cinzel.
- Nav overrides are keyed by website paths, so the app needs a path -> route
  table -- the one new cross-repo coupling here. An override for a path the app
  does not surface in its menu is ignored: a nav override may never introduce
  navigation.
- The gates are untouched. MenuAccess and MenuEntry.feature still run after the
  merge, so hidden:false cannot un-hide what a role or the shard's visibility
  config withholds.
- Read the resolved theme/brand fields, never the raw theme_visual/brand_assets
  rows that ride along in the same payload -- re-deriving a palette from them
  would be a second resolveThemeTokens in Kotlin, guaranteed to drift.

Nine phases into a fresh edge in both repos, reaching main as one edge -> main
merge, the same shape the website side used. Phase 0 must change nothing on
screen. Phase 7 (the authenticated navs) is marked optional: nav_player reaches
two app rows and nav_admin two, which is a thin return for a new authenticated
fetch and its cache teardown.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TgfKv5cz5pbY3dPeofSE5a
This commit is contained in:
2026-08-08 01:39:52 -05:00
parent 518f1e0449
commit e86b04567f
2 changed files with 551 additions and 0 deletions

View File

@@ -990,6 +990,55 @@ push, and Play (M6M8) follow the designed app.
Visibility, Spawn Atlas and Cliloc import — alongside the hero/CMS block editor, Discord-bot
config, uo-link config and OAuth-provider setup.
13. **M12 — Admin theming & navigation parity** (post-v1; scoped 2026-08-08). The website merged
runtime admin theming, brand assets and nav overrides to `main` (website#126 / docs#109). The app
reads exactly one field of it — `brand.accent` — and renders a hardcoded `APP_MENU`, so an admin
who re-skins the site and restructures the header sees none of it on the phone. This milestone
makes the app a full consumer of that contract.
**Design of record: [`THEMING_AND_NAV.md`](./THEMING_AND_NAV.md)** — the token map, the phase
list and the locked decisions live there rather than here, mirroring how the website side kept
[`../website/THEMING_AND_NAV.md`](../website/THEMING_AND_NAV.md) separate from its own plan.
**No backend work.** Everything consumed is already live on `website/main`:
`GET /public/settings` gained `theme` (the full resolved token map) and `nav_public`, its `brand`
block now returns *effective* values, and `GET /api/v1/settings/nav` serves the admin/player
overrides to any authenticated account.
The points that shaped the plan, and that a reader of this file should know without opening it:
- **The app's palette is already the `runic-gateway` preset**, value for value — M5 was drawn
from the same `theme.css` the preset was later extracted from. So the website's governing
invariant (an untouched instance renders byte-for-byte as before) carries over as a *testable
equality assertion* on the resolved `ColorScheme`, not an approximation.
- **Radii apply as a ratio, not as literal dp.** The app's `Shapes` came from the M5 mockup and
genuinely differ from the web tokens (`medium` 12dp vs `--radius-card` 10px); a literal mapping
would restyle the untouched app the day this ships. A ratio against the `runic-gateway`
baseline makes an untouched instance a provable no-op while still tracking the admin's intent.
- **Fonts are bundled, not downloadable.** Seven families join the already-bundled Cinzel
(~1.52.5 MB, against a 4.2 MB signed release). Downloadable fonts were rejected: they need the
Play Store provider, so a de-Googled device silently falls back.
- **Nav overrides are keyed by *website* paths**, so the app needs a path → route table — the one
new cross-repo coupling here. Two asymmetries are decided rather than papered over: an override
for a path the app does not surface in its menu (champs / guilds / governors / houses, which
live behind the Shard hub) is **ignored**, because a nav override may never *introduce*
navigation; and the app's own entries with no web counterpart keep their coded order.
- **The gates are untouched.** `MenuAccess` and `MenuEntry.feature` still run *after* the merge,
so `hidden: false` cannot un-hide what a role or the shard's visibility config withholds — the
same boundary the website's §7 draws.
- **The authenticated navs are thinner than they look.** `nav_player` reaches two app rows and
`nav_admin` two (`/player`, `/account`, `/admin`, `/admin/moderation`); the sidebar's other
~18 rows are admin *configuration* the app excludes, and two of the app's four staff entries
are aggregates with no single web row. That is why they honor `label` and `hidden` only — and
why that phase is scheduled **last and marked optional**, so it can be dropped on its merits
once the rest is working.
- **Excluded**, in the same class as M10's and M11's exclusions: the admin *configuration* panels
themselves. The app does not gain Appearance or Navigation editors; it is a consumer.
Nine phases into a fresh `edge` in both repos, reaching `main` as one `edge` → `main` merge —
the same shape the website side used. Phase 0 (the contract and the appearance store) carries a
hard rule: it must change nothing on screen.
### Deferred (not a milestone)
- **`/api/mobile` facade migration + app-version floor** — briefly planned as its own milestone