Overview
Eon Aligner is a clear-aligner brand with a growing network of certified providers across the Middle East. The brief for this project covered the needs of 2 distinct users: patients trying to find nearby clinics, and a marketing team that needs to manage listings without an in-house developer.
For this project, I was responsible for the entire process end-to-end — from understanding the marketing team’s business requirements and translating them into a technical solution, to designing the UI and overall user experience for the doctor finder. I also developed the React frontend and implemented a headless Airtable CMS to manage doctor listings efficiently.
<a href="https://eon-aligner-doctor-finder.netlify.app/" target="_blank" rel="noopener noreferrer" class="external-site-link group relative inline-flex min-w-0 max-w-full flex-wrap items-center justify-start gap-1.5 rounded-md px-3.5 py-2 text-left text-sm font-medium text-primary no-underline transition-colors duration-300 hover:text-white dark:text-primaryInverted dark:hover:text-white" aria-label="eon-aligner-doctor-finder.netlify.app (opens in new tab)"
eon-aligner-doctor-finder.netlify.app <svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3 shrink-0 fill-current transition-transform duration-200 group-hover:translate-x-px group-hover:-translate-y-px motion-reduce:transform-none sm:h-3.5 sm:w-3.5" viewBox="0 0 256 256" aria-hidden="true" focusable="false"
The Challenge
A doctor finder sounds simple until you hold it to three standards at once: it has to feel intuitive to first-time users, meet accessibility expectations for a healthcare-adjacent audience, and stay operable by a team without engineering support.
Patient Experience First
Visitors needed to find a certified provider in seconds with map context, location-based filtering, and a clear next step to directions. The interface had to feel native to how people already use Google Maps.
Accessibility at the Core
A healthcare-adjacent audience meant accessibility best practices were the absolute minimum that I had to include from the first commit, and not retrofit after launnch.
Full Autonomy for Marketing Team
Adding a new clinic, updating a doctor's hours, or removing a provider couldn't require a developer. The team needed to manage the network end-to-end through tools they already used every day.
The Core Insight
The most valuable thing I could build wasn't the map — it was a system the team could extend without me.
That meant two foundations had to land before any interface work began. The first was a small design system: tokens named by purpose (brand, ink, surface) instead of by color, so the visual language could evolve without renaming every component.
The second was a CMS the marketing team already understood — Airtable — so the patient-facing map became a thin, fast view over data they already owned, and knew how to modify.
Every other decision from the component structure, the accessibility patterns, the serverless proxy to the lazy geocoding flowed downstream from those two.
The win wasn't the React app. It was a design system any future contributor could read, and the absence of a developer in the loop every time a new clinic opened.
The Approach
Three decisions shaped the build:
A purpose-named design system as the foundation. Before writing a single component, I established a token layer in Tailwind v4: semantic color names (brand, accent, surface, ink), a typographic scale, a radius scale, and a single source of truth for the brand. Components were built against tokens, not hex values — so a future palette change is a five-line CSS edit, not a search-and-replace across the codebase.
Accessibility designed in, not bolted on. Semantic HTML (<main>, <section>, <nav>, <ul>/<li>), proper heading hierarchy, ARIA states on interactive elements, a skip-to-content link, visible focus styles, and WCAG AA contrast across the full UI. Every component was built with keyboard and screen-reader use as a primary path, not a fallback.
Airtable as the headless CMS. The marketing team already worked in spreadsheets. Rather than build a custom admin panel they'd have to learn, I made Airtable the source of truth. New columns, new providers, new countries — all managed through an interface they were already fluent in.
Interactions tuned to intent. Instead of bolting on filters and toggles, I focused on making the map feel right — zoom levels that match what the user just selected, geocoding that loads only when needed, filter dropdowns that depend on each other so the user can't get lost.
Design System & Technical Architecture
A Semantic Token Layer
The design system is small on purpose. Eleven color tokens, one font, four radius steps — all defined in CSS using Tailwind v4's @theme directive and named for the role they play in the UI, not the color they currently are. A teammate adding a new component asks "is this a surface or a muted surface?" — that's a meaningful design decision. Asking "is this white or off-white?" is just guessing. The naming makes intent legible at the point of use and makes a future dark-mode pass a single-file change.
Accessibility as a First-Class Concern
Every surface in the app meets WCAG AA contrast (4.5:1 for body text, 3:1 for large text and UI), verified token by token. Interactive elements use semantic HTML — doctor cards are <button> elements inside <li>s with aria-pressed to communicate selection state, not styled <div>s. Loading states are wrapped in role="status" live regions; error states use role="alert". A visible-on-focus skip link gets keyboard users straight to the main content. The result: the app is usable in full with a keyboard alone or a screen reader alone.
Context-Aware Map Camera
The map responds to what the user is doing through a priority-based zoom system:
- Selecting a doctor zooms to street level (15) and centers on their clinic
- Selecting a city zooms to neighborhood level (12), centered on the available providers in that city
- Selecting only a country calculates the geographic centroid by averaging all doctor coordinates in that country, then zooms to regional level (6)
The result: the user always sees relevant context, regardless of how they navigate.
Hybrid Geocoding Cache
Converting lat/lng to a human-readable address is a paid Google API call. To minimize calls and keep the UI snappy, the app uses a two-tier cache: an in-memory Map for session-fast lookups, persisted to localStorage so addresses survive across visits. Geocoding is also lazy — it only runs when a user opens a doctor's info window.
Secure API Layer
The Airtable API key never touches the browser. A Netlify Function sits between the React app and Airtable, handles authentication and pagination, and returns sanitized data with a 5-minute cache window. The whole backend is one file, with no infrastructure for the team to maintain.
Tech Stack
Frontend
React 19, Vite, Tailwind CSS v4
Design System
Custom token layer (colors, type, radii) in Tailwind @theme
Maps
@vis.gl/react-google-maps — Advanced Markers and Geocoding
Backend / CMS
Airtable as a no-code database, proxied through a Netlify Function
Key Design Decisions
Tokens Named by Purpose, Not by Color
A class like bg-surface tells the next developer what it is (a card background). bg-white only tells them the color. That distinction is what separates a palette from a design system — and it's what makes a future theming change a tractable problem instead of a refactor.
WCAG-Compliant From the First Token
Contrast was a constraint applied to the token system, not checked after the fact. When the brand teal failed AA against white, the fix was a system-level swap — brand-ink became dark — so every CTA across the app inherited the correction automatically. Accessibility lives in the tokens, not in patches.
Airtable Over a Custom Admin
A bespoke CMS would have been a project of its own — and another tool for marketing to learn. Choosing Airtable meant the team's existing spreadsheet fluency became their CMS skill set on day one. Adding a clinic is the same as adding a row.
Map Behavior Tuned to Intent
A map that always zooms the same way fails its users. By tying zoom level to what the user just selected — a doctor, a city, or a country — the camera always lands on something useful. Small detail; large effect on how the app feels to use.
Outcome
A live, production tool the marketing team operates independently, on a frontend foundation any future contributor can extend.
Zero Developer Bottleneck
Marketing adds, edits, and removes providers in Airtable — changes show up live without a deployment, a ticket, or a developer.
A Foundation That Scales
A semantic token layer and accessibility-first components mean future work — new countries, dark mode, brand refresh — is incremental, not a rebuild.
Project Images
Initial Map View — Clinics Across the Network
Cascading Country and City Filters
Reflection
The interesting work on this project wasn't the React app. It was figuring out how the marketing team's business requirements translate into a business solution.
A "doctor finder" sounds like a frontend project. But the team's real problem wasn't a missing interface, it was that every clinic update required a developer. Once that's the framing, the answer stops being "build a better UI" and starts being "remove the developer from the loop." That meant Airtable as a headless CMS instead of a custom admin, semantic tokens instead of hardcoded values, and a small focused system instead of a heavier framework.
Three weeks of work, scoped to what the team actually needed. Not bigger, not smaller. That's the part I'm proudest of: judgment about where to draw the boundaries and not creating an over-engineered solution.