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