0%
John Hurley
Book Intro

Case Study

Closing the Trust Gap

OpenGov 311 was losing $676k in deals. One competitive audit changed the company's iOS and Android strategy.

Role

Product Design Engineer & User Researcher

Timeframe

February – June 2026

in addressable product-gap revenue

$676k

A woman checking her phone outside her home

311 is the front door to city government — the app residents open to report a pothole, flag graffiti, or ask why the trash wasn't picked up. When OpenGov's 311 product started losing competitive deals, the easy explanation was pricing. The data said otherwise: it was a short, specific, fixable list of product gaps that nobody had connected to a design roadmap.

Reading through the sales and product-analytics data surfaced a pattern sales hadn't named out loud: a multi-million-dollar slice of closed-lost deals referenced 311 directly in the loss reason, and a meaningful chunk of that was traceable to specific, recurring, nameable functionality gaps rather than one-off objections. Layered on top of that was a much larger list of existing customers already running adjacent OpenGov products with no 311 attached — the biggest untapped cross-sell opportunity in the portfolio, sitting there unaddressed because the roadmap had no instrumentation connecting sales losses to product decisions. Post-sale, the internal tracker showed zero recorded gap votes for 311 — a real blind spot on churn risk that nobody had noticed because nobody was looking.

That reframed the assignment. This wasn't “add some features” — it was find out exactly which gaps were costing closed deals, which were blocking cross-sell, and which were invisible friction with no instrumentation at all, then build the case, the architecture, and the shipped product to close them.

01Turning “missing functionality” into a named list

A 26-product competitive audit, triangulated against unfiltered public sentiment.

The sales data said “missing functionality.” That's not something a design team can act on. So the first move was to build a feature-by-feature audit across 26 products — 25 competitor 311 platforms spanning cities from Boston and Philadelphia to Los Angeles, Chicago, and San Francisco, plus OpenGov's own web proof-of-concept as the baseline — scored Y/N and color-coded across eight core capabilities: city news feed, frequently reported issues, anonymous posting, events calendar, map view and watch areas, notifications and following, submit/upload flows, and status tracking.

That matrix turned a vague loss reason into nine named, prioritized features: commenting, upvoting, threads and replies, an issues feed, map view, content moderation, watch areas, notifications, and issue following. In other words, the audit didn't just say OpenGov was behind — it said behind on exactly what, ranked by how often competitors shipped it and how often it showed up in lost deals.

To keep that list honest, I ran it against a second, independent read: sales data is internal and self-interested, so I scraped and thematically analyzed unfiltered public sentiment — App Store and Google Play reviews, Reddit threads, social comments, and news coverage of 311 apps — using manual and AI-assisted thematic analysis run in parallel and checked against each other for convergence. The same participatory gaps kept surfacing, alongside something the sales data couldn't see at all:

“I notice there is a much faster response in rich neighborhoods versus poorer ones. That's not right since every resident deserves fair and equal access to city resources.”— SF311 review, Apple App Store

That wasn't an isolated complaint. Independent published research backed it up: an analysis of roughly 466,000 Philadelphia 311 requests found lower-income zip codes waiting substantially longer than higher-income ones for the same complaint types; a Health Affairs study of Boston housing-inspection requests found a statistically adjusted gap tied to neighborhood racial composition; and a decade of DC data showed early response-time disparities by neighborhood demographics. It's a perception and equity problem that's structurally invisible to CRM data, because nobody files a ticket titled “my neighborhood waits longer.” The response wasn't a defensive metric buried in a dashboard — it became the design rationale for a public, checkable map view, one of the nine named features, so residents can see response patterns for themselves rather than take the city's word for it.

OpenGov 311 screens showing commenting, community reporting, and AI-assisted content moderation
OpenGov 311 screens showing commenting, community reporting, and AI-assisted content moderation
Three of the nine audited-and-prioritized features, shipped: commenting, community reporting, and AI-assisted content moderation.

02One button, two experiences

The architecture decision that had to happen before any screen got designed.

Before any of those nine features could be designed, one platform decision would determine whether every feature after it actually worked: build the native app as a Capacitor/WebView wrapper around the existing web experience, or as React Native. This wasn't an aesthetic preference — it was a compliance and equity question with a real deadline attached. Municipal apps serving 50,000+ residents face an ADA Title II compliance deadline (WCAG 2.1 AA) that had just been pushed to April 2027 via a DOJ interim rule; ADA digital lawsuits topped 4,000 in a single recent year, with live precedent — DOJ v. State of Alaska, over inaccessible voting technology — for exactly this category of government service; and over 90% of people who use assistive technology for a disability rely on a mobile screen reader.

To make the decision concretely rather than architecturally, I traced what a blind resident actually experiences tapping “Report a pothole” to expand a form section — the same interaction, built two different ways.

OpenGov 311 reporting flow for selecting a request type, pinning a location, and submitting details
OpenGov 311 reporting flow for selecting a request type, pinning a location, and submitting details
The reporting flow used as the accessibility test case — set request type, pin location, then submit details.

Capacitor / WebView — ARIA

  • Often announces stale state
  • Name resolution unreliable
  • Focus stays on the old element
  • Can't cross the WebView boundary

React Native — native accessibility

  • Synchronous via UIAccessibility
  • Matches accessibilityLabel directly
  • Moves focus to the first new field
  • Auditable with Axe DevTools Mobile, Accessibility Inspector
Comparison of WebView ARIA and React Native accessibility trees
Comparison of WebView ARIA and React Native accessibility trees
Same button, two accessibility trees: ARIA attributes translated through a WebView versus native accessibility props read directly by the OS.

Five concrete failure modes, evaluated against Section 508 and WCAG 2.2, made the call for us. Announcement timing: ARIA state changes are asynchronous, so VoiceOver frequently announces “collapsed” even after a section has visibly opened — the resident has no idea their tap registered. Gesture conflicts: VoiceOver's two-finger scroll competes with the WebView's own touch handler, so the page scrolls but the screen reader's reading position doesn't follow it. Role fidelity: role="button" is a suggestion to the browser; accessibilityRole="button" is a direct instruction to the OS's accessibility API, which is the difference between Voice Control reliably finding a button and not. Focus after DOM changes: native apps move focus to newly revealed content automatically; in a WebView, focus just stays put and new fields are invisible to the screen reader. And there's no audit path — tools like axe and Accessibility Inspector can't reliably cross the WebView boundary, so these issues stay hidden until a real screen-reader user hits them in production.

Rather than let a hard deadline force native accessibility work under pressure later, the sequencing was deliberate: ship the web app first, which covers mobile browser users — including VoiceOver on Safari — immediately, and use that runway to build native properly.

03A bifurcated component strategy, not a shared one

React Native still leaves the question open: what is a button, a tab bar, a modal — on each platform?

Choosing React Native solves the ARIA problem, but it doesn't automatically solve the deeper one. The default, fastest path with React Native is to ship one generic, cross-platform component set everywhere — which is precisely the trap the original proof-of-concept had already fallen into with its WebView-era patterns: plain checkboxes, a stock cancel/apply modal, no platform-native treatment, because that's whatever the cross-platform template ships with by default.

Instead, I developed a bifurcated component strategy: rather than one shared visual layer skinned two ways, specific components were deliberately built per platform — iOS pieces built to native UIKit/Swift conventions, Android pieces built to Material Design conventions — so each platform's version of a control inherits that platform's real accessibility behavior, gesture handling, and focus order instead of approximating it. The bottom navigation bar is the clearest example: rather than a shared custom tab bar skinned to look native, it's built against each platform's actual native tab-bar pattern, which is exactly what lets VoiceOver and TalkBack treat it as a first-class navigation landmark rather than a generic list of touch targets.

Comparison of generic web controls and platform-native mobile components
Comparison of generic web controls and platform-native mobile components
The proof-of-concept's generic web chrome versus the updated design's native-mapped components — including the platform-native navigation bar called out at bottom.

This is the part of the project that closes the loop between the accessibility research and the actual product: the five WebView failure modes above are exactly the failure modes a lowest-common-denominator component library would have recreated inside React Native. Mapping components to each platform's native conventions — rather than sharing one design system wholesale — was what actually delivered on the promise React Native made.

04Impact

26competitor products audited
9features named, prioritized, and shipped
5accessibility failure modes that decided the architecture
Org-wideReact Native adopted as the native app standard

Modeled against an already-signed, conservative pipeline, the first-phase build case penciled out to a six-figure investment against a return that cleared the initial cost within the first year — enough to move this from “nice to have” to funded. The bigger number was the untapped one: well over a thousand existing accounts running adjacent OpenGov products with no 311 attached, a cross-sell opportunity that stays closed until the named gaps do.

This is the shape of it. The full case study goes deeper into the complete 26-product feature matrix, the manual-versus-AI sentiment analysis corpus, and the platform-by-platform component mapping behind the design system — happy to walk through it in detail.