Course outline

HTML & CSS / Adaptable interfaces / Lesson 12

Form layout, focus, and error states

Style forms without losing labels, native behavior, keyboard visibility, or actionable feedback.

About 4 min readContent reviewed
On this page

Lesson goals

  • Arrange relationships rather than coordinates
  • Provide focus that is actually visible
  • Verify recovery, not only rejection

Arrange relationships rather than coordinates

A form layout should keep each label, control, instruction, and error visibly related. A simple stacked field often adapts more reliably than absolute positioning. Use consistent spacing within a field and a larger separation between fields so grouping survives without borders. Grid can align a label column on wide layouts, but the source should still read label then control then help. Do not use placeholder text as a compact-layout substitute for labels. Long translations, zoom, autofill, and validation messages can all change height, so avoid fixed rows that assume every field stays on one line.

Preserve native affordances

Inputs and buttons have platform-specific appearance that communicates interaction. Resetting all appearance can remove useful affordances and creates a responsibility to restore every necessary state. Inherit font settings where suitable, provide adequate padding, and keep borders distinguishable from adjacent backgrounds. Disabled and readonly are different: a disabled control is generally unavailable for interaction and submission, while a readonly text control can remain focusable and submitted. Do not disable a control simply to make it look muted if users still need its value or explanation. Styling should reinforce the underlying HTML contract rather than contradict it.

Provide focus that is actually visible

The first example uses focus-visible to add a strong outline to keyboard-focused controls while keeping the browser’s default behavior available elsewhere. It does not start by removing every outline. Outline-offset separates the indicator from the border, and nearby spacing prevents it being clipped by another box. A focus style needs contrast against its surroundings in each state and theme. Test real Tab movement, not just a manually toggled pseudo-class in developer tools. A parent’s overflow clipping or a sticky header can hide an otherwise correct indicator, so focus visibility is a layout concern as well as a color choice.

html Form focus
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Form focus</title>
<style>
.field { display: grid; gap: .4rem; margin-block-end: 1rem; } input, button { font: inherit; padding: .6rem; } input:focus-visible, button:focus-visible { outline: 3px solid #174e9a; outline-offset: 3px; }
</style>
</head>
<body>
<main><h1>Contact details</h1><form><div class="field"><label for="contact">Contact name</label><input id="contact" name="contact" autocomplete="name"></div><button type="button">Local preview</button></form></main>
</body>
</html>
Expected output
A stacked field and a visible keyboard-focus treatment.

The label and control remain grouped in source and layout. Keyboard focus adds an explicit indicator without changing the input’s role.

Make feedback specific and associated

The second example is a static error-state demonstration. The control has aria-invalid="true", its error text is visible, and aria-describedby links that text to the field. The message says how to correct the problem rather than displaying only “invalid.” This markup does not implement a live validation system or announce dynamically inserted messages automatically. A real application must update state at an appropriate time, manage any summary and focus behavior deliberately, and retain entered values when possible. Client validity pseudo-classes can help presentation, but showing errors before a person has interacted can be confusing and must be designed intentionally.

html Associated error
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Associated error</title>
<style>
.error { color: #8b0000; } [aria-invalid="true"] { border: 2px solid #8b0000; } input { font: inherit; padding: .5rem; }
</style>
</head>
<body>
<main><h1>Quantity correction</h1><form><label for="quantity">Quantity</label><input id="quantity" name="quantity" type="number" min="1" value="0" aria-invalid="true" aria-describedby="quantity-error"><p id="quantity-error" class="error">Enter a quantity of at least 1.</p><button type="button">Static preview only</button></form></main>
</body>
</html>
Expected output
Quantity 0 is marked invalid with a specific correction message.

A visible correction message supplies meaning beyond the red border. The static aria-invalid state must be updated by real application behavior after correction.

Verify recovery, not only rejection

A usable form helps someone succeed after an error. Test submitting synthetic invalid values, finding the first problem, understanding its message, correcting it, and reaching a truthful confirmation. Check that errors remain associated after responsive rearrangement and that focus is not trapped or obscured. Color and icons can reinforce text but cannot replace it. Server-side failures need feedback too, especially when an operation may have partially succeeded or its result is uncertain. HTML and CSS provide structure and presentation; robust request handling, authorization, and persistence belong to the application layer and cannot be supplied by a green success border.

Common errors and fixes

Keyboard focus disappears

Why it happens
A reset removed outlines without a replacement.
How to fix it
Restore a visible indicator and inspect clipping.
Prevent it next time
Navigate every control with a keyboard.

An error cannot be connected to its field

Why it happens
The text is only visually adjacent.
How to fix it
Associate it with a stable id and appropriate description.
Prevent it next time
Review the accessible description with errors present.

A disabled value vanishes from submission

Why it happens
Disabled was used only to obtain a muted appearance.
How to fix it
Choose readonly or ordinary presentation according to the contract.
Prevent it next time
Test serialized data for every control state.

Check your understanding

1. Design a message for a date field that accepts future workshop dates only.

Show worked solution

Keep the visible label “Workshop date,” add a specific message such as “Choose a date after today,” associate it with the field, and mark the current invalid state. A real validator must use the application’s date and timezone policy rather than merely coloring the input red.

2. What should be tested after replacing default form styles with custom ones?

Show worked solution

Check keyboard focus, disabled and readonly behavior, checked states, error messages, autofill, zoom, high-contrast settings, and actual submitted values. The new appearance owns the responsibility for every affordance removed by the reset; a successful default screenshot covers only one of those states.

Key takeaways

  • Focus visibility depends on layout and contrast.
  • Associate errors and explain correction.
  • Form states have behavior as well as appearance.

Official references