element-plus源码解读3——【scss】颜色系统完整流程

一、基础颜色定义(源头)

位置:packages/theme-chalk/src/common/var.scss

scss知识点:$ 表示变量

  • 定义所有颜色的基础值
js 复制代码
// types
$types: primary, success, warning, danger, error, info;

// Color
$colors: () !default;
$colors: map.deep-merge(
  (
    'white': #ffffff,
    'black': #000000,
    'primary': (
      'base': #409eff,
    ),
    'success': (
      'base': #67c23a,
    ),
    'warning': (
      'base': #e6a23c,
    ),
    'danger': (
      'base': #f56c6c,
    ),
    'error': (
      'base': #f56c6c,
    ),
    'info': (
      'base': #909399,
    ),
  ),
  $colors
);

二、自动生成颜色变体(light/dark)

位置:packages/theme-chalk/src/common/var.scss

定义一个mixin: set-color-mix-level:

将基础颜色与白色或黑色混合

生成指定级别的浅色或深色变体

将生成的颜色添加到$colors map中

js 复制代码
// mix colors with white/black to generate light/dark level
@mixin set-color-mix-level(
  $type,
  $number,
  $mode: 'light',
  $mix-color: $color-white
) {
  $colors: map.deep-merge(
    (
      $type: (
        /**
        * roundColor:将颜色的 RGB 通道值四舍五入为整数,并返回 rgba() 格式的颜色。
        * color.mix:混合颜色 第一个参数和第二个参数按照第三个参数的百分比混合颜色
        * map.get($colors, $type, 'base'):获取颜色
        * math.percentage(math.div($number, 10)):将数字转换为百分比
        */
          '#{$mode}-#{$number}': roundColor(
            color.mix(
              $mix-color,
              map.get($colors, $type, 'base'),
              math.percentage(math.div($number, 10))
            )
          ),
      ),
    ),
    $colors
  ) !global;
}

使用

js 复制代码
// $colors.primary.light-i
// --el-color-primary-light-i
// 10% 53a8ff
// 20% 66b1ff
// 30% 79bbff
// 40% 8cc5ff
// 50% a0cfff
// 60% b3d8ff
// 70% c6e2ff
// 80% d9ecff
// 90% ecf5ff

// 外层循环:遍历颜色类型
@each $type in $types {
  // 内层循环:生成 1-9 的变体
  @for $i from 1 through 9 {
    // 调用 mixin 生成不同亮度的颜色
    @include set-color-mix-level($type, $i, 'light', $color-white);
  }
}


// 第 1 轮:$type = primary
@include set-color-mix-level(primary, 1, 'light', $color-white);  // 生成 primary-light-1
@include set-color-mix-level(primary, 2, 'light', $color-white);  // 生成 primary-light-2
@include set-color-mix-level(primary, 3, 'light', $color-white);  // 生成 primary-light-3
// ... 继续到 9

// 第 2 轮:$type = success
@include set-color-mix-level(success, 1, 'light', $color-white);   // 生成 success-light-1
@include set-color-mix-level(success, 2, 'light', $color-white);   // 生成 success-light-2
// ... 继续到 9

// 依此类推,直到遍历完所有 6 种类型

三、生成 CSS 变量(全局)

位置:packages/theme-chalk/src/var.scss

js 复制代码
// join var name
// joinVarName(('button', 'text-color')) => '--el-button-text-color'
// 将$list遍历中间用-拼接每一个$item
@function joinVarName($list) {
  $name: '--' + config.$namespace;
  @each $item in $list {
    @if $item != '' {
      $name: $name + '-' + $item;
    }
  }
  @return $name;
}

===============================================================

// set css var value, because we need translate value to string
// for example:
// @include set-css-var-value(('color', 'primary'), red);
// --el-color-primary: red;
// 返回 变量名:颜色值 的形式
@mixin set-css-var-value($name, $value) {
  #{joinVarName($name)}: #{$value};
}

================================================================

@mixin set-css-color-type($colors, $type) {
  // 生成基础颜色变量
  @include set-css-var-value(('color', $type), map.get($colors, $type, 'base'));
  // 结果:--el-color-primary: #409eff;

  // 生成浅色变量(3, 5, 7, 8, 9)
  @each $i in (3, 5, 7, 8, 9) {
    @include set-css-var-value(
      ('color', $type, 'light', $i),
      map.get($colors, $type, 'light-#{$i}')
    );
  }
  // 结果:
  // --el-color-primary-light-3: #79bbff;
  // --el-color-primary-light-5: #a0cfff;
  // --el-color-primary-light-7: #c6e2ff;
  // --el-color-primary-light-8: #d9ecff;
  // --el-color-primary-light-9: #ecf5ff;

  // 生成深色变量
  @include set-css-var-value(
    ('color', $type, 'dark-2'),
    map.get($colors, $type, 'dark-2')
  );
  // 结果:--el-color-primary-dark-2: ...;
}

=================================================================

:root {
  color-scheme: light;

  // --el-color-#{$type}
  // --el-color-#{$type}-light-{$i}
  @each $type in (primary, success, warning, danger, error, info) {
    @include set-css-color-type($colors, $type);
  }
  
生成的css变量,以primary为例子
:root {
  --el-color-primary: #409eff;
  --el-color-primary-light-3: #79bbff;
  --el-color-primary-light-5: #a0cfff;
  --el-color-primary-light-7: #c6e2ff;
  --el-color-primary-light-8: #d9ecff;
  --el-color-primary-light-9: #ecf5ff;
  --el-color-primary-dark-2: #337ecc;
}

四、组件级使用------以el-button为例子

位置:packages/theme-chalk/src/button.scss

js 复制代码
// generate css var from existing css var
// for example:
// @include css-var-from-global(('button', 'text-color'), ('color', $type))
// --el-button-text-color: var(--el-color-#{$type});
@mixin css-var-from-global($var, $gVar) {
  $varName: joinVarName($var);
  $gVarName: joinVarName($gVar);
  #{$varName}: var(#{$gVarName});
}

==========================================================

$button-color-types 是一个 SCSS 变量(map),定义在 button-variant mixin 内部。它是一个嵌套的 map,用于定义按钮在不同状态下的颜色映射关系。

$button-color-types: (
    '': (
      'text-color': (
        'color',
        'white',
      ),
      'bg-color': (
        'color',
        $type,
      ),
      'border-color': (
        'color',
        $type,
      ),
      'outline-color': (
        'color',
        $type,
        'light-5',
      ),
      'active-color': (
        'color',
        $type,
        'dark-2',
      ),
    ),
    'hover': (
      'text-color': (
        'color',
        'white',
      ),
      'link-text-color': (
        'color',
        $type,
        'light-5',
      ),
      'bg-color': (
        'color',
        $type,
        'light-3',
      ),
      'border-color': (
        'color',
        $type,
        'light-3',
      ),
    ),
    'active': (
      'bg-color': (
        'color',
        $type,
        'dark-2',
      ),
      'border-color': (
        'color',
        $type,
        'dark-2',
      ),
    ),
    'disabled': (
      'text-color': (
        'color',
        'white',
      ),
      'bg-color': (
        'color',
        $type,
        'light-5',
      ),
      'border-color': (
        'color',
        $type,
        'light-5',
      ),
    ),
  );
  
  // 结构层次
  $button-color-types (第一层:状态)
  ├─ '' (默认状态)
  │   ├─ 'text-color' → ('color', 'white')
  │   ├─ 'bg-color' → ('color', $type)
  │   ├─ 'border-color' → ('color', $type)
  │   ├─ 'outline-color' → ('color', $type, 'light-5')
  │   └─ 'active-color' → ('color', $type, 'dark-2')
  │
  ├─ 'hover' (悬停状态)
  │   ├─ 'text-color' → ('color', 'white')
  │   ├─ 'link-text-color' → ('color', $type, 'light-5')
  │   ├─ 'bg-color' → ('color', $type, 'light-3')
  │   └─ 'border-color' → ('color', $type, 'light-3')
  │
  ├─ 'active' (激活状态)
  │   ├─ 'bg-color' → ('color', $type, 'dark-2')
  │   └─ 'border-color' → ('color', $type, 'dark-2')
  │
  └─ 'disabled' (禁用状态)
      ├─ 'text-color' → ('color', 'white')
      ├─ 'bg-color' → ('color', $type, 'light-5')
      └─ 'border-color' → ('color', $type, 'light-5')

================================================================

  @each $type, $typeMap in $button-color-types {
    // 内层循环,遍历第二层(属性):text-color, bg-color, border-color, outline-color, active-color
    // $typeColor:属性名,例如:'text-color'
    // $list:属性值,例如:('color', 'white')
    @each $typeColor, $list in $typeMap {
      // 调用 css-var-from-global 生成 CSS 变量
      // 例如:@include css-var-from-global(('button', 'hover', 'text-color'), ('color', 'white'));
      @include css-var-from-global(('button', $type, $typeColor), $list);
    }
  }
  
 // 当 $type = 'primary' 时,会生成:
 /* 默认状态 */
--el-button-text-color: var(--el-color-white);
--el-button-bg-color: var(--el-color-primary);
--el-button-border-color: var(--el-color-primary);
--el-button-outline-color: var(--el-color-primary-light-5);
--el-button-active-color: var(--el-color-primary-dark-2);

/* 悬停状态 */
--el-button-hover-text-color: var(--el-color-white);
--el-button-hover-link-text-color: var(--el-color-primary-light-5);
--el-button-hover-bg-color: var(--el-color-primary-light-3);
--el-button-hover-border-color: var(--el-color-primary-light-3);

/* 激活状态 */
--el-button-active-bg-color: var(--el-color-primary-dark-2);
--el-button-active-border-color: var(--el-color-primary-dark-2);

/* 禁用状态 */
--el-button-disabled-text-color: var(--el-color-white);
--el-button-disabled-bg-color: var(--el-color-primary-light-5);
--el-button-disabled-border-color: var(--el-color-primary-light-5);

=====================================================================

完整源码:
@mixin button-variant($type) {
  $button-color-types: (
    '': (
      'text-color': (
        'color',
        'white',
      ),
      'bg-color': (
        'color',
        $type,
      ),
      'border-color': (
        'color',
        $type,
      ),
      'outline-color': (
        'color',
        $type,
        'light-5',
      ),
      'active-color': (
        'color',
        $type,
        'dark-2',
      ),
    ),
    'hover': (
      'text-color': (
        'color',
        'white',
      ),
      'link-text-color': (
        'color',
        $type,
        'light-5',
      ),
      'bg-color': (
        'color',
        $type,
        'light-3',
      ),
      'border-color': (
        'color',
        $type,
        'light-3',
      ),
    ),
    'active': (
      'bg-color': (
        'color',
        $type,
        'dark-2',
      ),
      'border-color': (
        'color',
        $type,
        'dark-2',
      ),
    ),
    'disabled': (
      'text-color': (
        'color',
        'white',
      ),
      'bg-color': (
        'color',
        $type,
        'light-5',
      ),
      'border-color': (
        'color',
        $type,
        'light-5',
      ),
    ),
  );

  // 外层循环,遍历第一层(状态):'', 'hover', 'active', 'disabled'
  // $type状态名例如'hover';
  // $typeMap:状态对应的属性值,例如:('text-color': (
  //   'color',
  //   'white',
  // ),)
  @each $type, $typeMap in $button-color-types {
    // 内层循环,遍历第二层(属性):text-color, bg-color, border-color, outline-color, active-color
    // $typeColor:属性名,例如:'text-color'
    // $list:属性值,例如:('color', 'white')
    @each $typeColor, $list in $typeMap {
      // 调用 css-var-from-global 生成 CSS 变量
      // 例如:@include css-var-from-global(('button', 'hover', 'text-color'), ('color', 'white'));
      @include css-var-from-global(('button', $type, $typeColor), $list);
    }
  }

  &.is-plain,
  &.is-text,
  &.is-link {
    @include button-plain($type);
  }
}

五、应用样式(最终渲染)

位置:packages/theme-chalk/src/button.scss

js 复制代码
@include b(button) {
  display: inline-flex;
  justify-content: center;
  align-items: center;

  line-height: 1;
  // min-height will expand when in flex
  height: map.get($input-height, 'default');
  white-space: nowrap;
  cursor: pointer;
  color: getCssVar('button', 'text-color');
  text-align: center;
  box-sizing: border-box;
  outline: none;
  transition: 0.1s;
  font-weight: getCssVar('button', 'font-weight');
  user-select: none;
  vertical-align: middle;
  -webkit-appearance: none;
  background-color: getCssVar('button', 'bg-color');
  border: getCssVar('border');
  border-color: getCssVar('button', 'border-color');

  &:hover {
    color: getCssVar('button', 'hover', 'text-color');
    border-color: getCssVar('button', 'hover', 'border-color');
    background-color: getCssVar('button', 'hover', 'bg-color');
    outline: none;
  }
 
 // 这些生成el-button的基础变量
  ======================================================
 
   @each $type in (primary, success, warning, danger, info) {
    @include m($type) {
      @include button-variant($type);
    }
  }

  
  // $type = 'primary' 时,最终编译后的 CSS(.el-button--primary):
  .el-button--primary {
  /* 基础样式 */
  display: inline-flex;
  justify-content: center;
  align-items: center;
  
  /* 颜色(使用 CSS 变量) */
  color: var(--el-button-text-color);
  /* ↓ 展开为 */
  color: var(--el-color-white);  /* #ffffff */
  
  background-color: var(--el-button-bg-color);
  /* ↓ 展开为 */
  background-color: var(--el-color-primary);  /* #409eff */
  
  border-color: var(--el-button-border-color);
  /* ↓ 展开为 */
  border-color: var(--el-color-primary);  /* #409eff */
}

.el-button--primary:hover {
  background-color: var(--el-button-hover-bg-color);
  /* ↓ 展开为 */
  background-color: var(--el-color-primary-light-3);  /* #79bbff */
  
  border-color: var(--el-button-hover-border-color);
  /* ↓ 展开为 */
  border-color: var(--el-color-primary-light-3);  /* #79bbff */
}

.el-button--primary:active {
  background-color: var(--el-button-active-bg-color);
  /* ↓ 展开为 */
  background-color: var(--el-color-primary-dark-2);  /* #337ecc */
  
  border-color: var(--el-button-active-border-color);
  /* ↓ 展开为 */
  border-color: var(--el-color-primary-dark-2);  /* #337ecc */
}

六、完整流程图

js 复制代码
┌─────────────────────────────────────────────────────────┐
│ 阶段 1: 基础颜色定义                                     │
│ var.scss: primary.base = #409eff                       │
└─────────────────┬───────────────────────────────────────┘
                  ↓
┌─────────────────────────────────────────────────────────┐
│ 阶段 2: 自动生成颜色变体                                 │
│ set-color-mix-level()                                   │
│ - primary.light-3 = #79bbff (30% 白色混合)             │
│ - primary.dark-2 = #337ecc (20% 黑色混合)              │
└─────────────────┬───────────────────────────────────────┘
                  ↓
┌─────────────────────────────────────────────────────────┐
│ 阶段 3: 生成全局 CSS 变量                                │
│ var.scss: :root {                                       │
│   --el-color-primary: #409eff                          │
│   --el-color-primary-light-3: #79bbff                  │
│   --el-color-primary-dark-2: #337ecc                    │
│ }                                                       │
└─────────────────┬───────────────────────────────────────┘
                  ↓
┌─────────────────────────────────────────────────────────┐
│ 阶段 4: 按钮组件使用颜色                                  │
│ button-variant('primary')                                │
│ 生成组件级 CSS 变量:                                     │
│ --el-button-bg-color: var(--el-color-primary)           │
│ --el-button-hover-bg-color: var(--el-color-primary-light-3)│
│ --el-button-active-bg-color: var(--el-color-primary-dark-2)│
└─────────────────┬───────────────────────────────────────┘
                  ↓
┌─────────────────────────────────────────────────────────┐
│ 阶段 5: 最终渲染                                         │
│ .el-button--primary {                                   │
│   background-color: var(--el-button-bg-color);          │
│   /* 浏览器解析为 #409eff */                            │
│ }                                                       │
└─────────────────────────────────────────────────────────┘

七、优势

  • 统一管理:所有颜色在一个地方定义。
  • 自动计算:light/dark 变体自动生成。
  • 易于定制:修改基础色即可影响所有变体和组件。
  • 性能:使用 CSS 变量,支持运行时动态切换主题。
相关推荐
n***F8759 小时前
Vue项目中 安装及使用Sass(scss)
vue.js·sass·scss
q***04054 天前
Vue项目中 安装及使用Sass(scss)
vue.js·sass·scss
Lsx_5 天前
🔥Vite+ElementPlus 自动按需加载与主题定制原理全解析
前端·javascript·element
yqcoder8 天前
在 scss 中,&>div 作用
前端·css·scss
小马哥编程8 天前
这个variables.scss文件中$menuText:#bfcbd9;:export {menuText: $menuText; }的语法符合要求吗
前端·css·scss
天外来物12 天前
element-plus主题配置及动态切换主题
前端·css·element
百***680414 天前
Vue项目中 安装及使用Sass(scss)
vue.js·sass·scss
LXA080915 天前
在Vue 3项目中配置和使用SCSS
vue.js·rust·scss
thomaswade16 天前
SCSS新手教学(知识点概览)
scss