

A kennel site
its owners actually run.
Springer spaniels and Portuguese water dogs, in Joroinen, Finland.
5
collections they edit
0
deploys to publish
2026
built
The project
What it is
Tia and Tua breed English springer spaniels and Portuguese water dogs in Joroinen, in eastern Finland. They needed somewhere to show their dogs, announce litters, and post the health results and show placings that a breeder is judged on. I designed and built it.




The problem
A kennel's content never stops
Litters are born, puppies get named and rehomed, health results come back from the vet, and show placings land most weekends. A planned litter's sire is often unknown until late, too. A site where any of that had to go through me would be out of date within a month, and the version I updated by hand would be a worse site than the one they update themselves.
A litter arrives
Eight puppies need names, photographs and a page each, and then one by one they are rehomed.
Health results come back
Hips, elbows, eyes and gene tests land from the vet months apart, and each one belongs on that dog's page.
A show weekend happens
Placings and titles arrive most weekends through the season, and a kennel is partly judged on those being current.
A litter is planned
The dam is known long before the sire is, so the announcement has to go up while half of it is still undecided.

The shape of it
The content model
A pedigree fans out from one dog to two parents to four grandparents, and the content model has the same shape. This is the litter pages' own grid, read left to right. The left column is what the breeders edit, the middle is what sits on it, and the right is an example of what goes in.
Content model
Three more sit beside these two: news posts, planned litters, and show and trial results.

The handover
They edit, the site changes
Nothing about publishing goes through me. This is a rebuilt version of what that looks like. Type in the panel and the public card follows.
Try it: rename the dog, clear the photo, or switch it to draft.
Edit dog
Unlock The Nine-Tailed Fox
"Foxy"
Hips
A/A
Published before it is finished
A planned litter goes up as soon as there is one to announce, and at that point the dam is known and the sire usually is not. So the page publishes what exists and says so. The dam's card is filled in, the sire's says he will be announced later, and a drawing holds the place until there is a photograph. When no litter is planned at all, the section is not there. The model was built to publish a half-known record rather than make them wait until everything is settled.

Underneath
What holds it up
Tia and Tua publish straight to production, so everything underneath has to be built for that.
Seven cached endpoints
Every read is cached and revalidated in the background, so a stale answer goes out immediately while a fresh one is fetched behind it. The CMS is never in a visitor's critical path.
Images are resized on the way out
Asset URLs carry their own width, format and quality. A breeder uploading a six-megabyte photo from a phone does not ship a six-megabyte photo.
CMS text is sanitised before it renders
Content from a CMS is still untrusted input. It is parsed as markdown and stripped before it reaches the page.
The sitemap rebuilds itself
A new dog or litter is discoverable without anyone editing a file, and so are its images.
When the CMS is down
The pages return a clean error and the sitemap returns nothing, rather than failing the build.

Built with
The stack
- Nuxt 4
- Vue 3
- TypeScript
- Tailwind 4
- Directus
- marked
- DOMPurify
- Docker
- nixpacks
The editing panel above is a rebuild, not the real thing. Directus and that project's icon set stay in that project. Nothing on this page talks to a CMS.
Kennel Unlock