MobTranslate styleguide

Icon Button

A named 44px action target for a familiar symbol, including a working selected-state example.

When to use

Use an icon action for a familiar, repeated operation where compact presentation helps. Use visible text when the meaning is unfamiliar, consequential or important to a first-time reader. A tooltip may support discovery but is not the only accessible name.

Anatomy and specification

PartContract
Target44×44 CSS px from Button size="icon"
GlyphTypically 20–24px inside the target; decorative SVG is aria-hidden
NameRequired aria-label string; supply meaningful non-empty action wording
Default styleghost; --radius-md; inherits semantic foreground
Selected stateCaller-owned aria-pressed plus an observable visual/text result
Native typebutton by default; submit/reset can be explicit

Reference sizes, density and hover help

The reference offers five icon-button sizes, three widths and separate default/toggle color mappings; its accessibility page specifies 48dp targets and its web-density paragraph names 48 CSS pixels. This library’s nominal 44px square is an explicit local target policy, not parity with those reference measurements. Local IconButton fixes size=icon and accepts the existing Button styling/native props. Do not describe it as the complete expressive sizing API or apply density that makes the target smaller. A product requiring a larger target can increase its minimum dimensions and verify the actual box.

The live example keeps one named button, exposes selected state with aria-pressed, changes the bookmark silhouette and supplies a local status result. Its real tooltip appears on hover/focus and describes the operation. Tooltip text supplements the action name; it does not replace the required label or establish that an unfamiliar icon will be understood.

Working selected state

Example is not saved. No dictionary record is changed.

tsx
'use client';
import { useState, useRef } from 'react';
import { IconButton, TooltipProvider, Tooltip, TooltipTrigger, TooltipPortal, TooltipPositioner, TooltipPopup } from '@mobtranslate/ui';

export function IconActionsExample() {
  const portalContainer=useRef<HTMLDivElement>(null);
  const [saved, setSaved] = useState(false);
  return <div ref={portalContainer} data-icon-action className="space-y-3">
    <TooltipProvider><Tooltip><TooltipTrigger render={<IconButton aria-label="Save example entry" aria-pressed={saved} onClick={() => setSaved(!saved)}>
      <svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" focusable="false" fill={saved ? 'currentColor' : 'none'} stroke="currentColor" strokeWidth="2"><path d="M6 4h12v16l-6-4-6 4z" /></svg>
    </IconButton>} /><TooltipPortal container={portalContainer}><TooltipPositioner sideOffset={8}><TooltipPopup>Toggle saved state for this example.</TooltipPopup></TooltipPositioner></TooltipPortal></Tooltip></TooltipProvider>
    <p role="status">{saved ? 'Example saved locally.' : 'Example is not saved.'} No dictionary record is changed.</p>
  </div>;
}

State contract

  • IconButton does not own selection. Keep aria-pressed and the visual glyph synchronized with application state.
  • For a toggle, keep a stable action name and expose pressed state. For a changing command such as Play/Pause, change the action name deliberately.
  • Disabled and loading follow Button behavior. A pending icon keeps a meaningful name and aria-busy; announce progress outside the glyph.
  • Do not treat a saved-item toggle as language review, consent or publication. Persist and verify those tasks separately.

Reference updates, placement and task availability

The captured expressive reference changes its FAB size/color families, discourages its older small/surface configurations and adds three extended sizes. Local sm/md/lg remain the rem-based API documented above; those names do not promise matching native geometry or color tokens. A labelled extended action stays one focusable target. Icon-only mode supplies actual hover/focus help; visible extended text already explains its task.

Use one promoted constructive action in the relevant workspace. Check logical placement, reading order and focus clearance in the consuming layout. The current specimen is in flow; it does not automatically float, collapse on scroll, move across tabs or run a container transform. Keep the label/icon relationship stable when deliberately changing presentation, and retain a meaningful destination when a launcher disappears.

The reference favors omitting an unavailable FAB. A permanently inapplicable promoted action should be omitted. A temporary pending operation in the local creation-menu example keeps the visible launcher unavailable to reject duplicates, while its retained task status and completion/cancel controls remain reachable. That explicit task policy is not a claimed native disabled-FAB design.

API reference

PropContract
aria-labelRequired string identifying the action
variantAll Button variants; ghost default
typebutton default; submit/reset explicit
childrenVisible symbol or glyph
aria-pressedNative React ARIA attribute; controlled by the caller
disabled / loading / loadingTextSame task-state behavior as Button
refRef<HTMLElement> to the native button
size / asChild / fullWidthExcluded; fixed target size. Use Button for those composition choices

Behavior and accessibility

  • Use a real button for an action and a link for navigation. Visible labels and accessible names must identify the same task.
  • Native buttons activate with Enter and Space; ordinary disabled buttons leave the tab order. Do not rely on a spinner as the only pending announcement.
  • Report pending, completion, cancellation and failure with task-specific status text. A local animation is not proof that a request completed.
  • Retain a meaningful focus destination when a pending button becomes disabled or disappears. The lab returns focus after completion or cancellation.
  • Focus, meaningful boundaries and icons need their own contrast checks. Text-pair measurements alone do not certify a complete component.
  • Respect reduced motion and forced colors. Do not obscure focus with a floating control or sticky header.

Failure cases

  • Avoid unlabeled icons, duplicated SVG announcements and a title attribute as the only name.
  • Do not reduce the target to the glyph’s 20px size.
  • Avoid generic names such as Bookmark icon when the action is Save entry.
  • Do not automatically mirror every symbol for RTL; review directional meaning separately.

Verification

  • Activate with Enter, Space and pointer input; aria-pressed, glyph fill and local status agree.
  • Inspect the accessibility tree for one named button and its selected state.
  • Measure the 44px target and verify adjacent controls do not overlap.
  • Inspect both themes and forced colors; do not certify an unfamiliar symbol solely from a screenshot.