ParkMarks

An interactive map application to record and track my U.S. National Parks journey

A Map That Holds My National Parks Story

Some trips are easy to forget until a photo pops up years later and pulls you right back in. National parks feel different. They leave behind more than pictures: a trail you hiked in silence, a sunrise you waited for in the cold, and a small detail you did not notice until a second visit. Over the years, I realized I was not simply checking parks off a list. I was building a personal timeline of places that shaped how I travel. ParkMarks started from that idea. I wanted a single, calm place to record which parks I have visited over the years, what those trips meant to me, and which parks are still waiting for a future chapter.

From a Simple Idea to Something I Actually Use

At first, I thought I only needed a checklist. But a checklist does not feel like a journey; it feels like a task. I wanted something more visual, calmer, and closer to how travel memories actually work. So I built ParkMarks with the map as the starting point. The map is not just decoration; it is the main interface. Hovering a state highlights it, and clicking one zooms in and filters the view down to just that state’s parks, with a small pill to jump back out to the full map. Clicking a park marker opens the park details, and that is where the app shifts from map to journal.

A List That Complements the Map

The map is great for exploring by geography, but sometimes I already know what I am looking for. That is why ParkMarks also has a floating parks list panel, opened from a corner pill and layered right on top of the map. It has a search box for finding a park by name or state, three filter tabs — All, Visited, and Wishlist — and a sort toggle that ranks parks by rating, highest or lowest first. The list stays in sync with whatever state I have zoomed into on the map, and selecting a park from either the list or the map opens the same detail view. On a phone held upright, the list opens by default, since the map is mostly a decorative backdrop at that width and I usually want the list first.

What I Wanted the App to Capture

When I look back at a park, I usually remember either the first time I saw it or the way it felt when I returned. That is why ParkMarks supports multiple visits for the same park. A revisit is not a duplicate; it is part of the story. I can also leave a personal rating, not as a score for the park itself, but as a reflection of that particular trip: the weather, the season, the mood, and the people. And because travel planning is part of travel, I can keep parks in a Wishlist, a gentle reminder of what I am drawn to next.

A Progress Dashboard at a Glance

The header doubles as a small dashboard: a visited-count-over-total readout next to a circular progress ring showing my completion percentage across all national parks. It is a quick, ambient way to see how the journey is going without opening the list or scanning the map.

Light and Dark, However You Like It

ParkMarks follows my system’s light/dark preference by default, and a toggle in the header lets me override it — the choice is remembered in localStorage so it sticks between visits.

Designing for Real Life

I wanted ParkMarks to work in the situations where I actually think about parks:

  • Planning on a desktop when I have time to browse
  • Checking details quickly on a phone
  • Sharing a park with a friend using a single link

The layout adapts to different screens. On desktop, the park details open in a persistent right-hand sidebar; on mobile and tablet, they open in a bottom sheet so the map stays visible underneath. I can share a specific park using a URL parameter such as ?park=<slug-or-id>, or a specific state using ?state=<state-id>, so someone can land on the exact park or state I am talking about without extra searching. Browser back and forward also stay in sync with whichever park or state is open.

How I Keep ParkMarks Updated

The frontend stays a static site on GitHub Pages, but the park data itself now lives in a Supabase Postgres table and is fetched at runtime, so I can log a new trip without rebuilding or redeploying the site. There is no in-app write UI or auth by design: I edit the parks table directly in Supabase Studio, which is protected by a read-only Row Level Security policy — only select is allowed, so there is no insert/update/delete path even if the anon key were misused.

src/data/parks.js still exists as the source dataset, but its role has changed: it is the seed source for scripts/seed.mjs, which populates the Supabase table, and it doubles as an offline fallback the app renders from if Supabase is unconfigured or unreachable. In practice, updating the log usually looks like this:

  1. Mark a park as visited when you have been there
  2. Add one or more visit dates (I keep them in a consistent format like YYYY-MMM-DD)
  3. Update a rating to reflect how that visit felt
  4. Optionally swap the image if you want a different memory attached to the park

Here is a simplified example of what a single park entry can look like:

{
  id: 1,
  name: "Yellowstone",
  fullName: "Yellowstone National Park",
  state: "Wyoming / Montana / Idaho",
  lat: 44.6,
  lon: -110.5,
  established: "1872-Mar-01",
  visited: true,
  dates: ["2025-May-31", "2025-Jun-01", "2025-Jun-02", "2025-Jun-03"],
  rating: 4.5,
  image: "images/yellowstone.jpg",
  desc: "A U.S. national park located in Wyoming, Montana, and Idaho...",
}

If I add my own photos, I make sure the image path matches where the file is stored, under public/images/ in the project. If I ever change the dataset in a way that should also change the fallback, I keep src/data/parks.js in sync and re-run the seed script so the table matches it again.

The Tech Behind the Experience

ParkMarks pairs a static front end with a lightweight managed backend, so it stays simple to run while no longer being limited to data baked into the build.

  • React 19 drives the UI and interactions
  • Vite 7 keeps development and builds snappy
  • Tailwind CSS, loaded via CDN with a class-based dark mode strategy, styles the app
  • Supabase hosts the park data in Postgres and exposes it through its auto-generated REST API, gated by a read-only RLS policy
  • Lucide React provides lightweight icons
  • ESLint helps keep the codebase consistent

Why ParkMarks?

Because every park deserves a mark, not just a checkbox, and every visit becomes part of a larger story. A mark that says: I was here, this mattered, I want to return, I am not done yet. ParkMarks is my companion for that kind of journey, one park at a time.

Notes and Attribution

  1. The base SVG map is sourced from amCharts SVG Maps.
  2. Park coordinates and basic park information are derived from public National Parks datasets.