How we use CSS custom properties to customize your newsletter archive

With Mailcoach, you can create a customized archive website for your campaigns. To blend your website with the style of your newsletters, we support setting a primary color and theme. With CSS custom properties, we’re able to modify colors, typography, and more on the fly.


We wanted to control multiple colors (text color, background color…) with minimal configuration, so we decided to derive a palette from one primary color.

Colors are often defined in RGB, but RGB is difficult to transform without custom functions. We ask the user to define the primary color in HSL instead. The benefit of HSL is that it’s easy to control a color by tweaking the parameters.

HSL stands for hue, saturation lightness. Hue (0–360) defines the base color. By modifying the hue, you can go from red, to blue, to green… Saturation (0–100%) controls the strength of the color. A low saturation will err toward gray, a high saturation will generate a vibrant color. Lightness (0–100%) controls whether it should be light or dark.

A straightforward way to generate a uniform color palette with HSL is to keep the hue in place, and tweak the saturation and lightness. With CSS custom properties, we define each parameter individually, and rebuilt the color with hsl(…). We then generate a set of derived colors with predetermined saturation and lightness values.

Here are our base values:

:root {
    /* Primary color parameters */
    --primary-hue: 236;
    --primary-saturation: 100%;
    --primary-lightness: 50%;

    /* Derived colors */
    --primary-color: hsl(var(--primary-hue), var(--primary-saturation), var(--primary-lightness));
    --text-color: hsl(var(--primary-hue), 30%, 10%);
    --light-text-color: hsl(var(--primary-hue), 30%, 70%);
    --border-color: hsl(var(--primary-hue), 30%, 90%);
    --background-color: hsl(var(--primary-hue), 40%, 98%);
    --card-header-color: hsl(var(--primary-hue), 40%, 96%);
    --shadow-color: hsla(var(--primary-hue), 40%, 90%, 0.3);

For shadows, we use hsla(…), which also controls opacity.

Next, we created a template file to inject a set of custom properties in the layout, which overrides the values with the user’s preferred color.

:root {
    --primary-hue: {{ $color['hue'] }};
    --primary-saturation: {{ $color['saturation'] }};
    --primary-lightness: {{ $color['lightness'] }};


For other theme values, we have a set of defaults configured for properties like font-family, border-radius, padding…

:root {
    --font-family: system-ui, sans-serif;
    --font-size: 18px;
    --border-radius: 0.375rem;
    --spacing: 4rem;
    --padding: 4rem;
    --box-shadow: 0 10px 15px -3px var(--shadow-color), 0 4px 6px -4px var(--shadow-color);

Each theme has its own template file that overrides the base CSS variables. Here’s what the default and typewriter themes look like:

<!-- themes/default.blade.php -->
    @import url('');
    :root {
        --font-family: 'Inter', sans-serif;
        --font-size: 16px;
        --border-radius: 3px;

<!-- themes/typewriter.blade.php -->
    :root {
        --font-family: 'Courier';
        --font-size: 16px;
        --border-radius: 0px;
        --box-shadow: none;

By using plain <style> blocks, we have the possibility to do more than set properties, like importing a webfont when needed.

CSS custom properties have been a great addition to the language. Especially with more advanced capabilities like creating colors, they open up new possibilities to customize your style on the fly.

Ready to get started?