# IconButton Guides

## Patterns

### Compact Toolbar Action

When to use:

- Dense toolbars or headers need compact actions.

Composition notes:

- Keep labels explicit through `aria-label`.
- Reserve icon-only treatment for familiar actions.

Trade-offs:

- Saves space.
- Less explicit than text buttons.

```tsx
import { Icon, IconButton, Text } from '@vega-ui/react';
import { EllipsisIcon, PencilIcon, Share2Icon } from '@vega-ui/icons';
import { useState } from 'react';

export default function BasicUsage() {
  const [lastAction, setLastAction] = useState('');

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--spacing-2)', width: '100%' }}>
      <header style={{ display: 'flex', alignItems: 'center', gap: 'var(--spacing-2)' }}>
        <Text size={4} fontWeight={600} style={{ flex: 1, minWidth: 0 }}>Brand guidelines</Text>
        <IconButton type='button' size='sm' variant='secondary' appearance='transparent' aria-label='Rename document' onClick={() => setLastAction('Rename')}>
          <Icon><PencilIcon /></Icon>
        </IconButton>
        <IconButton type='button' size='sm' variant='secondary' appearance='transparent' aria-label='Share document' onClick={() => setLastAction('Share')}>
          <Icon><Share2Icon /></Icon>
        </IconButton>
        <IconButton type='button' size='sm' variant='secondary' appearance='transparent' aria-label='More actions' onClick={() => setLastAction('More')}>
          <Icon><EllipsisIcon /></Icon>
        </IconButton>
      </header>
      <Text size={2} style={{ color: 'var(--label-secondary)' }}>
        {lastAction === '' ? 'Updated 2 hours ago' : `Clicked: ${lastAction}`}
      </Text>
    </div>
  );
}
```

### Inline Clear Action

When to use:

- A small local control clears or removes adjacent state.

Composition notes:

- Place the button close to the thing it affects.
- Keep the action narrowly scoped.

Trade-offs:

- Very compact.
- Easy to overuse in forms without enough context.

```tsx
import type { CSSProperties } from 'react';
import { Icon, IconButton, Label, TextField, TextFieldInput } from '@vega-ui/react';
import { SearchIcon, XIcon } from '@vega-ui/icons';
import { FormEvent, useRef, useState } from 'react';

export default function InlineClearAction() {
  const input = useRef<HTMLInputElement>(null);
  const [query, setQuery] = useState('quarterly report');
  const [submitted, setSubmitted] = useState('');

  const clear = () => {
    setQuery('');
    input.current?.focus();
  };

  const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    setSubmitted(String(new FormData(event.currentTarget).get('q')));
  };

  return (
    <form onSubmit={handleSubmit} style={{ display: 'flex', flexDirection: 'column', gap: 'var(--spacing-3)', width: '100%' }}>
      <Label htmlFor='file-search'>Search files</Label>
      <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--spacing-4)' }}>
        <TextField style={{ flex: 1, minWidth: 0, paddingInlineEnd: 'var(--spacing-5)' }}>
          <TextFieldInput
            ref={input}
            id='file-search'
            name='q'
            value={query}
            onChange={(event) => setQuery(event.target.value)}
          />
          {query !== '' && (
            <IconButton
              type='button'
              variant='secondary'
              appearance='ghost'
              aria-label='Clear search'
              onClick={clear}
              style={{ flexShrink: 0, '--icon-button-size': '24px', '--icon-button-br-ratio': 0.5 } as CSSProperties}
            >
              <Icon size='xs'><XIcon strokeWidth={2.5} /></Icon>
            </IconButton>
          )}
        </TextField>
        <IconButton type='submit' aria-label='Search'>
          <Icon><SearchIcon /></Icon>
        </IconButton>
      </div>
      <output style={{ color: 'var(--label-secondary)', overflowWrap: 'anywhere' }}>
        {submitted === '' ? 'Press Enter or the search button' : `Searched for “${submitted}”`}
      </output>
    </form>
  );
}
```

### Navigational Icon Surface

When to use:

- Navigation needs icon-button visuals while preserving link semantics.

Composition notes:

- Use `asChild` with a real anchor.
- Keep the destination obvious from the surrounding context and accessible label.

Trade-offs:

- Strong visual consistency.
- Easy to misuse for ambiguous navigation.

```tsx
import { Icon, IconButton } from '@vega-ui/react';
import { BookOpenIcon, LifeBuoyIcon, RssIcon } from '@vega-ui/icons';

const links = [
  { href: '/docs', label: 'Documentation', icon: <BookOpenIcon /> },
  { href: '/support', label: 'Help center', icon: <LifeBuoyIcon /> },
  { href: '/blog/rss.xml', label: 'Blog RSS feed', icon: <RssIcon /> },
];

export default function NavigationalIconSurface() {
  return (
    <nav aria-label='Resources'>
      <ul style={{ display: 'flex', gap: 'var(--spacing-4)', margin: 0, padding: 0, listStyle: 'none' }}>
        {links.map((link) => (
          <li key={link.href}>
            <IconButton asChild variant='secondary' appearance='ghost'>
              <a href={link.href} aria-label={link.label}>
                <Icon>{link.icon}</Icon>
              </a>
            </IconButton>
          </li>
        ))}
      </ul>
    </nav>
  );
}
```

## Comparison

### Quick Decision Rule

Use `IconButton` when the action is compact, common, and already recognizable without visible text.

### `IconButton` vs `Button`

Use `IconButton` when:

- space is constrained
- the action is common and recognizable

Use `Button` when:

- visible text improves clarity

Main trade-off: icon button is denser, while button is clearer.

### `IconButton` vs `Link`

Use `IconButton` when:

- the interaction is an action

Use `Link` when:

- the interaction primarily navigates

Main trade-off: icon button is action semantics, while link is navigation semantics.

### `IconButton` vs `ButtonBase`

Use `IconButton` when:

- the product explicitly wants the icon-button size and layout contract

Use `ButtonBase` when:

- a lower-level custom icon action surface is being built

### Choose This Component When

- The action is icon-first and well understood.
- Space is constrained.
- The UI still needs a button-like action surface.
- the action should stay compact without becoming a generic low-level primitive

### Do Not Choose This Component When

- The action meaning is unclear without visible text.
- The interaction primarily navigates.
- A regular button would improve comprehension.

## Troubleshooting

### Action Meaning Is Unclear

#### Symptom

Users see the icon but do not know what the action does.

#### Likely Cause

`IconButton` was used for an action that needs visible text or stronger context.

#### How To Verify

- Hide surrounding context and inspect whether the action is still obvious.
- Check whether the same icon has a stable meaning elsewhere in the product.

#### Fix

- Switch to `Button` if visible text improves clarity.
- Keep icon-only treatment for familiar actions.

### Missing Accessible Name

#### Symptom

The button works visually but has no clear accessible label.

#### Likely Cause

No `aria-label` or equivalent supported labeling was provided.

#### How To Verify

- Inspect the rendered markup.
- Test with accessibility tooling or screen reader output.

#### Fix

- Provide an explicit accessible label.

### Polymorphic IconButton Behaves Strangely

#### Symptom

The icon-button visuals render, but the child element does not support the intended semantics or props.

#### Likely Cause

`asChild` was used with an incompatible child element.

#### How To Verify

- Inspect the rendered child element.
- Check whether it matches the intended action or navigation semantics.

#### Fix

- Use a child that supports the intended behavior.
- Keep default rendering when polymorphism is unnecessary.
