Skip to content

Signed-in web survey (step 1) โ€‹

  • Status: survey complete. No web UI code was changed by this run.
  • Ran: 2026-08-27, headless chromium at 1440x1000, signed in as the main test account.
  • Target: http://localhost:5173 (the operator's running web dev server, untouched). Not deployed dev.
  • Why it exists: the 2026-08-24 dashboards survey reached only the PUBLIC web routes and said so (its W3). Everything a signed-in user sees had never been measured.
  • Evidence: screens/ (one full-page capture per route), harness/ (the two Playwright drivers and their JSON output).
  • Method: getBoundingClientRect and getComputedStyle on the live page, per route. Nothing here is read off a stylesheet.

What was actually driven? โ€‹

RouteRenderedNote
#/yesDashboard home
#/lightyesSame shell as #/, with a "Tap a venue" instruction bar
#/activenoBlank page, see W-A
#/wavesnoSilently redirects to #/, see W-B
#/frensyesEmpty state (0 frens)
#/activityyesEmpty state
#/scheduleyesVenue picker step 1
#/profileyesProfile tab of the Settings shell
#/profile/settingsyes
#/profile/privacyyes
#/profile/safetyyes
#/supportyes
#/feedbackyesModal, no bottom nav
#/venue/<id>yesThe seeded alpha venue, with a sponsored offer
  • 14 routes attempted, 12 rendered. The two that did not are findings in their own right.
  • Not driven: #/login, #/signup, #/migrate/phone, #/devtools, #/dev/location-recovery, #/frens/<id> chat, #/schedule/confirmed, and the three legal pages. The first two are public and already surveyed; the rest need state this run did not have (a lit lantern, an accepted wave, a scheduled light).

What did it find? โ€‹

Nine items, W-A through W-I, numbered to continue the dashboards survey's W1-W3 rather than restart it. Options, not verdicts.

W-A. #/active renders a completely blank page. โ€‹

  • Measured: hash stays #/active, document.body.innerText is the empty string, zero visible headings, zero icons, zero buttons. Not even the bottom nav paints. Capture: screens/active.png, which is 6.8KB of pure background.
  • Condition: signed in, geolocated inside the alpha venue geofence, with no lantern currently lit.
  • Why it matters here: #/active is not an orphan. App.jsx navigates to it from two places (lines 1169 and 1204) and lists it in the routes that keep the dashboard shell mounted (lines 1048 and 1150). A user who lets a lantern expire while sitting on that route gets a black screen with no way out.
  • The grammar it breaks: every other empty condition in the app renders prose (see W-H). This one renders nothing.
OptionWhat it doesRisk
A. Redirect to #/ when there is no active lanternMatches what #/waves already doesSilent redirects are their own defect, see W-B
B. Render the app's empty-state block ("No lantern lit", plus the light CTA)Consistent with #/activity and #/frensOne more empty state to write
C. Keep the shell and nav mounted, empty content areaCheapest, and the nav alone fixes the trapStill an unexplained blank region

W-B. #/waves silently redirects to #/, and nothing says so. โ€‹

  • Measured: navigating to #/waves ends with location.hash === '#/' and the dashboard home rendered. Byte-identical probe output to #/.
  • App.jsx:1208 navigates to #/waves from within the app, so this is a live in-app destination that does not exist.
  • Related to W2 in the dashboards survey (three profile routes render one signed-out screen). Same class: a route that resolves somewhere the user did not ask for, with no message.

W-C. Three signed-in screens have no h1, and the profile family gives four different pages the same one. โ€‹

  • No h1 at all: #/schedule (starts at h2:Schedule a Light), #/feedback (h2:Submit Request), #/venue/<id> (h2:Lantern Alpha Venue).
  • One h1 for four pages: #/profile, #/profile/settings, #/profile/privacy and #/profile/safety all render h1:Settings. The tab that is actually open never appears at the top of the outline.
  • A heading carrying data: #/venue/<id> renders h3:Lit Lanterns\n0, so the count is inside the heading text.
  • A heading carrying an emoji: #/profile/privacy renders h3: plus a construction emoji plus Dev: Location Spoofing.
  • Story a11y already gates heading order at error level for apps/web components (AGENTS.md, Testing). These are screen-level compositions that no story renders, so nothing catches them.

W-D. Emoji and text glyphs in chrome, on four signed-in screens. โ€‹

This extends the dashboards survey's W1 (which found three sites, all on public routes) into the signed-in app.

ScreenGlyphWhere
#/frenshandshake emojiThe "No mutual frens yet" empty state, rendered at 60px/400
#/profilelock emojiInside the "This profile is anonymous" prose line
#/profile/privacyconstruction emojiInside an <h3>
#/profile/privacy5 checkmark charactersUsed as the list bullets of "Privacy Guarantees"
#/feedbacksparkles, bug, lightning, question markThe four request-type category buttons
  • The four on #/feedback are the clearest chrome case: they are category affordances in a form, exactly the role Lucide fills everywhere else.
  • The five checkmarks on #/profile/privacy are the judgement case step 3 already names: a checkmark leading a list item is arguably punctuation. They are, however, list bullets rather than prose, which puts them nearer chrome than the in-sentence case.
  • The lock on #/profile is genuinely in prose and is the one this survey would leave.

W-E. Four interactive targets on every signed-in screen are under 24px. โ€‹

  • Bottom nav, every screen: Info measures 22x41 and Me measures 22x41. Frens is 27x41 and Places is 32x41, so the row is not uniform either. The measured element is the label plus icon group; the tap area may be larger, and that is worth confirming before acting.
  • Tour tooltip: Skip measures 25x16.
  • #/profile/privacy: Delete my account measures 123x20.
  • #/feedback: a checkbox input measures 16x16.
  • WCAG 2.5.8 asks for 24x24. The e2e:mobile-pwa harness already audits touch targets under phone emulation; this run measured them at desktop width, where the same numbers hold.

W-F. The type ramp is 10 to 14 distinct combinations per screen. โ€‹

Counted as unique font-size / font-weight / line-height / letter-spacing tuples on visible leaf text nodes.

ScreenDistinct combinations
#/profile14
#/frens13
#/venue/<id>12
#/, #/profile/privacy, #/support10 to 11
#/activity5
  • Outliers worth naming: 18px/400/18px on #/profile (line-height equal to font-size, so lines touch), 60px/400 used for exactly one emoji on #/frens, and 13px/400/21.125px plus 15px/500/22.5px on #/support which appear on no other screen.
  • #/activity is the counter-example at 5 combinations, which suggests the sprawl is accumulation rather than necessity.

W-G. Four corner radii plus one that is not a value. โ€‹

  • Measured across elements wider than 80px: 16px (45 elements), 20px (37), 12px (20), 4px (5), plus 3.35544e+07px (15), which is the computed form of a full-round pill.
  • The admin portal draws radii from --radius* tokens. The web app uses raw Tailwind rounded-* classes, so the two surfaces have no shared radius vocabulary at all. This is a "one product" gap the tokens do not currently close.

W-H. The signed-in web app has NO empty-value glyph, and that is a deliberate-looking grammar the portal does not share. โ€‹

  • Measured across all 12 rendered routes: zero em dash, zero en dash, zero hyphen placeholders. The empty-value problem step 2 fixes is portal-only.
  • What the app does instead is prose: "No activity yet", "No mutual frens yet", "No venues found", "Light a lantern or send a wave to get started".
  • This is a direct input to step 2's decision. The web app already answers "what does nothing look like" with words. The portal answers it with punctuation, in three vocabularies. The two surfaces will not read as one product until someone says which is right where.
  • The honest reading: prose works in a card or an empty list; it does not fit a table cell or a stat tile, which is where every portal instance lives. So the two answers are compatible, and the rule is about SURFACE rather than about taste. That framing is what step 2 adopted.

W-I. The dev location-spoofing panel is purple, and purple is not in the chrome system. โ€‹

  • ProfileSettings.jsx:829-994 uses bg-purple-900/20, border-purple-900/40, text-purple-400, focus:border-purple-400 and bg-purple-600 for the panel, its heading, its two inputs and its primary button.
  • The design skill is explicit that there is no purple or indigo in the chrome system, and that the purple in packages/ui/colors.js is user-selectable DOMAIN data (a lantern match colour). This panel is chrome.
  • It renders only in dev, which is why it survived. It is still a live signed-in surface on the dev build the operator uses.

What could not be seen? โ€‹

  • Anything needing a lit lantern: #/active with real content, the venue chat surface, #/frens/<id>.
  • Anything needing a second account: waves received, an accepted connection, a chat thread.
  • Phone form factor: this run measured desktop 1440x1000. e2e:mobile-loop and e2e:mobile-pwa already cover phone emulation and should be read alongside, not re-derived here.
  • The onboarding tour overlaps content: on #/profile/privacy the "STEP 1 OF 3" tooltip sits on top of the account-recovery card and obscures its title. Recorded as an observation rather than an item, because it is a tour-sequencing question, not a design grammar.

What does this change about the pass? โ€‹

Step 2's scope is portal-only, and that is now measured rather than assumed. โ€‹

  • W-H is the finding. No web file needed an empty-value change, so step 2 landed entirely in apps/admin.

Step 3 gains five sites and loses none. โ€‹

  • W-D adds #/feedback (4 glyphs), #/profile/privacy (1 emoji in an h3, 5 checkmark bullets), #/profile (1 in prose) to the three the dashboards survey already had. The checkmark-in-prose judgement step 3 anticipated now has a concrete borderline case: checkmarks as LIST BULLETS.

Three items are new and belong to no step yet. โ€‹

  • W-A and W-B are navigation defects, which is step 6's subject. They fit its rule (a route that behaves differently from how it looks) and are the natural companions to W2 and X7.
  • W-C (heading outlines) has no step. It is cross-cutting, it is an a11y surface the story gate cannot reach, and it is cheap. Raised, not sequenced.
  • W-E, W-F, W-G and W-I are raised in backlog.md with what would promote each.

Built with VitePress