Vanilla Breeze

← All tutorials · ~10 minutes · Layer focus: forms & validation

Tutorial: Contact form

Build an accessible contact form with native HTML validation, helpful error messages, and a submit-success toast — in that order. Every layer works without the next, and the whole thing gracefully degrades if JS never loads.

Step 1 — A form the platform already understands

Before any framework, HTML forms did required fields, email validation, and error messages. Start there.

This form submits to a server, validates on the client, and shows browser-default error tooltips. No CSS, no JS.

Step 2 — Lay it out with data-*

A form is a vertical stack. Buttons belong in a cluster. The whole thing gets a readable width via data-layout-max="narrow". No wrappers added.

Notice the <footer> inside the form: it's semantically fine and we use data-layout="cluster" to keep the buttons on one line, wrapping on narrow screens.

Step 3 — Group fields with <form-field>

The <form-field> custom element wraps a label, control, and message slot. You get consistent spacing, accessible error messaging, and a hook for live validation feedback — without rewriting your inputs.

Every data-message-* attribute maps to a ValidityState flag. The form still uses native HTML validation — <form-field> just surfaces the right message at the right time, in plain text, next to the right input.

Step 4 — Confirm success with <toast-msg>

When the form submits, the server either renders a thank-you page or returns a 200. For the JS-enhanced path, pop a toast so the reader knows it worked — without navigating.

If JavaScript fails to load, the form still submits to /contact and the server takes over. The toast is a progressive enhancement, not a dependency.

What you learned