Field Group
Field Group components are used as a wrapper component to create a consistent look and feel for form inputs and to provide a consistent way to handle error messages and helper text. It is suggest that this component be baked into your normal form input component.
<FieldGroup label="Label" name="example-1" sublabel="Sub-label content here lorem ipsum" instructions="Instruction content goes here lorem ipsum dolor sit."> <div>Slot</div></FieldGroup>
<div class="field-group" data-layout="FieldGroup"> <label for="example-1" id="label-example-1"> <span class="field-label">Label</span> <span class="field-sublabel">Sub-label content here lorem ipsum</span> </label> <div>Slot</div> <span class="field-instructions" id="instructions-example-1">Instruction content goes here lorem ipsum dolor sit.</span></div>
API Reference
This component is based on the <div>
element and can take in any additional HTML attributes. The following are the custom props that can be passed to the component.
Prop | Type | Default |
---|---|---|
name * | string | — |
label * | string | — |
labelHidden | boolean | false |
sublabel | string | — |
instructions | string | — |
required | boolean | "long" | false |
errorMessages | string[] | — |
Accessibility
The <FieldGroup>
component includes some accessibility features to help ensure that the component is usable and understandable by all users. The following are some of the key accessibility features that are included in the component and worth considering:
- The
label
element is associated with the input field by using thefor
attribute and includes any sublabel text. - The
label
andinstructions
elements are associated with the input field by using thearia-labelledby
attribute. This is an important consideration specifically for the instructions so that they are read out by screen readers when the field is focused, rather than after the input field. - The
errorMessages
are displayed below the input field and are marked up as a list of error messages. - The
labelHidden
prop visually hides the label but still ensures it is available to screen readers. - The
required
prop marks the input field as required and can include explicit “required” text if the value is set to"long"
.
Examples
Required
Use the required
prop to mark a field as required. You can also use the required
prop to mark a field as required and include explicit “required” text by passing the value "long"
.
<FieldGroup label="Label" name="example-2" required> <div>Slot</div></FieldGroup><FieldGroup label="Label" name="example-3" required="long"> <div>Slot</div></FieldGroup>
<div class="field-group" data-required data-layout="FieldGroup"> <label for="example-2" id="label-example-2"> <span class="field-label">Label</span> </label> <div>Slot</div></div><div class="field-group" data-required="long" data-layout="FieldGroup"> <label for="example-2" id="label-example-2"> <span class="field-label">Label</span> </label> <div>Slot</div></div>
With Helper Text
Use the sublabel
and instructions
props to add helper text to the input field.
<FieldGroup label="Label" name="example-4" sublabel="Sublabel lorem ipsum dolor sit amet." instructions="Instructions lorem ipsum dolor."> <div>Slot</div></FieldGroup>
<div class="field-group" data-required data-layout="FieldGroup"> <label for="example-4" id="label-example-4"> <span class="field-label">Label</span> <span class="field-sublabel"> Sublabel lorem ipsum dolor sit amet. </span> </label> <div>Slot</div> <span class="field-instructions" id="instructions-example-4"> Instructions lorem ipsum dolor. </span></div>
With Errors
Use the errorMessages
prop to display error messages below the input field.
- Error message 1
- Error message 2
<FieldGroup label="Label" name="example-5" errorMessages={['Error message 1', 'Error message 2']}> <div>Slot</div></FieldGroup>
<div class="field-group" data-state="error" data-layout="FieldGroup"> <label for="example-5" id="label-example-5"> <span class="field-label">Label</span> </label> <div>Slot</div> <ul class="field-errors"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="absolute left-0 top-2 size-16 shrink-0" > <line x1="12" y1="9" x2="12" y2="13"></line> <path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path> <line x1="12" y1="17" x2="12.01" y2="17"></line> </svg> <li>Error message 1</li> <li>Error message 2</li> </ul></div>
With Hidden Label
Use the labelHidden
prop to visually hide the label. When hidden, the label will still be available to screen readers.
Astro Component
It’s recommended that an element like this is made as a reusable component. In this case, we have put together the following Astro component to serve as a blueprint for how you could possibly set up a field group component, and what properties to consider.
---import type { HTMLAttributes } from 'astro/types'
interface Props extends HTMLAttributes<'div'> { /** Unique name of the input field. Should follow a kebab-case naming convention (e.g. "first-name") */ name: string /** Visual or assistive label for the input field */ label: string /** Whether to visually hide the label or not. When hidden, the label will still be available to screen readers */ labelHidden?: boolean /** Sublabel for the input field */ sublabel?: string /** Instructions for the input field that will be placed below the input */ instructions?: string /** Whether the input field is required or not. If set to 'long', the input field will be marked as required and include explicit "required" text. */ required?: boolean | 'long' /** Error messages to display below the input field */ errorMessages?: string[]}
const { name, label, labelHidden = false, sublabel, instructions, required = false, errorMessages = [], class: className, ...attrs} = Astro.props---
<div class:list={['field-group', className]} data-required={required} data-state={errorMessages.length > 0 ? 'error' : null} data-layout="FieldGroup" {...attrs}> <label for={name} id={`label-${name}`}> <span class:list={['field-label', { 'sr-only': labelHidden }]}>{label}</span > {sublabel && <span class="field-sublabel">{sublabel}</span>} </label>
<slot />
{ instructions && ( <span class="field-instructions" id={`instructions-${name}`}> {instructions} </span> ) }
{ errorMessages.length > 0 && ( <ul class="field-errors"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="absolute left-0 top-2 size-16 shrink-0" > <line x1="12" y1="9" x2="12" y2="13" /> <path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" /> <line x1="12" y1="17" x2="12.01" y2="17" /> </svg> {errorMessages.map((message) => ( <li>{message}</li> ))} </ul> ) }</div>