Skip to content

Form Layout with Flexbox

Styling individual inputs and buttons is only half the job. The other half is layout: how labels relate to their inputs, how groups of fields are spaced from each other, and where the submit button sits. Flexbox handles all of it cleanly.

The STO contact form wraps each label and its input in a .form-group div:

<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" name="name" placeholder="Your name" required>
</div>

This pattern is standard practice. The wrapper div gives you a target to control the relationship between the label and the input without touching the global layout.

The label needs to sit above the input, with a small gap between them. display: flex; flex-direction: column achieves this precisely:

.form-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
}

flex-direction: column stacks children vertically — label first, then input. gap: 0.5rem adds consistent space between them without a margin on either child.

Why Flexbox instead of just display: block? Because gap gives you precise control over the label-to-input spacing that is independent of the elements themselves. If you need to add an error message or helper text between the label and input later, gap handles that automatically without adjusting margin values.

While you are in this area, add styles for the label itself:

.form-group label {
font-weight: 600;
font-size: 0.9rem;
color: #1a1a1a;
}

A slightly smaller, bolder label distinguishes it visually from the input text without requiring a different color.

Each .form-group needs space below it to separate it from the next field. There are two ways to handle this:

Option A — margin on each group:

.form-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
margin-bottom: 1.5rem;
}

Option B — gap on the form container:

.contact-form {
display: flex;
flex-direction: column;
gap: 1.5rem;
}

Option B is cleaner. Making .contact-form a flex column container and using gap means you never need to remove the margin-bottom from the last group (the .form-group:last-child workaround). The gap only adds space between groups, not after the final one.

.contact-form {
display: flex;
flex-direction: column;
gap: 1.5rem;
max-width: 640px;
}

max-width: 640px constrains the form width on wide screens — a full-width contact form on a 1400px monitor is uncomfortable to fill out.

Some forms need two fields side by side — “First Name” and “Last Name” on the same row, for example. Wrap those groups in a row container:

<div class="form-row">
<div class="form-group">
<label for="first-name">First Name</label>
<input type="text" id="first-name" name="first-name">
</div>
<div class="form-group">
<label for="last-name">Last Name</label>
<input type="text" id="last-name" name="last-name">
</div>
</div>
.form-row {
display: flex;
gap: 1.5rem;
}
.form-row .form-group {
flex: 1;
}

flex: 1 on each .form-group inside .form-row distributes the available width equally. On mobile, you may want to stack them — that is covered in Module 07 (Responsive Design).

The STO contact form does not use two-column rows, but this pattern is worth knowing for your next project.

The .form-submit div wraps the submit button in the STO contact form. Align it to the right side of the form to match the conventional position for primary actions:

.form-submit {
display: flex;
justify-content: flex-end;
}

justify-content: flex-end pushes the button to the right edge of the form. If you want the button full-width on mobile, you can override this in a media query — covered in Module 07.

.contact-form {
display: flex;
flex-direction: column;
gap: 1.5rem;
max-width: 640px;
}
.form-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.form-group label {
font-weight: 600;
font-size: 0.9rem;
color: #1a1a1a;
}
.form-submit {
display: flex;
justify-content: flex-end;
}

Apply the form layout to the STO contact form:

  1. Add the layout CSS above to style.css.

  2. Open contact.html. Each label should sit directly above its input with a small gap. All form groups should be evenly spaced from each other.

  3. Verify the “Send Message” button is aligned to the right edge of the form.

  4. In DevTools, toggle flex-direction: column to flex-direction: row on .form-group. The label and input should move side by side — useful for visualizing how the flex axis controls the layout.

  5. Reduce the browser window width. On very narrow screens the form should still stack vertically, but the max-width constrains it on wide screens. Notice how max-width: 640px prevents the form from becoming too wide to fill out comfortably.

  6. If you want to test the two-column row pattern, add a .form-row div wrapping two .form-group divs in contact.html temporarily, apply the .form-row CSS, and observe how the fields split the available width equally.

  • The .form-group wrapper contains each <label> + <input> pair and is the target for Flexbox layout control.
  • display: flex; flex-direction: column; gap: 0.5rem on .form-group stacks the label above the input with precise spacing.
  • Make the form container a flex column with gap: 1.5rem to space groups evenly — this avoids the last-child margin workaround.
  • max-width: 640px on the form container prevents it from stretching uncomfortably wide on large screens.
  • Two-column form rows use display: flex; gap: 1.5rem on a .form-row wrapper, with flex: 1 on each .form-group inside.
  • justify-content: flex-end on .form-submit right-aligns the submit button, matching conventional form layout patterns.

Lesson 05 adds state styling — different appearances for focused, hovered, disabled, and invalid fields.