A one-page bilingual landing for a tow-truck service covering Kyiv and the surrounding region around the clock, built to load instantly on a phone at the roadside and to cost almost nothing to run.
- Client
- Den Evakuator, a 24/7 tow-truck service in Kyiv
- My role
- Design and development, end to end
- Period
- 2026

Project Overview
Everything about this site is shaped by where it gets read: at the side of a road, on a phone, by someone whose car has just stopped. That rules out a great deal. It has to arrive fast on a bad connection, the phone number has to be reachable without scrolling, and nothing may stand between the visitor and a call - no form to fill in, no account, no consent wall over the one button that matters. So it is a single page in two languages with no database behind it, and the design work went into removing things rather than adding them. The one place I spent effort instead of saving it was the coverage map, because that is the question a stranger actually has: do you come out to where I am.
The project in numbers
- Coverage map
An embedded map, a key and a billing account
to3 KB of inline SVG
The section only ever needed to answer one question - do you come to me. An embedded map answers it with roads and labels nobody asked for, an API key, a billing account and an external request on every pageview. The Kyiv region and city outlines are drawn from OpenStreetMap data instead: simplified to 190 and 69 points, coloured by the theme's own tokens, and sharp on any screen.
- Client-side JavaScript
Two islands
Conversion tracking, as a single delegated click listener for the whole page, and the photo lightbox - which earns its place because arrow keys and Escape cannot be expressed in markup. Everything the mockup held in React state is markup here: the language switch is two links, the FAQ is a native details element, and the responsive behaviour is media queries rather than a width measured in JavaScript.
- Pages
One, in two languages
Ukrainian and Russian, a single route and a single sitemap entry. A tow-truck service is one decision - call or do not call - and splitting that across five pages only adds places to lose the visitor.
- Runtime dependencies
Four
next, next-intl, react and react-dom. Tailwind is a build-time tool and ships no runtime of its own.
- Data stored about visitors
None
No database, no accounts, no forms on the page at all: the calls to action are the phone and the messengers the owner already uses, so there is nothing to store, secure or lose.
Challenges
- Loading fast on a phone with one bar of signal, for someone who is already having a bad day.
- Showing a service area without an embedded map, its API key, its billing account and its external request on every pageview.
- Serving Ukrainian and Russian equally without doubling the work of every edit.
- Keeping a landing page from turning into a five-page site with the phone number three clicks deep.
- Baking analytics identifiers into pages that are fully static, where there is no server left at runtime to inject them.
Solutions
- Built the whole site as one static page in Next.js 16 with next-intl and Tailwind v4, Ukrainian by default and Russian under /ru, with a single route and a single sitemap entry.
- Drew the coverage map as inline SVG from OpenStreetMap boundary data, simplified with Douglas-Peucker to 190 points for the region and 69 for the city, on an equirectangular projection corrected for latitude so no trigonometry runs at display time. Kyiv is a hole in the region polygon, so it is a second path drawn over the first.
- Credited the map data in the footer under ODbL, in the form OpenStreetMap asks for, and documented in the source that the credit stays as long as the map does.
- Held the client-side JavaScript to two islands and solved the rest with markup: two links for the locale switch, a native details element for the FAQ, media queries for the responsive behaviour.
- Passed finished strings into the client components instead of the translation function, so the dictionary never reaches the browser bundle.
- Rendered the gallery grid as server HTML and only the opened photo at full size, so a fourteen-image gallery costs nothing until someone actually opens one.
- Moved the analytics identifier into a Docker build argument, since a fully static page has its counters baked in at build time and env files are deliberately not copied into the image.
- Wired the Telegram lead pipeline and the conversion-tracking attributes ahead of the features that will use them, so adding a form or switching ads on is a configuration change rather than a rebuild of the page.
Key Features
- One Page, Two Languages - Ukrainian and Russian, a single route, no navigation to get lost in.
- Inline Coverage Map - The Kyiv region and city drawn from OpenStreetMap data as 3 KB of themeable SVG.
- Call-First - Phone and messengers as the only calls to action, with no form standing in the way.
- Photo Gallery - Server-rendered thumbnails with a keyboard-navigable lightbox.
- Native FAQ - A details-element accordion that closes its siblings without a line of JavaScript.
- Conversion Tracking - One delegated listener for the whole page, with the markup already in place for ads.
- Static by Design - No database, no runtime server work, nothing stored about anyone.
- Push to Deploy - Docker Compose on a self-hosted box, released through GitHub Actions.
Project Info
Technologies
Frontend
Translations
Messaging
Security
Hosting & Deployment
DevOps & CI/CD
Monitoring & Analytics
Need something like this?
Tell me what you are building and where it is stuck. I will tell you plainly whether it is work I should be doing.