
Hopscotch
A digital loyalty card wallet. Invisible until the moment it matters.
A plastic loyalty card is a solved problem until you forget it or lose it. Hopscotch puts your cards on your phone, your watch, your lock screen, and your digital wallet — then uses your location to surface the right one automatically.
The guiding principle is a zero-friction, integrated experience — after setup, a user should never have to think about the app. It just appears the moment they need it. Hopscotch spans both iOS and Android, and works on the surface a user prefers: phone, watch, widget, or wallet.
3 weeks
Field observation to shipped
2 platforms
iOS + Android
8 surfaces
Phone, watch, wallet, widget — on each
1 + 11
Human + AI agent team
“I used Stocard for years — until Klarna bought it and put everything I relied on behind a password wall that made me the slow person at the door. I wanted something that worked like a plastic key card: always with me, no friction, no app to find, no account to remember.”
My role
Product lead and sole designer. Took Hopscotch from a field observation to shipping across every surface — directing an 11-agent AI team across engineering, marketing, legal, and design exploration.
Where judgment was required
The moments that shaped the product.
A first-person view at a gym front desk: a phone held up, barcode on screen, the staff member leaning forward with a curious expression. Caption overlay: 'What is that?' Composition frames the moment as the origin of the business model, not just a nice anecdote.
The product pivoted when the cashier looked up
Product-market fit signals aren't always quantitative. When gym staff interrupted a scan to ask 'wait, what is that?' — that was the moment I stopped treating it as a personal tool and started treating it as a product. If the simplicity read from across a counter — to people who scan barcodes all day — the problem was shared, not just mine. That reaction is what led me to contact the gym about partnering, and to reframe the consumer app as the front end of a two-sided marketplace.
A split composition. Left: an overflowing email inbox, muted, tagged 'lost in noise.' Right: a phone on a counter showing Hopscotch's discovery feed with a featured local business promo at the top. Caption beneath: 'The channel small brands couldn't buy anywhere else.'
Email is cluttered. Hopscotch is a direct line to customers who already walked in.
Small businesses can't outbid big brands in the inbox. Hopscotch gives them a channel the incumbents don't have — the moment a customer is already at the door. One included promo push per month turns a punch card into a direct-to-customer channel at $19/month — 58% under Square Loyalty, with no hardware and no POS integration. Featured placement ($49/month, capped at 3–5 per geographic area for scarcity) is the leveling mechanism: a local coffee shop can buy prime discovery real estate that a national chain would only get through a full ad campaign. The business model is the design decision.
A two-sided marketplace diagram. Left side labeled 'Consumer' — free, no account, no tracking — with a thick arrow pointing to the center. Right side labeled 'Business' — $19/month, punch program, promo push — with an arrow pointing to the center. The center is a single node labeled 'Hopscotch.' Annotation beneath: 'The free side is what makes the paid side work.'
The consumer app is free forever. That's not generosity — it's strategy.
Consumer adoption is the supply side of the marketplace. Any friction to consumer adoption — a paywall, an account requirement, ad tracking — is friction to business value. The business side pays $19/month precisely because the consumer side is frictionless and trustworthy. Pricing, privacy, and positioning are the same decision.
A three-screen flow on iPhone. Screen 1: explanation screen ('Location is used only to surface your card at the right moment'). Screen 2: iOS system location permission prompt. Screen 3: 'Always Allow' escalation with the 'Save Anyway' escape hatch button visible below. Connected by arrows. The escape hatch is annotated.
iOS gives you one shot at the system prompt. I built three stages.
iOS gives you one shot at the system location prompt. I built a three-stage escalation: explain -> request When In Use -> request Always. The 'Save Anyway' escape hatch means users who decline location access aren't blocked from the core product. A user who chooses 'When In Use only' can still save a geofence — the app works with what it has and tells the user exactly what they're getting. The system never punishes a cautious permission choice.
The circular complication at 40pt in three states side by side. State 1: default — outline icon on teal circle. State 2: nearby — outline icon, category label visible. State 3: geofence active — filled icon, teal ring border. Shown on an actual Watch face background.
Watch complications: outline = available, filled = relevant now
A 40pt circular complication has room for exactly one piece of information. The outline/filled distinction uses Apple's own visual grammar to encode two states: the card exists, or the card is relevant right now. When you enter a geofenced location, the complication fills and earns a relevance boost in Smart Stack. The right card surfaces without scrolling. One binary, correctly chosen, does the work of a notification without interrupting anyone.
Two side-by-side Watch screenshots in the barcode detail view. Left: dark mode — barcode bars rendered but hard to read, scanner icon in red indicating failure. Right: forced light mode — dark bars on white background, scanner icon in green. Caption: 'Functional requirement, not aesthetic choice.'
Dark mode aesthetics yield to a physical constraint. Every time.
Barcode scanners require dark bars on a light background. The Watch barcode view forces .colorScheme(.light) regardless of the user's Watch face setting. This is a functional requirement, not a preference. The design decision to sacrifice dark mode aesthetics for functional reliability was made once and applied consistently across every surface that renders a scannable barcode. Platform constraint as a design clarifier.
Three widget sizes — small, medium, large — displayed side by side on a home screen background. Each has consistent visual padding despite different system margin baselines. A small code annotation below: max(0, targetPadding - systemMargins.top). The visual shows consistency; the annotation explains the mechanism.
I wrote one formula. It works on every device Apple hasn't shipped yet.
Instead of hardcoding padding values, the formula reads system-provided margins (which vary by widget size and device) and adds only the delta needed. If Apple ships new widget sizes, the padding adapts automatically without a code change. This is the correct response to a variable design constraint: encode the principle, not the current values. The design doesn't need a maintainer every time hardware changes.
Process
Paper punch card at a coffee shop — a market failure hiding as nostalgia.
Zero-dependency stack: SwiftData, CoreLocation, PassKit, WatchConnectivity. Apple frameworks only.
4 phases: card management -> geofencing -> Watch companion -> widgets.
Two platforms, eight surfaces. One design system. Zero dependencies at submit.
What Shipped
2
Platforms
8
Surfaces
0
Dependencies
7
Barcode formats
iOS and Android, each across phone, watch, wallet, and widget — eight surfaces in total, built from zero third-party dependencies. In beta now. Business-side marketplace platform in active development. Seven barcode formats supported.
- Two platforms, eight surfaces: iOS and Android, each with phone, watch, wallet, and widget
- Zero third-party dependencies — runs entirely on first-party platform frameworks
- Seven barcode formats supported, four Watch complication families
- Two-sided marketplace business model active: consumer app ships, business platform in development
What I Learned
Designing for three surfaces teaches you something that designing for one surface can't: the same design principle produces completely different solutions depending on what the surface can do. 'The right card at the right time' is the same principle on iPhone, Watch, and Wallet. On iPhone it's a geofence notification. On Watch it's a complication that fills when you're nearby. On Wallet it's the actual barcode on your lock screen. Same principle, three different interaction models. Flattening those differences — making the Watch app a shrunken phone app — would have violated the principle it was trying to serve. The more uncomfortable lesson: complexity is not the enemy of good UX. Hidden complexity is. The barcode format picker is genuinely hard. Pretending otherwise produces an app that fails silently. Making the complexity navigable — surfacing the auto-detected answer, showing all options with real previews, keeping the recommendation visible after the user explores — is what 'designed' actually means.
What this demonstrates