Patterns
Inputs & Forms
How the input components combine into a form: labelling, required fields, help text, and the states a field moves through. This is the pattern that governs every form in the suite.
Figma source: Inputs ★ ↗Anatomy of a field
Every field is the same four parts, in the same order. Only the control changes.
<div class="cds-field">
<!-- 1. Label — always present, always persistent -->
<label class="cds-field__label" for="rate">
Indirect cost rate<span class="cds-field__required" aria-hidden="true">*</span>
</label>
<!-- 2. Descriptive caption — optional, above the control -->
<span class="cds-field__caption" id="rate-help">
The federally negotiated rate for this institution.
</span>
<!-- 3. The control -->
<input class="cds-input" id="rate" type="text"
aria-describedby="rate-help rate-err" aria-invalid="true">
<!-- 4. Error message — replaces nothing, appears below -->
<span class="cds-field__error" id="rate-err">Enter a rate between 0 and 100.</span>
</div>| Part | Token | Rules |
|---|---|---|
| Label | --input-label, Label style 14/16 Bold | Always present. 8px above the control — the grid’s label rule. |
| Required marker | --input-required | A red asterisk, aria-hidden, with the real required attribute carrying the meaning. |
| Descriptive caption | --input-caption, Captions 12/14 | Optional. Persistent. Bound with aria-describedby. |
| Error message | --input-error-caption, Captions 12/14 Bold | Appears below the control. Never replaces the help text. |
Labels
The Figma Label property offers three values — Basic, Required and
None — and the third needs care.
| Value | When | Accessibility requirement |
|---|---|---|
| Basic | The default. A visible label above the control. | <label for> bound to the control’s id. |
| Required | The field must be completed to submit. | Visible asterisk plus the required attribute. Never the asterisk alone. |
| None | Only where an adjacent element already labels the control — a search field beside a Search button, or a repeated field in a table row where the column header is the label. | There must still be an accessible name: a visually hidden <label>, or aria-labelledby pointing at the element that labels it. |
A control with no accessible name is announced as “edit, blank” — the user is told there is a field, and nothing about what goes in it. It is one of the most common and most damaging accessibility failures in form-heavy software, and this suite is form-heavy.
If you set Label to None, you owe the field a hidden label or an
aria-labelledby. There is no third option.
Writing labels
| Instead of | Write | Why |
|---|---|---|
| PI | Principal investigator | Expand abbreviations on first use; the label is not the database column. |
| Date | Project start date | Say which date. Forms have several. |
| Amount ($) | Direct costs | Put the unit in the field or the help text, not the label. |
| Enter your project title | Project title | The label names the thing; it does not instruct. |
Required and optional
When to use it
- Mark required fields with an asterisk and the
requiredattribute. - Explain the asterisk once at the top of the form — “Fields marked * are required”.
- Where most fields are optional, mark the optional ones instead and say so.
- Only require what genuinely cannot be omitted.
When not to use it
- Do not mark required fields with colour alone.
- Do not use the asterisk without also setting
required— screen-reader users hear nothing. - Do not mark every field required and then let submission proceed anyway.
- Do not switch marking conventions between forms in the same product.
The Figma description for the Inputs pattern makes the dependency explicit: “Required fields must be completed for the user to complete said section of forms.” That is a section-level rule, which is why the Form Section List shows a required state per section rather than per field.
Field types
The pattern’s Field Type property enumerates nine variants. Several are
a Text Input with different validation rather than distinct components — which is worth
knowing, because it tells you where the behaviour lives.
| Field type | Built from | Notes from the file |
|---|---|---|
| Basic Text Input Field | Text Input | The base case. |
| Combobox | Combobox, Data Pill | “Components used: Search Assisted Entry, Data Pill.” |
| Date Input Field | Text Input + Date Picker | “Text Inputs, Date Picker (on click). See documentation on Date & Time.” |
| Time Input Field | Text Input | “See documentation on Date & Time.” |
| Number Input Field | Text Input | Numeric validation; right-align in tables. |
| Currency Input Field | Text Input | “See documentation on Currency.” |
| Resizable Text Area | Text Area | Vertical resize only. |
| Checkboxes | Checkbox | Wrapped in a fieldset with a legend. |
| Radio Buttons | Radio | Wrapped in a fieldset with a legend. |
The annotations point to “documentation on Date & Time” and “documentation on Currency” for the formatting rules those field types depend on. Neither is a page in the Cayuse DS Figma file, so the rules they describe — date format, time zone handling, currency symbol placement, rounding, negative values — are not written down anywhere this site can reach.
These are exactly the rules that go wrong quietly and expensively in research administration software. Worth locating those documents, or writing them, and linking them here.
Validation
Validation is the part of the pattern with the most ways to go wrong. The rules below hold for every field type.
When to validate
| Moment | Do | Do not |
|---|---|---|
| While typing | Nothing, unless clearing an error the user is actively fixing. | Flag an incomplete entry as wrong. The user is not finished. |
| On blur | Validate the field the user just left. | Move focus away from where the user went. |
| On submit | Validate everything, summarise, and move focus to the first problem. | Rely on the browser’s native prompt. |
When to use it
- Show an error summary at the top listing every problem, each linking to its field.
- Show the error inline at the field as well — the summary tells the user what is wrong, the inline message tells them where.
- Say what to do, not what went wrong: “Enter a rate between 0 and 100”, not “Invalid input”.
- Clear the error the moment the value becomes valid.
- Keep everything the user already typed.
When not to use it
- Do not validate on keystroke.
- Do not use colour alone — the border thickens to 2px and a text message appears.
- Do not use the browser’s native validation prompt.
- Do not put the error where the help text was; both should be visible together.
- Do not disable submit to indicate an invalid form — see Button.
Forms in this suite are split across Tabs and sections. If a hidden field fails validation, the user presses Submit and nothing appears to happen — the error is real but off-screen.
The fix has three parts: mark the section or tab as containing errors, include those fields in the error summary, and reveal the section when the user follows the summary link.
Field states
The pattern’s State property carries eight values — four more than the
individual components, because they describe selection and view-only conditions the base
components do not model.
| State | Meaning | Where it applies |
|---|---|---|
| Default | Resting. | All |
| Focus | Keyboard or pointer focus. | All |
| Disabled | Temporarily unavailable — may become available. | All |
| Error | Failed validation. | All |
| Results | A filtered list is showing. | Combobox |
| Single Selected / Multiple Selected | One or several values chosen. | Combobox, Select |
| View-Only | Permanently not editable for this user. | All |
Disabled means “not right now” — a dependency is unmet, and the field may become available. View-only means “not for you” — the value is displayed because the user needs to see it, and no interaction will ever change that.
Rendering view-only content as a disabled input is a common mistake with a real
cost: disabled controls are removed from the tab order, so a screen-reader user
navigating by form field never encounters the value at all. Read-only data should be
rendered as text with its label, or as an input with readonly — which
stays focusable and announced — not as disabled.
Form layout
When to use it
- One column. Multi-column forms are misread and skipped, especially when the columns are unrelated.
- Group related fields, 16px apart, with 24px or more between groups.
- Order fields the way the user holds the information, not the way the database stores it.
- Size fields to their content — a postcode field should look like a postcode field.
- Put the submit action at the end of the form, aligned with its start.
When not to use it
- Do not place two unrelated fields side by side to save vertical space.
- Do not use placeholder text instead of labels.
- Do not scatter required and optional fields randomly; group the required ones where you can.
- Do not reset the form on error.