Import the public field primitive from the package entrypoint.

field.ts
import {
  KuiError,
  KuiField,
  KuiFieldAffix,
  KuiHint,
  KuiInput,
  KuiInputGroup,
  KuiLabel,
  provideKuiDefaults,
} from '@kikita-labs/ui';

Use kui-field whenever a visible label, hint, error, or required marker is needed.

Use your work email

<div class="basic-field-example">
  <kui-field label="Email" hint="Use your work email">
    <input kuiInput type="email" placeholder="mira@company.dev" />
  </kui-field>

  <kui-field label="Project" error="Project name is required" required>
    <input kuiInput value="" />
  </kui-field>
</div>

Bind Angular Signal Forms to the native control with [formField]. The field owns label, hint, error text, the required marker and the ARIA description; Signal Forms owns the form state.

profile.html
<kui-field label="Project" hint="Minimum 3 characters">
  <input kuiInput [formField]="profileForm.project" />
</kui-field>

<!-- hideErrors keeps the invalid state but hides the rendered messages -->
<kui-field label="Project" hint="Minimum 3 characters" hideErrors>
  <input kuiInput [formField]="profileForm.project" />
</kui-field>
  • With a required(...) validator on the bound field the required marker is inferred; an explicit required input on kui-field overrides it.
  • The first error message of the bound field is rendered automatically unless hideErrors is set. hideErrors hides every rendered message, including the explicit error input and projected kuiError content, but keeps the invalid state.

Use the kuiLabel, kuiHint and kuiError markers when the label, hint or error needs template logic. They keep the same field wiring.

Use your work email

Stored encrypted. Learn more.

<div class="field-projected-example">
  <kui-field label="Email">
    <input kuiInput type="email" placeholder="mira@company.dev" />
    <p kuiHint>Use your work email</p>
    <p kuiError>Email is required</p>
  </kui-field>

  <kui-field label="API key">
    <input kuiInput />
    <p kuiHint class="kui-field-message">
      <span>Stored encrypted. <a href="#encryption">Learn more</a>.</span>
    </p>
  </kui-field>
</div>

The same markers also exist for the label, which the field wires to the control for you:

email.html
<kui-field>
  <label kuiLabel>Email</label>
  <input kuiInput type="email" [formField]="profileForm.email" />
  <p kuiHint>Use your work email</p>

  @if (shouldShowEmailError()) {
    <p kuiError>Email is required</p>
  }
</kui-field>
  • kuiError is manual content: render it with @if under your own visibility rules, and never hide it with [hidden], so it only joins the field semantics when it is shown.
  • Add .kui-field-message to a projected hint or error for icon and text layouts; projected errors stay part of aria-describedby.

Drop kuiFieldAffix directly inside kui-field for prefix text, suffix text, leading icons or trailing actions. The field switches its control slot to the shared input-group chrome automatically.

https://.dev

The prefix and suffix are visual field chrome.

<div class="field-affix-example">
  <kui-field label="Project URL" hint="The prefix and suffix are visual field chrome.">
    <span kuiFieldAffix>https://</span>
    <input kuiInput aria-label="Project slug" />
    <span kuiFieldAffix>.dev</span>
  </kui-field>

  <kui-field label="Search">
    <kui-icon kuiFieldAffix name="search" />
    <input kuiInput aria-label="Search query" />
    <button kuiFieldAffix type="button" aria-label="Clear search">
      <kui-icon name="x" />
    </button>
  </kui-field>
</div>
  • One directive, three looks, detected from the host: any button gets compact action styling (do not add kuiButton or kuiIconButton to it), kui-icon a fixed square icon slot, anything else muted text (the emphasis input switches it to full color).
  • A span kuiLoader kuiFieldAffix is detected as an icon and keeps its own role="status" and label. kuiFieldAffixIcon and kuiFieldAction remain as explicit overrides for older markup.
  • Decorative icons use aria-hidden="true"; interactive field actions must be native buttons with accessible names.

For field chrome the affixes do not cover, build .kui-input-group by hand and import KuiInputGroup. It delegates clicks on non-interactive chrome to the first enabled native control.

<div class="field-input-group-example">
  <kui-field label="API key">
    <div class="kui-input-group">
      <input kuiInput />
      <span class="kui-affix-spinner" role="status" aria-label="Checking key"></span>
    </div>
  </kui-field>
</div>

Interactive descendants such as clear buttons, chevrons, visibility toggles, links and the control itself keep their own behavior.

Set defaults.field once for the application or for a subtree. Each option resolves as local input > defaults.field.<option> > built-in default.

import { provideKikitaUi } from '@kikita-labs/ui';

export const appConfig: ApplicationConfig = {
  providers: [
    provideKikitaUi({
      defaults: {
        field: {
          /* options below */
        },
      },
    }),
  ],
};
NameTypeDefaultDescription
clearableboolean — When true, field controls with clear affordances show a clear button by default.
size'xs' | 'sm' | 'md' | 'lg' — Default kui-field size when no local size input is provided.
hideErrorsboolean — Hides automatically rendered Angular Signal Forms error messages by default.

Set any of these on the component or an ancestor to restyle one part. Each token is optional: when it is not set, the part uses the role or scale token in the default column.

NameTypeDefaultDescription
--kui-field-label-gapCSS custom property--kui-space-1Label gap
--kui-field-label-row-gapCSS custom property--kui-space-1Label row gap
--kui-field-affix-font-sizeCSS custom property--kui-text-sm-sizeAffix font size
--kui-field-spinner-radiusCSS custom property--kui-radius-fullSpinner corner radius

Inputs, projected markers, provider defaults and tokens verified against @kikita-labs/ui v2.0.0 public typings.

NameTypeDefaultDescription
labelstring — Visible label shorthand. Prefer this for simple field labels.
hintstring — Optional help text rendered below the control and wired through aria-describedby.
errorstring — Explicit error text rendered below the control and wired through aria-describedby.
hideErrorsboolean — Hides automatic, explicit, and projected error messages while keeping invalid state.
requiredboolean — Explicit required marker override. Omit when Signal Forms can infer it. Also drives aria-required on the projected control.
size'xs' | 'sm' | 'md' | 'lg' — Field spacing and projected control size. Falls back to defaults.field.size, then the global defaults.size.
[kuiLabel] / [kuiHint] / [kuiError]marker directives-Projected label, hint and error with the same field wiring. kuiError is manual content: render it with @if.
[kuiFieldAffix]directive-Prefix, suffix, icon or action chrome inside the field: button hosts get action styling, kui-icon an icon slot, anything else muted text (emphasis input for full color). kuiFieldAffixIcon and kuiFieldAction are explicit overrides.
.kui-input-group / KuiInputGroupclass and directive-Hand-built input group chrome; the directive delegates clicks on non-interactive chrome to the first enabled control.
KuiFieldOptionsinterface-Shape of defaults.field: clearable, size and hideErrors. Controls inside the field take an explicit field size first, then their own defaults key, then defaults.field.size.

Keep field semantics native-first. Let kui-field wire ids and descriptions instead of duplicating ARIA by hand.

  • Prefer a visible label through label or projected kuiLabel.
  • Projected kuiHint and kuiError participate in aria-describedby.
  • The field does not replace the native control role.
  • The required marker is visual only (aria-hidden). The merged required state, from the explicit required input or a Signal Forms required(...) validator, reaches assistive technology as aria-required="true" on the control. kui-field never writes the native required attribute, so nothing is announced twice.
  • Controls with a role that supports it receive aria-required: Input, Textarea, Checkbox, Switch, Number Input, Color Input, Select, Combobox, Date Picker and Time Picker. A single Radio does not (the requirement belongs to the group you build), Slider does not, kui-otp-input marks only its first cell, and kui-segmented exposes no required state.

Consumer-visible changes of @kikita-labs/ui 2.0 for Field.

  • The classes are KuiField, KuiInput, KuiInputGroup, KuiFieldAffix, KuiFieldAffixIcon and KuiFieldAction (they had a Component or Directive suffix); ng update @kikita-labs/ui renames the imports.
  • KUI_FIELD_OPTIONS is removed and kuiProvideFieldOptions is deprecated until 3.0: set defaults.field through provideKuiDefaults or provideKikitaUi({ defaults }). A nested provider now merges with its parent per property.
  • kui-field exposes its required state as aria-required on the controls that support it, so snapshot tests of the rendered markup change.
  • Field styles moved into the kui.components layer without ::ng-deep: the field needs kikita-ui.css like every other component, and an unlayered application rule overrides it without raising specificity.