Skip to content

Junction

Junction is a site for journeys along a route: a scenic or heritage railway, a tour operator, a tourist region or trail, a boat or bus tour, a festival or conference with a programme. It’s made like a model railway: a panorama the train crosses as you scroll, the stations down a track with a little train riding past them, journeys sold as tickets, a departure board and a visit page with the line on a map. It runs from edge to edge of the window.

Junction 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.

Page Edit it in
Home Pages → Home page (a list of sections)
Journeys, timetable, journal, visit, 404 Pages → the page’s own entry
Journeys (what you sell, with the route) Content → Journeys
Timetable (days and departures) Content → Timetable
Journal posts Content → Journal
Name, description, email, address, the line (name, length, since), navigation, header button, profiles Pages → Site settings
Buttons, labels, the departure board, chat, search, emails, game texts Content → Interface texts
Privacy, terms Pages → Legal

In headlines, words wrapped in *stars* get a brass lining, as in “All aboard for the *valley*”.

The home page opens with the departure: a wide panorama (about 7:3) that holds still while you scroll through it, while the train runs along the track from the platform and out at the right; its wheels turn and smoke drifts back. The next departures from the timetable show on a board beside the headline.

The line lists your stations in order (two to seven), each with its nameboard, distance, a title, a few lines and a photo; a small train rides down the track in the middle of the screen as they pass, and each station’s signal drops to clear. Both are CSS only; visitors who prefer reduced motion see the train waiting at the platform.

The train, track, signals and nameboards are drawn by the template in your brand’s colours, so they follow every palette.

Journeys marked Featured come first, then by Order. Each has a tagline, summary, duration, fare (“£24”) and what it’s for (“adult return”), when it runs, the route (stops with times and an optional note, drawn down a track on its page), what’s included, a photo and a colour.

To sell tickets online, turn on Tickets → Sell tickets with Stripe Checkout and add a one-time Stripe Price id: the journey’s page shows a button that takes the payment with Stripe Checkout and brings the customer back to the journey. Until your Stripe key and the Price id are set, the page says what’s missing. After paying, the Stripe webhook (/api/stripe/webhook) emails the customer; add your own steps there, such as your seat plan.

Each running day is an entry: a label (“Saturday 18 October”), a date that sorts the days, an optional special day (“Steam Gala”) and note, and its departures (time, journey, station, platform, and a status: on time, few seats, full or special). The home page shows the first days on a departure board, the timetable page all of them. Remove days that have passed, or mark them as drafts.

Everything works without keys; each feature switches on with its variables in .env.example.

Feature Where Service
Tickets Journey pages with tickets turned on Stripe
Groups and private hire Visit page, loads on click Cal.com
Contact form Visit page; messages go to the email in Site settings Resend
Newsletter Home and journal pages, with a welcome email Supabase
AI chat On every page, answers from your journeys, timetable, visit page and journal Claude
Search ⌘K or the search button: journeys, timetable, journal, visit built in, or Algolia
Analytics Every page; Google Analytics only after consent Plausible, Umami, Google Analytics

The home page is built from these sections, in any order: The departure, The line, Journeys, Timetable (the next days), Numbers, Latest from the journal, Newsletter sign-up and Closing call (a photo from edge to edge).

src/content/singletons/brand.json (Pages → Brand in Keystatic) holds three colors: the accent is the livery (buttons, the train, the track, nameboards), the second the brass lining (tickets, highlights, the departure board), the third the painted sky (soft fills, the train’s windows). Buttons are darkened until their cream text is readable, the second and third are lightened until ink text on them is; a color that can’t get there stops the build with a message. The cream baseboard and the night run (deep blue, for the dark theme and the departure board) are derived from them. Brand also sets the fonts (text, headings, and the mono font for times and boards) and the corners (rounded like enamel signs, or square).

Junction is ready for more languages: see Languages.

A small game, Switch the points: trains in your brand’s colours leave the yard one by one; tap a junction (or press 1, 2 or 3) to throw its points so each train reaches the platform of its own colour. Three wrong platforms and the signal box closes; the best score is kept in the visitor’s browser.

  • AGENTS.md describes the project for coding assistants (Claude Code reads it through CLAUDE.md, Cursor through .cursor/rules/), including what makes the look.
  • /llms.txt lists your pages, journeys and journal for AI assistants, in every language.
  • Pages carry JSON-LD: the line as a TouristAttraction, each journey as a TouristTrip with its stops and fare, the questions on the visit page, and each post.
Terminal window
pnpm dev # local site, editor at /keystatic
pnpm build # production build
pnpm 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).