Malevich

Elements / Form/

Field-group

Canonical form composite. Label + control + helper/error.

Field-group

Canonical composite for a form field: Label + control + HelperText + Error. State is driven by a single data-state attribute on the wrapper — no JavaScript required. Per ADR-0013 + the answered design question recorded with this component.

When to use

Any time you need a labeled form control with optional helper or error text. The component owns the spacing, error visibility, and the relationship between elements.

Anatomy

<!-- Normal state -->
<div class="field-group">
  <label class="label" for="email" data-required="true">Email</label>
  <input class="input" type="email" id="email"
         aria-describedby="email-hint email-error" />
  <p class="helper-text" id="email-hint">We'll never share your email.</p>
  <p class="error" id="email-error">Enter a valid email address.</p>
</div>

<!-- Error state -->
<div class="field-group" data-state="error">
  <label class="label" for="email" data-required="true">Email</label>
  <input class="input" type="email" id="email"
         aria-invalid="true"
         aria-describedby="email-hint email-error" />
  <p class="helper-text" id="email-hint">We'll never share your email.</p>
  <p class="error" id="email-error">Enter a valid email address.</p>
</div>

Visibility rule: the .helper-text and .error may both live in the DOM. CSS hides whichever is not active based on data-state:

This lets the author render both elements with stable IDs (for aria-describedby) and toggle state via the wrapper attribute. No JS-driven DOM mutation.

Variants

Variant Class Use for
Default .field-group Stacked column
Horizontal .field-group.-horizontal Label left, control right

Tokens used

From semantic tier

Component-tier (defined inline)

Accessibility

Edge cases

Do

Don't