Field unit

Fields usually consist of a label, a control, and some messages below them. This pattern shows how to build them with Brio.

All the patterns on this page are made with existing compositions and classes.

Label + Field Unit

Most systems have form-related classes for managing layout in a form. Brio takes a slightly different approach by relying more on existing compositions that exist in the library. This reduces the amount of CSS and HTML needed to build forms, and you can reuse a familiar set of composition patterns.

For forms, I tend to use a small wrapper with `.stack.is-tight. The label sits directly before the control, with the control directly underneath. .stack helps keep the vertical layout between them consistent.

<div class="stack is-tight" style="max-inline-size: 24rem">
            <label for="unit-name">Name</label>
            <input id="unit-name" type="text" class="input" />
        </div>

You can do something similar with the label to the left or right of an input control by using the .cluster.is-snug composition instead.

<div class="stack">
            <div class="cluster is-snug" style="max-inline-size: 24rem">
                <label for="unit-name-left">Name</label>
                <input id="unit-name-left" type="text" class="input" style="flex-grow: 1" />
            </div>
        </div>

Required fields

A small * is added to the label when the native required attribute is present. When the label directly precedes a required control inside the stack, Brio draws the asterisk itself and keeps it out of the field's accessible name, so the label you write is the label a screen reader describes.

A field without required reads as optional. If almost every field in a form is required, turn it around: mark only the exceptions and write (optional) in their labels.

<div class="stack is-snug" style="max-inline-size: 24rem">
            <div class="stack is-tight">
                <label for="unit-email">Email</label>
                <input id="unit-email" type="email" class="input" required />
            </div>
            <div class="stack is-tight">
                <label for="unit-company">Company (optional)</label>
                <input id="unit-company" type="text" class="input" />
            </div>
        </div>

Help text

Helper text often goes udner the control, usually muted and one step smaller, and wired with aria-describedby pointing at its id. It can be used to describe the field's purpose or expected format. I like to add helper text, because placeholder text disappears the moment someone starts typing, and helper text does not.

Lowercase letters, numbers, and dashes. This is how you appear in mentions.

<div>
            <div class="flow flow-space-2xs" style="max-inline-size: 24rem">
                <label for="unit-handle">Handle</label>
                <input
                    id="unit-handle"
                    type="text"
                    class="input"
                    required
                    aria-describedby="unit-handle-help"
                />
                <p id="unit-handle-help" class="text-muted text--1">
                    Lowercase letters, numbers, and dashes. This is how you appear in mentions.
                </p>
            </div>
        </div>

Error messages

An error message is often reported by the server, or sometimes by the frontend, depending on the project. It's used to describe what went wrong or what validation step failed. It's similar to the help text, but usually uses text-danger, and the control it belongs to usually has the aria-invalid attribute set to "true", which makes the border and the text change together. If the field also has standing help text, list both ids, space separated; a screen reader then announces the help and the verdict in one pass.

Twelve characters, from your invitation email.

That code has expired.

<div>
            <div class="stack is-snug" style="max-inline-size: 24rem">
                <div class="stack is-tight">
                    <label for="unit-code">Invite code</label>
                    <input
                        id="unit-code"
                        type="text"
                        class="input"
                        required
                        aria-invalid="true"
                        aria-describedby="unit-code-help unit-code-error"
                    />
                </div>
                <p id="unit-code-help" class="text-muted text--1">
                    Twelve characters, from your invitation email.
                </p>
                <p id="unit-code-error" class="text-danger text--1">That code has expired.</p>
            </div>
        </div>