How I Designed a City Repair Tracker in a Weekend

How I designed a single map for everyone at HackYeah 2026.

HackYeah 2026 is a wrap. It's the biggest stationary hackathon in Europe, which in practice means three thousand participants, one Tauron Arena in Kraków, a lot of laptops, and a sleep schedule nobody should be proud of. Our category alone had over a hundred teams, and somewhere among them, there we were...

What we came up with is mRadar, a map of city faults in Kraków. You report a problem by talking to it or by dragging a pin. The app groups similar reports, an operator approves a ticket, and the right city unit gets it. Meanwhile everyone can watch the repair move along on the map.

A power outage, and the exact building it's in, lit up so nobody has to guess.

The problem

Have you ever seen a broken window at a bus stop? Did you know where to report it? Well, I didn't. Turns out reports reach the city through several channels, or not at all. Nobody can see what's already been reported, so the same broken window gets reported over and over. Someone matches the reports up by hand, works out whose window it is, the unit hears about it late, and the people who reported it never hear anything back.

So we ended up with three screens: a public map for residents, a panel for the city's operators and an inbox for city units. My job was, as always, designing the product and building the frontend. Now, how do you let three groups of people with very different jobs look at the same broken window without building three separate products?

The rules I set

My brief for the look fit in one sentence: neutral, small corners, very little shadow, polished, and please, not much blue. A city map is busy enough already. Anything decorative would just be one more thing standing between you and the actual problem. There is a reason why management apps are boring. Who wants gradients in Jira?

Two rules survived the whole weekend, which at a hackathon is a small miracle. Color only shows up where it means something: categories, status and the heatmap. And every screen floats over the same map and reuses the same detail panel, so a broken window looks like the same broken window to everyone.

The visual system

The interface is plain gray, by design, so the only thing you notice is the only thing that matters: the reports! The main button is near-black like every other control, so nothing shouts until it really has to. A gray app with a black button, how very 2026 of me. But on top of a city map, anything with an opinion becomes noise.

One sweep, one line, one dot. The dot gets to be the only color, just like in the app.

I recolored the base map into the same grays and gave main roads a faint warm tint, so you can still find your way home. The heatmap uses a single rose color that gets deeper where reports pile up. One color reads as "a lot happening here". A green-to-red scale would read as "panic", and it would fight with the category colors anyway.

A gray city, so the problems are the only thing that stands out.

Each of the eight categories, from power to air and noise, has its own color and icon, and the two always travel together. Give it a minute and you start reading the city by color.

Same color, same icon, wherever a category shows up.

The resident map

Zoomed out, you see the heatmap. Zoom in and it fades away, and each incident becomes a marker that grows with the number of people affected. The building with the problem lights up in its category color as a 3D block. If it's a road problem, a short stretch of the road lights up instead.

A glowing building says “power outage” much better than a pin does.

Click a marker and the panel opens, with a progress timeline and the most important button in the app: "I'm affected too". It turns what would have been report number twelve into a +1 on the one that already exists.

Dark mode, for reporting burst pipes at 2 am. The map goes dark with the panels.

On phones the panel turns into a bottom sheet, and the controls ride up with it so your thumb can still reach them.

Same controls on a phone, just closer to your thumb.

Reporting

Report gives you two ways in: talk to it, or fill in a form. The form starts with your location, because that's the part everyone gets wrong. You move the map under a fixed pin, then pick a category and say what's wrong. No account, and no address field. The pin already answered that.

Pick a way in, drop the pin, say what's broken. Done before the bus arrives.

The voice button, powered by ElevenLabs, reminds you that real emergencies still go to 112. We are not the emergency services. While you talk, the assistant fills the form for you, shows the address it heard on the map and asks if it got it right, which beats reading a street name back to you and hoping.

One button, and the map stays in view while you talk.

The operator panel

An operator's day is basically a to-do list, so their screen is a queue next to the same map. It's Jira, except every bug is physically somewhere in Kraków. Every row has the same shape: a title, the category icon, what needs doing and how long it's been waiting. Urgent rows swap the icon for a red warning, so they're hard to miss.

The queue does the talking, the map gives the context.

On the map, a dashed circle shows the area the system used to group reports, so the operator can see why five reports turned into one incident.

The panel is the same one residents get, with exactly one next step underneath. Usually that's a ticket written as a plain sentence, "Create a service ticket for Electricity Service", the exact message that will be sent, and one button: Approve and send.

One next step per incident. What you see in the box is what the city unit gets.

When the system can't make up its mind, the next step becomes a question instead: which incident does this report belong to, or who's responsible for this noise complaint? A sparking cable cabinet gets a red notice that a ticket won't send the emergency services, in case anyone was hoping.

When things are unclear, the panel asks one question and gives you the answers to pick from.

The city unit inbox

The city unit's inbox is the operator screen with less stuff in it. A unit sees only its own tickets. The water company doesn't need to hear about your neighbor's late-night drilling. Again, there's one next step at a time: acknowledge, start work, mark it resolved with a note. The note goes back to the operator, and the resident's timeline moves along with it.

Once it's approved, the next step turns into the ticket's timeline.
The request, one button and a note field. Who reported it stays private.

The whole thing hangs on one idea: an incident should look the same wherever you meet it. Same title, same color, same panel, and a timeline that moves for everyone at once.

One outage, three people looking at it. When the crew starts, the resident sees it too.

The small stuff

Icons instead of labels. I love icons so much. Staff screens show short facts led by little icons: a clock for time, a ruler for distance, a ticket for a reference number. Screen readers still get the words. That's how I made every queue row fit on just two lines.

A clock, a ruler, a ticket. Nobody needed the word “Time:” anyway.

Phones get everything too, so a repair crew can update a ticket's status right away, from the actual hole in the road. Fast updates are the whole point of the app.

The same rows and the same panel, one screen at a time.

Something that is very important to me: accessibility is the foundation, not a bonus. Status never relies on color alone, because "the red one" is not a status, and everything works from the keyboard. An automated check of all three screens in 32 states found no WCAG 2.2 AA violations.

The operator screen after dark, for the night shift.

How it's built

The frontend is Next.js and React on Appica UI, which I found on Twitter a while back and wanted to try. I turned all its blue accents gray, so no stray component could sneak the blue back in. The map is MapLibre with OpenFreeMap tiles, which means no API key and no surprise bill. It shares the theme with the panels, so dark mode recolors the whole city too.

Lighting up a single building needed a workaround: the map data glues neighboring buildings together under one ID, so my first attempt lit up entire blocks. Great for a light show, bad for an outage report. Now, with a clever heuristic, the map copies just the one building into its own layer.

Dark mode on a phone, fittingly for a street lights outage.

Finishing notes

Using one map and one panel for all three groups was the best call of the weekend. Second best was going to sleep at all. It saved a pile of time, and it made the handoff easy to follow, because everyone looking at an incident sees the same shape. And most importantly, we built one product, not three.

What's next? First up is a phone line for older residents, where the same voice assistant takes your report without an app, followed by mObywatel sign-in, tickets going straight into the city's own systems, and notifications, so you know when your bus stop gets its window back.

Keeping color for things that mean something did more for the map than any styling could. Eight categories, a heatmap and four statuses only stay readable if everything else agrees to be gray. The map might seem boring. I know, and that's by design.