Austin Ban - Frontend: Met Collection search app (React + Vite) - #331
Open
austinban wants to merge 6 commits into
Open
Austin Ban - Frontend: Met Collection search app (React + Vite)#331austinban wants to merge 6 commits into
austinban wants to merge 6 commits into
Conversation
A client-side single-page app for searching The Metropolitan Museum of Art's open Collection API. Replaces the old Create React App scaffold with a Vite + React 18 setup. - Two-step data flow: search returns object IDs; details are fetched and paged lazily (load-more), a page of objects in parallel. - Resilient paging: per-object fetch failures are counted and surfaced to the user rather than silently dropped. - Query + department filter mirrored to the URL (two-way), debounced, with stale requests aborted so a slow search can't overwrite a newer one. - Saved "Collection" (localStorage) with CSV export; accessible detail modal. - Light/dark theme, 5-language custom Intl-based i18n, WCAG AA foundations. - Vitest unit tests for the pure helpers (normalize, medium translation, CSV). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
The focus trap filters candidates by offsetParent, which is null for position: fixed elements — so the zoomed-image close button was unreachable by keyboard. Anchor it to a positioned container instead. Co-authored-by: Cursor <cursoragent@cursor.com>
Trims the app back toward the take-home brief: - Remove the i18n system (LocaleProvider, t(), five locale catalogs, the language switcher). Copy is now inlined English at each call site, with small helpers for the pluralized/count strings; result counts still use Intl.NumberFormat. Drops the medium translation table with it. - Remove the "How I built this" essay page and its lazy route (and the now-unused Spinner used only for its Suspense fallback). - Drop "WCAG 2.x Level AA" claims from README, PLAN, and code comments while keeping the actual accessibility work (skip link, focus trap, labeled controls, live regions, focus-visible rings, reduced-motion). Co-authored-by: Cursor <cursoragent@cursor.com>
- Landing/empty state: no default search runs; show an intro plus example searches grouped by category (public-domain-skewed terms) that run a real search when picked. Restore the focus-dropdown quick picks, derived from the same category data. - Search history: push distinct queries as history entries so Back/Forward walk the search history; sync the debounce on navigation so results update without lag; bypass the debounce for explicit suggestion picks. - Rename the app to "Meet the Met". - Lightbox: close on any click (image or outside) and add cursor affordances. - Select: custom caret with proper right padding (native arrow was flush). - Artwork row: move corner clipping to the thumbnail so tooltips aren't cut off. - Reduce page size to 12 to ease API rate limiting. Co-authored-by: Cursor <cursoragent@cursor.com>
- Simplify search URL sync: track the last-written query so the field follows any external navigation (Back/Forward, nav link to the empty state, edited URL) without clobbering typing; drop the POP-only special case. - Model page-load failure as a first-class view state, fixing an error panel that briefly flashed on the happy path; remove the redundant focus dropdown. - Accessibility: dedicated form-control border token meeting WCAG 1.4.11 (3:1); release the underlying dialog's focus trap / aria-modal when the lightbox stacks; sr-only public-domain label on mobile rows; decorative tooltip. - Resilience: one retry/backoff policy in the API layer so rate-limited object fetches aren't silently dropped; simplify useDepartments accordingly. - Migrate localStorage keys to the new name without orphaning saved data. - Softer error copy plus a themed, transparent, size-optimized illustration. - Add hook tests (debounce, paging race, URL sync incl. Back and nav-clear). Co-authored-by: Cursor <cursoragent@cursor.com>
Prefix the header logo and error-state illustration with import.meta.env.BASE_URL so they resolve under a non-root base path (e.g. /interview/ on GitHub Pages). Hardcoded absolute paths aren't rewritten by Vite the way index.html and CSS url() refs are. No effect locally, where BASE_URL is "/". Co-authored-by: Cursor <cursoragent@cursor.com>
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.
Note from the author
I'm sure I went slightly beyond scope for this project, I just started having a really great time with it! I even had to strip back scope I built out such as summary pages, multiple language support, etc... Hoping to lean back into some simplicity to make it easier to review and respect you all's time. Looking forward to hearing back from you all and maybe can work together in the future.
Cheers,
Austin
Summary
A single-page app for the Frontend interview that searches The Metropolitan Museum of Art's open collection (keyless, CORS-enabled public API) and lets you browse, inspect, and save works. Built with React + Vite + Tailwind. All changes are under
frontend/.Highlights:
?q,?dept) as the single source of truth — so Back/Forward, shareable links, and a nav-link back to the empty state all just work. Results page in lazily (12 at a time) and skip individual objects that fail to load rather than failing the whole page.aria-modalare released on the underlying dialog while the lightbox is stacked.localStorage; exportable.aria-liveresult counts, skip link,prefers-reduced-motion, a dedicated form-control border token meeting WCAG 1.4.11 (~3:1), and sr-only public-domain labels where the visible mark is hidden on mobile.Test plan
cd frontend && npm installnpm run dev— search (e.g. "Vermeer"), filter by department, open a work, zoom the image, save/unsave, and export the collection?q=to confirm the field and results stay in syncnpm test(18 tests) andnpm run buildMade with in Cursor using Opus 4.8