Case study

Santa-Cruz Travel Agency

A bilingual travel-agency site built from scratch, with a full internal admin panel

Outcome: Live at santacruz.od.ua · 26+ pages · every form lands as a real lead (email via Resend and the admin panel inbox)

  • Type: Client work
  • Client: Santa-Cruz (family travel agency, Odesa, Ukraine)
  • Role: Design Engineer
  • Year: 2026
  • Tools: React, Vite, Tailwind CSS, Framer Motion, Supabase, Resend, Anthropic Claude API, Cloudflare Pages, Base44 (legacy), OpenWeatherMap
  • Tags: Web Design, Full Stack, Design System, Motion Design, Accessibility, SEO & Performance, Localization, AI Integration

View the live project

The case in brief

Context
Santa-Cruz is a family-run travel agency in Odesa with 25+ years in business. It sells package holidays, and every sale closes over the phone: a manager calls the visitor back within two hours.
Problem
The first version ran on a Base44 prototype whose email only worked for registered, logged-in users. A first-time visitor would have had to create an account just to ask about a tour, which would kill the two-minute lead form the whole funnel depends on.
Scope
Visual identity and design system, a bilingual UA/EN site of 26+ pages with a full motion system, live integrations, a custom admin panel, and the domain and hosting setup. Out of scope by design: online payment, since the sale is closed by a person.
Role
Design Engineer
Constraints
Every form had to work for an anonymous visitor in one submit. Fonts needed full Ukrainian Cyrillic coverage. Non-technical staff had to run the whole site without touching code, and leads had to reach the channels the team already uses.
Key decisions
  1. Moved off Base44 to an owned React + Supabase stack on Cloudflare Pages, with email through Resend and no login wall.
  2. Every conversion point ends in a phone call or a direct message, not a checkout.
  3. Tour requests arrive by email through Resend and in the admin panel's Tour Requests inbox, so the two-hour callback window starts on time.
  4. Sunshine yellow fills only the single primary action per view — its rarity is what makes it read as "click here".
  5. An admin panel that lets staff edit all content, text and images included, not just structured records.
  6. I noticed that a link to any page, shared in Telegram, showed the homepage preview: React sets meta tags in the browser, and messengers don't wait for it. I fixed it with prerendering at build time, without moving to SSR or changing the stack.
Outcome
Live at santacruz.od.ua · 26+ pages · every form lands as a real lead (email via Resend and the admin panel inbox)
Reflection
Owning the stack instead of working around a platform limit is what unlocked every form on the site. The contrast audit showed that vivid colours which look fine as fills fail WCAG AA as text, so each got a darker text token.

A human-led funnel, not a checkout flow

Santa-Cruz sells package vacations, but nothing on the site actually completes a transaction. The product truth driving every design decision: a manager calls the visitor back within two hours and closes the sale over the phone. So every conversion point — the tour-request form, price-drop alerts, booking cards — is designed to end in a phone call or a direct message, not an online payment. The brand voice leans into 25+ years in business with self-aware warmth rather than stiff corporate trust signals (the recurring mascot is a woolly mammoth: "we've known tourism since the last millennium").

Before Google Maps — established and personal, never corporate and self-service. Every visual decision reads as a person you call, not a booking platform.

— Brand voice

From a Base44 prototype to a fully owned stack

The site began as a Base44 prototype. Base44's built-in email only worked for registered, logged-in users — and requiring a first-time visitor to create an account just to ask about a tour would have killed the two-minute lead form the whole funnel depends on. Rather than bend the product around that platform limit, the build moved onto a fully owned stack — React + Supabase on Cloudflare Pages — with transactional email wired directly through Resend and no login wall in front of it. That single call is what unlocked everything below: every form on the site (tour requests, newsletter, price alerts) now works for an anonymous visitor in one submit, and the site has grown to 26+ pages — including 8 destination pages and 5 SEO articles — all served from santacruz.od.ua, purchased and wired up as part of the move.

What it's built with

  • React 18 + Vite
  • Tailwind CSS
  • Framer Motion
  • Supabase (Postgres, auth, Edge Functions)
  • react-router-dom
  • Custom UA/EN i18n context
  • Cloudflare Pages hosting, deployed via Wrangler CLI
  • Custom domain purchase and DNS setup (santacruz.od.ua)

Real integrations, not static widgets

A lot of the site's surface looks like typical travel-site chrome — a weather widget, a chat bubble, a subscribe form — but underneath, every one of those is wired to a live external service instead of hardcoded content:

  • Live weather sync — the per-destination weather widget pulls real current conditions from a weather API, not placeholder icons
  • Resend-powered email — tour requests, price-drop alerts and newsletter signups all send real transactional email through Resend, triggered from Supabase Edge Functions
  • A Claude-powered chat assistant — the live chat widget is backed by the Anthropic API, answering visitor questions about tours, pricing and destinations instead of a static FAQ bot

A designed identity, not a template

Built and documented as a real design system: a warm navy-and-sunshine-yellow palette with amber as a supporting accent, a Lora (serif headings) + Montserrat (geometric sans body) pairing chosen for Ukrainian Cyrillic coverage, pill-shaped interactive elements throughout, and 24px-radius cards. One rule ties it together: sunshine yellow fills only the single primary action per view — its rarity is what makes it read as "click here."

The Vivid-Color-Is-Not-Text-Color Rule

A live contrast audit found amber, teal and the semantic success/error colors all fail WCAG AA as text on light backgrounds (1.7–3.5:1) despite looking fine as fills and icons. Each got a darkened -text token variant clearing 4.5:1+, applied across 17 live instances sitewide.

The Physical-Press Rule

Depth comes from motion, not shadow. Every button lifts 2px on hover and compresses to 97% on press — a single global CSS rule, not opt-in per component — instead of a hover-shadow escalation pattern.

Where the design-engineering work lives

The motion system had to hold up against Emil Kowalski's design-engineering principles — purpose, easing, physicality, interruptibility, performance, accessibility, cohesion. Here's what it took to get there:

  • Origin-aware popovers — header dropdowns and the chat window scale in from their trigger corner with a shared easing token, instead of teleporting in flat
  • Performance pass — replaced transition: all on the shared button/link classes (137 elements sitewide) with an explicit property list
  • Touch-safe hover states — gated every hover-triggered transform behind @media (hover: hover) and (pointer: fine) so mobile taps don't leave a stuck lifted state
  • A custom WebGL gallery — hand-built shader-based "morph" transitions for the hero, photos dissolving into each other through fbm noise, with a DOM cross-fade fallback
  • Self-correcting scroll — cross-page "jump to the tour form" actions poll and re-correct until the target position stabilizes
  • Consistent stagger rhythm — consolidated outlier list-entrance intervals to the site's i × 0.08s convention

Public-facing capabilities

  • Multi-slide hero (WebGL morph + destination spotlights)
  • Hot deals feed
  • 8-country destination pages
  • Cost calculator
  • Visa requirements by country
  • Tour request form
  • Price-drop alerts
  • Newsletter signup
  • User-submitted, moderated memory gallery
  • Wishlist drawer
  • Live chat widget powered by Claude
  • Blog / travel tips
  • Live weather widget by destination
  • Vacation countdown
  • Packing checklist generator

Internal tooling: the admin panel

A single-page internal tool (Supabase-authenticated, role-gated) that lets non-technical staff run the entire site without touching code, and edit its actual content — site text and images included, not just structured records: Hot Deals CRUD, a Tour Requests inbox, an Image Manager for site photos, Memory Gallery moderation, newsletter and price-alert subscriber lists, per-country visa info, calculator pricing rules, header/hero text overrides, and a Blog CMS. New tour requests and subscriptions land directly in the admin panel's Tour Requests inbox and subscriber lists, with the owner notified by email.

How it shipped

  1. Started as a Base44 prototype, then migrated to a self-owned Cloudflare + Supabase stack, including the domain purchase and DNS setup
  2. Design system first — product truths and a full visual language captured as living documents so every later session builds on the same decisions instead of re-litigating them
  3. Full-site design audit — scored across accessibility, performance, theming, responsiveness and implementation integrity, prioritized by leverage
  4. Iterative polish — dozens of small, verified-live fixes across the whole site
  5. Every change verified before shipping — build, verify live in a browser (desktop + mobile), deploy, verify on the production domain, commit, push

See the finished site in production.

Visit santacruz.od.ua

All projects · Start a project