Case study · Work

Bicycle maker

Sixty bikes that the ERP counts as nine hundred, three languages, and a login for every shop that stocks them, with no ecommerce plugin, because nothing is for sale.

A road bicycle leaning against a wall with a cycling helmet on the floor beside it

The numbers

PlatformCraft CMS 5, multi-site
LocalesEnglish, German, French
Range≈900 SKUs from ERP
Block kit12 blocks
Figma to live10 weeks

A product design studio brought us their biggest client: a bicycle maker with sixty-odd models, stockists across Europe, and a site that had been rebuilt three times without anyone fixing the underlying problem. The range lived in the CMS but was maintained in the ERP, and the two disagreed constantly.

Where it started

This one started with a phone call rather than a file, because the studio's question wasn't can you build it, it was can anyone. Two rebuilds had already failed on the range, and the client was tired of paying for optimism. So instead of a quote, the first document we produced was a one-page description of the import: which system owns which field, what happens when they disagree, and who gets emailed when a row makes no sense. The client signed it before anyone drew a screen.

The Figma arrived six weeks later and was the easy part: twelve blocks, a model template with a long geometry table, and a colourway picker the studio had prototyped so thoroughly that we mostly transcribed it.

The build

The range now has one source of truth. A nightly import reads the ERP export, creates and retires models, and leaves the editorial fields (the photography, the designer's notes, the ride reviews) alone. The content people own the words, the ERP owns the SKUs, and neither can tread on the other.

Bikes come in sizes and colourways, which looks like ecommerce until you notice there's no basket. Shops sell; the site doesn't. So variants are modelled as plain Craft fields rather than a commerce plugin's product model: one licence fewer, and the model pages stay simple enough for the three-language setup to hold. English, German and French run as Craft sites with translated slugs rather than a bolted-on translation layer.

The stockist area sits behind a login: regional price lists, high-res assets, and the assembly manuals that used to be emailed around as attachments named FINAL_v7. Every shop gets an account, and the client's sales team can see who downloads what.

The import, specifically

The ERP export is a forty-column CSV of which the site needs eleven. The console command hashes each row so that it only touches entries whose hash has changed, so the nightly run across nine hundred SKUs finishes in about a minute and the revision history stays readable instead of recording nine hundred phantom edits a night.

Models the ERP retires don't 404. They redirect to their family, which keeps years of shop bookmarks and magazine reviews alive. Rows the command can't parse go into a morning email to one named person at the client, with the line number and the reason. The length of that email has become the client's informal health check for the whole pipeline.

The three languages run as full Craft sites rather than a translation plugin. Geometry fields propagate across all three automatically, because a millimetre is a millimetre in German. Editorial fields stay separate, because the designer's notes read differently in each market and the client has a translator who is picky in a way we like.

What Craft gave us for free

The twelve blocks share their field groups, which is where the ten weeks came from. A new block is mostly assembly: a heading field here, an asset field there, and the Twig partial that arranges them. Nine hundred SKUs would punish lazy queries, so every listing eager-loads its sizes, colourways and imagery up front, and the heaviest range page runs the same handful of queries as the lightest. The multi-site propagation rules did a lot of the work. Geometry fields copy across the three languages on save, editorial fields don't, and that one piece of configuration replaced what the previous rebuild had attempted with a plugin and hope.

The geometry tables are the responsive test case we now show other clients. At desk widths they're true tables. Below a breakpoint each frame size reflows into a label-value card, because pinch-zooming a table on a phone is how people end up ordering the wrong size. The colourway picker crossfades between frames, drives from the keyboard with visible focus, and under reduced motion swaps the crossfade for a plain cut. Nothing else on the site moves, which suits a site people visit to read measurements.

Checks, in three languages

Every template gets proofed with German content, because German breaks layouts English never will. Scheibenbremsaufnahme does not truncate politely. The geometry tables took a full week of the ten, most of it on keyboard navigation and a screen-reader pass that made us rename half the column headers to things a person would say out loud.

For the last three weeks the import ran nightly against staging while the old site stayed live, so by cutover it had already survived nineteen real exports, two of them malformed in ways nobody had predicted. The stockist area went to twelve pilot shops a fortnight before the rest. Their complaints renamed three things, got the price lists a search box, and settled an argument about download buttons that had been running since week two.

After launch

A content team of two runs all three sites. The studio art-directs each new model year and sends us a Figma page. The blocks already exist for most of it, so those amends are small. The import has run every night since launch and emailed its named person fourteen times in the first year. Eleven of those were the ERP's fault, a score the client now quotes at their vendor.

They asked harder questions about our ERP than our ERP vendor ever has.
The client's marketing director, via the studio

Yours could
be next

Send one component from your current Figma and we'll build it for free. Check it against the file yourself before you sign anything.