Bỏ qua, đến nội dung chính
Design System9 min read25/08/2026

Xây dựng Design System & UI Primitives với Tailwind CSS v4, CVA và Base UI

Từng bước thiết kế hệ thống component có thể tái sử dụng cao: tận dụng Tailwind CSS v4 CSS-first token, Class Variance Authority (CVA) quản lý biến thể linh hoạt và Headless UI chuẩn Accessible.

N

Nguyễn Duy Noa

Frontend Developer

Một trong những bài toán lớn nhất của các nhóm phát triển Frontend là làm sao để duy trì tính nhất quán về mặt giao diện (Visual Consistency) khi dự án phình to. Việc viết CSS ad-hoc hay copy paste class Tailwind lặp đi lặp lại sẽ dẫn đến sự hỗn loạn về mã nguồn và rất khó bảo trì.

Giải pháp chuẩn mực hiện nay là kết hợp sức mạnh của **Tailwind CSS v4** (với kiến trúc @theme inline), **Class Variance Authority (CVA)** để định nghĩa component variants, và các thư viện **Headless UI Primitives** (@base-ui/react, Radix UI) để đảm bảo chuẩn Accessibility (a11y).

1. Kiến trúc CSS-First Token trong Tailwind CSS v4

Khác với các phiên bản trước phụ thuộc vào file cấu hình tailwind.config.js, Tailwind CSS v4 chuyển toàn bộ cấu hình token về file CSS thông qua chỉ thị @theme:

globals.css
css
@import "tailwindcss";

@theme inline {
  --color-brand-primary: #10b981;
  --color-brand-accent: #06b6d4;
  --color-surface-card: #18181b;
  --radius-custom: 0.75rem;
  --font-display: "Outfit", sans-serif;
}

/* Các biến theme tối/sáng */
:root {
  --background: #09090b;
  --foreground: #f4f4f5;
}

2. Quản lý biến thể Component với Class Variance Authority (CVA)

CVA cho phép bạn định nghĩa các biến thể giao diện (variants) như primary, secondary, danger cùng kích thước sm, md, lg với Type Safety 100%:

components/ui/Badge.tsx
typescript
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
import React from "react";

const badgeVariants = cva(
  "inline-flex items-center gap-1.5 font-mono text-xs font-semibold px-2.5 py-1 rounded-full border transition-colors",
  {
    variants: {
      variant: {
        default: "bg-zinc-900 border-zinc-800 text-zinc-300",
        success: "bg-emerald-500/10 border-emerald-500/20 text-emerald-400",
        warning: "bg-amber-500/10 border-amber-500/20 text-amber-400",
        danger: "bg-red-500/10 border-red-500/20 text-red-400",
      },
    },
    defaultVariants: {
      variant: "default",
    },
  }
);

export interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement>, VariantProps<typeof badgeVariants> {}

export function Badge({ className, variant, ...props }: BadgeProps) {
  return <span className={cn(badgeVariants({ variant }), className)} {...props} />;
}

3. Tích hợp Headless UI cho các Component phức tạp (Dialog, Dropdown)

Đối với các component có trạng thái phức tạp như Modal/Dialog, Menu, Tooltip, bạn không nên tự viết logic bắt phím Escape hay khóa scroll. Hãy sử dụng @base-ui/react hoặc Radix UI:
- Đầy đủ tính năng Focus Management & ARIA Attributes.
- Tự do style 100% bằng class Tailwind của bạn.

components/ui/Modal.tsx
tsx
"use client";

import { Dialog } from "@base-ui/react";
import { X } from "@phosphor-icons/react";

export function CustomModal({ isOpen, onClose, title, children }: any) {
  return (
    <Dialog.Root open={isOpen} onOpenChange={onClose}>
      <Dialog.Portal>
        <Dialog.Backdrop className="fixed inset-0 bg-black/70 backdrop-blur-sm z-50 animate-fade-in" />
        <Dialog.Popup className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-full max-w-lg bg-zinc-900 border border-zinc-800 rounded-2xl p-6 shadow-2xl z-50">
          <div className="flex items-center justify-between mb-4">
            <Dialog.Title className="text-lg font-bold text-zinc-100">{title}</Dialog.Title>
            <Dialog.Close className="text-zinc-500 hover:text-zinc-200">
              <X size={20} />
            </Dialog.Close>
          </div>
          <Dialog.Description className="text-zinc-400 text-sm">{children}</Dialog.Description>
        </Dialog.Popup>
      </Dialog.Portal>
    </Dialog.Root>
  );
}

Bạn đang gặp vấn đề tương tự?

Tôi có thể giúp bạn tối ưu website — liên hệ để trao đổi miễn phí.

Kết luận

Kết hợp Tailwind CSS v4, CVA và Base UI tạo nên một nền tảng vững chắc để phát triển sản phẩm. Codebase của bạn sẽ vừa chuẩn mực về mặt thẩm mỹ, vừa dễ mở rộng và thân thiện với tất cả người dùng.

Tailwind CSS v4Design SystemCVABase UIUI/UXTypeScript

Muốn thảo luận thêm?

Nếu bạn có câu hỏi cụ thể về web performance, React, hoặc muốn hợp tác dự án — liên hệ trực tiếp với tôi.

Gửi email