RFC-0009: Responsive, Dark-Default, Themeable UI #164
Closed
kn4oqw-clint
announced in
RFCs
Replies: 1 comment
|
This has already been implemented. |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
ui/static, the dashboard + settings pages) and the auth gate (RFC-0002), whose claim/login screens are still placeholdersSummary
Make the whole UI usable from a phone: the dashboard and the settings page
responsive down to 360 px, a genuine light theme alongside the
dark default, and — the piece that is currently a stub — real first-run
screens (claim + login) so a device can be set up entirely on a phone. Dark
stays the default; light is a mode the operator can switch to and that composes
with the existing accent themes.
The acceptance is manual and visual (setup wizard + live dashboard at 360 / 768 /
1280 px in both themes), so this RFC fixes the contract those manual checks
verify — the breakpoint set, the touch-target floor, the light-mode token set, and
the first-run screens — and the PR verifies it in a real browser at each width in
each theme.
Motivation
Requirement #6's provenance is stark: a complete dark-mode PR sat ignored on
Pi-Star for four years (#173); "too small and not mobile responsive" (#141); the
2018 third-party Mobile Dashboard add-on existed only because the incumbent UI
wasn't phone-usable, and it is now abandoned. A hotspot is a device you configure
next to the radio — often with a phone, often with no desktop nearby. A UI that
assumes a 1280 px window fails the most common real setup moment.
Waypoint's UI is already a self-contained, offline-safe, accessible dark SPA (no
CDN, focus rings, skip links, WCAG-AA text), but three gaps keep it off the phone:
and stop there — nothing tunes the header, the settings tab rail, touch
targets, or type for a 360 px screen.
accent; all are dark. RFC-0018: Mesh Transports and the Text/Position Plane #173's four-year-ignored ask is a genuine light mode.
writePlaceholderstubs that tell theoperator to
curl POST /api/claim— not a flow anyone completes on a phone.Design
Breakpoint system
Three tiers, matching the acceptance widths, applied to both
index.htmlandsettings.html:unchanged.
is single-column; cards go full-width.
horizontally-scrollable strip (or a native
<select>jump) instead of avertical list that eats the viewport — superseded: the strip was replaced by a
grid of touch tiles sectioned by nav group once the tab count outgrew a single
scrollable row; the intent (a rail that does not eat the viewport, ≥ 44 px
targets, no sideways page scroll) is unchanged; the status bar wraps and shrinks; page
padding drops from ~30 px to ~14 px; headings scale down; every interactive
control is ≥ 44 × 44 px (WCAG 2.5.5 / Apple HIG touch target); tables get an
overflow-x: autowrapper so a wide row scrolls inside its card instead ofblowing out the page width.
The page body never scrolls horizontally at any width — wide content (event log,
config tables, the generated-INI preview) scrolls inside its own container. This
is a hard rule the manual check verifies at 360 px.
Theming: a mode dimension that composes with accent
Today
data-themeconflates accent and (implicitly) dark. This RFC splits theminto two orthogonal dimensions so light composes with any accent:
data-modeon:root— absent = dark (default);"light"swaps thestructural tokens (
--bg,--panel,--field, the line/side/row colors, andthe
--ink*/--label/--muted/--dim/--fainttext ramp) to a light palette.data-themeon:root— absent = phosphor green;"amber"/"ice"swap theaccent tokens, in either mode.
Because everything downstream already reads the CSS variables, the entire UI
re-themes from these two attributes with no per-component change. Light mode
also darkens each accent (green/amber/blue) to a shade that clears 4.5:1 on a
white panel, so accent-colored text (callsign chip, breadcrumb, links) stays
AA-legible — the light palette is validated against the same contrast bar the dark
one already meets, not eyeballed.
The switcher gains a Dark / Light toggle above the accent swatches. Both
choices persist (
wp-mode,wp-themeinlocalStorage) and apply on load beforefirst paint (an inline head script sets the attributes from storage, so there is no
dark-flash on a light-mode reload). First visit with no stored mode honors the
browser's
prefers-color-schemeonce as a hint, then the operator's explicitchoice wins forever after — "dark default" holds unless the OS says light and the
user hasn't chosen.
First-run screens (claim + login)
The gate serves claim/login pre-auth, so these screens must be fully
self-contained (no gated asset, no CDN) — which they already must be for the
offline/security posture. This RFC replaces the two placeholder strings with real,
responsive, dark-default, themed pages:
/while unclaimed): a single centered card — "Claim this device" —with username + password + confirm-password fields, inline validation (the
8-char password floor the API enforces, matching passwords), a submit that
POSTs/api/claimand on 201 redirects to/(now claimed → the SPA loadsbehind the fresh session cookie). Errors from the API render inline, not as a raw
JSON dump.
/while claimed but unauthenticated): the same card shape —username + password —
POST /api/session, redirect to/on success, inlineerror (with the login damper's lockout message surfaced legibly) on failure.
Both are phone-first: one column, large touch targets, the same token set so they
match the app and respect light mode. They carry the viewport meta and the
inline theme-from-storage script so a returning operator sees their chosen theme.
Password fields never autofill-submit and are
type="password"; the pages postJSON (no credentials in a URL). No new endpoints — they drive the existing
/api/claimand/api/session.What stays out of scope
by design (offline-safe single artifact). This RFC keeps that.
1280 px view is unchanged.
"first-run flow" is the claim/login gate, which is what blocks phone setup
today.
The contract (what the manual + automated checks verify)
Manual (the #6 acceptance), at 360 / 768 / 1280 px in dark and light:
networks, gateways, and event-log cards stack and stay within the viewport;
wide tables scroll inside their card.
every field and toggle is tappable (≥ 44 px); no control is clipped; the
generated-INI/preview areas scroll internally.
focusable, validation legible, submit works, redirect lands on the app.
persists across reloads with no flash of the wrong mode.
Automated (Go, in this PR):
<form>and posts to/api/claim) while unclaimed, and a real login page (posts to/api/session) while claimed-but-unauthenticated — not the old placeholdercopy. A guard test asserts the served HTML contains the form and the viewport
meta, so the screens can't regress to a stub.
Alternatives considered
compose with amber/ice and would grow to six themes to cover the matrix. Two
orthogonal attributes (
data-mode×data-theme) is fewer tokens and coversevery combination.
breaks the offline-safe, no-CDN, single-artifact rule and adds weight to a Pi's
tiny page. Hand-written media queries over the existing variable system are
smaller and match the codebase.
Rejected — the SPA and its assets sit behind the auth wall on purpose (a fresh
device leaks nothing). Self-contained claim/login pages keep that boundary while
still being real screens.
prefers-color-scheme. Rejected as thedefault — Responsive, dark-mode-default web UI usable from a phone #6 says dark default.
prefers-color-schemeis honored only as afirst-visit hint; the explicit toggle is authoritative and persists.
Open questions
first-run gate (claim/login) as the phone-blocking piece and treats a
multi-step guided first-config (callsign → modem → first network) as a later
slice on the now-responsive settings surface. Is the gate enough to call Responsive, dark-mode-default web UI usable from a phone #6
done, or should the first-config wizard land here too? Leaning: gate now, wizard
as a fast-follow tracked separately.
prefers-reduced-motionto still them is a small addition worth doing alongsidethis — folded in if cheap, noted here otherwise.
viewport media queries for finer control, but browser support on the
older phones a ham might use is uneven; viewport breakpoints are the safe choice
now.
Migrated from
docs/rfcs/0009-responsive-theming.md; the drafting history is in the git log.All reactions