Elena de Gregorio®
Available for 2026
EN ES

Work / Front-end / GMF Reformas · The new website

A website that lives up to the best reputation in town

GMF Construcciones y Reformas has been renovating homes in Aranjuez since 1998 and holds the best Google rating in the area: a 5.0 across thirty-three reviews. Its website, though, was a Google Sites page without a single call button. The audit left thirty findings; Phase 1 turned the important ones into a design approved in Figma; and this is Phase 2: the whole site, hand-written in HTML, CSS and JavaScript without a single library, with a form that actually arrives and measurement that only starts if the visitor allows it.

Front-end development · vanilla HTML, CSS and JS + PHP — 2026

Project 10 of 30 in the archive 01 of 10 in Front-end development See the whole discipline ↗

The home page of the new GMF Reformas website: the headline «Renovations in Aranjuez with a delivery date signed by contract», the Google rating, the two quote buttons and a renovated bathroom with two round mirrors

Client

GMF Construcciones y Reformas · Aranjuez, Madrid

Discipline

Front-end development

Year

2026 · under review with the client

Role

Architecture, markup and styling, accessibility, form and measurement

Scope

12 pages · 6 service pages · 8 comparators · 21 site photos · 12 reviews

Stack

Dependency-free HTML, CSS and JavaScript · PHP 8 for the form · a Python generator

Deliverable

A static site: 70 KB of CSS and 31 KB of JavaScript, no libraries

Where it comes from

Digital audit → Phase 1, UX/UI design in Figma → Phase 2, this one

The site, at full size · password required

It is hosted on this very domain, as a private preview, for the client to review

While GMF decides what changes and what stays, the site is not public: it lives in a folder of this portfolio, is not indexed and asks for a password before showing anything. If you have it, the button takes you in; if not, you will see the door and nothing else. The captures below tell the rest.

(01) — The starting point

Thirty findings, an approved design and zero libraries

The previous site was a four-page Google Sites: not one button or clickable phone number outside the contact page, a Google Form embedded in a third-party iframe as the only form, and a platform that does not let you insert a single line of measurement. The digital audit sorted that into four blocks —conversion, competition, customer and accessibility— and thirty findings, starting with the ones that cost nothing. What sold GMF was not on its website: it was in its reviews.

Phase 1 took that to Figma —the site's UX/UI design, in high fidelity— and the client approved it. Phase 2 is turning that design into a website that can be published on an ordinary shared host, with no frameworks, no content manager and no dependency that can expire: hand-written HTML, CSS and JavaScript, plus a single PHP file to receive the form. What the client pays for is the website, not a subscription.

12

Static pages

Home, six service pages, thank-you, 404 and three legal pages
0

Third-party libraries

No jQuery, no framework, no plugin: 101 KB of hand-written CSS and JS
0

axe-core issues

WCAG 2.1 A/AA across all twelve pages, desktop and mobile
100

Lighthouse on desktop

Performance 100 · accessibility 100 · best practices 96 · SEO 100. On mobile, 91 for performance

The identity comes from the brand; it is not invented here

The green and the stone of the arch come from the logo; the gold of the motto “Superación y compromiso” stays decorative because on a light background it does not reach the required contrast, and for text there is a darker gold that does. Headlines are set in Poiret One, uppercase and letter-spaced, and since that family exists in a single weight with no italic, emphasis is marked in green with a gold underline rather than with a bold that does not exist. Body text is Manrope at an 18-pixel base.

(02) — How it is built

A home page that sells what the reviews already said

The home page walks, in the order a customer decides, through what the audit had identified as the argument: the Google rating at the very top, the delivery date signed by contract in the headline, five written guarantees, the six types of work, before and after, real jobs, the five-step process, the reviews, starting prices, the service area and the questions everyone asks before renovating. And at the end, the form.

The years in business are not typed in: they are computed from 1998, so the site does not go stale every January. And the figures show with JavaScript off, which was a bug in the previous version: a “0” used to appear.

01 · Six services, six pages, one source

Every card in “What we renovate” opens its own page: bathrooms, kitchens, full renovations, lofts and roofs, pools and outdoors, accessibility. All six share the same structure —a hero with a photo, what is included, before and after, a gallery, timings and price, questions, other services and the form with the renovation type preselected— and all six come out of a single Python file that describes each service and generates them.

The home page works the same way: it is written in a source HTML with three macros —IMG, CMP and GAL— and the generator turns them into <picture> elements with three widths, into comparators and into gallery cards, reading each photo's real size to write its width and height. Nobody types a srcset by hand, which is why none of them is wrong.

_src · build.py · servicios.py · index.src.htmlPillow reads the dimensions · 418 image files generated

The “What we renovate” section: six photo cards —bathroom, kitchen, full renovation, lofts and roofs, pools and outdoors, accessibility— each with a link to its page
What we renovate · six cards, six pagesGenerated from servicios.py, with the form preselected

02 · Before and after, with a control that understands the keyboard

Eight comparators of real jobs, four in view and four more on request. Each one is an invisible <input type="range"> covering the photo: it works with the mouse, with a finger and with the arrow keys, and it tells the screen reader where it is. There is no comparison library; there is a native control, well placed.

When it enters the viewport, the divider moves on its own once. That is the hint that it can be dragged, and it never does it again. With “reduce motion” on at system level, not even that.

js/main.js · comparatorsOne native range per photo · keyboard and screen reader

The “See the change with your own eyes” section: four before-and-after comparators with the divider in the middle, the Before and After badges, and a caption with the town and duration of each job
Before and after · eight jobs, four visibleThe divider is a native range: mouse, finger and arrows

03 · Real work in real homes: the gallery and its viewer

The audit said it plainly: for a renovation company, the photo is the argument. Twenty-one photographs of the company's own work, none from a stock library, with filters by type —bathrooms, kitchens, full renovations, outdoors, lofts— and a viewer built on a native <dialog> that traps focus, closes with Esc and moves between photos with the arrow keys.

And they weigh what they should: every photo exists in three widths —480, 960 and 1,400 pixels— and two formats, WebP with a JPG fallback, with its dimensions written so the page does not jump while loading. The only image that does not wait is the hero, which is preloaded.

index.html · gallery · js/main.js · viewer21 photos · 5 filters · <dialog> with trapped focus

The “Real work in real homes” section: the filter bar —All, Bathrooms, Kitchens, Full renovations, Outdoors, Lofts— and the grid of site photographs, each with its label and town
Our work · 21 photos with filtersThree widths and two formats per photo · accessible viewer

04 · The reviews, on a dark band with the Google score up front

Twelve real Google reviews, quoted with first name and initial, in a carousel with controls, with its state announced to screen readers, and which stops if the system asks for reduced motion. The score —5.0 across 33 reviews— sits in a card of its own with a link to the Google Maps listing, because a rating is only credible if it can be checked.

The section is dark on purpose. It is the only one on the home page that is, and that is why it stands out: Phase 1 designed it as the moment the page changes voice and the customer speaks.

index.html · reviews · js/main.js · carousel12 reviews · controls, announced state, paused under reduced motion

The reviews section on a dark green background: the headline “The best rating in Aranjuez”, the card with the 5.0 and the 33 verified Google reviews, and the carousel with three customer reviews
Reviews · twelve real Google reviewsThe score links to the listing: what is claimed can be checked

05 · Starting prices and financing, because nobody in the area publishes them

The audit listed it in the table of what a customer looks for and could not find: price guidance, “does not exist”; reviews, “does not exist”. Here there are three indicative ranges —bathroom, kitchen and full renovation— with what each includes and a clear note that the fixed price comes after the site visit. And a financing block, because one in three renovation customers would ask for it and, of the sixteen companies in the southern corridor the benchmark looked at, only one offers it.

The amounts are marked as provisional until GMF validates them; this page says so further down, under what is still missing.

index.html · prices3 ranges · financing · a note that they are indicative

The “How much does your renovation cost” section: three price cards —bathroom from €6,500, kitchen from €8,900 highlighted in gold, and full renovation from €550/m²— with what each includes, and the financing block below
Indicative prices · three ranges and financingAlmost nobody in the area publishes a price; this site does

06 · A form that arrives, and knows how to say when it does not

Four required fields, an optional message and the privacy checkbox. The browser validates first and moves focus to the first error; then the submission goes by fetch to a single PHP file on the same host that validates everything again, because browser validation can be bypassed. If the server rejects a field, the error appears under that field, not in a generic message.

Against spam, three silent layers: an invisible honeypot field, a minimum of three seconds —no human fills four fields faster— and a cap of five submissions per hour and IP. The bot is told everything went fine, and nothing happens. Every request is also saved to a CSV in case the email fails, and consent is logged with date, policy version and a salted, anonymised IP: what the GDPR asks for and almost nobody does.

api · presupuesto.php · config.phpDouble validation · 3 antispam layers · GDPR log · CSV copy

The contact section: on the left the three channels —phone in dark, WhatsApp in green and email— with opening hours and address; on the right the “Get your free quote” form card with name, phone, renovation type, town, message and the privacy checkbox
Contact · three channels and the formValidated in the browser and again on the server

07 · On the phone, which is where a renovation gets requested

Seven in ten visits to a renovation website come from a phone, and the audit described the previous site as “shrunk to mobile, not optimised for it”, so what got measured most was 390 pixels. A fixed bottom bar with Call, WhatsApp and Quote; a full-screen dark menu with those three channels always at the end; and a compact cookie notice that no longer covers the headline or the bar.

Two small decisions that show: on mobile the floating WhatsApp button disappears, because it was already in the bar and covered the buttons; and the header compacts between 961 and 1,520 pixels so that “Free quote” does not get cut off on a laptop. Version 6.1 also fixed a menu that the header's blur was clipping into a 76-pixel box: the first three links were off-screen and could not be tapped.

css/styles.css · v6 block · js/main.js · menuNo horizontal overflow at 390 px on any page

The same site at 390 pixels on three screens: the home page with the Google rating and the headline, the before-and-after comparator, and the quote form; on all three, the fixed bottom bar with Call, WhatsApp and Quote
The same site at 390 px, on three screensFixed bar with Call · WhatsApp · Quote

08 · The service pages, with the work on video

Each service has its own page with its preloaded photo, its “what is included”, its comparators, its gallery, its timings and its price. Two of them also carry site video shot by the team itself: a bathroom with a built-in washing machine, from the inside, and on the full-renovation page the same flat before and after, in two paired videos.

The videos play muted when they enter the viewport and pause when they leave; they do not start if the system asks for data saving or reduced motion; and the “Sound” button turns on the audio of one and mutes the rest. They come from WhatsApp originals run through ffmpeg: from 8 and 15 MB down to 3.9 and 4.9, with a poster taken from a frame of the video itself and VideoObject structured data.

_src/servicios.py · video key · js/main.js · videoPlays on entering the viewport · one sound at a time

The hero of the bathroom renovation page: the breadcrumb “Home / Services / Bathrooms”, the headline “New bathrooms in 2–3 weeks, with a signed date”, the supporting text, the quote buttons and a photograph of a renovated bathroom
Bathroom renovation · the service pagePreloaded photo, preselected form, same skeleton across all six
The video section of the bathroom page: the headline “A bathroom with a built-in washing machine, from the inside”, the explanatory text and the vertical video with its poster and sound button
A bathroom with a built-in washing machine, from the insideFrom 8 MB to 3.9 · muted on entry, sound on request

Nothing loads without permission, measurement included

Out of the box, the site talks to nobody but Google Fonts. Tag Manager and Microsoft Clarity only load if the visitor accepts cookies, with Consent Mode v2 denied by default. What is there from the first moment is the audit's measurement plan, written into dataLayer: clicks on the phone, on WhatsApp and on the quote buttons —with the zone of the page where each happens—, outbound clicks to Google Maps, form start, error and submit, the confirmed conversion on the thank-you page and reading depth at 25, 50, 75 and 90%. When GMF pastes its IDs, not a line will need touching.

(03) — Performance, accessibility and search

What was checked before showing it

A renovation website gets opened on a phone, in the street, on mobile data. So performance is not a Lighthouse score: it is the hero photo being preloaded, everything else loading when it should, nothing jumping into place —a CLS of zero on every page measured— and static files carrying a year of cache, with the version number in the URL so a change shows up on day one.

Accessibility was measured with axe-core on all twelve pages, on desktop and on mobile, and came out at zero. Not through a final sweep: the contrast of every colour is computed from the tokens, focus is visible, touch targets measure 44 pixels, and the two components that usually fail —the comparator and the photo viewer— work with the keyboard and tell the screen reader what they are doing.

0

CLS

No page jumps while loading: every image carries its dimensions
91

Performance on mobile

And 100 · 96 · 100 for accessibility, best practices and SEO
4.5:1

Minimum contrast

The green on the paper reaches 5.5:1; the text gold, 5:1
11

301 redirects

The old Google Sites addresses land on their new section, not on a 404

Google does not notice the site has moved

The .htaccess forces HTTPS and www —a single canonical address— and redirects every old URL with a 301, accents included, to the page or section that now corresponds to it: “who we are” to the guarantees, “our work / full bathroom renovation” to the bathrooms page. The pages carry HomeAndConstructionBusiness structured data with the Google rating, opening hours and the ten towns, and the home page adds a FAQPage with the eight questions. The sitemap dates itself at build time.

(04) — What is missing, and why it is said

A site under review that does not pretend otherwise

This is not published yet: it sits in a private, password-protected preview so the client can walk through it calmly and say what changes. What remains does not depend on the code. Still to be validated: the photo captions and towns, the job figures —“800 jobs”, “300 bathrooms”—, the four starting prices, the company name and tax ID in the footer and legal texts, the legal review of those texts, the names on the twelve reviews, the measurement IDs and the final recipient of the form.

In the meantime, each of those things is marked in the code and in the README, in brackets or with a checkbox, and the preview's form sends requests to the studio, not to the client, so it can be tested end to end without a test turning into a quote. When GMF signs off, publishing means copying the folder to the reformasgmf.com host and filling in one configuration file.

117 MB

The whole preview weighs (v9)

Almost all of it is the site photos in three widths and two formats, plus the two videos
1

Configuration file

Recipients, sender and salt: the only thing that changes at launch
3

Versions in one day

v6, v6.1 and v6.2: the mobile menu, “Home” on inner pages and the full-renovation videos
9

Boxes pending with the client

Copy, prices, tax ID, legal, reviews, form, analytics and domain

How to show a website that is not public yet

The preview lives in a folder of this very portfolio, behind a PHP door: a password, a thirty-day cookie and an Apache rule that lets no request through without it. Eight failed attempts block the IP for a quarter of an hour. The pages carry noindex and a header that repeats it, and the folder is barred in robots.txt. The client sees the site on a real domain, with its form working, and nobody else finds it.

The site, running

Below is the whole site, in the usual frame. The first thing you will see is the door: type the password and the frame loads the home page. Drag a comparator, filter the gallery, open a photo full size and try submitting the form with an eight-digit phone number.

And use the control at the top right: set the frame to phone size and you will see the fixed bottom bar appear and the full-screen menu. There is no scaling; it is being handed a different width.

Notice · password-protected preview

This copy is the real version 9, the latest after the content review with the client, with the data GMF has not yet closed: prices, job figures and legal texts are provisional, and the site says so where it matters. The form works end to end, but while the review lasts the requests go to the studio, not to GMF. Without the password, the frame stays at the door.

reformasgmf.com — v9, preview

Hand-written HTML, CSS and JS · PHP for the form · 70 KB of CSS and 31 KB of JavaScript · 0 libraries

Where it comes from

This is the third case study for the same client. The first is the digital audit: four blocks, thirty findings and the sentence that sums it up —the best reputation in town and the worst website on the market—. The second is the identity: the logo, the green, the stone of the arch and the gold of the motto, which this site inherits as they are. Between those two and this one came Phase 1 in Figma, the high-fidelity UX/UI design the client approved.

Doing all three changes how each one is done. Whoever writes an audit knowing they will implement it stops drafting recommendations and starts drafting things that can be finished; and whoever draws a brand knowing they will code it draws it in tokens. The result is a website with nothing in it that was not decided beforehand, and a preview where the client can check, button by button, that what a report promised is there.

Next in Front-end development

Retina · GAME TV — interface rebuild in Next.js