Field
Form layout parts that group a label, control, description and error.
import { … } from '@inlaylab/ivorie/field'import { ChangeDetectionStrategy, Component } from '@angular/core';
import { IV_FIELD_IMPORTS } from '@inlaylab/ivorie/field';
import { IV_INPUT_IMPORTS } from '@inlaylab/ivorie/input';
import { IvButton } from '@inlaylab/ivorie/button';
@Component({
selector: 'demo-field',
imports: [IV_FIELD_IMPORTS, IV_INPUT_IMPORTS, IvButton],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<fieldset ivFieldSet class="w-full max-w-sm">
<legend ivFieldLegend>Create account</legend>
<iv-field-group>
<iv-field>
<label ivLabel for="signup-name">Full name</label>
<input ivInput id="signup-name" />
<p ivFieldDescription>As it appears on your profile.</p>
</iv-field>
<iv-field>
<label ivLabel for="signup-email">Email</label>
<input ivInput id="signup-email" type="email" />
<p ivFieldDescription>We'll send a confirmation link.</p>
</iv-field>
<iv-field [invalid]="true">
<label ivLabel for="signup-password">Password</label>
<input ivInput id="signup-password" type="password" aria-invalid="true" value="abc" />
<iv-field-error message="Must be at least 8 characters." />
</iv-field>
<iv-field orientation="horizontal">
<input type="checkbox" id="signup-updates" class="mt-1 size-4" checked />
<iv-field-content>
<iv-field-title>
<label for="signup-updates">Email me about product updates</label>
</iv-field-title>
<p ivFieldDescription>Occasional, no spam.</p>
</iv-field-content>
</iv-field>
</iv-field-group>
<button ivButton class="w-full">Create account</button>
</fieldset>
`,
})
export default class FieldDemo {}
Installation
npx ivorie add fieldimport { … } from '@inlaylab/ivorie/field'Layout only. It does not manage form state.
Usage
import { IV_FIELD_IMPORTS } from '@inlaylab/ivorie/field';<iv-field>
<label ivLabel for="email">Email</label>
<input ivInput id="email" />
<p ivFieldDescription>We never share this address.</p>
</iv-field>Examples
Horizontal
Once a month, at most.
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { IV_FIELD_IMPORTS } from '@inlaylab/ivorie/field';
import { IV_INPUT_IMPORTS } from '@inlaylab/ivorie/input';
@Component({
selector: 'demo-field-horizontal',
imports: [IV_FIELD_IMPORTS, IV_INPUT_IMPORTS],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<iv-field orientation="horizontal" class="w-full max-w-sm">
<input type="checkbox" id="field-news" class="mt-1 size-4" />
<iv-field-content>
<iv-field-title>
<label for="field-news">Marketing emails</label>
</iv-field-title>
<p ivFieldDescription>Once a month, at most.</p>
</iv-field-content>
</iv-field>
`,
})
export default class FieldHorizontalDemo {}
Error
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { IV_FIELD_IMPORTS } from '@inlaylab/ivorie/field';
import { IV_INPUT_IMPORTS } from '@inlaylab/ivorie/input';
@Component({
selector: 'demo-field-error',
imports: [IV_FIELD_IMPORTS, IV_INPUT_IMPORTS],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<iv-field [invalid]="true" class="w-full max-w-sm">
<label ivLabel for="field-user">Username</label>
<input ivInput id="field-user" aria-invalid="true" value="ab" />
<iv-field-error message="Use at least 3 characters." />
</iv-field>
`,
})
export default class FieldErrorDemo {}
Fieldset
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { IV_FIELD_IMPORTS } from '@inlaylab/ivorie/field';
import { IV_INPUT_IMPORTS } from '@inlaylab/ivorie/input';
@Component({
selector: 'demo-field-fieldset',
imports: [IV_FIELD_IMPORTS, IV_INPUT_IMPORTS],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<fieldset ivFieldSet class="w-full max-w-sm">
<legend ivFieldLegend>Address</legend>
<iv-field-group>
<iv-field>
<label ivLabel for="field-street">Street</label>
<input ivInput id="field-street" />
</iv-field>
<iv-field>
<label ivLabel for="field-city">City</label>
<input ivInput id="field-city" />
</iv-field>
</iv-field-group>
</fieldset>
`,
})
export default class FieldFieldsetDemo {}
Separator
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { IV_FIELD_IMPORTS } from '@inlaylab/ivorie/field';
import { IV_INPUT_IMPORTS } from '@inlaylab/ivorie/input';
import { IvButton } from '@inlaylab/ivorie/button';
@Component({
selector: 'demo-field-separator',
imports: [IV_FIELD_IMPORTS, IV_INPUT_IMPORTS, IvButton],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<iv-field-group class="w-full max-w-sm">
<iv-field>
<label ivLabel for="field-sep-email">Email</label>
<input ivInput id="field-sep-email" type="email" />
</iv-field>
<iv-field-separator>Or continue with</iv-field-separator>
<button ivButton variant="outline">GitHub</button>
</iv-field-group>
`,
})
export default class FieldSeparatorDemo {}
API
Every part also accepts class.
iv-field
| Name | Type | Default | Description |
|---|---|---|---|
orientation |
'vertical' | 'horizontal' | 'responsive' |
'vertical' |
Stack direction. responsive is a column that becomes a row at the @md/field-group container-query breakpoint. |
invalid |
boolean |
false |
Sets data-invalid="true" for error styling. |
iv-field-legend, legend[ivFieldLegend]
| Name | Type | Default | Description |
|---|---|---|---|
variant |
'legend' | 'label' |
'legend' |
Heading or label size. |
iv-field-error
| Name | Type | Default | Description |
|---|---|---|---|
message |
string |
'' |
Error text. Projected content is also shown. |
Other parts
No inputs besides class: iv-field-set (or fieldset[ivFieldSet]), iv-field-group, iv-field-content, iv-field-title, iv-field-description (or p[ivFieldDescription]), iv-field-separator.
Accessibility
iv-field has role="group". iv-field-error has role="alert", so errors are announced when they appear. It renders nothing, and takes no space, when it has no message and no content. Use fieldset and legend to group related controls.
Notes
iv-field-group is the container-query root for responsive fields.