First slice of the host theme contract (#644): give plugins a host-owned way to
read the active theme + its device affordances, so a feature renders correctly
under any theme instead of binding to whichever one the dev saw.
theme-core.js previously only APPLIED themes and emitted --fbv-* vars only while
a theme was equipped (nothing to read in the default state), with no read API.
Now, all additive + feature-detected:
- Always-present default `fb` palette as `--fbv-*` on :root (the un-themed look
is unchanged — fb-* utilities still use their compiled defaults; this only
hands plugins a stable host token to read + derive surfaces from). Adds two
keystone ROLES the palette lacked: `on-accent` (legible fg on the accent fill)
and `focus-ring`.
- window.feedBack.theme.get() -> {id, isThemed, tokens}; .capabilities() ->
{glow, gradients, motion} (the device-affordance signal; recolor-only themes
report defaults, a theme may opt out via `capabilities` in its payload, motion
is reduced-motion-gated); .prefersReducedMotion().
- Normalized `theme:changed` event from the single apply() chokepoint.
The apply side stays on window.v3Theme; the read surface is attached defensively
so it survives the feedBack bus being (re)built by capabilities.js regardless of
load order. Verified via a headless render (apply/unequip intact, defaults
present + restored, capability opt-out honored, event payload correct) +
tests/js/v3_theme_read_api.test.js. See docs/host-theme-contract.md.
Claude-Session: https://claude.ai/code/session_01QbexxfTt8q2tAn436MqGWF
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>