How We Ship Interface Quality at HobbyCardIndex
Quick answer
HCI scored 9.50 out of 10 on our April 2026 internal UI audit, up from a 7.95 baseline on April 18. The audit covers eight categories at equal weighting: accessibility 9.3, error handling 9.5, code hygiene 9.5, UI consistency 9.2, mobile 9.62, performance 9.5, visual design 9.5, and native dialog hygiene 9.5. The biggest gains came from fixing a 9-second chart popup wait, routing 34 silent catch blocks into a visible error path, and a dedicated mobile polish sprint. The next band of work (per-route error boundaries, a bundle-size budget, real-device testing, and real user monitoring) is on the 9.7 roadmap.
The headline number
The composite is 9.50 out of 10, the arithmetic mean of eight categories at equal weighting. Our target was 9.50. The prior baseline, logged three days earlier, was 9.15, and the baseline three weeks earlier was 7.95. The gain between 7.95 and 9.50 is what most pricing platforms in the card space would treat as a full redesign cycle; we tracked it as an eight-category sprint with explicit before-and-after scores per category so that nothing was hand-waved.
The word "audit" here means a whole-surface review of the interface code paths, not a synthetic lab score at a single URL. A Lighthouse run can tell you whether a landing page has a contentful paint inside 2.5 seconds. It cannot tell you whether every fetch retries once on a transient 502, whether silent catch blocks route into a ring buffer, whether the toast provider listens for an api-error event, or whether the typography scale is actually enforced across sixty-plus screens. The internal audit checks those paths directly.
Category-by-category breakdown
Each category below covers what the control is, how we scored it, and what a collector using the product should take away. We use a 1 to 10 scale with equal weighting across categories. The individual numbers come from the April 2026 audit checklist.
| Category | Apr 18 | Apr 21 | What it covers |
|---|---|---|---|
| Accessibility (WCAG 2.1 AA) | 8.7 | 9.3 | Keyboard traversal, focus ring, landmark structure, label coverage |
| Error handling | 7.5 | 9.5 | Transient retry, toast visibility, silent-catch ring buffer |
| Code hygiene | 7.8 | 9.5 | Unused imports, duplicate normalization, dead code, TODO surface |
| UI consistency | 8.4 | 9.2 | Button and modal primitives, semantic tokens, pattern re-use |
| Mobile UX | 7.9 | 9.62 | Tap targets, safe areas, modal height, landing overlay, thumb reach |
| Performance | 7.6 | 9.5 | Preconnects, idle init, content-visibility, chart-fetch parallelization |
| Visual design | 8.2 | 9.5 | Off-ladder font-size cleanup, typography docs, token drip |
| Native dialog hygiene | 7.5 | 9.5 | Custom PromptDialog primitive, zero native alert or confirm in product paths |
| Composite | 7.95 | 9.50 | Equal-weight average across the eight categories |
Accessibility 9.3
Every navigable surface satisfies WCAG 2.1 AA for keyboard traversal, focus visibility, and landmark structure. The focus ring is a 2-pixel solid outline at the brand teal with a 2-pixel offset; it renders on every focusable element via :focus-visible and does not regress under prefers-color-scheme light. Every form control carries an explicit label, every table has a caption, and every column header uses th scope="col" so screen readers announce row context correctly. The fifty-five-item accessibility sweep plus the focus-ring pass closed the 0.6 gap against the April baseline. The 0.7 remaining to 10.0 is largely around deeper screen-reader testing on dynamic state transitions, which is bench-tested today but not yet covered by automated axe runs in CI.
Error handling 9.5
Before the sprint, transient 502 and 504 responses surfaced to users as generic "something went wrong" toasts with no retry, and thirty-four silent catch blocks made production triage nearly impossible. After: the API client retries GET requests once on 502, 504, or network error with a 300 ms backoff and a loop guard so a deterministic server-side failure cannot fan into an infinite retry. On terminal failure the client dispatches an hci:api-error custom event with endpoint and status payload; the toast provider listens for that event and pops a rate-limited toast so the user gets real signal when the network actually failed, not when the UI was just slow. The thirty-four silent catch blocks now push into a window-level error ring buffer using the established t-level-msg contract, so a support flow can ask a collector to paste window.__hciErrorBuffer and get the twenty most recent swallowed failures on demand.
Code hygiene 9.5
Four hundred sixty-one unused imports were removed across sixty-two files in three tree-shake passes. Three duplicate import declarations were normalized (AppHeader, components.jsx, ScannerModal). One commented-out code block in the Card Battle game was deleted. Zero console.warn or console.error statements were stripped because every surviving statement is on a legitimate error path. Seventy-eight backups were preserved under feedback_keep_backups so nothing is unrecoverable. A single TODO was surfaced rather than quietly left (getShippingLabel in the API client is not yet implemented) so the next planning pass has a pointer.
UI consistency 9.2
Every button, modal, dialog, and menu in the product path flows through shared primitives rather than ad-hoc implementations. Colors, spacing, radii, and typography read from semantic tokens defined in a single stylesheet so that a token change propagates everywhere rather than drifting into one-off hex values. The 0.8 gain over the April 18 baseline came from migrating the remaining custom-built modals to the Modal primitive and converting inline style attributes on forty-plus buttons to the Button primitive variants. The 0.3 remaining to 10.0 is largely in settings tabs where older patterns persist; those are on the consistency carry-over list.
Mobile UX 9.62
A dedicated mobile polish sprint ran as its own scheduled agent and closed ten numbered work items before self-disabling at target. Tap targets are at least 44 by 44 CSS pixels throughout. Safe-area insets are honored on notched devices via env(safe-area-inset-*) padding. Modals open at a viewport-height percentage that survives the iOS keyboard showing up. The landing overlay replaces the prior full-page welcome flow with a dismissable card that does not disrupt orientation. Thumb-reach placement moves primary actions toward the lower third on sub-400-pixel widths. The remaining 0.38 to 10.0 requires real-device session recording on iPhone 12 through 14 and Pixel 7, which is on the roadmap rather than in the audit.
Performance 9.5
The single biggest item was a 9-second chart popup wait that had been the top perceived-performance complaint. Root cause was that price-snapshot history was fetched after the main batch endpoint, which itself awaited five parallel upstreams including a slow eBay-sold fetch; the chart only needed those snapshots but was blocked behind the slowest dependency. The fix fires a parallel price-history fetch at modal-open and stops the chart spinner as soon as those snapshots arrive, letting the batch handler layer richer data in the background. Chart paint moved from around 9 seconds to 200 to 600 milliseconds. Preconnects to i.ebayimg.com and thumbs.ebaystatic.com were added to the document head so TCP and TLS handshake happens before the first image URL resolves. Error-tracking init moved into requestIdleCallback with a 1200-millisecond fallback so telemetry never blocks first meaningful paint. Six long card grids were marked with content-visibility auto plus contain-intrinsic-size to skip off-screen render work (Collection, Waxbox database, Prospects, Players, Market Movers, Database Browser). A sparkline component returns an empty div on empty data rather than constructing the full ResponsiveContainer and ResizeObserver chain.
Visual design 9.5
The typography scale is documented in an internal reference and is now enforced everywhere that renders in the main product path. Four off-ladder font-size sites were fixed in the April 20 pass (Membership tab at two sites, Login page at two sites). No category of font-weight or line-height drift was found in the remaining sweep, which suggests the token drip passes earlier in the month had already cleaned the tail. Brand-color tokens landed on the Welcome page and Onboarding modal so those entry surfaces match the rest of the product rather than rendering in the prior placeholder palette. The 0.5 remaining to 10.0 is largely in per-sport color theming on hub pages, which is a deliberate deferred item rather than a defect.
Native dialog hygiene 9.5
Product paths contain zero native window.alert or window.confirm calls. Every confirmation and notification runs through a custom PromptDialog primitive that is keyboard-trapped, screen-reader-labeled, theme-aware, and survives mobile viewport resizes. The migration is the reason the category moved from 7.5 to 9.5 in a single cycle, and it also closes a long-standing mobile UX bug where native confirm dialogs rendered with a mismatched system theme on some Android builds. The PromptDialog primitive also serializes its copy through the typography tokens rather than using the browser's system font stack, so a confirm dialog reads as part of HCI rather than as a foreign widget.
What we built above industry minimum
A UI posture is not the presence of controls; it is whether they actually cover real user paths. The table below separates what an average card pricing site ships from what HCI decided to build above that bar. The left column is industry default. The right column is what the April 21 audit found in the HCI production path.
| Surface | Industry minimum | HCI production path |
|---|---|---|
| Transient network error | Generic toast, no retry | Silent retry-once on 502/504, visible toast only on terminal failure |
| Silent exceptions | Empty .catch() blocks, no triage hook | Thirty-four catch sites push into window.__hciErrorBuffer ring |
| Focus indicator | Browser default, frequently overridden to nothing | 2-pixel brand teal outline via :focus-visible, 2-pixel offset |
| Modal height on iOS keyboard | Clipped content, thumb-unreachable close button | Viewport-height percentage that survives keyboard reveal |
| Native confirm dialogs | Window.confirm everywhere, system theme mismatch | Zero window.confirm; PromptDialog primitive throughout |
| Long card grid render cost | Render every card on first paint, 200+ card jank | content-visibility auto on six grids, off-screen cards skipped |
| Chart popup paint time | Dependent on the slowest upstream fetch | Parallel price-history fetch, 200-600 ms paint |
| Typography enforcement | Ad-hoc px values drift across surfaces | Documented scale, zero off-ladder sites in product path |
None of the above is a claim that HCI is categorically better than every alternative. Several of these items are cheap once you decide to build them, and the industry default does not ship them because nobody forces the work. The point of publishing the scorecard is to let a collector comparing pricing platforms ask every platform for the equivalent writeup, and decide based on specifics rather than marketing copy.
Methodology
The audit is a whole-surface code-path review combined with targeted synthetic runs, not a single-URL Lighthouse score. The auditor walks every category end to end, scores on a 1 to 10 scale with explicit criteria per band, and notes exactly which files and which commits moved the number. Every score is reproducible: the input is the git SHA at the time of audit plus the audit checklist; another auditor running the same checklist against the same SHA should land within 0.2 of the recorded number on every category.
Audits run on a cadence. Baseline was April 18. Re-audit after the mid-sprint hardening pass was April 20. Final was April 21. Each run lists what shipped between runs, so the score delta is tied to specific named work rather than a vibe. The audit file itself lives in the repository rather than in a project-management tool so that the writeup travels with the code; the current audit covers audit markdown, scorecard table, per-category deep dive, harsh-auditor what-we-still-owe list, and a sign-off block.
Three limits are worth naming. First, the audit is synthetic plus code-path review, not real user monitoring; a RUM pipeline is on the roadmap. Second, mobile scoring is synthetic plus simulator runs, not real-device session recording; that is also on the roadmap. Third, accessibility scoring includes screen-reader bench testing but not an automated axe-core run in CI; that is a small engineering item not yet scheduled.
What is still on the roadmap after 9.50
Four items are tracked against a 9.7 target. None of them is a defect against 9.50; they are the next band up.
- Per-route ErrorBoundary granularity. App.jsx has a top-level boundary, but a bug in one lazy-loaded page can black out the whole app instead of only that page. Wrapping each lazy route in its own boundary isolates failures to the page where they occur and lets the rest of the product stay navigable.
- Bundle-size budget enforced in CI. The current index bundle is 321 KB gzipped. The target budget is 350 KB with a CI alert at 340 to catch regressions before they merge, especially as SEO landing pages ship at volume and pull new imports into the shared chunk.
- Real-device mobile testing with session recording. iPhone 12 through 14 and Pixel 7 coverage with a session recording product to validate the tap-target and safe-area work in the field rather than in simulators. This is the primary lift that moves the mobile category past 9.7.
- Real user monitoring for Core Web Vitals and interaction latency. A lightweight RUM pipeline covering LCP, INP, and CLS plus a few app-specific timers (chart paint, grid paint, search-suggest latency) so that the synthetic performance score has an in-the-field companion. This closes the gap between audit-time performance and perceived performance at traffic.
A second tier of items sits on the first-ninety-days list: automated axe-core accessibility run in CI with a ratchet so we cannot merge a regression, a typography-scale lint rule so new off-ladder sites cannot land, a Storybook-style primitives catalog so contributors learn the Button and Modal shapes before writing new ad-hoc components, and a documented error-taxonomy so the ring-buffer payloads get structured tags instead of free-form messages. Useful work, not launch gates.
Bottom line for collectors
If you use HCI to look up a card value or manage a collection, the interface-layer work that touches you most is the error-handling pattern, the mobile polish, the chart-popup paint fix, and the focus-ring pass. Network errors retry silently when they are transient and surface visibly when they are not. Mobile viewport behavior survives iOS keyboard reveal and Android back-swipe. Chart popups paint in a fraction of a second instead of waiting on slow upstream fetches. Keyboard navigation works everywhere, and the focus indicator is visible under both dark and light system themes.
If you are evaluating HCI against another pricing platform, the audit methodology is repeatable. Ask the other platform for their composite score, their category breakdown, their baseline-to-current delta, and their roadmap. If the response is "we care about UX" without numbers, that is a useful signal. See also our security posture writeup which applies the same audit methodology to the sixteen-category security scorecard.
We publish the scorecard, the methodology, and the roadmap because the sports card pricing space historically has not. The companion security audit covers the sixteen-category security scorecard at 9.05 composite, and the independence pledge covers ownership and conflict-of-interest posture.