Mở đầu — vì sao bài này quan trọng
Hãy tưởng tượng bạn đang làm việc trong một team thiết kế 5 người tại một startup fintech ở TP.HCM. Team vừa quyết định đổi màu thương hiệu chính từ xanh dương sang xanh lá. Nghe thì đơn giản, nhưng thực tế là một cơn ác mộng: màu #3B82F6 xuất hiện ở 340 chỗ trong file Figma, rải rác trong 12 màn hình, 60 component, và không ai chắc chắn liệu cái màu xanh ở nút "Đăng nhập" có phải cùng một mã với màu xanh ở icon thông báo hay không. Ba designer ngồi search-and-replace thủ công suốt hai ngày, vẫn sót vài chỗ, và dev khi nhận handoff còn hỏi lại "sao chỗ này là #3C82F5, lệch một chút so với chỗ kia?".
Đây chính xác là vấn đề mà Design Tokens sinh ra để giải quyết. Nếu ở các bài trước bạn đã học cách xây dựng typography scale, color system, spacing system — thì bài này là mảnh ghép biến tất cả những hệ thống rời rạc đó thành một nguồn sự thật duy nhất (single source of truth) có tên, có cấu trúc, và có thể thay đổi ở một chỗ mà lan tỏa ra toàn bộ sản phẩm.
Design Tokens là foundation layer — tầng nền móng — của mọi design system nghiêm túc. Không có nó, bạn chỉ có một bộ sưu tập các quyết định thiết kế nằm rải rác. Có nó, bạn có một hệ thống sống, mở rộng được, và nói chung một ngôn ngữ với đội kỹ sư. Bài này sẽ dạy bạn tư duy về tokens trước khi bạn học các công cụ cụ thể (Figma Variables ở Bài 15, Tokens Studio ở Bài 35).
Khái niệm cốt lõi
Token là gì?
Một design token là một quyết định thiết kế nguyên tử (atomic design decision) được đặt cho một cái tên có ý nghĩa. "Nguyên tử" ở đây nghĩa là không thể chia nhỏ hơn nữa: một màu, một khoảng cách, một cỡ chữ, một bán kính bo góc.
So sánh hai cách viết:
- Hardcode:
color: #3B82F6 - Token:
color: color.brand.primary(vàcolor.brand.primaryđược định nghĩa =#3B82F6)
#22C55E, cái tên vẫn đúng — vì ý định "màu thương hiệu chính" không đổi. Đó là lý do bạn chỉ sửa một dòng thay vì 340 chỗ.Một token tối thiểu gồm ba phần:
- Name (tên):
color.brand.primary— có cấu trúc phân cấp, đọc là hiểu. - Value (giá trị):
#3B82F6— dữ liệu thô mà máy hiểu. - Type (kiểu):
color— để công cụ biết cách xử lý (màu, kích thước, thời lượng...).
Ba tầng tokens (Token Tiers)
Đây là phần quan trọng nhất của bài, và cũng là phần junior designer hay bỏ qua. Một design token trưởng thành không phẳng — nó có ba tầng, mỗi tầng tham chiếu (reference) đến tầng dưới:
Tầng 1 — Primitive / Global tokens (token gốc): Đây là bảng màu thô, thang đo thô. Chúng chỉ mô tả giá trị, chưa mang ý nghĩa sử dụng.
blue.500 = #3B82F6
blue.600 = #2563EB
gray.900 = #111827
space.4 = 16px
radius.md = 8px
Tên ở tầng này mô tả cái nó là, không phải cái nó dùng để làm gì. blue.500 chỉ là "màu xanh độ đậm 500", không cần biết nó dùng ở đâu.
Tầng 2 — Semantic / Alias tokens (token ngữ nghĩa): Tầng này gán ý nghĩa cho token gốc. Nó trả lời câu hỏi "màu này dùng để làm gì?".
color.action.primary = blue.500
color.action.primary.hover = blue.600
color.text.default = gray.900
color.background.danger = red.100
Chú ý: color.action.primary không chứa giá trị #3B82F6 — nó trỏ tới blue.500. Đây là tầng mà designer và dev làm việc thường xuyên nhất. Khi bạn dùng nút màu chính, bạn dùng color.action.primary, không bao giờ đụng trực tiếp vào blue.500.
Tầng 3 — Component tokens (token cấp component): Tầng cụ thể nhất, gắn với một component riêng. Trả lời "nút button dùng màu nền gì?".
button.primary.background = color.action.primary
button.primary.background.hover = color.action.primary.hover
button.primary.text = color.text.on-primary
input.border.focus = color.action.primary
Tầng này cho phép bạn tinh chỉnh một component mà không ảnh hưởng phần còn lại. Ví dụ nếu sếp muốn nút chính đậm hơn một chút so với các link chính, bạn chỉ đổi button.primary.background mà semantic layer vẫn nguyên.
Vì sao cần tới ba tầng, không phải một?
Đây là câu hỏi tôi luôn hỏi học viên. Câu trả lời nằm ở khả năng thay đổi có kiểm soát:
- Đổi cả bảng màu thương hiệu → sửa tầng 1 (primitive).
- Đổi ý nghĩa "màu nguy hiểm dùng đỏ hay cam" → sửa tầng 2 (semantic).
- Tinh chỉnh riêng một component → sửa tầng 3 (component).
button.primary.background → color.action.primary → blue.500 → #3B82F6 nghĩa là một thay đổi ở gốc lan tỏa lên trên một cách tự động và nhất quán. Đây là sức mạnh mà hardcode không bao giờ có.Tình huống thực tế
Ví dụ 1 — Momo và bài toán dark mode
Giả sử đội thiết kế của một ví điện tử lớn tại Việt Nam (tạm gọi bối cảnh giống Momo) cần ra mắt dark mode cho 200+ màn hình. Nếu họ hardcode màu, việc làm dark mode nghĩa là vẽ lại từng màn — công sức khổng lồ, dễ lệch.
Nhưng vì họ dùng semantic tokens, mọi thứ đổi khác. Màn hình không dùng gray.50 (màu nền trắng cụ thể) mà dùng color.background.surface. Ở chế độ sáng, color.background.surface = gray.50; ở chế độ tối, color.background.surface = gray.900. Toàn bộ 200 màn hình tự động chuyển màu đúng, vì chúng chưa bao giờ biết giá trị thật — chúng chỉ biết ý định "bề mặt nền".
Bài học rút ra: Semantic layer chính là thứ khiến theming (sáng/tối, thương hiệu con...) trở nên khả thi. Nếu bạn hardcode, dark mode là dự án hàng tháng; nếu bạn dùng token đúng cách, đó là việc định nghĩa lại một tập giá trị.
Ví dụ 2 — Startup B2B đổi thương hiệu sau vòng gọi vốn
Một startup SaaS ở Singapore, sau vòng Series A, thuê agency làm lại thương hiệu. Màu chính đổi từ tím sang xanh navy, màu nhấn (accent) đổi từ cam sang vàng. Team dev lo lắng vì sản phẩm đã có 80 màn hình.
May mắn là ngay từ đầu, designer đã thiết lập primitive tokens (purple., orange.) và semantic tokens (color.action.primary, color.accent). Việc rebrand quy về: (1) thêm bộ primitive mới navy., yellow.; (2) trỏ lại color.action.primary → navy.600 và color.accent → yellow.400. Tổng thời gian cập nhật token: khoảng 3 giờ. Sau đó toàn sản phẩm cập nhật đồng bộ, chỉ cần QA thị giác.
Bài học rút ra: Con số ở đây rất thật — cùng một tác vụ có thể là 2 tuần hoặc 3 giờ, khác biệt hoàn toàn nằm ở việc bạn có tách tầng token hay không. Chi phí thiết lập token ban đầu là khoản đầu tư trả lãi kép về sau.
Ví dụ 3 — Junior tự "phát minh" màu và cái giá phải trả
Trong một team tôi từng cố vấn, một bạn junior khi thiết kế màn hình mới cần một màu xanh "nhạt hơn chút". Thay vì dùng blue.400 có sẵn, bạn ấy tự chọn #5B9BF8 từ color picker. Nhìn thì ổn. Nhưng ba tháng sau, khi audit design system, team phát hiện có 17 sắc xanh dương khác nhau trong sản phẩm — hầu hết chỉ lệch nhau vài phần trăm, mắt thường khó phân biệt nhưng đủ để làm giao diện thiếu chuyên nghiệp và khiến dev không thể ánh xạ về token nào.
Bài học rút ra: Token chỉ có giá trị khi cả team kỷ luật dùng nó. Một token không được tôn trọng thì cũng vô dụng như không có. Quy tắc vàng: nếu bạn cần một giá trị chưa có trong token, đừng tự chế — hãy đề xuất thêm vào hệ thống một cách có chủ đích.
Hướng dẫn từng bước
Đây là quy trình xây foundation layer từ con số 0, áp dụng được ngay cho dự án của bạn:
Bước 1 — Kiểm kê (audit) hiện trạng. Liệt kê tất cả màu, cỡ chữ, khoảng cách, bán kính đang dùng trong thiết kế hiện tại. Bạn sẽ bất ngờ vì số lượng biến thể "vô tình" xuất hiện. Đây là dữ liệu đầu vào để gom nhóm.
Bước 2 — Xây tầng Primitive.
Gom các giá trị thô thành các thang có hệ thống: blue.50 đến blue.900, gray.50 đến gray.900, space.1 đến space.12 (theo hệ số 8 nếu bạn đã học Bài 9), radius.sm/md/lg/full. Đặt tên trung tính, mô tả cái nó là.
Bước 3 — Định nghĩa tầng Semantic.
Với mỗi vai trò trong UI, tạo một alias trỏ về primitive: color.text.default, color.text.muted, color.background.default, color.background.surface, color.action.primary, color.action.primary.hover, color.feedback.danger, color.feedback.success. Đây là bảng từ vựng mà cả designer lẫn dev sẽ nói hằng ngày.
Bước 4 — Chỉ tạo Component token khi thật cần.
Đừng tạo component token cho mọi thứ. Chỉ tạo khi một component cần lệch khỏi semantic layer, hoặc khi bạn muốn cho phép tùy biến sâu về sau. Ví dụ button.primary.background, card.padding. Nếu component dùng đúng semantic là đủ, thì bỏ qua tầng này.
Bước 5 — Đặt quy ước đặt tên nhất quán.
Chọn một cấu trúc và giữ vững: category.property.variant.state. Ví dụ color.action.primary.hover. Dùng dấu chấm phân cấp, viết thường, không dấu tiếng Việt. (Naming convention là cả một chủ đề riêng — bạn sẽ đào sâu ở Bài 34.)
Bước 6 — Tài liệu hóa ý định.
Với mỗi semantic token, ghi một dòng mô tả khi nào dùng. Ví dụ: color.action.primary — dùng cho hành động chính duy nhất trên mỗi màn hình. Điều này ngăn việc lạm dụng token sai chỗ.
Lỗi thường gặp & mẹo
Lỗi 1 — Bỏ qua semantic layer, dùng thẳng primitive trong thiết kế.
Nếu bạn gán blue.500 trực tiếp cho nút, bạn mất khả năng theming. Luôn để component tham chiếu semantic, semantic tham chiếu primitive. Mẹo: quy tắc "không bao giờ dùng primitive token trực tiếp trên bề mặt UI".
Lỗi 2 — Đặt tên theo giá trị thay vì theo ý nghĩa.
Đặt semantic token là color.blue là sai — vì mai nó có thể không còn xanh. Hãy đặt theo vai trò: color.action.primary. Tên mô tả dùng để làm gì, không mô tả trông thế nào.
Lỗi 3 — Tạo quá nhiều token (over-tokenization). Không phải mọi pixel đều cần một token. 47 màu xám semantic khác nhau là dấu hiệu bạn đang làm quá. Bắt đầu tối giản, thêm token khi thực sự có nhu cầu lặp lại. Token thừa còn khó bảo trì hơn token thiếu.
Lỗi 4 — Token "mồ côi" không ai trỏ tới. Sau vài tháng, hệ thống tích tụ các token không còn được dùng. Định kỳ audit và dọn dẹp, giống như dọn dead code.
Mẹo vàng: Hãy nghĩ về token như API của design system — bạn thiết kế cái tên cho người khác dùng, không phải cho chính bạn hôm nay. Một cái tên tốt là cái tên mà dev đọc xong hiểu ngay phải dùng ở đâu.
Bài tập thực hành
- Audit nhanh: Mở một thiết kế bạn đang làm (hoặc một app bạn thích). Liệt kê tất cả các sắc màu xanh dương bạn tìm thấy. Đếm xem có bao nhiêu biến thể — kết quả thường gây sốc.
- Xây bộ token 3 tầng cho màu: Tạo một bảng gồm 3 cột (Primitive → Semantic → Component) cho hệ màu của một nút button với 3 trạng thái: default, hover, disabled. Viết đầy đủ chuỗi tham chiếu, ví dụ
button.primary.background → color.action.primary → blue.500 → #2563EB.
- Thử thách theming: Với bộ semantic token bạn vừa tạo, hãy định nghĩa lại giá trị primitive cho một dark mode giả định. Ghi ra: những token nào cần đổi, những màn hình nào sẽ tự động cập nhật mà bạn không phải đụng vào.
- Đặt tên phản biện: Cho 5 token sau, chỉ ra cái nào đặt tên sai (theo giá trị thay vì ý nghĩa) và sửa lại:
color.green,color.feedback.success,color.dark-blue-button,color.text.muted,spacing.big.
Tóm tắt
- Design token là một quyết định thiết kế nguyên tử có tên mang ý nghĩa; nó tách ý định khỏi giá trị, nhờ đó thay đổi ở một chỗ lan tỏa toàn hệ thống.
- Foundation layer trưởng thành có ba tầng: Primitive (giá trị thô, mô tả cái-nó-là) → Semantic (gán ý nghĩa, mô tả cái-nó-dùng-để-làm-gì) → Component (cụ thể theo từng component). Mỗi tầng tham chiếu tầng dưới.
- Ba tầng cho phép thay đổi có kiểm soát: đổi thương hiệu ở primitive, đổi ý nghĩa ở semantic, tinh chỉnh riêng ở component.
- Semantic layer là chìa khóa cho theming (dark mode, đa thương hiệu) — như ví dụ ví điện tử và startup rebrand đã cho thấy: cùng một việc có thể tốn 2 tuần hoặc 3 giờ.
- Token chỉ giá trị khi team kỷ luật dùng nó; tự chế màu ngoài hệ thống là con đường dẫn tới 17 sắc xanh khó bảo trì.
- Bắt đầu tối giản, đặt tên theo ý nghĩa, tài liệu hóa "khi nào dùng", và coi token như API của design system.