Bỏ qua, sang nội dung chính
← Về danh sách

· Working ·

Design token là gì? Ba lớp raw, alias, semantic

Design token là một cặp tên và giá trị cho một quyết định thiết kế, dùng chung giữa Figma và code. Bài này giải thích ba lớp raw, alias, semantic, khác biệt với biến CSS, cách làm trong Figma Variables và Tailwind v4, kèm token thật của site này.

Design token là một cặp tên và giá trị lưu lại một quyết định thiết kế, ví dụ --muted-foreground bằng #595959. Component gọi token bằng tên, không gõ giá trị. Khi tổ chức token thành ba lớp raw, alias, semantic, bạn đổi màu thương hiệu hay thêm dark mode ở một chỗ mà không phải sửa từng component.

Tôi là Hai Le, UI/UX designer biết code. Site bạn đang đọc có hơn 160 biến màu chia đúng ba lớp trong file app/globals.css. Mọi ví dụ dưới đây chép từ file đó.

Design token là gì, nói đơn giản?

Đặc tả của W3C Design Tokens Community Group định nghĩa token là "thông tin gắn với một cái tên con người đọc được, tối thiểu là một cặp tên và giá trị". Ví dụ trong chính đặc tả: color-text-primary: #000000.

Định nghĩa đó đúng nhưng thiếu phần quan trọng. Cái tên mới là thứ có giá trị. #595959 chỉ là một mã màu. muted-foreground cho biết đây là màu chữ phụ, đứng sau chữ chính, và mọi chỗ cần chữ phụ đều phải dùng nó. Khi tôi đổi mã màu, cái tên vẫn giữ nguyên lời hứa đó.

Nói "design token" với hầu hết mọi người, thứ hiện ra trong đầu là một bảng mã hex. Bảng đó chỉ là phần dễ thấy nhất. Phần quyết định hệ thống có sống qua lần đổi thương hiệu đầu tiên hay không là cách bạn xếp các tên thành lớp.

Design token khác biến CSS ở đâu?

Biến CSS là cơ chế lưu trữ. Token là quy ước: giá trị này thuộc lớp nào, ai được gọi tới nó. Hai dự án cùng dùng biến CSS có thể một bên có hệ token thật, một bên chỉ có các hằng số đặt tên đẹp.

Biến CSSDesign tokenFigma Variable
Là gìTính năng của CSS: --ten: giá-trịQuy ước đặt tên và phân lớp cho quyết định thiết kếTính năng của Figma để lưu giá trị dùng lại
Sống ở đâuFile CSS, trình duyệt đọcBất kỳ đâu: CSS, JSON, FigmaFile Figma
Trỏ tới nhauvar(--khac)Tuỳ định dạng, ví dụ {brand.500} trong JSONAliasing, trỏ tới variable cùng kiểu
Đổi theo chế độGhi đè trong selector, ví dụ .darkTuỳ công cụMode trong collection

Nói cách khác, biến CSS là một trong các chỗ token có thể sống. Ở site này, token sống trong biến CSS và được mô tả lại bằng chữ trong DESIGN.md.

Ba lớp token raw, alias, semantic là gì?

Lớp raw chỉ có giá trị. --gray-600 là #717171. Nó không biết mình sẽ thành chữ hay thành nền.

Lớp alias gắn vai trò cho cả một thang. --neutral-* là thang trung tính, --primary-* là thang thương hiệu, --error-* là thang báo lỗi. Đây vẫn là thang 11 bậc, nhưng đã có ý định.

Lớp semantic là thứ component chạm vào: --background, --foreground, --muted-foreground, --border, --primary. Tên của mỗi token nói nó dùng ở đâu. Màu cụ thể do lớp bên dưới quyết định.

Đây là ba dòng thật trong app/globals.css, nối một chuỗi từ giá trị tới component:

/* LỚP 0 — raw: chỉ có giá trị */
--gray-700: #595959;

/* LỚP 1 — alias: thang có vai trò */
--neutral-700: var(--gray-700);

/* LỚP 2 — semantic: component chạm vào đây */
--muted-foreground: var(--neutral-700);

Và bảng vai trò của ba lớp trong site này:

LớpVí dụAi được gọiĐổi khi nào
Raw--brand-500: #ff009d, --gray-700: #595959Chỉ lớp aliasĐổi thương hiệu, chỉnh tương phản
Alias--primary-500, --neutral-700, --error-700Lớp semantic, đôi khi componentHiếm khi
Semantic--primary, --muted-foreground, --destructiveComponentKhi vai trò đổi, ví dụ nút lỗi đậm hơn

Ở lớp semantic, tôi chỉ ghi var() trỏ về lớp alias, không ghi mã hex. Quy tắc đó được viết ngay đầu file CSS để người và AI sửa file đều thấy.

Ba lớp token. Chỉ lớp semantic nên xuất hiện trong component.

Vì sao không nên bỏ lớp alias ở giữa?

Bỏ lớp giữa, tức là để semantic trỏ thẳng về raw, nhìn có vẻ tiết kiệm. Vấn đề lộ ra vào lần đổi thương hiệu đầu tiên. Người ta phát hiện nửa codebase đang gõ thẳng giá trị raw ở những chỗ lẽ ra phải qua semantic, và không còn cách lần ngược lại ngoài tìm và thay bằng tay.

Lớp alias giải quyết chuyện đó bằng một lời hứa đơn giản: bậc số trong thang có nghĩa cố định. Ở site này, tôi đánh số thang trung tính theo quy ước của Radix. neutral-50 luôn là nền, neutral-950 luôn là chữ đậm nhất, ở cả chế độ sáng lẫn tối. Số bậc đo khoảng cách so với nền. Ở chế độ tối, neutral-950 là màu sáng nhất.

Nhờ vậy, dark mode chỉ cần ghi đè lớp raw:

.dark {
  --gray-50: #111111;   /* nền tối */
  --gray-700: #a8a8a8;  /* chữ phụ trên nền tối */
  --gray-950: #ededed;  /* chữ chính trên nền tối */
}

--muted-foreground vẫn trỏ về --neutral-700, vẫn trỏ về --gray-700. Không component nào phải biết dark mode tồn tại. Cách chọn bậc cho thang kiểu này nằm ở bài thang màu Radix, còn các câu hỏi khó hơn về chế độ tối nằm ở bài câu hỏi thường gặp về dark mode.

Đừng để component gõ thẳng lớp raw (bg-gray-600). Luôn đi qua semantic (bg-muted), kể cả khi hai lớp đang trỏ tới cùng một giá trị. Ở site này, lớp raw cố ý không được nối sang Tailwind, nên bg-gray-600 không tồn tại để mà gõ nhầm.

Làm design token thế nào trong Figma (Variables) và trong code?

Trong Figma

Figma lưu token dưới dạng variable. Theo tài liệu của Figma, có sáu kiểu variable: color, number, string, boolean, và gần đây thêm timing, easing cho chuyển động. Variable được gom vào collection. Mỗi collection có thể có nhiều mode, ví dụ Light và Dark, mỗi mode giữ một giá trị cho mỗi variable. Số mode tối đa mỗi collection phụ thuộc gói Figma bạn dùng.

Cách dựng ba lớp trong Figma:

  1. Tạo collection Raw chứa các thang màu thô. Ẩn collection này khỏi người dùng thư viện nếu có thể, để không ai tô màu thẳng bằng nó.
  2. Tạo collection Semantic với hai mode Light và Dark. Mỗi variable ở đây trỏ tới một variable trong Raw bằng aliasing. Figma chỉ cho alias tới variable cùng kiểu.
  3. Đặt tên semantic giống hệt tên trong code: muted-foreground, border, primary.
  4. Trong phần Code syntax của từng variable, ghi tên dùng trong code cho Web, Android hoặc iOS. Với Web, ghi kèm var(), ví dụ var(--muted-foreground), để Dev Mode hiện tên biến thay vì mã hex.

Bước 4 hay bị bỏ qua nhất, và nó là thứ giúp dev hoặc AI đọc file Figma ra đúng tên token. Nếu bạn dùng Claude Code, bài Figma MCP và Claude Code chỉ cách kéo variable từ Figma ra thành token CSS mà không sinh trùng.

Trong code, với Tailwind v4

Tailwind v4 cấu hình bằng CSS. Bạn khai báo theme variable trong khối @theme, và Tailwind sinh class tương ứng. Một biến trong namespace --color-* sinh ra bg-*, text-*, border-* và các class màu khác.

Site này dùng @theme inline, và chỉ nối lớp alias và semantic sang Tailwind:

@theme inline {
  --color-background: var(--background);
  --color-muted-foreground: var(--muted-foreground);
  --color-primary: var(--primary);
  --color-neutral-700: var(--neutral-700);
}

Tài liệu Tailwind giải thích rằng với tuỳ chọn inline, class sẽ dùng giá trị của biến thay vì tham chiếu tới theme variable. Điều này quan trọng khi biến của bạn lại trỏ tới biến khác, như chuỗi ba lớp ở trên. Trong component, tôi chỉ viết text-muted-foreground hoặc hover:bg-muted.

Trong file JSON

Nếu cần xuất token ra iOS, Android hay một công cụ build, định dạng chuẩn là Design Tokens Format Module của W3C (bản 2025.10). Site này không dùng file JSON, nên đoạn dưới là cách token thương hiệu của tôi sẽ trông thế nào theo đặc tả:

{
  "brand": {
    "$type": "color",
    "500": {
      "$value": {
        "colorSpace": "srgb",
        "components": [1, 0, 0.616],
        "hex": "#ff009d"
      }
    }
  },
  "primary": {
    "$type": "color",
    "$value": "{brand.500}",
    "$description": "Nền nút chính. Không dùng cho chữ nhỏ."
  }
}

$value của màu là một object có không gian màu và các thành phần số, hex là trường tuỳ chọn. Tham chiếu tới token khác dùng ngoặc nhọn, ví dụ {brand.500}. Style Dictionary đọc được định dạng này từ bản 4.

Muốn AI hiểu thêm lý do đằng sau từng token, bạn cần một lớp chữ đi kèm. Bài DESIGN.md là gì giải thích cách tôi viết lớp đó.

Những sai lầm thường gặp với design token là gì?

Đặt tên theo giá trị. --pink, --gray-dark sẽ nói dối ngay khi bạn đổi màu. Tên semantic nên mô tả vai trò: --primary, --muted-foreground.

Cho component gọi lớp raw. Một #717171 gõ tay trong component là một chỗ dark mode sẽ không đổi theo.

Một token cho hai việc. Màu thương hiệu #ff009d trên nền trắng chỉ đạt tương phản 3.66:1. Đủ cho nền nút có chữ trắng, nhưng trượt chuẩn WCAG AA cho chữ nhỏ. Tôi tách thành hai token: --primary cho mảng nền, --primary-text (#be0074, 6.09:1) cho chữ magenta từ 20px trở xuống.

Thêm token cho mỗi lần thiết kế cần. Site này chỉ có ba giá trị bề mặt (#ffffff, #f8f8f8, #efefef) và bốn bậc chữ. Khi tôi thấy mình cần giá trị thứ tư, thường là bố cục đang sai.

Figma và code dùng tên khác nhau. Designer gọi Text/Secondary, code gọi muted-foreground. Mỗi lần bàn giao lại phải tra bảng. Đặt cùng tên ngay từ đầu, rồi ghi tên code vào Code syntax.

Ghi đè cả lớp semantic khi làm dark mode. Nếu thang đánh số theo khoảng cách tới nền, dark mode chỉ cần đổi lớp raw. Ghi đè từng token semantic là dấu hiệu thang chưa được thiết kế cho hai chế độ.

Bắt đầu với design token từ đâu?

Nếu bạn đang có file Figma với styles rời rạc, đừng chuyển hết một lúc. Bắt đầu với năm token semantic: nền, chữ chính, chữ phụ, viền, màu chính. Cho mỗi cái trỏ về một bậc trong thang raw. Khi cả năm chạy đúng ở chế độ sáng và tối, mở rộng dần sang trạng thái lỗi, thành công, cảnh báo. Nếu bạn mới học Figma, đọc trước bài học Figma từ số 0.

Tôi từng dựng hệ thống hơn 50 component cho Joyme, và phần khó nhất luôn là thống nhất tên token giữa Figma và code. Bạn có thể xem cách tôi làm ở case study Joyme. Nếu team bạn cần người dựng design system chạy được trên cả Figma lẫn code, xem thêm về tôi ở trang giới thiệu.

Nguồn

Câu hỏi thường gặp

Design token có phải chỉ là màu không?
Không chỉ màu. Đặc tả của W3C Design Tokens Community Group định nghĩa các loại như color, dimension, font family, font weight, duration, cubic Bézier, number, và các loại ghép như border, shadow, typography. Màu thường là nơi người ta bắt đầu vì nó dễ thấy nhất.
Design token và Figma Variables có giống nhau không?
Figma Variables là cách Figma lưu token trong file thiết kế: có collection, mode sáng/tối, và cho một variable trỏ tới variable khác (aliasing). Token là khái niệm rộng hơn. Cùng một token có thể sống trong Figma, trong CSS và trong file JSON.
Có định dạng file chuẩn cho design token không?
Có. Design Tokens Community Group của W3C công bố bản Design Tokens Format Module 2025.10 vào ngày 28/10/2025. File dùng đuôi .tokens hoặc .tokens.json, mỗi token có $value và $type, tham chiếu tới token khác bằng cú pháp ngoặc nhọn.
Dự án nhỏ có cần ba lớp token không?
Nên có ngay từ đầu, vì chi phí thấp: lớp alias chỉ là vài chục dòng trỏ tới lớp raw. Thứ có thể để sau là công cụ chuyển đổi như Style Dictionary, chỉ cần khi token phải xuất ra nhiều nền tảng.