JavaScript and React course Β· Module 14: Advanced Styling
Design Tokens and Systematic CSS - Cosmic Design Code
In this lesson10
Pilot, imagine that every ship in the galactic fleet uses exactly the same status colors, the same spacing between panels, and the same typography on monitors. This is no coincidence -- it's a design system. In the frontend world, the equivalent is design tokens -- a central source of truth for all visual values in the application.
What are Design Tokens?
Design tokens are named values representing design decisions: colors, spacing, font sizes, shadows, border radii. Instead of scattering magic numbers throughout the code, you define them in one place:
1// tokens.js -- central source of truth
2const tokens = {
3 colors: {
4 primary: '#7c4dff',
5 primaryLight: '#b388ff',
6 primaryDark: '#651fff',
7 success: '#00e676',
8 warning: '#ffa726',
9 danger: '#f44336',
10 bgDark: '#0a0a2e',
11 bgSurface: '#1a1a4e',
12 textPrimary: '#e0e0e0',
13 textMuted: '#9e9e9e',
14 border: '#3949ab',
15 },
16 spacing: {
17 xs: '4px',
18 sm: '8px',
19 md: '16px',
20 lg: '24px',
21 xl: '32px',
22 xxl: '48px',
23 },
24 fontSize: {
25 xs: '12px',
26 sm: '14px',
27 md: '16px',
28 lg: '20px',
29 xl: '28px',
30 xxl: '36px',
31 },
32 borderRadius: {
33 sm: '4px',
34 md: '8px',
35 lg: '12px',
36 xl: '20px',
37 full: '9999px',
38 },
39 shadows: {
40 sm: '0 2px 4px rgba(0,0,0,0.1)',
41 md: '0 4px 12px rgba(0,0,0,0.2)',
42 lg: '0 8px 24px rgba(0,0,0,0.3)',
43 glow: '0 0 20px rgba(124,77,255,0.3)',
44 },
45};Using tokens in components
Once you have tokens defined, you use them instead of direct values:
1import { tokens } from './tokens';
2
3// In inline styles
4function Card({ children }) {
5 return (
6 <div style={{
7 background: tokens.colors.bgSurface,
8 padding: tokens.spacing.lg,
9 borderRadius: tokens.borderRadius.lg,
10 border: `1px solid ${tokens.colors.border}`,
11 }}>
12 {children}
13 </div>
14 );
15}1// In styled-components
2const Card = styled.div`
3 background: ${tokens.colors.bgSurface};
4 padding: ${tokens.spacing.lg};
5 border-radius: ${tokens.borderRadius.lg};
6 border: 1px solid ${tokens.colors.border};
7`;Tokens as CSS Variables
The best approach is to combine JavaScript tokens with CSS Variables -- you define tokens in JS (easy to manage) and pass them to CSS as variables (efficient rendering):
1function applyTokens(tokens) {
2 const vars = {};
3 vars['--color-primary'] = tokens.colors.primary;
4 vars['--color-bg'] = tokens.colors.bgDark;
5 vars['--color-surface'] = tokens.colors.bgSurface;
6 vars['--color-text'] = tokens.colors.textPrimary;
7 vars['--spacing-md'] = tokens.spacing.md;
8 vars['--spacing-lg'] = tokens.spacing.lg;
9 vars['--radius-lg'] = tokens.borderRadius.lg;
10 return vars;
11}
12
13function App() {
14 return (
15 <div style={applyTokens(tokens)} className="app">
16 {/* CSS uses var(--color-primary) etc. */}
17 </div>
18 );
19}Consistent color system
A good color system has structure:
1const colorSystem = {
2 // Base colors
3 blue: { 50: '#e3f2fd', 100: '#bbdefb', 500: '#2196f3', 900: '#0d47a1' },
4 purple: { 50: '#f3e5f5', 100: '#e1bee7', 500: '#9c27b0', 900: '#4a148c' },
5
6 // Semantic colors (used in code)
7 primary: 'var(--blue-500)',
8 success: 'var(--green-500)',
9 warning: 'var(--orange-500)',
10 danger: 'var(--red-500)',
11
12 // Surface colors
13 background: '#0a0a2e',
14 surface: '#1a1a4e',
15 surfaceHover: '#2a2a5e',
16};Semantic colors (primary, success, danger) are separated from base colors (blue-500, red-400). This way, changing the theme is just a mapping change, not searching through the entire codebase.
Spacing system
Spacing based on a scale -- each next value is a multiple of the base:
1// 4px scale (Tailwind-style)
2const spacing = {
3 0: '0px',
4 1: '4px',
5 2: '8px',
6 3: '12px',
7 4: '16px',
8 5: '20px',
9 6: '24px',
10 8: '32px',
11 10: '40px',
12 12: '48px',
13 16: '64px',
14};Using a scale instead of arbitrary values guarantees visual consistency -- elements on the screen are evenly spaced, giving a professional look.
Typography as a system
1const typography = {
2 fontFamily: {
3 sans: "'Inter', 'Segoe UI', sans-serif",
4 mono: "'Fira Code', monospace",
5 },
6 fontSize: {
7 xs: ['12px', { lineHeight: '16px' }],
8 sm: ['14px', { lineHeight: '20px' }],
9 base: ['16px', { lineHeight: '24px' }],
10 lg: ['20px', { lineHeight: '28px' }],
11 xl: ['28px', { lineHeight: '36px' }],
12 '2xl': ['36px', { lineHeight: '44px' }],
13 },
14 fontWeight: {
15 normal: 400,
16 medium: 500,
17 semibold: 600,
18 bold: 700,
19 },
20};Each font size has an assigned line height -- this eliminates typographic chaos.
Token Composition -- composing tokens
An advanced pattern is token composition -- creating more specific tokens from base ones:
1// Base tokens (primitive)
2const primitives = {
3 blue500: '#7c4dff',
4 blue300: '#b388ff',
5 green500: '#00e676',
6 red500: '#f44336',
7 gray900: '#0a0a2e',
8 gray800: '#1a1a4e',
9 gray100: '#e0e0e0',
10 gray400: '#9e9e9e',
11};
12
13// Semantic tokens (composed from base)
14const semantic = {
15 colorPrimary: primitives.blue500,
16 colorPrimaryLight: primitives.blue300,
17 colorSuccess: primitives.green500,
18 colorDanger: primitives.red500,
19 colorBackground: primitives.gray900,
20 colorSurface: primitives.gray800,
21 colorText: primitives.gray100,
22 colorTextMuted: primitives.gray400,
23};
24
25// Component tokens (composed from semantic)
26const components = {
27 buttonPrimaryBg: semantic.colorPrimary,
28 buttonPrimaryText: '#ffffff',
29 buttonDangerBg: semantic.colorDanger,
30 cardBg: semantic.colorSurface,
31 cardBorder: primitives.blue500 + '40',
32};Three layers: base (values) -> semantic (meaning) -> component (usage). Changing a single base token cascades updates everywhere.
Style Dictionary -- automating tokens
In large projects, tokens are stored in JSON format and automatically generated for different platforms using the Style Dictionary tool:
1{
2 "color": {
3 "primary": { "value": "#7c4dff" },
4 "success": { "value": "#00e676" },
5 "background": { "value": "#0a0a2e" }
6 },
7 "spacing": {
8 "sm": { "value": "8px" },
9 "md": { "value": "16px" },
10 "lg": { "value": "24px" }
11 }
12}Style Dictionary generates from this:
- CSS Variables -- for web applications
- JavaScript/TypeScript -- for React
- Swift/Kotlin -- for mobile applications
One source of truth, multiple output formats.
Automating consistency
Creating a TokenPreview component helps control the entire system:
1function TokenPreview({ tokens }) {
2 return (
3 <div>
4 <h2>Colors</h2>
5 {Object.entries(tokens.colors).map(([name, value]) => (
6 <div key={name} style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
7 <div style={{ width: 40, height: 40, background: value, borderRadius: 8 }} />
8 <span>{name}: {value}</span>
9 </div>
10 ))}
11
12 <h2>Spacing</h2>
13 {Object.entries(tokens.spacing).map(([name, value]) => (
14 <div key={name} style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
15 <div style={{ width: value, height: 16, background: '#7c4dff' }} />
16 <span>{name}: {value}</span>
17 </div>
18 ))}
19 </div>
20 );
21}When to use design tokens?
- Any project larger than a landing page -- tokens save time in the long run
- Team collaboration -- every developer uses the same values
- Multiple themes -- changing one object changes the entire appearance
- Cross-platform consistency -- Style Dictionary generates tokens for React, React Native, iOS, Android
- Token composition -- three layers (base -> semantic -> component) give full control
Code for this lesson: App.jsx
1import React, { useState } from 'react';
2import './styles.css';
3
4// Design tokens -- single source of truth
5const tokens = {
6 colors: {
7 primary: '#7c4dff', primaryLight: '#b388ff', primaryDark: '#651fff',
8 success: '#00e676', warning: '#ffa726', danger: '#f44336',
9 bgDark: '#0a0a2e', bgSurface: '#1a1a4e',
10 textPrimary: '#e0e0e0', textMuted: '#9e9e9e', border: '#3949ab',
11 },
12 spacing: { xs: '4px', sm: '8px', md: '16px', lg: '24px', xl: '32px' },
13 fontSize: { xs: '12px', sm: '14px', md: '16px', lg: '20px', xl: '28px' },
14 borderRadius: { sm: '4px', md: '8px', lg: '12px', full: '9999px' },
15};
16
17// Converting tokens to CSS Variables
18function tokensToVars(t) {
19 return {
20 '--color-primary': t.colors.primary,
21 '--color-primary-light': t.colors.primaryLight,
22 '--color-success': t.colors.success,
23 '--color-warning': t.colors.warning,
24 '--color-danger': t.colors.danger,
25 '--color-bg': t.colors.bgDark,
26 '--color-surface': t.colors.bgSurface,
27 '--color-text': t.colors.textPrimary,
28 '--color-text-muted': t.colors.textMuted,
29 '--color-border': t.colors.border,
30 '--spacing-sm': t.spacing.sm,
31 '--spacing-md': t.spacing.md,
32 '--spacing-lg': t.spacing.lg,
33 '--radius-lg': t.borderRadius.lg,
34 };
35}
36
37// Color preview
38function ColorSwatch({ name, value }) {
39 return (
40 <div className="swatch">
41 <div className="swatch-color" style={{ background: value }} />
42 <div className="swatch-info">
43 <span className="swatch-name">{name}</span>
44 <span className="swatch-value">{value}</span>
45 </div>
46 </div>
47 );
48}
49
50// Spacing preview
51function SpacingBar({ name, value }) {
52 return (
53 <div className="spacing-row">
54 <span className="spacing-name">{name}</span>
55 <div className="spacing-bar" style={{ width: value, background: 'var(--color-primary)' }} />
56 <span className="spacing-value">{value}</span>
57 </div>
58 );
59}
60
61// Example of using tokens in a component
62function TokenCard({ title, children }) {
63 return (
64 <div className="token-card">
65 <h3 className="token-card-title">{title}</h3>
66 {children}
67 </div>
68 );
69}
70
71function TokenButton({ variant, children }) {
72 return (
73 <button className={`token-btn token-btn-${variant}`}>{children}</button>
74 );
75}
76
77export default function App() {
78 return (
79 <div style={tokensToVars(tokens)} className="app">
80 <h1 className="heading">Design Tokens</h1>
81 <p className="subtitle">Single source of truth -- all values in one place</p>
82
83 <TokenCard title="Colors">
84 <div className="swatch-grid">
85 {Object.entries(tokens.colors).map(([name, value]) => (
86 <ColorSwatch key={name} name={name} value={value} />
87 ))}
88 </div>
89 </TokenCard>
90
91 <TokenCard title="Spacing">
92 {Object.entries(tokens.spacing).map(([name, value]) => (
93 <SpacingBar key={name} name={name} value={value} />
94 ))}
95 </TokenCard>
96
97 <TokenCard title="Components using tokens">
98 <div className="component-demo">
99 <TokenButton variant="primary">Primary</TokenButton>
100 <TokenButton variant="success">Success</TokenButton>
101 <TokenButton variant="danger">Danger</TokenButton>
102 <TokenButton variant="outline">Outline</TokenButton>
103 </div>
104 <p className="demo-note">
105 Each button uses var(--color-...) from the tokens.
106 Changing a token changes all components at once!
107 </p>
108 </TokenCard>
109 </div>
110 );
111}Spotted a mistake in this lesson?
Check yourself
Answer the questions from this lesson. Pick an answer to see right away whether it is correct.
1. What are design tokens in the context of frontend development?
2. Why is it worth separating semantic colors (primary, danger) from base colors (blue-500, red-400) in a design tokens system?
These are 2 of 4 questions for this lesson. Solve the rest in the game.
Hands-on tasks in the game
- Horizontal ordering
Arrange the syntax for using a color token in CSS with a CSS Variable:
- Horizontal ordering
Arrange the syntax for defining a spacing token in a JavaScript object:
- Click in order
Arrange the steps for converting a JavaScript token to a CSS Variable in React:
- Vertical ordering
Arrange the design tokens system layers from the most basic to the most specific:
- Vertical ordering
Arrange the steps for implementing design tokens in a new React project:
- Code editor
Create a design tokens system and use it in a component
- Vertical ordering
Arrange the font size tokens from smallest to largest:
- Code editor
Create a visual preview of color tokens