feat(ui): UX pass — funnel, results triage, identity, polish#57
Merged
Conversation
Four-slice frontend overhaul driven by a research → critique → design pipeline. Slice A — Funnel + a11y foundations - Asymmetric CTA pattern: one solid primary + text-link sibling on anon result, exhausted-trial card, and results header (Hick's Law fix). - Mobile credits pill: drop hidden sm:inline-flex; compact dot+count <sm, full label sm+. aria-label replaces title. - Replace <details> DB picker with role="radiogroup" chip row (roving tabindex, arrow-key nav, Space/Enter select, localStorage persistence). - Honest loading copy on anon path: "Analyzing your query…" + indeterminate bar, animation gated behind prefers-reduced-motion. Authed copy shifts "ML analysis" → "Deep analysis". - SQL field error association (aria-invalid, aria-describedby, role="alert", red wrapper border). - Toast + messages region get role="status" aria-live="polite"; errors upgrade to role="alert". - Theme toggle gains aria-pressed; <main> gets tabindex="-1" so the skip link works in Safari/Firefox. - User dropdown converted from hover-only to click-toggle (aria-expanded, aria-controls, ESC + click-outside close, focus first item on open). - Focus ring rework: focus-visible:ring-2 ring-offset-2 ring-indigo-500 globally on primary CTAs (replaces low-opacity ring that failed contrast). - Reusable _grade_pill.html partial with aria-label="Grade D, score 42 of 100"; account/history/results all use it. - CodeMirror font-size 14px → 16px (kills iOS Safari zoom-on-focus). - Anon hero h1 steps down: text-3xl sm:text-4xl md:text-5xl. Slice B — Results page becomes a triage tool - Top-issue hero at top of grade_results: highest-severity issue rendered next to a read-only CodeMirror with .qg-line-issue line-highlight when the offending token can be located via regex. Graceful fallback to a "Found in <clause>" pill — no invented line numbers. - Severity indicators pair color + shape (● high, ■ medium, ▲ low) + text label (WCAG 1.4.1). - "Other issues" feed with Jump-to-fix links; recommendations carry id="fix-N" and a Back-to-issue link when matched. - #issue-N / #fix-N anchor system: .qg-anchor-target gets scroll-margin-top: 5rem; hashchange listener shifts focus to target (tabindex="-1"), applies .qg-pulse-target flash gated behind prefers-reduced-motion: no-preference. - Sticky right-rail TOC hidden lg:block with <nav aria-label> and aria-current="true" on the active anchor. - Index recommendations auto-collapse for grades A/B via <details>. Slice C — Visual identity + dark mode rework - Self-host JetBrains Mono 500/600 woff2 at analyzer/static/analyzer/fonts/; register via @font-face in base.html with font-display: swap. - Extend Tailwind font-mono via Play CDN tailwind.config. - Apply font-mono + tabular-nums to h1/h2 and numeric displays (score, big grade glyph, stats) across account/history/results/auth pages. Body stays Inter. - Grade-pill dark-mode contrast rework: distinct rgba backgrounds and brighter *-300 text per grade (each pair clears 4.5:1). - Surface tokens planted at :root / html.dark (--qg-surface, --qg-surface-2, --qg-border, --qg-ink, --qg-ink-muted) for future use. - Audit text-*-900 headings; pair every one with dark:text-white. - One indigo→slate decorative swap on account.html stat tile. Slice D — Polish - Replace native alert() shortcuts in grade_form with <dialog id="qg-shortcuts-dialog"> via showModal() (native focus trap + ESC). Global "?" hotkey opens it, gated to non-input focus so it doesn't fight the SQL editor. - New footer: wordmark + © year, status dot, "What's new" → Releases, GitHub link. - Authenticated navbar gains an md:hidden hamburger that toggles a stacked nav drawer (44px tap targets, ESC + click-outside + link-click close, aria-expanded). - History filter row: flex-wrap → grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4; selects span full width on mobile. Verification: python manage.py check → 0 issues. collectstatic copies the new font files. No new bare text-*-900 introduced. dark-mode.css audited — no */ inside comments. Out of scope (deferred): Lucide stroke icon migration; analytics events for the new funnel surfaces; build SHA pill in the footer (no env wiring); compare_results / batch_results anchor rollout.
Owner
Author
Code reviewFound 2 issues:
QueryGrade/analyzer/static/analyzer/css/dark-mode.css Lines 243 to 247 in 308a655
QueryGrade/analyzer/templates/analyzer/grade_results.html Lines 221 to 278 in 308a655 |
- base.html: drop role="status" aria-live="polite" from the Django messages wrapper. Wrapping per-message role="alert" children in a polite live region produces implementation-defined behavior across screen readers; individual role="alert" on error messages is sufficient and non-error messages render at page load. - dark-mode.css: remove the html.dark .bg-slate-900 and html.dark .text-slate-900 global overrides. Same-specificity selectors lose to Tailwind dark: variants only by source order, and since dark-mode.css loads after the Tailwind CDN, the override won — inverting the selected DB chip's intended dark:bg-white + dark:text-slate-900 back to near-white-on-white. Inline comment now flags the cascade trap. - grade_results.html: refresh CodeMirror editors when the index-recommendations <details> opens. The panel defaults closed for grades A/B; CodeMirror editors initialized inside a hidden container measure zero gutter width until the browser repaints. Per CLAUDE.md: "CodeMirror in hidden tabs: always call refresh() inside setTimeout(0) after unhiding."
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Four-slice frontend overhaul driven by a research → critique → design pipeline. Touches the anon→paid funnel, the results page, visual identity, and polish.
role="radiogroup"DB chips, honest loading copy, SQL field error association, toast/messagesaria-live, theme-togglearia-pressed, click-toggle user dropdown, focus-ring rework, reusable_grade_pill.htmlpartial witharia-label, CodeMirror font-size 16px (kills iOS zoom-on-focus), h1 step-down.#issue-N/#fix-Nanchor system withscroll-margin-top: 5rem, focus shift, and reduced-motion-gated pulse; sticky right-rail TOChidden lg:block; index-recs auto-collapsed for grades A/B.font-monoextended; h1/h2/score numbers swapped to mono +tabular-nums; grade-pill dark-mode contrast rework (distinct rgba bgs, brighter*-300text); surface tokens (--qg-surface,--qg-ink, etc.) planted; everytext-*-900heading paired withdark:text-white.<dialog>shortcuts modal withshowModal()+ global?hotkey (gated to non-input focus); footer with status dot + "What's new" + GitHub link; mobile hamburger nav atmdbreakpoint; history-page filter row → responsive grid (1/2/4 cols).Why
UX research + opinionated critique flagged three structural problems:
text-*-900headings were near-invisible on dark surfaces.Plus WCAG 2.1 AA failures (hover-only user dropdown, focus ring failed contrast in dark, no field-level error association, color-only severity dots) and mobile breakage (navbar overflow ~375px, iOS zoom on CodeMirror focus from
font-size: 14px,<details>DB picker burying a core differentiator).Files
base.html,index.html,grade_form.html,grade_results.html,account.html,query_history.html,login.html,register.html, plusdark-mode.css)_grade_pill.html)jetbrains-mono-{500,600}.woff2, ~94 KB each, self-hosted — no CSP changes needed)Out of scope (intentionally deferred)
compare_results.html/batch_results.htmlenhanced_grade_results.html(pre-existing XSS pattern flagged separately)Test plan
python manage.py check→ 0 issues (passes locally)python manage.py collectstatic --noinput→ 169+ files, fonts present/): DB chips render, keyboard nav works (arrows + Space), inline grade fires, "Open full report" is the primary CTA, loading bar is honest, error states show red border +role="alert"/grade/): Shortcuts dialog opens via button and?key (when not in editor), traps focus, ESC closes; loading overlay says "Deep analysis", not "ML analysis"/grade/results/<id>/): top-issue hero renders, CodeMirror line-highlight if regex matches, "Jump to fix" anchors work and shift focus, right-rail TOC visible atlg+, index-recs collapsed for A/Baria-label, filter row stacks responsively on mobile, no pill-contrast collisions in dark mode<md, drawer toggles cleanly, user dropdown still works (click-toggle, ESC closes)aria-pressedupdates, no invisible headings, grade pills are visually distinct from each otherprefers-reduced-motion