Learn to build a scalable Design System using design tokens and theme-aware components. Eliminate hardcoded values and enforce UI consistency in React apps.
Previously in this course, we explored using Portals for UI overlays to handle complex, detached DOM structures. Now, we shift our focus from functional DOM placement to visual architectural integrity: building a Design System that scales.
In large-scale React applications, "style drift" is the silent killer of productivity. When developers hardcode hex values or spacing units, the UI loses its cohesion. Today, we define the foundation of a robust Design System by implementing design tokens and theme-aware components.
Design tokens are the atomic building blocks of your UI. Instead of using raw values like #3b82f6 or 16px directly in your components, you define a semantic mapping.
blue-500: #3b82f6).brand-primary: var(--blue-500)).button-bg: var(--brand-primary)).By decoupling the value from the intent, you enable theming and global style updates without touching a single component file.
To enforce consistency, we move away from standard CSS and toward a CSS-in-JS or CSS Variable approach. Let's build a theme provider that leverages CSS variables for maximum performance and compatibility.
First, define your design tokens in a central configuration object.
JAVASCRIPT// tokens.js export const themeTokens = { light: { CE9178">'--color-bg': CE9178">'#ffffff', CE9178">'--color-text': CE9178">'#1a1a1a', CE9178">'--spacing-md': CE9178">'16px', }, dark: { CE9178">'--color-bg': CE9178">'#1a1a1a', CE9178">'--color-text': CE9178">'#f0f0f0', CE9178">'--spacing-md': CE9178">'16px', } };
We use a ThemeProvider to inject these tokens into the root of our application. This allows us to switch themes dynamically by simply swapping the CSS variable definitions.
JSX// ThemeProvider.jsx import { createContext, useContext, useEffect } from CE9178">'react'; const ThemeContext = createContext(CE9178">'light'); export const ThemeProvider = ({ theme, children }) => { useEffect(() => { const root = document.documentElement; const tokens = themeTokens[theme]; Object.entries(tokens).forEach(([key, value]) => { root.style.setProperty(key, value); }); }, [theme]); return ( <ThemeContext.Provider value={theme}> {children} </ThemeContext.Provider> ); };
To ensure developers don't bypass the system, we create "Primitive" components. These are low-level building blocks like Box, Text, and Stack that expose our design tokens as a strict API.
JSX// Box.jsx import styled from CE9178">'styled-components'; export const Box = styled.divCE9178">` background-color: var(--color-bg); color: var(--color-text); padding: var(--spacing-md); transition: background-color 0.2s ease; `;
By wrapping our layout logic in these primitives, we ensure that every Box in the application automatically responds to theme changes and adheres to our spacing scale.
Your task is to extend the ThemeProvider to support a user-defined theme toggle.
useTheme hook that exposes the current theme and a toggleTheme function.ThemeToggle button that updates the state in the ThemeProvider.Box components using the CSS variable system.var(--spacing-md)) rather than raw numbers. If you find yourself writing padding: 16px, create a new token.Building a Design System requires a shift in mindset: stop thinking about pixels and start thinking about intent. By implementing CSS-in-JS patterns or CSS variables, you enable theming as a first-class citizen. This approach guarantees consistency across your application, making it easier to maintain and faster to scale.
As you advance, remember that your design system should evolve with your application. If you're struggling with data-heavy views, consider how these UI primitives might integrate with API Design Caching Strategies or the API Design Soft Delete Patterns we've discussed for backend stability.
Up next: Managing Large-Scale Data Fetching.
Master Security Best Practices in React by learning to sanitize inputs, implement CSP, and manage data flow to prevent XSS and sensitive data leakage.
Read moreLearn to architect scalable micro-frontends with React and Module Federation. Discover how to manage shared dependencies and handle cross-app communication.
Building Design System Primitives
Advanced Ref Usage
Memoization Pitfalls
Mastering React Patterns for Scalability
Advanced TypeScript with React