Concept
The gentle consult
Wattlebird Veterinary Clinic is a fictional vet for Wembley and Perth's inner west, pitched at dog and cat owners — new-puppy households, families with an ageing labrador, cat owners who dread the carrier. The page has one job: book an appointment. The direction is calm clinical warmth — no cutesy pet-shop kitsch, no cold hospital white — a page that lowers the owner's heart rate before it asks for anything, and answers the money question before it's asked.
Palette
Argued from the consult room
The ink/line split is deliberate: the spec's bright accents (#D97F4E, #5B7B9A) draw lines and marks; wherever those hues had to carry words, a darker sibling was derived and contrast-checked.
Typography
A nameplate and a bedside manner
Fraunces (500–700, optical sizing on) is the practice nameplate and every heading — a warm, humane serif whose old-style quirks read like a hand on the shoulder rather than a hospital sign. The hero sets its one typographic bravery moment: "Gentle with them. Straight with you." with the italic carrying the brand promise. Nunito Sans handles body and UI — soft without being childish, with the weight range (400–800) doing the hierarchy work in forms, price cards and the trust strip. Body is 16px+ throughout.
Techniques
The Life-Stage Care Planner
The signature is a species toggle (dog/cat) plus an age slider (8 weeks → 16 years) driving three synchronised surfaces, all procedural SVG rebuilt per frame from one state object:
- An ageing portrait. A parametric silhouette generator maps age to growth (puppy proportions — bigger head ratio, shorter legs — lengthening into adult), then to seniority: grey-muzzle overlay opacity ramps via smoothstep from age 7 (dog) / 8 (cat), the back sags and the tail lowers from 10, and the senior cat slims. Paths are string-built each update; slider moves tween through a 240ms cubic ease so the animal visibly ages under your thumb.
- A care timeline. Five life stages per species; the apricot progress line is drawn to the exact age fraction, past stages get heeler-blue ticks, and each node carries a transparent 20px hit circle — click a stage to jump the whole planner to its midpoint.
- An honest stage card. Each stage surfaces its essentials as price cards fed from a single price constant object (the same source the services grid quotes), with plain-language notes — parasite cover, teeth, arthritis watch. "Book this visit" pre-fills the enquiry form with species, age and the stage's suggested visit type, then scrolls to it: the demo interaction becomes a warmer lead.
The rest state is designed, not blank: a dog at 7 years, timeline three stages ticked, the senior-watch card showing — mid-demonstration at the 7-second thumbnail. A 4.6s breathing idle (scaleY 1.012, transform-box: fill-box) keeps the portrait alive at rest; from age 12 a small wattlebird lands on the portrait ring — the second-read detail. Everything else is restrained: one entrance choreography gated on fonts.ready with a fallback timer, and prefers-reduced-motion kills the intro, the breath and the tween, rendering a settled, complete frame. A state probe (window.__vet) carries species, age, stage, card prices and form status for headless QA.
Conversion decisions
Why the page is shaped this way
- Above the fold at 375px: name, trade + area, "Book an appointment" (apricot-ink fill, the page's only loud object), a tel: button, and two trust lines — measured at 630px of a 740px viewport.
- Price honesty as the trust strategy: "Consultations from $98" sits in the hero sub-line because cost anxiety is the real booking blocker for vet visits; "we quote before we treat" repeats at hero, services and footer.
- Form fields are the receptionist's questions in order: name, phone, suburb, pet's name, species, age, visit type, notes. Front-end validation with per-field messages; the confirmation is local and honest ("Thanks — request received… we'd call you back within one business hour", flagged "Demo only — nothing was sent"). No fake network round-trip.
- After-hours honesty band: instead of a fictional 24/7 promise, a scrub-dark section tells owners to go straight to a 24-hour emergency hospital at 2am and call ahead — the answer a good day practice actually gives.
- Sticky mobile bar (call + book, 48px targets) appears under 880px; most trade traffic is a phone in one hand.
Local SEO
What we'd implement for the real business
This demo page ships only WebPage schema naming it a demonstration — fictional structured data in a real index is dishonest markup. For a real clinic we'd ship VeterinaryCare LocalBusiness schema wired to their genuine details and live review feed:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "VeterinaryCare",
"name": "Wattlebird Veterinary Clinic",
"url": "https://www.wattlebirdvet.com.au",
"telephone": "+61 8 9000 0231",
"priceRange": "$$",
"address": {
"@type": "PostalAddress",
"streetAddress": "[real street address]",
"addressLocality": "Wembley",
"addressRegion": "WA",
"postalCode": "6014",
"addressCountry": "AU"
},
"geo": { "@type": "GeoCoordinates", "latitude": -31.93, "longitude": 115.80 },
"openingHoursSpecification": [
{ "@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday","Tuesday","Wednesday","Thursday","Friday"],
"opens": "07:30", "closes": "18:30" },
{ "@type": "OpeningHoursSpecification",
"dayOfWeek": "Saturday", "opens": "08:00", "closes": "13:00" }
],
"areaServed": ["Wembley","West Leederville","Subiaco","Floreat",
"Churchlands","City Beach","Jolimont","Shenton Park"],
"aggregateRating": { "@type": "AggregateRating",
"ratingValue": "[from live review feed]", "reviewCount": "[live]" },
"review": [ { "@type": "Review",
"author": { "@type": "Person", "name": "[real reviewer]" },
"reviewRating": { "@type": "Rating", "ratingValue": "5" },
"reviewBody": "[pulled from Google Business Profile]" } ]
}
</script>Plus: Google Business Profile with service categories per suburb, FAQPage schema on the six real questions, and suburb-level service pages once the practice wants to rank beyond Wembley.
The real client
What would change for an actual clinic
- Online bookings: the form hands off to Vetstoria (real-time diary slots against their PMS) or the booking module in ezyVet — the two systems Perth small-animal practices actually run.
- Practice management: ezyVet or Covetrus (Avimark/RxWorks lineage) drives appointment types, so the planner's "book this visit" maps to real consult codes.
- Recalls & reminders: vaccination and senior-check recalls via PetPack or Covetrus Rapport — the life-stage planner becomes a genuine recall funnel, not just a demo toy.
- Their content: real vet and clinic photography replacing the procedural art, their genuine fee schedule feeding the single price-source object, and their Google reviews replacing the demo copy.
Iteration log
Three passes, honestly kept
Pass 1 — Craft
Screenshot review (full-page desktop plus hero and planner clips) caught the two real defects: the hero illustration read as mud — the dog a blob with a handle-loop ear, the banksia cone reading as a second creature — and the adult dog portrait's head detached from its body from age ~4, because the neck path that connects at puppy proportions thinned to nothing at full growth (the collar arc floated under the chin like a smile). Redrew the hero as a resting dog watching a wattlebird perched on a banksia stem; rebuilt the portrait neck as a solid quad polygon, moved the collar onto it, chunked the muzzle. Also fixed a harness artifact: toggle events fire async, so the FAQ probe was read before it ran.
Pass 2 — Depth
Added the breathing idle, made timeline nodes clickable (jump to stage midpoint through the 240ms tween), gave stage cards a hover lift, and kept the age-12 wattlebird as the second-read. One Chanel cut: the apricot line under the perched mini-bird read as a swing — removed. The harness gained an independently reimplemented price oracle: dog @ 8 weeks must show $180–$240 / $65 / $450–$750 and cat @ 12 years $190 / $98 — asserted against the rendered DOM, not the page's own data. Cross-surface greps then caught microchipping $65 present in the planner but absent from the services grid; added it to the vaccinations card so the page can't argue with itself.
Pass 3 — Hardening
375px and reduced-motion screenshot reviews. The timeline's per-stage range sub-labels compressed to ~5px at 375 — now hidden below 600px, the stage names carry alone. Reduced motion verified as a settled complete frame (breath animation computes to none, planner fully rendered, asserted headlessly). Final sweep: 47 checks green — zero console errors at both viewports, exactly one h1 per page, no horizontal overflow at 375, sticky-bar tap targets 48px, empty submit flags all six required fields, filled submit renders the local confirmation, and every phone number on the page uses the reserved (08) 9000 exchange.