Entity reference

The single inline cross-reference to a record: the name under a dashed underline and — where the call site provides onOpen — a click that opens it. One component, three variants: default, mini and removable. React packages/ui/src/components/EntityRef.tsx; primitives .eref-chip, .eref-chip.eref-mini, .eref-token and .eref-x in apps/web/src/ds.css.

Until 11 Sep 2026 mini and removable had pages of their own. They are the same component with a different prop, so they now live here as variants (AD, ER-1).

Variants

  • Default (variant="default") — the identity mark, then the name. For a value slot: a rail row, a card, a form field, a picker result. The mark follows the type through EntityMark: person and user a circle avatar, company a square one, every other type — including project since 13 Sep 2026 (acrm !3466) — a solid tile of its world with a reserved glyph. The same marks are used by entity-preview, so a chip and its own preview card never draw different glyphs.
  • Mini (variant="mini") — the name alone at text size, sitting on the baseline. For prose, a byline, a dense row, a cell, a feed. The component draws no mark in this variant.
  • Removable (removable + onRemove) — either of the two wrapped in a token with a trailing ×. For a multi-value field of references and for picks that can be taken back.

Default or mini is decided by the line the reference sits in, not by taste. Measured in a --cell line: the default chip raises the line to 28px and offsets it by 4px; mini keeps it at 23px with no offset.

When to use

  • A value slot that points at another record — default.
  • A reference inside a sentence, a byline or a task title, or a dense row where the default chip's mark and padding would push the row height — mini.
  • A list of several references where full chips would read as a wall of buttons — mini.
  • A multi-value field of references (participants, related records, a picked set) — removable.

When not to use

  • A list of records the user scans — that is item-list.
  • A tag or a category — that is pill, whose .x is a different control on a different primitive.
  • Default inside running prose — it has vertical-align: -3px and pushes the line around; use mini.
  • Mini in a slot where the record's type must be obvious — mini has no mark.
  • Removable in a read-only list, or in a single-value field — there the way to change it is to pick again.
  • A record that no longer exists and cannot be opened — render it without onOpen, as plain text, rather than as a dead button.

Anatomy

Default

  • button.eref-chip (a <button> only when onOpen is given; plain text otherwise) › the mark › span.eref-chip-nm › optional span.eref-date.
  • The dashed underline lives on .eref-chip-nm (1px dashed --border-strong). Hover darkens it to --ink-700 and tints the chip ground; it never turns solid.
  • The name is a single line, clipped with an ellipsis; the full name stays in title.
  • date renders a muted suffix for meetings — part of the reference, not a second chip.
  • Measured: 23px tall.

Mini

  • button.eref-chip.eref-mini: no mark, gap: 0, padding: 0 1px, vertical-align: baseline, border-radius: var(--radius-xs).
  • .eref-chip-nm at 12px in --fg-secondary, keeping the dashed underline; hover lifts the text to --ink-900 and the underline to --ink-500.
  • Hover does not tint the ground (background: none).
  • .eref-date drops to 11px with a --sp-1 left margin.
  • Measured: 19px tall.

Removable

  • span.eref-token › the .eref-chip (default or mini) › button.eref-x.
  • The × is a 13px real button, revealed on :hover and :focus-within of the token, so a keyboard user reaching it makes it visible.
  • removeLabel sets its aria-label, defaulting to Odebrat {name}.
  • The chip inside keeps its own behaviour: onOpen still opens the record, onRemove only removes it from this list.
  • Measured: clicking a × removes exactly that token and the row reflows; removing the last one leaves the field's own empty text.

Best practices

Do

  • Give onOpen whenever the target can be opened, and leave it off when it cannot.
  • Let the type pick the mark; never pass a tile by hand.
  • Keep the default chip in a slot with a known width — it clips rather than wraps.
  • Use mini for every reference that appears in running text, and let a row of several mini references wrap rather than scroll.
  • Keep the two actions of a removable token distinct: the chip opens, the × removes.
  • Let removeLabel name the record (Odebrat Josef Novák) — a row of unnamed × buttons is unusable from a screen reader.
  • Show the field's empty text when the last token goes, rather than an empty box.

Don't

  • Mix mini and default chips in one line.
  • Add a second chip for the date; that is what date is for.
  • Restyle the underline or the hover tint, or give mini a hover ground "to make it more clickable".
  • Invent a tone for a new type — extend the marks in EntityMark, so the chip and the preview stay in step.
  • Make the whole removable token remove on click.
  • Offer a × for a reference the user is not allowed to remove — leave it off instead of disabling it.
  • Nest a pill × and an .eref-x in the same row; they are different sizes and different primitives.

Content

  • name is the record's display name, unabbreviated.
  • date for meetings, in the year convention (26. 8.).
  • The remove label names the record, in the imperative.
  • entity-preview — the card the reference can open (not for project, which has no preview; the reference renders without one).
  • dialog / formless-dialog — the header of a create/edit dialog shows the primary record as this reference.
  • avatar — the mark for people and companies.
  • mention-input — where mini references are produced by typing @.
  • item-list, task-list — the usual hosts of mini references.
  • record-select, multi-value — the fields that produce removable tokens.
  • Stories: components--entity-reference--default, --variants, --states, --composition; components--entity-reference-mini--*; components--entity-reference-removable--*.

Mention inside running text (AD 1. 10. 2026, FA242)

  • mini inside prose (MentionText in notes, messages, comments) inherits the surrounding type: font-size and line-height inherit, colour = the text colour. The affordance is the dashed underline only; the mark is 1em on the text baseline. No „@“, no accent colour (accent is for actions and links, not people).
  • mini in dense lists, cells and feeds stays 12 px --fg-secondary.
  • One canonical rule (prose context set by MentionText), never a one-off class in a page.

Source: docs/entity-reference/entity-ref.usage.md · GitLab