Thêm màu brand vào Tailwind CSS | Custom color palette từ HEX (v3/v4)
Thêm màu brand bằng theme.extend (v3) hoặc @theme (v4) — không ghi đè toàn bộ theme.colors. Đưa HEX thành scale có tên (brand-500), giữ class chuỗi đầy đủ trong source, và chỉ tạo custom khi HEX lệch rõ khỏi palette mặc định. Công cụ màu Tailwind giúp map HEX → class gần nhất trước khi sửa config.
Designer gửi #0F766E và #CCFBF1. Bạn thêm bg-brand — local ổn. Deploy lên Vercel/Netlify: class biến mất, hoặc tệ hơn là cả text-gray-500 trong app cũng biến mất vì bạn đã thay palette mặc định. Custom color trong Tailwind không khó — khó ở chỗ hình dạng config, content scan, và cách viết class không khớp nhau.
Bài này dành cho frontend Việt Nam (freelance, team outsourcing Next/React/Astro, sinh viên làm đồ án) khi nhận brief từ client Nhật/Mỹ hoặc agency nội địa: đưa HEX brand vào Tailwind đúng cách, v3 và v4.
Ai cần đọc bài này
- Dev nhận file Figma chỉ có swatch HEX, không có token Tailwind
- Team monorepo (
apps/web+packages/ui) từng mất utility trên CI - Ai đang migrate Tailwind v3 → v4 và bối rối chỗ
@theme - Designer/dev cần thống nhất tên
brand-500thay vìbrandPrimaryDarkest
Custom color trong Tailwind là gì?
Custom color là màu bạn thêm vào theme để dùng như utility: bg-brand-500, text-primary, border-accent/40. Không phải hard-code style={{ color: '#0F766E' }} rải khắp component.
Hai hướng phổ biến:
- Map HEX → màu mặc định gần nhất (
teal-700,sky-500) — ít config, đồng bộ với docs Tailwind - Extend namespace riêng (
brand,primary) — đúng khi brand lệch palette hoặc cần semantic rename sau này
| Cách | Ưu | Khi nào chọn |
|---|---|---|
| Dùng blue-500 / zinc-700 sẵn | Không đụng config; docs và plugin quen thuộc | HEX gần scale mặc định; UI nội bộ, MVP |
| extend colors.brand | Đổi HEX một chỗ; class semantic ổn định | Brand riêng; design system; bàn giao client dài hạn |
| CSS variable + theme | Dark mode / white-label đổi runtime | SaaS đa tenant, theme switcher |
Muốn biết HEX có “gần” teal-700 không: dán vào công cụ tìm màu Tailwind (chạy trên trình duyệt, không cần đăng ký). Nếu Delta E nhỏ — cân nhắc dùng utility mặc định thay vì nhân đôi palette.
v3: extend — đừng clobber
Cấu hình đúng
// tailwind.config.ts (Tailwind v3)
import type { Config } from "tailwindcss";
export default {
content: ["./src/**/*.{html,js,ts,jsx,tsx,astro}"],
theme: {
extend: {
colors: {
brand: {
50: "#f0fdfa",
100: "#ccfbf1",
500: "#0f766e",
700: "#115e59",
DEFAULT: "#0f766e",
},
},
},
},
} satisfies Config;
Sau đó dùng: bg-brand, bg-brand-500, text-brand-700, ring-brand/30.
Sai lầm hay gặp
// ❌ Thay toàn bộ palette mặc định
theme: {
colors: {
brand: { 500: "#0f766e" },
},
}
Mọi bg-gray-100, text-red-600 biến mất khỏi CSS build. Local có thể “vẫn xanh” vì bạn chỉ nhìn vài màn brand — production mới lộ khi trang auth/error dùng gray/red mặc định.
Quy tắc: merge bằng extend trừ khi redesign cố ý bỏ default scale.
v4: thêm token bằng @theme
Tailwind v4 đẩy nhiều token sang CSS. Cùng tư duy “thêm, không xóa”:
/* app.css — Tailwind v4 */
@import "tailwindcss";
@theme {
--color-brand-50: #f0fdfa;
--color-brand-100: #ccfbf1;
--color-brand-500: #0f766e;
--color-brand-700: #115e59;
--color-brand: #0f766e;
}
Utility sinh ra tương tự: bg-brand-500, text-brand. Nếu project còn tailwind.config legacy, kiểm tra docs phiên bản bạn đang dùng — đừng copy lẫn khối v3 vào dự án v4 thuần CSS.
| Phiên bản | Nơi chính | Lưu ý |
|---|---|---|
| Tailwind v3 | tailwind.config → theme.extend.colors | content globs quyết định class nào vào CSS |
| Tailwind v4 | @theme trong CSS (+ config tùy dự án) | Tên --color-* map sang utility; tránh trùng token mặc định nếu không cố ý |
Từ dump HEX Figma → scale dùng được
Designer outsourcing thường gửi 3–5 HEX, không phải thang 50–950. Bạn vẫn cần tên khớp cách viết utility.
| Nhu cầu | Cách làm |
|---|---|
| Một accent | brand / brand-DEFAULT + brand-soft / brand-dark |
| UI đầy đủ | Map hoặc generate 50–900; 500 = màu chính |
| Giữ gray Tailwind | Giữ zinc/slate mặc định; chỉ thêm brand |
Quy ước đặt tên (handoff designer ↔ dev)
- Gọi bậc chính
brand-500(hoặcDEFAULT) — cùng ngôn ngữ với Figma styles - Tránh one-off:
brandPrimaryDarkestHoverkhông map được sang scale - Semantic (
primary,danger,success) khi màu có thể đổi theo brand mà class JSX giữ nguyên
Checklist 10 phút trước khi sửa config:
- HEX chính →
500/DEFAULT - Soft background →
50–100 - Text trên nền sáng →
700–900(đo contrast) - So HEX với palette Tailwind — tránh trùng gần
teal-700rồi tạobrandsong song không cần thiết
Opacity /50 và CSS variable
bg-brand/50 cần channel alpha. HEX trực tiếp trong config thường vẫn hoạt động trên Tailwind v3 gần đây, nhưng pattern bền hơn cho dark mode:
:root {
--color-brand-500: 15 118 110; /* R G B, không # */
}
.dark {
--color-brand-500: 45 212 191;
}
// v3 theme.extend.colors
brand: {
500: "rgb(var(--color-brand-500) / <alpha-value>)",
}
Đổi theme = đổi biến, không regenerate tên utility. Phù hợp SaaS white-label hoặc landing campaign Tết đổi palette tạm.
Bẫy class động — lý do “mất màu” trên production
// Nguy hiểm — scanner có thể không thấy bg-brand-500
const shade = "500";
return <div className={`bg-brand-${shade}`} />;
JIT/content scan tìm chuỗi class đầy đủ trong source. Nối chuỗi ẩn mất chúng.
Cách xử lý:
const shadeClass = {
500: "bg-brand-500",
700: "bg-brand-700",
} as const;
return <div className={shadeClass[500]} />;
Hoặc dùng CSS variable + style / utility cố định tham chiếu biến. Safelist chỉ khi thực sự dynamic và có kiểm soát — đừng safelist cả thang màu “cho chắc”.
Monorepo quên glob
apps/web/src/** ✓
packages/ui/src/** ← quên; Button bg-brand không được scan
Sau khi thêm màu: search repo chuỗi bg-brand-500, rồi mở CSS build xác nhận selector tồn tại.
Case study ngắn
1) Agency VN — landing SME từ Figma JP
Brief: logo #E11D48, nền #FFF1F2. Dev map rose-600 / rose-50 qua công cụ màu Tailwind — đủ gần, không cần extend. PR gọn, designer chấp nhận “gần brand trong ngưỡng”.
2) Product outsourcing — brand lệch scale
Fintech dùng teal #0F766E làm CTA, cần brand riêng vì marketing đổi HEX theo quý. Team dùng extend + CSS variable, checklist PR: không clobber colors, không template string class, contrast AA trên nút. Deploy ổn định qua 3 tenant.
Checklist trước khi merge PR palette
- Màu nằm trong
extendhoặc@themebổ sung — không wipe default - Mọi utility trong JSX tồn tại dạng literal đầy đủ (hoặc map object)
content/ source globs cover mọi package dùng class đó- Dark mode: biến CSS hoặc class cặp — không “nhớ chỉnh HEX sau”
- Document
500/DEFAULTcho designer; đo contrast text/CTA trên nền thật - (Tuỳ chọn) So HEX với default Tailwind trước — tránh duplicate gần giống
Cách dùng công cụ trên Kawa
- Mở Màu Tailwind
- Dán HEX từ Figma hoặc brand guideline
- Xem class gần nhất (ví dụ
teal-700) và khoảng cách màu - Quyết định: dùng default hay
extendnamespacebrand - Copy tên/scale vào config hoặc
@theme
Công cụ chạy trong trình duyệt — phù hợp khi HEX nằm trong brief nội bộ, không muốn upload lên service lạ.