Who this is for
Data Visualization Engineers, BI developers, and analytics engineers who build reusable charts, dashboards, and UI patterns that must stay consistent across products and themes.
Prerequisites
- Basic understanding of CSS and component-based UI (or templating) concepts.
- Familiarity with chart anatomy: axes, legends, tooltips, grids, series.
- Awareness of color scales (categorical, sequential, diverging).
Why this matters
In real projects, you will:
- Ship chart components (bar/line/area, legend, tooltip) used across many dashboards.
- Support light/dark mode and accessibility without rewriting styles for every chart.
- Move quickly: add new themes or palettes by updating tokens, not code.
- Ensure brand and data visuals stay consistent across teams and platforms.
Concept explained simply
Design tokens are named values (like variables) for visual decisions: colors, spacing, typography, radii, shadows, motion. Component libraries are reusable UI building blocks (Legend, Tooltip, Axis, Button) that reference tokens instead of hard-coded values.
Mental model
- Tokens = the dictionary of design decisions.
- Components = LEGO bricks that read from the dictionary.
- Themes = alternative dictionaries with the same keys but different values.
See a minimal token set
{
"color": {
"brand": {"primary": "#2F5DFF"},
"text": {"default": "#222", "muted": "#666", "inverse": "#FFF"},
"bg": {"canvas": "#FFF", "surface": "#F7F7F9"},
"data": {
"categorical": {"1": "#2F5DFF", "2": "#00A676", "3": "#FF7A59", "4": "#7A6FF0"},
"sequential": {"0": "#EAF2FF", "1": "#C6DAFF", "2": "#8AB3FF", "3": "#4D86FF", "4": "#1F5AFF"},
"diverging": {"neg": "#CF2E2E", "mid": "#F3F4F6", "pos": "#118A4E"}
}
},
"space": {"0": 0, "1": 4, "2": 8, "3": 12, "4": 16, "5": 24},
"radius": {"sm": 4, "md": 8},
"font": {"family": "Inter, system-ui, sans-serif", "size": {"sm": 12, "md": 14, "lg": 16}}
}
Worked examples
Example 1 — Map data colors via semantic tokens
Define semantic aliases so charts don’t depend on brand hex codes directly.
{
"color": {
"data": {
"category-1": {"value": "{color.data.categorical.1}"},
"category-2": {"value": "{color.data.categorical.2}"},
"highlight": {"value": "{color.brand.primary}"}
}
}
}
In CSS variables, this could look like:
:root {
--color-data-category-1: #2F5DFF;
--color-data-category-2: #00A676;
--color-data-highlight: #2F5DFF;
}
Your BarChart component uses var(--color-data-category-1), not #2F5DFF.
Example 2 — Consistent spacing and typography for axes
:root {
--space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px;
--font-size-sm: 12px; --font-size-md: 14px;
}
.chart-axis-label { font-size: var(--font-size-sm); margin-top: var(--space-2); }
.chart-legend { gap: var(--space-2); padding: var(--space-3); }
When you change --space-2 from 8px to 10px, axis and legend spacing update automatically.
Example 3 — Light/Dark theming for charts
:root[data-theme="light"] {
--color-text-default: #222; --color-bg-canvas: #FFFFFF;
--color-data-category-1: #2F5DFF; --color-data-category-2: #00A676;
}
:root[data-theme="dark"] {
--color-text-default: #F5F7FA; --color-bg-canvas: #0F1216;
--color-data-category-1: #7BA5FF; --color-data-category-2: #36C79A;
}
Same token names, different values per theme. Components don’t change.
Example 4 — Legend and Tooltip components consuming tokens
.legend {
display: grid; grid-auto-flow: column; gap: var(--space-2);
color: var(--color-text-default);
}
.legend-swatch { width: 10px; height: 10px; border-radius: var(--radius-sm); }
.legend-item:nth-child(1) .legend-swatch { background: var(--color-data-category-1); }
.legend-item:nth-child(2) .legend-swatch { background: var(--color-data-category-2); }
.tooltip {
background: var(--color-bg-surface, #1B1F24);
color: var(--color-text-inverse, #FFF);
padding: var(--space-2) var(--space-3);
border-radius: var(--radius-md);
box-shadow: 0 4px 12px rgba(0,0,0,0.25);
}
Naming and structure
- Prefer stable, descriptive keys:
color.data.category-1notblue. - Separate base tokens (raw values) from semantic tokens (aliases): e.g.,
color.base.blue.600andcolor.data.category-1 => color.base.blue.600. - Plan scales: space (4px steps), font sizes, radii.
- Document theme variants using the same token keys.
Accessibility with tokens
- Maintain contrast by testing token pairs (text vs background, lines vs background).
- Support color-vision deficiencies by ensuring distinct luminance/shape encodings (e.g., dashed lines + markers).
- Use semantic states:
color.data.highlight,color.data.mutedto control emphasis consistently.
Governance and versioning
- Version tokens:
v1stable; proposev2in a branch/PR; deprecate keys with notes. - Write a brief change log for token updates impacting charts.
- Run a "token audit" pre-release: search for hard-coded values like
#orpxthat should be tokens.
Exercises
Do the task below. Your progress in the quick test saves if you are logged in; everyone can take it for free.
Exercise 1 — Build a minimal data-viz token map and a legend spec
Goal: Create tokens for a dashboard with 4 categorical series and light/dark themes. Then define how a Legend component uses them.
- Define base tokens for colors and space (4px scale).
- Create semantic data tokens:
color.data.category-1..4,color.data.highlight. - Provide light and dark theme values.
- Specify a Legend component API: items read color tokens by index.
Expected output
- A token JSON or CSS variable snippet for light/dark.
- A short spec of Legend behavior using those tokens.
Hints
- Keep token keys stable across themes; vary only values.
- Map legend item n to
color.data.category-n.
Show solution
{
"color": {
"base": {
"blue-600": "#2F5DFF", "teal-600": "#00A676", "orange-600": "#FF7A59", "violet-600": "#7A6FF0",
"blue-400": "#7BA5FF", "teal-400": "#36C79A", "orange-400": "#FF9D82", "violet-400": "#A59BFF"
},
"text": {"default": {"light": "#222", "dark": "#F5F7FA"}},
"bg": {"canvas": {"light": "#FFF", "dark": "#0F1216"}}
},
"data": {
"category-1": {"light": "{color.base.blue-600}", "dark": "{color.base.blue-400}"},
"category-2": {"light": "{color.base.teal-600}", "dark": "{color.base.teal-400}"},
"category-3": {"light": "{color.base.orange-600}", "dark": "{color.base.orange-400}"},
"category-4": {"light": "{color.base.violet-600}", "dark": "{color.base.violet-400}"},
"highlight": {"light": "{color.base.blue-600}", "dark": "{color.base.blue-400}"}
},
"space": {"0": 0, "1": 4, "2": 8, "3": 12, "4": 16}
}
/* Legend spec */
.legend { display: grid; grid-auto-flow: column; gap: var(--space-2); }
.legend-item { display: inline-flex; align-items: center; gap: var(--space-1); }
.legend-swatch { width: 10px; height: 10px; border-radius: 3px; }
/* Map item index to token */
.legend-item:nth-child(1) .legend-swatch { background: var(--color-data-category-1); }
.legend-item:nth-child(2) .legend-swatch { background: var(--color-data-category-2); }
.legend-item:nth-child(3) .legend-swatch { background: var(--color-data-category-3); }
.legend-item:nth-child(4) .legend-swatch { background: var(--color-data-category-4); }
Checklist for your component library
- [ ] All chart components use tokens, not hex or px literals.
- [ ] Data palettes defined for categorical, sequential, diverging.
- [ ] Light and dark themes share identical token keys.
- [ ] Spacing, radii, and typography pulled from tokens.
- [ ] Tooltip and legend read from data tokens and text tokens.
- [ ] Contrast checked for text and key lines/markers.
- [ ] Token change log exists; version bump when breaking changes occur.
Common mistakes and self-check
- Mistake: Using brand colors for data categories directly. Fix: Separate UI brand tokens from data palette tokens.
- Mistake: Hard-coded spacing and font sizes. Fix: Replace with
space.*andfont.size.*tokens. - Mistake: One palette used in both light/dark leading to low contrast. Fix: Provide theme-specific values for the same keys.
- Mistake: No semantic tokens for states (highlight/muted). Fix: Add tokens to control emphasis consistently.
- Mistake: Diverging scale without neutral mid. Fix: Include a readable midpoint token.
Self-check steps
- Search code for
#andpx; replace with tokens. - Flip between light/dark and ensure visuals remain readable.
- Test with grayscale and color-blind simulator; ensure series are still distinguishable.
Practical projects
- Create a token-driven Bar, Line, and Scatter chart set that supports light/dark and high-contrast modes.
- Build a configurable Legend component that truncates labels and supports reordering, all token-based.
- Implement a tokenized Tooltip with compact and spacious density options via spacing tokens.
Learning path
- Before this: color scales and chart accessibility basics.
- This topic: define tokens and wire components to them.
- Next: theming, density modes, and dynamic palettes from data domains.
Mini challenge
Extend your token set with a sequential palette for heatmaps (5 steps). Update a Heatmap component to reference color.data.sequential.0..4. Verify contrast in both light and dark themes.
Next steps
- Refactor an existing dashboard to remove hard-coded values and adopt tokens.
- Document token usage in your component README so others can adopt quickly.
- Take the quick test to confirm understanding. Your progress saves if logged in.