WlButton
Измените props и посмотрите результат. Настройки одинаковы для примера и кода.
Настройки
Подпись «Добавить» передаётся в default-слот, иконка plus — в icon.
Пример
Нажатий: 0
Loading заменяет иконку спиннером и блокирует повторный click. Disabled тоже блокирует действие. В этом примере нет формы: submit и reset меняют атрибут type, а поведение формы проверяйте в приложении.
Код для приложения
Vue SFC · текущие настройки
<script setup lang="ts">
import { WlButton, WlIcon } from "gavia-ui";
import { ref } from "vue";
const count = ref(0);
</script>
<template>
<div class="wl-stack" data-space="md">
<div>
<WlButton v-bind='{"variant":"secondary","size":"md","density":"default","loading":false,"disabled":false,"block":false,"type":"button"}' @click="count++"><template #icon><WlIcon name="plus" /></template>{{ "Добавить" }}</WlButton>
</div>
<p role="status" class="wl-text-small">{{ "Нажатий:" }} {{ count }}</p>
</div>
</template>Все варианты
Сравните значения variant для основного, дополнительного действия и удаления.
primarysecondaryghostsoftlinkprimary + dangerghost + dangersoft + dangerВыберите любой вариант: все восемь кнопок выполняют локальное действие.
Vue SFC · пример для приложения
<script setup lang="ts">
import { WlButton, type WlButtonVariant } from "gavia-ui";
import { ref } from "vue";
// The public union makes a missing or unsupported variant a type error.
const labels = {
primary: "Создать",
secondary: "Черновик",
ghost: "Отмена",
soft: "Поделиться",
link: "Подробнее"
} satisfies Record<WlButtonVariant, string>;
const variants = Object.keys(labels) as WlButtonVariant[];
const lastAction = ref("");
</script>
<template>
<div class="wl-stack" data-space="lg">
<div class="button-variant-grid">
<div
v-for="variant in variants"
:key="variant"
class="wl-stack"
data-space="sm"
>
<div><WlButton :variant="variant" @click="lastAction = labels[variant]">{{ labels[variant] }}</WlButton></div>
<code class="wl-text-small">{{ variant }}</code>
</div>
<div class="wl-stack" data-space="sm">
<div>
<WlButton variant="primary" tone="danger" @click="lastAction = 'Удалить'">
{{ "Удалить" }}
</WlButton>
</div>
<code class="wl-text-small">primary + danger</code>
</div>
<div class="wl-stack" data-space="sm">
<div>
<WlButton variant="ghost" tone="danger" @click="lastAction = 'Удалить тихо'">
{{ "Удалить тихо" }}
</WlButton>
</div>
<code class="wl-text-small">ghost + danger</code>
</div>
<div class="wl-stack" data-space="sm">
<div>
<WlButton variant="soft" tone="danger" @click="lastAction = 'Архив'">
{{ "Архив" }}
</WlButton>
</div>
<code class="wl-text-small">soft + danger</code>
</div>
</div>
<p class="wl-text-small wl-text-muted" role="status">
{{ lastAction
? "Последнее действие: " + lastAction
: "Выберите любой вариант: все восемь кнопок выполняют локальное действие." }}
</p>
</div>
</template>
<style scoped>
.button-variant-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 140px), 1fr));
gap: var(--wl-space-lg);
align-items: start;
}
</style>Размеры и плотность
Четыре размера в обычной и компактной плотности. Сравните их сразу или скройте компактный ряд.
Обычная плотность
Компактная плотность
size выбирает размер, density="compact" уменьшает высоту. Размеры берутся из токенов выбранной темы.
Vue SFC · пример для приложения
<script setup lang="ts">
import { WlButton, WlCheckbox, type WlDensity, type WlSize } from "gavia-ui";
import { computed, ref } from "vue";
const labels = { xs: "XS", sm: "SM", md: "MD", lg: "LG" } satisfies Record<WlSize, string>;
const sizes = Object.keys(labels) as WlSize[];
const showCompact = ref(true);
const densities = computed<readonly WlDensity[]>(() => showCompact.value ? ["default", "compact"] : ["default"]);
</script>
<template>
<div class="wl-stack" data-space="lg">
<WlCheckbox v-model="showCompact">{{ "Показать компактный ряд" }}</WlCheckbox>
<div
v-for="density in densities"
:key="density"
class="wl-stack"
data-space="sm"
:data-button-density="density"
>
<h3 class="wl-text-label">{{ density === 'compact' ? "Компактная плотность" : "Обычная плотность" }}</h3>
<div class="wl-inline" data-space="md">
<WlButton
v-for="size in sizes"
:key="size"
:size="size"
:density="density"
variant="primary"
>{{ labels[size] }}</WlButton>
</div>
</div>
<p class="wl-text-small wl-text-muted">{{ "size выбирает размер, density=\"compact\" уменьшает высоту. Размеры берутся из токенов выбранной темы." }}</p>
</div>
</template>Disabled и loading
Disabled и loading, а также пример сохранения с завершением, отменой и повтором.
Disabled блокирует действие. Loading тоже блокирует кнопку и заменяет icon-слот спиннером.
Запросы не отправляются. Завершение и отмена в этом примере выполняются вручную.
Нажмите «Сохранить», затем завершите или отмените локальную загрузку. Сохранений: 0.
Vue SFC · пример для приложения
<script setup lang="ts">
import { WlButton, WlCheckbox, WlIcon } from "gavia-ui";
import { ref } from "vue";
const noAccess = ref(false);
const loading = ref(false);
const saved = ref(0);
const message = ref("Нажмите «Сохранить», затем завершите или отмените локальную загрузку.");
function finish(): void {
loading.value = false;
saved.value++;
message.value = "Сохранение завершено.";
}
function cancel(): void {
loading.value = false;
message.value = "Загрузка отменена. Можно повторить сохранение.";
}
</script>
<template>
<div class="wl-stack" data-space="lg">
<div class="wl-inline" data-space="md">
<WlButton disabled aria-describedby="button-disabled-reason">{{ "Недоступно" }}</WlButton>
<WlButton variant="primary" loading>{{ "Сохранение…" }}</WlButton>
</div>
<p id="button-disabled-reason" class="wl-text-small wl-text-muted">
{{ "Disabled блокирует действие. Loading тоже блокирует кнопку и заменяет icon-слот спиннером." }}
</p>
<WlCheckbox v-model="noAccess">{{ "Нет прав на сохранение" }}</WlCheckbox>
<div class="wl-inline" data-space="sm">
<WlButton
variant="primary"
:disabled="noAccess"
:loading="loading"
aria-describedby="button-saving-reason"
@click="loading = true"
>
<template #icon><WlIcon name="check" :size="16" /></template>
{{ loading ? "Сохраняем…" : "Сохранить" }}
</WlButton>
<WlButton v-if="loading" @click="finish">{{ "Завершить сохранение" }}</WlButton>
<WlButton v-if="loading" variant="ghost" @click="cancel">{{ "Отменить загрузку" }}</WlButton>
</div>
<p id="button-saving-reason" class="wl-text-small wl-text-muted">
{{ noAccess
? "Для этого действия нужны права на редактирование."
: "Запросы не отправляются. Завершение и отмена в этом примере выполняются вручную." }}
</p>
<p class="wl-text-small" role="status">{{ message }} {{ "Сохранений:" }} {{ saved }}.</p>
</div>
</template>Иконки и содержимое слотов
Иконки слева и справа, кнопка без текста и счётчик в default-слоте.
icon размещает иконку слева. В default-слот можно добавить иконку справа или счётчик. Кнопка без текста получает aria-label.
Нажмите кнопку и проверьте действие и доступное имя.
Vue SFC · пример для приложения
<script setup lang="ts">
import { WlButton, WlIcon } from "gavia-ui";
import { ref } from "vue";
const lastAction = ref("");
const unread = ref(3);
function readMessages(): void {
unread.value = 0;
lastAction.value = "Сообщения прочитаны";
}
</script>
<template>
<div class="wl-stack" data-space="lg">
<div class="wl-inline" data-space="md">
<WlButton variant="primary" @click="lastAction = 'Новая задача'">
<template #icon><WlIcon name="plus" :size="16" /></template>
{{ "Новая задача" }}
</WlButton>
<WlButton @click="lastAction = 'Найти'">
<template #icon><WlIcon name="search" :size="16" /></template>
{{ "Найти" }}
</WlButton>
<WlButton @click="lastAction = 'Продолжить'">
<span class="button-slot-inline">{{ "Продолжить" }}<WlIcon name="arrow-right" :size="16" /></span>
</WlButton>
<WlButton
variant="ghost"
tone="danger"
aria-label="Удалить черновик"
@click="lastAction = 'Черновик удалён'"
>
<template #icon><WlIcon name="trash" :size="16" /></template>
</WlButton>
<WlButton variant="soft" @click="readMessages">
<span class="button-slot-inline">{{ "Сообщения" }}<strong>{{ unread }}</strong></span>
</WlButton>
</div>
<p class="wl-text-small wl-text-muted">{{ "icon размещает иконку слева. В default-слот можно добавить иконку справа или счётчик. Кнопка без текста получает aria-label." }}</p>
<p class="wl-text-small" role="status">
{{ lastAction
? "Последнее действие: " + lastAction
: "Нажмите кнопку и проверьте действие и доступное имя." }}
</p>
</div>
</template>
<style scoped>
.button-slot-inline { display: inline-flex; align-items: center; gap: var(--wl-space-sm); }
</style>Ширина кнопки и отправка формы
Переключите block, заполните обязательное поле и сравните button, submit и reset.
Форма ещё не отправлена. Отправок: 0. Проверок черновика: 0.
Обычная WlButton имеет type="button" и не отправляет форму. Submit и reset задаются явно. Этот пример хранит результат локально.
Vue SFC · пример для приложения
<script setup lang="ts">
import { WlButton, WlCheckbox, WlInput } from "gavia-ui";
import { ref } from "vue";
const name = ref("");
const block = ref(true);
const submitted = ref(0);
const drafts = ref(0);
const message = ref("Форма ещё не отправлена.");
function submit(): void {
submitted.value++;
message.value = "Создан проект «" + name.value.trim() + "».";
}
function reset(): void {
name.value = "";
message.value = "Форма очищена.";
}
</script>
<template>
<div class="wl-stack" data-space="lg">
<WlCheckbox v-model="block">{{ "Кнопка отправки на всю ширину" }}</WlCheckbox>
<form
class="button-form-example wl-stack"
data-space="md"
aria-label="Создание проекта"
@submit.prevent="submit"
@reset.prevent="reset"
>
<div class="wl-stack" data-space="xs">
<label for="button-form-name" class="wl-text-label">{{ "Название проекта" }}</label>
<WlInput
id="button-form-name"
v-model="name"
name="projectName"
required
minlength="3"
pattern=".*\S.*"
aria-describedby="button-form-hint"
/>
<p id="button-form-hint" class="wl-text-small wl-text-muted">
{{ "Не менее трёх символов. Нативная проверка выполняется до submit." }}
</p>
</div>
<div data-button-form-submit>
<WlButton type="submit" variant="primary" :block="block">{{ "Создать проект" }}</WlButton>
</div>
<div class="wl-inline" data-space="sm">
<WlButton @click="drafts++">{{ "Проверить черновик" }}</WlButton>
<WlButton type="reset" variant="ghost">{{ "Сбросить форму" }}</WlButton>
</div>
</form>
<p class="wl-text-small" role="status">
{{ message }} {{ "Отправок:" }} {{ submitted }}{{ ". Проверок черновика:" }} {{ drafts }}.
</p>
<p class="wl-text-small wl-text-muted">{{ "Обычная WlButton имеет type=\"button\" и не отправляет форму. Submit и reset задаются явно. Этот пример хранит результат локально." }}</p>
</div>
</template>
<style scoped>
.button-form-example { width: 100%; max-width: 28rem; }
</style>Когда использовать
- primary — основное действие группы; secondary — дополнительное.
- ghost и link — менее заметные действия; tone="danger" обозначает действие, которое может удалить данные.
- block заполняет доступную ширину; size и density меняют размеры и отступы, сохраняя DOM-контракт.
Полный список вариантов берётся из манифеста в настройках и API. Для перехода по URL используйте ссылку; WlButton рендерит нативный button.
Props
Типы, допустимые значения и значения по умолчанию взяты из публичного манифеста. «—» означает, что prop не задан.
| Prop | Тип / значения | По умолчанию | Назначение |
|---|---|---|---|
variant | WlButtonVariant | "secondary" | Визуальный вариант кнопки. |
tone | WlTone | — | Семантический цвет, независимый от варианта оформления. |
size | WlSize | "md" | Размер кнопки. |
density | WlDensity | "default" | В режиме compact высота кнопки меньше. |
loading | boolean | false | Показывает загрузку вместо иконки и блокирует нажатие. |
disabled | boolean | false | Отключает кнопку. |
block | boolean | false | Растянуть на всю ширину контейнера. |
type | "button" | | "button" | Нативный type кнопки. |
pt | WlPt< | — | Атрибуты внутренних элементов Gavia UI. |
as | "button" | | — | Явно выбирает отрисовку ссылки; href или to тоже включают этот режим. |
href | string | — | Адрес ссылки, передаваемый в WlLink. |
to | unknown | — | Назначение маршрута приложения, которое обрабатывает настроенный адаптер ссылок. |
iconPosition | "start" | | — | Располагает иконку перед основным слотом или после него. |
События
| Событие | Payload | Когда возникает |
|---|---|---|
click | [event: MouseEvent] | Клик (не срабатывает при disabled/loading). |
Слоты
| Слот | Тип / значения | Содержимое |
|---|---|---|
default | — | Текст или другое содержимое кнопки. |
icon | — | Иконка слева (скрывается при loading). |
Pass-through: pt
Секции WlButton перечислены ниже. Атрибуты объединяются в порядке createWlPt() WlConfig.pt.buttonpt экземпляра. Class и style объединяются; последнее заданное значение другого атрибута имеет приоритет.
| Секция | DOM | Назначение |
|---|---|---|
root | button | Атрибуты корневой кнопки, дополнительные class и style. |
label | span.wl-btn__label | Обёртка default-слота. Появляется, когда в слот передано содержимое. |
loadingIcon | span.wl-btn__spinner | Спиннер при loading; скрыт от вспомогательных технологий. |
Иконка передаётся через слот icon; отдельной pt-секции для неё нет. Class, style, aria-* и data-* можно передать самой кнопке. Корень сохраняет data-wl="button", data-variant, data-size и data-density.
Клавиатура и состояния
Клавиатура и ответственность приложения
| Клавиша | Действие |
|---|---|
| Tab / Shift+Tab | Перейти между доступными ссылками, действиями и полями. |
| Enter / Space | Выполнить нативное действие; для перехода по ссылке нажмите Enter. |
Задайте понятное доступное имя и свяжите подпись, подсказку и ошибку с фокусируемым элементом. Проверьте поведение со своими данными и скринридером.
WAI-ARIA Authoring Practices- Tab переводит фокус на доступную кнопку, Enter или Space выполняют действие. Фокус виден во всех темах.
- При
disabledилиloadingнативныйdisabledблокирует click и исключает кнопку из Tab-порядка. loadingзадаётaria-busy="true". Спиннер имеетaria-hidden="true"; текст кнопки остаётся её доступным именем.- Объясняйте причину недоступности рядом с действием. Не передавайте смысл только цветом или иконкой.
- У кнопки только с иконкой должно быть доступное имя, например
aria-label. Для этого сценария также есть WlIconButton. - Внутри формы задавайте
type="submit"илиtype="reset"намеренно; по умолчанию используетсяtype="button", который не отправляет форму.
Проверка на этой странице: вернитесь к примерам, нажмите «Фокус на примере», нажмите Enter/Space и проверьте счётчик. Затем включите disabled или loading: действие перестанет выполняться.