Stroll
Stroll is a site for guided walks in a real city: walking-tour companies, independent guides, tourist information centres and cities. Its maps are the real city, drawn from OpenStreetMap in an illustrated 3D style by a command in the template, and every walk’s route follows the real streets. On the home page the walk draws itself as you scroll, the camera flying from sight to sight. The demo walks through Prague. It runs from edge to edge of the window.
Stroll comes in two variants, chosen when you create the site: files/Sanity (static pages, any
host) and EmDash (--cms emdash, rendered on request on Cloudflare). The
pages are the same in both.
Your city
Section titled “Your city”The template draws the map itself, so it can be any city:
- Set the area in
map.config.json:areawith its south, west, north and east edges in degrees (right-click on openstreetmap.org → “Show address” gives you a point’s latitude and longitude). About 2-3 km across works best. - Run
npm run map -- fetch. It downloads the area from OpenStreetMap (one request, up to a minute), builds the map’s geometry insrc/data/map/and draws four base maps intopublic/map/: the city in 3D and as a plan, each by day and at dusk, saved as AVIF in every width the site uses (so building the site never re-encodes them). - Give your sights their latitude and longitude, your walks their route and meeting point. The routes are found along the real streets when the site builds.
The map’s colours are in map.config.json → colors; after changing them, npm run map -- render redraws the maps without downloading again. camera sets how steeply the 3D view looks
down, how tall buildings are drawn, and how many floors a building has when OpenStreetMap doesn’t
know.
Map data is © OpenStreetMap contributors, under the Open Database License. Every map shows the
attribution; keep it. See the template’s LICENSES.md.
Pages and their content
Section titled “Pages and their content”| Page | Edit it in |
|---|---|
| Home | Pages → Home page (a list of sections) |
| Walks, sights, guides, journal, visit, 404 | Pages → the page’s own entry |
| Map labels (districts, the river) | Pages → The map |
| Walks (route, meeting point, tickets) | Content → Walks |
| Sights (with their latitude and longitude) | Content → Sights |
| Guides | Content → Guides |
| Journal posts | Content → Journal |
| Name, description, city, email, address, navigation, header button, profiles | Pages → Site settings |
| Buttons, labels, chat, search, emails, game texts | Content → Interface texts |
| Privacy, terms | Pages → Legal |
In headlines, words wrapped in *stars* are underlined with a dotted gold route, as in “The
whole of old Prague, *one street at a time*”.
Walks and sights
Section titled “Walks and sights”- A walk has a route: the sights in the order you visit them (by their file name), plus waypoints with a latitude and longitude where you want it to take a particular street. The site finds the way between them along the walkable streets, measures the distance and draws it on the maps. It also has a meeting point, duration, times, languages, group size, price, highlights, a picture and a colour.
- Tickets: turn on “Sell tickets with Stripe Checkout” and put the one-time Price id of one
ticket there; the walk’s page gets a working button. Walkers come back to the walks page, and
the Stripe webhook (
/api/stripe/webhook) emails them the meeting point. - Private walks are booked with Cal.com on the walks pages.
- A sight has its kind (the sights page filters by it), a summary, latitude and longitude, when it was built, hours, entry and a picture. Sights are numbered on the maps by their order.
Integrations
Section titled “Integrations”Everything works without keys; each feature switches on with its variables in .env.example.
| Feature | Where | Service |
|---|---|---|
| Tickets | Each walk’s page | Stripe |
| Private walks | Walks pages, loads on click | Cal.com |
| Newsletter | Home and journal pages, with a welcome email | Supabase |
| Contact form | Visit page; messages go to the email in Site settings | Resend |
| AI chat | On every page, answers from your walks (with their routes), sights, guides, visit page and journal | Claude |
| Search | ⌘K or the search button: walks, sights, guides, journal, visit | built in, or Algolia |
| Analytics | Every page; Google Analytics only after consent | Plausible, Umami, Google Analytics |
Sections
Section titled “Sections”The home page is built from these sections, in any order: The walk (the route draws itself as you scroll; pick which walk), Walks, Sights, Guides, Numbers, From the journal, Newsletter sign-up and Closing call (a picture from edge to edge).
Colors and brand
Section titled “Colors and brand”src/content/singletons/brand.json (Pages → Brand in Keystatic) holds three colors: the
accent (buttons and pins), the second (the river: cool fills) and the third (gold:
highlights and the walk’s line on the maps). Buttons are darkened until their white text is
readable, the second and third are lightened until dark text on them is; a color that can’t get
there stops the build with a message. Brand also sets the fonts (text, headings, and the mono font
for labels and distances) and the corners.
Languages
Section titled “Languages”Stroll is ready for more languages: see Languages.
The 404 page
Section titled “The 404 page”A small game, Postcard: a postcard of your city, cut from the 3D map around one of your sights, is cut into tiles and shuffled. Slide the tiles back into place (tap a tile next to the gap, or use the arrow keys). Fewer moves, more points; three postcards, the last one cut finer, and the best score is kept in the visitor’s browser.
AI assistants and search engines
Section titled “AI assistants and search engines”AGENTS.mddescribes the project for coding assistants (Claude Code reads it throughCLAUDE.md, Cursor through.cursor/rules/), including how to draw your city./llms.txtlists your pages, walks, sights and journal for AI assistants, in every language.- Pages carry JSON-LD: the company as a
TravelAgency, walks asTouristTrips with their stops, sights asTouristAttractions with their coordinates, the questions on the visit page, and each post.
Commands
Section titled “Commands”pnpm dev # local site, editor at /keystaticpnpm map # draw your city (map.config.json)pnpm build # production buildpnpm cms sanity schema|seed # move content to Sanity (see Content → Sanity)In the EmDash variant: npm run dev, npm run deploy (Cloudflare) and npm run emdash:schema
(after changing the content model).