Components › Forms & input › Button › Icon button

Icon button

canon

Square button carrying only an icon; label lives in the tooltip. Quiet at rest, variant="framed" for the bordered one, variant="secondary" (beta) for a hairline-bordered tool row.

variant="secondary" has ONE size (owner 19 Oct 2026, acrm ACRM-542): --control-h square + --ico-sm glyph; size/iconSize are ignored; text beside a row uses .acrm-icobtn-text (padding-inline --sp-4). Window headers stay quiet (AD 27 Sep 2026). variant="framed" (11 September 2026, BTN-A3) is a 1:1 port of the hand-drawn .pr-iconbtn / .rr-iconbtn, which were deleted with it: 32px wide on the base height, 1px --border-strong, --surface-100, --shadow-xs, --ico-sm glyph in --ink-700, hover --surface-75. Pressed, open and disabled deliberately keep the rest look, because the hand-drawn rules had no such states; the validator rule iconbtn-framed-raw blocks the old classes. Variant by CONTEXT (AD 26 Sep 2026, acrm !3910 — in the docstring): dense list row (ItemList, timeline, relations, thread, project/meeting/milestone row) = quiet size="sm"; detail header / toolbar = quiet default; variant="framed" ONLY next to framed siblings (rail action row, section header next to framed buttons, dialog footer next to Button default). Kebab = dots-vertical; a kebab with one item is replaced by the direct action (trash + confirm, link-off without danger) — rule "kebab with one action" landed as the wave C cleanup (AD 26 Sep 2026, acrm !3997: ProjectMilestonesSection trash, IdentityImageDialog trash, ProjectMeetingsTab link-off, none get a Popover around a single choice). Wave 16 (acrm !3997) finished the context rule above by migrating the last hand-drawn icon buttons onto it: 32px `.icobtn` (Uživatelé domains, segment detail/share ×2) → framed size default (30); 22px `.eml-icobtn` (draft/pinned mail rows) → `size="sm"`; 26px `.aichat-hbtn` (assistant panel header) → default, with `pressed` for the settings toggle; TabStrip scroll arrows → `size="sm"`. Exception kept: the App shell chrome's 32px `.as-icobtn` / `.as-util .icobtn` sits among 32–34px neighbours and stays hand-drawn. State pressed (acrm !3936): pressed?: boolean → aria-pressed + --active-surface, also on hover — for toggles (format, panel, template/AI in mail compose). The open look applies only to menu triggers ([aria-expanded=true][aria-haspopup]); a disclosure (collapse/expand) carries its state in the glyph. Link (`href`, AD 27 Sep 2026 — attachment download): renders `<a class="acrm-icobtn …">` with the same look, same pattern as `<Button href>` — real link semantics (middle-click, "save link as", a screen reader announces a link), `target="_blank"` gets `rel="noopener noreferrer"` merged in, `disabled` with `href` renders `<a aria-disabled>` without `href` (a link cannot be disabled by attribute). External/file URLs only — in-app navigation stays on the router. `ref` forwards onto the `<a>` too (review fix in !3997), so it can anchor a Popover. Touch target (owner 8 Oct 2026, acrm !4882, RULES §19): below 720 px an invisible ::after makes the tap area ≥ 44 × 44 without changing the look; checked on dev 8 Oct (⋮ top bar 42×44 at the screen edge, 0 stolen taps). Clipped targets fixed in acrm !4892 (checked on dev 8 Oct: Ajda 78×44, Pošta stars 44×44 except a row cut by the scroll edge). Remaining by design: column-header ⋮ 24×40 (header row is 40 px). Registry repaired 17 Oct 2026 (review F6): added exported Icon to react[].

Used in 474 files · 154 areas · 1727× — components/AiBadge, components/AppShell, components/Board, components/CallbackTakeoverModal, components/CheckMenu, components/DuplicatesPanel …

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