Design verification โ
Verified 2026-08-27 against the running console (local admin portal on 3011, local analytics-api on 8092, branch feat/query-console-958). Measured with getComputedStyle and getBoundingClientRect on the real page, not read off the stylesheet.
This is a verification against the existing design system, not a redesign.
Does the surface follow the design system? โ
Yes on every mechanical rule, and the new Firestore code is the cleanest part of the file. โ
- Tokens, not literals: zero hex values in any of the Firestore blocks. Colour comes from
--accent-500,--surface,--border,--text-*. - No native
<select>: every dropdown isStyledSelect. A test already pins this. - No emoji in chrome: Lucide icons throughout (
Database,HardDrive,Plus,X,AlertTriangle,ExternalLink). - One primary button:
bq-btn--primaryappears once on the console surface (Run). The second instance is the save dialog's submit, which is its own surface. - Typography: Inter via the admin shell. No serif anywhere.
- Page structure:
PageHeader+PageTabsinside theuser-management-containeridiom, matching sibling admin pages.
The control heights were wrong, and the stylesheet could not have told you. โ
- A filter row rendered 42, 42, 34, 24 under
align-items: center, so four controls in one row sat on four different baselines. - The trap:
StyledSelectDECLARESminHeight: 38pxand RENDERS at 42px, because react-select's control and value-container padding carry it past the minimum. A fix that matched the declared 38 still did not line up. - Fixed by giving the builder one
--bq-fs-control-heightset to the measured 42px. Verified: all four controls now report 42.
What did the operator say about it? โ
Feedback log, 2026-08-27. โ
"I see it working now, thank you! That said, we definitely need some design and functionality considerations. First off, we probably need to create a reference doc for this. Or at bare minimum have some breadcrumbs ( like what is firestore for vs bigquery? ). Also, how would someone know how to even query firestore? For example, how would they know to query how many lanterns are lit at an x venue for example?"
Her example is the proof of her own point, and the answer is not the obvious one. โ
- The console CAN answer "how many lanterns are lit at venue X". The field is
venues.activeLanternCount, and it is allowlisted. - A user reaching for that answer would look for a
lanternscollection. There isn't one, and there deliberately never will be:lanternsandwavesARE the who-met-whom-where-and-when edges the privacy architecture exists to minimize, so they are named in the service header as permanently absent. - So the surface currently fails her question twice over. It does not say the right field exists, and it does not say why the collection she expects is missing. She reads that as "the console cannot do it" when it can.
What is missing from the surface? โ
Nothing says what each source is FOR. โ
- The tab strip offers BigQuery and Firestore as equals with no statement of when to reach for which.
- The page subtitle does carry one clause per source ("BigQuery holds the event stream, queried with SQL" / "Firestore holds current state, queried by collection, filter and order"), which is correct and easy to miss, because it sits above the tabs and changes under you when you switch.
- The distinction that actually decides it: BigQuery answers what HAPPENED, Firestore answers what IS. An event that fired last Tuesday is BigQuery; an offer sitting in the moderation queue right now is Firestore.
A field name is not a question, and the builder only shows field names. โ
- The collection picker offers five nouns and the field picker offers a list of identifiers. Neither says what question the combination answers.
activeLanternCountis the clearest case: it is the answer to a question a user would phrase completely differently.
An absent collection is indistinguishable from a missing feature. โ
- The allowlist is a privacy decision with real reasoning behind it, and none of that reasoning reaches the screen. The builder's footnote says only "Only allowlisted fields are readable, and the list is per collection."
- A user who cannot find
usersorlanternshas no way to tell a deliberate refusal from an oversight, so the natural next step is to ask for it to be added.
The two sources behave differently on failure, on one page. โ
- Firestore clears the results grid when a query fails (deliberate, added in
c2bf044d). BigQuery keeps the stale grid under a red strip. - Both are defensible; having both on one page is not. This one is a product call, so it is recorded rather than changed.
What was checked and is genuinely fine? โ
- The Firestore builder's own CSS block introduces no raw hex and no unmanaged scroll container.
- Every new form control has an accessible name. Icon-only buttons are labelled.
- The route move keeps
/admin/analytics/bigquery/consoleworking as a redirect. - Layout sizes to content rather than stretching: the pickers carry explicit widths (200 / 190 / 170px) instead of
1frtracks.
What did the verification NOT cover? โ
- Contrast measurement against the darkest ground. Not run. The surface introduces no new colour, so nothing new to measure, but that is an argument rather than a measurement.
- Storybook.
ConsolePanel.stories.jsxexists; the Firestore source was not reviewed as a story.lint:storiespasses (83 stories, no stubs, titles on-convention). - The sidebar nav entry, which is out of scope for this branch by dispatch. It is a
<div onClick>with no role or tabIndex, so a keyboard-only admin cannot reach this page from the nav. Pre-existing and shared by every sibling, tracked as#922: the portal's sidebar navigation cannot be reached or activated by keyboard.
What is pre-existing in this file and not from this work? โ
Recorded so a later reader does not attribute them to this branch, and so a cleanup pass has a starting list.
| Finding | Where | Scale |
|---|---|---|
| Raw hex literals where tokens exist | ConsolePanel.css, status colours | 5 (#34d399, #fbbf24, #f87171 and repeats) |
| Raw hex for the SQL syntax palette | ConsolePanel.css lines 1071 to 1174 | 8, includes #c084fc purple, which the system says it does not have |
| Scroll containers showing a native scrollbar | ConsolePanel.css, 8 overflow: auto rules | 8, zero use .no-scrollbar |
- The syntax palette is arguably a legitimate exception: a code editor needs colour distinctions the design system does not define. That is a decision worth making explicitly rather than leaving as drift.