Color Arrays in Pure CSS
CSS has evolved well beyond simple property-value pairs. With variables, math functions, and new pseudo-selectors, we can now do things that were once reserved for preprocessors or JavaScript. One such technique: defining a comma-separated list of colors in a single variable and then using an index to pick a value from that list — essentially, an array.
This isn't a new CSS feature. It's built from existing parts: variables, color-mix(), gradients, and a bit of creative thinking. Let's walk through the implementation, starting with the simplest case.
Two Colors, One Variable
Start by storing two colors in a variable. Then, use color-mix() in an unconventional way — not to blend the two colors together, but to return one of them based on an index.
--colors: black, white;
--i: 0;
The trick is to mix the two colors in unequal proportions. When the index is 0, you mix 100% of the first color with 0% of the second. The result is the first color. When the index is 1, you flip those percentages, and you get the second color.
background: color-mix(in hsl, var(--colors) calc(var(--i) * 100%), white);
Let's trace the logic. If --i: 0, the expression becomes:
color-mix(in hsl, black 0%, white)
That yields white. If --i: 1, it becomes:
color-mix(in hsl, black 100%, white)
And that yields black. The hsl argument in color-mix() is the interpolation method. It's required, but since we aren't truly mixing colors, any method — srgb, oklch, oklab — works fine.
This pattern is useful for a dark/light mode switch. Define both colors in the variable, then toggle the index. The browser handles the rest.
From Two Colors to N Colors
The two-color approach is limited. For a longer array, a different method using linear gradients is more practical.
Define the array and its length as variables:
--colors: red, orange, yellow, green, blue, purple;
--n: 6;
--i: 0;
Then, build a linear gradient from these colors and give it a very large size.
background: linear-gradient(var(--colors)) no-repeat;
background-size: 100% calc(1px * infinity); /* A very large height */
background-position: 0 0;
A standard gradient smoothly transitions between colors. By increasing the gradient's height dramatically — to infinity — you effectively zoom into the gradient. The element only shows the portion near the top, which is the first color in the array.
To pick a different color, shift the gradient's vertical position. The index selects the color:
background-position: 0 calc(var(--i) * -1px * infinity);
With --i: 0, you'll see the top of the gradient. The following complete code renders a box that cycles through the colors as you change --i:
.box {
--colors: red, orange, yellow, green, blue, purple;
--n: 6;
--i: 0;
background: linear-gradient(var(--colors)) no-repeat;
background-size: 100% calc(1px * infinity);
background-position: 0 calc(var(--i) * -1px * infinity);
}
Note: theno-repeatkeyword was added to thebackgroundshorthand. It seems necessary here, as browsers might not correctly handle infinite-size gradients with repetition.
Unbounded Index with a Conic Gradient
The linear-gradient method requires the index to stay within the array bounds (from 0 to N-1). Any value outside that range returns no color. To allow any integer index — even negative stops — we can swap the linear gradient for a conical one.
A conic gradient circles the element. By defining the gradient with the array colors and then re-adding the first color at the end, you create a seamless loop:
--colors: red, orange, yellow, green, blue, purple, red; /* First color repeated */
--n: 6;
The gradient is then scaled so that only a small portion near the top is visible.
background: conic-gradient(from 0deg, var(--colors));
background-size: calc(1px * infinity) calc(1px * infinity);
background-position: 50% 0;
With this setup, the element shows only the color at the 12 o'clock position of the wheel. Rotating the wheel via background-position or transform changes the visible color. Because it's a circle, you can spin it any number of times in either direction and always land on a valid color. The index maps directly to a rotation angle.
This effectively gives you modulo arithmetic. For instance, an index of 21 with an array of six colors behaves like 21 mod 6 = 3, selecting the fourth color. A negative index like -5 behaves like -5 mod 6 = 1, selecting the second color.
A Future Alternative with mod()
CSS now has a native mod() function. If and when browser support matures, the linear-gradient method can be simplified by applying modulo directly to the index:
--index: mod(var(--i), var(--n));
background-position: 0 calc(var(--index) * -1px * infinity);
This is a more straightforward expression of the same idea. For now, it serves as a preview of what's possible once mod() is widely available.
Practical Limitations
This is a clever hack, but it has constraints. Most importantly, it only works with the background property and its related sub-properties. You can't directly use these color arrays for text, borders, or other properties. There are workarounds, like clipping the background to text with background-clip: text, but those are cosmetic and limited as well.
Performance is another concern, especially with the infinity sizing trick. Forcing the browser to handle gradients of "infinite" dimensions could lead to rendering lag or unexpected behavior in some environments. If that happens, stick to the two-color color-mix() method, which is safe and doesn't rely on any extreme values.
Despite the caveats, this experiment shows the flexibility of modern CSS. With variables, math, and gradients, you can emulate data structures — and even a programming language feature like an array — directly in style sheets. The whole technique boils down to a few variables and a background declaration.



