Handoff Figma → CSS: chuyển HEX / RGB / HSL không lệch brand【2026】
Lệch brand khi handoff Figma → CSS gần như luôn đến từ quy trình, không phải từ ‘công thức sai’. Khóa một nguồn sự thật (thường #RRGGBB), sinh RGB/HSL từ đó, và đừng nhân opacity hai lần. Color converter giúp đối chiếu HEX ↔ RGB (và Unity Color 0–1) ngay trên trình duyệt — không cần đăng ký, phù hợp freelance và team outsourcing Việt Nam.
Designer gửi chip #6366F1. Dev dán vào Tailwind. QA mở staging: nút “tím hơn một chút”, hoặc nhạt hơn vì layer Figma đang 80% opacity trong khi CSS lại opacity: 0.8 lần nữa. Ticket “fix màu brand” — quen thuộc với team product và studio outsource.
Bài này tập trung handoff Figma → CSS: khi nào dùng HEX / RGB / HSL, cách chuyển không lệch, lỗi hay gặp, và checklist token. Không phải bài định nghĩa màu học thuật.
Bài viết này giúp bạn
- Hiểu vì sao cùng HEX vẫn lệch giữa mock và production
- Chọn HEX / RGB / HSL đúng ngữ cảnh (token, CSS, chỉnh theme)
- Có ví dụ số và bảng so sánh — không chỉ lý thuyết
- Có checklist trước khi merge PR màu
- Biết dùng color converter để đối chiếu nhanh, local
Vì sao handoff Figma → CSS dễ lệch brand?
Ba nguồn lệch phổ biến nhất trong team Việt (freelance, agency, product):
- Nhiều nguồn sự thật — Figma Styles ghi HEX, ticket Slack gửi
rgb(), PR dùnghsl()làm tròn khác nhau. - Opacity kép — fill 100% + layer 70% trên Figma; CSS ghi cả alpha trong màu và
opacitytrên wrapper. - Làm tròn chuỗi — HEX → HSL (tool A) → RGB (tool B) → HEX lại: lệch 1–2 đơn vị kênh, mắt vẫn thấy trên nền tương phản cao.
Màu là số. Bug là đơn vị và quy ước team, không phải “CSS không hiểu Figma”.
HEX, RGB, HSL — cùng màu, ba việc khác nhau
| Dạng | Ví dụ | Phù hợp handoff |
|---|---|---|
| HEX | #6366F1 | Token brand, Tailwind, chia sẻ Slack/Notion. Nguồn sự thật mặc định. |
| RGB / RGBA | rgb(99 102 241) | Inspector, Canvas, blend; alpha rõ ràng bằng số 0–1 hoặc %. |
| HSL / HSLA | hsl(239 84% 67%) | Chỉnh sáng/tối theme, hover/pressed từ cùng hue — không thay brand gốc bằng mắt. |
HEX — nguồn sự thật cho brand
#RRGGBB: mỗi cặp hex = một kênh 0–255.
Ví dụ indigo Tailwind-ish:
| Kênh | HEX | Thập phân |
|---|---|---|
| R | 63 | 99 |
| G | 66 | 102 |
| B | F1 | 241 |
→ CSS: #6366F1 hoặc rgb(99, 102, 241).
Quy ước team: luôn uppercase hoặc luôn lowercase trong repo — đừng mix #6366f1 và #6366F1 trong diff nếu linter chưa normalize (review ồn, git blame rối).
RGB — khi cần alpha hoặc debug kênh
/* Cùng màu fill */
--brand-primary: #6366f1;
--brand-primary-rgb: 99 102 241; /* tiện cho rgb(var(--brand-primary-rgb) / 0.4) */
Pattern R G B space-separated giúp đổi alpha mà không copy lại ba số:
.btn-ghost {
background: rgb(var(--brand-primary-rgb) / 40%);
}
HSL — chỉnh biến thể, không thay thế token gốc
HSL tách hue / saturation / lightness. Tiện khi:
- Dark mode: giữ hue, hạ lightness có kiểm soát
- Hover: tăng lightness 4–8% thay vì chọn màu mới bằng mắt trên Figma
Cảnh báo: HSL từ tool khác nhau làm tròn khác nhau (67% vs 66.9%). Nếu commit cả HEX lẫn HSL “tương đương”, sau vài vòng chuyển đổi sẽ lệch. Cách an toàn:
- Token gốc = HEX
- HSL chỉ sinh khi cần biến thể, ghi rõ công thức trong comment hoặc design token JSON
- Không round-trip HSL → HEX trong CI trừ khi có test snapshot
Quy trình handoff 4 bước (Figma → CSS)
1. Lấy fill thuần, rồi mới tính opacity
Trên Figma: chọn layer → copy HEX của fill. Nếu layer opacity ≠ 100%, ghi riêng:
- Fill:
#6366F1 - Layer opacity:
70%
CSS chỉ được áp một trong hai:
/* Cách A: alpha trong màu */
.chip { background: #6366f1b3; } /* ~70% nếu AA = B3 */
/* Cách B: opacity phần tử — đừng kết hợp với A */
.chip { background: #6366f1; opacity: 0.7; } /* ảnh hưởng cả chữ bên trong */
opacity trên phần tử làm mờ cả children — thường sai cho nút có chữ. Ưu tiên alpha trên nền.
2. Đối chiếu HEX ↔ RGB một lần
Mở chuyển đổi màu (color converter), dán #6366F1 (hoặc chọn bằng color picker):
- RGB hiển thị:
99, 102, 241 - Nếu team Unity/C#: thêm
new Color(0.388f, 0.400f, 0.945f)(chia 255) — tránh dán nhầmnew Color(99, 102, 241)(sai đơn vị 0–1)
Công cụ chạy local trên trình duyệt, không cần tài khoản — phù hợp khi palette brand chưa public.
3. Sinh HSL có chủ đích (nếu cần)
Ví dụ gần đúng cho #6366F1:
/* Nguồn: HEX */
--color-primary: #6366f1;
/* Biến thể — HSL chỉnh L, không đổi H/S lung tung */
--color-primary-hover: hsl(239 84% 72%);
--color-primary-pressed: hsl(239 84% 58%);
Ghi trong PR: “hover = +5% L từ primary”. Reviewer kiểm được; mắt không phải nguồn sự thật.
4. Kiểm tra trên nền product + contrast
Mock Figma thường nền sáng. Product có sidebar tối / card xám. Cùng #6366F1 trên nền #0F172A nhìn “cháy” hơn trên #FFFFFF. Sau khi gắn token, chạy kiểm tra contrast cho chữ trên nền brand (AA tối thiểu cho text thường).
Ví dụ số: một màu — ba dạng — một token
Giả sử brand primary = indigo #6366F1.
| Ngữ cảnh | Giá trị | Ghi chú |
|---|---|---|
| Token CSS | --color-primary: #6366f1 | Nguồn sự thật |
| RGB kênh | 99, 102, 241 | Đối chiếu converter / Canvas |
| CSS modern | rgb(99 102 241 / 1) | Alpha tách |
| HSL (gần) | hsl(239 84% 67%) | Chỉ để biến thể; đừng round-trip |
| Unity Color | new Color(0.388f, 0.400f, 0.945f) | 0–1 float, không phải 0–255 |
| Unity Color32 | new Color32(99, 102, 241, 255) | Giữ byte 0–255 |
Sai điển hình trong sprint game + web (outsourcing phổ biến ở VN):
// SAI — Unity Color kỳ vọng 0–1
var c = new Color(99f, 102f, 241f);
// ĐÚNG
var c = new Color(99f / 255f, 102f / 255f, 241f / 255f);
// hoặc copy từ color converter
Web team và Unity team phải dùng cùng HEX gốc; mỗi bên convert một lần theo đơn vị của platform.
Checklist trước khi merge PR màu
| Mục | Hỏi nhanh |
|---|---|
| Một nguồn | Token primary chỉ xuất hiện một lần (theme file)? Không hard-code lại trong 4 component? |
| Opacity | Alpha chỉ ở một chỗ (màu hoặc CanvasGroup / opacity wrapper)? |
| Đối chiếu | HEX trong Figma Styles = HEX trong CSS (diff exact)? |
| HSL | Nếu có HSL: có comment công thức? Có test visual/regression không? |
| Nền thật | Đã xem trên light + dark (hoặc nền product) chứ không chỉ artboard trắng? |
| Contrast | Chữ trên nền brand đạt AA? Primary nhạt trên nền sáng thường fail. |
| Tên | primary / brand / accent thống nhất với design system — không purple2 vs indigo. |
Case study ngắn
Case 1: Freelance — client Figma, bạn Next.js + Tailwind
Tình huống: Client gửi file Figma, Styles tên Brand/Primary. Bạn map vào tailwind.config / @theme.
Làm: Copy HEX từ Styles (không eyedrop artboard đã overlay). Đặt --color-primary. Biến thể hover bằng HSL +ΔL hoặc shade cố định trong palette. Đối chiếu RGB bằng color converter trước khi gửi preview.
Tránh: Eyedrop screenshot Slack (JPEG lệch); copy màu từ layer đang 50% opacity rồi trách CSS “sai”.
Case 2: Team outsourcing — web UI + Unity client cùng brand
Tình huống: App web marketing và game client phải cùng indigo.
Làm: File brand-colors.json chỉ chứa HEX. Web import CSS variable; Unity sinh Color/Color32 từ cùng HEX (tool hoặc helper một chỗ). Không chat “khoảng 99,100,240”.
Tránh: Designer gửi HSL từ plugin A; Unity artist nhập RGB từ plugin B — hai bên “gần đúng”, build store lệch.
Case 3: Dark mode làm lệch primary
Tình huống: Light dùng #6366F1; dark tự giảm opacity toàn cục → nút trông bẩn.
Làm: Token riêng --color-primary-dark (HEX chọn có chủ đích) hoặc HSL hạ L có kiểm soát; không opacity: 0.85 lên cả button.
Lỗi thường gặp
- Round-trip HSL — HEX → HSL → HEX khác 1 digit → “brand drift” sau 3 PR.
- Opacity kép — Figma layer × CSS alpha.
- Unity 0–255 vào
Color— glow / clip. Dùng converter hoặcColor32. - Eyedrop ảnh nén — luôn lấy từ Styles / Inspect.
- Hard-code rải rác —
#6366f1trong 12 file; đổi brand = 12 diff sót. Gom token.
Cách dùng color converter (Kawa)
- Mở Color converter — HEX / RGB / Unity
- Dán
#6366F1hoặc chọn color picker - Đọc RGB
99, 102, 241; copy Unitynew Color(...)nếu cần - Lịch sử ~5 màu gần nhất — tiện khi map cả palette primary/success/danger
Giới hạn trung thực: tool tập trung HEX ↔ RGB ↔ Unity Color, xem trước và lịch sử local. Phần HSL trong bài là quy ước CSS/handoff — hãy sinh HSL từ token HEX bằng một pipeline cố định (PostCSS, Style Dictionary, hoặc công thức team), đừng tin vào ba tool online khác nhau trong cùng một PR.
Liên quan: rgba ↔ hex, color contrast WCAG, Tailwind color.
Tóm tắt
- Khóa HEX làm nguồn brand; RGB/HSL là dạng xuất.
- Một lần opacity — không nhân Figma × CSS.
- Đối chiếu số, không đối chiếu mắt trên artboard trắng.
- Web + Unity: cùng HEX, convert theo đơn vị platform.
- Dùng color converter để xác nhận HEX/RGB (và Unity) không cần đăng ký, chạy trên trình duyệt.
Handoff sạch = ít ticket “màu hơi lệch” — và brand nhìn thống nhất từ Figma tới production.