Roles, not swatches: why your color palette needs a job description
You have five colors. Congratulations. So does everyone.
The question isn't which colors you picked. It's whether those colors know what they're supposed to do. A palette is a list of hex values. A color system is a set of decisions. Most teams have the first. Very few have the second.
The difference matters more than you think.
Palettes vs. systems
A palette says: here are five colors. Use them. Good luck.
A system says: this color is the background. This one is for text. This one is the accent — use it on CTAs only, never as body text, and always with dark text on top of it. This one is support — secondary surfaces and dividers. This one is neutral — borders, disabled states, subtle backgrounds.
Same five hex values. Completely different utility.
The palette gives you ingredients. The system gives you a recipe. And if you've ever watched someone try to cook with just ingredients and no recipe, you know how that ends.
The five roles
Every effective color system maps its colors to roles. Not names like "Blue-500" or "Brand-Primary." Roles. Functions. Jobs.
Background
The default surface. Pages, cards, modals, panels. This is the canvas everything else sits on. It needs to be light enough (or dark enough, in dark mode) to support readable text. It's the color your users see the most and notice the least. That's the point.
Ink
Text, headings, icons, rules. Everything that carries information. Ink must have a high contrast ratio against Background — WCAG AA minimum (4.5:1 for body text), AAA preferred (7:1). This is non-negotiable. If people can't read your text, nothing else matters.
Accent
The signal color. CTAs, focus rings, selected states, highlights. It draws attention. It says "look here." It should be used sparingly — an accent that's everywhere isn't an accent, it's noise. Critical rule: Accent is almost always a fill or stroke color, not a text color. Most accent colors fail WCAG contrast as body text on light backgrounds.
Support
The second voice. Subheadings, secondary buttons, dividers, helper text. Support carries information that's important but not primary. It sits between Ink and Background in visual weight. Think of it as Ink's quieter sibling.
Neutral
The infrastructure color. Borders, disabled states, skeleton loading screens, subtle chips, separator lines. Neutral should be visually quiet — close to Background in lightness but still distinguishable. It's the color of things that need to exist but shouldn't demand attention.
Why roles matter
Roles encode intent
"Use the accent" is clearer than "use #FC6911." When someone reads "accent," they understand the purpose: this is the attention color. When they read a hex code, they see a number. Intent doesn't survive hex notation.
Roles survive palette changes
This is the big one. If you swap your entire palette — new background, new ink, new accent — the roles don't change. Background is still for surfaces. Ink is still for text. Accent is still for CTAs. Your components reference roles, not hex values. The colors change; the system holds.
Try doing that with hardcoded hex values. You'll be doing a find-and-replace across 200 files and praying you didn't miss one.
Roles prevent misuse
When a color has a role, misuse becomes obvious. "You used Accent as body text" is a clear violation. "You used #FC6911 in a paragraph" isn't — maybe that was intentional. Roles make wrong usage visible and reviewable.
How Paletter assigns roles
We don't ask you to assign roles manually. The algorithm does it based on color science.
Lightness-based assignment
Background and Ink are determined by lightness in LAB color space. The lightest color becomes Background. The darkest becomes Ink. This guarantees maximum contrast for the most critical pairing in your system: text on surfaces.
Saturation-based accent selection
The most saturated color becomes Accent. Saturation correlates with visual salience — the eye is drawn to it. That's exactly what you want for CTAs and highlights.
Delta-E for uniqueness
Every color must be perceptually distinct from every other color. We measure Delta-E (the perceptual distance between colors in LAB space) and ensure no two colors are too similar. If your "Support" and "Neutral" look the same to the human eye, the system has a problem. We catch it before you ship it.
Remaining roles by elimination
Once Background, Ink, and Accent are assigned, Support and Neutral fill the remaining slots based on lightness and saturation. Support takes the role with moderate contrast against Background. Neutral gets the lowest-contrast remaining color — quiet by design.
Roles enable AI
Here's where roles become essential for modern workflows. When an AI coding tool sees "Background," it knows to use that color for surfaces, cards, and page backgrounds. When it sees #F5DBC1, it sees a number with no meaning.
Roles are semantic. Hex codes are not. AI tools understand semantics. They're very good at following instructions like "use the Background color for all surface elements." They're terrible at inferring that #F5DBC1 should be a background and not a border.
A COLORS.md file with roles, contrast ratios, and usage rules gives an AI everything it needs to make correct color decisions. Without roles, the AI guesses. With roles, it follows instructions. Learn more about design tokens and roles.
Implementing roles in code
Roles map directly to CSS custom properties and design tokens:
--color-bg/--color-surface— Background--color-text/--color-ink— Ink--color-primary/--color-accent— Accent--color-secondary/--color-support— Support--color-muted/--color-neutral— Neutral
Your components reference roles, not colors. background: var(--color-bg) works regardless of whether the background is warm paper or cool gray. Swap the palette, keep the roles, your UI still works.
Generate a palette with roles
Every Paletter palette ships with five assigned roles, contrast-checked pairings, and export-ready tokens. Not swatches. A system.
Generate a palette with roles