Back to Portfolio
WaterBaby Serres — Baby & Children's Swimming School

WaterBaby Serres — Baby & Children's Swimming School

WaterBaby Serres teaches babies from four months and children up to ten, in a heated indoor pool at the Elpida Resort. The site had to speak to Greek parents first and to visitors in English second, without becoming two sites. So every visitor-facing string ships twice inside the same file and the switcher only toggles which one is hidden: one URL per page, no reload, no fetch, no second set of pages to keep in sync, and the Greek reads perfectly with JavaScript switched off. Six pages carry it — home, about, the two programmes, a gallery and contact — with the four children's levels laid out from beginner to advanced, the equipment and paperwork a parent needs, and an FAQ. GSAP and ScrollTrigger are vendored locally rather than pulled from a CDN, and every animation is additive: nothing is pre-hidden in CSS, so a failed script leaves a complete page rather than a blank one. The arrival animation on the homepage drains a wave down the viewport once per session and clears itself through CSS keyframes, so it cannot strand a visitor behind an overlay even if the JavaScript dies. Structured data covers the courses, the FAQ, the gallery and the breadcrumbs; an llms.txt states the verified facts for AI assistants and is pinned to the pages by the test suite, so the two cannot drift. Twenty-one Node tests hold the whole thing in place — page structure, Greek copy, the redirect stubs, the loader's safety properties and WCAG contrast on every orange surface.

Technologies

HTML5 CSS3 Vanilla JS (ES Modules) GSAP 3 ScrollTrigger JSON-LD i18n (EL/EN) Node Test Runner WCAG 2.2 AA SEO

Challenges

A bilingual site normally means two of everything — two page sets, two URLs, two chances to update one and forget the other. This one had a single instructor maintaining it, so a second copy of every page was a liability rather than a feature. Greek also had to be the real default, readable before any script runs, because the audience is local parents. And the flourishes a swimming school wants — an arrival animation, scroll reveals, a lightbox — are exactly the things that leave a blank page when a script fails on a phone with a weak signal.

Solutions

Both languages ship inline in every file and the switcher only toggles a hidden attribute, so there is one page to edit, one URL to share and nothing to keep in sync; the preference lives in localStorage and the switch fires a custom event the gallery listens to. Non-text strings — aria-labels, placeholders, meta descriptions, even the document title — translate through paired data attributes handled in the same function, so a translated page has no untranslated corners. Motion is additive by rule and a test enforces it: CSS is never allowed to pre-hide an animated element, so GSAP failing to load costs the animation and nothing else. The arrival overlay ships hidden and is revealed only by a class JavaScript adds, while its exit is a CSS keyframe with fill-mode forwards — script off, blocked or thrown, the overlay still clears. Libraries are vendored locally so a CDN outage cannot take the site's motion with it, and the twenty-one tests were each verified by deliberately breaking what they protect.