Home / Snippets / UI Components /
Icon-only button
Compact icon buttons with tooltip, focus ring, and disabled state — no text label needed.
Quick implementation
/* HTML: <button class="icon-btn" aria-label="Edit"><svg>...</svg></button> */
.icon-btn {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
width: 2.75rem;
height: 2.75rem;
border: 1px solid oklch(0.3 0.02 260);
border-radius: 0.5rem;
background: oklch(0.19 0.02 260);
color: oklch(0.93 0.01 260);
cursor: pointer;
transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}
.icon-btn svg {
width: 1.25rem;
height: 1.25rem;
fill: none;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
}
.icon-btn:hover {
background: oklch(0.72 0.19 265);
border-color: oklch(0.72 0.19 265);
color: oklch(1 0 0);
}
.icon-btn:focus-visible {
outline: 2px solid oklch(0.72 0.19 265);
outline-offset: 2px;
}
.icon-btn[disabled] {
opacity: 0.4;
cursor: not-allowed;
}
.icon-btn[disabled]:hover {
background: oklch(0.19 0.02 260);
border-color: oklch(0.3 0.02 260);
color: oklch(0.93 0.01 260);
}
Prompt this to your LLM
Includes role, constraints, two framework variants, and edge cases to handle.
You are a senior frontend engineer building a design system component library.
Goal: An icon-only button that is fully accessible, with hover/focus states and an optional tooltip — no JavaScript for styling.
Technical constraints:
- Use a native <button> element with aria-label for the accessible name.
- Size the button at 2.75rem × 2.75rem with inline-flex centering.
- SVG icon inherits color via currentColor on stroke.
- Hover state swaps background to the accent color with a 0.2s transition.
- Use :focus-visible (not :focus) for the keyboard focus ring with outline-offset.
- Use oklch() for all color values, not hex or rgba().
Framework variant (pick one):
A) Vanilla HTML + CSS only.
B) React component — accept icon (ReactNode), ariaLabel (string), disabled (boolean), onClick handler, and optional tooltip (string) props.
Edge cases to handle:
- Disabled state: opacity 0.4, cursor not-allowed, hover styles suppressed.
- Tooltip must not clip outside the viewport on edge-positioned buttons.
- Screen readers must announce the aria-label, not the tooltip text.
- Touch targets must meet WCAG 2.2 minimum of 24×24px (2.75rem exceeds this).
Return HTML + CSS.
Why this matters in 2026
Toolbars, action menus, and compact UIs rely heavily on icon-only buttons. Without a visible text label, accessibility depends entirely on aria-label and a discoverable tooltip. Modern CSS handles the hover-reveal tooltip, focus ring via :focus-visible, and disabled state without a single line of JavaScript — making the component lighter, faster, and easier to maintain across frameworks.
The logic
The button uses inline-flex with fixed dimensions to center the SVG icon. The SVG inherits its color through stroke: currentColor, so changing the button's color property on hover recolors the icon automatically. The tooltip is an absolutely-positioned child that transitions opacity from 0 to 1 on :hover and :focus-visible. The disabled attribute reduces opacity and overrides hover styles to prevent visual feedback on non-interactive elements.
Accessibility & performance
Every icon-only button must have an aria-label — without it, screen readers announce nothing or read the SVG markup. The tooltip is purely decorative (not role="tooltip") since the aria-label already provides the accessible name. Using :focus-visible instead of :focus ensures the outline only appears for keyboard navigation, not mouse clicks. The transition targets only background, border-color, and color — all compositor-friendly properties that avoid layout thrashing.