Form Layout
Purpose
The form layout pattern defines how form fields, field groups, labels, helper text, validation messages, and action buttons are arranged within any data-entry context in Force UI. It applies to standalone forms within a page, forms inside cards or panels, forms inside modals, and step content inside wizard flows. The form layout does not prescribe the outer container (card, modal, page section) — only the internal structure of the fields themselves.
Structure
A form is organized as a vertical stack of field groups, separated from action buttons at the bottom. Each field group clusters related inputs under a group heading. The action button row sits below all field groups, separated by a visible divider or sufficient spacing.
+-----------------------------------------------+
| [Group Heading — optional] |
| [Group description — optional, secondary] |
| |
| [Label] [Required marker *] |
| [Input field — full width of form column] |
| [Helper text — below input, secondary color] |
| |
| [Label] |
| [Input field] |
| [Validation error — below input, error color]|
| |
| [Label] [Label] |
| [Input ] [Input ] ← two-column sub-row |
| |
| ─────────────────────────────────────────── |
| [Cancel] [Save / Submit] |
+-----------------------------------------------+
Fields are full-width within the form column by default. A form column is typically a single column occupying the full width of its container (a card, modal, or page section). For forms with more than six fields, or where related short fields belong side-by-side (for example, "First name" and "Last name"), a two-column sub-grid may be used within a group. Two-column sub-grids use a gap of --force-spacing-4 (16px) between columns.
Each field unit consists of four layers stacked vertically:
- The label (always above the input, never inline or to the left except in data-dense read-only metadata displays).
- The input control (text input, select, textarea, toggle, etc.).
- Helper text (optional, appears when guidance is always visible regardless of validation state).
- Validation message (appears only when the field has an error or success state; replaces helper text when both would appear simultaneously — the validation message takes priority).
The vertical gap between the label and the input is --force-spacing-1 (4px). The vertical gap between the input and the helper/validation text is --force-spacing-1 (4px). The vertical gap between consecutive field units within the same group is --force-spacing-4 (16px). The vertical gap between field groups is --force-spacing-8 (32px). The vertical gap between the last field group and the action row is --force-spacing-6 (24px) minimum, or a 1px divider in --force-color-border-default followed by --force-spacing-4 (16px) padding.
Group Headings
A group heading is a short title (using --force-font-size-base, 16px, semibold, --force-color-text-primary) followed optionally by a one-sentence description (using --force-font-size-sm, 14px, --force-color-text-secondary). The gap between the group heading block and the first field in the group is --force-spacing-4 (16px).
Do not use group headings for forms with fewer than four fields. Do not add more than four groups in a single form; a form that complex should be broken into a multi-step wizard instead.
Labels
Labels use --force-font-size-sm (14px), font weight medium (--force-font-weight-medium, 500), and --force-color-text-primary. Required fields display an asterisk (*) immediately after the label text, colored --force-color-text-error to communicate requirement without relying on text alone. An aria-required="true" attribute on the input is mandatory regardless of whether the asterisk is shown. Optional fields do not need any marker; most fields in enterprise forms are required, so marking optional fields with "(optional)" in the label is preferred over marking every required field.
Labels must always be visually positioned above their input. Do NOT use placeholder text as a substitute for a visible label. Placeholder text may supplement a label but must not replace it.
Helper Text
Helper text sits below the input and uses --force-font-size-xs (12px), regular weight, --force-color-text-tertiary. It describes constraints, format hints, or contextual notes that a user needs before interacting with the field. Keep helper text to one or two short sentences.
Validation Messages
Validation messages appear below the input (in the same position as helper text, replacing it when both would be shown). Error messages use --force-font-size-xs (12px), regular weight, --force-color-text-error. An error icon (Material Symbols, size xs, 14px) may precede the message text. The input border becomes --force-color-border-error and when the input is focused while in error state, the focus shadow becomes --force-shadow-focus-ring-error.
Success validation messages use --force-color-text-success with a checkmark icon. Warning messages use --force-color-text-warning with a warning icon.
Inline validation should fire on blur (when the user leaves the field), not on every keystroke. Form-level validation summary messages (if used) appear above the action buttons and below the last field group using a --force-color-bg-error-subtle callout.
Action Buttons
Action buttons are placed at the bottom right of the form. The primary action (Save, Submit, Create) uses the primary button variant. The secondary action (Cancel, Back) uses the secondary button variant. The cancel action must come to the left of the primary action in the visual reading order. Both buttons use size md (40px height) by default; size sm (32px) is acceptable inside compact modals.
Do NOT place destructive actions (Delete, Revoke) in the primary action slot unless the entire form is a confirmation of a destructive operation. In mixed forms, destructive actions live in a separate area far from the primary action, often in a "Danger zone" card at the bottom of the settings page rather than in the inline form footer.
Do NOT use the action button row to navigate forward in a multi-step wizard — the wizard pattern has its own navigation buttons within its sidebar-plus-content layout.
Responsive Behavior
At and above the large breakpoint (--force-breakpoint-lg, 992px), two-column field sub-grids within groups display side-by-side. The form column itself may be constrained to a narrower max-width (typically 640px–720px) within the content area for readability, particularly for settings pages where the form does not benefit from full-width treatment.
Between the small and large breakpoints, two-column sub-grids collapse to a single column, each field taking full width. Group headings retain their size. Action buttons remain right-aligned.
Below the small breakpoint (--force-breakpoint-sm, 576px), action buttons stack vertically, with the primary action on top and cancel below, each spanning full width. Labels, inputs, and helper text remain in their vertical stack order.
Token Usage
| Element | Token |
|---|---|
| Label text size | --force-font-size-sm (14px) |
| Label font weight | --force-font-weight-medium (500) |
| Label color | --force-color-text-primary |
| Required asterisk color | --force-color-text-error |
| Input background (default) | --force-color-bg-surface |
| Input border (default) | --force-color-border-default |
| Input border radius | --force-radius-input (6px) |
| Input border (focus) | --force-color-border-focus |
| Input focus shadow | --force-shadow-focus-ring |
| Input border (error) | --force-color-border-error |
| Input error focus shadow | --force-shadow-focus-ring-error |
| Input background (disabled) | --force-color-bg-muted |
| Input text (disabled) | --force-color-text-disabled |
| Helper text size | --force-font-size-xs (12px) |
| Helper text color | --force-color-text-tertiary |
| Error message color | --force-color-text-error |
| Success message color | --force-color-text-success |
| Group heading size | --force-font-size-base (16px) |
| Group heading weight | --force-font-weight-semibold (600) |
| Group description color | --force-color-text-secondary |
| Label-to-input gap | --force-spacing-1 (4px) |
| Input-to-helper gap | --force-spacing-1 (4px) |
| Field-to-field gap (within group) | --force-spacing-4 (16px) |
| Group-to-group gap | --force-spacing-8 (32px) |
| Last group to action row gap | --force-spacing-6 (24px) |
| Two-column field sub-grid gap | --force-spacing-4 (16px) |
| Form section divider color | --force-color-border-default |
Accessibility
Every input must have an associated <label> element connected via for/id pairing, or via aria-labelledby. Do not use aria-label as a replacement for a visible label in production forms. Visible labels are mandatory.
Helper text and validation messages must be associated with their input using aria-describedby. The input's aria-describedby should point to the id of the helper text element if it is always visible, and to the validation message element when an error is present. When both helper and validation text exist simultaneously, aria-describedby can list both ids separated by a space, but prefer replacing helper text with the validation message in the DOM for simplicity.
Error messages must not rely on color alone. The error icon and the text together communicate the error state visually. The aria-invalid="true" attribute must be set on the input when it is in an error state, and removed (or set to "false") when the error is resolved.
Fieldsets with a <legend> must be used when grouping radio buttons or checkboxes that share a single question or label. Do not wrap all form fields in a single <fieldset> unless the entire form represents a single logical unit with a meaningful title.
Form submission via Enter key must be handled consistently: pressing Enter while focused on any single-line input should submit the form. Multi-line textareas should not submit on Enter (they insert a newline). Pressing Tab must cycle through all interactive form controls in DOM order.
After a form submission error (server-side), focus must be programmatically moved to the error summary or to the first field in error state, so screen reader users are notified without requiring them to scan the entire form.
Guidance
Keep forms short and purposeful. If a form requires more than eight fields, evaluate whether a multi-step wizard is more appropriate. Long single-page forms are the most common source of user abandonment in enterprise UIs.
Use the two-column field sub-grid only for genuinely related short fields (first/last name, start/end date, city/state). Do not use it purely for space efficiency — it creates a scanning burden.
Do NOT mix input sizes (sm, md, lg) within the same form. Choose one size and use it consistently. The default is md (40px height). Use sm (32px) only for forms embedded in data-dense panels or table cells where vertical space is constrained.
Do NOT float action buttons to the left. The right-aligned primary action is the consistent position across all Perforce products. Users develop muscle memory for this placement.
Do NOT use primary buttons for both the submit and a secondary confirmation inside the same form. Only one element on a screen should use the primary button variant at a time.
Do NOT add decorative icons inside input fields unless they serve a functional purpose (a search icon triggering search, an eye icon toggling password visibility, a calendar icon opening a date picker). Decorative left-side icons in inputs add visual noise without aiding comprehension.
Form sections within a settings page should be wrapped in Cards. The card header contains the group heading and description. The card body contains the fields. The card footer contains the action buttons for that section. This keeps form actions scoped to their section rather than presenting a single save button at the bottom of a long page.
Composition
The form layout composes Field, Label, Input (or Select, Textarea, RadioGroup, Checkbox, Switch), and Button components. Within a settings page, it is wrapped by the Card composition. Within a modal, it is placed inside the Dialog body with the action buttons in the Dialog footer (not duplicated inside the form). Within a wizard step, the form fields occupy the right panel content area and the navigation buttons (Back/Next) are part of the wizard's own footer, separate from any in-form validation buttons.