/react-dynamic-theme

CRA+Antd实现动态主题切换

Primary LanguageJavaScript

React + Sass + Antd 动态主题配置

使用 CRA + Sass + Antd 搭建 React 项目,并基于动态 Sass 变量形式实现动态主题切换;

实现方式

创建 src/var.scss 文件,定义不同主题变量:

$themeList: (
  light: (
    bg: #fff,
    fontColor: #333,
    primaryColor: blue,
    inputSize: 32px,
    shadow: blue red 10px 10px,
  ),
  dark: (
    bg: #333,
    fontColor: #fff,
    primaryColor: rgb(224, 41, 41),
    inputSize: 46px,
    shadow: blue red 10px 10px,
  ),
);

通过 themeify mixin 函数,将所有变量抽离成 global 变量:

@mixin themei {
  @each $theme-name, $theme-map in $themeList {
    // 把局部变量强升为全局变量
    $theme-map: $theme-map !global;
    // #{}是sass的插值表达式
    [data-theme='theme-#{$theme-name}'] & {
      // @content是混合器插槽
      @content;
    }
  }
}

抽离公共 mixin 函数,使用主题变量:

// 定义参数默认值
$value: null !default;

/**
* $property { string } css属性值,类似:border、color、background-color
* $key { string } 在themeList中定义的变量key值
* $left { string } null css属性值,补充到变量前
* $right { string } null css属性值,补充到变量后
*/
@mixin s($property, $key, $left: $value, $right: $value) {
  @if $left && $right {
    @include themeify {
      #{$property}: $left map-get($theme-map, $key) $right;
    }
  } @else if $left && $left != null {
    @include themeify {
      #{$property}: $left map-get($theme-map, $key, $left);
    }
  } @else if $right && $right != null {
    @include themeify {
      #{$property}: map-get($theme-map, $key) $right;
    }
  } @else {
    @include themeify {
      #{$property}: $left map-get($theme-map, $key) $right;
    }
  }
}

如上所述,所有的动态 scss 变量和使用函数就定义完成了,接下来我们看看如何使用。

如何使用

在组件根目录使用:

const THEME = {
  one: 'dark',
  two: 'light',
};

function App() {
  const [theme, setTheme] = React.useState(THEME.one);

  const themeStyle =
    theme === THEME.one ? `theme-${THEME.one}` : `theme-${THEME.two}`;

  <div className="App" data-theme="theme-light">
    <header className="App-header">
      <div className="box">
        <img src={logo} className="App-logo" alt="logo" />
      </div>

      <div className="box">
        <XYButton
          type="primary"
          onClick={() => {
            setTheme(theme === THEME.one ? THEME.two : THEME.one);
          }}
        >
          切换主题
        </XYButton>
      </div>
    </header>
  </div>;
}

通过在根节点定义 data-theme 自定义属性,动态设置 "theme-light" 或者 "theme-dark" 即可实现主体切换;

在样式中使用:

// 引入var变量函数
@import './var.scss';

.App-header {
  // 使用s mixin函数,定义color样式
  @include s('color', 'fontColor');
  background-size: 30px !important;
  // 使用s mixin函数,定义background样式
  @include s(
    'background',
    'bg',
    null,
    url('./flower.jpg') center center no-repeat
  );
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: calc(10px + 2vmin);
}

详细动态设置 antd 组件样式参见:scr/componetes/Button/index.scss 或者 scr/componetes/Input/index.scss