Using CSS variables with AntD Design system
Tags: react, javascript, webdev, designsystem
Published: 2023-10-30 14:00:00

Ant Design offers a CSS-in-JS approach for styling your custom components. But how can developers use its Design System tokens in CSS or preprocessors (SASS, LESS)?
---
Let’s take a look at a small example with a set of panels with buttons in a row.

```javascript
export const Test: React.FC = () => (
  <div className="app">
    {map(range(4), (index) => (
      <div
        key={index}
        className='container'
      >
        <Button type='primary'>
          Button {index}
        </Button>
      </div>
    ))}
  </div>
);
```

Application component is wrapped with ConfigProvider component to set a custom theme.

```javascript
const App: React.FC = () => (
  <ConfigProvider theme={customTheme}>
    <CssTokenBridge />
    <Test />
  </ConfigProvider>
);
```

CssTokenBridge is a component for passing token values to CSS variables:

```javascript
import { theme } from 'antd';
import { forEach, includes, isNumber, isString, kebabCase } from 'lodash';
import React from 'react';

function saveToken(value, tokenName) {
  const isPrivateField = tokenName.startsWith('_');
  if (isPrivateField) return;
  const variableName = `--antd-${kebabCase(tokenName)}`;
  if (isString(value)) document.documentElement.style.setProperty(variableName, value);
  if (isNumber(value)) {
    const propertyValue = isPureNumberProperty(tokenName) ? value : `${value}px`;
    document.documentElement.style.setProperty(variableName, propertyValue);
  }
}

const isPureNumberProperty = (tokenName) =>
  includes(tokenName, 'zIndex') ||
  includes(tokenName, 'Weight') ||
  includes(tokenName, 'motion') ||
  includes(tokenName, 'opacity') ||
  includes(tokenName, 'lineHeight');

export const CssTokenBridge = () => {
  const { token } = theme.useToken();
  React.useLayoutEffect(() => {
    forEach(token, saveToken);
  }, [ token ]);

  return null;
};
```

I use kebab case names for CSS variable names and add an antd prefix. So, the colorPrimary token is saved to the — antd-color-primary CSS variable. There is a workaround for defining units of token value. Most of the numeric tokens use px units, but some of them should be just numbers (like zIndex, lineHeight). The isPureNumberProperty function should return true for such tokens.

Now, we can easily use Design System token values without hard-coded values in styles.

```javascript
.app {
  display: flex;
  gap: var(--antd-padding-content-horizontal);

  .container {
    background-color: var(--antd-green-1);
    border-radius: var(--antd-border-radius);
    box-shadow: var(--antd-box-shadow);
    padding: var(--antd-padding);
  }
}
```

![Rendered layout](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2m6wbnzur4s8k1apkf92.png)

One of the advantages of this approach is that it’s easy to supervise design. Token name and its value is available in DevTools.

![DevTools with CSS variables in styles](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/173hp6nxrx5uw0ctz2k3.png)

Hope the next versions of AntD will support css variables for dynamically changed custom themes out of the box.

Repository [https://github.com/mvp-v/antd-css-vars](https://github.com/mvp-v/antd-css-vars)