Giao diện
CButton
Nút hành động chuẩn của framework. Bọc a-button của Ant Design Vue, thêm bộ biến thể bám design token nên màu và trạng thái luôn khớp theme, kể cả chế độ tối.
Dùng khi: người dùng cần thực hiện một hành động — lưu, xoá, mở drawer, tải xuống, gửi duyệt.
Không dùng khi: chỉ điều hướng sang trang khác và không có tác dụng phụ — dùng variant="link" hoặc thẻ liên kết thật để trình duyệt mở tab mới, bookmark được.
vue
<!-- Đăng ký toàn cục qua layer, không cần import -->
<CButton variant="primary">Lưu</CButton>Biến thể
Chọn theo vai trò của hành động, không theo màu muốn nhìn thấy.
| Biến thể | Dùng cho | Số lượng trong một khu vực |
|---|---|---|
primary | Hành động chính của màn hình | Một |
secondary / outline | Hành động phụ ngang hàng nhau | Vài cái |
ghost | Hành động phụ trong toolbar, nền mờ | Vài cái |
danger | Hành động phá huỷ: xoá, huỷ đơn, thu hồi | Một, tách khỏi nhóm khác |
link | Điều hướng trong ngữ cảnh văn bản | Không giới hạn |
text | Hành động nhẹ nhất: Huỷ, Đóng, Bỏ qua | Vài cái |
vue
<script setup lang="ts">
// Bảy biến thể. Chọn theo VAI TRÒ của hành động, không theo màu muốn thấy.
</script>
<template>
<div style="display: flex; gap: 8px; flex-wrap: wrap; align-items: center">
<CButton variant="primary">
Lưu
</CButton>
<CButton variant="secondary">
Nhập từ Excel
</CButton>
<CButton variant="outline">
Xuất dữ liệu
</CButton>
<CButton variant="ghost">
Bộ lọc
</CButton>
<CButton variant="danger">
Xoá
</CButton>
<CButton variant="link">
Xem chi tiết
</CButton>
<CButton variant="text">
Huỷ
</CButton>
</div>
</template>Cỡ nút
vue
<script setup lang="ts">
// sm dùng trong bảng và toolbar; md là mặc định; lg cho hành động chính của form dài.
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 12px">
<div style="display: flex; gap: 8px; align-items: center">
<CButton size="sm">
Nhỏ
</CButton>
<CButton size="md">
Mặc định
</CButton>
<CButton size="lg">
Lớn
</CButton>
</div>
<div style="max-width: 280px">
<CButton
variant="primary"
block
>
Giãn hết chiều ngang
</CButton>
</div>
</div>
</template>sm cho toolbar và ô hành động trong bảng; md là mặc định; lg cho nút chính ở cuối form dài. block giãn nút hết chiều ngang phần tử cha — hay dùng trong form đăng nhập và khung hẹp.
Kèm icon
vue
<script setup lang="ts">
import { IconDownload, IconPlus, IconTrash } from '@tabler/icons-vue'
// Icon đặt trong slot #icon để antd canh đúng khoảng cách với nhãn.
// Dùng @tabler/icons-vue — bộ icon đã có sẵn trong template dự án.
</script>
<template>
<div style="display: flex; gap: 8px; flex-wrap: wrap; align-items: center">
<CButton variant="primary">
<template #icon>
<IconPlus :size="16" />
</template>
Thêm mới
</CButton>
<CButton variant="outline">
<template #icon>
<IconDownload :size="16" />
</template>
Tải xuống
</CButton>
<CButton
variant="danger"
size="sm"
>
<template #icon>
<IconTrash :size="15" />
</template>
Xoá
</CButton>
<CButton
variant="ghost"
aria-label="Thêm mới"
>
<template #icon>
<IconPlus :size="16" />
</template>
</CButton>
</div>
</template>Icon đặt trong slot #icon để antd canh khoảng cách với nhãn; đặt vào slot mặc định sẽ lệch. Nút chỉ có icon phải có aria-label, nếu không người dùng trình đọc màn hình không biết nút làm gì.
Trạng thái
vue
<script setup lang="ts">
import { ref } from 'vue'
// `loading` và `disabled` là thuộc tính gốc của antd, CButton chuyển tiếp nguyên vẹn.
// Khoá nút trong lúc gửi để tránh bấm hai lần.
const submitting = ref(false)
function submit() {
submitting.value = true
setTimeout(() => (submitting.value = false), 1500)
}
</script>
<template>
<div style="display: flex; gap: 8px; flex-wrap: wrap; align-items: center">
<CButton
variant="primary"
:loading="submitting"
@click="submit"
>
Gửi duyệt
</CButton>
<CButton
variant="outline"
disabled
>
Chưa đủ điều kiện
</CButton>
<CButton
variant="danger"
loading
>
Đang xoá
</CButton>
</div>
</template>loading và disabled là thuộc tính gốc của antd, CButton chuyển tiếp nguyên vẹn cùng mọi thuộc tính khác (html-type, href, title…). Trong lúc gửi dữ liệu thì bật loading thay vì tự khoá bằng disabled — antd đã chặn click sẵn, và người dùng thấy được là hệ thống đang xử lý.
Nhóm hành động
vue
<script setup lang="ts">
import { IconPlus } from '@tabler/icons-vue'
// Một nhóm hành động chỉ có MỘT nút primary. Hành động phụ dùng outline/text,
// hành động phá huỷ dùng danger và luôn đứng tách khỏi nhóm còn lại.
</script>
<template>
<div
style="
display: flex;
align-items: center;
gap: 8px;
padding: 12px 16px;
background: var(--tasco-color-surface);
border: 1px solid var(--tasco-color-border);
border-radius: var(--tasco-radius-lg);
"
>
<strong style="margin-right: auto; color: var(--tasco-color-text)">Đơn hàng DH-0001</strong>
<CButton
variant="text"
size="sm"
>
Huỷ
</CButton>
<CButton
variant="outline"
size="sm"
>
Lưu nháp
</CButton>
<CButton
variant="primary"
size="sm"
>
<template #icon>
<IconPlus :size="15" />
</template>
Tạo đơn
</CButton>
</div>
</template>Thứ tự đọc từ trái sang phải là thứ tự tăng dần mức độ cam kết: hành động rút lui (text) đứng trước, hành động chính (primary) đứng cuối cùng bên phải.
API
Props
| Tên | Kiểu | Mặc định | Mô tả |
|---|---|---|---|
variant | "primary" | "secondary" | "outline" | "ghost" | "danger" | "link" | "text" | 'primary' | Kiểu nút: primary là hành động chính của màn hình, danger cho hành động phá huỷ. |
size | "md" | "sm" | "lg" | 'md' | Cỡ nút; `sm` dùng cho toolbar và trong bảng. |
block | boolean | false | Giãn hết chiều ngang của phần tử cha. |
Slot
| Tên | Props của slot | Mô tả |
|---|---|---|
#default | any | Nhãn nút. |
#icon | any | Icon đặt trước nhãn (khuyến nghị |
Giao diện
| Biến thể | Nền | Chữ |
|---|---|---|
primary | --tasco-gradient-primary | #fff |
secondary, outline | --tasco-color-surface, viền --tasco-color-border-strong | --tasco-color-primary |
ghost | --tasco-color-primary-soft | --tasco-color-primary |
danger | --tasco-gradient-danger | #fff |
link, text | trong suốt | --tasco-color-primary, --tasco-color-text |
| Biến | Sáng | Tối | Tương phản |
|---|---|---|---|
--tasco-color-primary | #203368 | #7090dc | 12.10:1 / 5.21:1 |
--tasco-color-primary-hover | #2f4b8f | #94ade6 | 8.32:1 / 7.28:1 |
--tasco-color-primary-active | #182544 | #35549e | 15.14:1 / 2.26:1 |
--tasco-color-primary-soft | #eef3fb | #1a2740 | 1.11:1 / 1.09:1 |
Cần chỉnh khoảng cách hay bo góc: ghi đè class BEM c-btn, c-btn--<variant>, c-btn--<size>, c-btn--block. Không hardcode mã màu — xem Viết CSS trong dự án.
Khả năng tiếp cận
- Phần tử render ra là
<button>thật: focus được bằngTab, kích hoạt bằngEnter/Space. - Nút chỉ có icon bắt buộc có
aria-labelmô tả hành động. - Nhãn nói rõ hành động ("Xoá đơn hàng") thay vì chung chung ("OK") — nhãn còn được đọc lên khi người dùng duyệt danh sách nút.
- Đừng dùng màu làm kênh thông tin duy nhất: hành động phá huỷ nên có cả nhãn nói rõ lẫn
danger.
Lưu ý
- Một nhóm hành động chỉ nên có một nút
primary; nhiều nút primary làm mất trọng tâm. - Hành động không thể hoàn tác thì hỏi lại trước khi chạy — xem
useConfirm. - Chặn quyền bằng
v-canthay vì tự viếtv-ifrải rác:v-canđể ẩn,v-can:disableđể khoá. - Nút gửi form đặt
html-type="submit"nếu muốn Enter trong ô nhập cũng gửi được.
Liên quan
CTag— nhãn trạng thái, không bấm được.- Chặn theo quyền — ẩn/khoá nút theo quyền.
- Trang form — vị trí nút trong form.