HTML Tutorial
HTML Project: Contact Form
A contact page with name, email, topic, and message fields, plus labels that match every input.
What you will build
A contact page for Lumen Workshop. The form asks for a name, an email, a topic, and a message. A submit button sends the package. There is no server on StudyGrid, so method="post" andaction="#" are enough to practice the markup.
Every control has a matching label with for equal to the control’sid. A fieldset groups the details under a legend so the form is not a pile of boxes.
Preview with Try it in HTML at /html/try. Submitting will not store a message. The labels and focus order still have to be correct.
Tags you will use
| Tag | Job on this page |
|---|---|
form | Wraps the fields and the submit button |
fieldset and legend | Names the group of personal details |
label | Visible name of each control, tied with for |
input | Name and email |
select and option | Topic list |
textarea | The message body |
button | Submit, with an explicit type |
Build in slices
Pair one label with one text field. Click the word Name. The cursor should land in the box. If it does not, for and id do not match.
Example
<label for="full-name">Name</label>
<input id="full-name" name="full-name" type="text" autocomplete="name" required>Next wrap two fields in a fieldset. The legend is the group title, not a fake heading inside the form.
Example
<form action="#" method="post">
<fieldset>
<legend>How we can reach you</legend>
<p>
<label for="full-name">Name</label>
<input id="full-name" name="full-name" type="text" autocomplete="name" required>
</p>
<p>
<label for="email">Email</label>
<input id="email" name="email" type="email" autocomplete="email" required>
</p>
</fieldset>
<button type="submit">Send message</button>
</form>Placeholder text is not a label. If you hide the label with CSS, the for andid pair must still exist. Test that pair in /html/try by clicking the label text.
Complete document
Name, email, topic, message, submit. Labels on every control. A short intro above the form so the page is not only a widget.
Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Contact Lumen Workshop</title>
<style>
body {
margin: 0;
background: #eef2f6;
color: #1e293b;
font-family: "Segoe UI", sans-serif;
}
main {
max-width: 32rem;
margin: 0 auto;
padding: 1.5rem 1.2rem 2rem;
}
form {
background: #fff;
border: 1px solid #cbd5e1;
padding: 1rem 1.1rem 1.2rem;
}
fieldset {
border: 1px solid #cbd5e1;
margin: 0 0 1rem;
padding: 0.75rem 0.9rem;
}
legend { padding: 0 0.35rem; }
label { display: block; margin-bottom: 0.25rem; font-weight: 600; }
input, select, textarea {
width: 100%;
box-sizing: border-box;
margin-bottom: 0.85rem;
padding: 0.45rem 0.5rem;
border: 1px solid #94a3b8;
font: inherit;
}
textarea { min-height: 8rem; resize: vertical; }
button {
background: #0f766e;
color: #fff;
border: 0;
padding: 0.55rem 1rem;
font: inherit;
cursor: pointer;
}
</style>
</head>
<body>
<main>
<header>
<h1>Contact Lumen Workshop</h1>
<p>Tell us about a class, a hire, or a repair. We read every note.</p>
</header>
<form action="#" method="post">
<fieldset>
<legend>How we can reach you</legend>
<p>
<label for="full-name">Name</label>
<input id="full-name" name="full-name" type="text" autocomplete="name" required>
</p>
<p>
<label for="email">Email</label>
<input id="email" name="email" type="email" autocomplete="email" required>
</p>
</fieldset>
<p>
<label for="topic">Topic</label>
<select id="topic" name="topic" required>
<option value="">Choose one</option>
<option value="class">Evening class</option>
<option value="hire">Studio hire</option>
<option value="repair">Tool repair</option>
</select>
</p>
<p>
<label for="message">Message</label>
<textarea id="message" name="message" required></textarea>
</p>
<button type="submit">Send message</button>
</form>
</main>
</body>
</html>A second complete form
Same four fields, a different workshop. The empty first option on the select forces a real choice.
Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Write to East Wharf Bindery</title>
<style>
body { margin: 0; background: #1c1917; color: #f5f5f4; font-family: Georgia, serif; }
main { max-width: 30rem; margin: 0 auto; padding: 1.4rem; }
form { background: #292524; padding: 1rem; }
fieldset { border: 1px solid #57534e; margin-bottom: 1rem; }
label { display: block; margin: 0.4rem 0 0.2rem; }
input, select, textarea {
width: 100%; box-sizing: border-box; margin-bottom: 0.6rem;
padding: 0.4rem; border: 1px solid #78716c; background: #1c1917; color: inherit; font: inherit;
}
button { background: #f59e0b; border: 0; padding: 0.5rem 0.9rem; font: inherit; }
</style>
</head>
<body>
<main>
<h1>East Wharf Bindery</h1>
<p>Commissions, repairs, and workshop places.</p>
<form action="#" method="post">
<fieldset>
<legend>Your details</legend>
<label for="full-name">Name</label>
<input id="full-name" name="full-name" type="text" required>
<label for="email">Email</label>
<input id="email" name="email" type="email" required>
</fieldset>
<label for="topic">Topic</label>
<select id="topic" name="topic" required>
<option value="">Choose one</option>
<option value="commission">Commission</option>
<option value="repair">Repair</option>
<option value="class">Class booking</option>
</select>
<label for="message">Message</label>
<textarea id="message" name="message" required></textarea>
<p><button type="submit">Send</button></p>
</form>
</main>
</body>
</html>Common mistakes
- A label with no
for, sitting next to an input that has noid. Clicking the text then does nothing. - Using the same
idtwice. Identifiers must be unique on the page. - A
buttonwithouttype="submit"inside a second nested form. Keep one form. - Placeholder-only fields. When the box is filled, the hint disappears. Keep a visible label.
- Pointing
actionat a live inbox you do not control. Use#in this preview.
Practice tasks
- Add a telephone field with a label. Use
type="tel"and a uniqueid. - Add a fourth topic option. Keep the empty “Choose one” option at the top of the select.
- Move the topic select inside the fieldset. Confirm every label still focuses its control at /html/try.