Skip to content

CSS Color Transforms

Theemo supports color transformations, either at build time or at runtime by generating CSS color transform functions.

Uses (provided by registering theemo extensions):

WARNING

The output is using hsl() function. Meanwhile DTCG has added support for mulitiple color functions in the Design Tokens Color Module. The implementation in Theemo landed earlier and does not support the new format yet.

See Issue #1434

Input

These tokens may have color schemes as feature constraints or regular references.

json
{
  "intent": {
    "action": {
      "base": {
        "background": {
          "$value": "{palette.brand.500}",
          "$description": "",
          "$type": "color"
        },
        "border": {
          "$value": "{intent.action.base.background}",
          "$description": "",
          "$type": "color"
        },
        "text": {
          "$value": "{palette.letters.brand-highlight}",
          "$description": "",
          "$type": "color"
        }
      },
      "disabled": {
        "background": {
          "$value": {
            "value": "{intent.action.base.background}",
            "transforms": {
              "alpha": -35
            }
          },
          "$description": "",
          "$type": "color"
        },
        "border": {
          "$value": {
            "value": "{intent.action.base.border}",
            "transforms": {
              "alpha": -35
            }
          },
          "$description": "",
          "$type": "color"
        },
        "text": {
          "$value": {
            "value": "{intent.action.base.text}",
            "transforms": {
              "alpha": -35
            }
          },
          "$description": "",
          "$type": "color"
        }
      }
    }
  }
}
json
{
  "palette": {
    "brand": {
      "red": {
        "$value": "#e41b1b",
        "$description": "",
        "$type": "color"
      },
      "400": {
        "$value": {
          "value": "{palette.brand.500}",
          "transforms": {
            "lightness": -10
          }
        },
        "$description": "",
        "$type": "color"
      },
      "500": {
        "$value": "{palette.brand.red}",
        "$description": "",
        "$type": "color"
      },
      "600": {
        "$value": {
          "value": "{palette.brand.500}",
          "transforms": {
            "lightness": 10
          }
        },
        "$description": "",
        "$type": "color"
      }
    }
  }
}

Configuration

In addition to the config used at [CSS custom properties & CSS light-dark()] token conversions, this will add the useCSSColorTransform flag to tell the color/transforms to output using CSS color functions.

config.js
js
import 
StyleDictionary
from 'style-dictionary';
import {
isConstrainedByPlatform
,
isConstrainedToken
,
isCSSProperty
,
matchesConstraints
,
registerTheemo
} from '@theemo/style-dictionary';
registerTheemo
(
StyleDictionary
);
/** @type import("style-dictionary/types").Config */ export default {
source
: ['tokens/**/*.json'],
preprocessors
: ['theemo/token'],
platforms
: {
css
: {
transformGroup
: 'theemo',
buildPath
: 'build/',
options
: {
outputReferences
: true,
showFileHeader
: false,
useCSSColorTransform
: true
},
files
: [
{
format
: 'css/properties',
destination
: 'properties.css',
filter
:
isCSSProperty
}, {
format
: 'css/variables',
destination
: 'vars.css',
filter
: (
token
) =>
// when no CSS property !
isCSSProperty
(
token
) &&
// and matches color-scheme constraint (
matchesConstraints
(
token
, {
features
: {
'color-scheme': ['light', 'dark'] } }) || // but no other constraint !
isConstrainedToken
(
token
))
} ] } } };

Output

useCSSColorTransform: true

When setting useCSSColorTransform: true it will output CSS using color functions.

vars.css
css
:root {
  --palette-brand-500: var(--palette-brand-red);
  --palette-brand-100: hsl(from var(--palette-brand-500) h s calc(l - 40));
  --palette-brand-200: hsl(from var(--palette-brand-500) h s calc(l - 30));
  --palette-brand-300: hsl(from var(--palette-brand-500) h s calc(l - 20));
  --palette-brand-400: hsl(from var(--palette-brand-500) h s calc(l - 10));
  --palette-brand-600: hsl(from var(--palette-brand-500) h s calc(l + 10));
  --palette-brand-700: hsl(from var(--palette-brand-500) h s calc(l + 20));
  --palette-brand-800: hsl(from var(--palette-brand-500) h s calc(l + 30));
  --palette-brand-900: hsl(from var(--palette-brand-500) h s calc(l + 40));
  --intent-action-base-text: var(--palette-letters-brand-highlight);
  --intent-action-base-background: var(--palette-brand-500);
  --intent-action-base-border: var(--intent-action-base-background);
  --intent-action-disabled-text: hsl(from var(--intent-action-base-text) h s l / 0.65);
  --intent-action-disabled-background: hsl(from var(--intent-action-base-background) h s l / 0.65);
  --intent-action-disabled-border: hsl(from var(--intent-action-base-border) h s l / 0.65);
  /* ... more tokens ... */
}

useCSSColorTransform: false

When setting useCSSColorTransform: false the color values are transformed at build time. Use this, when your browser support doesn't include CSS color functions yet.

vars.css
css
:root {
  --palette-brand-500: var(--palette-brand-red);
  --palette-brand-100: #2e0505;
  --palette-brand-200: #5b0b0b;
  --palette-brand-300: #891010;
  --palette-brand-400: #b61616;
  --palette-brand-600: #e94949;
  --palette-brand-700: #ef7676;
  --palette-brand-800: #f4a4a4;
  --palette-brand-900: #fad1d1;
  --intent-action-base-text: var(--palette-letters-brand-highlight);
  --intent-action-base-background: var(--palette-brand-500);
  --intent-action-base-border: var(--intent-action-base-background);
  --intent-action-disabled-text: rgba(255, 236, 64, 0.65);
  --intent-action-disabled-background: rgba(228, 27, 27, 0.65);
  --intent-action-disabled-border: rgba(228, 27, 27, 0.65);
  /* ... more tokens ... */
}