Field

Form layout parts that group a label, control, description and error.

import { … } from '@inlaylab/ivorie/field'
Create account

As it appears on your profile.

We'll send a confirmation link.

Must be at least 8 characters.

Occasional, no spam.

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

CLInpm
npx ivorie add 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

Use at least 3 characters.
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

Address
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

Or continue with
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.