Icon button

The icon-only control. React <IconButton> (packages/ui/src/components/IconButton.tsx) emits .acrm-icobtn (apps/web/src/ds.css): transparent at rest, --hover-surface on hover, square at --control-h.

variant="framed" (.acrm-icobtn.framed) is the bordered one: 32px wide on the same height, 1px var(--border-strong), --surface-100, --shadow-xs, an --ico-sm glyph in --ink-700, hover --surface-75. It replaced the hand-drawn .pr-iconbtn / .rr-iconbtn on 11 September 2026 (BTN-A3), which were deleted in the same change.

variant="secondary" (.acrm-icobtn.secondary, beta — candidate approved by the AD on 18 October 2026, ACRM-542) is for rows of tools that need structure without the weight of framed: a 1px var(--line-200) hairline, no shadow, transparent at rest, --line-100 on hover, --selected + --accent-ink when pressed or open. It has ONE universal size (owner 19 October 2026, "everything should be one size"): --control-h square (30×30) with an --ico-sm glyph; size and iconSize do not apply to it. Used in the compose footer (attach, template, signature, AI, reminder), the rich-text format toolbar, and the message tool rows (message page tools, message card actions, message dock footer, draft panel header). Window headers (assistant panel, assistant surface, record drawer) stay quiet (AD 27 September 2026). Text next to a secondary row (e.g. the "2/14" counter) takes .acrm-icobtn-text (padding-inline: var(--sp-4)), always wider than the gap between buttons; each carries an application Tooltip with the shortcut instead of a native title.

href (AD 27 Sep 2026, acrm !3997) renders the same look as an <a> instead of a <button> — real link semantics for a download or an external address, same pattern as <Button href>.

When to use

  • Actions in a Section top bar, record header or drawer chrome: close, back, previous/next record, more.
  • Framed: the action row of a record rail (person, company, deal), row kebabs on cards, actions in a section block header.
  • Row-level actions where a text label would add noise: edit, remove, open.
  • Any place the icon alone is unambiguous and the action is secondary.
  • href: a download or an external/file address (e.g. an attachment) that should behave like a real link — middle-click, "save link as", a screen reader announcing a link.

When not to use

  • The main action of a screen — use Button primary with a label.
  • An action whose icon is not self-explanatory — use Button with a label and an icon.
  • Hand-drawn framed buttons (.pr-iconbtn, .rr-iconbtn) — removed 11 September 2026; use variant="framed", which the validator rule iconbtn-framed-raw now enforces.
  • In-app navigation — href is for external URLs, downloads and deep links only; the router owns navigation inside the app.
  • A kebab (dots-vertical) that opens a menu with a single item — replace it with the direct action instead (AD 26 Sep 2026, "kebab with one action", acrm !3997).

Anatomy

  • Square container --control-h × --control-h (30px), size="sm" = 24px × 24px. Framed is 32 × 30 and has one size only — size does not apply to it.
  • Framed keeps its rest look when pressed, when its menu is open and when disabled: the hand-drawn rules it ports had no such states, and inventing them would have changed 31 places. Focus comes from the foundation ring.
  • No border, no background at rest, radius --radius-sm.
  • Icon <Icon name> from the Tabler set, 1em by default, iconSize overrides in px.
  • Colour --fg-secondary at rest, --fg on hover; hover background --hover-surface.
  • type="button" by default so it never submits a surrounding form.
  • Required accessible name via aria-label (and title for the tooltip).
  • pressed?: boolean (acrm !3936) draws aria-pressed and the --active-surface look, kept on hover — for toggles (format, panel, template/AI in mail compose), not for a menu trigger's open state.
  • href (with optional target, rel, download) swaps the <button> for an <a class="acrm-icobtn …"> with the identical look; target="_blank" merges in rel="noopener noreferrer"; disabled renders <a aria-disabled="true"> without href, since a link cannot be disabled by attribute; ref forwards onto the <a> too, so it can still anchor a Popover.

Best practices

Do

  • Give every icon button an aria-label that names the action: "Close", "Next record".
  • Group related icon buttons with a SectionTopBarDivider between groups (navigation | more).
  • Use size="sm" inside dense rows and chips where 30px would break the row height.
  • Keep the same icon for the same action across screens (pencil = edit, x = close, dots-vertical = more).

Don't

  • Icon button without aria-label or title — the button has no text, so it has no name.
  • More than 3–4 icon buttons in a row without grouping — the bar becomes a strip of glyphs.
  • Icon button for a destructive action without confirmation — a trash icon deletes on one click.
  • Custom size or colour via inline style — use size and iconSize only.

Content

  • aria-label and title: sentence case, verb phrase, no period ("Previous record").
  • The tooltip (title) and aria-label should carry the same wording.
  • Button — labelled actions; the primary action of a section; href follows the same pattern.
  • Section top bar — the row that hosts icon buttons for navigation and more.
  • Kbd hint — not rendered inside Icon button; document shortcuts in the tooltip text instead.
  • Attachment — uses IconButton size="sm" href for its download action.

Source: docs/button/icon-button.usage.md · GitLab