Pill
canonOutlined label whose colour is carried by a saturated dot: seven hues for distinctness, plus one reserved tone, critical.
No React component of its own — TagPills, StatusChip and the listing columns all emit .acrm-pill. AD 6 Sep 2026 (after Linear): the tinted ground was replaced by an outlined chip with a saturated --ic-* dot and full-contrast text. The old ground measured only 1.03-1.19:1 against the canvas, so in dark theme the tone did almost nothing and the colour was really carried by the text; it also left grey with no shape at all and needed a background AND an ink per tone. A label with an icon has no dot (:has(.ti)). The .dot class is now a no-op. There is deliberately no exception for tables: dropping the frame there needed two conditions, and the second (label alone in the cell) would make the same column render differently for a record with one tag than for one with two. AD 5 Sep 2026: the two-family tone model was dropped; red is the one reserved word. Leading icon from the whole Tabler set (owner 27 Sep 2026, acrm !4004): `TablerIcon` lazy-fetches `/tabler/<name>.svg`, caches it, restrokes to 1.75 and wraps it in `.ti` — same no-dot rule, DS `<Icon>` allowlist unchanged, app-level escape hatch for AI-picked meeting-topic glyphs only. "Section tags" composition (acrm !4013): removable `.acrm-pill` row with no frame + a quiet section action opening a `Popover` with one `Input` to add — used by meeting Minutes "Probraná témata"; a framed tag input stays a form-only control. Pill wave acrm !4079 (owner-approved 28 Sep 2026, variant A): ~20 hand-made "pills" with tinted backgrounds (20–24 px, 10–12 px type) replaced by ONE shape — `.acrm-pill` sm, 20 px, 12/500, outline + tone dot, no tinted fill. Rule: a CATEGORY (role, owner/share, segment) = `.acrm-pill` with a tone dot; a STATE (active/inactive, free/busy, call state, meeting state, prediction, import match, user status) = status chip (`.acrm-pill sm` + `data-status`); no new pill variant. Busy room = amber, never red (red is reserved for errors). Room equipment is NOT a pill: quiet text with a 14 px DS glyph per item (map apps/web/src/lib/room-equipment.ts, unknown item = text only), no dots between items (the icon separates), --fg-muted. Kept aside: `.grade` (credit grade) and `.pdr-pill`. The same MR fixed meeting status always showing grey "done". Leading icon carries the tone (owner 8 Oct 2026, acrm !4915, AD FA258-re no. 5): a pill with a DIRECT leading icon child has no dot (`:has(> .ti)`, not `:has(.ti)` — the remove cross `.x .ti` is a grandchild and must not take the dot away) and the icon is coloured by the tone instead: `.acrm-pill > .ti { color: var(--pill-dot, var(--ic-grey)) }`. General rule for every pill with a leading icon, not a special case of one component; first consumer is Project label.
Used in 9 files · 9 areas · 11× — components/TagsEditor, components/TagsField, components/projects, pages/CompanyRecordPage, pages/DialerPage, pages/LidePage …
apps/web <TagPills> <StatusChip> <ContactStatusChip>.acrm-pill .acrm-pill.sm .acrm-pill::before .acrm-pill .x +2SourceStoryLegacy card pills-status.htmlLegacy card colors-pills.htmlKeyboard
- A plain pill is a
<span>: not focusable, and correctly so — it is a label, not a control. - A removable pill contains a real
button.x, so Tab reaches the remove control and Enter or Space fires it. The pill body stays inert. - There are no shortcuts; removing is a click or Enter on the
.x.
ARIA
- No role. A pill is text with a background, and that is what it should be announced as.
- The
.xneeds an explicitaria-labelnaming the thing (Odebrat VIP) — without it a screen reader hears an unnamed button, and one row of tags becomes several identical unnamed buttons. - The
.dotmarker is a::beforepseudo-element, so it contributes nothing to the accessible name; whatever it signals must also be in the words. - A leading
TablerIcon(acrm !4004) isaria-hidden="true"on the fetched SVG, same as the DS<Icon>— the glyph never enters the accessible name, so a topic pill's meaning must still be carried entirely by its label text while the SVG loads and after. - A leading icon that carries the tone (Project label) is
aria-hiddenand its colour is not announced either; the name says what the icon would. - Tone is never announced.
greenandcriticalreach a screen reader as the same plain text, so the state must be readable from the label itself — a greenVyhránoworks, a greenAnodoes not.
Focus
- The
.xtakes the foundation ring fromds.css(outline: 2px solid var(--accent); outline-offset: 1px) — verified in the browser on the removable example. - Focus ring
--accent: 4.44:1 light, 3.83:1 dark.
Contrast
Computed from packages/ui/src/tokens/tokens.css:
- Label text is
--fgon the canvas — 17.59:1 light, 14.53:1 dark. This is the main gain of the dot model: the old tinted pill put tone-coloured ink on a tone-coloured ground (4.6–7.0:1 depending on hue), whereas the text now sits at full document contrast in every tone. - The frame is
--border— 1.15:1 light, 1.22:1 dark. It is a boundary, not information; nothing depends on seeing it. - The dot uses the saturated
--ic-*set, so it is far more visible than the old ground was (which measured 1.03–1.19:1 against the canvas). It is still colour alone, which is exactly why the label text must name the thing.
Known limits
- The dot is colour alone and is never announced; the label text has to carry the meaning on its own.
- The tone carries no accessible meaning at all; the label must be self-sufficient.
11.5pxat weight 600 is small text — it passes AA on colour, but it is at the lower edge of comfortable reading, and the pill never scales with the surrounding type.
Source: docs/status/pill.a11y.md · GitLab