Nicolas Holzapfel — Portfolio

AI-first design (personal project)

Shipping a web app solo with AI


Context

Trains2Green is an app developed in my spare time to solve a problem for UK hikers and to explore ways of designing and developing with AI.

London is surrounded by beautiful countryside — including 9 national parks/landscapes — and the dense regional rail network facilitates easy access for the half of Londoners without a car. Alas, multiple barriers conspire to stop people making the most of this.

The bad UX of hiking

  1. There's no way to form a mental model of what's available, and compare potential hikes by the journey required to get there.
  2. Hiking app sites Alltrails and Komoot provide endless choices of station walks, but no way of quickly evaluating walk quality.
  3. There are multiple different books and organisations promoting high quality walks by train, but making use of them is a research project in itself.
  4. Many people like the idea of a walk in the countryside, but are intimidated by the logistics of organising it, and by self-navigation.
  5. Station-to-station walks can be particularly confusing to plan, since it's not clear what train tickets to buy given that it's not a simple return journey.
  6. If I want to go on a walk with someone who lives elsewhere, there's no easy way to find locations and train journeys that work well for both of us.

Optimal AI

In addition to making a useful app, I wanted to immerse myself in the emerging design workflows that AI makes possible:

  • How much design control/fidelity does an AI-first approach give me?
  • Can I avoid the cookie-cutter “LLM look” and produce an opinionated, distinctive UI?
  • Can I maintain a consistent design system in the coding environment?

Tech stack

The React + Tailwind + shadcn/ui combo is the default tech stack used in nearly all AI tools — AI works with it much more effectively than other stacks. It's also completely customisable, so I could go as far as I liked in giving it a distinctive brand.

Tools & approach

I set up the project manually, initially using a combination of hand-coding and Claude to build it. As the project progressed I found myself hand-coding less and less, after I realised how powerful Claude had become since Opus 4.6. Eventually I switched to using the Claude Code CLI only, not touching code directly at all.

Solution

Space, meet time

The core of the app:

  • 2,200 station-to-station walks aggregated from 58 organisations (all credited with backlinks for full walk info!)
  • Visualise your walk-by-train rail options
  • Distinguish walk options by quality
  • See options from any station in Britain
  • Customise according to your journey-time tolerance

Beauty & expanse

My intention is that using the app should be a pleasurable experience where users can imagine themselves enjoying the peace, beauty and novelty of the countryside — a pleasant browsing experience as much as a fast utility. To this end, the UI is defined by friendly rounded edges and slightly exaggerated padding and sizes, with a palette of bright nature colours (green, aquamarine and cream), consistent across both map and UI. I generated abstract artwork on Midjourney to find a palette that worked.

Beyond colour, my main customisation of the map was to radically declutter it — I've removed all roads and other labelling, and the visual contrast between urban and rural areas has been heightened to help users view how urban a hiking area is.

Taking a breather

The photo overlay that each station opens onto is a key part of creating the experience I wanted. A more efficiently utilitarian approach would be to show the walks touching a station as a table, so users could filter and sort. But this isn't a business app and I wanted the experience to make for pleasant and inspiring browsing, and so chose a card-based grid approach instead.

Finally, I added a number of subtle micro-animations — e.g. the train on the slider animates-in on app-load, the checkbox icon gets drawn, and the station-icons grow and shrink. Nothing abruptly appears or disappears — the intention is that everything should feel organic and smooth.

The animated lines showing the actual path of the train journeys don't add utility, but provide an additional sense of aliveness to the map, reinforcing the reality of the journeys.

Simplicity out-features features

Behind the scenes, there is a lot more walk and journey data than I make visible in the app. I chose to simplify the data rather than show exact metrics, showing just enough detail to support decision-making, but with minimal cognitive load. For example, while standard hiking apps give precise ascent and descent figures, I've simplified this into four categories — “flat”, “gentle”, “rolling” and “steep” — which seem more realistic units for decision-making than knowing that a walk involves “180m ascent, 150 metres descent” as standard hiking apps will inform you.

Leading hiking apps tend toward information-overload; Trains2Green prioritises simplicity and progressive disclosure

Filters are currently kept to an absolute minimum.

Onboarding without onboarding

In general I avoided any kind of walkthrough, since the best onboarding is always one where the UI speaks for itself, and learning is through doing. The significant exception is the one-off welcome overlay, which I created to reinforce that the map shows stations not walks, since I found from user testing that this is easily lost on new users, given that it goes against the existing mental model for finding a walk.

On hover, each station is surrounded by 7 km and 14 km radii. This is to help users estimate what might be feasible when planning their own station-to-station hikes. It's easily missed, but since it's a nice-to-have feature for veteran hikers only, I chose not to clutter the onboarding experience by highlighting it. Instead, it's there for users to discover in their own time as a kind of Easter egg.

Mobile

As a casual personal app, it's essential that Trains2Green works well on mobile. I made multiple tweaks to get this right:

  • Greater spacing and sizes on mobile, to accommodate the imprecision of tapping rather than clicking.
  • On first load, the map is positioned such that all key stations are visible despite the filter taking up most of the screen. The zoom level at which station names first appear on the map is adjusted for mobile to make this possible.
  • The stations' photos are full-bleed, so they can make full use of the limited available space.

Designcraft learnings

A key goal for this project was to explore AI-enabled design workflows in light of February's breakthrough in model capabilities. Here's a Q&A with myself to work through my reflections:

Is Figma dead?

For the most part Claude is stunningly effective in creating plausible starting points and then responding effectively to precise instructions. I ran into a hard barrier with the design of the walk-card, where my descriptions of what I wanted had Claude stumped. Even supplying hand-drawn sketches only marginally improved Claude's performance. To get this right, I exported the design system from code to Figma via the MCP server, and manually created it just like in the old days before Opus 4.6. Claude finally understood and we got back on track. I believe this was because the design involved an unusual use of vectors, alien to standard design patterns.

In other ways I was able to go much further and faster with Claude Code than I could with Figma. This was particularly evident with animations. Animations are possible with Figma but generally fiddly to pull off. It was much faster to simply iterate with Claude.

The problem — walk info is too reliant on text alone
Okay I’ve figured out how I want to fix this; I’ll tell Claude
Claude, what are you doing?!
Aargh!
Okay Figma, you win. We still need you.
Nice! If I draw exactly what I mean in Figma, Claude finally understands.

Claude the designer?

It can make superficially-plausible and often attractive designs, but consistently adds clutter and needless complexity. Its characteristic approach is to use design patterns which are, in themselves, respectable, but don't make sense for the particular context.

It's also surprisingly bad at keeping to existing components.

Claude’s design exploration via Pencil
Claude’s design exploration via Paper

That cookie cutter AI look?

There was no barrier at all here. AI will produce generic shadcn/ui components with only light customisation, but you can push the customisation as far as you like.

Where'd my design system go?

With the React + Tailwind v4 + shadcn/ui combo there's a single globals.css file that contains all the colour variables, and where you can override Tailwind defaults. Once you understand that and how it relates to UI components by way of Tailwind utility classes, you understand everything you need to know to control and maintain the fundamentals of the design system.

Recently we've seen the introduction of design.md files, but most of what design.md files cover can be more effectively controlled directly with the Tailwind variables and React components, via a built-in design system. For Trains2Green, I had Claude build a design system mini-app to help me visualise what was going on.

Coding required?

Much less than I imagined! It was helpful, but not necessary, that I understand HTML, CSS, Tailwind and the basics of React. Having a clear mental model of how, for example, Flexbox layouts work meant I could easily see where Claude had made mistakes and how these could be corrected. Initially, I would sometimes jump directly into the code and edit Tailwind utilities, but over time just stopped, as the slightly more direct control didn't justify the environment shift.

Understanding the basics of React components and being able to see how this is organised in the code base is also helpful for instructing Claude to make changes that are systematic and modular, rather than messy one-off hacks, but this requires only minimal code-literacy — anyone who has designed a CMS will already be familiar with this way of thinking.

Backend work was handled entirely by Claude. It's not possible for me to evaluate the cleanliness of this but… it works!

The one big exception is the design system. Without my being able to understand and control how the design tokens and semantic variables are implemented in the code, I think Claude would have created an inconsistent mess of hard-coded colour values. Understanding and manually editing the globals.css file meant I was able to keep control of that and rapidly overhaul the look and feel in a consistent way.


Conclusion

It was enormously liberating to be able to implement the design exactly as I wanted. It was also just extremely fun and even addictive to be able to create so much so easily. This was principally a personal design challenge, but I'm keen to promote it as a real product, so the next question for me is: can AI help me with marketing on a zero budget?

Trains2Green app →