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 throughEntityMark: person and user a circle avatar, company a square one, every other type — includingprojectsince 13 Sep 2026 (acrm !3466) — a solid tile of its world with a reserved glyph. The same marks are used byentity-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.xis a different control on a different primitive. - Default inside running prose — it has
vertical-align: -3pxand 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 whenonOpenis given; plain text otherwise) › the mark ›span.eref-chip-nm› optionalspan.eref-date.- The dashed underline lives on
.eref-chip-nm(1px dashed --border-strong). Hover darkens it to--ink-700and tints the chip ground; it never turns solid. - The name is a single line, clipped with an ellipsis; the full name stays in
title. daterenders 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-nmat 12px in--fg-secondary, keeping the dashed underline; hover lifts the text to--ink-900and the underline to--ink-500.- Hover does not tint the ground (
background: none). .eref-datedrops to 11px with a--sp-1left 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
:hoverand:focus-withinof the token, so a keyboard user reaching it makes it visible. removeLabelsets itsaria-label, defaulting toOdebrat {name}.- The chip inside keeps its own behaviour:
onOpenstill opens the record,onRemoveonly 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
onOpenwhenever 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
removeLabelname 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
dateis 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-xin the same row; they are different sizes and different primitives.
Content
nameis the record's display name, unabbreviated.datefor meetings, in the year convention (26. 8.).- The remove label names the record, in the imperative.
Related
entity-preview— the card the reference can open (not forproject, 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)
miniinside prose (MentionText in notes, messages, comments) inherits the surrounding type: font-size and line-heightinherit, 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).miniin 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.