Add landing page and contact form - #3
Merged
Conversation
Minimal landing page (Home + Contact nav only, per the design decision to avoid dead links for unbuilt sections) and a full contact form: - Real accessible form markup with a topic select, email/message validation, and a submit button that stays enabled with inline errors on invalid submission (not disabled-until-valid, which is a documented accessibility anti-pattern) - Honeypot field plus Google reCAPTCHA v2 (checkbox variant, chosen over v3's behavioral scoring, which risks penalizing keyboard-only and screen-reader interaction patterns) plus KV-backed rate limiting - Messages relayed via the Gmail API (OAuth2), not raw SMTP, since Workers does not reliably support raw SMTP - All logic (validation, reCAPTCHA verification, rate limiting, Gmail send) factored into pure/testable modules with unit test coverage - Playwright/axe accessibility tests for the contact page, plus a keyboard-focus test for the inline-validation error flow Also fixes a real WCAG 2.2 AA violation the axe suite caught in the new nav (insufficient touch target size/spacing per criterion 2.5.8). Requires manual setup before functioning in production: a Cloudflare KV namespace for rate limiting, Gmail API OAuth2 credentials, and a reCAPTCHA site/secret key pair (see docs/SECURITY.md Open Items).
LinkedIn, X, and GitHub links, sized/spaced to meet WCAG 2.2 AA target-size criterion (2.5.8), consistent with the nav.
target="_blank" with rel="noopener noreferrer" (prevents the new tab from accessing the opening page via window.opener), plus visually hidden "(opens in a new tab)" text on each link so screen reader users get advance notice of the context change.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
aria-describedby, focus moves to the first invalid field — not disabled-until-valid, a documented accessibility anti-pattern)Not yet functional in production
Requires manual setup outside this repo before the contact form actually works: a Cloudflare KV namespace (
RATE_LIMIT), Gmail API OAuth2 credentials, and a reCAPTCHA site/secret key pair. Seedocs/SECURITY.mdOpen Items.Test plan
npm run typecheckpassesnpm run lint:js/lint:md/lint:htmlpassnpm run test:unitpasses (21 tests, including all new contact-form modules)npm run buildsucceedsnpm run test:e2epasses (5 tests: axe scans on both pages, skip-link, always-enabled submit button, inline-error focus behavior)🤖 Generated with Claude Code