CSS Variables + React: Combo mạnh nhất bạn chưa khai thác hết
CSS custom properties không chỉ để làm theme toggle. Kết hợp với React state và JavaScript, chúng trở thành công cụ performance cực mạnh — không cần re-render, không cần styled-components runtime.
CSS Variables không chỉ là "biến màu sắc"
Khi mới biết đến CSS custom properties, tôi dùng chúng đơn giản để lưu brand colors và switch dark mode. Đó là cách dùng phổ biến nhất — và cũng là cách bỏ phí 80% tiềm năng của chúng.
Sau khi đọc bài của Josh W. Comeau về CSS variables với React và thực hành kỹ hơn, tôi nhận ra: đây là tool performance cực kỳ mạnh khi kết hợp đúng cách với React state.
Tại sao CSS Variables + React = Performance win?
Khi bạn update React state, component re-renders và React reconciles virtual DOM. Với một số loại state — đặc biệt là visual state như vị trí chuột, progress bar, animation values — việc này tốn kém không cần thiết.
CSS variables cập nhật trực tiếp trên DOM element mà không trigger React re-render. Browser xử lý nó nhanh hơn nhiều:
// ❌ Cách cũ: Re-render mỗi khi chuột di chuyển
function MagneticButton() {
const [pos, setPos] = useState({ x: 0, y: 0 });
const handleMouseMove = (e) => {
setPos({ x: e.clientX, y: e.clientY }); // trigger re-render!
};
return (
);
}
// ✅ Cách mới: CSS Variables, zero re-render
function MagneticButton() {
const btnRef = useRef(null);
const handleMouseMove = (e: MouseEvent) => {
const rect = btnRef.current!.getBoundingClientRect();
const x = ((e.clientX - rect.left) / rect.width - 0.5) * 20;
const y = ((e.clientY - rect.top) / rect.height - 0.5) * 20;
btnRef.current!.style.setProperty(''--mag-x'', `${x}px`);
btnRef.current!.style.setProperty(''--mag-y'', `${y}px`);
};
return (
);
}
Kết quả? Mousemove handler không trigger re-render, không flicker, animation 60fps mượt mà. Đây là pattern tôi dùng cho tất cả hover effects và microinteractions.
Dynamic Theming không cần Context
Vấn đề phổ biến trong React: bạn cần pass theme xuống deep component tree. Giải pháp thường là React Context — nhưng Context update trigger re-render toàn bộ consumer tree.
CSS Variables là "implicit context" của CSS. Set ở root, dùng được mọi nơi mà không cần Provider:
// Theme hook — set CSS vars trực tiếp trên :root
function useTheme(theme: Theme) {
useEffect(() => {
const root = document.documentElement;
root.style.setProperty(''--color-primary'', theme.colors.primary);
root.style.setProperty(''--color-bg'', theme.colors.background);
root.style.setProperty(''--color-text'', theme.colors.text);
root.style.setProperty(''--font-size-base'', `${theme.fontSize.base}px`);
root.style.setProperty(''--radius-md'', `${theme.borderRadius.md}px`);
return () => {
// Optional cleanup — reset về default values
};
}, [theme]);
}
// Component dùng CSS vars — không cần useContext!
function Card({ children }: { children: React.ReactNode }) {
return (
{children}
);
}
/* CSS */
.card {
background: var(--color-bg);
color: var(--color-text);
border-radius: var(--radius-md);
border: 1px solid var(--color-primary);
}
Kết hợp với Tailwind CSS: CSS Variables làm bridge
Nếu bạn dùng Tailwind, CSS variables là bridge tuyệt vời giữa dynamic React state và static Tailwind classes:
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
brand: {
primary: ''var(--brand-primary)'',
secondary: ''var(--brand-secondary)'',
}
},
spacing: {
''dynamic'': ''var(--dynamic-spacing)''
}
}
}
};
// Component: set CSS var từ runtime data
function ProductCard({ accentColor, spacing }) {
return (
{/* Tailwind classes sử dụng dynamic values! */}
);
}
Pattern này đặc biệt hữu ích khi build multi-tenant app hoặc white-label product — mỗi tenant có brand color riêng mà không cần generate CSS khác nhau.
Responsive với CSS Variables: Thoát khỏi media query hell
Thay vì media queries ở nhiều chỗ trong codebase, centralize responsive logic:
/* Single source of truth cho responsive values */
:root {
--container-width: min(90vw, 1200px);
--grid-columns: 1;
--card-padding: 1rem;
--heading-size: clamp(1.5rem, 4vw, 2.5rem);
}
@media (min-width: 768px) {
:root {
--grid-columns: 2;
--card-padding: 1.5rem;
}
}
@media (min-width: 1024px) {
:root {
--grid-columns: 3;
--card-padding: 2rem;
}
}
/* Component CSS — zero media queries needed */
.product-grid {
display: grid;
grid-template-columns: repeat(var(--grid-columns), 1fr);
padding: var(--card-padding);
max-width: var(--container-width);
}
AI assistant giúp gì trong workflow này?
Tôi dùng Cursor để generate CSS variable naming systems và refactor old useState-for-visual-state thành CSS var patterns. Prompt tôi hay dùng:
"Tôi có component X đang dùng useState để track [visual property]. Refactor thành CSS custom properties pattern để tránh re-render. Giữ nguyên behavior, chỉ thay đổi cách update style."
Agent hiểu pattern này rất tốt và thường suggest thêm cả useRef cho element reference — đúng như ví dụ MagneticButton ở trên.
TL;DR — Khi nào dùng CSS Variables thay vì React state?
- Dùng CSS Variables: Animation values, mouse position, scroll progress, hover state, color themes, spacing values, anything purely visual
- Dùng React state: Data-driven UI (show/hide components, conditional rendering), anything ảnh hưởng đến DOM structure, business logic
Rule of thumb: nếu change chỉ ảnh hưởng đến appearance mà không thay đổi structure, CSS Variables là lựa chọn tốt hơn.
Admin
Bình luận (0)
Đăng nhập để bình luận
Chưa có bình luận nào. Hãy là người đầu tiên!