Single line and message
The label sits inside the field and floats up when the field has focus or text. It is a real <label> connected to a real <input>, so browsers and screen readers treat it as a normal field. Click into these to try it.
View HTML
<div class="field">
<div class="field__control">
<input class="field__input" id="ff-name" name="name" type="text" placeholder=" " required value="" autocomplete="name" aria-describedby="ff-name-error">
<label class="field__label" for="ff-name">Name</label>
</div>
<p class="field__error" id="ff-name-error" hidden>
<!-- icon: priority_high -->
<span class="field__error-text"></span>
</p>
</div>
<div class="field field--multiline">
<div class="field__control">
<textarea class="field__input" id="ff-message" name="message" placeholder=" " required aria-describedby="ff-message-error"></textarea>
<label class="field__label" for="ff-message">Message</label>
</div>
<p class="field__error" id="ff-message-error" hidden>
<!-- icon: priority_high -->
<span class="field__error-text"></span>
</p>
</div>
Error
An error adds a red rule along the bottom, an icon, and a message in words that starts with "Error:". The field is marked aria-invalid and the message is linked with aria-describedby. The focus ring stays black, never red.
Error: Enter a valid email address.
View HTML
<div class="field">
<div class="field__control">
<input class="field__input" id="ff-email" name="email" type="email" placeholder=" " required value="" aria-invalid="true" aria-describedby="ff-email-error">
<label class="field__label" for="ff-email">Email</label>
</div>
<p class="field__error" id="ff-email-error">
<!-- icon: priority_high -->
<span class="field__error-text">Error: Enter a valid email address.</span>
</p>
</div>
Disabled and sending
While a form is sending, fields are read-only, not disabled, so their values still submit. Disabled fields use grey text and a quiet rule.
View HTML
<div class="field">
<div class="field__control">
<input class="field__input" id="ff-dis" name="disabled" type="text" placeholder=" " required value="" aria-describedby="ff-dis-error" disabled>
<label class="field__label" for="ff-dis">Disabled</label>
</div>
<p class="field__error" id="ff-dis-error" hidden>
<!-- icon: priority_high -->
<span class="field__error-text"></span>
</p>
</div>
<div class="field">
<div class="field__control">
<input class="field__input" id="ff-send" name="sending" type="text" placeholder=" " required value="Typed text" aria-describedby="ff-send-error" readonly>
<label class="field__label" for="ff-send">Sending</label>
</div>
<p class="field__error" id="ff-send-error" hidden>
<!-- icon: priority_high -->
<span class="field__error-text"></span>
</p>
</div>
States
| State | Bottom rule | Other |
|---|---|---|
| Rest | 1px, strong neutral | Label inside the box |
| Hover | 2px | No fill change |
| Focus | 2px | 4px focus ring, label floats up |
| Error | 1px alert (2px on focus) | Icon and "Error:" message below |
| Disabled or sending | 1px, quiet | Grey text, no hover change |
Color says valid or invalid. Stroke thickness says interaction. Those are two separate channels, so neither is the only signal.
Up next
Theme controls
The Light and Dark controls, and how the system follows the browser.