Field

canon

Wrapper that stacks label, control, hint and error message.

acrm MR !4085 (28 Sep 2026, "fix(ui): chyba pod DS Field se čtečce provází sama") added a new export, `useFieldDescribedBy`: `<Field>` now gives the rendered hint/error span an automatic `id` (`useId()`, error takes precedence over hint) and provides that id through a context. Any control rendered as a child of `<Field>`/`<DrawerField>` can call `useFieldDescribedBy()` and fall back to it for `aria-describedby` unless the caller passed an explicit `describedBy` — `masked-input` and `phone-input` both do this now. This does not add `htmlFor`/label association (the pre-existing known limit in `field.a11y.md` — no automatic label–control link), and it only helps a control that explicitly opts into the hook; a plain `<input>` still needs it wired by hand.

Used in 29 files · 15 areas · 214× — components/accounting, components/callwrapup, components/clen-tymu, components/datamart, components/mail, components/person …

The wrapper around any form control: label, control, and one line under it (hint or error). React <Field> (packages/ui/src/components/Field.tsx) renders .acrm-field > label.acrm-field-label + children + span.acrm-field-hint / span.acrm-field-error (apps/web/src/ds.css). Error wins over hint — only one line is ever shown.

When to use

  • Every labelled control in a form, drawer or dialog: Input, Textarea, Select, Date field, Checkbox group, Tag input, Slider.
  • A control that needs a format hint ("8 digits") or a validation message.
  • A label with an inline extra on the right (labelExtra — an icon or help affordance, pattern from DrawerField).

When not to use

  • A control in a table cell, rail row or toolbar where the column header or row label already names it — the label would repeat.
  • Search inputs in a top bar — they carry an aria-label and a placeholder, not a visible label.
  • Inline <label style={…}> or a page-level .xyz-label — the label typography is one token (--label) and lives here.

Anatomy

  • Container .acrm-field: display: grid; gap: var(--sp-1); takes the column width — it has no width of its own.
  • Label .acrm-field-label: font: var(--label) (500 12px/22px --font-display, Manrope — AD 25 Aug 2026), color: var(--fg-secondary); rendered as flex with gap: var(--sp-2) so labelExtra sits inline after the text.
  • Hint .acrm-field-hint: font: var(--hint) (400 12px/1.45), color: var(--fg-muted) — the only class for help text under a field (.acrm-field-help was retired 26 Aug 2026).
  • Note .acrm-note: the same typography (font: var(--hint), color: var(--fg-muted)) with no Field around it — see below.
  • Error .acrm-field-error: font: var(--hint), color: var(--critical-ink).
  • Props: label, labelExtra, hint, error, className, style, children; no size or layout props.

Best practices

Do
  • One Field per control; stack fields with the form's own gap, not with margins on the label.
  • Use the hint for format and scope ("8 digits, verified in the register"), the error for what to fix.
  • Pair the message with the frame: error on Field plus .is-error on the Input so both agree.
  • Give a group explanation above the first field of the group and name the fields it covers ("Position, initials and phones fill the e-mail signature").
Don’t
  • Hint and error at once — Field shows only the error; do not stack a second line under it.
  • Explanatory paragraphs under a field — nobody reads them; fix the label or the control instead.
  • A label in capitals or ending with a colon.
  • Hiding the label to save space in a form — use aria-label only for search-type inputs outside forms.

Content

  • Label: short noun in sentence case ("Phone", "Industry"); no colon, no period.
  • Hint: one clause, format or limit, no period unless a full sentence ("8 digits").
  • Error: one sentence, what is wrong and how to fix it, with a period ("Enter a valid e-mail address.").
  • A group note goes above the group, lists the fields it applies to, and is never placed under the last field.
  • Optional fields are not marked "(optional)"; required ones are not marked with * — the form's validation tells.

.acrm-note — the free-standing sibling

.acrm-note (apps/web/src/ds.css) is quiet descriptive text that sits next to a value, not under a control: the file size on an attachment chip, "uploading…" on a chip on its way to a draft, the subtitle of a printed document, the body preview of a saved prompt, the one line an empty thread explains itself with.

Same role as the hint — explanation, not a value — so it carries the same token --hint (400 12px/1.45, AD 26 Aug 2026: weight 400, because 500 would be louder than the value it describes). The only difference is who sets it: .acrm-field-hint is written by <Field> under a control, .acrm-note is written by the caller anywhere.

Introduced 9 Sep 2026 (AI-A1). Before that, six places wrote className="sub" and picked up the global .sub of the navigation row (apps/web/src/ds/segment-screen.css) — 28px height, padding, border-radius, cursor: pointer and a hover background on a piece of text. .sub stays with navigation; the descriptor got its own name. Deleting .sub was not an option: it is a port of the canon (_ds/segment-screen.css) and ds:sync-check then reports it MISSING against those six files.

Scoped overrides are allowed where a surface has its own scale — .aichat-empty .acrm-note (--subtitle, an empty state speaks up), .sml-title .acrm-note (the printed contract has its own type scale). A scoped override that only repeats --hint is noise; delete it.

Do not use .acrm-note for a hint under a control — that is <Field hint>, and Field owns the error/hint precedence.

  • input, textarea, select, date-field, checkbox — controls that sit inside.
  • input-with-icon-kbd — the only text field that usually goes without a Field (search).
  • .acrm-note — the same typography outside a Field; .sub (navigation row) is not a text style and never was.
  • Stories: components--field--default, components--field--variants, components--field--states, components--field--composition; legacy components--inputs--fields.

Source: docs/text-field/field.usage.md · GitLab