Form
Field
Usage#
import { WDS_FIELD } from '@wds/angular/field';Examples#
Input#
wds-field links wds-field-label and wds-field-description with the control, also across the shadow DOM of wds-* controls.
import { Component } from '@angular/core';
import { WdsFieldComponent, WdsFieldDescriptionComponent, WdsFieldGroupComponent, WdsFieldLabelComponent } from '@wds/angular/field';
import { WdsInputComponent } from '@wds/angular/input';
@Component({
selector: 'app-field-input-demo',
imports: [WdsFieldGroupComponent, WdsFieldComponent, WdsFieldLabelComponent, WdsFieldDescriptionComponent, WdsInputComponent],
template: `
<wds-field-group class="w-full max-w-md">
<wds-field>
<wds-field-label>Username</wds-field-label>
<wds-input placeholder="Max Leiter" autocomplete="username"></wds-input>
<wds-field-description>Choose a unique username for your account.</wds-field-description>
</wds-field>
<wds-field>
<wds-field-label>Password</wds-field-label>
<wds-field-description>Must be at least 8 characters long.</wds-field-description>
<wds-input type="password" placeholder="••••••••" autocomplete="new-password"></wds-input>
</wds-field>
</wds-field-group>
`,
})
export class FieldInputDemo {}Textarea#
import { Component } from '@angular/core';
import { WdsFieldComponent, WdsFieldDescriptionComponent, WdsFieldLabelComponent } from '@wds/angular/field';
import { WdsTextareaComponent } from '@wds/angular/textarea';
@Component({
selector: 'app-field-textarea-demo',
imports: [WdsFieldComponent, WdsFieldLabelComponent, WdsFieldDescriptionComponent, WdsTextareaComponent],
template: `
<wds-field class="w-full max-w-md">
<wds-field-label>Feedback</wds-field-label>
<wds-textarea placeholder="Your feedback helps us improve..." rows="4"></wds-textarea>
<wds-field-description>Share your thoughts about our service.</wds-field-description>
</wds-field>
`,
})
export class FieldTextareaDemo {}Fieldset#
wds-field-set is a group named by its wds-field-legend; a description right after the legend describes the group.
import { Component } from '@angular/core';
import { WdsFieldComponent, WdsFieldDescriptionComponent, WdsFieldGroupComponent, WdsFieldLabelComponent, WdsFieldLegendComponent, WdsFieldSetComponent } from '@wds/angular/field';
import { WdsInputComponent } from '@wds/angular/input';
@Component({
selector: 'app-field-fieldset-demo',
imports: [WdsFieldSetComponent, WdsFieldLegendComponent, WdsFieldDescriptionComponent, WdsFieldGroupComponent, WdsFieldComponent, WdsFieldLabelComponent, WdsInputComponent],
template: `
<wds-field-set class="w-full max-w-md">
<wds-field-legend>Address Information</wds-field-legend>
<wds-field-description>We need your address to deliver your order.</wds-field-description>
<wds-field-group>
<wds-field>
<wds-field-label>Street Address</wds-field-label>
<wds-input placeholder="123 Main St" autocomplete="street-address"></wds-input>
</wds-field>
<div class="grid grid-cols-2 gap-4">
<wds-field>
<wds-field-label>City</wds-field-label>
<wds-input placeholder="New York" autocomplete="address-level2"></wds-input>
</wds-field>
<wds-field>
<wds-field-label>Postal Code</wds-field-label>
<wds-input placeholder="90502" autocomplete="postal-code"></wds-input>
</wds-field>
</div>
</wds-field-group>
</wds-field-set>
`,
})
export class FieldFieldsetDemo {}Checkbox#
orientation="horizontal" puts the control next to the label. wds-field-content stacks a label and a description.
import { Component } from '@angular/core';
import { WdsFieldComponent, WdsFieldContentComponent, WdsFieldDescriptionComponent, WdsFieldGroupComponent, WdsFieldLabelComponent, WdsFieldLegendComponent, WdsFieldSetComponent } from '@wds/angular/field';
import { WdsCheckboxComponent } from '@wds/angular/checkbox';
@Component({
selector: 'app-field-checkbox-demo',
imports: [WdsFieldGroupComponent, WdsFieldSetComponent, WdsFieldLegendComponent, WdsFieldDescriptionComponent, WdsFieldComponent, WdsFieldLabelComponent, WdsFieldContentComponent, WdsCheckboxComponent],
template: `
<wds-field-group class="w-full max-w-md">
<wds-field-set>
<wds-field-legend variant="label">Show these items on the desktop</wds-field-legend>
<wds-field-description>Select the items you want to show on the desktop.</wds-field-description>
<wds-field-group data-checkbox-group>
<wds-field orientation="horizontal">
<wds-checkbox checked></wds-checkbox>
<wds-field-label>Hard disks</wds-field-label>
</wds-field>
<wds-field orientation="horizontal">
<wds-checkbox></wds-checkbox>
<wds-field-label>External disks</wds-field-label>
</wds-field>
<wds-field orientation="horizontal">
<wds-checkbox></wds-checkbox>
<wds-field-label>CDs, DVDs, and iPods</wds-field-label>
</wds-field>
</wds-field-group>
</wds-field-set>
<wds-field orientation="horizontal">
<wds-checkbox checked></wds-checkbox>
<wds-field-content>
<wds-field-label>Sync Desktop & Documents folders</wds-field-label>
<wds-field-description>Your Desktop & Documents folders are being synced with iCloud Drive. You can access them from other devices.</wds-field-description>
</wds-field-content>
</wds-field>
</wds-field-group>
`,
})
export class FieldCheckboxDemo {}Radio#
import { Component } from '@angular/core';
import { WdsFieldDescriptionComponent, WdsFieldLegendComponent, WdsFieldSetComponent } from '@wds/angular/field';
import { WdsRadioComponent, WdsRadioGroupComponent } from '@wds/angular/radio';
@Component({
selector: 'app-field-radio-demo',
imports: [WdsFieldSetComponent, WdsFieldLegendComponent, WdsFieldDescriptionComponent, WdsRadioGroupComponent, WdsRadioComponent],
template: `
<wds-field-set class="w-full max-w-md">
<wds-field-legend variant="label">Subscription Plan</wds-field-legend>
<wds-field-description>Yearly and lifetime plans offer significant savings.</wds-field-description>
<wds-radio-group value="monthly">
<wds-radio value="monthly">Monthly ($9.99/month)</wds-radio>
<wds-radio value="yearly">Yearly ($99.99/year)</wds-radio>
<wds-radio value="lifetime">Lifetime ($299.99)</wds-radio>
</wds-radio-group>
</wds-field-set>
`,
})
export class FieldRadioDemo {}Switch#
import { Component } from '@angular/core';
import { WdsFieldComponent, WdsFieldContentComponent, WdsFieldDescriptionComponent, WdsFieldLabelComponent } from '@wds/angular/field';
import { WdsSwitchComponent } from '@wds/angular/switch';
@Component({
selector: 'app-field-switch-demo',
imports: [WdsFieldComponent, WdsFieldContentComponent, WdsFieldLabelComponent, WdsFieldDescriptionComponent, WdsSwitchComponent],
template: `
<wds-field class="w-full max-w-md" orientation="horizontal">
<wds-field-content>
<wds-field-label>Multi-factor authentication</wds-field-label>
<wds-field-description>Enable multi-factor authentication. If you do not have a two-factor device, you can use a one-time code sent to your email.</wds-field-description>
</wds-field-content>
<wds-switch></wds-switch>
</wds-field>
`,
})
export class FieldSwitchDemo {}Choice card#
import { Component } from '@angular/core';
import { WdsFieldDescriptionComponent, WdsFieldLegendComponent, WdsFieldSetComponent } from '@wds/angular/field';
import { WdsRadioComponent, WdsRadioGroupComponent } from '@wds/angular/radio';
@Component({
selector: 'app-field-choice-card-demo',
imports: [WdsFieldSetComponent, WdsFieldLegendComponent, WdsFieldDescriptionComponent, WdsRadioGroupComponent, WdsRadioComponent],
template: `
<wds-field-set class="w-full max-w-md">
<wds-field-legend variant="label">Compute Environment</wds-field-legend>
<wds-field-description>Select the compute environment for your cluster.</wds-field-description>
<wds-radio-group value="kubernetes">
<wds-radio variant="card" value="kubernetes">
Kubernetes
<span slot="description">Run GPU workloads on a K8s configured cluster.</span>
</wds-radio>
<wds-radio variant="card" value="vm">
Virtual Machine
<span slot="description">Access a VM configured cluster to run GPU workloads.</span>
</wds-radio>
</wds-radio-group>
</wds-field-set>
`,
})
export class FieldChoiceCardDemo {}Field group#
wds-field-group stacks fields and sets; wds-field-separator divides them.
import { Component } from '@angular/core';
import { WdsFieldComponent, WdsFieldDescriptionComponent, WdsFieldGroupComponent, WdsFieldLabelComponent, WdsFieldLegendComponent, WdsFieldSeparatorComponent, WdsFieldSetComponent } from '@wds/angular/field';
import { WdsCheckboxComponent } from '@wds/angular/checkbox';
@Component({
selector: 'app-field-field-group-demo',
imports: [WdsFieldGroupComponent, WdsFieldSetComponent, WdsFieldLegendComponent, WdsFieldDescriptionComponent, WdsFieldComponent, WdsFieldLabelComponent, WdsFieldSeparatorComponent, WdsCheckboxComponent],
template: `
<wds-field-group class="w-full max-w-md">
<wds-field-set>
<wds-field-legend variant="label">Responses</wds-field-legend>
<wds-field-description>Get notified when a response to a long request, like research, is ready.</wds-field-description>
<wds-field-group data-checkbox-group>
<wds-field orientation="horizontal" disabled>
<wds-checkbox checked disabled></wds-checkbox>
<wds-field-label>Push notifications</wds-field-label>
</wds-field>
</wds-field-group>
</wds-field-set>
<wds-field-separator></wds-field-separator>
<wds-field-set>
<wds-field-legend variant="label">Tasks</wds-field-legend>
<wds-field-description>Get notified when tasks you've created have updates. <a href="#">Manage tasks</a></wds-field-description>
<wds-field-group data-checkbox-group>
<wds-field orientation="horizontal">
<wds-checkbox></wds-checkbox>
<wds-field-label>Push notifications</wds-field-label>
</wds-field>
<wds-field orientation="horizontal">
<wds-checkbox checked></wds-checkbox>
<wds-field-label>Email notifications</wds-field-label>
</wds-field>
</wds-field-group>
</wds-field-set>
</wds-field-group>
`,
})
export class FieldFieldGroupDemo {}Responsive#
orientation="responsive" is vertical in a narrow wds-field-group and horizontal once the group is at least 28rem wide (a container query).
import { Component } from '@angular/core';
import { WdsFieldComponent, WdsFieldContentComponent, WdsFieldDescriptionComponent, WdsFieldGroupComponent, WdsFieldLabelComponent, WdsFieldSeparatorComponent } from '@wds/angular/field';
import { WdsInputComponent } from '@wds/angular/input';
@Component({
selector: 'app-field-responsive-demo',
imports: [WdsFieldGroupComponent, WdsFieldComponent, WdsFieldContentComponent, WdsFieldLabelComponent, WdsFieldDescriptionComponent, WdsFieldSeparatorComponent, WdsInputComponent],
template: `
<wds-field-group class="w-full max-w-2xl">
<wds-field orientation="responsive">
<wds-field-content>
<wds-field-label>Name</wds-field-label>
<wds-field-description>Provide your full name for identification.</wds-field-description>
</wds-field-content>
<wds-input placeholder="Evil Rabbit"></wds-input>
</wds-field>
<wds-field-separator>or</wds-field-separator>
<wds-field orientation="responsive">
<wds-field-content>
<wds-field-label>Nickname</wds-field-label>
<wds-field-description>Shown instead of your name.</wds-field-description>
</wds-field-content>
<wds-input placeholder="evilrabbit"></wds-input>
</wds-field>
</wds-field-group>
`,
})
export class FieldResponsiveDemo {}Validation#
invalid on wds-field colors the label and sets invalid on the control. wds-field-error is linked to the control as a description; in Angular it takes errors() from Signal Forms, in HTML and React the validationMessage of the control.
import { Component, computed, signal } from '@angular/core';
import { FormField, form, minLength, required } from '@angular/forms/signals';
import { WDS_FIELD } from '@wds/angular/field';
import { WdsTextField } from '@wds/angular/forms';
import { WdsInputComponent } from '@wds/angular/input';
@Component({
selector: 'app-field-validation-demo',
imports: [...WDS_FIELD, FormField, WdsInputComponent, WdsTextField],
template: `
<wds-field class="w-sm max-w-full" [invalid]="showErrors()">
<wds-field-label>Username</wds-field-label>
<wds-input placeholder="Max Leiter" autocomplete="username" [formField]="accountForm.username" />
<wds-field-description>At least 3 characters.</wds-field-description>
<wds-field-error [errors]="showErrors() ? accountForm.username().errors() : []" />
</wds-field>
`,
})
export class FieldValidationDemo {
protected readonly account = signal({ username: '' });
protected readonly accountForm = form(this.account, (path) => {
required(path.username, { message: 'Username is required.' });
minLength(path.username, 3, { message: 'Username must be at least 3 characters.' });
});
/** Errors appear after the user leaves the field. */
protected readonly showErrors = computed(() => this.accountForm.username().touched() && this.accountForm.username().invalid());
}API References#
wds-field#
wds-* controls).
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
| orientation | WdsFieldOrientation | 'vertical' | wds-field-group is at least 28rem wide. |
| invalid | boolean | false | invalid on the control. |
| disabled | boolean | false | disabled on the control itself). |
Slots
| Slot | Description |
|---|---|
| (default) | wds-field-label, the control, wds-field-content, wds-field-description, wds-field-error. |
CSS custom properties
| Property | Description |
|---|---|
| --wds-field-gap |
wds-field-label#
for, or the first control in the same wds-field.
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
| for.htmlFor | string | '' | for on <label>). |
Slots
| Slot | Description |
|---|---|
| (default) |
wds-field-description#
wds-field links it to the control as its description.
Slots
| Slot | Description |
|---|---|
| (default) |
CSS parts (::part)
| Part | Description |
|---|---|
| base |
wds-field-error#
errors, e.g. from Signal Forms) or slotted content.
Hidden when there are no errors. wds-field links it to the control as a description.
Properties
| Property | Description |
|---|---|
| errorsReadonlyArray<WdsFieldErrorItem> |
Slots
| Slot | Description |
|---|---|
| (default) | errors). |
wds-field-content#
Slots
| Slot | Description |
|---|---|
| (default) | wds-field-label or wds-field-title, and wds-field-description. |
wds-field-title#
Slots
| Slot | Description |
|---|---|
| (default) |
wds-field-set#
<fieldset>: role group named by the legend).
A wds-field-description right after the legend describes the group (aria-describedby).
Slots
| Slot | Description |
|---|---|
| (default) | wds-field-legend, wds-field-description, fields, groups. |
wds-field-legend#
wds-field-set. The label variant is for groups of inline controls (checkbox, radio).
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
| variant | WdsFieldLegendVariant | 'legend' | legend is a section heading; label has the size of a field label. |
Slots
| Slot | Description |
|---|---|
| (default) |
CSS parts (::part)
| Part | Description |
|---|---|
| base |
wds-field-group#
orientation="responsive".
With the data-checkbox-group attribute the spacing is tighter (a list of checkboxes).
Slots
| Slot | Description |
|---|---|
| (default) | wds-field-set, wds-field-separator, nested groups. |
wds-field-separator#
Slots
| Slot | Description |
|---|---|
| (default) |
CSS parts (::part)
| Part | Description |
|---|---|
| base |
Accessibility#
Screen reader#
wds-field-label names the control of its wds-field: a native control through aria-labelledby, a wds-* control through aria-label forwarded into its shadow DOM. for points the label to a control with that ID instead.
wds-field-description and wds-field-error describe the control (aria-describedby or aria-description). wds-field-error has role="alert", so a new message is announced.
wds-field-set has role="group" named by wds-field-legend and described by the wds-field-description right after the legend. wds-field-separator has role="separator".
invalid on wds-field sets invalid (and aria-invalid) on the control. disabled only dims the label: disable the control itself.
Keyboard support#
wds-field-label moves focus to the control and toggles a checkbox or a switch.