Cách Mình Thiết Kế Giao Diện
Nền tảng design system, brand guidelines, và tiêu chuẩn UI components
Giao diện mà mỗi trang trông một kiểu thì user mất niềm tin ngay. Design system giúp mọi thứ nhìn đồng bộ -- từ button cho tới spacing, color cho tới typography -- để user dùng app mình mà không thấy "lạ lạ".
Ý tưởng cốt lõi: Nhất quán tạo niềm tin. Mọi trang, mọi component phải trông như cùng một sản phẩm.
Design System Của Mình
Mình build trên Tailwind CSS + Shadcn UI, customize cho brand:
┌─────────────────────────────────────────┐
│ Design Tokens │
│ Colors, Typography, Spacing, Shadows │
├─────────────────────────────────────────┤
│ Base Components │
│ Button, Input, Card, Dialog (Shadcn) │
├─────────────────────────────────────────┤
│ Pattern Components │
│ DataTable, FormSection, PageHeader │
├─────────────────────────────────────────┤
│ Pages │
│ Dashboard, Settings, Results │
└─────────────────────────────────────────┘
Design tokens là nền tảng, Shadcn cho base components, mình xây thêm pattern components phía trên, rồi ghép vào pages.
Colors
Bảng màu chính
| Màu | Tailwind Class | Dùng cho |
|---|---|---|
| Primary | bg-primary | Actions chính, links, focus states |
| Secondary | bg-secondary | Actions phụ, backgrounds |
| Accent | bg-accent | Highlights, notifications |
| Destructive | bg-destructive | Delete, error states |
Semantic Colors
| Mục đích | Light Mode | Dark Mode |
|---|---|---|
| Background | bg-background | dark:bg-background |
| Foreground | text-foreground | dark:text-foreground |
| Muted | bg-muted | dark:bg-muted |
| Border | border-border | dark:border-border |
Chọn màu nào cho trường hợp nào
| Tình huống | Màu |
|---|---|
| Button action chính | bg-primary |
| Button phụ/cancel | bg-secondary |
| Action nguy hiểm | bg-destructive |
| Thành công | text-green-600 |
| Cảnh báo | text-yellow-600 |
| Info/neutral | text-blue-600 |
Nguyên tắc đơn giản: primary color chỉ dùng cho main actions thôi. Phần lớn UI nên neutral -- dùng color có chiến lược chứ không phải cái gì cũng tô màu.
Typography
Font Stack
/* System fonts mặc định, ưu tiên performance */
font-family: ui-sans-serif, system-ui, sans-serif;
/* Monospace cho code */
font-family: ui-monospace, monospace;
Text Sizes
| Element | Tailwind | Size |
|---|---|---|
| Tiêu đề trang | text-3xl font-bold | 30px |
| Heading section | text-2xl font-semibold | 24px |
| Tiêu đề card | text-lg font-medium | 18px |
| Body text | text-base | 16px |
| Text nhỏ | text-sm | 14px |
| Caption | text-xs text-muted-foreground | 12px |
Spacing
Mình dùng thang spacing của Tailwind, giữ nhất quán xuyên suốt:
| Trường hợp | Spacing | Tailwind |
|---|---|---|
| Giữa các form fields | 16px | space-y-4 |
| Giữa các sections | 24px | space-y-6 |
| Card padding | 24px | p-6 |
| Button padding | 16px horizontal | px-4 py-2 |
| Page margins | 32px | p-8 |
Quan trọng là pick một convention rồi giữ xuyên suốt. Card padding là p-6 thì tất cả cards đều p-6 -- đừng trang này p-4 trang kia p-8.
Component Patterns
Buttons
// Action chính
<Button>Save Changes</Button>
// Action phụ
<Button variant="secondary">Cancel</Button>
// Action nguy hiểm
<Button variant="destructive">Delete</Button>
// Action ghost/subtle
<Button variant="ghost">Learn More</Button>
// Với loading state
<Button disabled>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Saving...
</Button>
Forms
<form className="space-y-4">
<div className="space-y-2">
<Label htmlFor="name">Name</Label>
<Input id="name" placeholder="Enter name" />
</div>
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Input id="email" type="email" />
<p className="text-sm text-muted-foreground">
Mình sẽ không chia sẻ email của bạn.
</p>
</div>
<Button type="submit">Submit</Button>
</form>
Cards
<Card>
<CardHeader>
<CardTitle>Card Title</CardTitle>
<CardDescription>Mô tả ngắn</CardDescription>
</CardHeader>
<CardContent>
{/* Nội dung chính */}
</CardContent>
<CardFooter>
<Button>Action</Button>
</CardFooter>
</Card>
Data Tables
<Table>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
<TableHead>Status</TableHead>
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell>Item name</TableCell>
<TableCell>
<Badge variant="success">Active</Badge>
</TableCell>
<TableCell className="text-right">
<Button variant="ghost" size="sm">Edit</Button>
</TableCell>
</TableRow>
</TableBody>
</Table>
Layout Patterns
Cấu trúc trang
<div className="container mx-auto p-8">
{/* Header trang */}
<div className="mb-8">
<h1 className="text-3xl font-bold">Page Title</h1>
<p className="text-muted-foreground">Mô tả trang</p>
</div>
{/* Nội dung chính */}
<div className="grid gap-6">
{/* Các sections */}
</div>
</div>
Dashboard Grid
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
<Card>{/* Metric 1 */}</Card>
<Card>{/* Metric 2 */}</Card>
<Card>{/* Metric 3 */}</Card>
</div>
Những Lỗi Hay Gặp
Spacing lung tung là lỗi phổ biến nhất. Trang này p-4, trang kia p-6, chỗ thì p-8 -- user nhìn vào thấy "lộn xộn" mà không biết tại sao. Cách sửa đơn giản: pick convention rồi giữ. Card padding p-6, section gaps space-y-6, xong.
Tương tự với color -- dùng quá nhiều màu, cái gì cũng highlight thì cuối cùng chẳng cái nào nổi bật cả. Primary color chỉ cho main actions, còn lại neutral.
Một lỗi nữa mà ai cũng mắc: không có loading state. User click button, không thấy gì xảy ra, click tiếp, click tiếp -- tạo 5 request cùng lúc. Luôn disable button + show spinner khi đang xử lý async.
Checklist
Đang ổn nếu:
- Tất cả buttons cùng size và style
- Spacing đều đặn giữa các elements
- Màu dùng có mục đích, không tô lung tung
- Loading states hiển thị đúng chỗ
- Error messages rõ ràng
- Dark mode không bể layout
Cần sửa nếu:
- Mỗi trang button trông khác nhau
- Spacing random, không theo quy tắc
- Quá nhiều thứ "nổi bật", user không biết nhìn đâu
- Không phân biệt được cái gì click được
- Form submit lỗi mà user không biết lỗi gì
Tham Khảo Nhanh
| Element | Pattern |
|---|---|
| Primary button | <Button> |
| Secondary button | <Button variant="secondary"> |
| Destructive action | <Button variant="destructive"> |
| Form spacing | space-y-4 |
| Section spacing | space-y-6 |
| Card padding | p-6 |
| Page margins | p-8 |