Building the Complete STO Contact Form
This lesson is the integration point for everything in Module 06. The previous five lessons covered each technique in isolation — resetting browser defaults, styling inputs, customizing buttons and selects, laying out the form with Flexbox, and adding state feedback. Now you put it all together in one complete CSS section for the STO contact form.
The complete contact form HTML
Section titled “The complete contact form HTML”Before writing the CSS, confirm your contact.html has this structure. The class names must match exactly — the CSS targets them precisely.
<section class="contact-section"> <div class="content-wrapper"> <h1>Contact Us</h1> <form class="contact-form" action="#" method="post">
<div class="form-group"> <label for="name">Name</label> <input type="text" id="name" name="name" placeholder="Your name" required> </div>
<div class="form-group"> <label for="email">Email</label> <input type="email" id="email" name="email" placeholder="you@example.com" required> </div>
<div class="form-group"> <label for="tour">Interested in</label> <select id="tour" name="tour"> <option value="">Select a tour...</option> <option value="alpine">Alpine Meadows Trek</option> <option value="rainforest">Olympic Rainforest Trail</option> </select> </div>
<div class="form-group"> <label for="message">Message</label> <textarea id="message" name="message" rows="5" placeholder="Tell us about your group..."></textarea> </div>
<div class="form-submit"> <button type="submit" class="btn btn-primary">Send Message</button> </div>
</form> </div></section>The complete contact form CSS
Section titled “The complete contact form CSS”Add this entire block to your style.css, organized as a single contact form section:
/* =========================== Contact Form =========================== */
.contact-section { padding: 5rem 0;}
/* Form layout */.contact-form { display: flex; flex-direction: column; gap: 1.5rem; max-width: 640px;}
/* Form group: label stacked above input */.form-group { display: flex; flex-direction: column; gap: 0.5rem;}
.form-group label { font-weight: 600; font-size: 0.9rem; color: #1a1a1a;}
/* All interactive controls */.form-group input,.form-group textarea,.form-group select { width: 100%; padding: 0.75rem 1rem; border: 1px solid #d8d2c8; border-radius: 4px; background-color: #ffffff; color: #1a1a1a; font: inherit; font-size: 1rem; transition: border-color 150ms ease, box-shadow 150ms ease;}
/* Hover */.form-group input:hover,.form-group textarea:hover,.form-group select:hover { border-color: #5a5a5a;}
/* Focus — replaces browser outline */.form-group input:focus,.form-group textarea:focus,.form-group select:focus { outline: none; border-color: #2c4a1e; box-shadow: 0 0 0 3px rgba(44, 74, 30, 0.2);}
/* Validation error after user interaction */.form-group input:invalid:not(:placeholder-shown),.form-group textarea:invalid:not(:placeholder-shown) { border-color: #c0392b; box-shadow: 0 0 0 3px rgba(192, 57, 43, 0.15);}
/* Disabled */.form-group input:disabled,.form-group textarea:disabled,.form-group select:disabled { opacity: 0.5; cursor: not-allowed; background-color: #eae4da;}
/* Placeholder text */.form-group input::placeholder,.form-group textarea::placeholder { color: #5a5a5a; font-style: italic;}
/* Textarea */.form-group textarea { resize: vertical; min-height: 140px;}
/* Select: custom arrow */.form-group select { appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%235a5a5a' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 1rem center; padding-right: 2.5rem;}
/* Submit button alignment */.form-submit { display: flex; justify-content: flex-end;}Walking through the structure
Section titled “Walking through the structure”The CSS is organized in a clear progression:
- Section layout —
contact-sectionpadding separates the form from surrounding page content - Form layout —
.contact-formis a flex column;gap: 1.5remspaces all groups without individual margins;max-width: 640pxkeeps the form comfortable to fill out on wide screens - Group layout —
.form-groupis a flex column;gap: 0.5remtightly connects the label to its input - Label — slightly smaller and bolder than body text, clearly distinct from the input content
- Controls — shared rules for all three interactive elements: width, padding, border, background, font, transition
- States — hover, focus, invalid, disabled — in that order. CSS applies them by specificity, but the cascade means later declarations override earlier ones for the same specificity, so define in the order they should naturally override
- Placeholders — subordinate visual treatment
- Textarea specifics — resize control and minimum height
- Select specifics — native arrow removed, custom SVG arrow added
- Button alignment — right-aligned with
flex-end
What changes in Module 08
Section titled “What changes in Module 08”In Module 08 (Styling STO End-to-End) you will convert all hardcoded hex values to CSS custom properties:
/* This lesson */border-color: #2c4a1e;box-shadow: 0 0 0 3px rgba(44, 74, 30, 0.2);
/* Module 08 equivalent */border-color: var(--color-primary);box-shadow: 0 0 0 3px rgba(44, 74, 30, 0.2); /* rgba stays hardcoded */The contact form rules themselves do not change — only the color references become variables defined in :root. Everything you built here carries directly into the final module.
Exercise
Section titled “Exercise”Test the complete contact form:
-
Add the full CSS block above to
style.css. Opencontact.html. -
Run through the default state: all inputs should have the warm gray border, white background, and your page font.
-
Hover over each field — the border should darken slightly. Click into it — the green focus ring should appear. Tab through every field with the keyboard — each one should show the focus ring.
-
Test placeholder text: the placeholder should be gray and italic, clearly different from typed text.
-
Type in the Message textarea. Drag the resize handle at the bottom-right — it should allow vertical resizing but not horizontal.
-
Test email validation: type
notanemailin the Email field, then click elsewhere. A red border and shadow should appear. Typehello@example.com— the red should disappear. -
Test the empty-field timing: clear the Email field completely. The red border should NOT appear for an empty required field — the
:not(:placeholder-shown)rule only applies after the user has typed something invalid. -
Open DevTools and manually add the
disabledattribute to the select element. It should fade to 50% opacity with the alternate background. -
Click “Send Message” — the button should lift with a shadow. Hold the click — it should press back down. Tab to the button with keyboard — the focus ring should be visible.
Module 06 recap
Section titled “Module 06 recap”Across the six lessons in this module you have:
- Reset browser defaults that make form elements look like OS-native controls
- Applied
font: inheritso all form elements use the page font - Styled inputs, textareas, and selects with consistent padding, border, background, and border-radius
- Replaced
outline: nonewith a custombox-shadowfocus ring — maintaining accessibility - Added
::placeholderstyles that are visually subordinate to entered text - Controlled textarea resize behavior and minimum height
- Reset the browser button default and built the
.btn/.btn-primarydesign - Applied
:hover,:active,:disabled, and:focusstates to buttons - Removed the native
<select>arrow and added a custom CSS-drawn chevron - Used Flexbox to stack labels above inputs, space form groups evenly, and right-align the submit button
- Styled the complete state progression: default → hover → focus → invalid → disabled
- Used
:invalid:not(:placeholder-shown)to show validation errors only after user interaction
Module 07 covers Responsive Design — making the STO site adapt from mobile to desktop using the mobile-first methodology and media queries.