Most websites launch with a color palette: a primary color, maybe a secondary, a neutral gray or two, and some intuition about what looks good. That palette lives in a Figma file or a style guide doc, gets applied loosely across the site, and quietly accumulates exceptions over two years until the marketing team has six shades of blue and nobody can agree on which one is "the brand blue."
A color system is a different thing. It is a structured set of decisions about how colors relate to each other, what roles they serve, how they behave across light and dark contexts, and how they translate into code. Getting it right upfront costs a few extra hours. Not getting it right costs much more, usually during a rebrand or accessibility audit.

Photo by freephotocc on Pixabay
Why a Palette Is Not a System
A palette is a list of hex codes. A system is a set of rules that determines which color goes where and why.
The difference shows up in edge cases. A palette tells you your brand color is #1A73E8. A system tells you that #1A73E8 is used for primary interactive elements, that it requires a white or light text label to meet WCAG AA contrast requirements, that it has a dark-mode variant at #4DA3FF, and that it is never used as a background behind body text.
Without those rules, designers and developers make judgment calls that drift over time. The sixteenth person to implement a button makes slightly different assumptions than the first, and the UI gradually becomes inconsistent in ways that are expensive to audit and fix.
A color system also makes accessibility tractable. Instead of checking every text-and-background combination at the end of a project, you encode the contrast relationships into the system itself so that anything using approved color tokens is guaranteed to pass.
The Three Core Layers
A practical color system for a web project has three layers: base colors, role-based semantic tokens, and component-level decisions.
Base Colors
The base palette is the full set of raw hex values. For most projects, this includes the brand primary, one or two accent colors, a complete neutral scale (typically 10-12 steps from near-white to near-black), and any functional colors: success green, warning amber, error red, informational blue.
The neutral scale is often underbuilt. Designers pick three or four grays and then add more ad hoc as the project progresses. Starting with a full 10-step neutral scale (100 through 1000, or light-1 through dark-10) eliminates most of those additions and gives you a coherent foundation for both light and dark themes.
Semantic Tokens
Semantic tokens map roles to base colors. Rather than using a raw hex in a component, you use a token name like color.background.primary or color.text.interactive. The token references a base color that can swap between themes without the component needing to know anything about it.
Common semantic token categories:
- Background tokens: page background, surface, elevated surface, overlay
- Text tokens: primary text, secondary text, placeholder, disabled
- Interactive tokens: link color, button background, button text, focus ring
- Border tokens: default border, focused border, error border
- Status tokens: success background and text, error background and text, warning
The benefit of semantic naming is that a dark-mode switch only needs to remap the tokens, not every individual color usage across the codebase.

Photo by citypraiser on Pixabay
Component-Level Decisions
The component level is where tokens get applied. A button component uses color.background.interactive for its default state and color.background.interactive-hover for its hover state. These are not new colors - they are references to semantic tokens that are already defined. The component has no hardcoded colors; it only references the system.
This separation is what makes large codebases maintainable. Changing the brand primary updates one token, which propagates to every component that references it.
Contrast and WCAG Compliance
The Web Content Accessibility Guidelines (WCAG) define minimum contrast ratios for text readability. At the AA level: 4.5:1 for normal-weight text under 18pt, and 3:1 for large text (18pt+ normal weight or 14pt bold) and UI components like buttons and icons.
The practical implication is that you need to validate contrast ratios for every text-on-background combination in your system, not just the obvious ones. Text on a brand-colored button is easy to remember. Text on a semi-transparent overlay over a photo is not. Light gray helper text on a light gray input background is often the one that fails.
The right time to check these ratios is when you define the semantic tokens, not after the components are built. If color.text.secondary on color.background.surface fails 4.5:1, you need to adjust one of the two. Catching that before the component library is built saves a round of fixes later.
Tools for contrast checking are built into browser developer tools (Chrome's accessibility audit, Firefox's accessibility inspector) and available as plugins for Figma. The WCAG accessibility guidelines published by the W3C are the authoritative reference for contrast ratios and what "AA" and "AAA" compliance mean in practice.
Dark Mode and System Themes
Dark mode is no longer optional for consumer-facing web products. Operating systems expose a prefers-color-scheme media query that websites can use to apply a dark theme automatically based on system preference. Users expect it to work; the question is just how much of it you build upfront.
The cleanest implementation uses semantic tokens throughout. When the dark theme is active, the tokens remap: color.background.primary switches from near-white to near-black, color.text.primary switches from dark gray to near-white, and interactive colors adjust to maintain their contrast ratios against the new backgrounds.
A common mistake is to invert the light palette mechanically. Dark mode is not "everything flipped." Dark surfaces use the near-dark end of the neutral scale (not pure black), text is near-white (not pure white), and brand colors typically shift to lighter values with less saturation to avoid oversaturation on dark backgrounds. Inverting a palette directly often produces dark themes that fail contrast requirements or feel harsh.
"Color systems age better than color palettes. If you find yourself adding new hex values to fix a contrast problem rather than adjusting a token, that is a signal the system architecture needs attention, not the color." - Dennis Traina, founder of 137Foundry

Photo by Matheus Bertelli on Pexels
Translating the System Into Design Tokens in Code
A color system only delivers its benefits if it is implemented in the codebase in a way that developers can use it consistently. The standard approach is CSS custom properties (also called CSS variables), set at the root level and overridden for the dark theme through a media query or a data attribute on the <html> element.
:root {
--color-background-primary: #ffffff;
--color-text-primary: #1a1a1a;
--color-interactive: #1a73e8;
}
@media (prefers-color-scheme: dark) {
:root {
--color-background-primary: #121212;
--color-text-primary: #e8e8e8;
--color-interactive: #4da3ff;
}
}
Components reference var(--color-interactive) rather than a hardcoded hex, which means the theme switch happens entirely in the root token definitions.
For design-to-code consistency, Material Design 3 has a publicly documented color system that serves as a useful reference for token naming conventions and role definitions. The MDN documentation covers CSS custom properties in detail if the implementation is unfamiliar.
Practical Tools for Building the System
A few tools simplify the process. Coolors is useful for generating and exploring color palettes from a starting brand color, with real-time contrast feedback. Most design tools (Figma, Sketch) have built-in variable or token systems that can hold semantic colors and swap themes at the library level.
For accessibility testing during development, browser devtools handle most checks. The W3C's published guidelines remain the standard for what counts as compliant.
Applying This at the Project Level
For new builds, the right time to define the color system is before any components are designed, not after. Three to four hours building the token architecture prevents three to four weeks of inconsistency auditing later.
For existing projects, the practical approach is to audit the current color usage, identify patterns, and replace ad hoc hex values with tokens over one or two refactoring sprints. The full system does not need to be in place before the refactor starts, but the semantic token structure should be defined first.
137Foundry works with clients on web design and front-end architecture decisions at this level, including design system implementation for new builds and color-system audits for existing sites with accumulated inconsistency. The web development service covers the full spectrum from design-system architecture to production front-end implementation.

Photo by Aedrian Salazar on Pexels
The System Payoff
A well-built color system pays dividends every time a designer adds a new component, every time a developer implements a spec without guessing, and every time an accessibility review finds no issues because the issues were designed out. The upfront investment is modest. The ongoing cost of not having it, in inconsistency, accessibility debt, and rebranding friction, tends to accumulate faster than most teams expect.
Start with the token layer, validate contrast at that layer, and build components that reference tokens rather than values. The rest of the system follows from those three decisions.
One more thing worth noting: color systems require maintenance. As products grow, teams add new sections, partner with new brands on co-marketed features, or support white-label variants. A token-based system accommodates these additions cleanly. A palette-based approach means renegotiating the color rules from scratch each time. The initial investment in structure compounds into long-term flexibility that teams consistently underestimate until they need it.