Phase 4: implement full static site (Eleventy, 15 pages, CSS, JS)

This commit is contained in:
Tom Roth
2026-07-31 08:33:06 +02:00
parent ecbd9f94e5
commit 34f7f5a1f5
24 changed files with 5986 additions and 8 deletions

161
src/contact.njk Normal file
View File

@@ -0,0 +1,161 @@
---
layout: layouts/base.njk
title: "Request a Quote"
description: "Request a quote from Diana — San Francisco's trusted handywoman. Tell her about your project and she'll get back to you within 12 business days."
---
{# ── Page hero ─────────────────────────────────────────────────────────────── #}
<section class="page-hero bg-graphite">
<div class="page-hero__inner container">
<p class="section-eyebrow">Get in touch</p>
<h1 class="page-hero__title">Request a Quote.</h1>
<p class="page-hero__lead">Tell Diana about your project. She responds within 1&ndash;2 business days.</p>
</div>
</section>
{# ── Contact grid ──────────────────────────────────────────────────────────── #}
<section class="contact-page section">
<div class="contact-page__grid container">
{# ── Form column ─────────────────────────────────────────────────────── #}
<div>
<form class="quote-form" id="quote-form" novalidate aria-label="Quote request form">
{# Honeypot — hidden from real users, catches bots #}
<div class="visually-trap" aria-hidden="true">
<label for="website">Leave this blank</label>
<input type="text" id="website" name="website" tabindex="-1" autocomplete="off">
</div>
{# Error banner — shown by JS when form has validation errors #}
<div class="form-error-banner" role="alert" aria-live="assertive" hidden>
<p>Please correct the errors below before sending.</p>
</div>
{# Name #}
<div class="form-field">
<label for="name">Your name <span aria-hidden="true">*</span></label>
<input
type="text"
id="name"
name="name"
required
autocomplete="name"
aria-required="true"
aria-describedby="name-error"
>
<p class="form-field__error" id="name-error" aria-live="polite"></p>
</div>
{# Address / Location #}
<div class="form-field">
<label for="address">Project address <span aria-hidden="true">*</span></label>
<input
type="text"
id="address"
name="address"
required
autocomplete="off"
aria-required="true"
aria-describedby="address-hint address-error"
>
<p class="form-field__hint" id="address-hint">Where is the work located? City or full address.</p>
<p class="form-field__error" id="address-error" aria-live="polite"></p>
</div>
{# Phone #}
<div class="form-field">
<label for="phone">Phone number <span aria-hidden="true">*</span></label>
<input
type="tel"
id="phone"
name="phone"
required
inputmode="tel"
autocomplete="tel"
aria-required="true"
aria-describedby="phone-error"
>
<p class="form-field__error" id="phone-error" aria-live="polite"></p>
</div>
{# Email #}
<div class="form-field">
<label for="email">Email address <span aria-hidden="true">*</span></label>
<input
type="email"
id="email"
name="email"
required
inputmode="email"
autocomplete="email"
aria-required="true"
aria-describedby="email-error"
>
<p class="form-field__error" id="email-error" aria-live="polite"></p>
</div>
{# Project description #}
<div class="form-field">
<label for="description">Project description <span aria-hidden="true">*</span></label>
<textarea
id="description"
name="description"
rows="5"
required
aria-required="true"
aria-describedby="description-hint description-char-count description-error"
></textarea>
<p class="form-field__hint" id="description-hint">What needs to be fixed, replaced, or improved?</p>
<p class="form-field__char-count" id="description-char-count" aria-live="polite"></p>
<p class="form-field__error" id="description-error" aria-live="polite"></p>
</div>
<button type="submit" class="btn btn--primary form__submit">Send My Request</button>
{# Success message — shown by JS after successful submission #}
<div class="form-success" role="status" hidden>
<p class="form-success__icon" aria-hidden="true">&#x2713;</p>
<h2 class="form-success__title">Your request is on its way!</h2>
<p class="form-success__body">Diana will review your request and get back to you within 1&ndash;2 business days. If your project is urgent, you can also reach her directly at <a href="{{ site.phone | telHref }}">{{ site.phoneDisplay }}</a>.</p>
</div>
</form>
</div>
{# ── Contact secondary column ─────────────────────────────────────────── #}
<aside class="contact-secondary">
<h2>Other ways to reach Diana</h2>
<div class="contact-secondary__method">
<span class="contact-secondary__icon" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">
<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07A19.5 19.5 0 0 1 4.69 12 19.79 19.79 0 0 1 1.61 3.4 2 2 0 0 1 3.6 1.22h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L7.91 8.78a16 16 0 0 0 6 6l.92-.92a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 21.5 16h.5a2 2 0 0 1 .02.92z"/>
</svg>
</span>
<div>
<p class="contact-secondary__label">Call or text</p>
<a href="{{ site.phone | telHref }}" class="contact-secondary__value">{{ site.phoneDisplay }}</a>
</div>
</div>
<div class="contact-secondary__method">
<span class="contact-secondary__icon" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/>
<polyline points="22,6 12,13 2,6"/>
</svg>
</span>
<div>
<p class="contact-secondary__label">Email</p>
<a href="mailto:{{ site.email }}" class="contact-secondary__value">{{ site.email }}</a>
</div>
</div>
<p>For urgent repairs, calling is fastest.</p>
<p class="contact-secondary__area">Diana serves {{ site.serviceArea }}.</p>
</aside>
</div>
</section>