CANON · Icons
An icon that needs explanation is a failed icon. Most of them fail.
The "nobody knows what that means" test
Before any icon enters your UI, answer: would a first-time user of this product understand what clicking it does, without a tooltip?
Icons that pass (almost universal): ✕ (close), ⌕ (search), ← (back), ⬇ (download), ▶ (play), 🖨️ (print, desktop only), 🗑️ (delete).
Icons that fail more than they succeed: ≡ (hamburger on desktop), ⚙️ (settings is fine; customize/config/admin are not), ∅ (empty? no? disabled?), 🔁 (refresh? loop? sync?), custom brand icons for custom features.
If the icon fails the test, pair it with a label. Always.
Sizing
Pick a pixel size, not a visual size. Icon grids are designed at specific sizes; scaling them creates sub-pixel blur.
| Size | Context |
|---|---|
| 12–14px | Inline with caption text, inside chips |
| 16px | Inline with body text, input adornments, menu items |
| 20px | Buttons (default), nav items |
| 24px | Primary buttons, toolbars |
| 32–40px | Tab bars (mobile), large actions |
| 48–64px | Empty state illustrations, onboarding |
Mobile tap targets require 44×44px total hit area, even if the icon glyph is 20px. Pad around the icon.
Stroke weight
Icons in a set should share one stroke weight. Mixing looks amateur.
| Style | Stroke | Use |
|---|---|---|
| Outline / linear | 1.5–2px | Modern, airy |
| Duotone | 1.5–2px + filled accent | Playful |
| Solid / filled | No stroke | Dense UIs, small sizes (< 16px read better filled) |
| Glyph (mono) | Custom per icon | Brand iconography |
Don't mix outline and filled in the same toolbar. If you want filled-active and outline-inactive, pick a library that supports both.
Pairing icons with text
Research shows icon + label outperforms icon-only for comprehension in almost every tested case. Icon-alone is acceptable only when:
- The meaning is truly universal (✕, search, back arrow).
- Space is genuinely exhausted (mobile tab bar, dense toolbar).
- The action is secondary and a tooltip on hover/focus is available.
Even then, provide a tooltip + aria-label.
Accessibility
Icon is decorative (paired with text)
<button>
<svg aria-hidden="true" focusable="false">...</svg>
Save changes
</button>
aria-hidden="true"andfocusable="false"on SVG.- Screen reader reads the text label only.
Icon is the only signal (icon-only button)
<button aria-label="Delete item">
<svg aria-hidden="true" focusable="false">...</svg>
</button>
aria-labelon the button, not the SVG.- Still
aria-hidden="true"on the SVG.
Meaningful decorative icons (inline with content)
<p>
<svg aria-hidden="true" focusable="false">...</svg>
5 items saved
</p>
- If the text already conveys the meaning, icon is decorative.
Format choice — SVG wins
| Format | Use? |
|---|---|
| SVG (inline) | Default. Recolorable via CSS, sharp at any size, small file size |
| SVG sprite | Good for libraries with many icons in use |
| Icon font | Avoid. Screen reader confusion, no pixel-perfect control, blocks render |
| PNG / raster | Only for complex illustrations where SVG isn't feasible |
| Emoji | For user-facing casual UI only (chat messages, reactions). Not for interface chrome. |
Why not icon fonts
- Screen readers sometimes read the private-use-area codepoint.
- Font loading delays icon rendering.
- Can't style parts of the icon.
- Rasterized at non-integer sizes.
Use SVG.
Color
- Icons inherit
colorviacurrentColorin SVG. Setstroke="currentColor"orfill="currentColor"in the SVG source. - Icons follow text color in most contexts. Separate tokens only for special uses (success checkmark green, destructive red trash).
- Contrast: 3:1 minimum for interactive icons against the background. WCAG 1.4.11 Non-Text Contrast.
Emoji in UI
Emoji render differently across platforms. An emoji in one button on iOS looks different on Android looks different on Windows.
| Use emoji for | Don't use emoji for |
|---|---|
| User-generated content (chat, reactions) | Interface chrome (nav, buttons, labels) |
| Marketing / casual comms | Status indicators (red circle could be anything) |
| Empty state flavor text | Primary UI signals |
If you must use emoji in the UI, test across iOS, Android, Windows, macOS — they render very differently.
Icon + text alignment
When inline with text, icons should visually center, not baseline-align.
.icon-text {
display: inline-flex;
align-items: center;
gap: 8px;
}
Gap between icon and label: 6–8px at 14–16px text size.
Brand icons (logos)
- Preserve aspect ratio.
- Keep minimum size constraints (brand guidelines usually specify).
- Don't recolor locked brand marks.
- Pair with
aria-labelnaming the brand for screen readers.
Anti-patterns
| Anti-pattern | Why it fails |
|---|---|
| Mixing outline and filled icons in one set | Visual chaos |
| Different stroke weights across the same toolbar | Amateur appearance |
| Icon fonts in production | Screen reader + rendering issues |
Icon-only buttons without aria-label |
Inaccessible |
| Hamburger menu on desktop | 30–50% lower discoverability (Luke W. research) |
| Emoji for critical status | Renders inconsistently across platforms |
| Icon size < 16px for tap targets | Not even close to the 44px floor |
| Custom icons for universal actions (back, close) | Users have to relearn |
| Animated icons without reduced-motion fallback | Triggers motion sensitivity |
| Lottie animations for simple loaders | 500KB for 3 seconds of spinning |
Decision tree
Does the icon's meaning pass the "first-time user" test?
├─ No → Pair with a text label
└─ Yes → Icon-only OK (with aria-label + tooltip)
Inline with text or standalone?
├─ Inline → Size matches x-height (16px for 14–16px text)
└─ Standalone → Size from the button scale (20–24px)
SVG or emoji?
├─ Interface chrome → SVG, always
└─ User-generated / casual → Emoji acceptable
Animated?
└─ Only if it communicates state change. Always with prefers-reduced-motion fallback.
Audit checklist
- All icons in a set share one stroke weight
- All icons in a set share one visual style (outline OR filled, not mixed)
- Icons use SVG, not icon fonts
- SVGs have
aria-hidden="true"andfocusable="false"when decorative - Icon-only buttons have
aria-labelon the button - Icon + text uses
currentColoror shares color tokens - Icon contrast ≥ 3:1 against background
- Mobile tap targets ≥ 44×44px
- Icons inline with text are visually centered
- Gap between icon and label is 6–8px
- No emoji in critical UI status
- No animated icons without reduced-motion fallback
Sources
- WCAG 2.2 · 1.1.1 Non-text Content, 1.4.11 Non-Text Contrast, 2.5.5 Target Size
- Material Design 3 · Iconography
- Apple HIG · SF Symbols (sizing, semantic scale)
- Phosphor / Lucide / Heroicons · stroke-weight reference
- Nielsen Norman · "Icon Usability" (icon + label research)