# ButtonBase: Руководства

## Паттерны

### Общий примитив действия

Когда использовать:
Создайте продуктовую обёртку для действия, которая всё равно наследует стили кнопок VegaUI.

Заметки по композиции:
Оберните `ButtonBase` в локальный компонент и задавайте `variant`, `appearance` и `type` явно.

Компромиссы:
Вы получаете гибкость, но берёте на себя ответственность за доступное имя, раскладку и корректность семантики.

```tsx
import { ButtonBase, type ButtonBaseProps, Icon } from '@vega-ui/react';
import { BoldIcon, ItalicIcon, UnderlineIcon } from '@vega-ui/icons';
import { useState } from 'react';

function ToolbarAction({ style, ...props }: ButtonBaseProps) {
  return (
    <ButtonBase
      type='button'
      variant='secondary'
      appearance='transparent'
      style={{ display: 'flex', padding: 'var(--spacing-3)', borderRadius: 'var(--radius-2)', ...style }}
      {...props}
    />
  );
}

export default function SharedActionPrimitive() {
  const [bold, setBold] = useState(false);
  const [italic, setItalic] = useState(false);
  const [underline, setUnderline] = useState(false);

  return (
    <div
      style={{
        display: 'flex',
        flexDirection: 'column',
        width: '100%',
        border: '1px solid var(--separator-opaque)',
        borderRadius: 'var(--radius-5)',
        background: 'var(--surface-primary)',
        overflow: 'hidden',
      }}
    >
      <div
        role='toolbar'
        aria-label='Text formatting'
        style={{
          display: 'flex',
          gap: 'var(--spacing-2)',
          padding: 'var(--spacing-3)',
          borderBlockEnd: '1px solid var(--separator-opaque)',
        }}
      >
        <ToolbarAction aria-label='Bold' aria-pressed={bold} appearance={bold ? 'ghost' : 'transparent'} onClick={() => setBold(!bold)}>
          <Icon size='sm'><BoldIcon /></Icon>
        </ToolbarAction>
        <ToolbarAction aria-label='Italic' aria-pressed={italic} appearance={italic ? 'ghost' : 'transparent'} onClick={() => setItalic(!italic)}>
          <Icon size='sm'><ItalicIcon /></Icon>
        </ToolbarAction>
        <ToolbarAction aria-label='Underline' aria-pressed={underline} appearance={underline ? 'ghost' : 'transparent'} onClick={() => setUnderline(!underline)}>
          <Icon size='sm'><UnderlineIcon /></Icon>
        </ToolbarAction>
      </div>
      <p
        style={{
          margin: 0,
          padding: 'var(--spacing-8)',
          fontWeight: bold ? 700 : 400,
          fontStyle: italic ? 'italic' : 'normal',
          textDecoration: underline ? 'underline' : 'none',
        }}
      >
        Release notes for version 2.4
      </p>
    </div>
  );
}
```

### Полиморфное навигационное действие

Когда использовать:
Нужно отрендерить цель навигации, которая визуально выглядит как кнопка.

Заметки по композиции:
Используйте `asChild` ровно с одной дочерней ссылкой или ссылкой роутера.

Компромиссы:
Результат сохраняет семантику ссылки, поэтому поведение клавиатуры и объявления скринридера отличаются от нативной кнопки.

```tsx
import { ButtonBase, Icon } from '@vega-ui/react';
import { UsersIcon } from '@vega-ui/icons';

export default function PolymorphicNavigationAction() {
  return (
    <ButtonBase asChild variant='secondary' appearance='ghost'>
      <a
        href='/settings/members'
        style={{
          display: 'inline-flex',
          alignItems: 'center',
          gap: 'var(--spacing-4)',
          padding: 'var(--spacing-3) var(--spacing-8)',
          borderRadius: 'var(--radius-3)',
          fontSize: 'var(--font-size-2)',
        }}
      >
        <Icon size='sm'><UsersIcon /></Icon>
        Manage members
      </a>
    </ButtonBase>
  );
}
```

## Сравнение

### Быстрое правило выбора

Выбирайте `ButtonBase`, только когда продукту нужен самый низкоуровневый общий примитив действия. Для обычных продуктовых кнопок предпочитайте `Button` или `IconButton`.

### `ButtonBase` или `Button`

Используйте `ButtonBase`, когда нужны базовые стили действия с собственной структурой.

Используйте `Button`, когда продуктовый код должен описывать стандартную текстовую кнопку через более высокоуровневый публичный API.

### `ButtonBase` или `IconButton`

Используйте `ButtonBase`, когда раскладка с одной иконкой нестандартная или иконка не главное.

Используйте `IconButton`, когда действие в первую очередь выражено иконкой и должно следовать контракту размеров кнопки-иконки.

### `ButtonBase` или `Link`

Используйте `ButtonBase`, когда взаимодействие — это действие.

Используйте `Link`, когда взаимодействие — это навигация и нужно сохранить семантику ссылки.

### Выбирайте этот компонент, когда

- собственному компоненту действия всё равно нужны стили кнопок VegaUI
- `asChild` необходим для роутера или примитива ссылки
- высокоуровневые API раскладки кнопок только мешали бы

### Не выбирайте этот компонент, когда

- продукту нужна просто стандартная кнопка
- взаимодействие — очевидно текстовая ссылка
- потребителям не стоит разбираться в низкоуровневой семантике и `type`

## Решение проблем

### Кнопка неожиданно отправляет форму

#### Симптом

Клик по действию отправляет окружающую форму, хотя в интерфейсе задумывалась обычная кнопка.

#### Вероятная причина

`type` не задан явно, и поведение браузера по умолчанию в этом контексте сработало как отправка формы.

#### Как проверить

- отрендерите кнопку внутри настоящей формы
- кликните по ней без намерения отправить форму
- проверьте, срабатывает ли обработчик отправки формы

#### Исправление

Задайте `type='button'` для действий, которые не отправляют форму.

### Действие выглядит правильно, но клавиатурная семантика не та

#### Симптом

Элемент выглядит как кнопка, но поведение клавиатуры или вывод скринридера соответствуют семантике ссылки.

#### Вероятная причина

`asChild` отрендерил ссылку или ссылку роутера, поэтому итоговый элемент сохранил семантику ссылки.

#### Как проверить

- посмотрите итоговый DOM-тег
- проверьте активацию с клавиатуры через `Enter` и `Space`
- проверьте объявления скринридера

#### Исправление

Используйте нативную кнопку, когда взаимодействие — это действие, а не навигация.

### Содержимое только из иконки объявляется невнятно

#### Симптом

Действие видно, но вспомогательные технологии не получают осмысленного имени.

#### Вероятная причина

Кнопка содержит только иконку, и `aria-label` не передан.

#### Как проверить

- посмотрите доступное имя в инструментах доступности браузера
- проверьте кнопку со скринридером

#### Исправление

Добавьте явный `aria-label` или видимый текст.
