<!-- https://docs.elchi.dev/standards | Web standards | Elchi Docs -->

# How we build a website

Every site we deliver is built to the same standard. This page states it
plainly so a client can check afterwards whether they got it, and so a
developer can see what they would be taking over.

## What is measured

Three numbers are checked before a site goes live, with the tool named so the
result can be reproduced:

| What | Target | Measured with |
|---|---|---|
| Largest Contentful Paint | under 1.5 s on a throttled 4G connection | Google PageSpeed Insights |
| Lighthouse performance | 90 or above on mobile | Lighthouse CI, median of five runs |
| Cumulative Layout Shift | under 0.05 | the same run |

A median of five runs rather than a single result, because Lighthouse varies by
ten points or more between runs and one flattering screenshot proves nothing.

## The stack

**Server-rendered HTML from Go.** No client-side framework and no hydration
step. The markup that arrives is the finished page, which is why the first
paint happens before any JavaScript has run, and why a crawler and an answer
engine see exactly what a visitor sees.

**The stylesheet is inlined.** It is small enough that a separate request would
cost more than it saves, so the critical path is one request.

**JavaScript is optional.** Every form works without it. Where script is used,
it is deferred and enhances something that already functions.

## Images

Uploads are converted to AVIF and WebP at six widths and served from a separate
asset host. The browser picks the smallest format it understands through a
`picture` element; there is no user-agent sniffing.

A real example from a site we built: a 324 kB source photograph becomes a
16 kB AVIF at 960 pixels wide, five per cent of the original.

Every image carries its dimensions in the markup and a BlurHash placeholder,
which is around 30 characters and travels inside the HTML. That removes the
blank-then-pop effect without an extra request, and keeps layout shift at zero.

## Analytics without a cookie banner

Visitor statistics run on the client's own server. A visitor is identified by

```
SHA-256(ip + "|" + date + "|" + user-agent hash)
```

The raw IP address is never stored and the identifier changes every 24 hours,
so nobody can be followed across days. No third party is involved and no
persistent identifier exists, which is why no consent banner is required under
the ePrivacy Directive or the Swiss revDSG.

The practical effect is that a visitor sees the site immediately instead of a
dialog, which is worth more than the analytics.

## Content the client controls

Text, images, prices and legal documents are edited in an admin area without
asking us. Legal documents are versioned and append-only: publishing a new
version inserts a row rather than replacing one, so the exact text a customer
agreed to on a given date stays reproducible.

## Forms and enquiries

Every form on a site has a key of its own, and every enquiry is stored with it
before any mail goes out, so an enquiry is never lost to a mail that did not
arrive. The admin area lists the enquiries per form, and each one says who was
told about it.

Who is told is set per form in the admin area, by the client's owners and
admins: one or more addresses for each form, and a default list for every form
without its own. The building office's form can reach the building office and
the school's form its secretariat, neither of them the other's. A change holds
for the next enquiry, and each change is in the admin area's audit log with who
made it and the addresses before and after.

Each address gets a notification of its own, so nobody on a form's list sees
who else is on it. The notification carries the enquiry, and a reply to it
reaches the person who wrote. Notification mails are sent through Resend, a service in the United
States. For a form whose answers should not travel by mail, the client can
choose a notification without content: it says only that an enquiry has come
in through that form and links to it in the admin area, where it is read after
signing in. No name, no address and no message is in that mail.

## Search and answer engines

Structured data as JSON-LD, one graph with cross-referenced identifiers rather
than several disconnected blocks. Correct `hreflang` between language versions,
including `x-default`, so two translations are not read as duplicates competing
with each other.

Plus an `llms.txt`: a plain-language summary an assistant can read without
crawling the site. Being cited when somebody asks an assistant for a supplier
in a particular region is a channel that appears in no analytics dashboard and
still produces enquiries.

## What the client owns

The site, the backend, the content and the data. The export includes
everything, in formats another developer can read.

We think being able to leave is a feature. A supplier who keeps a client by
making departure painful has stopped competing on the work.
