The client was me. I wanted a clean one-page site to present my IT services (hardware and software assistance, network setup, remote and on-site support) with a credible web presence, without paying a template builder or shipping the same Wix-looking landing page everyone else has. Target audience: residents and small businesses in Sicily, plus tourists in the Aeolian Islands who often need an English-speaking technician.
Bilingual because the demand is real: IT default for locals, EN toggle for tourists. Constraints: one page, no framework, no build step, free hosting.
Why vanilla, why Cloudflare Pages
HTML, CSS and JavaScript with no external libraries: no framework, no bundler. System font stack, no webfonts: zero extra requests and immediate first render even on shaky 4G. For a marketing page it's the natural call.
Cloudflare Pages for the same reason: direct GitHub integration, deploy on every push, global edge, free tier. I commit, and seconds later it's live. An honest note: the first version of script.js had ~40 lines disabling right-click, F12 and Ctrl+U, the classic «source protection» theatre. I deleted it: it broke legitimate actions like «open in new tab» and was counterproductive on a site meant to signal competence.
Bilingual without a build step
The pattern is small: a translations object with two languages, data-i18n / data-i18n-aria-label / data-i18n-title attributes on elements, a setLanguage function that walks them when the user toggles. The choice is saved to localStorage; if missing, fall back to the browser language; if neither IT nor EN, default to English.
Side note: I built this pattern here on eolietech first, then ported it back to the portfolio. It works well enough that I haven't touched it since.
The contact form, without a backend
I didn't want an endpoint to maintain or to depend on third-party services to receive four messages a month. The form collects name, contact and message, then opens WhatsApp with the text already written:
function sendForm(e) {
e.preventDefault();
const lang = document.documentElement.lang || "it";
const name = form.name.value.trim();
const message = form.message.value.trim();
const intro =
lang === "en"
? `Hi Francesco, I'm ${name}.`
: `Ciao Francesco, sono ${name}.`;
const body = `${intro}\n\n${message}`;
const url = `https://wa.me/393518579386?text=${encodeURIComponent(body)}`;
window.open(url, "_blank", "noopener,noreferrer");
}
No API, no tokens, no CORS. The user lands on WhatsApp with a pre-filled message and just hits send. The message is locale-aware: Italian visitors get "Ciao Francesco, …", English ones "Hi Francesco, …".
If volume grows I'll consider a dedicated endpoint. For now this is the right call: zero infrastructure.
The half-day bug: backdrop-filter and fixed children
The header had backdrop-filter: blur(10px) for a frosted-glass effect. The mobile menu (a position: fixed panel) was appearing as a thin strip trapped inside the header instead of covering the full screen. Thirty minutes of debugging, convinced there was a JavaScript bug. Then I found it: backdrop-filter creates a new CSS containing block, and any position: fixed descendant gets positioned relative to that ancestor, not the viewport. It was on MDN, in a low-visibility note. The fix was deleting one line.
/* backdrop-filter creates a CSS containing block.
Any position: fixed child is trapped inside it. */
.site-header {
backdrop-filter: blur(10px); /* ← culprit: delete this line */
}
.nav-panel {
position: fixed;
inset: 0; /* positioned relative to .site-header, not viewport */
}
/* Fix: move the blur to a ::before pseudo-element instead */
.site-header::before {
content: "";
position: absolute;
inset: 0;
backdrop-filter: blur(10px);
z-index: -1;
}
Performance
In the latest pass I tightened SEO foundations (metadata, hreflang, and bilingual page alignment) and unified dark/light behavior, including mobile theme-color sync. Lighthouse still sits on a solid base: 91 performance, 100 accessibility and SEO.
What shipped lately
After launch, small things matter. In the first hours of the site being live I found a soft hyphen (U+00AD) hiding inside the string «Perché scegliermi», invisible in the browser, but it broke copy-paste of that exact string. One line in the diff, one character invisible in a normal editor. Gone.
The first WhatsApp contact arrived with the first line «Ciao Francesco, sono …» pre-filled in the right language: confirmation that, even on a four-page site, localized copy is worth the care. For EN readers, the same intro arrives in their language. A greeting in the right language is still the simplest way to say «I read you».
Where it stands
It's live on eolietech.com. No analytics planned, the main work channel is WhatsApp, not site traffic. I'll update these notes when meaningful changes land.
If I started over
I'd add self-hosted webfonts (Inter or similar) from day one: the system stack is fast but neutral. I'd include the WebSite and Person JSON-LD from the beginning instead of retrofitting it later. And I'd move from Pages to Workers upfront, to have more room for edge logic if needed.