KonradSzwarc
2/24/2020 - 2:30 PM

Ant Design LESS theme generator

Script that generates ant design theme overwtires based on the configuration object.

import { writeFileSync } from 'fs';
import { resolve } from 'path';

import { theme } from './theme';

const lessVariablesOverwrite = `${`
module.exports = {
  '@primary-color': '${theme.colors.primary.main}',
  '@info-color': '${theme.colors.info.main}',
  '@success-color': '${theme.colors.success.main}',
  '@error-color': '${theme.colors.error.main}',
  '@warning-color': '${theme.colors.warning.main}',
  '@white': '${theme.colors.common.white}',
  '@black': '${theme.colors.common.black}',
  '@shadow-color': '${theme.colors.shadow}',

  '@primary-1': '${theme.colors.primary.background}',
  '@primary-3': '${theme.colors.primary.border}',
  '@primary-5': '${theme.colors.primary.light}',
  '@primary-6': '${theme.colors.primary.main}',
  '@primary-7': '${theme.colors.primary.dark}',

  '@heading-color': '${theme.colors.text.heading}',
  '@text-color': '${theme.colors.text.primary}',
  '@text-color-secondary': '${theme.colors.text.secondary}',
  '@disabled-color': '${theme.colors.text.disabled}',
  '@border-color-base': '${theme.colors.border.base}',
  '@border-color-split': '${theme.colors.border.split}',
  '@background-color-light': '${theme.colors.background.light}',
  '@background-color-base': '${theme.colors.background.base}',

  '@alert-success-border-color': '${theme.colors.success.border}',
  '@alert-success-bg-color': '${theme.colors.success.background}',
  '@alert-info-border-color': '${theme.colors.info.border}',
  '@alert-info-bg-color': '${theme.colors.info.background}',
  '@alert-warning-border-color': '${theme.colors.warning.border}',
  '@alert-warning-bg-color': '${theme.colors.warning.background}',
  '@alert-error-border-color': '${theme.colors.error.border}',
  '@alert-error-bg-color': '${theme.colors.error.background}',

  '@font-family':
    '${theme.fonts.fontFamily}',
  '@code-family': '${theme.fonts.codeFamily}',

  '@heading-1-size': '${theme.fontSizes.h1}',
  '@heading-2-size': '${theme.fontSizes.h2}',
  '@heading-3-size': '${theme.fontSizes.h3}',
  '@heading-4-size': '${theme.fontSizes.h4}',
  '@font-size-lg': '${theme.fontSizes.large}',
  '@font-size-base': '${theme.fontSizes.normal}',
  '@font-size-sm': '${theme.fontSizes.small}',

  '@card-shadow': '${theme.shadows.card}',
  '@box-shadow-base': '${theme.shadows.base}',
  '@shadow-1-up': '${theme.shadows.up}',
  '@shadow-1-down': '${theme.shadows.down}',
  '@shadow-1-left': '${theme.shadows.left}',
  '@shadow-1-right': '${theme.shadows.right}',
  '@shadow-2': '${theme.shadows.large}',
};
`.trim()}\n`;

writeFileSync(resolve(__dirname, '..', 'src', 'generated', 'customizeTheme.js'), lessVariablesOverwrite, 'utf8');

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const generate = (obj: any, path = ''): any =>
  Object.keys(obj).map(key => {
    if (typeof obj[key] === 'object') return generate(obj[key], `${key}.`);

    return path + key;
  });

const colorKeys = []
  .concat(...generate(theme.colors))
  .map(key => `'${key}'`)
  .join('\n  | ');

writeFileSync(
  resolve(__dirname, '..', 'src', 'generated', 'ColorKeys.ts'),
  `export type ColorKeys =\n  | ${colorKeys};\n`,
  'utf8',
);
const colors = {
  primary: {
    dark: '#096DD9',
    main: '#1890FF',
    light: '#40A9FF',
    border: '#91D5FF',
    background: '#E6F7FF',
  },
  text: {
    heading: 'rgba(0, 0, 0, 0.85)',
    primary: 'rgba(0, 0, 0, 0.65)',
    secondary: 'rgba(0, 0, 0, 0.45)',
    disabled: 'rgba(0, 0, 0, 0.25)',
  },
  border: {
    base: '#D9D9D9',
    split: '#E8E8E8',
  },
  background: {
    base: '#F5F5F5',
    light: '#FAFAFA',
    body: '#F0F2F5',
    component: '#FFFFFF',
  },
  error: {
    dark: '#CF1322',
    main: '#F5222D',
    light: '#FF4D4F',
    border: '#FFA39E',
    background: '#FFF1F0',
  },
  warning: {
    main: '#FAAD14',
    border: '#FFE58F',
    background: '#FFFBE6',
  },
  info: {
    main: '#13C2C2',
    border: '#87E8DE',
    background: '#E6FFFB',
  },
  success: {
    main: '#52C41A',
    border: '#B7EB8F',
    background: '#F6FFED',
  },
  common: {
    black: '#000000',
    white: '#FFFFFF',
  },
  shadow: 'rgba(0, 0, 0, 0.15)',
};

const shadows = {
  button: '0 2px 0 rgba(0, 0, 0, 0.015)',
  card: '0 2px 8px rgba(0, 0, 0, 0.09)',
  base: `0 -2px 8px ${colors.shadow}`,
  up: `0 -2px 8px ${colors.shadow}`,
  down: `0 2px 8px ${colors.shadow}`,
  left: `-2px 0 8px ${colors.shadow}`,
  right: `2px 0 8px ${colors.shadow}`,
  large: `0 4px 12px ${colors.shadow}`,
};

const fonts = {
  fontFamily:
    '-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"',
  codeFamily: '"SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace',
};

const fontSizes = {
  h1: '38px',
  h2: '30px',
  h3: '24px',
  h4: '20px',
  large: '16px',
  normal: '14px',
  small: '12px',
};

const lineHeights = {
  h1: '48px',
  h2: '40px',
  h3: '32px',
  h4: '28px',
  large: '24px',
  normal: '20px',
  small: '12px',
};

export const breakpoints = {
  xs: '480px',
  sm: '576px',
  md: '768px',
  lg: '992px',
  xl: '1200px',
  xxl: '1600px',
};

export const space = {};

export const theme = {
  space,
  colors,
  fonts,
  fontSizes,
  lineHeights,
  shadows,
  breakpoints,
};
import React from 'react';
import { ThemeProvider as EmotionThemeProvider } from 'emotion-theming';

import { theme } from './theme';

export const ThemeProvider: React.FC = ({ children }) => {
  return <EmotionThemeProvider theme={theme}>{children}</EmotionThemeProvider>;
};
import { useTheme as useEmotionTheme } from 'emotion-theming';
import { theme } from './theme';

export const useTheme = (): typeof theme => useEmotionTheme<typeof theme>();
const CracoAntDesignPlugin = require('craco-antd');
const customizeTheme = require('./src/generated/customizeTheme');

module.exports = {
  plugins: [
    { plugin: CracoAntDesignPlugin, options: { customizeTheme } },
  ],
  babel: {
    plugins: ['emotion'],
  },
};