Procházet zdrojové kódy

chore: init theme (#14508)

* chore: init theme

* chore: update faas config
iamkun před 6 roky
rodič
revize
febdd3bbb4
63 změnil soubory, kde provedl 5536 přidání a 789 odebrání
  1. 3 0
      build/webpack.demo.js
  2. binární
      examples/assets/images/term-arial.png
  3. binární
      examples/assets/images/term-helvetica.png
  4. binární
      examples/assets/images/term-hiragino.png
  5. binární
      examples/assets/images/term-microsoft.png
  6. binární
      examples/assets/images/term-pingfang.png
  7. binární
      examples/assets/images/term-sf.png
  8. binární
      examples/assets/images/theme-no-config.png
  9. binární
      examples/assets/images/typography.png
  10. 18 0
      examples/color.js
  11. 29 7
      examples/components/header.vue
  12. 55 0
      examples/components/theme-configurator/docStyle.vue
  13. 76 0
      examples/components/theme-configurator/download.vue
  14. 86 0
      examples/components/theme-configurator/editor/borderRadius.vue
  15. 155 0
      examples/components/theme-configurator/editor/boxShadow.vue
  16. 8 0
      examples/components/theme-configurator/editor/color-picker/index.js
  17. 316 0
      examples/components/theme-configurator/editor/color-picker/src/color.js
  18. 132 0
      examples/components/theme-configurator/editor/color-picker/src/components/alpha-slider.vue
  19. 113 0
      examples/components/theme-configurator/editor/color-picker/src/components/color-list.vue
  20. 123 0
      examples/components/theme-configurator/editor/color-picker/src/components/hue-slider.vue
  21. 134 0
      examples/components/theme-configurator/editor/color-picker/src/components/picker-dropdown.vue
  22. 61 0
      examples/components/theme-configurator/editor/color-picker/src/components/predefine.vue
  23. 100 0
      examples/components/theme-configurator/editor/color-picker/src/components/sv-panel.vue
  24. 36 0
      examples/components/theme-configurator/editor/color-picker/src/draggable.js
  25. 190 0
      examples/components/theme-configurator/editor/color-picker/src/main.vue
  26. 89 0
      examples/components/theme-configurator/editor/color.vue
  27. 101 0
      examples/components/theme-configurator/editor/fontLineHeight.vue
  28. 101 0
      examples/components/theme-configurator/editor/fontSize.vue
  29. 106 0
      examples/components/theme-configurator/editor/fontWeight.vue
  30. 45 0
      examples/components/theme-configurator/editor/input.vue
  31. 75 0
      examples/components/theme-configurator/editor/mixin.vue
  32. 38 0
      examples/components/theme-configurator/editor/simpleText.vue
  33. 232 0
      examples/components/theme-configurator/index.vue
  34. 35 0
      examples/components/theme-configurator/loading.vue
  35. 124 0
      examples/components/theme-configurator/main.vue
  36. 6 0
      examples/components/theme-configurator/progress.js
  37. 108 0
      examples/components/theme-configurator/progress.vue
  38. 65 0
      examples/components/theme-configurator/utils/ajax.js
  39. 20 0
      examples/components/theme-configurator/utils/api.js
  40. 59 0
      examples/components/theme-configurator/utils/boxShadow.js
  41. 80 0
      examples/components/theme-configurator/utils/utils.js
  42. 181 0
      examples/docs/en-US/border.md
  43. 245 52
      examples/docs/en-US/color.md
  44. 150 119
      examples/docs/en-US/typography.md
  45. 181 0
      examples/docs/es/border.md
  46. 245 53
      examples/docs/es/color.md
  47. 150 119
      examples/docs/es/typography.md
  48. 181 0
      examples/docs/fr-FR/border.md
  49. 244 52
      examples/docs/fr-FR/color.md
  50. 151 118
      examples/docs/fr-FR/typography.md
  51. 181 0
      examples/docs/zh-CN/border.md
  52. 244 52
      examples/docs/zh-CN/color.md
  53. 152 120
      examples/docs/zh-CN/typography.md
  54. 75 0
      examples/i18n/theme-editor.json
  55. 16 0
      examples/nav.config.json
  56. 41 3
      examples/pages/template/component.tpl
  57. 16 3
      package.json
  58. 13 13
      packages/theme-chalk/src/button.scss
  59. 6 6
      packages/theme-chalk/src/checkbox.scss
  60. 134 61
      packages/theme-chalk/src/common/var.scss
  61. 1 1
      packages/theme-chalk/src/dialog.scss
  62. 7 7
      packages/theme-chalk/src/radio-button.scss
  63. 3 3
      packages/theme-chalk/src/radio.scss

+ 3 - 0
build/webpack.demo.js

@@ -172,6 +172,9 @@ const webpackConfig = {
     ]),
     new ProgressBarPlugin(),
     new VueLoaderPlugin(),
+    new webpack.DefinePlugin({
+      'process.env.FAAS_ENV': JSON.stringify(process.env.FAAS_ENV)
+    }),
     new webpack.LoaderOptionsPlugin({
       vue: {
         compilerOptions: {

binární
examples/assets/images/term-arial.png


binární
examples/assets/images/term-helvetica.png


binární
examples/assets/images/term-hiragino.png


binární
examples/assets/images/term-microsoft.png


binární
examples/assets/images/term-pingfang.png


binární
examples/assets/images/term-sf.png


binární
examples/assets/images/theme-no-config.png


binární
examples/assets/images/typography.png


+ 18 - 0
examples/color.js

@@ -0,0 +1,18 @@
+export const tintColor = (c, tint) => {
+  const color = c.replace('#', '');
+  let red = parseInt(color.slice(0, 2), 16);
+  let green = parseInt(color.slice(2, 4), 16);
+  let blue = parseInt(color.slice(4, 6), 16);
+
+  if (tint === 0) { // when primary color is in its rgb space
+    return [red, green, blue].join(',');
+  } else {
+    red += Math.round(tint * (255 - red));
+    green += Math.round(tint * (255 - green));
+    blue += Math.round(tint * (255 - blue));
+    red = red.toString(16);
+    green = green.toString(16);
+    blue = blue.toString(16);
+    return `#${ red }${ green }${ blue }`;
+  }
+};

+ 29 - 7
examples/components/header.vue

@@ -17,6 +17,7 @@
     .container {
       height: 100%;
       box-sizing: border-box;
+      border-bottom: 1px solid #DCDFE6;
     }
 
     .nav-lang-spe {
@@ -122,23 +123,24 @@
 
       a {
         text-decoration: none;
-        color: #888;
+        color: #1989FA;
+        opacity: 0.5;
         display: block;
         padding: 0 22px;
 
         &.active,
         &:hover {
-          color: #333;
+          opacity: 1;
         }
 
         &.active::after {
           content: '';
           display: inline-block;
           position: absolute;
-          bottom: 15px;
-          left: calc(50% - 7px);
-          width: 14px;
-          height: 4px;
+          bottom: 0;
+          left: calc(50% - 15px);
+          width: 30px;
+          height: 2px;
           background: #409EFF;
         }
       }
@@ -356,7 +358,8 @@
           
           <!--theme picker-->
           <li class="nav-item nav-theme-switch" v-show="isComponentPage">
-            <theme-picker></theme-picker>
+            <theme-configurator :key="lang" v-if="showThemeConfigurator"></theme-configurator>
+            <theme-picker v-else></theme-picker>
           </li>
         </ul>
       </div>
@@ -365,9 +368,12 @@
 </template>
 <script>
   import ThemePicker from './theme-picker.vue';
+  import ThemeConfigurator from './theme-configurator';
   import AlgoliaSearch from './search.vue';
   import compoLang from '../i18n/component.json';
   import Element from 'main/index.js';
+  import bus from '../bus';
+
   const { version } = Element;
 
   export default {
@@ -389,6 +395,7 @@
 
     components: {
       ThemePicker,
+      ThemeConfigurator,
       AlgoliaSearch
     },
 
@@ -404,6 +411,10 @@
       },
       isComponentPage() {
         return /^component/.test(this.$route.name);
+      },
+      showThemeConfigurator() {
+        const host = location.hostname;
+        return host.match('localhost') || host.match('elenet');
       }
     },
 
@@ -441,6 +452,17 @@
       };
       xhr.open('GET', '/versions.json');
       xhr.send();
+      let primaryLast = '#409EFF';
+      bus.$on('user-theme-config-update', (val) => {
+        let primaryColor = val.global['$--color-primary'];
+        if (!primaryColor) primaryColor = '#409EFF';
+        const base64svg = 'data:image/svg+xml;base64,';
+        const imgSet = document.querySelectorAll('h1 img');
+        imgSet.forEach((img) => {
+          img.src = `${base64svg}${window.btoa(window.atob(img.src.replace(base64svg, '')).replace(primaryLast, primaryColor))}`;
+        });
+        primaryLast = primaryColor;
+      });
     }
   };
 </script>

+ 55 - 0
examples/components/theme-configurator/docStyle.vue

@@ -0,0 +1,55 @@
+<script>
+const ORIGINAL_THEME = '#409EFF';
+import { get as ajaxGet } from './utils/ajax';
+import { updateDomHeadStyle } from './utils/utils.js';
+export default {
+  data() {
+    return {
+      docs: '', // content of docs css
+      theme: ORIGINAL_THEME
+    };
+  },
+  methods: {
+    updateDocStyle(e) {
+      const val = e.global['$--color-primary'] || ORIGINAL_THEME;
+      const oldVal = this.theme;
+      const getHandler = (variable, id) => {
+        return () => {
+          let newStyle = this.updateStyle(this[variable], ORIGINAL_THEME, val);
+          updateDomHeadStyle(id, newStyle);
+        };
+      };
+      const docsHandler = getHandler('docs', 'docs-style');
+      if (!this.docs) {
+        const links = [].filter.call(document.querySelectorAll('link'), link => {
+          return /docs\..+\.css/.test(link.href || '');
+        });
+        links[0] && this.getCSSString(links[0].href, docsHandler, 'docs');
+      } else {
+        docsHandler();
+      }
+
+      const styles = [].slice.call(document.querySelectorAll('style'))
+        .filter(style => {
+          const text = style.innerText;
+          return new RegExp(oldVal, 'i').test(text) && !/Chalk Variables/.test(text);
+        });
+      styles.forEach(style => {
+        const { innerText } = style;
+        if (typeof innerText !== 'string') return;
+        style.innerText = this.updateStyle(innerText, oldVal, val);
+      });
+      this.theme = val;
+    },
+    updateStyle(style, oldColor, newColor) {
+      return style.replace(new RegExp(oldColor, 'ig'), newColor);
+    },
+    getCSSString(url, callback, variable) {
+      ajaxGet(url).then((res) => {
+        this[variable] = res;
+        callback();
+      });
+    }
+  }
+};
+</script>

+ 76 - 0
examples/components/theme-configurator/download.vue

@@ -0,0 +1,76 @@
+<template>
+  <div class="action-area">
+    <div class="action-area-main">
+    <div class="action-button">
+      <el-button type="warning" @click.stop="onReset">{{getActionDisplayName("reset-theme")}}</el-button>
+    </div>
+    <div class="action-button">
+      <el-button 
+        type="primary" 
+        :loading=downloading
+        @click.stop="onDownload">
+        {{getActionDisplayName("download-theme")}}
+      </el-button>
+      </div>
+    </div>
+  </div>
+</template>
+<style>
+.action-area { 
+  width: 24%;
+  max-width: 400px;
+  position: fixed;
+  right: 0;
+  bottom: 0;
+  padding-right: 1%;
+}
+@media (min-width: 1600px) {
+  .action-area {
+    padding-right: calc((100% - 1600px) / 2);
+  }
+}
+.action-area-main {
+  opacity: .95;
+  background: #F5F7FA;
+  height: 70px;
+  bottom: 0;
+  width: 97%;
+  box-sizing: border-box;
+  margin: 0 .5% 0 5%;
+}
+.action-button {
+  width: 50%;
+  text-align: center;
+  display: inline-block;
+  padding-top: 15px;
+}
+</style>
+<script>
+import { getActionDisplayName } from './utils/utils.js';
+export default {
+  data() {
+    return {
+      downloading: false
+    };
+  },
+  methods: {
+    onReset() {
+      this.$parent.onReset();
+    },
+    getActionDisplayName(key) {
+      return getActionDisplayName(key);
+    },
+    onDownload() {
+      this.downloading = true;
+      this.$parent.onDownload()
+        .then()
+        .catch((err) => {
+          this.$parent.onError(err);
+        })
+        .then(() => {
+          this.downloading = false;
+        });
+    }
+  }
+};
+</script>

+ 86 - 0
examples/components/theme-configurator/editor/borderRadius.vue

@@ -0,0 +1,86 @@
+<template>
+  <section class="config" :key="displayName">
+    <div class="config-label">
+      {{displayName}}
+    </div>
+    <div class="config-content">
+      <theme-input 
+        v-if="isGlobal"
+        :val="value"
+        @change="onChange"
+      ></theme-input>
+      <el-select 
+        v-if="!isGlobal"
+        v-model="value" 
+        class="select"
+        @change="onSelectChange"
+      >
+        <el-option
+          v-for="item in options"
+          :key="item.value"
+          :label="item.label"
+          :value="item.value">
+        </el-option>
+      </el-select>
+    </div>
+  </section>
+</template>
+
+<style>
+.select {
+  width: 100%;
+}
+</style>
+
+<script>
+import Mixin from './mixin';
+import Input from './input';
+import { getStyleDisplayName } from '../utils/utils.js';
+
+export default {
+  data() {
+    return {
+      options: [],
+      value: ''
+    };
+  },
+  components: {
+    themeInput: Input
+  },
+  mixins: [Mixin],
+  computed: {
+    isGlobalInputValue() {
+      return this.config.value.startsWith('$');
+    }
+  },
+  methods: {
+    onSelectChange(e) {
+      this.onChange(e);
+    },
+    initSelectOption() {
+      this.options = [];
+      const golbalV = this.golbalValue.border;
+      if (golbalV) {
+        Object.keys(golbalV).forEach((font) => {
+          if (font.includes('border-radius')) {
+            const size = golbalV[font];
+            this.options.push({
+              value: size.key,
+              label: getStyleDisplayName(size)
+            });
+          }
+        });
+      }
+    }
+  },
+  watch: {
+    'mergedValue': {
+      immediate: true,
+      handler(value) {
+        this.initSelectOption();
+        this.value = this.mergedValue;
+      }
+    }
+  }
+};
+</script>

+ 155 - 0
examples/components/theme-configurator/editor/boxShadow.vue

@@ -0,0 +1,155 @@
+<template>
+  <section class="config" :key="displayName">
+    <div class="config-label">
+      {{displayName}} 
+      <el-button 
+        class="plus-button" 
+        size="mini" 
+        round 
+        icon="el-icon-plus"
+        @click.stop="onAddShadow"
+      >
+      </el-button>
+    </div>
+    <div class="config-content" v-for="(each, key) in valueArr" :key="key">
+      <div class="content-10">
+        <color-picker 
+          size="mini"
+          class="colorPicker"
+          v-model="each.color" 
+          @change="val => onInputChange(val, key, 'color')"
+          show-alpha
+        ></color-picker>
+        <span class="content-tip">Color</span>
+      </div>
+      <div class="content-20">
+        <theme-input  
+          size="mini"
+          :val="each.offsetX" 
+          @change="val => onInputChange(Number(val), key, 'offsetX')"
+        >
+        </theme-input>
+        <span class="content-tip">X-px</span>
+      </div>
+      <div class="content-20">
+        <theme-input 
+          size="mini"
+          :val="each.offsetY" 
+          @change="val => onInputChange(Number(val), key, 'offsetY')"
+        >
+        </theme-input>
+        <span class="content-tip">Y-px</span>
+      </div>
+      <div class="content-20">
+        <theme-input 
+          size="mini"
+          :val="each.spreadRadius" 
+          @change="val => onInputChange(Number(val), key, 'spreadRadius')"
+        >
+        </theme-input>
+        <span class="content-tip">Spread</span>
+      </div>
+      <div class="content-20">
+        <theme-input 
+          size="mini"
+          :val="each.blurRadius" 
+          @change="val => onInputChange(Number(val), key, 'blurRadius')"
+        >
+        </theme-input>
+        <span class="content-tip">Blur</span>
+      </div>
+      <div class="content-10">
+        <el-button 
+          size="mini" 
+          round 
+          icon="el-icon-minus"
+          @click.stop="val => onMinusShadow(key)"
+        ></el-button>
+      </div>
+    </div>
+  </section>
+</template>
+
+<style scoped>
+.plus-button {
+  position: absolute;
+  left: 90%;
+}
+.colorPicker {
+  margin-left: 0;
+}
+.content-20 .el-input__suffix-inner span{
+  line-height: 28px;
+}
+.content-20 {
+  padding: 0 3px;
+}
+.content-10 { 
+  vertical-align: top;
+}
+.content-tip {
+  color: #909399;
+  font-size: 12px;
+}
+.config-content {
+  padding: 5px 0;
+}
+/* Element buton style override */
+.el-button--mini.is-round {
+  padding: 3px 3px;  
+}
+</style>
+<script>
+import Mixin from './mixin';
+import Input from './input';
+import { parse as parseShaodw, stringify as stringifyShaodw } from '../utils/boxShadow.js';
+import ColorPicker from './color-picker';
+
+export default {
+  components: {
+    ColorPicker,
+    themeInput: Input
+  },
+  data() {
+    return {
+      valueArr: []
+    };
+  },
+  mixins: [Mixin],
+  methods: {
+    onAddShadow() {
+      this.valueArr.push({
+        offsetX: 0,
+        offsetY: 0,
+        spreadRadius: 0,
+        blurRadius: 0,
+        color: 'rgba(0,0,0,0)',
+        inset: false
+      });
+    },
+    onMinusShadow(index) {
+      this.valueArr.splice(index, 1);
+      this.onShadowChange();
+    },
+    onInputChange(e, index, key) {
+      const arr = this.valueArr[index];
+      arr[key] = e;
+      this.valueArr.splice(index, 1, arr);
+      this.onShadowChange();
+    },
+    onShadowChange() {
+      this.onChange(
+        stringifyShaodw(this.valueArr)
+      );
+    }
+  },
+  watch: {
+    'mergedValue': {
+      immediate: true,
+      handler(value) {
+        this.valueArr = parseShaodw(value);
+      }
+    }
+  }
+};
+</script>

+ 8 - 0
examples/components/theme-configurator/editor/color-picker/index.js

@@ -0,0 +1,8 @@
+import ColorPicker from './src/main';
+
+/* istanbul ignore next */
+ColorPicker.install = function(Vue) {
+  Vue.component(ColorPicker.name, ColorPicker);
+};
+
+export default ColorPicker;

+ 316 - 0
examples/components/theme-configurator/editor/color-picker/src/color.js

@@ -0,0 +1,316 @@
+const hsv2hsl = function(hue, sat, val) {
+  return [
+    hue,
+    (sat * val / ((hue = (2 - sat) * val) < 1 ? hue : 2 - hue)) || 0,
+    hue / 2
+  ];
+};
+
+// Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
+// <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>
+const isOnePointZero = function(n) {
+  return typeof n === 'string' && n.indexOf('.') !== -1 && parseFloat(n) === 1;
+};
+
+const isPercentage = function(n) {
+  return typeof n === 'string' && n.indexOf('%') !== -1;
+};
+
+// Take input from [0, n] and return it as [0, 1]
+const bound01 = function(value, max) {
+  if (isOnePointZero(value)) value = '100%';
+
+  const processPercent = isPercentage(value);
+  value = Math.min(max, Math.max(0, parseFloat(value)));
+
+  // Automatically convert percentage into number
+  if (processPercent) {
+    value = parseInt(value * max, 10) / 100;
+  }
+
+  // Handle floating point rounding errors
+  if ((Math.abs(value - max) < 0.000001)) {
+    return 1;
+  }
+
+  // Convert into [0, 1] range if it isn't already
+  return (value % max) / parseFloat(max);
+};
+
+const INT_HEX_MAP = { 10: 'A', 11: 'B', 12: 'C', 13: 'D', 14: 'E', 15: 'F' };
+
+const toHex = function({ r, g, b }) {
+  const hexOne = function(value) {
+    value = Math.min(Math.round(value), 255);
+    const high = Math.floor(value / 16);
+    const low = value % 16;
+    return '' + (INT_HEX_MAP[high] || high) + (INT_HEX_MAP[low] || low);
+  };
+
+  if (isNaN(r) || isNaN(g) || isNaN(b)) return '';
+
+  return '#' + hexOne(r) + hexOne(g) + hexOne(b);
+};
+
+const HEX_INT_MAP = { A: 10, B: 11, C: 12, D: 13, E: 14, F: 15 };
+
+const parseHexChannel = function(hex) {
+  if (hex.length === 2) {
+    return (HEX_INT_MAP[hex[0].toUpperCase()] || +hex[0]) * 16 + (HEX_INT_MAP[hex[1].toUpperCase()] || +hex[1]);
+  }
+
+  return HEX_INT_MAP[hex[1].toUpperCase()] || +hex[1];
+};
+
+const hsl2hsv = function(hue, sat, light) {
+  sat = sat / 100;
+  light = light / 100;
+  let smin = sat;
+  const lmin = Math.max(light, 0.01);
+  let sv;
+  let v;
+
+  light *= 2;
+  sat *= (light <= 1) ? light : 2 - light;
+  smin *= lmin <= 1 ? lmin : 2 - lmin;
+  v = (light + sat) / 2;
+  sv = light === 0 ? (2 * smin) / (lmin + smin) : (2 * sat) / (light + sat);
+
+  return {
+    h: hue,
+    s: sv * 100,
+    v: v * 100
+  };
+};
+
+// `rgbToHsv`
+// Converts an RGB color value to HSV
+// *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]
+// *Returns:* { h, s, v } in [0,1]
+const rgb2hsv = function(r, g, b) {
+  r = bound01(r, 255);
+  g = bound01(g, 255);
+  b = bound01(b, 255);
+
+  const max = Math.max(r, g, b);
+  const min = Math.min(r, g, b);
+  let h, s;
+  let v = max;
+
+  const d = max - min;
+  s = max === 0 ? 0 : d / max;
+
+  if (max === min) {
+    h = 0; // achromatic
+  } else {
+    switch (max) {
+      case r:
+        h = (g - b) / d + (g < b ? 6 : 0);
+        break;
+      case g:
+        h = (b - r) / d + 2;
+        break;
+      case b:
+        h = (r - g) / d + 4;
+        break;
+    }
+    h /= 6;
+  }
+
+  return { h: h * 360, s: s * 100, v: v * 100 };
+};
+
+// `hsvToRgb`
+// Converts an HSV color value to RGB.
+// *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]
+// *Returns:* { r, g, b } in the set [0, 255]
+const hsv2rgb = function(h, s, v) {
+  h = bound01(h, 360) * 6;
+  s = bound01(s, 100);
+  v = bound01(v, 100);
+
+  const i = Math.floor(h);
+  const f = h - i;
+  const p = v * (1 - s);
+  const q = v * (1 - f * s);
+  const t = v * (1 - (1 - f) * s);
+  const mod = i % 6;
+  const r = [v, q, p, p, t, v][mod];
+  const g = [t, v, v, q, p, p][mod];
+  const b = [p, p, t, v, v, q][mod];
+
+  return {
+    r: Math.round(r * 255),
+    g: Math.round(g * 255),
+    b: Math.round(b * 255)
+  };
+};
+
+export default class Color {
+  constructor(options) {
+    this._hue = 0;
+    this._saturation = 100;
+    this._value = 100;
+    this._alpha = 100;
+
+    this.enableAlpha = false;
+    this.format = 'hex';
+    this.value = '';
+
+    options = options || {};
+
+    for (let option in options) {
+      if (options.hasOwnProperty(option)) {
+        this[option] = options[option];
+      }
+    }
+
+    this.doOnChange();
+  }
+
+  set(prop, value) {
+    if (arguments.length === 1 && typeof prop === 'object') {
+      for (let p in prop) {
+        if (prop.hasOwnProperty(p)) {
+          this.set(p, prop[p]);
+        }
+      }
+
+      return;
+    }
+
+    this['_' + prop] = value;
+    this.doOnChange();
+  }
+
+  get(prop) {
+    return this['_' + prop];
+  }
+
+  toRgb() {
+    return hsv2rgb(this._hue, this._saturation, this._value);
+  }
+
+  fromString(value) {
+    if (!value) {
+      this._hue = 0;
+      this._saturation = 100;
+      this._value = 100;
+
+      this.doOnChange();
+      return;
+    }
+
+    const fromHSV = (h, s, v) => {
+      this._hue = Math.max(0, Math.min(360, h));
+      this._saturation = Math.max(0, Math.min(100, s));
+      this._value = Math.max(0, Math.min(100, v));
+
+      this.doOnChange();
+    };
+
+    if (value.indexOf('hsl') !== -1) {
+      const parts = value.replace(/hsla|hsl|\(|\)/gm, '')
+        .split(/\s|,/g).filter((val) => val !== '').map((val, index) => index > 2 ? parseFloat(val) : parseInt(val, 10));
+
+      if (parts.length === 4) {
+        this._alpha = Math.floor(parseFloat(parts[3]) * 100);
+      } else if (parts.length === 3) {
+        this._alpha = 100;
+      }
+      if (parts.length >= 3) {
+        const { h, s, v } = hsl2hsv(parts[0], parts[1], parts[2]);
+        fromHSV(h, s, v);
+      }
+    } else if (value.indexOf('hsv') !== -1) {
+      const parts = value.replace(/hsva|hsv|\(|\)/gm, '')
+        .split(/\s|,/g).filter((val) => val !== '').map((val, index) => index > 2 ? parseFloat(val) : parseInt(val, 10));
+
+      if (parts.length === 4) {
+        this._alpha = Math.floor(parseFloat(parts[3]) * 100);
+      } else if (parts.length === 3) {
+        this._alpha = 100;
+      }
+      if (parts.length >= 3) {
+        fromHSV(parts[0], parts[1], parts[2]);
+      }
+    } else if (value.indexOf('rgb') !== -1) {
+      const parts = value.replace(/rgba|rgb|\(|\)/gm, '')
+        .split(/\s|,/g).filter((val) => val !== '').map((val, index) => index > 2 ? parseFloat(val) : parseInt(val, 10));
+
+      if (parts.length === 4) {
+        this._alpha = Math.floor(parseFloat(parts[3]) * 100);
+      } else if (parts.length === 3) {
+        this._alpha = 100;
+      }
+      if (parts.length >= 3) {
+        const { h, s, v } = rgb2hsv(parts[0], parts[1], parts[2]);
+        fromHSV(h, s, v);
+      }
+    } else if (value.indexOf('#') !== -1) {
+      const hex = value.replace('#', '').trim();
+      let r, g, b;
+
+      if (hex.length === 3) {
+        r = parseHexChannel(hex[0] + hex[0]);
+        g = parseHexChannel(hex[1] + hex[1]);
+        b = parseHexChannel(hex[2] + hex[2]);
+      } else if (hex.length === 6 || hex.length === 8) {
+        r = parseHexChannel(hex.substring(0, 2));
+        g = parseHexChannel(hex.substring(2, 4));
+        b = parseHexChannel(hex.substring(4, 6));
+      }
+
+      if (hex.length === 8) {
+        this._alpha = Math.floor(parseHexChannel(hex.substring(6)) / 255 * 100);
+      } else if (hex.length === 3 || hex.length === 6) {
+        this._alpha = 100;
+      }
+
+      const { h, s, v } = rgb2hsv(r, g, b);
+      fromHSV(h, s, v);
+    }
+  }
+
+  compare(color) {
+    return Math.abs(color._hue - this._hue) < 2 &&
+      Math.abs(color._saturation - this._saturation) < 1 &&
+      Math.abs(color._value - this._value) < 1 &&
+      Math.abs(color._alpha - this._alpha) < 1;
+  }
+
+  doOnChange() {
+    const { _hue, _saturation, _value, _alpha, format } = this;
+
+    if (this.enableAlpha) {
+      switch (format) {
+        case 'hsl':
+          const hsl = hsv2hsl(_hue, _saturation / 100, _value / 100);
+          this.value = `hsla(${ _hue }, ${ Math.round(hsl[1] * 100) }%, ${ Math.round(hsl[2] * 100) }%, ${ _alpha / 100})`;
+          break;
+        case 'hsv':
+          this.value = `hsva(${ _hue }, ${ Math.round(_saturation) }%, ${ Math.round(_value) }%, ${ _alpha / 100})`;
+          break;
+        default:
+          const { r, g, b } = hsv2rgb(_hue, _saturation, _value);
+          this.value = `rgba(${r}, ${g}, ${b}, ${ _alpha / 100 })`;
+      }
+    } else {
+      switch (format) {
+        case 'hsl':
+          const hsl = hsv2hsl(_hue, _saturation / 100, _value / 100);
+          this.value = `hsl(${ _hue }, ${ Math.round(hsl[1] * 100) }%, ${ Math.round(hsl[2] * 100) }%)`;
+          break;
+        case 'hsv':
+          this.value = `hsv(${ _hue }, ${ Math.round(_saturation) }%, ${ Math.round(_value) }%)`;
+          break;
+        case 'rgb':
+          const { r, g, b } = hsv2rgb(_hue, _saturation, _value);
+          this.value = `rgb(${r}, ${g}, ${b})`;
+          break;
+        default:
+          this.value = toHex(hsv2rgb(_hue, _saturation, _value));
+      }
+    }
+  }
+};

+ 132 - 0
examples/components/theme-configurator/editor/color-picker/src/components/alpha-slider.vue

@@ -0,0 +1,132 @@
+<template>
+  <div class="el-color-alpha-slider" :class="{ 'is-vertical': vertical }">
+    <div class="el-color-alpha-slider__bar"
+         @click="handleClick"
+         ref="bar"
+         :style="{
+           background: background
+         }">
+    </div>
+    <div class="el-color-alpha-slider__thumb"
+         ref="thumb"
+         :style="{
+           left: thumbLeft + 'px',
+           top: thumbTop + 'px'
+         }">
+    </div>
+  </div>
+</template>
+
+<script>
+  import draggable from '../draggable';
+
+  export default {
+    name: 'el-color-alpha-slider',
+
+    props: {
+      color: {
+        required: true
+      },
+      vertical: Boolean
+    },
+
+    watch: {
+      'color._alpha'() {
+        this.update();
+      },
+
+      'color.value'() {
+        this.update();
+      }
+    },
+
+    methods: {
+      handleClick(event) {
+        const thumb = this.$refs.thumb;
+        const target = event.target;
+
+        if (target !== thumb) {
+          this.handleDrag(event);
+        }
+      },
+
+      handleDrag(event) {
+        const rect = this.$el.getBoundingClientRect();
+        const { thumb } = this.$refs;
+
+        if (!this.vertical) {
+          let left = event.clientX - rect.left;
+          left = Math.max(thumb.offsetWidth / 2, left);
+          left = Math.min(left, rect.width - thumb.offsetWidth / 2);
+
+          this.color.set('alpha', Math.round((left - thumb.offsetWidth / 2) / (rect.width - thumb.offsetWidth) * 100));
+        } else {
+          let top = event.clientY - rect.top;
+          top = Math.max(thumb.offsetHeight / 2, top);
+          top = Math.min(top, rect.height - thumb.offsetHeight / 2);
+
+          this.color.set('alpha', Math.round((top - thumb.offsetHeight / 2) / (rect.height - thumb.offsetHeight) * 100));
+        }
+      },
+
+      getThumbLeft() {
+        if (this.vertical) return 0;
+        const el = this.$el;
+        const alpha = this.color._alpha;
+
+        if (!el) return 0;
+        const thumb = this.$refs.thumb;
+        return Math.round(alpha * (el.offsetWidth - thumb.offsetWidth / 2) / 100);
+      },
+
+      getThumbTop() {
+        if (!this.vertical) return 0;
+        const el = this.$el;
+        const alpha = this.color._alpha;
+
+        if (!el) return 0;
+        const thumb = this.$refs.thumb;
+        return Math.round(alpha * (el.offsetHeight - thumb.offsetHeight / 2) / 100);
+      },
+
+      getBackground() {
+        if (this.color && this.color.value) {
+          const { r, g, b } = this.color.toRgb();
+          return `linear-gradient(to right, rgba(${r}, ${g}, ${b}, 0) 0%, rgba(${r}, ${g}, ${b}, 1) 100%)`;
+        }
+        return null;
+      },
+
+      update() {
+        this.thumbLeft = this.getThumbLeft();
+        this.thumbTop = this.getThumbTop();
+        this.background = this.getBackground();
+      }
+    },
+
+    data() {
+      return {
+        thumbLeft: 0,
+        thumbTop: 0,
+        background: null
+      };
+    },
+
+    mounted() {
+      const { bar, thumb } = this.$refs;
+
+      const dragConfig = {
+        drag: (event) => {
+          this.handleDrag(event);
+        },
+        end: (event) => {
+          this.handleDrag(event);
+        }
+      };
+
+      draggable(bar, dragConfig);
+      draggable(thumb, dragConfig);
+      this.update();
+    }
+  };
+</script>

+ 113 - 0
examples/components/theme-configurator/editor/color-picker/src/components/color-list.vue

@@ -0,0 +1,113 @@
+<template>
+  <div class="el-color-predefine color-list-container">
+    <div class="el-color-predefine__colors color-list">
+      <div class="color-list-item"
+           :class="{selected: item.selected, 'is-alpha': item._alpha < 100}"
+           v-for="(item, index) in rgbaColors"
+           :key="colors[index].variable"
+           @click="handleSelect(index)">
+        <span class="color-list-item-ball" :style="{'background-color': item.value}">
+        </span>
+        <div class="color-list-item-label">
+          {{item.info.label}} 
+        </div>
+        <div class="color-list-item-value">
+          {{item.info.value}} 
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+<style>
+  .color-list-container {
+    border-top: 1px solid #EBEEF5;
+    margin-top: 15px;
+    padding-top: 10px;
+    width: 100%;
+  }
+  .color-list {
+    max-height: 138px;
+    overflow: auto;
+  }
+  .color-list-item {
+    height: 24px;
+    width: 100%;
+    cursor: pointer;
+    margin: 2px 0;
+  }
+  .color-list-item:hover {
+    background: #efefef;
+  }
+  .color-list-item-ball {
+    height: 20px;
+    width: 20px;
+    margin-top: 2px;
+    margin-left: 5px;
+    border-radius: 100%;
+    display: inline-block;
+  }
+  .color-list-item-label {
+    margin-left: 15px;
+    font-size: 13px;
+    line-height: 24px;
+    display: inline-block;
+    vertical-align: super;
+    width: 30%;
+  }
+  .color-list-item-value { 
+    margin-left: 15px;
+    font-size: 13px;
+    line-height: 24px;
+    display: inline-block;
+    vertical-align: super;
+  }
+</style>
+
+<script>
+  import Color from '../color';
+
+  export default {
+    props: {
+      colors: { type: Array, required: true },
+      color: { required: true }
+    },
+    data() {
+      return {
+        rgbaColors: this.parseColors(this.colors, this.color)
+      };
+    },
+    methods: {
+      handleSelect(index) {
+        this.color.fromString(this.colors[index].value);
+        this.$emit('select', this.colors[index]);
+      },
+      parseColors(colors, color) {
+        return colors.map(value => {
+          const c = new Color();
+          c.enableAlpha = true;
+          c.format = 'rgba';
+          c.fromString(value.value);
+          c.info = value;
+          c.selected = c.value === color.value;
+          return c;
+        });
+      }
+    },
+    watch: {
+      '$parent.currentColor'(val) {
+        const color = new Color();
+        color.fromString(val);
+
+        this.rgbaColors.forEach(item => {
+          item.selected = color.compare(item);
+        });
+      },
+      colors(newVal) {
+        this.rgbaColors = this.parseColors(newVal, this.color);
+      },
+      color(newVal) {
+        this.rgbaColors = this.parseColors(this.colors, newVal);
+      }
+    }
+  };
+</script>

+ 123 - 0
examples/components/theme-configurator/editor/color-picker/src/components/hue-slider.vue

@@ -0,0 +1,123 @@
+<template>
+  <div class="el-color-hue-slider" :class="{ 'is-vertical': vertical }">
+    <div class="el-color-hue-slider__bar" @click="handleClick" ref="bar"></div>
+    <div class="el-color-hue-slider__thumb"
+         :style="{
+           left: thumbLeft + 'px',
+           top: thumbTop + 'px'
+         }"
+         ref="thumb">
+    </div>
+  </div>
+</template>
+
+<script>
+  import draggable from '../draggable';
+
+  export default {
+    name: 'el-color-hue-slider',
+
+    props: {
+      color: {
+        required: true
+      },
+
+      vertical: Boolean
+    },
+
+    data() {
+      return {
+        thumbLeft: 0,
+        thumbTop: 0
+      };
+    },
+
+    computed: {
+      hueValue() {
+        const hue = this.color.get('hue');
+        return hue;
+      }
+    },
+
+    watch: {
+      hueValue() {
+        this.update();
+      }
+    },
+
+    methods: {
+      handleClick(event) {
+        const thumb = this.$refs.thumb;
+        const target = event.target;
+
+        if (target !== thumb) {
+          this.handleDrag(event);
+        }
+      },
+
+      handleDrag(event) {
+        const rect = this.$el.getBoundingClientRect();
+        const { thumb } = this.$refs;
+        let hue;
+
+        if (!this.vertical) {
+          let left = event.clientX - rect.left;
+          left = Math.min(left, rect.width - thumb.offsetWidth / 2);
+          left = Math.max(thumb.offsetWidth / 2, left);
+
+          hue = Math.round((left - thumb.offsetWidth / 2) / (rect.width - thumb.offsetWidth) * 360);
+        } else {
+          let top = event.clientY - rect.top;
+          top = Math.min(top, rect.height - thumb.offsetHeight / 2);
+          top = Math.max(thumb.offsetHeight / 2, top);
+
+          hue = Math.round((top - thumb.offsetHeight / 2) / (rect.height - thumb.offsetHeight) * 360);
+        }
+
+        this.color.set('hue', hue);
+      },
+
+      getThumbLeft() {
+        if (this.vertical) return 0;
+        const el = this.$el;
+        const hue = this.color.get('hue');
+
+        if (!el) return 0;
+        const thumb = this.$refs.thumb;
+        return Math.round(hue * (el.offsetWidth - thumb.offsetWidth / 2) / 360);
+      },
+
+      getThumbTop() {
+        if (!this.vertical) return 0;
+        const el = this.$el;
+        const hue = this.color.get('hue');
+
+        if (!el) return 0;
+        const thumb = this.$refs.thumb;
+        return Math.round(hue * (el.offsetHeight - thumb.offsetHeight / 2) / 360);
+      },
+
+      update() {
+        this.thumbLeft = this.getThumbLeft();
+        this.thumbTop = this.getThumbTop();
+      }
+    },
+
+    mounted() {
+      const { bar, thumb } = this.$refs;
+
+      const dragConfig = {
+        drag: (event) => {
+          this.handleDrag(event);
+        },
+        end: (event) => {
+          this.handleDrag(event);
+        }
+      };
+
+      draggable(bar, dragConfig);
+      draggable(thumb, dragConfig);
+      this.update();
+    }
+  };
+</script>

+ 134 - 0
examples/components/theme-configurator/editor/color-picker/src/components/picker-dropdown.vue

@@ -0,0 +1,134 @@
+<template>
+  <transition name="el-zoom-in-top" @after-leave="doDestroy">
+    <div
+      class="el-color-dropdown"
+      v-show="showPopper">
+      <div class="el-color-dropdown__main-wrapper">
+        <hue-slider ref="hue" :color="color" vertical style="float: right;"></hue-slider>
+        <sv-panel ref="sl" :color="color"></sv-panel>
+      </div>
+      <alpha-slider v-if="showAlpha" ref="alpha" :color="color"></alpha-slider>
+      <predefine v-if="predefine" :color="color" :colors="predefine"></predefine>
+      <div class="el-color-dropdown__btns">
+        <span class="el-color-dropdown__value">
+          <el-input
+            v-model="customInput"
+            @keyup.native.enter="handleConfirm"
+            @blur="handleConfirm"
+            :validate-event="false"
+            size="mini">
+          </el-input>
+        </span>
+        <!-- <el-button
+          size="mini"
+          type="text"
+          class="el-color-dropdown__link-btn"
+          @click="$emit('clear')">
+          {{ t('el.colorpicker.clear') }}
+        </el-button> -->
+        <el-button
+          plain
+          size="mini"
+          class="el-color-dropdown__btn"
+          @click="confirmValue">
+          {{ t('el.colorpicker.confirm') }}
+        </el-button>
+      </div>
+      <color-list 
+        v-if="colorList && colorList.length > 0" 
+        :color="color" 
+        :colors="colorList"
+        @select=onColorListSelect
+      ></color-list>
+    </div>
+  </transition>
+</template>
+
+<script>
+  import SvPanel from './sv-panel';
+  import HueSlider from './hue-slider';
+  import AlphaSlider from './alpha-slider';
+  import Predefine from './predefine';
+  import ColorList from './color-list';
+  import Popper from 'element-ui/src/utils/vue-popper';
+  import Locale from 'element-ui/src/mixins/locale';
+  import ElInput from 'element-ui/packages/input';
+  import ElButton from 'element-ui/packages/button';
+
+  export default {
+    name: 'el-color-picker-dropdown',
+
+    mixins: [Popper, Locale],
+
+    components: {
+      SvPanel,
+      HueSlider,
+      AlphaSlider,
+      ElInput,
+      ElButton,
+      Predefine,
+      ColorList
+    },
+
+    props: {
+      color: {
+        required: true
+      },
+      showAlpha: Boolean,
+      predefine: Array,
+      colorList: Array
+    },
+
+    data() {
+      return {
+        customInput: ''
+      };
+    },
+
+    computed: {
+      currentColor() {
+        const parent = this.$parent;
+        return !parent.value && !parent.showPanelColor ? '' : parent.color.value;
+      }
+    },
+
+    methods: {
+      confirmValue() {
+        this.$emit('pick');
+      },
+
+      onColorListSelect(e) {
+        this.$emit('pick', e);
+      },
+
+      handleConfirm() {
+        this.color.fromString(this.customInput);
+      }
+    },
+
+    mounted() {
+      this.$parent.popperElm = this.popperElm = this.$el;
+      this.referenceElm = this.$parent.$el;
+    },
+
+    watch: {
+      showPopper(val) {
+        if (val === true) {
+          this.$nextTick(() => {
+            const { sl, hue, alpha } = this.$refs;
+            sl && sl.update();
+            hue && hue.update();
+            alpha && alpha.update();
+          });
+        }
+      },
+
+      currentColor: {
+        immediate: true,
+        handler(val) {
+          this.customInput = val;
+        }
+      }
+    }
+  };
+</script>

+ 61 - 0
examples/components/theme-configurator/editor/color-picker/src/components/predefine.vue

@@ -0,0 +1,61 @@
+<template>
+  <div class="el-color-predefine">
+    <div class="el-color-predefine__colors">
+      <div class="el-color-predefine__color-selector"
+           :class="{selected: item.selected, 'is-alpha': item._alpha < 100}"
+           v-for="(item, index) in rgbaColors"
+           :key="colors[index]"
+           @click="handleSelect(index)">
+        <div :style="{'background-color': item.value}">
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+  import Color from '../color';
+
+  export default {
+    props: {
+      colors: { type: Array, required: true },
+      color: { required: true }
+    },
+    data() {
+      return {
+        rgbaColors: this.parseColors(this.colors, this.color)
+      };
+    },
+    methods: {
+      handleSelect(index) {
+        this.color.fromString(this.colors[index]);
+      },
+      parseColors(colors, color) {
+        return colors.map(value => {
+          const c = new Color();
+          c.enableAlpha = true;
+          c.format = 'rgba';
+          c.fromString(value);
+          c.selected = c.value === color.value;
+          return c;
+        });
+      }
+    },
+    watch: {
+      '$parent.currentColor'(val) {
+        const color = new Color();
+        color.fromString(val);
+
+        this.rgbaColors.forEach(item => {
+          item.selected = color.compare(item);
+        });
+      },
+      colors(newVal) {
+        this.rgbaColors = this.parseColors(newVal, this.color);
+      },
+      color(newVal) {
+        this.rgbaColors = this.parseColors(this.colors, newVal);
+      }
+    }
+  };
+</script>

+ 100 - 0
examples/components/theme-configurator/editor/color-picker/src/components/sv-panel.vue

@@ -0,0 +1,100 @@
+<template>
+  <div class="el-color-svpanel"
+      :style="{
+        backgroundColor: background
+      }">
+    <div class="el-color-svpanel__white"></div>
+    <div class="el-color-svpanel__black"></div>
+    <div class="el-color-svpanel__cursor"
+      :style="{
+        top: cursorTop + 'px',
+        left: cursorLeft + 'px'
+      }">
+      <div></div>
+    </div>
+  </div>
+</template>
+
+<script>
+  import draggable from '../draggable';
+
+  export default {
+    name: 'el-sl-panel',
+
+    props: {
+      color: {
+        required: true
+      }
+    },
+
+    computed: {
+      colorValue() {
+        const hue = this.color.get('hue');
+        const value = this.color.get('value');
+        return { hue, value };
+      }
+    },
+
+    watch: {
+      colorValue() {
+        this.update();
+      }
+    },
+
+    methods: {
+      update() {
+        const saturation = this.color.get('saturation');
+        const value = this.color.get('value');
+
+        const el = this.$el;
+        let { clientWidth: width, clientHeight: height } = el;
+
+        this.cursorLeft = saturation * width / 100;
+        this.cursorTop = (100 - value) * height / 100;
+
+        this.background = 'hsl(' + this.color.get('hue') + ', 100%, 50%)';
+      },
+
+      handleDrag(event) {
+        const el = this.$el;
+        const rect = el.getBoundingClientRect();
+
+        let left = event.clientX - rect.left;
+        let top = event.clientY - rect.top;
+        left = Math.max(0, left);
+        left = Math.min(left, rect.width);
+
+        top = Math.max(0, top);
+        top = Math.min(top, rect.height);
+
+        this.cursorLeft = left;
+        this.cursorTop = top;
+        this.color.set({
+          saturation: left / rect.width * 100,
+          value: 100 - top / rect.height * 100
+        });
+      }
+    },
+
+    mounted() {
+      draggable(this.$el, {
+        drag: (event) => {
+          this.handleDrag(event);
+        },
+        end: (event) => {
+          this.handleDrag(event);
+        }
+      });
+
+      this.update();
+    },
+
+    data() {
+      return {
+        cursorTop: 0,
+        cursorLeft: 0,
+        background: 'hsl(0, 100%, 50%)'
+      };
+    }
+  };
+</script>

+ 36 - 0
examples/components/theme-configurator/editor/color-picker/src/draggable.js

@@ -0,0 +1,36 @@
+import Vue from 'vue';
+let isDragging = false;
+
+export default function(element, options) {
+  if (Vue.prototype.$isServer) return;
+  const moveFn = function(event) {
+    if (options.drag) {
+      options.drag(event);
+    }
+  };
+  const upFn = function(event) {
+    document.removeEventListener('mousemove', moveFn);
+    document.removeEventListener('mouseup', upFn);
+    document.onselectstart = null;
+    document.ondragstart = null;
+
+    isDragging = false;
+
+    if (options.end) {
+      options.end(event);
+    }
+  };
+  element.addEventListener('mousedown', function(event) {
+    if (isDragging) return;
+    document.onselectstart = function() { return false; };
+    document.ondragstart = function() { return false; };
+
+    document.addEventListener('mousemove', moveFn);
+    document.addEventListener('mouseup', upFn);
+    isDragging = true;
+
+    if (options.start) {
+      options.start(event);
+    }
+  });
+}

+ 190 - 0
examples/components/theme-configurator/editor/color-picker/src/main.vue

@@ -0,0 +1,190 @@
+<template>
+  <div
+    :class="[
+      'el-color-picker',
+      colorDisabled ? 'is-disabled' : '',
+      colorSize ? `el-color-picker--${ colorSize }` : ''
+    ]"
+    v-clickoutside="hide">
+    <div class="el-color-picker__mask" v-if="colorDisabled"></div>
+    <div class="el-color-picker__trigger" @click="handleTrigger">
+      <span class="el-color-picker__color" :class="{ 'is-alpha': showAlpha }">
+        <span class="el-color-picker__color-inner"
+          :style="{
+            backgroundColor: displayedColor
+          }"></span>
+        <span class="el-color-picker__empty el-icon-close" v-if="!value && !showPanelColor"></span>
+      </span>
+      <span class="el-color-picker__icon el-icon-arrow-down" v-show="value || showPanelColor"></span>
+    </div>
+    <picker-dropdown
+       ref="dropdown"
+       :class="['el-color-picker__panel', popperClass || '']"
+       v-model="showPicker"
+       @pick="confirmValue"
+       @clear="clearValue"
+       :color="color"
+       :show-alpha="showAlpha"
+       :predefine="predefine"
+       :colorList="colorList">
+    </picker-dropdown>
+  </div>
+</template>
+
+<script>
+  import Color from './color';
+  import PickerDropdown from './components/picker-dropdown.vue';
+  import Clickoutside from 'element-ui/src/utils/clickoutside';
+  import Emitter from 'element-ui/src/mixins/emitter';
+
+  export default {
+    name: 'ElColorPicker',
+
+    mixins: [Emitter],
+
+    props: {
+      value: String,
+      showAlpha: Boolean,
+      colorFormat: String,
+      disabled: Boolean,
+      size: String,
+      popperClass: String,
+      predefine: Array,
+      colorList: Array
+    },
+
+    inject: {
+      elForm: {
+        default: ''
+      },
+      elFormItem: {
+        default: ''
+      }
+    },
+
+    directives: { Clickoutside },
+
+    computed: {
+      displayedColor() {
+        if (!this.value && !this.showPanelColor) {
+          return 'transparent';
+        }
+
+        return this.displayedRgb(this.color, this.showAlpha);
+      },
+
+      _elFormItemSize() {
+        return (this.elFormItem || {}).elFormItemSize;
+      },
+
+      colorSize() {
+        return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size;
+      },
+
+      colorDisabled() {
+        return this.disabled || (this.elForm || {}).disabled;
+      }
+    },
+
+    watch: {
+      value(val) {
+        if (!val) {
+          this.showPanelColor = false;
+        } else if (val && val !== this.color.value) {
+          this.color.fromString(val);
+        }
+      },
+      color: {
+        deep: true,
+        handler() {
+          this.showPanelColor = true;
+        }
+      },
+      displayedColor(val) {
+        if (!this.showPicker) return;
+        const currentValueColor = new Color({
+          enableAlpha: this.showAlpha,
+          format: this.colorFormat
+        });
+        currentValueColor.fromString(this.value);
+
+        const currentValueColorRgb = this.displayedRgb(currentValueColor, this.showAlpha);
+        if (val !== currentValueColorRgb) {
+          this.$emit('active-change', val);
+        }
+      }
+    },
+
+    methods: {
+      handleTrigger() {
+        if (this.colorDisabled) return;
+        this.showPicker = !this.showPicker;
+      },
+      confirmValue(selection) {
+        const value = selection || this.color.value;
+        this.$emit('input', value);
+        this.$emit('change', value);
+        this.dispatch('ElFormItem', 'el.form.change', value);
+        this.showPicker = false;
+      },
+      clearValue() {
+        this.$emit('input', null);
+        this.$emit('change', null);
+        if (this.value !== null) {
+          this.dispatch('ElFormItem', 'el.form.change', null);
+        }
+        this.showPanelColor = false;
+        this.showPicker = false;
+        this.resetColor();
+      },
+      hide() {
+        this.showPicker = false;
+        this.resetColor();
+      },
+      resetColor() {
+        this.$nextTick(_ => {
+          if (this.value) {
+            this.color.fromString(this.value);
+          } else {
+            this.showPanelColor = false;
+          }
+        });
+      },
+      displayedRgb(color, showAlpha) {
+        if (!(color instanceof Color)) {
+          throw Error('color should be instance of Color Class');
+        }
+
+        const { r, g, b } = color.toRgb();
+        return showAlpha
+          ? `rgba(${ r }, ${ g }, ${ b }, ${ color.get('alpha') / 100 })`
+          : `rgb(${ r }, ${ g }, ${ b })`;
+      }
+    },
+
+    mounted() {
+      const value = this.value;
+      if (value) {
+        this.color.fromString(value);
+      }
+      this.popperElm = this.$refs.dropdown.$el;
+    },
+
+    data() {
+      const color = new Color({
+        enableAlpha: this.showAlpha,
+        format: this.colorFormat
+      });
+
+      return {
+        color,
+        showPicker: false,
+        showPanelColor: false
+      };
+    },
+
+    components: {
+      PickerDropdown
+    }
+  };
+</script>

+ 89 - 0
examples/components/theme-configurator/editor/color.vue

@@ -0,0 +1,89 @@
+<template>
+  <section class="config" :key="displayName">
+    <div class="config-label">
+      {{displayName}}
+    </div>
+    <div class="config-content">
+      <div class="content-80">
+        <el-input
+          :value=displayValue
+          readonly
+          slot="reference"
+          @click.native="onInputClick"
+        ></el-input>
+      </div>
+      <div class="content-20">
+        <color-picker 
+          ref="colorPicker"
+          class="colorPicker"
+          v-model="pickerColor" 
+          @change=onPickerChange
+          :colorList="golbalColorList"
+        ></color-picker>
+      </div>
+    </div>
+  </section>
+</template>
+
+<style>
+input {
+  cursor: pointer;
+}
+.colorPicker {
+  margin-left: 10px;
+  vertical-align: bottom;
+}
+</style>
+
+<script>
+import Mixin from './mixin';
+import { getStyleDisplayValue, getStyleDisplayName } from '../utils/utils.js';
+import ColorPicker from './color-picker';
+
+export default {
+  components: {
+    ColorPicker
+  },
+  data() {
+    return {
+      pickerColor: ''
+    };
+  },
+  mixins: [Mixin],
+  watch: {
+    displayValue: {
+      immediate: true,
+      handler(value) {
+        if (value.startsWith('#')) {
+          this.pickerColor = value;
+        }
+      }
+    }
+  },
+  computed: {
+    golbalColor() {
+      return this.golbalValue.color;
+    },
+    displayValue() {
+      return getStyleDisplayValue(this.mergedValue, this.golbalColor);
+    },
+    golbalColorList() {
+      return this.isGlobal ? [] : Object.keys(this.golbalColor).map((c) => (
+        {
+          label: getStyleDisplayName(this.golbalColor[c]),
+          value: this.golbalColor[c].value,
+          variable: c
+        }
+      ));
+    }
+  },
+  methods: {
+    onInputClick() {
+      this.$refs.colorPicker && this.$refs.colorPicker.handleTrigger();
+    },
+    onPickerChange(e) {
+      this.onChange(e.variable || e);
+    }
+  }
+};
+</script>

+ 101 - 0
examples/components/theme-configurator/editor/fontLineHeight.vue

@@ -0,0 +1,101 @@
+<template>
+  <section class="config" :key="displayName">
+    <div class="config-label">
+      {{displayName}}
+    </div>
+    <div class="config-content">
+      <el-select 
+        v-model="value" 
+        class="select"
+        @change="onSelectChange"
+      >
+        <el-option
+          v-for="item in options"
+          :key="item.value"
+          :label="item.label"
+          :value="item.value">
+        </el-option>
+      </el-select>
+    </div>
+  </section>
+</template>
+
+<style>
+.select {
+  width: 100%;
+}
+</style>
+
+<script>
+const defaultFontLineHeight = [
+  '1',
+  '1.3',
+  '1.5',
+  '1.7',
+  '12px',
+  '16px',
+  '20px',
+  '24px',
+  '28px'
+];
+import Mixin from './mixin';
+import { getStyleDisplayName } from '../utils/utils.js';
+
+export default {
+  props: {
+    componentName: {
+      type: String
+    },
+    golbalValue: {
+      type: Object
+    }
+  },
+  data() {
+    return {
+      options: [],
+      value: ''
+    };
+  },
+  mixins: [Mixin],
+  computed: {
+    isGlobalInputValue() {
+      return this.config.value.startsWith('$');
+    }
+  },
+  methods: {
+    onSelectChange(e) {
+      this.onChange(e);
+    },
+    initSelectOption() {
+      this.options = [];
+      defaultFontLineHeight.forEach((size) => {
+        this.options.push({
+          value: size,
+          label: size
+        });
+      });
+      const golbalTypography = this.golbalValue.typography;
+      if (this.isGlobalInputValue && golbalTypography) {
+        Object.keys(golbalTypography).forEach((font) => {
+          if (font.includes('font-line-height')) {
+            const size = golbalTypography[font];
+            this.options.push({
+              value: size.key,
+              label: getStyleDisplayName(size)
+            });
+          }
+        });
+      }
+    }
+  },
+  watch: {
+    'mergedValue': {
+      immediate: true,
+      handler(value) {
+        this.initSelectOption();
+        this.value = this.mergedValue;
+      }
+    }
+  }
+};
+</script>

+ 101 - 0
examples/components/theme-configurator/editor/fontSize.vue

@@ -0,0 +1,101 @@
+<template>
+  <section class="config" :key="displayName">
+    <div class="config-label">
+      {{displayName}}
+    </div>
+    <div class="config-content">
+      <el-select 
+        v-model="value" 
+        class="select"
+        @change="onSelectChange"
+      >
+        <el-option
+          v-for="item in options"
+          :key="item.value"
+          :label="item.label"
+          :value="item.value">
+        </el-option>
+      </el-select>
+    </div>
+  </section>
+</template>
+
+<style>
+.select {
+  width: 100%;
+}
+</style>
+
+<script>
+const defaultFontSize = [
+  '12px',
+  '13px',
+  '14px',
+  '16px',
+  '18px',
+  '20px',
+  '28px',
+  '36px',
+  '48px'
+];
+import Mixin from './mixin';
+import { getStyleDisplayName } from '../utils/utils.js';
+
+export default {
+  props: {
+    componentName: {
+      type: String
+    },
+    golbalValue: {
+      type: Object
+    }
+  },
+  data() {
+    return {
+      options: [],
+      value: ''
+    };
+  },
+  mixins: [Mixin],
+  computed: {
+    isGlobalInputValue() {
+      return this.config.value.startsWith('$');
+    }
+  },
+  methods: {
+    onSelectChange(e) {
+      this.onChange(e);
+    },
+    initSelectOption() {
+      this.options = [];
+      defaultFontSize.forEach((size) => {
+        this.options.push({
+          value: size,
+          label: size
+        });
+      });
+      const golbalTypography = this.golbalValue.typography;
+      if (this.isGlobalInputValue && golbalTypography) {
+        Object.keys(golbalTypography).forEach((font) => {
+          if (font.includes('font-size')) {
+            const size = golbalTypography[font];
+            this.options.push({
+              value: size.key,
+              label: getStyleDisplayName(size)
+            });
+          }
+        });
+      }
+    }
+  },
+  watch: {
+    'mergedValue': {
+      immediate: true,
+      handler(value) {
+        this.initSelectOption();
+        this.value = this.mergedValue;
+      }
+    }
+  }
+};
+</script>

+ 106 - 0
examples/components/theme-configurator/editor/fontWeight.vue

@@ -0,0 +1,106 @@
+<template>
+  <section class="config" :key="displayName">
+    <div class="config-label">
+      {{displayName}}
+    </div>
+    <div class="config-content">
+      <el-select 
+        v-model="value" 
+        class="select"
+        @change="onSelectChange"
+      >
+        <el-option
+          v-for="item in options"
+          :key="item.value"
+          :label="item.label"
+          :value="item.value">
+        </el-option>
+      </el-select>
+    </div>
+  </section>
+</template>
+
+<style>
+.select {
+  width: 100%;
+}
+</style>
+
+<script>
+const defaultFontWeight = [
+  'normal',
+  'bold',
+  'bolder',
+  'lighter',
+  '100',
+  '200',
+  '300',
+  '400',
+  '500',
+  '600',
+  '700',
+  '800',
+  '900',
+  'inherit'
+];
+import Mixin from './mixin';
+import { getStyleDisplayName } from '../utils/utils.js';
+
+export default {
+  props: {
+    componentName: {
+      type: String
+    },
+    golbalValue: {
+      type: Object
+    }
+  },
+  data() {
+    return {
+      options: [],
+      value: ''
+    };
+  },
+  mixins: [Mixin],
+  computed: {
+    isGlobalInputValue() {
+      return this.config.value.startsWith('$');
+    }
+  },
+  methods: {
+    onSelectChange(e) {
+      this.onChange(e);
+    },
+    initSelectOption() {
+      this.options = [];
+      defaultFontWeight.forEach((weight) => {
+        this.options.push({
+          value: weight,
+          label: weight
+        });
+      });
+      const golbalTypography = this.golbalValue.typography;
+      if (this.isGlobalInputValue && golbalTypography) {
+        Object.keys(golbalTypography).forEach((font) => {
+          if (font.includes('font-weight')) {
+            const weight = golbalTypography[font];
+            this.options.push({
+              value: weight.key,
+              label: getStyleDisplayName(weight)
+            });
+          }
+        });
+      }
+    }
+  },
+  watch: {
+    'mergedValue': {
+      immediate: true,
+      handler(value) {
+        this.initSelectOption();
+        this.value = this.mergedValue;
+      }
+    }
+  }
+};
+</script>

+ 45 - 0
examples/components/theme-configurator/editor/input.vue

@@ -0,0 +1,45 @@
+<template>
+  <el-input 
+    @keyup.enter.native="onUpdate"
+    v-model="value"
+    @blur="onUpdate"
+    v-bind="$attrs"
+  >
+    <template slot="suffix">
+      <slot name="suffix"></slot>
+    </template>
+  </el-input>
+</template>
+
+<script>
+
+export default {
+  props: ['val', 'onChange'],
+  data() {
+    return {
+      value: '',
+      oldValue: ''
+    };
+  },
+  methods: {
+    onUpdate(e) {
+      const { value } = e.target;
+      if (value !== this.oldValue) {
+        this.oldValue = value;
+        this.$emit('change', value);
+      }
+    }
+  },
+  watch: {
+    val: {
+      immediate: true,
+      handler(value) {
+        this.value = value;
+        if (!this.oldValue) {
+          this.oldValue = value;
+        }
+      }
+    }
+  }
+};
+</script>

+ 75 - 0
examples/components/theme-configurator/editor/mixin.vue

@@ -0,0 +1,75 @@
+<style>
+    .config {
+      padding: 5px 0;
+    }
+    .config-label {
+      color: #606266;;
+      font-size: 14px;
+      padding-bottom: 5px;
+      position: relative;
+    }
+    .config-content {
+
+    }
+    .content-80 {
+      box-sizing: border-box;
+      display: inline-block;
+      width: 80%;
+    }
+    .content-20 {
+      box-sizing: border-box;
+      display: inline-block;
+      width: 20%;
+      vertical-align: bottom;
+    }
+    .content-10 {
+      box-sizing: border-box;
+      display: inline-block;
+      width: 10%;
+      vertical-align: bottom;
+    }
+    .content-15 {
+      box-sizing: border-box;
+      display: inline-block;
+      width: 15%;
+      vertical-align: bottom;
+    }
+</style>
+<script>
+import { getStyleDisplayName } from '../utils/utils.js';
+export default {
+  props: {
+    config: {
+      type: Object
+    },
+    userConfig: {
+      type: Object
+    },
+    golbalValue: {
+      type: Object
+    },
+    componentName: {
+      type: String
+    }
+  },
+  computed: {
+    mergedValue() {
+      return this.userConfig[this.config.key] || this.config.value;
+    },
+    displayName() {
+      return getStyleDisplayName(this.config, this.componentName);
+    },
+    isGlobal() {
+      return !this.config.value.startsWith('$');
+    }
+  },
+  methods: {
+    onChange(value) {
+      this.$emit('onChange', {
+        key: this.config.key,
+        value
+      });
+    }
+  }
+};
+</script>

+ 38 - 0
examples/components/theme-configurator/editor/simpleText.vue

@@ -0,0 +1,38 @@
+<template>
+  <section class="config" :key="displayName">
+    <div class="config-label">
+      {{displayName}}
+    </div>
+    <div class="config-content">
+      <theme-input 
+        :val="value"
+        @change="onChange"
+      ></theme-input>
+    </div>
+  </section>
+</template>
+
+<script>
+import Input from './input';
+import Mixin from './mixin';
+
+export default {
+  components: {
+    themeInput: Input
+  },
+  data() {
+    return {
+      value: ''
+    };
+  },
+  mixins: [Mixin],
+  watch: {
+    'mergedValue': {
+      immediate: true,
+      handler(value) {
+        this.value = this.mergedValue;
+      }
+    }
+  }
+};
+</script>

+ 232 - 0
examples/components/theme-configurator/index.vue

@@ -0,0 +1,232 @@
+<template>
+  <div>
+    <el-button
+      round
+      type="primary"
+      size="mini"
+      @click.stop="showConfigurator"
+    >{{getActionDisplayName("theme-editor")}}</el-button>
+    <transition name="fade">
+      <div v-show="visible" class="configurator" ref="configurator">
+        <div 
+          class="main-configurator"
+          ref="mainConfigurator"
+        >
+          <div v-if="currentConfig">
+            <main-panel
+              :currentConfig="currentConfig"
+              :defaultConfig="defaultConfig"
+              :userConfig="userConfig"
+              :globalValue="globalValue"
+              @onChange="userConfigChange"
+            ></main-panel>
+          </div>
+          <div v-if="init && !currentConfig" class="no-config">
+            <img src="../../assets/images/theme-no-config.png" alt>
+            <span>{{getActionDisplayName("no-config")}}</span>
+          </div>
+          <download-area></download-area>
+        </div>
+      </div>
+    </transition>
+  </div>
+</template>
+
+<style>
+.configurator {
+  height: 100%;
+  width: 24%;
+  max-width: 400px;
+  position: fixed;
+  overflow-y: auto;
+  top: 79px;
+  right: 0;
+  bottom: 0;
+  background: #fff;
+  color: #666;
+  line-height: 24px;
+  padding-right: 1%;
+}
+.configurator .main-configurator {
+  height: 100%;
+  overflow: auto;
+  background: #F5F7FA;
+  border: 1px solid #EBEEF5;
+  box-shadow: 0 2px 10px 0 rgba(0,0,0,0.06);
+  border-radius: 8px;
+  width: 97%;
+  box-sizing: border-box;
+  margin: 0 .5% 0 5%;
+}
+.no-config {
+  margin-top: 130px;
+  text-align: center;
+  img {
+    width: 50%;
+    display: block;
+    margin: 0 auto;
+  }
+  span {
+    margin-top: 10px;
+    display: block;
+    color: #909399;
+    font-size: 14px;
+  }
+}
+.fade-enter,.fade-leave-to {
+    transform:translateX(100%);
+}
+.fade-enter-active ,.fade-leave-active {
+    transition:all 0.3s ease;
+}
+
+@media (min-width: 1600px) {
+  .configurator {
+    padding-right: calc((100% - 1600px) / 2);
+  }
+}
+
+</style>
+
+<script>
+import bus from '../../bus';
+import { getVars, updateVars } from './utils/api.js';
+import mainPanel from './main';
+import {
+  filterConfigType,
+  filterGlobalValue,
+  updateDomHeadStyle,
+  getActionDisplayName
+} from './utils/utils.js';
+import DocStyle from './docStyle';
+import Loading from './loading';
+import DownloadArea from './download';
+
+export default {
+  components: {
+    mainPanel,
+    DownloadArea
+  },
+  data() {
+    return {
+      init: false,
+      visible: false,
+      defaultConfig: null,
+      currentConfig: null,
+      userConfig: {
+        global: {},
+        local: {}
+      },
+      lastApply: 0
+    };
+  },
+  mixins: [DocStyle, Loading],
+  computed: {
+    globalValue() {
+      return filterGlobalValue(this.defaultConfig, this.userConfig);
+    }
+  },
+  methods: {
+    getActionDisplayName(key) {
+      return getActionDisplayName(key);
+    },
+    showConfigurator() {
+      this.visible = !this.visible;
+      bus.$emit('user-theme-config-visible', this.visible);
+      window.userThemeConfigVisible = Boolean(this.visible);
+      if (this.init) return;
+      this.$nextTick(() => {
+        const loading = this.$loading({
+          target: this.$refs.configurator
+        });
+        let defaultConfig;
+        getVars()
+          .then((res) => {
+            defaultConfig = res;
+          })
+          .catch((err) => {
+            this.onError(err);
+          })
+          .then(() => {
+            setTimeout(() => {
+              if (defaultConfig) {
+                this.defaultConfig = defaultConfig;
+                this.filterCurrentConfig();
+                this.init = true;
+              }
+              loading.close();
+            }, 300); // action after transition
+          });
+      });
+    },
+    filterCurrentConfig() {
+      this.currentConfig = this.defaultConfig.find((config) => {
+        return config.name === this.$route.path.split('/').pop().toLowerCase();
+      });
+    },
+    userConfigChange(e) {
+      this.$set(this.userConfig[filterConfigType(this.currentConfig.name)], e.key, e.value);
+      this.onAction();
+    },
+    applyStyle(res, time) {
+      if (time < this.lastApply) return;
+      this.updateDocs();
+      updateDomHeadStyle('chalk-style', res);
+      this.lastApply = time;
+    },
+    onDownload() {
+      return updateVars(Object.assign({}, this.userConfig, {download: true}), (xhr) => {
+        xhr.responseType = 'blob';
+      });
+    },
+    onReset() {
+      this.userConfig = {
+        global: {},
+        local: {}
+      };
+      this.onAction();
+    },
+    onAction() {
+      this.triggerComponentLoading(true);
+      const time = +new Date();
+      updateVars(this.userConfig)
+        .then((res) => {
+          this.applyStyle(res, time);
+        })
+        .catch((err) => {
+          this.onError(err);
+        })
+        .then(() => {
+          this.triggerComponentLoading(false);
+        });
+    },
+    onError(err) {
+      let message;
+      try {
+        message = JSON.parse(err).message;
+      } catch (e) {
+        message = err;
+      }
+      this.$message.error(message);
+    },
+    triggerComponentLoading(val) {
+      bus.$emit('user-theme-config-loading', val);
+    },
+    updateDocs() {
+      window.userThemeConfig = JSON.parse(JSON.stringify(this.userConfig));
+      bus.$emit('user-theme-config-update', this.userConfig);
+      this.updateDocStyle(this.userConfig);
+    }
+  },
+  watch: {
+    '$route.path'() {
+      this.defaultConfig && this.filterCurrentConfig();
+      if (!this.$refs.mainConfigurator) return;
+      this.$nextTick(() => {
+        this.$refs.mainConfigurator.scrollTop = 0;
+      });
+
+    }
+  }
+};
+</script>

+ 35 - 0
examples/components/theme-configurator/loading.vue

@@ -0,0 +1,35 @@
+<style>
+  .loadingClass {
+    z-index: 0!important;
+    .el-loading-spinner {
+      top: 0%;
+      margin-top: 30%;
+    }
+  }
+</style>
+<script>
+
+import bus from '../../bus.js';
+import './progress.js';
+
+export default {
+  data() {
+    return {
+      count: 0
+    };
+  },
+  created() {
+    bus.$on('user-theme-config-loading', val => {
+      if (val) {
+        this.count++;
+        if (this.count > 1) return;
+        this.$bar.start();
+      } else {
+        this.count--;
+        if (this.count) return;
+        this.$bar.finish();
+      }
+    });
+  }
+};
+</script>

+ 124 - 0
examples/components/theme-configurator/main.vue

@@ -0,0 +1,124 @@
+<template>
+  <div class="main">
+    <!-- <span>{{configName}}</span> -->
+    <div v-for="(config, key) in configByOrder" :key="key">
+      <span 
+        v-if="showCategory(config.category, key + 1)"
+        class="category-name"
+      >
+        {{getCategoryDisplayName(config.category)}}
+      </span>
+      <component 
+        :is="editorComponent(config.type)"
+        :componentName=configName
+        :config=config
+        :userConfig=userConfigByType
+        :golbalValue=globalValue
+        @onChange=onChange
+      >
+      </component>
+    </div>
+  </div>
+</template>
+
+<style>
+.main {
+  margin-bottom: 140px;
+  padding: 10px;
+}
+.category-name {
+  color: #C0C4CC;
+  font-size: 18px;
+  display: block;
+  margin: 8px 0 4px 0;
+}
+</style>
+
+<script>
+import ColorEditor from './editor/color';
+import fontWeightEditor from './editor/fontWeight';
+import fontSizeEditor from './editor/fontSize';
+import fontLineHeightEditor from './editor/fontLineHeight';
+import borderRadiusEditor from './editor/borderRadius';
+import boxShadowEditor from './editor/boxShadow';
+import simpleTextEditor from './editor/simpleText';
+import { filterConfigType, getCategoryDisplayName } from './utils/utils.js';
+
+export default {
+  components: {
+    ColorEditor,
+    fontSizeEditor,
+    fontLineHeightEditor,
+    simpleTextEditor,
+    borderRadiusEditor,
+    boxShadowEditor,
+    fontWeightEditor
+  },
+  props: {
+    defaultConfig: {
+      type: Array
+    },
+    currentConfig: {
+      type: Object
+    },
+    userConfig: {
+      type: Object
+    },
+    globalValue: {
+      type: Object
+    }
+  },
+  computed: {
+    configName() {
+      return this.currentConfig.name;
+    },
+    userConfigByType() {
+      return this.userConfig[filterConfigType(this.configName)];
+    },
+    configByOrder() {
+      return this.currentConfig.config.sort((a, b) => (a.order - b.order));
+    }
+  },
+  methods: {
+    getCategoryDisplayName(key) {
+      return getCategoryDisplayName(key);
+    },
+    editorComponent(type) {
+      switch (type) {
+        case 'color':
+          return ColorEditor;
+        case 'fontWeight':
+          return fontWeightEditor;
+        case 'fontSize':
+          return fontSizeEditor;
+        case 'fontLineHeight':
+          return fontLineHeightEditor;
+        case 'borderRadius':
+          return borderRadiusEditor;
+        case 'boxShadow':
+          return boxShadowEditor;
+        default:
+          return simpleTextEditor;
+      }
+    },
+    onChange(e) {
+      this.$emit('onChange', e);
+    },
+    showCategory(name, key) {
+      if (!name) {
+        return false;
+      }
+      if (!this.categoryDisplay[name] || this.categoryDisplay[name] === key) {
+        this.categoryDisplay[name] = key;
+        return true;
+      }
+      return false;
+    }
+  },
+  data() {
+    return {
+      categoryDisplay: {}
+    };
+  }
+};
+</script>

+ 6 - 0
examples/components/theme-configurator/progress.js

@@ -0,0 +1,6 @@
+import Vue from 'vue';
+import ProgressBar from './progress.vue';
+
+Vue.prototype.$bar = new Vue(ProgressBar).$mount();
+
+document.body.appendChild(Vue.prototype.$bar.$el);

+ 108 - 0
examples/components/theme-configurator/progress.vue

@@ -0,0 +1,108 @@
+<template>
+  <div class="progress" :style="{
+    'width': percent+'%',
+    'height': height,
+    'background-color': canSuccess? successColor() : failedColor(),
+    'opacity': show ? 1 : 0
+  }"></div>
+</template>
+
+<script>
+/* eslint-disable */
+export default {
+  data () {
+    return {
+      percent: 0,
+      show: false,
+      canSuccess: true,
+      duration: 3000,
+      height: '2px'
+    }
+  },
+  methods: {
+    successColor() {
+      return this.userSelectColor()['$--color-primary'] || '#409EFF';
+    },
+    failedColor() {
+      return this.userSelectColor()['$--color-danger'] || '#F56C6C';
+    },
+    userSelectColor() {
+      return window.userThemeConfig && window.userThemeConfig.global || {}
+    },
+    start () {
+      this.show = true
+      this.canSuccess = true
+      if (this._timer) {
+        clearInterval(this._timer)
+        this.percent = 0
+      }
+      // Code from Nuxt.js!
+      this._cut = 10000 / Math.floor(this.duration)
+      this._timer = setInterval(() => {
+        this.increase(this._cut * Math.random())
+        if (this.percent >= 90) {
+          this.percent = 90;
+        }
+      }, 100)
+      return this
+    },
+    set (num) {
+      this.show = true
+      this.canSuccess = true
+      this.percent = Math.floor(num)
+      return this
+    },
+    get () {
+      return Math.floor(this.percent)
+    },
+    increase (num) {
+      this.percent = this.percent + Math.floor(num)
+      return this
+    },
+    decrease (num) {
+      this.percent = this.percent - Math.floor(num)
+      return this
+    },
+    finish () {
+      this.percent = 100
+      this.hide()
+      return this
+    },
+    pause () {
+      clearInterval(this._timer)
+      return this
+    },
+    hide () {
+      clearInterval(this._timer)
+      this._timer = null
+      setTimeout(() => {
+        this.show = false
+        this.$nextTick(() => {
+          setTimeout(() => {
+            this.percent = 0
+          }, 200)
+        })
+      }, 500)
+      return this
+    },
+    fail () {
+      this.canSuccess = false
+      return this
+    }
+  }
+}
+</script>
+
+<style scoped>
+.progress {
+  position: fixed;
+  top: 0px;
+  left: 0px;
+  right: 0px;
+  height: 2px;
+  width: 0%;
+  transition: width 0.2s, opacity 0.4s;
+  opacity: 1;
+  z-index: 999999;
+}
+</style>

+ 65 - 0
examples/components/theme-configurator/utils/ajax.js

@@ -0,0 +1,65 @@
+const defaultError = 'Server Error 500';
+const defaultTimeout = 'Request Timeout';
+const xhr = (method, url, data = null, cb) => {
+  return new window.Promise((resolve, reject) => {
+    const xhr = new XMLHttpRequest();
+    const doReject = (xhr) => {
+      reject(xhr.response || xhr.statusText || defaultError);
+    };
+    xhr.open(method, url);
+    xhr.setRequestHeader('Content-Type', 'application/json');
+    xhr.timeout = 10000;
+    if (cb) cb(xhr);
+    xhr.onload = () => {
+      if (xhr.readyState === 4) {
+        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
+          let response = xhr.response;
+          const type = xhr.getResponseHeader('Content-Type');
+          if (type.indexOf('zip') > -1) {
+            let filename = 'style.zip';
+            const disposition = xhr.getResponseHeader('content-disposition');
+            if (disposition && disposition.indexOf('attachment') !== -1) {
+              var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
+              var matches = filenameRegex.exec(disposition);
+              if (matches != null && matches[1]) {
+                filename = matches[1].replace(/['"]/g, '');
+              }
+            }
+            var blob = new Blob([response], { type });
+            var zipUrl = URL.createObjectURL(blob);
+            var link = document.createElement('a');
+            link.href = zipUrl;
+            link.download = filename;
+            link.click();
+            resolve(response);
+            return;
+          }
+          try {
+            response = JSON.parse(xhr.response);
+          } catch (e) {}
+          resolve(response);
+        } else {
+          doReject(xhr);
+        }
+      } else {
+        doReject(xhr);
+      }
+    };
+    xhr.onerror = () => {
+      doReject(xhr);
+    };
+    xhr.ontimeout = () => {
+      xhr.abort();
+      reject(defaultTimeout);
+    };
+    xhr.send(JSON.stringify(data));
+  });
+};
+
+export const post = (url, data, cb) => {
+  return xhr('POST', url, data, cb);
+};
+
+export const get = (url) => {
+  return xhr('GET', url);
+};

+ 20 - 0
examples/components/theme-configurator/utils/api.js

@@ -0,0 +1,20 @@
+import Element from 'main/index.js';
+import { post, get } from './ajax';
+
+const { version } = Element;
+
+const hostList = {
+  local: 'http://localhost:3008/',
+  alpha: 'https://ssr.alpha.elenet.me/element-theme-server/',
+  production: 'https://ssr.elenet.me/element-theme-server/'
+};
+
+const host = hostList[process.env.FAAS_ENV] || hostList.production;
+
+export const getVars = () => {
+  return get(`${host}getVariable?version=${version}`);
+};
+
+export const updateVars = (data, cb) => {
+  return post(`${host}updateVariable?version=${version}`, data, cb);
+};

+ 59 - 0
examples/components/theme-configurator/utils/boxShadow.js

@@ -0,0 +1,59 @@
+const VALUES_REG = /,(?![^\(]*\))/;
+const PARTS_REG = /\s(?![^(]*\))/;
+const LENGTH_REG = /^[0-9]+[a-zA-Z%]+?$/;
+
+const parseValue = str => {
+  const parts = str.split(PARTS_REG);
+  const inset = parts.includes('inset');
+  const last = parts.slice(-1)[0];
+  const color = !isLength(last) ? last : undefined;
+
+  const nums = parts
+    .filter(n => n !== 'inset')
+    .filter(n => n !== color)
+    .map(toNum);
+  const [ offsetX, offsetY, blurRadius, spreadRadius ] = nums;
+
+  return {
+    inset,
+    offsetX,
+    offsetY,
+    blurRadius,
+    spreadRadius,
+    color
+  };
+};
+
+const stringifyValue = obj => {
+  const {
+    inset,
+    offsetX = 0,
+    offsetY = 0,
+    blurRadius = 0,
+    spreadRadius,
+    color
+  } = obj || {};
+
+  return [
+    (inset ? 'inset' : null),
+    offsetX,
+    offsetY,
+    blurRadius,
+    spreadRadius,
+    color
+  ].filter(v => v !== null && v !== undefined)
+    .map(toPx)
+    .map(s => ('' + s).trim())
+    .join(' ');
+};
+
+const isLength = v => v === '0' || LENGTH_REG.test(v);
+const toNum = v => {
+  if (!/px$/.test(v) && v !== '0') return v;
+  const n = parseFloat(v);
+  return !isNaN(n) ? n : v;
+};
+const toPx = n => typeof n === 'number' && n !== 0 ? (n + 'px') : n;
+
+export const parse = str => str.split(VALUES_REG).map(s => s.trim()).map(parseValue);
+export const stringify = arr => arr.map(stringifyValue).join(', ');

+ 80 - 0
examples/components/theme-configurator/utils/utils.js

@@ -0,0 +1,80 @@
+import deepmerge from 'deepmerge';
+import constant from '../../../i18n/theme-editor.json';
+
+export const filterConfigType = (name) => {
+  switch (name) {
+    case 'color':
+    case 'typography':
+    case 'border':
+      return 'global';
+    default:
+      return 'local';
+  }
+};
+
+export const filterGlobalValue = (defaultConfig, userConfig) => {
+  const valueObject = {};
+  const globalArr = ['color', 'typography', 'border'];
+  globalArr.forEach((global) => {
+    const configObj = {};
+    defaultConfig
+      .find(config => (config.name === global))
+      .config.forEach(c => (configObj[c.key] = deepmerge({}, c)));
+    valueObject[global] = configObj;
+    Object.keys(configObj).forEach((c) => {
+      if (userConfig.global[c]) {
+        configObj[c].value = userConfig.global[c];
+      }
+    });
+  });
+  return valueObject;
+};
+
+export const getStyleDisplayValue = (displayValue, global) => {
+  if (displayValue.startsWith('$')) {
+    return global[displayValue].value;
+  }
+  return displayValue;
+};
+
+const getLang = () => {
+  return location.hash.replace('#', '').split('/')[1] || 'zh-CN';
+};
+
+const getNameFromI18N = (name) => {
+  const lang = getLang();
+  return constant.filter(config => config.lang === lang)[0][name];
+};
+
+export const getStyleDisplayName = (config, componentName) => {
+  const displayNameMap = getNameFromI18N('display-name');
+  if (config.name !== '[]') {
+    const langIndex = {'zh-CN': '0', 'es': 2, 'fr-FR': 3}[getLang()] || 1;
+    const nameArr = config.name.replace(/\[?\]?/g, '').split(',');
+    return nameArr[langIndex] || nameArr[1];
+  }
+  let displayName = config.key.replace(`$--${componentName}-`, '').replace();
+  Object.keys(displayNameMap).forEach((name) => {
+    displayName = displayName.replace(name, displayNameMap[name]);
+  });
+  displayName = displayName.replace(/-/g, ' ');
+  return displayName.trim();
+};
+
+export const getActionDisplayName = (key) => {
+  return getNameFromI18N('action')[key] || key;
+};
+
+export const getCategoryDisplayName = (key) => {
+  return getNameFromI18N('category')[key] || key;
+};
+
+export const updateDomHeadStyle = (id, styleContent) => {
+  let styleTag = document.getElementById(id);
+  if (!styleTag) {
+    styleTag = document.createElement('style');
+    styleTag.setAttribute('id', id);
+    document.head.appendChild(styleTag);
+  }
+  styleTag.innerText = styleContent.replace(/@font-face{[^}]+}/, '');
+};

+ 181 - 0
examples/docs/en-US/border.md

@@ -0,0 +1,181 @@
+<script>
+  import bus from '../../bus';
+  const varMap = {
+    '$--box-shadow-light': 'boxShadowLight',
+    '$--box-shadow-base': 'boxShadowBase',
+    '$--border-radius-base': 'borderRadiusBase',
+    '$--border-radius-small': 'borderRadiusSmall'
+  };
+  const original = {
+    boxShadowLight: '0 2px 12px 0 rgba(0, 0, 0, 0.1)',
+    boxShadowBase: '0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04)',
+    borderRadiusBase: '4px',
+    borderRadiusSmall: '2px'
+  }
+  export default {
+    created() {
+      bus.$on('user-theme-config-update', this.setGlobal);
+    },
+    mounted() {
+      this.setGlobal();
+    },
+    methods: {
+      setGlobal() {
+        if (window.userThemeConfig) {
+          this.global = window.userThemeConfig.global;
+        }
+      }
+    },
+    data() {
+      return {
+        global: {},
+        boxShadowLight: '',
+        boxShadowBase: '',
+        borderRadiusBase: '',
+        borderRadiusSmall: ''
+      }
+    },
+    watch: {
+      global: {
+        immediate: true,
+        handler(value) {
+          Object.keys(varMap).forEach((c) => {
+            if (value[c]) {
+              this[varMap[c]] = value[c]
+            } else {
+              this[varMap[c]] = original[varMap[c]]
+            }
+          });
+        }
+      }
+    }
+  }
+</script>
+
+<style>
+.demo-border .text {
+  width: 15%;
+}
+.demo-border .line {
+  width: 70%;
+}
+.demo-border .line div{
+  width: 100%;
+  height: 0;
+  border: 1px solid #EEE;
+}
+.demo-border .line .dashed{ 
+  border: 2px dashed #EEE;
+}
+.demo-shadow {
+  height: 100px;
+  width: 50%;
+  border: 1px solid #eee;
+}
+.demo-shadow-text {
+  line-height: 50px;
+  color: #666;
+  font-size: 14px;
+}
+.demo-radius .title{
+  color: #666;
+  font-size: 18px;
+  margin: 10px 0;
+}
+.demo-radius .value{
+  color: #333;
+  font-size: 16px;
+  margin: 10px 0;
+}
+.demo-radius .radius {
+  height: 60px;
+  width: 70%;
+  border: 1px solid #D7DAE2;
+  border-radius: 0;
+  margin-top: 20px;
+}
+.demo-radius .radius-30 {
+  border-radius: 30px;
+}
+</style>
+
+## Border
+
+We standardize the borders that can be used in buttons, cards, pop-ups and other components.
+
+### Border
+
+There are few border styles to choose.
+
+<table class="demo-border">
+  <tbody>
+    <tr>
+      <td class="text">Name</td>
+      <td class="text">Thickness</td>
+      <td class="line">Demo</td>
+    </tr>
+    <tr>
+      <td class="text">Solid</td>
+      <td class="text">1px</td>
+      <td class="line">
+        <div></div>
+      </td>
+    </tr>
+    <tr>
+      <td class="text">Dashed</td>
+      <td class="text">2px</td>
+      <td class="line">
+        <div class="dashed"></div>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+### Radius
+
+There are few radius styles to choose.
+
+<el-row :gutter="12" class="demo-radius">
+  <el-col :span="6" :xs="{span: 12}">
+    <div class="title">No Radius</div>
+    <div class="value">border-radius: 0px</div>
+    <div class="radius"></div>
+  </el-col>
+  <el-col :span="6" :xs="{span: 12}">
+    <div class="title">Small Radius</div>
+    <div class="value">border-radius: {{borderRadiusSmall}}</div>
+    <div 
+      class="radius" 
+      :style="{ borderRadius: borderRadiusSmall }"
+    ></div>
+  </el-col>
+  <el-col :span="6" :xs="{span: 12}">
+    <div class="title">Large Radius</div>
+    <div class="value">border-radius: {{borderRadiusBase}}</div>
+    <div 
+      class="radius"
+      :style="{ borderRadius: borderRadiusBase }"
+    ></div>
+  </el-col>
+  <el-col :span="6" :xs="{span: 12}">
+    <div class="title">Round Radius</div>
+    <div class="value">border-radius: 30px</div>
+    <div class="radius radius-30"></div>
+  </el-col>
+</el-row>
+
+### Shadow
+
+There are few shaodw styles to choose.
+
+<div 
+class="demo-shadow"
+:style="{ boxShadow: boxShadowBase }"
+></div>
+<span class="demo-shadow-text">Basic Shaodw box-shadow: {{boxShadowBase}}</span>
+
+<div 
+class="demo-shadow"
+:style="{ boxShadow: boxShadowLight }"
+></div>
+<span class="demo-shadow-text">Light Shadow box-shadow: {{boxShadowLight}}</span>

+ 245 - 52
examples/docs/en-US/color.md

@@ -1,9 +1,101 @@
+<script>
+  import bus from '../../bus';
+  import { tintColor } from '../../color.js';
+  const varMap = {
+    'primary': '$--color-primary',
+    'success': '$--color-success',
+    'warning': '$--color-warning',
+    'danger': '$--color-danger',
+    'info': '$--color-info',
+    'white': '$--color-white',
+    'black': '$--color-black',
+    'textPrimary': '$--color-text-primary',
+    'textRegular': '$--color-text-regular',
+    'textSecondary': '$--color-text-secondary',
+    'textPlaceholder': '$--color-text-placeholder',
+    'borderBase': '$--border-color-base',
+    'borderLight': '$--border-color-light',
+    'borderLighter': '$--border-color-lighter',
+    'borderExtraLight': '$--border-color-extra-light'
+  };
+  const original = {
+    primary: '#409EFF',
+    success: '#67C23A',
+    warning: '#E6A23C',
+    danger: '#F56C6C',
+    info: '#909399',
+    white: '#FFFFFF',
+    black: '#000000',
+    textPrimary: '#303133',
+    textRegular: '#606266',
+    textSecondary: '#909399',
+    textPlaceholder: '#C0C4CC',
+    borderBase: '#DCDFE6',
+    borderLight: '#E4E7ED',
+    borderLighter: '#EBEEF5',
+    borderExtraLight: '#F2F6FC'
+  }
+  export default {
+    created() {
+      bus.$on('user-theme-config-update', this.setGlobal);
+    },
+    mounted() {
+      this.setGlobal();
+    },
+    methods: {
+      tintColor(color, tint) {
+        return tintColor(color, tint);
+      },
+      setGlobal() {
+        if (window.userThemeConfig) {
+          this.global = window.userThemeConfig.global;
+        }
+      }
+    },
+    data() {
+      return {
+        global: {},
+        primary: '',
+        success: '',
+        warning: '',
+        danger: '',
+        info: '',
+        white: '',
+        black: '',
+        textPrimary: '',
+        textRegular: '',
+        textSecondary: '',
+        textPlaceholder: '',
+        borderBase: '',
+        borderLight: '',
+        borderLighter: '',
+        borderExtraLight: ''
+      }
+    },
+    watch: {
+      global: {
+        immediate: true,
+        handler(value) {
+          Object.keys(original).forEach((o) => {
+            if (value[varMap[o]]) {
+              this[o] = value[varMap[o]]
+            } else {
+              this[o] = original[o]
+            }
+          });
+        }
+      }
+    },
+  }
+</script>
+
 <style>
   .demo-color-box {
+    position: relative;
     border-radius: 4px;
     padding: 20px;
     margin: 5px 0;
-    height: 74px;
+    height: 114px;
     box-sizing: border-box;
     color: #fff;
     font-size: 14px;
@@ -14,6 +106,12 @@
       line-height: 24px;
     }
   }
+  .demo-color-box-other {
+    height: 74px;
+    margin: 10px 0!important;
+    border-radius: 4px 4px 4px 4px!important;
+    padding: 15px 20px;
+  }
   .demo-color-box-group {
     .demo-color-box {
       border-radius: 0;
@@ -26,55 +124,48 @@
       border-radius: 0 0 4px 4px;
     }
   }
-  .bg-blue {
-    background-color: #409EFF;
-  }
-
-  .bg-success {
-    background-color: #13CE66;
+  .bg-color-sub {
+    width: 100%;
+    height: 40px;
+    left: 0;
+    bottom: 0;
+    position: absolute;
+    border-radius: 0 0 4px 4px;
   }
-  .bg-warning {
-    background-color: #f7ba2a;
+  .bg-blue-sub-item {
+    width: 11.1111111%;
+    height: 100%;
+    display: inline-block;
   }
-  .bg-danger {
-    background-color: #ff4949;
+  .bg-blue-sub-item:first-child {
+    border-radius: 0 0 0 4px;
   }
-  .bg-info {
-    background-color: #909399;
-  }
-
-  .bg-text-primary {
-    background-color: #303133;
-  }
-  .bg-text-regular {
-    background-color: #606266;
-  }
-  .bg-text-secondary {
-    background-color: #909399;
-  }
-  .bg-text-placeholder {
-    background-color: #c0c4cc;
-  }
-
-  .bg-border-base {
-    background-color: #dcdfe6;
+  .bg-success-sub-item {
+    width: 50%;
+    height: 100%;
+    display: inline-block;
   }
-  .bg-border-light {
-    background-color: #e4e7ed;
+  .bg-success-sub-item:first-child {
+    border-radius: 0 0 0 4px;
   }
-  .bg-border-lighter {
-    background-color: #ebeef5;
+  .bg-success-sub-item:last-child {
+    border-radius: 0 0 4px 0;
   }
-  .bg-border-extra-light {
-    background-color: #f2f6fc;
+  .bg-transparent {
+    border: 1px solid #FCC3C3;
+    color: #303133;
+    background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' preserveAspectRatio='none' viewBox='0 0 100 100'><path d='M0 98 L100 0 L100 1 L1 98' fill='%23FCC3C3' /></svg>");
+    background-repeat:no-repeat;
+    background-position:center center;
+    background-size: 100% 100%, auto;
   }
-
-  [class*=" bg-border-"] {
+  .demo-color-box-lite {
     color: #303133;
   }
 </style>
 
 ## Color
+
 Element uses a specific set of palettes to specify colors to provide a consistent look and feel for the products you build.
 
 ### Main Color
@@ -82,8 +173,24 @@ Element uses a specific set of palettes to specify colors to provide a consisten
 The main color of Element is bright and friendly blue.
 
 <el-row :gutter="12">
-  <el-col :span="6" :xs="{span: 12}">
-    <div class="demo-color-box bg-blue">Blue<div class="value">#409EFF</div></div>
+  <el-col :span="10" :xs="{span: 12}">
+    <div 
+      class="demo-color-box"
+      :style="{ background: primary }"
+    >
+      Brand Color<div class="value">#409EFF</div>
+    <div 
+      class="bg-color-sub"
+      :style="{ background: tintColor(primary, 0.9) }"
+    >
+    <div 
+      class="bg-blue-sub-item" 
+      v-for="(item, key) in Array(8)"
+      :key="key"
+      :style="{ background: tintColor(primary, (key + 1) / 10) }"
+        >
+    </div>
+    </div>
   </el-col>
 </el-row>
 
@@ -93,16 +200,72 @@ Besides the main color, you need to use different scene colors in different scen
 
 <el-row :gutter="12">
   <el-col :span="6" :xs="{span: 12}">
-    <div class="demo-color-box bg-success">Success<div class="value">#67C23A</div></div>
+    <div class="demo-color-box"
+    :style="{ background: success }"
+    >Success<div class="value">#67C23A</div>
+      <div 
+        class="bg-color-sub"
+      >
+        <div 
+          class="bg-success-sub-item" 
+          v-for="(item, key) in Array(2)"
+          :key="key"
+          :style="{ background: tintColor(success, (key + 8) / 10) }"
+            >
+        </div>
+      </div>
+    </div>
   </el-col>
   <el-col :span="6" :xs="{span: 12}">
-    <div class="demo-color-box bg-warning">Warning<div class="value">#E6A23C</div></div>
+    <div class="demo-color-box"
+    :style="{ background: warning }"
+    >Warning<div class="value">#E6A23C</div>
+      <div 
+          class="bg-color-sub"
+        >
+        <div 
+          class="bg-success-sub-item" 
+          v-for="(item, key) in Array(2)"
+          :key="key"
+          :style="{ background: tintColor(warning, (key + 8) / 10) }"
+            >
+        </div>
+      </div>
+    </div>
   </el-col>
   <el-col :span="6" :xs="{span: 12}">
-    <div class="demo-color-box bg-danger">Danger<div class="value">#F56C6C</div></div>
+    <div class="demo-color-box"
+    :style="{ background: danger }"
+    >Danger<div class="value">#F56C6C</div>
+      <div 
+          class="bg-color-sub"
+        >
+        <div 
+          class="bg-success-sub-item" 
+          v-for="(item, key) in Array(2)"
+          :key="key"
+          :style="{ background: tintColor(danger, (key + 8) / 10) }"
+            >
+        </div>
+      </div>
+    </div>
   </el-col>
   <el-col :span="6" :xs="{span: 12}">
-    <div class="demo-color-box bg-info">Info<div class="value">#909399</div></div>
+    <div class="demo-color-box"
+    :style="{ background: info }"
+    >Info<div class="value">#909399</div>
+      <div 
+          class="bg-color-sub"
+        >
+        <div 
+          class="bg-success-sub-item" 
+          v-for="(item, key) in Array(2)"
+          :key="key"
+          :style="{ background: tintColor(info, (key + 8) / 10) }"
+            >
+        </div>
+      </div>
+    </div>
   </el-col>
 </el-row>
 
@@ -113,18 +276,48 @@ Neutral colors are for text, background and border colors. You can use different
 <el-row :gutter="12">
   <el-col :span="6" :xs="{span: 12}">
     <div class="demo-color-box-group">
-      <div class="demo-color-box bg-text-primary">Primary Text<div class="value">#303133</div></div>
-      <div class="demo-color-box bg-text-regular">Regular Text<div class="value">#606266</div></div>
-      <div class="demo-color-box bg-text-secondary">Secondary Text<div class="value">#909399</div></div>
-      <div class="demo-color-box bg-text-placeholder">Placeholder Text<div class="value">#C0C4CC</div></div>
+      <div class="demo-color-box demo-color-box-other"
+      :style="{ background: textPrimary }"
+      >Primary Text<div class="value">{{textPrimary}}</div></div>
+      <div class="demo-color-box demo-color-box-other"
+      :style="{ background: textRegular }"
+      >
+      Regular Text<div class="value">{{textRegular}}</div></div>
+      <div class="demo-color-box demo-color-box-other"
+      :style="{ background: textSecondary }"
+      >Secondary Text<div class="value">{{textSecondary}}</div></div>
+      <div class="demo-color-box demo-color-box-other"
+      :style="{ background: textPlaceholder }"
+      >Placeholder Text<div class="value">{{textPlaceholder}}</div></div>
+    </div>
+  </el-col>
+  <el-col :span="6" :xs="{span: 12}">
+    <div class="demo-color-box-group">
+      <div class="demo-color-box demo-color-box-other demo-color-box-lite"
+      :style="{ background: borderBase }"
+      >Base Border<div class="value">{{borderBase}}</div></div>
+      <div class="demo-color-box demo-color-box-other demo-color-box-lite"
+      :style="{ background: borderLight }"
+      >Light Border<div class="value">{{borderLight}}</div></div>
+      <div class="demo-color-box demo-color-box-other demo-color-box-lite"
+      :style="{ background: borderLighter }"
+      >Lighter Border<div class="value">{{borderLighter}}</div></div>
+      <div class="demo-color-box demo-color-box-other demo-color-box-lite"
+      :style="{ background: borderExtraLight }"
+      >Extra Light Border<div class="value">{{borderExtraLight}}</div></div>
     </div>
   </el-col>
   <el-col :span="6" :xs="{span: 12}">
     <div class="demo-color-box-group">
-      <div class="demo-color-box bg-border-base">Base Border<div class="value">#DCDFE6</div></div>
-      <div class="demo-color-box bg-border-light">Light Border<div class="value">#E4E7ED</div></div>
-      <div class="demo-color-box bg-border-lighter">Lighter Border<div class="value">#EBEEF5</div></div>
-      <div class="demo-color-box bg-border-extra-light">Extra Light Border<div class="value">#F2F6FC</div></div>
+      <div 
+      class="demo-color-box demo-color-box-other"
+      :style="{ background: black }"
+      >Basic Black<div class="value">{{black}}</div></div>
+      <div
+      class="demo-color-box demo-color-box-other"
+      :style="{ background: white, color: '#303133', border: '1px solid #eee' }"
+      >Basic White<div class="value">{{white}}</div></div>
+      <div class="demo-color-box demo-color-box-other bg-transparent">Transparent<div class="value">Transparent</div>
     </div>
   </el-col>
 </el-row>

+ 150 - 119
examples/docs/en-US/typography.md

@@ -1,74 +1,96 @@
-<style>
-  .demo-typo-box {
-    height: 200px;
-    width: 200px;
-    position: relative;
-    border: 1px solid #eaeefb;
-    font-size: 40px;
-    color: #1f2d3d;
-    text-align: center;
-    line-height: 162px;
-    padding-bottom: 36px;
-    box-sizing: border-box;
-    display: inline-block;
-    margin-right: 17px;
-    border-radius: 4px;
-
-    .name {
-      position: absolute;
-      bottom: 0;
-      width: 100%;
-      height: 35px;
-      border-top: 1px solid #eaeefb;
-      font-size: 14px;
-      color: #8492a6;
-      line-height: 35px;
-      text-align: left;
-      text-indent: 10px;
-      font-family: 'Helvetica Neue';
-    }
+<script>
+  import bus from '../../bus';
+  const varMap = [
+    '$--font-size-extra-large',
+    '$--font-size-large',
+    '$--font-size-medium',
+    '$--font-size-base',
+    '$--font-size-small',
+    '$--font-size-extra-small'
+  ];
+  const original = {
+    'font_size_extra_large': '20px',
+    'font_size_large': '18px',
+    'font_size_medium': '16px',
+    'font_size_base': '14px',
+    'font_size_small': '13px',
+    'font_size_extra_small': '12px'
+  }
+  export default {
+    created() {
+      bus.$on('user-theme-config-update', this.setGlobal);
+    },
+    mounted() {
+      this.setGlobal();
+    },
+    methods: {
+      tintColor(color, tint) {
+        return tintColor(color, tint);
+      },
+      setGlobal() {
+        if (window.userThemeConfig) {
+          this.global = window.userThemeConfig.global;
+        }
+      }
+    },
+    data() {
+      return {
+        global: {},
+        'font_size_extra_large': '',
+        'font_size_large': '',
+        'font_size_medium': '',
+        'font_size_base': '',
+        'font_size_small': '',
+        'font_size_extra_small': ''
+      }
+    },
+    watch: {
+      global: {
+        immediate: true,
+        handler(value) {
+          varMap.forEach((v) => {
+            const key = v.replace('$--', '').replace(/-/g, '_')
+            if (value[v]) {
+              this[key] = value[v]
+            } else {
+              this[key] = original[key]
+            }
+          });
+        }
+      }
+    },
   }
+</script>
+<style>
   .demo-typo-size {
-    .h1 {
-      font-size: 20px;
-    }
-    .h2 {
-      font-size: 18px;
-    }
-    .h3 {
-      font-size: 16px;
-    }
-    .text-regular {
-      font-size: 14px;
-    }
-    .text-small {
-      font-size: 13px;
-    }
-    .text-smaller {
-      font-size: 12px;
-    }
     .color-dark-light {
       color: #99a9bf;
     }
   }
-  .typo-PingFang {
-    font-family: 'PingFang SC';
-  }
-  .typo-Hiragino {
-    font-family: 'Hiragino Sans GB';
+  .demo-term-box img{
+    width: 24%;
+    margin: 0 4% 20px 0;
   }
-  .typo-Microsoft {
-    font-family: 'Microsoft YaHei';
+
+  .lineH-left {
+    display: inline-block;
+    height: 80px
   }
-  /* 英文 */
-  .typo-Helvetica-Neue {
-    font-family: 'Helvetica Neue';
+  .lineH-right {
+    display: inline-block;
+    list-style: none;
+    padding: 0 0 0 90px;
+    margin: 0;
+    vertical-align: top;
   }
-  .typo-Helvetica {
-    font-family: 'Helvetica';
+  .lineH-right li{
+    font-size: 13px;
+    color: #666;
+    height: 20px;
+    line-height: 20px;
   }
-  .typo-Arial {
-    font-family: 'Arial';
+  .lineH-right li span{
+    padding-left: 40px;
   }
 </style>
 
@@ -76,76 +98,85 @@
 
 We create a font convention to ensure the best presentation across different platforms.
 
-### Chinese Font
-
-<div class="demo-typo-box typo-PingFang">
-  和畅惠风
-  <div class="name">PingFang SC</div>
-</div>
-<div class="demo-typo-box typo-Hiragino">
-  和畅惠风
-  <div class="name">Hiragino Sans GB</div>
+### Font
+<div class="demo-term-box">
+<img src="../../assets/images/term-pingfang.png" alt="">
+<img src="../../assets/images/term-hiragino.png" alt="">
+<img src="../../assets/images/term-microsoft.png" alt="">
+<img src="../../assets/images/term-sf.png" alt="">
+<img src="../../assets/images/term-helvetica.png" alt="">
+<img src="../../assets/images/term-arial.png" alt="">
 </div>
-<div class="demo-typo-box typo-Microsoft">
-  和畅惠风
-  <div class="name">Microsoft YaHei</div>
-</div>
-
-### English / Numberic Font
-
-<div class="demo-typo-box typo-Helvetica-neue">
-  RGag
-  <div class="name">Helvetica Neue</div>
-</div>
-<div class="demo-typo-box typo-Helvetica">
-  RGag
-  <div class="name">Helvetica</div>
-</div>
-<div class="demo-typo-box typo-Arial">
-  RGag
-  <div class="name">Arial</div>
-</div>
-
-### Font-family
-
-```css
-font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif;
-```
 
 ### Font Convention
 
 <table class="demo-typo-size">
   <tbody>
-    <tr>
-      <td class="h1">Main Title</td>
-      <td class="h1">Build with Element</td>
-      <td class="color-dark-light">20px  Extra large</td>
+  <tr
+    >
+      <td>Level</td>
+      <td>Font Size</td>
+      <td class="color-dark-light">Demo</td>
     </tr>
-    <tr>
-      <td class="h2">Title</td>
-      <td class="h2">Build with Element</td>
-      <td class="color-dark-light">18px large</td>
+    <tr
+    :style="{ fontSize: font_size_extra_small }"
+    >
+      <td>Supplementary text</td>
+      <td class="color-dark-light">{{font_size_extra_small}} Extra Small</td>
+      <td>Build with Element</td>
     </tr>
-    <tr>
-      <td class="h3">Small Title</td>
-      <td class="h3">Build with Element</td>
-      <td class="color-dark-light">16px Medium</td>
+    <tr
+    :style="{ fontSize: font_size_small }"
+    >
+      <td>Body (small)</td>
+      <td class="color-dark-light">{{font_size_small}} Small</td>
+      <td>Build with Element</td>
     </tr>
-    <tr>
-      <td class="text-regular">Body</td>
-      <td class="text-regular">Build with Element</td>
-      <td class="color-dark-light">14px Small</td>
+    <tr
+    :style="{ fontSize: font_size_base }"
+    >
+      <td>Body</td>
+      <td class="color-dark-light">{{font_size_base}} Base</td>
+      <td>Build with Element</td>
     </tr>
-    <tr>
-      <td class="text-small">Body (small)</td>
-      <td class="text-small">Build with Element</td>
-      <td class="color-dark-light">13px Extra Small</td>
+    <tr
+    :style="{ fontSize: font_size_medium }"
+    >
+      <td >Small Title</td>
+      <td class="color-dark-light">{{font_size_medium}} Medium</td>
+      <td>Build with Element</td>
     </tr>
-    <tr>
-      <td class="text-smaller">Supplementary text</td>
-      <td class="text-smaller">Build with Element</td>
-      <td class="color-dark-light">12px Extra Extra Small</td>
+    <tr
+    :style="{ fontSize: font_size_large }"
+    >
+      <td>Title</td>
+      <td class="color-dark-light">{{font_size_large}} large</td>
+      <td>Build with Element</td>
+    </tr>
+    <tr
+    :style="{ fontSize: font_size_extra_large }"
+    >
+      <td>Main Title</td>
+      <td class="color-dark-light">{{font_size_extra_large}} Extra large</td>
+      <td>Build with Element</td>
     </tr>
   </tbody>
 </table>
 
+### Font Line Height
+
+<div>
+<img class="lineH-left" src="~examples/assets/images/typography.png" />
+<ul class="lineH-right">
+<li>line-height:1 <span>No line height</span></li>
+<li>line-height:1.3 <span>Compact</span></li>
+<li>line-height:1.5 <span>Regular</span></li>
+<li>line-height:1.7 <span>Loose</span></li>
+</ul>
+</div>
+
+### Font-family
+
+```css
+font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif;
+```

+ 181 - 0
examples/docs/es/border.md

@@ -0,0 +1,181 @@
+<script>
+  import bus from '../../bus';
+  const varMap = {
+    '$--box-shadow-light': 'boxShadowLight',
+    '$--box-shadow-base': 'boxShadowBase',
+    '$--border-radius-base': 'borderRadiusBase',
+    '$--border-radius-small': 'borderRadiusSmall'
+  };
+  const original = {
+    boxShadowLight: '0 2px 12px 0 rgba(0, 0, 0, 0.1)',
+    boxShadowBase: '0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04)',
+    borderRadiusBase: '4px',
+    borderRadiusSmall: '2px'
+  }
+  export default {
+    created() {
+      bus.$on('user-theme-config-update', this.setGlobal);
+    },
+    mounted() {
+      this.setGlobal();
+    },
+    methods: {
+      setGlobal() {
+        if (window.userThemeConfig) {
+          this.global = window.userThemeConfig.global;
+        }
+      }
+    },
+    data() {
+      return {
+        global: {},
+        boxShadowLight: '',
+        boxShadowBase: '',
+        borderRadiusBase: '',
+        borderRadiusSmall: ''
+      }
+    },
+    watch: {
+      global: {
+        immediate: true,
+        handler(value) {
+          Object.keys(varMap).forEach((c) => {
+            if (value[c]) {
+              this[varMap[c]] = value[c]
+            } else {
+              this[varMap[c]] = original[varMap[c]]
+            }
+          });
+        }
+      }
+    }
+  }
+</script>
+
+<style>
+.demo-border .text {
+  width: 15%;
+}
+.demo-border .line {
+  width: 70%;
+}
+.demo-border .line div{
+  width: 100%;
+  height: 0;
+  border: 1px solid #EEE;
+}
+.demo-border .line .dashed{ 
+  border: 2px dashed #EEE;
+}
+.demo-shadow {
+  height: 100px;
+  width: 50%;
+  border: 1px solid #eee;
+}
+.demo-shadow-text {
+  line-height: 50px;
+  color: #666;
+  font-size: 14px;
+}
+.demo-radius .title{
+  color: #666;
+  font-size: 18px;
+  margin: 10px 0;
+}
+.demo-radius .value{
+  color: #333;
+  font-size: 16px;
+  margin: 10px 0;
+}
+.demo-radius .radius {
+  height: 60px;
+  width: 70%;
+  border: 1px solid #D7DAE2;
+  border-radius: 0;
+  margin-top: 20px;
+}
+.demo-radius .radius-30 {
+  border-radius: 30px;
+}
+</style>
+
+## Border
+
+We standardize the borders that can be used in buttons, cards, pop-ups and other components.
+
+### Border
+
+There are few border styles to choose.
+
+<table class="demo-border">
+  <tbody>
+    <tr>
+      <td class="text">Name</td>
+      <td class="text">Thickness</td>
+      <td class="line">Demo</td>
+    </tr>
+    <tr>
+      <td class="text">Solid</td>
+      <td class="text">1px</td>
+      <td class="line">
+        <div></div>
+      </td>
+    </tr>
+    <tr>
+      <td class="text">Dashed</td>
+      <td class="text">2px</td>
+      <td class="line">
+        <div class="dashed"></div>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+### Radius
+
+There are few radius styles to choose.
+
+<el-row :gutter="12" class="demo-radius">
+  <el-col :span="6" :xs="{span: 12}">
+    <div class="title">No Radius</div>
+    <div class="value">border-radius: 0px</div>
+    <div class="radius"></div>
+  </el-col>
+  <el-col :span="6" :xs="{span: 12}">
+    <div class="title">Small Radius</div>
+    <div class="value">border-radius: {{borderRadiusSmall}}</div>
+    <div 
+      class="radius" 
+      :style="{ borderRadius: borderRadiusSmall }"
+    ></div>
+  </el-col>
+  <el-col :span="6" :xs="{span: 12}">
+    <div class="title">Large Radius</div>
+    <div class="value">border-radius: {{borderRadiusBase}}</div>
+    <div 
+      class="radius"
+      :style="{ borderRadius: borderRadiusBase }"
+    ></div>
+  </el-col>
+  <el-col :span="6" :xs="{span: 12}">
+    <div class="title">Round Radius</div>
+    <div class="value">border-radius: 30px</div>
+    <div class="radius radius-30"></div>
+  </el-col>
+</el-row>
+
+### Shadow
+
+There are few shaodw styles to choose.
+
+<div 
+class="demo-shadow"
+:style="{ boxShadow: boxShadowBase }"
+></div>
+<span class="demo-shadow-text">Basic Shaodw box-shadow: {{boxShadowBase}}</span>
+
+<div 
+class="demo-shadow"
+:style="{ boxShadow: boxShadowLight }"
+></div>
+<span class="demo-shadow-text">Light Shadow box-shadow: {{boxShadowLight}}</span>

+ 245 - 53
examples/docs/es/color.md

@@ -1,19 +1,117 @@
+<script>
+  import bus from '../../bus';
+  import { tintColor } from '../../color.js';
+  const varMap = {
+    'primary': '$--color-primary',
+    'success': '$--color-success',
+    'warning': '$--color-warning',
+    'danger': '$--color-danger',
+    'info': '$--color-info',
+    'white': '$--color-white',
+    'black': '$--color-black',
+    'textPrimary': '$--color-text-primary',
+    'textRegular': '$--color-text-regular',
+    'textSecondary': '$--color-text-secondary',
+    'textPlaceholder': '$--color-text-placeholder',
+    'borderBase': '$--border-color-base',
+    'borderLight': '$--border-color-light',
+    'borderLighter': '$--border-color-lighter',
+    'borderExtraLight': '$--border-color-extra-light'
+  };
+  const original = {
+    primary: '#409EFF',
+    success: '#67C23A',
+    warning: '#E6A23C',
+    danger: '#F56C6C',
+    info: '#909399',
+    white: '#FFFFFF',
+    black: '#000000',
+    textPrimary: '#303133',
+    textRegular: '#606266',
+    textSecondary: '#909399',
+    textPlaceholder: '#C0C4CC',
+    borderBase: '#DCDFE6',
+    borderLight: '#E4E7ED',
+    borderLighter: '#EBEEF5',
+    borderExtraLight: '#F2F6FC'
+  }
+  export default {
+    created() {
+      bus.$on('user-theme-config-update', this.setGlobal);
+    },
+    mounted() {
+      this.setGlobal();
+    },
+    methods: {
+      tintColor(color, tint) {
+        return tintColor(color, tint);
+      },
+      setGlobal() {
+        if (window.userThemeConfig) {
+          this.global = window.userThemeConfig.global;
+        }
+      }
+    },
+    data() {
+      return {
+        global: {},
+        primary: '',
+        success: '',
+        warning: '',
+        danger: '',
+        info: '',
+        white: '',
+        black: '',
+        textPrimary: '',
+        textRegular: '',
+        textSecondary: '',
+        textPlaceholder: '',
+        borderBase: '',
+        borderLight: '',
+        borderLighter: '',
+        borderExtraLight: ''
+      }
+    },
+    watch: {
+      global: {
+        immediate: true,
+        handler(value) {
+          Object.keys(original).forEach((o) => {
+            if (value[varMap[o]]) {
+              this[o] = value[varMap[o]]
+            } else {
+              this[o] = original[o]
+            }
+          });
+        }
+      }
+    },
+  }
+</script>
+
 <style>
   .demo-color-box {
+    position: relative;
     border-radius: 4px;
     padding: 20px;
     margin: 5px 0;
-    height: 74px;
+    height: 114px;
     box-sizing: border-box;
     color: #fff;
     font-size: 14px;
-    
+
     & .value {
       font-size: 12px;
       opacity: 0.69;
       line-height: 24px;
     }
   }
+  .demo-color-box-other {
+    height: 74px;
+    margin: 10px 0!important;
+    border-radius: 4px 4px 4px 4px!important;
+    padding: 15px 20px;
+  }
   .demo-color-box-group {
     .demo-color-box {
       border-radius: 0;
@@ -26,50 +124,42 @@
       border-radius: 0 0 4px 4px;
     }
   }
-  .bg-blue {
-    background-color: #409EFF;
+  .bg-color-sub {
+    width: 100%;
+    height: 40px;
+    left: 0;
+    bottom: 0;
+    position: absolute;
+    border-radius: 0 0 4px 4px;
   }
-
-  .bg-success {
-    background-color: #13CE66;
+  .bg-blue-sub-item {
+    width: 11.1111111%;
+    height: 100%;
+    display: inline-block;
   }
-  .bg-warning {
-    background-color: #f7ba2a;
+  .bg-blue-sub-item:first-child {
+    border-radius: 0 0 0 4px;
   }
-  .bg-danger {
-    background-color: #ff4949;
+  .bg-success-sub-item {
+    width: 50%;
+    height: 100%;
+    display: inline-block;
   }
-  .bg-info {
-    background-color: #909399;
-  }
-
-  .bg-text-primary {
-    background-color: #303133;
-  }
-  .bg-text-regular {
-    background-color: #606266;
-  }
-  .bg-text-secondary {
-    background-color: #909399;
-  }
-  .bg-text-placeholder {
-    background-color: #c0c4cc;
-  }
-
-  .bg-border-base {
-    background-color: #dcdfe6;
+  .bg-success-sub-item:first-child {
+    border-radius: 0 0 0 4px;
   }
-  .bg-border-light {
-    background-color: #e4e7ed;
+  .bg-success-sub-item:last-child {
+    border-radius: 0 0 4px 0;
   }
-  .bg-border-lighter {
-    background-color: #ebeef5;
-  }
-  .bg-border-extra-light {
-    background-color: #f2f6fc;
+  .bg-transparent {
+    border: 1px solid #FCC3C3;
+    color: #303133;
+    background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' preserveAspectRatio='none' viewBox='0 0 100 100'><path d='M0 98 L100 0 L100 1 L1 98' fill='%23FCC3C3' /></svg>");
+    background-repeat:no-repeat;
+    background-position:center center;
+    background-size: 100% 100%, auto;
   }
-
-  [class*=" bg-border-"] {
+  .demo-color-box-lite {
     color: #303133;
   }
 </style>
@@ -82,8 +172,24 @@ Element utiliza un conjunto de paletas para especificar colores, y así, proporc
 El color principal de Element es el azul brillante y amigable.
 
 <el-row :gutter="12">
-  <el-col :span="6" :xs="{span: 12}">
-    <div class="demo-color-box bg-blue">Azul<div class="value">#409EFF</div></div>
+  <el-col :span="10" :xs="{span: 12}">
+    <div 
+      class="demo-color-box"
+      :style="{ background: primary }"
+    >
+      Brand Color<div class="value">#409EFF</div>
+    <div 
+      class="bg-color-sub"
+      :style="{ background: tintColor(primary, 0.9) }"
+    >
+    <div 
+      class="bg-blue-sub-item" 
+      v-for="(item, key) in Array(8)"
+      :key="key"
+      :style="{ background: tintColor(primary, (key + 1) / 10) }"
+        >
+    </div>
+    </div>
   </el-col>
 </el-row>
 
@@ -93,16 +199,72 @@ Además del color principal, se necesitan utilizar distintos colores para difere
 
 <el-row :gutter="12">
   <el-col :span="6" :xs="{span: 12}">
-    <div class="demo-color-box bg-success">Éxito<div class="value">#67C23A</div></div>
+    <div class="demo-color-box"
+    :style="{ background: success }"
+    >Success<div class="value">#67C23A</div>
+      <div 
+        class="bg-color-sub"
+      >
+        <div 
+          class="bg-success-sub-item" 
+          v-for="(item, key) in Array(2)"
+          :key="key"
+          :style="{ background: tintColor(success, (key + 8) / 10) }"
+            >
+        </div>
+      </div>
+    </div>
   </el-col>
   <el-col :span="6" :xs="{span: 12}">
-    <div class="demo-color-box bg-warning">Precaución<div class="value">#E6A23C</div></div>
+    <div class="demo-color-box"
+    :style="{ background: warning }"
+    >Warning<div class="value">#E6A23C</div>
+      <div 
+          class="bg-color-sub"
+        >
+        <div 
+          class="bg-success-sub-item" 
+          v-for="(item, key) in Array(2)"
+          :key="key"
+          :style="{ background: tintColor(warning, (key + 8) / 10) }"
+            >
+        </div>
+      </div>
+    </div>
   </el-col>
   <el-col :span="6" :xs="{span: 12}">
-    <div class="demo-color-box bg-danger">Peligro<div class="value">#F56C6C</div></div>
+    <div class="demo-color-box"
+    :style="{ background: danger }"
+    >Danger<div class="value">#F56C6C</div>
+      <div 
+          class="bg-color-sub"
+        >
+        <div 
+          class="bg-success-sub-item" 
+          v-for="(item, key) in Array(2)"
+          :key="key"
+          :style="{ background: tintColor(danger, (key + 8) / 10) }"
+            >
+        </div>
+      </div>
+    </div>
   </el-col>
   <el-col :span="6" :xs="{span: 12}">
-    <div class="demo-color-box bg-info">Info<div class="value">#909399</div></div>
+    <div class="demo-color-box"
+    :style="{ background: info }"
+    >Info<div class="value">#909399</div>
+      <div 
+          class="bg-color-sub"
+        >
+        <div 
+          class="bg-success-sub-item" 
+          v-for="(item, key) in Array(2)"
+          :key="key"
+          :style="{ background: tintColor(info, (key + 8) / 10) }"
+            >
+        </div>
+      </div>
+    </div>
   </el-col>
 </el-row>
 
@@ -113,18 +275,48 @@ Los colores neutrales son para texto, fondos y bordes. Puede usar diferentes col
 <el-row :gutter="12">
   <el-col :span="6" :xs="{span: 12}">
     <div class="demo-color-box-group">
-      <div class="demo-color-box bg-text-primary">Texto primario<div class="value">#303133</div></div>
-      <div class="demo-color-box bg-text-regular">Texto regular<div class="value">#606266</div></div>
-      <div class="demo-color-box bg-text-secondary">Texto secundario<div class="value">#909399</div></div>
-      <div class="demo-color-box bg-text-placeholder">Texto de placeholder<div class="value">#C0C4CC</div></div>
+      <div class="demo-color-box demo-color-box-other"
+      :style="{ background: textPrimary }"
+      >Texto primario<div class="value">{{textPrimary}}</div></div>
+      <div class="demo-color-box demo-color-box-other"
+      :style="{ background: textRegular }"
+      >
+      Texto regular<div class="value">{{textRegular}}</div></div>
+      <div class="demo-color-box demo-color-box-other"
+      :style="{ background: textSecondary }"
+      >Texto secundario<div class="value">{{textSecondary}}</div></div>
+      <div class="demo-color-box demo-color-box-other"
+      :style="{ background: textPlaceholder }"
+      >Texto de placeholder<div class="value">{{textPlaceholder}}</div></div>
+    </div>
+  </el-col>
+  <el-col :span="6" :xs="{span: 12}">
+    <div class="demo-color-box-group">
+      <div class="demo-color-box demo-color-box-other demo-color-box-lite"
+      :style="{ background: borderBase }"
+      >Borde base<div class="value">{{borderBase}}</div></div>
+      <div class="demo-color-box demo-color-box-other demo-color-box-lite"
+      :style="{ background: borderLight }"
+      >Borde ligero<div class="value">{{borderLight}}</div></div>
+      <div class="demo-color-box demo-color-box-other demo-color-box-lite"
+      :style="{ background: borderLighter }"
+      >Borde claro<div class="value">{{borderLighter}}</div></div>
+      <div class="demo-color-box demo-color-box-other demo-color-box-lite"
+      :style="{ background: borderExtraLight }"
+      >Borde extra claro<div class="value">{{borderExtraLight}}</div></div>
     </div>
   </el-col>
   <el-col :span="6" :xs="{span: 12}">
     <div class="demo-color-box-group">
-      <div class="demo-color-box bg-border-base">Borde base<div class="value">#DCDFE6</div></div>
-      <div class="demo-color-box bg-border-light">Borde ligero<div class="value">#E4E7ED</div></div>
-      <div class="demo-color-box bg-border-lighter">Borde claro<div class="value">#EBEEF5</div></div>
-      <div class="demo-color-box bg-border-extra-light">Borde extra claro<div class="value">#F2F6FC</div></div>
+      <div 
+      class="demo-color-box demo-color-box-other"
+      :style="{ background: black }"
+      >Basic Black<div class="value">{{black}}</div></div>
+      <div
+      class="demo-color-box demo-color-box-other"
+      :style="{ background: white, color: '#303133', border: '1px solid #eee' }"
+      >Basic White<div class="value">{{white}}</div></div>
+      <div class="demo-color-box demo-color-box-other bg-transparent">Transparent<div class="value">Transparent</div>
     </div>
   </el-col>
 </el-row>

+ 150 - 119
examples/docs/es/typography.md

@@ -1,74 +1,96 @@
-<style>
-  .demo-typo-box {
-    height: 200px;
-    width: 200px;
-    position: relative;
-    border: 1px solid #eaeefb;
-    font-size: 40px;
-    color: #1f2d3d;
-    text-align: center;
-    line-height: 162px;
-    padding-bottom: 36px;
-    box-sizing: border-box;
-    display: inline-block;
-    margin-right: 17px;
-    border-radius: 4px;
-    
-    .name {
-      position: absolute;
-      bottom: 0;
-      width: 100%;
-      height: 35px;
-      border-top: 1px solid #eaeefb;
-      font-size: 14px;
-      color: #8492a6;
-      line-height: 35px;
-      text-align: left;
-      text-indent: 10px;
-      font-family: 'Helvetica Neue';
-    }
+<script>
+  import bus from '../../bus';
+  const varMap = [
+    '$--font-size-extra-large',
+    '$--font-size-large',
+    '$--font-size-medium',
+    '$--font-size-base',
+    '$--font-size-small',
+    '$--font-size-extra-small'
+  ];
+  const original = {
+    'font_size_extra_large': '20px',
+    'font_size_large': '18px',
+    'font_size_medium': '16px',
+    'font_size_base': '14px',
+    'font_size_small': '13px',
+    'font_size_extra_small': '12px'
   }
+  export default {
+    created() {
+      bus.$on('user-theme-config-update', this.setGlobal);
+    },
+    mounted() {
+      this.setGlobal();
+    },
+    methods: {
+      tintColor(color, tint) {
+        return tintColor(color, tint);
+      },
+      setGlobal() {
+        if (window.userThemeConfig) {
+          this.global = window.userThemeConfig.global;
+        }
+      }
+    },
+    data() {
+      return {
+        global: {},
+        'font_size_extra_large': '',
+        'font_size_large': '',
+        'font_size_medium': '',
+        'font_size_base': '',
+        'font_size_small': '',
+        'font_size_extra_small': ''
+      }
+    },
+    watch: {
+      global: {
+        immediate: true,
+        handler(value) {
+          varMap.forEach((v) => {
+            const key = v.replace('$--', '').replace(/-/g, '_')
+            if (value[v]) {
+              this[key] = value[v]
+            } else {
+              this[key] = original[key]
+            }
+          });
+        }
+      }
+    },
+  }
+</script>
+<style>
   .demo-typo-size {
-    .h1 {
-      font-size: 20px;
-    }
-    .h2 {
-      font-size: 18px;
-    }
-    .h3 {
-      font-size: 16px;
-    }
-    .text-regular {
-      font-size: 14px;
-    }
-    .text-small {
-      font-size: 13px;
-    }
-    .text-smaller {
-      font-size: 12px;
-    }
     .color-dark-light {
       color: #99a9bf;
     }
   }
-  .typo-PingFang {
-    font-family: 'PingFang SC';
-  }
-  .typo-Hiragino {
-    font-family: 'Hiragino Sans GB';
+  .demo-term-box img{
+    width: 24%;
+    margin: 0 4% 20px 0;
   }
-  .typo-Microsoft {
-    font-family: 'Microsoft YaHei';
+
+  .lineH-left {
+    display: inline-block;
+    height: 80px
   }
-  /* 英文 */
-  .typo-Helvetica-Neue {
-    font-family: 'Helvetica Neue';
+  .lineH-right {
+    display: inline-block;
+    list-style: none;
+    padding: 0 0 0 90px;
+    margin: 0;
+    vertical-align: top;
   }
-  .typo-Helvetica {
-    font-family: 'Helvetica';
+  .lineH-right li{
+    font-size: 13px;
+    color: #666;
+    height: 20px;
+    line-height: 20px;
   }
-  .typo-Arial {
-    font-family: 'Arial';
+  .lineH-right li span{
+    padding-left: 40px;
   }
 </style>
 
@@ -76,76 +98,85 @@
 
 Creamos una convención de fuentes para asegurar la mejor presentación en diferentes plataformas.
 
-### Fuente en chino
-
-<div class="demo-typo-box typo-PingFang">
-  和畅惠风
-  <div class="name">PingFang SC</div>
-</div>
-<div class="demo-typo-box typo-Hiragino">
-  和畅惠风
-  <div class="name">Hiragino Sans GB</div>
-</div>
-<div class="demo-typo-box typo-Microsoft">
-  和畅惠风
-  <div class="name">Microsoft YaHei</div>
-</div>
-
-### Inglés / Fuente Numérica
-
-<div class="demo-typo-box typo-Helvetica-neue">
-  RGag
-  <div class="name">Helvetica Neue</div>
-</div>
-<div class="demo-typo-box typo-Helvetica">
-  RGag
-  <div class="name">Helvetica</div>
+### Fuente
+<div class="demo-term-box">
+<img src="../../assets/images/term-pingfang.png" alt="">
+<img src="../../assets/images/term-hiragino.png" alt="">
+<img src="../../assets/images/term-microsoft.png" alt="">
+<img src="../../assets/images/term-sf.png" alt="">
+<img src="../../assets/images/term-helvetica.png" alt="">
+<img src="../../assets/images/term-arial.png" alt="">
 </div>
-<div class="demo-typo-box typo-Arial">
-  RGag
-  <div class="name">Arial</div>
-</div>
-
-### Font-family
-
-```css
-font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif;
-```
 
 ### Convención de fuentes
 
 <table class="demo-typo-size">
   <tbody>
-    <tr>
-      <td class="h1">Main Title</td>
-      <td class="h1">Build with Element</td>
-      <td class="color-dark-light">20px  Extra large</td>
+  <tr
+    >
+      <td>Level</td>
+      <td>Font Size</td>
+      <td class="color-dark-light">Demo</td>
     </tr>
-    <tr>
-      <td class="h2">Title</td>
-      <td class="h2">Build with Element</td>
-      <td class="color-dark-light">18px large</td>
+    <tr
+    :style="{ fontSize: font_size_extra_small }"
+    >
+      <td>Supplementary text</td>
+      <td class="color-dark-light">{{font_size_extra_small}} Extra Small</td>
+      <td>Build with Element</td>
     </tr>
-    <tr>
-      <td class="h3">Small Title</td>
-      <td class="h3">Build with Element</td>
-      <td class="color-dark-light">16px Medium</td>
+    <tr
+    :style="{ fontSize: font_size_small }"
+    >
+      <td>Body (small)</td>
+      <td class="color-dark-light">{{font_size_small}} Small</td>
+      <td>Build with Element</td>
     </tr>
-    <tr>
-      <td class="text-regular">Body</td>
-      <td class="text-regular">Build with Element</td>
-      <td class="color-dark-light">14px Small</td>
+    <tr
+    :style="{ fontSize: font_size_base }"
+    >
+      <td>Body</td>
+      <td class="color-dark-light">{{font_size_base}} Base</td>
+      <td>Build with Element</td>
     </tr>
-    <tr>
-      <td class="text-small">Body (small)</td>
-      <td class="text-small">Build with Element</td>
-      <td class="color-dark-light">13px Extra Small</td>
+    <tr
+    :style="{ fontSize: font_size_medium }"
+    >
+      <td >Small Title</td>
+      <td class="color-dark-light">{{font_size_medium}} Medium</td>
+      <td>Build with Element</td>
     </tr>
-    <tr>
-      <td class="text-smaller">Supplementary text</td>
-      <td class="text-smaller">Build with Element</td>
-      <td class="color-dark-light">12px Extra Extra Small</td>
+    <tr
+    :style="{ fontSize: font_size_large }"
+    >
+      <td>Title</td>
+      <td class="color-dark-light">{{font_size_large}} large</td>
+      <td>Build with Element</td>
+    </tr>
+    <tr
+    :style="{ fontSize: font_size_extra_large }"
+    >
+      <td>Main Title</td>
+      <td class="color-dark-light">{{font_size_extra_large}} Extra large</td>
+      <td>Build with Element</td>
     </tr>
   </tbody>
 </table>
 
+### Font Line Height
+
+<div>
+<img class="lineH-left" src="~examples/assets/images/typography.png" />
+<ul class="lineH-right">
+<li>line-height:1 <span>No line height</span></li>
+<li>line-height:1.3 <span>Compact</span></li>
+<li>line-height:1.5 <span>Regular</span></li>
+<li>line-height:1.7 <span>Loose</span></li>
+</ul>
+</div>
+
+### Font-family
+
+```css
+font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif;
+```

+ 181 - 0
examples/docs/fr-FR/border.md

@@ -0,0 +1,181 @@
+<script>
+  import bus from '../../bus';
+  const varMap = {
+    '$--box-shadow-light': 'boxShadowLight',
+    '$--box-shadow-base': 'boxShadowBase',
+    '$--border-radius-base': 'borderRadiusBase',
+    '$--border-radius-small': 'borderRadiusSmall'
+  };
+  const original = {
+    boxShadowLight: '0 2px 12px 0 rgba(0, 0, 0, 0.1)',
+    boxShadowBase: '0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04)',
+    borderRadiusBase: '4px',
+    borderRadiusSmall: '2px'
+  }
+  export default {
+    created() {
+      bus.$on('user-theme-config-update', this.setGlobal);
+    },
+    mounted() {
+      this.setGlobal();
+    },
+    methods: {
+      setGlobal() {
+        if (window.userThemeConfig) {
+          this.global = window.userThemeConfig.global;
+        }
+      }
+    },
+    data() {
+      return {
+        global: {},
+        boxShadowLight: '',
+        boxShadowBase: '',
+        borderRadiusBase: '',
+        borderRadiusSmall: ''
+      }
+    },
+    watch: {
+      global: {
+        immediate: true,
+        handler(value) {
+          Object.keys(varMap).forEach((c) => {
+            if (value[c]) {
+              this[varMap[c]] = value[c]
+            } else {
+              this[varMap[c]] = original[varMap[c]]
+            }
+          });
+        }
+      }
+    }
+  }
+</script>
+
+<style>
+.demo-border .text {
+  width: 15%;
+}
+.demo-border .line {
+  width: 70%;
+}
+.demo-border .line div{
+  width: 100%;
+  height: 0;
+  border: 1px solid #EEE;
+}
+.demo-border .line .dashed{ 
+  border: 2px dashed #EEE;
+}
+.demo-shadow {
+  height: 100px;
+  width: 50%;
+  border: 1px solid #eee;
+}
+.demo-shadow-text {
+  line-height: 50px;
+  color: #666;
+  font-size: 14px;
+}
+.demo-radius .title{
+  color: #666;
+  font-size: 18px;
+  margin: 10px 0;
+}
+.demo-radius .value{
+  color: #333;
+  font-size: 16px;
+  margin: 10px 0;
+}
+.demo-radius .radius {
+  height: 60px;
+  width: 70%;
+  border: 1px solid #D7DAE2;
+  border-radius: 0;
+  margin-top: 20px;
+}
+.demo-radius .radius-30 {
+  border-radius: 30px;
+}
+</style>
+
+## Border
+
+We standardize the borders that can be used in buttons, cards, pop-ups and other components.
+
+### Border
+
+There are few border styles to choose.
+
+<table class="demo-border">
+  <tbody>
+    <tr>
+      <td class="text">Name</td>
+      <td class="text">Thickness</td>
+      <td class="line">Demo</td>
+    </tr>
+    <tr>
+      <td class="text">Solid</td>
+      <td class="text">1px</td>
+      <td class="line">
+        <div></div>
+      </td>
+    </tr>
+    <tr>
+      <td class="text">Dashed</td>
+      <td class="text">2px</td>
+      <td class="line">
+        <div class="dashed"></div>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+### Radius
+
+There are few radius styles to choose.
+
+<el-row :gutter="12" class="demo-radius">
+  <el-col :span="6" :xs="{span: 12}">
+    <div class="title">No Radius</div>
+    <div class="value">border-radius: 0px</div>
+    <div class="radius"></div>
+  </el-col>
+  <el-col :span="6" :xs="{span: 12}">
+    <div class="title">Small Radius</div>
+    <div class="value">border-radius: {{borderRadiusSmall}}</div>
+    <div 
+      class="radius" 
+      :style="{ borderRadius: borderRadiusSmall }"
+    ></div>
+  </el-col>
+  <el-col :span="6" :xs="{span: 12}">
+    <div class="title">Large Radius</div>
+    <div class="value">border-radius: {{borderRadiusBase}}</div>
+    <div 
+      class="radius"
+      :style="{ borderRadius: borderRadiusBase }"
+    ></div>
+  </el-col>
+  <el-col :span="6" :xs="{span: 12}">
+    <div class="title">Round Radius</div>
+    <div class="value">border-radius: 30px</div>
+    <div class="radius radius-30"></div>
+  </el-col>
+</el-row>
+
+### Shadow
+
+There are few shaodw styles to choose.
+
+<div 
+class="demo-shadow"
+:style="{ boxShadow: boxShadowBase }"
+></div>
+<span class="demo-shadow-text">Basic Shaodw box-shadow: {{boxShadowBase}}</span>
+
+<div 
+class="demo-shadow"
+:style="{ boxShadow: boxShadowLight }"
+></div>
+<span class="demo-shadow-text">Light Shadow box-shadow: {{boxShadowLight}}</span>

+ 244 - 52
examples/docs/fr-FR/color.md

@@ -1,9 +1,101 @@
+<script>
+  import bus from '../../bus';
+  import { tintColor } from '../../color.js';
+  const varMap = {
+    'primary': '$--color-primary',
+    'success': '$--color-success',
+    'warning': '$--color-warning',
+    'danger': '$--color-danger',
+    'info': '$--color-info',
+    'white': '$--color-white',
+    'black': '$--color-black',
+    'textPrimary': '$--color-text-primary',
+    'textRegular': '$--color-text-regular',
+    'textSecondary': '$--color-text-secondary',
+    'textPlaceholder': '$--color-text-placeholder',
+    'borderBase': '$--border-color-base',
+    'borderLight': '$--border-color-light',
+    'borderLighter': '$--border-color-lighter',
+    'borderExtraLight': '$--border-color-extra-light'
+  };
+  const original = {
+    primary: '#409EFF',
+    success: '#67C23A',
+    warning: '#E6A23C',
+    danger: '#F56C6C',
+    info: '#909399',
+    white: '#FFFFFF',
+    black: '#000000',
+    textPrimary: '#303133',
+    textRegular: '#606266',
+    textSecondary: '#909399',
+    textPlaceholder: '#C0C4CC',
+    borderBase: '#DCDFE6',
+    borderLight: '#E4E7ED',
+    borderLighter: '#EBEEF5',
+    borderExtraLight: '#F2F6FC'
+  }
+  export default {
+    created() {
+      bus.$on('user-theme-config-update', this.setGlobal);
+    },
+    mounted() {
+      this.setGlobal();
+    },
+    methods: {
+      tintColor(color, tint) {
+        return tintColor(color, tint);
+      },
+      setGlobal() {
+        if (window.userThemeConfig) {
+          this.global = window.userThemeConfig.global;
+        }
+      }
+    },
+    data() {
+      return {
+        global: {},
+        primary: '',
+        success: '',
+        warning: '',
+        danger: '',
+        info: '',
+        white: '',
+        black: '',
+        textPrimary: '',
+        textRegular: '',
+        textSecondary: '',
+        textPlaceholder: '',
+        borderBase: '',
+        borderLight: '',
+        borderLighter: '',
+        borderExtraLight: ''
+      }
+    },
+    watch: {
+      global: {
+        immediate: true,
+        handler(value) {
+          Object.keys(original).forEach((o) => {
+            if (value[varMap[o]]) {
+              this[o] = value[varMap[o]]
+            } else {
+              this[o] = original[o]
+            }
+          });
+        }
+      }
+    },
+  }
+</script>
+
 <style>
   .demo-color-box {
+    position: relative;
     border-radius: 4px;
     padding: 20px;
     margin: 5px 0;
-    height: 74px;
+    height: 114px;
     box-sizing: border-box;
     color: #fff;
     font-size: 14px;
@@ -14,6 +106,12 @@
       line-height: 24px;
     }
   }
+  .demo-color-box-other {
+    height: 74px;
+    margin: 10px 0!important;
+    border-radius: 4px 4px 4px 4px!important;
+    padding: 15px 20px;
+  }
   .demo-color-box-group {
     .demo-color-box {
       border-radius: 0;
@@ -26,50 +124,42 @@
       border-radius: 0 0 4px 4px;
     }
   }
-  .bg-blue {
-    background-color: #409EFF;
-  }
-
-  .bg-success {
-    background-color: #13CE66;
-  }
-  .bg-warning {
-    background-color: #f7ba2a;
-  }
-  .bg-danger {
-    background-color: #ff4949;
-  }
-  .bg-info {
-    background-color: #909399;
-  }
-
-  .bg-text-primary {
-    background-color: #303133;
-  }
-  .bg-text-regular {
-    background-color: #606266;
+  .bg-color-sub {
+    width: 100%;
+    height: 40px;
+    left: 0;
+    bottom: 0;
+    position: absolute;
+    border-radius: 0 0 4px 4px;
   }
-  .bg-text-secondary {
-    background-color: #909399;
+  .bg-blue-sub-item {
+    width: 11.1111111%;
+    height: 100%;
+    display: inline-block;
   }
-  .bg-text-placeholder {
-    background-color: #c0c4cc;
+  .bg-blue-sub-item:first-child {
+    border-radius: 0 0 0 4px;
   }
-
-  .bg-border-base {
-    background-color: #dcdfe6;
+  .bg-success-sub-item {
+    width: 50%;
+    height: 100%;
+    display: inline-block;
   }
-  .bg-border-light {
-    background-color: #e4e7ed;
+  .bg-success-sub-item:first-child {
+    border-radius: 0 0 0 4px;
   }
-  .bg-border-lighter {
-    background-color: #ebeef5;
+  .bg-success-sub-item:last-child {
+    border-radius: 0 0 4px 0;
   }
-  .bg-border-extra-light {
-    background-color: #f2f6fc;
+  .bg-transparent {
+    border: 1px solid #FCC3C3;
+    color: #303133;
+    background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' preserveAspectRatio='none' viewBox='0 0 100 100'><path d='M0 98 L100 0 L100 1 L1 98' fill='%23FCC3C3' /></svg>");
+    background-repeat:no-repeat;
+    background-position:center center;
+    background-size: 100% 100%, auto;
   }
-
-  [class*=" bg-border-"] {
+  .demo-color-box-lite {
     color: #303133;
   }
 </style>
@@ -82,8 +172,24 @@ Element utilise un ensemble de palettes spécifiques afin de fournir un style co
 La couleur principale d'Element est un bleu clair et agréable.
 
 <el-row :gutter="12">
-  <el-col :span="6" :xs="{span: 12}">
-    <div class="demo-color-box bg-blue">Bleu<div class="value">#409EFF</div></div>
+  <el-col :span="10" :xs="{span: 12}">
+    <div 
+      class="demo-color-box"
+      :style="{ background: primary }"
+    >
+      Brand Color<div class="value">#409EFF</div>
+    <div 
+      class="bg-color-sub"
+      :style="{ background: tintColor(primary, 0.9) }"
+    >
+    <div 
+      class="bg-blue-sub-item" 
+      v-for="(item, key) in Array(8)"
+      :key="key"
+      :style="{ background: tintColor(primary, (key + 1) / 10) }"
+        >
+    </div>
+    </div>
   </el-col>
 </el-row>
 
@@ -93,16 +199,72 @@ En plus de la couleur principale, vous devrez sans doute utiliser d'autres coule
 
 <el-row :gutter="12">
   <el-col :span="6" :xs="{span: 12}">
-    <div class="demo-color-box bg-success">Succès<div class="value">#67C23A</div></div>
+    <div class="demo-color-box"
+    :style="{ background: success }"
+    >Success<div class="value">#67C23A</div>
+      <div 
+        class="bg-color-sub"
+      >
+        <div 
+          class="bg-success-sub-item" 
+          v-for="(item, key) in Array(2)"
+          :key="key"
+          :style="{ background: tintColor(success, (key + 8) / 10) }"
+            >
+        </div>
+      </div>
+    </div>
   </el-col>
   <el-col :span="6" :xs="{span: 12}">
-    <div class="demo-color-box bg-warning">Avertissement<div class="value">#E6A23C</div></div>
+    <div class="demo-color-box"
+    :style="{ background: warning }"
+    >Warning<div class="value">#E6A23C</div>
+      <div 
+          class="bg-color-sub"
+        >
+        <div 
+          class="bg-success-sub-item" 
+          v-for="(item, key) in Array(2)"
+          :key="key"
+          :style="{ background: tintColor(warning, (key + 8) / 10) }"
+            >
+        </div>
+      </div>
+    </div>
   </el-col>
   <el-col :span="6" :xs="{span: 12}">
-    <div class="demo-color-box bg-danger">Danger<div class="value">#F56C6C</div></div>
+    <div class="demo-color-box"
+    :style="{ background: danger }"
+    >Danger<div class="value">#F56C6C</div>
+      <div 
+          class="bg-color-sub"
+        >
+        <div 
+          class="bg-success-sub-item" 
+          v-for="(item, key) in Array(2)"
+          :key="key"
+          :style="{ background: tintColor(danger, (key + 8) / 10) }"
+            >
+        </div>
+      </div>
+    </div>
   </el-col>
   <el-col :span="6" :xs="{span: 12}">
-    <div class="demo-color-box bg-info">Information<div class="value">#909399</div></div>
+    <div class="demo-color-box"
+    :style="{ background: info }"
+    >Info<div class="value">#909399</div>
+      <div 
+          class="bg-color-sub"
+        >
+        <div 
+          class="bg-success-sub-item" 
+          v-for="(item, key) in Array(2)"
+          :key="key"
+          :style="{ background: tintColor(info, (key + 8) / 10) }"
+            >
+        </div>
+      </div>
+    </div>
   </el-col>
 </el-row>
 
@@ -113,18 +275,48 @@ Les couleurs neutres sont les couleurs du fond, du texte et des bordures. Vous p
 <el-row :gutter="12">
   <el-col :span="6" :xs="{span: 12}">
     <div class="demo-color-box-group">
-      <div class="demo-color-box bg-text-primary">Texte principal<div class="value">#303133</div></div>
-      <div class="demo-color-box bg-text-regular">Texte normal<div class="value">#606266</div></div>
-      <div class="demo-color-box bg-text-secondary">Texte secondaire<div class="value">#909399</div></div>
-      <div class="demo-color-box bg-text-placeholder">Texte de placeholder<div class="value">#C0C4CC</div></div>
+      <div class="demo-color-box demo-color-box-other"
+      :style="{ background: textPrimary }"
+      >Texte principal<div class="value">{{textPrimary}}</div></div>
+      <div class="demo-color-box demo-color-box-other"
+      :style="{ background: textRegular }"
+      >
+      Texte normal<div class="value">{{textRegular}}</div></div>
+      <div class="demo-color-box demo-color-box-other"
+      :style="{ background: textSecondary }"
+      >Texte secondaire<div class="value">{{textSecondary}}</div></div>
+      <div class="demo-color-box demo-color-box-other"
+      :style="{ background: textPlaceholder }"
+      >Texte de placeholder<div class="value">{{textPlaceholder}}</div></div>
+    </div>
+  </el-col>
+  <el-col :span="6" :xs="{span: 12}">
+    <div class="demo-color-box-group">
+      <div class="demo-color-box demo-color-box-other demo-color-box-lite"
+      :style="{ background: borderBase }"
+      >Bordure de base<div class="value">{{borderBase}}</div></div>
+      <div class="demo-color-box demo-color-box-other demo-color-box-lite"
+      :style="{ background: borderLight }"
+      >Bordure claire<div class="value">{{borderLight}}</div></div>
+      <div class="demo-color-box demo-color-box-other demo-color-box-lite"
+      :style="{ background: borderLighter }"
+      >Bordure très claire<div class="value">{{borderLighter}}</div></div>
+      <div class="demo-color-box demo-color-box-other demo-color-box-lite"
+      :style="{ background: borderExtraLight }"
+      >Bordure extra claire<div class="value">{{borderExtraLight}}</div></div>
     </div>
   </el-col>
   <el-col :span="6" :xs="{span: 12}">
     <div class="demo-color-box-group">
-      <div class="demo-color-box bg-border-base">Bordure de base<div class="value">#DCDFE6</div></div>
-      <div class="demo-color-box bg-border-light">Bordure claire<div class="value">#E4E7ED</div></div>
-      <div class="demo-color-box bg-border-lighter">Bordure très claire<div class="value">#EBEEF5</div></div>
-      <div class="demo-color-box bg-border-extra-light">Bordure extra claire<div class="value">#F2F6FC</div></div>
+      <div 
+      class="demo-color-box demo-color-box-other"
+      :style="{ background: black }"
+      >Basic Black<div class="value">{{black}}</div></div>
+      <div
+      class="demo-color-box demo-color-box-other"
+      :style="{ background: white, color: '#303133', border: '1px solid #eee' }"
+      >Basic White<div class="value">{{white}}</div></div>
+      <div class="demo-color-box demo-color-box-other bg-transparent">Transparent<div class="value">Transparent</div>
     </div>
   </el-col>
 </el-row>

+ 151 - 118
examples/docs/fr-FR/typography.md

@@ -1,74 +1,96 @@
-<style>
-  .demo-typo-box {
-    height: 200px;
-    width: 200px;
-    position: relative;
-    border: 1px solid #eaeefb;
-    font-size: 40px;
-    color: #1f2d3d;
-    text-align: center;
-    line-height: 162px;
-    padding-bottom: 36px;
-    box-sizing: border-box;
-    display: inline-block;
-    margin-right: 17px;
-    border-radius: 4px;
-
-    .name {
-      position: absolute;
-      bottom: 0;
-      width: 100%;
-      height: 35px;
-      border-top: 1px solid #eaeefb;
-      font-size: 14px;
-      color: #8492a6;
-      line-height: 35px;
-      text-align: left;
-      text-indent: 10px;
-      font-family: 'Helvetica Neue';
-    }
+<script>
+  import bus from '../../bus';
+  const varMap = [
+    '$--font-size-extra-large',
+    '$--font-size-large',
+    '$--font-size-medium',
+    '$--font-size-base',
+    '$--font-size-small',
+    '$--font-size-extra-small'
+  ];
+  const original = {
+    'font_size_extra_large': '20px',
+    'font_size_large': '18px',
+    'font_size_medium': '16px',
+    'font_size_base': '14px',
+    'font_size_small': '13px',
+    'font_size_extra_small': '12px'
+  }
+  export default {
+    created() {
+      bus.$on('user-theme-config-update', this.setGlobal);
+    },
+    mounted() {
+      this.setGlobal();
+    },
+    methods: {
+      tintColor(color, tint) {
+        return tintColor(color, tint);
+      },
+      setGlobal() {
+        if (window.userThemeConfig) {
+          this.global = window.userThemeConfig.global;
+        }
+      }
+    },
+    data() {
+      return {
+        global: {},
+        'font_size_extra_large': '',
+        'font_size_large': '',
+        'font_size_medium': '',
+        'font_size_base': '',
+        'font_size_small': '',
+        'font_size_extra_small': ''
+      }
+    },
+    watch: {
+      global: {
+        immediate: true,
+        handler(value) {
+          varMap.forEach((v) => {
+            const key = v.replace('$--', '').replace(/-/g, '_')
+            if (value[v]) {
+              this[key] = value[v]
+            } else {
+              this[key] = original[key]
+            }
+          });
+        }
+      }
+    },
   }
+</script>
+<style>
   .demo-typo-size {
-    .h1 {
-      font-size: 20px;
-    }
-    .h2 {
-      font-size: 18px;
-    }
-    .h3 {
-      font-size: 16px;
-    }
-    .text-regular {
-      font-size: 14px;
-    }
-    .text-small {
-      font-size: 13px;
-    }
-    .text-smaller {
-      font-size: 12px;
-    }
     .color-dark-light {
       color: #99a9bf;
     }
   }
-  .typo-PingFang {
-    font-family: 'PingFang SC';
-  }
-  .typo-Hiragino {
-    font-family: 'Hiragino Sans GB';
+  .demo-term-box img{
+    width: 24%;
+    margin: 0 4% 20px 0;
   }
-  .typo-Microsoft {
-    font-family: 'Microsoft YaHei';
+
+  .lineH-left {
+    display: inline-block;
+    height: 80px
   }
-  /* 英文 \*/
-  .typo-Helvetica-Neue {
-    font-family: 'Helvetica Neue';
+  .lineH-right {
+    display: inline-block;
+    list-style: none;
+    padding: 0 0 0 90px;
+    margin: 0;
+    vertical-align: top;
   }
-  .typo-Helvetica {
-    font-family: 'Helvetica';
+  .lineH-right li{
+    font-size: 13px;
+    color: #666;
+    height: 20px;
+    line-height: 20px;
   }
-  .typo-Arial {
-    font-family: 'Arial';
+  .lineH-right li span{
+    padding-left: 40px;
   }
 </style>
 
@@ -76,75 +98,86 @@
 
 Nous avons créé une convention de police d'écriture afin d'assurer la meilleur présentation possible sur toutes le plateformes.
 
-### Police chinoise
+### Police
 
-<div class="demo-typo-box typo-PingFang">
-  和畅惠风
-  <div class="name">PingFang SC</div>
-</div>
-<div class="demo-typo-box typo-Hiragino">
-  和畅惠风
-  <div class="name">Hiragino Sans GB</div>
-</div>
-<div class="demo-typo-box typo-Microsoft">
-  和畅惠风
-  <div class="name">Microsoft YaHei</div>
+<div class="demo-term-box">
+<img src="../../assets/images/term-pingfang.png" alt="">
+<img src="../../assets/images/term-hiragino.png" alt="">
+<img src="../../assets/images/term-microsoft.png" alt="">
+<img src="../../assets/images/term-sf.png" alt="">
+<img src="../../assets/images/term-helvetica.png" alt="">
+<img src="../../assets/images/term-arial.png" alt="">
 </div>
 
-### Police anglaise / numérique
-
-<div class="demo-typo-box typo-Helvetica-neue">
-  RGag
-  <div class="name">Helvetica Neue</div>
-</div>
-<div class="demo-typo-box typo-Helvetica">
-  RGag
-  <div class="name">Helvetica</div>
-</div>
-<div class="demo-typo-box typo-Arial">
-  RGag
-  <div class="name">Arial</div>
-</div>
-
-### Font-family
-
-```css
-font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif;
-```
-
 ### Convention des polices
 
 <table class="demo-typo-size">
   <tbody>
-    <tr>
-      <td class="h1">Titre principal</td>
-      <td class="h1">Construit avec Element</td>
-      <td class="color-dark-light">20px  Extra large</td>
+  <tr
+    >
+      <td>Level</td>
+      <td>Font Size</td>
+      <td class="color-dark-light">Demo</td>
+    </tr>
+    <tr
+    :style="{ fontSize: font_size_extra_small }"
+    >
+      <td>Supplementary text</td>
+      <td class="color-dark-light">{{font_size_extra_small}} Extra Small</td>
+      <td>Build with Element</td>
     </tr>
-    <tr>
-      <td class="h2">Titre</td>
-      <td class="h2">Construit avec Element</td>
-      <td class="color-dark-light">18px large</td>
+    <tr
+    :style="{ fontSize: font_size_small }"
+    >
+      <td>Body (small)</td>
+      <td class="color-dark-light">{{font_size_small}} Small</td>
+      <td>Build with Element</td>
     </tr>
-    <tr>
-      <td class="h3">Sous-titre</td>
-      <td class="h3">Construit avec Element</td>
-      <td class="color-dark-light">16px Medium</td>
+    <tr
+    :style="{ fontSize: font_size_base }"
+    >
+      <td>Body</td>
+      <td class="color-dark-light">{{font_size_base}} Base</td>
+      <td>Build with Element</td>
     </tr>
-    <tr>
-      <td class="text-regular">Body</td>
-      <td class="text-regular">Construit avec Element</td>
-      <td class="color-dark-light">14px Small</td>
+    <tr
+    :style="{ fontSize: font_size_medium }"
+    >
+      <td >Small Title</td>
+      <td class="color-dark-light">{{font_size_medium}} Medium</td>
+      <td>Build with Element</td>
     </tr>
-    <tr>
-      <td class="text-small">Body (petit)</td>
-      <td class="text-small">Construit avec Element</td>
-      <td class="color-dark-light">13px Extra Small</td>
+    <tr
+    :style="{ fontSize: font_size_large }"
+    >
+      <td>Title</td>
+      <td class="color-dark-light">{{font_size_large}} large</td>
+      <td>Build with Element</td>
     </tr>
-    <tr>
-      <td class="text-smaller">Texte supplémentaire</td>
-      <td class="text-smaller">Construit avec Element</td>
-      <td class="color-dark-light">12px Extra Extra Small</td>
+    <tr
+    :style="{ fontSize: font_size_extra_large }"
+    >
+      <td>Main Title</td>
+      <td class="color-dark-light">{{font_size_extra_large}} Extra large</td>
+      <td>Build with Element</td>
     </tr>
   </tbody>
 </table>
+
+### Font Line Height
+
+<div>
+<img class="lineH-left" src="~examples/assets/images/typography.png" />
+<ul class="lineH-right">
+<li>line-height:1 <span>No line height</span></li>
+<li>line-height:1.3 <span>Compact</span></li>
+<li>line-height:1.5 <span>Regular</span></li>
+<li>line-height:1.7 <span>Loose</span></li>
+</ul>
+</div>
+
+### Font-family
+
+```css
+font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif;
+```

+ 181 - 0
examples/docs/zh-CN/border.md

@@ -0,0 +1,181 @@
+<script>
+  import bus from '../../bus';
+  const varMap = {
+    '$--box-shadow-light': 'boxShadowLight',
+    '$--box-shadow-base': 'boxShadowBase',
+    '$--border-radius-base': 'borderRadiusBase',
+    '$--border-radius-small': 'borderRadiusSmall'
+  };
+  const original = {
+    boxShadowLight: '0 2px 12px 0 rgba(0, 0, 0, 0.1)',
+    boxShadowBase: '0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04)',
+    borderRadiusBase: '4px',
+    borderRadiusSmall: '2px'
+  }
+  export default {
+    created() {
+      bus.$on('user-theme-config-update', this.setGlobal);
+    },
+    mounted() {
+      this.setGlobal();
+    },
+    methods: {
+      setGlobal() {
+        if (window.userThemeConfig) {
+          this.global = window.userThemeConfig.global;
+        }
+      }
+    },
+    data() {
+      return {
+        global: {},
+        boxShadowLight: '',
+        boxShadowBase: '',
+        borderRadiusBase: '',
+        borderRadiusSmall: ''
+      }
+    },
+    watch: {
+      global: {
+        immediate: true,
+        handler(value) {
+          Object.keys(varMap).forEach((c) => {
+            if (value[c]) {
+              this[varMap[c]] = value[c]
+            } else {
+              this[varMap[c]] = original[varMap[c]]
+            }
+          });
+        }
+      }
+    }
+  }
+</script>
+
+<style>
+.demo-border .text {
+  width: 15%;
+}
+.demo-border .line {
+  width: 70%;
+}
+.demo-border .line div{
+  width: 100%;
+  height: 0;
+  border: 1px solid #EEE;
+}
+.demo-border .line .dashed{ 
+  border: 2px dashed #EEE;
+}
+.demo-shadow {
+  height: 100px;
+  width: 50%;
+  border: 1px solid #eee;
+}
+.demo-shadow-text {
+  line-height: 50px;
+  color: #666;
+  font-size: 14px;
+}
+.demo-radius .title{
+  color: #666;
+  font-size: 18px;
+  margin: 10px 0;
+}
+.demo-radius .value{
+  color: #333;
+  font-size: 16px;
+  margin: 10px 0;
+}
+.demo-radius .radius {
+  height: 60px;
+  width: 70%;
+  border: 1px solid #D7DAE2;
+  border-radius: 0;
+  margin-top: 20px;
+}
+.demo-radius .radius-30 {
+  border-radius: 30px;
+}
+</style>
+
+## Border 边框
+
+我们对边框进行统一规范,可用于按钮、卡片、弹窗等组件里。
+
+### 边框
+
+我们提供了一下几种边框样式,以供选择。
+
+<table class="demo-border">
+  <tbody>
+    <tr>
+      <td class="text">名称</td>
+      <td class="text">粗细</td>
+      <td class="line">举例</td>
+    </tr>
+    <tr>
+      <td class="text">实线</td>
+      <td class="text">1px</td>
+      <td class="line">
+        <div></div>
+      </td>
+    </tr>
+    <tr>
+      <td class="text">虚线</td>
+      <td class="text">2px</td>
+      <td class="line">
+        <div class="dashed"></div>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+### 圆角
+
+我们提供了一下几种圆角样式,以供选择。
+
+<el-row :gutter="12" class="demo-radius">
+  <el-col :span="6" :xs="{span: 12}">
+    <div class="title">无圆角</div>
+    <div class="value">border-radius: 0px</div>
+    <div class="radius"></div>
+  </el-col>
+  <el-col :span="6" :xs="{span: 12}">
+    <div class="title">小圆角</div>
+    <div class="value">border-radius: {{borderRadiusSmall}}</div>
+    <div 
+      class="radius" 
+      :style="{ borderRadius: borderRadiusSmall }"
+    ></div>
+  </el-col>
+  <el-col :span="6" :xs="{span: 12}">
+    <div class="title">大圆角</div>
+    <div class="value">border-radius: {{borderRadiusBase}}</div>
+    <div 
+      class="radius"
+      :style="{ borderRadius: borderRadiusBase }"
+    ></div>
+  </el-col>
+  <el-col :span="6" :xs="{span: 12}">
+    <div class="title">圆形圆角</div>
+    <div class="value">border-radius: 30px</div>
+    <div class="radius radius-30"></div>
+  </el-col>
+</el-row>
+
+### 投影
+
+我们提供了一下几种投影样式,以供选择。
+
+<div 
+class="demo-shadow"
+:style="{ boxShadow: boxShadowBase }"
+></div>
+<span class="demo-shadow-text">基础投影 box-shadow: {{boxShadowBase}}</span>
+
+<div 
+class="demo-shadow"
+:style="{ boxShadow: boxShadowLight }"
+></div>
+<span class="demo-shadow-text">浅色投影 box-shadow: {{boxShadowLight}}</span>

+ 244 - 52
examples/docs/zh-CN/color.md

@@ -1,9 +1,101 @@
+<script>
+  import bus from '../../bus';
+  import { tintColor } from '../../color.js';
+  const varMap = {
+    'primary': '$--color-primary',
+    'success': '$--color-success',
+    'warning': '$--color-warning',
+    'danger': '$--color-danger',
+    'info': '$--color-info',
+    'white': '$--color-white',
+    'black': '$--color-black',
+    'textPrimary': '$--color-text-primary',
+    'textRegular': '$--color-text-regular',
+    'textSecondary': '$--color-text-secondary',
+    'textPlaceholder': '$--color-text-placeholder',
+    'borderBase': '$--border-color-base',
+    'borderLight': '$--border-color-light',
+    'borderLighter': '$--border-color-lighter',
+    'borderExtraLight': '$--border-color-extra-light'
+  };
+  const original = {
+    primary: '#409EFF',
+    success: '#67C23A',
+    warning: '#E6A23C',
+    danger: '#F56C6C',
+    info: '#909399',
+    white: '#FFFFFF',
+    black: '#000000',
+    textPrimary: '#303133',
+    textRegular: '#606266',
+    textSecondary: '#909399',
+    textPlaceholder: '#C0C4CC',
+    borderBase: '#DCDFE6',
+    borderLight: '#E4E7ED',
+    borderLighter: '#EBEEF5',
+    borderExtraLight: '#F2F6FC'
+  }
+  export default {
+    created() {
+      bus.$on('user-theme-config-update', this.setGlobal);
+    },
+    mounted() {
+      this.setGlobal();
+    },
+    methods: {
+      tintColor(color, tint) {
+        return tintColor(color, tint);
+      },
+      setGlobal() {
+        if (window.userThemeConfig) {
+          this.global = window.userThemeConfig.global;
+        }
+      }
+    },
+    data() {
+      return {
+        global: {},
+        primary: '',
+        success: '',
+        warning: '',
+        danger: '',
+        info: '',
+        white: '',
+        black: '',
+        textPrimary: '',
+        textRegular: '',
+        textSecondary: '',
+        textPlaceholder: '',
+        borderBase: '',
+        borderLight: '',
+        borderLighter: '',
+        borderExtraLight: ''
+      }
+    },
+    watch: {
+      global: {
+        immediate: true,
+        handler(value) {
+          Object.keys(original).forEach((o) => {
+            if (value[varMap[o]]) {
+              this[o] = value[varMap[o]]
+            } else {
+              this[o] = original[o]
+            }
+          });
+        }
+      }
+    },
+  }
+</script>
+
 <style>
   .demo-color-box {
+    position: relative;
     border-radius: 4px;
     padding: 20px;
     margin: 5px 0;
-    height: 74px;
+    height: 114px;
     box-sizing: border-box;
     color: #fff;
     font-size: 14px;
@@ -14,6 +106,12 @@
       line-height: 24px;
     }
   }
+  .demo-color-box-other {
+    height: 74px;
+    margin: 10px 0!important;
+    border-radius: 4px 4px 4px 4px!important;
+    padding: 15px 20px;
+  }
   .demo-color-box-group {
     .demo-color-box {
       border-radius: 0;
@@ -26,50 +124,42 @@
       border-radius: 0 0 4px 4px;
     }
   }
-  .bg-blue {
-    background-color: #409EFF;
-  }
-
-  .bg-success {
-    background-color: #67C23A;
-  }
-  .bg-warning {
-    background-color: #E6A23C;
-  }
-  .bg-danger {
-    background-color: #F56C6C;
-  }
-  .bg-info {
-    background-color: #909399;
-  }
-
-  .bg-text-primary {
-    background-color: #303133;
-  }
-  .bg-text-regular {
-    background-color: #606266;
+  .bg-color-sub {
+    width: 100%;
+    height: 40px;
+    left: 0;
+    bottom: 0;
+    position: absolute;
+    border-radius: 0 0 4px 4px;
   }
-  .bg-text-secondary {
-    background-color: #909399;
+  .bg-blue-sub-item {
+    width: 11.1111111%;
+    height: 100%;
+    display: inline-block;
   }
-  .bg-text-placeholder {
-    background-color: #c0c4cc;
+  .bg-blue-sub-item:first-child {
+    border-radius: 0 0 0 4px;
   }
-
-  .bg-border-base {
-    background-color: #dcdfe6;
+  .bg-success-sub-item {
+    width: 50%;
+    height: 100%;
+    display: inline-block;
   }
-  .bg-border-light {
-    background-color: #e4e7ed;
+  .bg-success-sub-item:first-child {
+    border-radius: 0 0 0 4px;
   }
-  .bg-border-lighter {
-    background-color: #ebeef5;
+  .bg-success-sub-item:last-child {
+    border-radius: 0 0 4px 0;
   }
-  .bg-border-extra-light {
-    background-color: #f2f6fc;
+  .bg-transparent {
+    border: 1px solid #FCC3C3;
+    color: #303133;
+    background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' preserveAspectRatio='none' viewBox='0 0 100 100'><path d='M0 98 L100 0 L100 1 L1 98' fill='%23FCC3C3' /></svg>");
+    background-repeat:no-repeat;
+    background-position:center center;
+    background-size: 100% 100%, auto;
   }
-
-  [class*=" bg-border-"] {
+  .demo-color-box-lite {
     color: #303133;
   }
 </style>
@@ -83,8 +173,24 @@ Element 为了避免视觉传达差异,使用一套特定的调色板来规定
 Element 主要品牌颜色是鲜艳、友好的蓝色。
 
 <el-row :gutter="12">
-  <el-col :span="6" :xs="{span: 12}">
-    <div class="demo-color-box bg-blue">Blue<div class="value">#409EFF</div></div>
+  <el-col :span="10" :xs="{span: 12}">
+    <div 
+      class="demo-color-box"
+      :style="{ background: primary }"
+    >
+      Brand Color<div class="value">#409EFF</div>
+    <div 
+      class="bg-color-sub"
+      :style="{ background: tintColor(primary, 0.9) }"
+    >
+    <div 
+      class="bg-blue-sub-item" 
+      v-for="(item, key) in Array(8)"
+      :key="key"
+      :style="{ background: tintColor(primary, (key + 1) / 10) }"
+        >
+    </div>
+    </div>
   </el-col>
 </el-row>
 
@@ -94,16 +200,72 @@ Element 主要品牌颜色是鲜艳、友好的蓝色。
 
 <el-row :gutter="12">
   <el-col :span="6" :xs="{span: 12}">
-    <div class="demo-color-box bg-success">Success<div class="value">#67C23A</div></div>
+    <div class="demo-color-box"
+    :style="{ background: success }"
+    >Success<div class="value">#67C23A</div>
+      <div 
+        class="bg-color-sub"
+      >
+        <div 
+          class="bg-success-sub-item" 
+          v-for="(item, key) in Array(2)"
+          :key="key"
+          :style="{ background: tintColor(success, (key + 8) / 10) }"
+            >
+        </div>
+      </div>
+    </div>
   </el-col>
   <el-col :span="6" :xs="{span: 12}">
-    <div class="demo-color-box bg-warning">Warning<div class="value">#E6A23C</div></div>
+    <div class="demo-color-box"
+    :style="{ background: warning }"
+    >Warning<div class="value">#E6A23C</div>
+      <div 
+          class="bg-color-sub"
+        >
+        <div 
+          class="bg-success-sub-item" 
+          v-for="(item, key) in Array(2)"
+          :key="key"
+          :style="{ background: tintColor(warning, (key + 8) / 10) }"
+            >
+        </div>
+      </div>
+    </div>
   </el-col>
   <el-col :span="6" :xs="{span: 12}">
-    <div class="demo-color-box bg-danger">Danger<div class="value">#F56C6C</div></div>
+    <div class="demo-color-box"
+    :style="{ background: danger }"
+    >Danger<div class="value">#F56C6C</div>
+      <div 
+          class="bg-color-sub"
+        >
+        <div 
+          class="bg-success-sub-item" 
+          v-for="(item, key) in Array(2)"
+          :key="key"
+          :style="{ background: tintColor(danger, (key + 8) / 10) }"
+            >
+        </div>
+      </div>
+    </div>
   </el-col>
   <el-col :span="6" :xs="{span: 12}">
-    <div class="demo-color-box bg-info">Info<div class="value">#909399</div></div>
+    <div class="demo-color-box"
+    :style="{ background: info }"
+    >Info<div class="value">#909399</div>
+      <div 
+          class="bg-color-sub"
+        >
+        <div 
+          class="bg-success-sub-item" 
+          v-for="(item, key) in Array(2)"
+          :key="key"
+          :style="{ background: tintColor(info, (key + 8) / 10) }"
+            >
+        </div>
+      </div>
+    </div>
   </el-col>
 </el-row>
 
@@ -114,18 +276,48 @@ Element 主要品牌颜色是鲜艳、友好的蓝色。
 <el-row :gutter="12">
   <el-col :span="6" :xs="{span: 12}">
     <div class="demo-color-box-group">
-      <div class="demo-color-box bg-text-primary">主要文字<div class="value">#303133</div></div>
-      <div class="demo-color-box bg-text-regular">常规文字<div class="value">#606266</div></div>
-      <div class="demo-color-box bg-text-secondary">次要文字<div class="value">#909399</div></div>
-      <div class="demo-color-box bg-text-placeholder">占位文字<div class="value">#C0C4CC</div></div>
+      <div class="demo-color-box demo-color-box-other"
+      :style="{ background: textPrimary }"
+      >主要文字<div class="value">{{textPrimary}}</div></div>
+      <div class="demo-color-box demo-color-box-other"
+      :style="{ background: textRegular }"
+      >
+      常规文字<div class="value">{{textRegular}}</div></div>
+      <div class="demo-color-box demo-color-box-other"
+      :style="{ background: textSecondary }"
+      >次要文字<div class="value">{{textSecondary}}</div></div>
+      <div class="demo-color-box demo-color-box-other"
+      :style="{ background: textPlaceholder }"
+      >占位文字<div class="value">{{textPlaceholder}}</div></div>
+    </div>
+  </el-col>
+  <el-col :span="6" :xs="{span: 12}">
+    <div class="demo-color-box-group">
+      <div class="demo-color-box demo-color-box-other demo-color-box-lite"
+      :style="{ background: borderBase }"
+      >一级边框<div class="value">{{borderBase}}</div></div>
+      <div class="demo-color-box demo-color-box-other demo-color-box-lite"
+      :style="{ background: borderLight }"
+      >二级边框<div class="value">{{borderLight}}</div></div>
+      <div class="demo-color-box demo-color-box-other demo-color-box-lite"
+      :style="{ background: borderLighter }"
+      >三级边框<div class="value">{{borderLighter}}</div></div>
+      <div class="demo-color-box demo-color-box-other demo-color-box-lite"
+      :style="{ background: borderExtraLight }"
+      >四级边框<div class="value">{{borderExtraLight}}</div></div>
     </div>
   </el-col>
   <el-col :span="6" :xs="{span: 12}">
     <div class="demo-color-box-group">
-      <div class="demo-color-box bg-border-base">一级边框<div class="value">#DCDFE6</div></div>
-      <div class="demo-color-box bg-border-light">二级边框<div class="value">#E4E7ED</div></div>
-      <div class="demo-color-box bg-border-lighter">三级边框<div class="value">#EBEEF5</div></div>
-      <div class="demo-color-box bg-border-extra-light">四级边框<div class="value">#F2F6FC</div></div>
+      <div 
+      class="demo-color-box demo-color-box-other"
+      :style="{ background: black }"
+      >基础黑色<div class="value">{{black}}</div></div>
+      <div
+      class="demo-color-box demo-color-box-other"
+      :style="{ background: white, color: '#303133', border: '1px solid #eee' }"
+      >基础白色<div class="value">{{white}}</div></div>
+      <div class="demo-color-box demo-color-box-other bg-transparent">透明<div class="value">Transparent</div>
     </div>
   </el-col>
 </el-row>

+ 152 - 120
examples/docs/zh-CN/typography.md

@@ -1,74 +1,96 @@
-<style>
-  .demo-typo-box {
-    height: 200px;
-    width: 200px;
-    position: relative;
-    border: 1px solid #eaeefb;
-    font-size: 40px;
-    color: #1f2d3d;
-    text-align: center;
-    line-height: 162px;
-    padding-bottom: 36px;
-    box-sizing: border-box;
-    display: inline-block;
-    margin-right: 17px;
-    border-radius: 4px;
-
-    .name {
-      position: absolute;
-      bottom: 0;
-      width: 100%;
-      height: 35px;
-      border-top: 1px solid #eaeefb;
-      font-size: 14px;
-      color: #8492a6;
-      line-height: 35px;
-      text-align: left;
-      text-indent: 10px;
-      font-family: 'Helvetica Neue';
-    }
+<script>
+  import bus from '../../bus';
+  const varMap = [
+    '$--font-size-extra-large',
+    '$--font-size-large',
+    '$--font-size-medium',
+    '$--font-size-base',
+    '$--font-size-small',
+    '$--font-size-extra-small'
+  ];
+  const original = {
+    'font_size_extra_large': '20px',
+    'font_size_large': '18px',
+    'font_size_medium': '16px',
+    'font_size_base': '14px',
+    'font_size_small': '13px',
+    'font_size_extra_small': '12px'
+  }
+  export default {
+    created() {
+      bus.$on('user-theme-config-update', this.setGlobal);
+    },
+    mounted() {
+      this.setGlobal();
+    },
+    methods: {
+      tintColor(color, tint) {
+        return tintColor(color, tint);
+      },
+      setGlobal() {
+        if (window.userThemeConfig) {
+          this.global = window.userThemeConfig.global;
+        }
+      }
+    },
+    data() {
+      return {
+        global: {},
+        'font_size_extra_large': '',
+        'font_size_large': '',
+        'font_size_medium': '',
+        'font_size_base': '',
+        'font_size_small': '',
+        'font_size_extra_small': ''
+      }
+    },
+    watch: {
+      global: {
+        immediate: true,
+        handler(value) {
+          varMap.forEach((v) => {
+            const key = v.replace('$--', '').replace(/-/g, '_')
+            if (value[v]) {
+              this[key] = value[v]
+            } else {
+              this[key] = original[key]
+            }
+          });
+        }
+      }
+    },
   }
+</script>
+<style>
   .demo-typo-size {
-    .h1 {
-      font-size: 20px;
-    }
-    .h2 {
-      font-size: 18px;
-    }
-    .h3 {
-      font-size: 16px;
-    }
-    .text-regular {
-      font-size: 14px;
-    }
-    .text-small {
-      font-size: 13px;
-    }
-    .text-smaller {
-      font-size: 12px;
-    }
     .color-dark-light {
       color: #99a9bf;
     }
   }
-  .typo-PingFang {
-    font-family: 'PingFang SC';
-  }
-  .typo-Hiragino {
-    font-family: 'Hiragino Sans GB';
+  .demo-term-box img{
+    width: 24%;
+    margin: 0 4% 20px 0;
   }
-  .typo-Microsoft {
-    font-family: 'Microsoft YaHei';
+
+  .lineH-left {
+    display: inline-block;
+    height: 80px
   }
-  /* 英文 */
-  .typo-Helvetica-Neue {
-    font-family: 'Helvetica Neue';
+  .lineH-right {
+    display: inline-block;
+    list-style: none;
+    padding: 0 0 0 90px;
+    margin: 0;
+    vertical-align: top;
   }
-  .typo-Helvetica {
-    font-family: 'Helvetica';
+  .lineH-right li{
+    font-size: 13px;
+    color: #666;
+    height: 20px;
+    line-height: 20px;
   }
-  .typo-Arial {
-    font-family: 'Arial';
+  .lineH-right li span{
+    padding-left: 40px;
   }
 </style>
 
@@ -76,75 +98,85 @@
 
 我们对字体进行统一规范,力求在各个操作系统下都有最佳展示效果。
 
-### 中文字体
-
-<div class="demo-typo-box typo-PingFang">
-  和畅惠风
-  <div class="name">PingFang SC</div>
-</div>
-<div class="demo-typo-box typo-Hiragino">
-  和畅惠风
-  <div class="name">Hiragino Sans GB</div>
-</div>
-<div class="demo-typo-box typo-Microsoft">
-  和畅惠风
-  <div class="name">Microsoft YaHei</div>
-</div>
-
-### 英文/数字字体
-
-<div class="demo-typo-box typo-Helvetica-neue">
-  RGag
-  <div class="name">Helvetica Neue</div>
-</div>
-<div class="demo-typo-box typo-Helvetica">
-  RGag
-  <div class="name">Helvetica</div>
+### 字体
+<div class="demo-term-box">
+<img src="../../assets/images/term-pingfang.png" alt="">
+<img src="../../assets/images/term-hiragino.png" alt="">
+<img src="../../assets/images/term-microsoft.png" alt="">
+<img src="../../assets/images/term-sf.png" alt="">
+<img src="../../assets/images/term-helvetica.png" alt="">
+<img src="../../assets/images/term-arial.png" alt="">
 </div>
-<div class="demo-typo-box typo-Arial">
-  RGag
-  <div class="name">Arial</div>
-</div>
-
-### Font-family 代码
-
-```css
-font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif;
-```
 
-### 字体使用规范
+### 字号
 
 <table class="demo-typo-size">
   <tbody>
-    <tr>
-      <td class="h1">主标题</td>
-      <td class="h1">用 Element 快速搭建页面</td>
-      <td class="color-dark-light">20px  Extra large</td>
+  <tr
+    >
+      <td>层级</td>
+      <td>字体大小</td>
+      <td class="color-dark-light">举例</td>
     </tr>
-    <tr>
-      <td class="h2">标题</td>
-      <td class="h2">用 Element 快速搭建页面</td>
-      <td class="color-dark-light">18px large</td>
+    <tr
+    :style="{ fontSize: font_size_extra_small }"
+    >
+      <td>辅助文字</td>
+      <td class="color-dark-light">{{font_size_extra_small}} Extra Small</td>
+      <td>用 Element 快速搭建页面</td>
     </tr>
-    <tr>
-      <td class="h3">小标题</td>
-      <td class="h3">用 Element 快速搭建页面</td>
-      <td class="color-dark-light">16px Medium</td>
+    <tr
+    :style="{ fontSize: font_size_small }"
+    >
+      <td>正文(小)</td>
+      <td class="color-dark-light">{{font_size_small}} Small</td>
+      <td>用 Element 快速搭建页面</td>
     </tr>
-    <tr>
-      <td class="text-regular">正文</td>
-      <td class="text-regular">用 Element 快速搭建页面</td>
-      <td class="color-dark-light">14px Small</td>
+    <tr
+    :style="{ fontSize: font_size_base }"
+    >
+      <td>正文</td>
+      <td class="color-dark-light">{{font_size_base}} Base</td>
+      <td>用 Element 快速搭建页面</td>
     </tr>
-    <tr>
-      <td class="text-small">正文(小)</td>
-      <td class="text-small">用 Element 快速搭建页面</td>
-      <td class="color-dark-light">13px Extra Small</td>
+    <tr
+    :style="{ fontSize: font_size_medium }"
+    >
+      <td>小标题</td>
+      <td class="color-dark-light">{{font_size_medium}} Medium</td>
+      <td>用 Element 快速搭建页面</td>
     </tr>
-    <tr>
-      <td class="text-smaller">辅助文字</td>
-      <td class="text-smaller">用 Element 快速搭建页面</td>
-      <td class="color-dark-light">12px Extra Extra Small</td>
+    <tr
+    :style="{ fontSize: font_size_large }"
+    >
+      <td>标题</td>
+      <td class="color-dark-light">{{font_size_large}} large</td>
+      <td>用 Element 快速搭建页面</td>
+    </tr>
+    <tr
+    :style="{ fontSize: font_size_extra_large }"
+    >
+      <td>主标题</td>
+      <td class="color-dark-light">{{font_size_extra_large}} Extra large</td>
+      <td>用 Element 快速搭建页面</td>
     </tr>
   </tbody>
 </table>
+
+### 行高
+
+<div>
+<img class="lineH-left" src="~examples/assets/images/typography.png" />
+<ul class="lineH-right">
+<li>line-height:1 <span>无行高</span></li>
+<li>line-height:1.3 <span>紧凑</span></li>
+<li>line-height:1.5 <span>常规</span></li>
+<li>line-height:1.7 <span>宽松</span></li>
+</ul>
+</div>
+
+### Font-family 代码
+
+```css
+font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif;
+```

+ 75 - 0
examples/i18n/theme-editor.json

@@ -0,0 +1,75 @@
+[
+  {
+    "lang": "zh-CN",
+    "display-name": {
+      "border-color": "边框颜色",
+      "font-color": "文字颜色",
+      "background-color": "背景颜色",
+      "font-weight": "文字粗细",
+      "font-size": "文字大小",
+      "font-line-height": "文字行高",
+      "border-radius": "边框圆角",
+      "color": "颜色"
+    },
+    "action": {
+      "theme-editor": "主题编辑器",
+      "no-config": "暂不可编辑,敬请期待",
+      "reset-theme": "重置",
+      "download-theme": "下载"
+    },
+    "category": {
+      "BrandColor": "品牌颜色",
+      "SecondaryColor": "辅助颜色",
+      "FontColor": "文字颜色",
+      "BorderColor": "边框颜色",
+      "BackgroundColor": "背景颜色",
+      "Other": "其他",
+      "Color": "颜色",
+      "Border": "边框",
+      "Font": "文字",
+      "Radius": "边框圆角",
+      "Shadow": "阴影",
+      "FontSize": "文字大小",
+      "FontWeight": "文字粗细",
+      "LineHeight": "文字行高"
+    }
+  },
+  {
+    "lang": "en-US",
+    "display-name": {
+      "border-color": "border color",
+      "font-color": "font color",
+      "background-color": "background color",
+      "font-weight": "font weight",
+      "font-size": "font size",
+      "font-line-height": "font line height",
+      "border-radius": "border radius"
+    },
+    "action": {
+      "theme-editor": "Theme Editor",
+      "no-config": "Please stay tuned",
+      "reset-theme": "Reset",
+      "download-theme": "Download"
+    },
+    "category": {
+      "BrandColor": "Brand Color",
+      "SecondaryColor": "Secondary Color",
+      "FontColor": "Font Color",
+      "BorderColor": "Border Color",
+      "BackgroundColor": "Background Color",
+      "FontSize": "Font Size",
+      "FontWeight": "Font Weight",
+      "LineHeight": "Line Height"
+    }
+  },
+  {
+    "lang": "es",
+    "display-name": {
+    }
+  },
+  {
+    "lang": "fr-FR",
+    "display-name": {
+    }
+  }
+]

+ 16 - 0
examples/nav.config.json

@@ -59,6 +59,10 @@
               "path": "/typography",
               "title": "Typography 字体"
             },
+            {
+              "path": "/border",
+              "title": "Border 边框"
+            },
             {
               "path": "/icon",
               "title": "Icon 图标"
@@ -313,6 +317,10 @@
               "path": "/typography",
               "title": "Typography"
             },
+            {
+              "path": "/border",
+              "title": "Border"
+            },
             {
               "path": "/icon",
               "title": "Icon"
@@ -567,6 +575,10 @@
               "path": "/typography",
               "title": "Typography"
             },
+            {
+              "path": "/border",
+              "title": "Border"
+            },
             {
               "path": "/icon",
               "title": "Icon"
@@ -821,6 +833,10 @@
               "path": "/typography",
               "title": "Typography"
             },
+            {
+              "path": "/border",
+              "title": "Border"
+            },
             {
               "path": "/icon",
               "title": "Icon"

+ 41 - 3
examples/pages/template/component.tpl

@@ -133,6 +133,37 @@
     }
   }
 
+  @media (min-width: 1140px) {
+    .page-component__content {
+      transition:padding-right 0.3s ease;
+      &.theme-config {
+        padding-right: 26%;  
+      }
+    }
+    .page-container.page-component {
+      transition:width 0.3s ease;
+      &.theme-config {
+        width: 98%;
+      }
+      .page-component__nav {
+        padding-left: 2%;
+      }
+    }
+  }
+
+  @media (min-width: 1600px) {
+    .page-component__content {
+      &.theme-config {
+        padding-right: 25%;
+      }
+    }
+    .page-container.page-component {
+      &.theme-config {
+        width: 1600px;
+      }
+    }
+  }
+
   @media (max-width: 768px) {
     .page-component {
       .page-component__nav {
@@ -163,11 +194,11 @@
 </style>
 <template>
   <el-scrollbar class="page-component__scroll" ref="componentScrollBar">
-  <div class="page-container page-component">
+  <div class="page-container page-component" :class="{'theme-config': isThemeConfigVisible}">
     <el-scrollbar class="page-component__nav">
       <side-nav :data="navsData[lang]" :base="`/${ lang }/component`"></side-nav>
     </el-scrollbar>
-    <div class="page-component__content">
+    <div class="page-component__content" :class="{'theme-config': isThemeConfigVisible}">
       <router-view class="content"></router-view>
       <footer-nav></footer-nav>
     </div>
@@ -200,7 +231,8 @@
         scrollTop: 0,
         showHeader: true,
         componentScrollBar: null,
-        componentScrollBoxElement: null
+        componentScrollBoxElement: null,
+        isThemeConfigVisible: false
       };
     },
     watch: {
@@ -261,6 +293,9 @@
       bus.$on('navFade', val => {
         this.navFaded = val;
       });
+      bus.$on('user-theme-config-visible', val => {
+        this.isThemeConfigVisible = val;
+      });
       window.addEventListener('hashchange', () => {
         if (location.href.match(/#/g).length < 2) {
           document.documentElement.scrollTop = document.body.scrollTop = 0;
@@ -271,6 +306,9 @@
       });
     },
     mounted() {
+      if (window.userThemeConfigVisible) {
+        this.isThemeConfigVisible = window.userThemeConfigVisible;
+      }
       this.componentScrollBar = this.$refs.componentScrollBar;
       this.componentScrollBox = this.componentScrollBar.$el.querySelector('.el-scrollbar__wrap');
       this.throttledScrollHandler = throttle(300, this.handleScroll);

+ 16 - 3
package.json

@@ -27,9 +27,22 @@
     "test": "npm run lint && npm run build:theme && cross-env CI_ENV=/dev/ BABEL_ENV=test karma start test/unit/karma.conf.js --single-run",
     "test:watch": "npm run build:theme && cross-env BABEL_ENV=test karma start test/unit/karma.conf.js"
   },
-  "faas": {
-    "domain": "element",
-    "public": "temp_web/element"
+  "faas": [
+    {
+      "domain": "element",
+      "public": "temp_web/element"
+    },
+    {
+      "domain": "element-theme",
+      "public": "examples/element-ui",
+      "build": [
+        "yarn",
+        "npm run deploy:build"
+      ]
+    }
+  ],
+  "engines": {
+    "node": "8"
   },
   "repository": {
     "type": "git",

+ 13 - 13
packages/theme-chalk/src/button.scss

@@ -9,10 +9,10 @@
   line-height: 1;
   white-space: nowrap;
   cursor: pointer;
-  background: $--button-default-fill;
+  background: $--button-default-background-color;
   border: $--border-base;
-  border-color: $--button-default-border;
-  color: $--button-default-color;
+  border-color: $--button-default-border-color;
+  color: $--button-default-font-color;
   -webkit-appearance: none;
   text-align: center;
   box-sizing: border-box;
@@ -75,11 +75,11 @@
     &,
     &:hover,
     &:focus {
-      color: $--button-disabled-color;
+      color: $--button-disabled-font-color;
       cursor: not-allowed;
       background-image: none;
-      background-color: $--button-disabled-fill;
-      border-color: $--button-disabled-border;
+      background-color: $--button-disabled-background-color;
+      border-color: $--button-disabled-border-color;
     }
 
     &.el-button--text {
@@ -91,8 +91,8 @@
       &:hover,
       &:focus {
         background-color: $--color-white;
-        border-color: $--button-disabled-border;
-        color: $--button-disabled-color;
+        border-color: $--button-disabled-border-color;
+        color: $--button-disabled-font-color;
       }
     }
   }
@@ -122,19 +122,19 @@
     padding: $--button-padding-vertical;
   }
   @include m(primary) {
-    @include button-variant($--button-primary-color, $--button-primary-fill, $--button-primary-border);
+    @include button-variant($--button-primary-font-color, $--button-primary-background-color, $--button-primary-border-color);
   }
   @include m(success) {
-    @include button-variant($--button-success-color, $--button-success-fill, $--button-success-border);
+    @include button-variant($--button-success-font-color, $--button-success-background-color, $--button-success-border-color);
   }
   @include m(warning) {
-    @include button-variant($--button-warning-color, $--button-warning-fill, $--button-warning-border);
+    @include button-variant($--button-warning-font-color, $--button-warning-background-color, $--button-warning-border-color);
   }
   @include m(danger) {
-    @include button-variant($--button-danger-color, $--button-danger-fill, $--button-danger-border);
+    @include button-variant($--button-danger-font-color, $--button-danger-background-color, $--button-danger-border-color);
   }
   @include m(info) {
-    @include button-variant($--button-info-color, $--button-info-fill, $--button-info-border);
+    @include button-variant($--button-info-font-color, $--button-info-background-color, $--button-info-border-color);
   }
   @include m(medium) {
     @include button-size($--button-medium-padding-vertical, $--button-medium-padding-horizontal, $--button-medium-font-size, $--button-medium-border-radius);

+ 6 - 6
packages/theme-chalk/src/checkbox.scss

@@ -256,10 +256,10 @@
     white-space: nowrap;
     vertical-align: middle;
     cursor: pointer;
-    background: $--button-default-fill;
+    background: $--button-default-background-color;
     border: $--border-base;
     border-left: 0;
-    color: $--button-default-color;
+    color: $--button-default-font-color;
     -webkit-appearance: none;
     text-align: center;
     box-sizing: border-box;
@@ -306,15 +306,15 @@
 
   &.is-disabled {
     & .el-checkbox-button__inner {
-      color: $--button-disabled-color;
+      color: $--button-disabled-font-color;
       cursor: not-allowed;
       background-image: none;
-      background-color: $--button-disabled-fill;
-      border-color: $--button-disabled-border;
+      background-color: $--button-disabled-background-color;
+      border-color: $--button-disabled-border-color;
       box-shadow: none;
     }
     &:first-child .el-checkbox-button__inner {
-      border-left-color: $--button-disabled-border;
+      border-left-color: $--button-disabled-border-color;
     }
   }
 

+ 134 - 61
packages/theme-chalk/src/common/var.scss

@@ -1,5 +1,8 @@
 /* Element Chalk Variables */
 
+// Special comment for theme configurator
+// type|[Chinese Name, English Name, Spanish Name, French Name]|Category|Order
+
 /* Transition
 -------------------------- */
 $--all-transition: all .3s cubic-bezier(.645,.045,.355,1) !default;
@@ -9,12 +12,14 @@ $--md-fade-transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1), opacity 3
 $--border-transition-base: border-color .2s cubic-bezier(.645,.045,.355,1) !default;
 $--color-transition-base: color .2s cubic-bezier(.645,.045,.355,1) !default;
 
-/* Colors
+/* Color
 -------------------------- */
-$--color-white: #fff !default;
-$--color-black: #000 !default;
-
+/// color|[主题色,primary color,color primario]|BrandColor|0
 $--color-primary: #409EFF !default;
+/// color|[基础白色,basic white,basic white]|BackgroundColor|4
+$--color-white: #FFFFFF !default;
+/// color|[基础黑色,basic black,basic black]|BackgroundColor|4
+$--color-black: #000000 !default;
 $--color-primary-light-1: mix($--color-white, $--color-primary, 10%) !default; /* 53a8ff */
 $--color-primary-light-2: mix($--color-white, $--color-primary, 20%) !default; /* 66b1ff */
 $--color-primary-light-3: mix($--color-white, $--color-primary, 30%) !default; /* 79bbff */
@@ -24,10 +29,13 @@ $--color-primary-light-6: mix($--color-white, $--color-primary, 60%) !default; /
 $--color-primary-light-7: mix($--color-white, $--color-primary, 70%) !default; /* c6e2ff */
 $--color-primary-light-8: mix($--color-white, $--color-primary, 80%) !default; /* d9ecff */
 $--color-primary-light-9: mix($--color-white, $--color-primary, 90%) !default; /* ecf5ff */
-
-$--color-success: #67c23a !default;
-$--color-warning: #e6a23c !default;
-$--color-danger: #f56c6c !default;
+/// color|[成功颜色,success color,success color]|SecondaryColor|1
+$--color-success: #67C23A !default;
+/// color|[警告颜色,warning color,warning color]|SecondaryColor|1
+$--color-warning: #E6A23C !default;
+/// color|[危险颜色,danger color,danger color]|SecondaryColor|1
+$--color-danger: #F56C6C !default;
+/// color|[信息颜色,info color,info color]|SecondaryColor|1
 $--color-info: #909399 !default;
 
 $--color-success-light: mix($--color-white, $--color-success, 80%) !default;
@@ -39,55 +47,81 @@ $--color-success-lighter: mix($--color-white, $--color-success, 90%) !default;
 $--color-warning-lighter: mix($--color-white, $--color-warning, 90%) !default;
 $--color-danger-lighter: mix($--color-white, $--color-danger, 90%) !default;
 $--color-info-lighter: mix($--color-white, $--color-info, 90%) !default;
-
+/// color|[主要文字颜色,primary text color,primary text color]|FontColor|2
 $--color-text-primary: #303133 !default;
+/// color|[常规文字颜色,regular text color,regular text color]|FontColor|2
 $--color-text-regular: #606266 !default;
+/// color|[次要文字颜色,secondary text color,secondary text color]|FontColor|2
 $--color-text-secondary: #909399 !default;
-$--color-text-placeholder: #c0c4cc !default;
+/// color|[占位文字颜色,placeholder text color,placeholder text color]|FontColor|2
+$--color-text-placeholder: #C0C4CC !default;
+/// color|[一级边框颜色,border color,border color]|BorderColor|3
+$--border-color-base: #DCDFE6 !default;
+/// color|[二级边框颜色,border color light,border color light]|BorderColor|3
+$--border-color-light: #E4E7ED !default;
+/// color|[三级边框颜色,border color lighter,border color lighter]|BorderColor|3
+$--border-color-lighter: #EBEEF5 !default;
+/// color|[四级边框颜色,border color extra light,border color extra light]|BorderColor|3
+$--border-color-extra-light: #F2F6FC !default;
+
+// Background
+/// color|[基础背景色,base background color,base background color]|BackgroundColor|4
+$--background-color-base: #f5f7fa !default;
 
 /* Link
 -------------------------- */
 $--link-color: $--color-primary-light-2 !default;
 $--link-hover-color: $--color-primary !default;
 
-/* Background
--------------------------- */
-$--background-color-base: #f5f7fa !default;
-
 /* Border
 -------------------------- */
 $--border-width-base: 1px !default;
 $--border-style-base: solid !default;
-$--border-color-base: #dcdfe6 !default;
-$--border-color-light: #e4e7ed !default;
-$--border-color-lighter: #ebeef5 !default;
-$--border-color-extra-light: #f2f6fc !default;
 $--border-color-hover: $--color-text-placeholder !default;
 $--border-base: $--border-width-base $--border-style-base $--border-color-base !default;
+/// borderRadius|[大圆角,border radius base,border radius base]|Radius|0
 $--border-radius-base: 4px !default;
+/// borderRadius|[小圆角,border radius small,border radius small]|Radius|0
 $--border-radius-small: 2px !default;
+/// borderRadius|[圆形圆角,border radius circle,border radius circle]|Radius|0
 $--border-radius-circle: 100% !default;
 
-/* Box-shadow
--------------------------- */
+// Box-shadow
+/// boxShadow|[基础投影,box shadow base,box shadow base]|Shadow|1
 $--box-shadow-base: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04) !default;
+// boxShadow|[深色投影,box shadow,box shadow]|Shadow|1
 $--box-shadow-dark: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .12) !default;
+/// boxShadow|[浅色投影,box shadow,box shadow]|Shadow|1
 $--box-shadow-light: 0 2px 12px 0 rgba(0, 0, 0, 0.1) !default;
 
 /* Fill
 -------------------------- */
 $--fill-base: $--color-white !default;
 
-/* Font
+/* Typography
 -------------------------- */
 $--font-path: 'fonts' !default;
+/// fontSize|[主标题文字大小,Extra large font size,Extra large font size]|FontSize|0
+$--font-size-extra-large: 20px !default;
+/// fontSize|[标题文字大小,large font size,large font size]|FontSize|0
+$--font-size-large: 18px !default;
+/// fontSize|[小标题文字大小,Medium font size,Medium font size]|FontSize|0
+$--font-size-medium: 16px !default;
+/// fontSize|[正文文字大小,base font size,base font size]|FontSize|0
 $--font-size-base: 14px !default;
+/// fontSize|[正文(小)文字大小,small font size,small font size]|FontSize|0
 $--font-size-small: 13px !default;
-$--font-size-large: 18px !default;
-$--font-color-disabled-base: #bbb !default;
+/// fontSize|[辅助文字大小,Extra small font size,Extra small font size]|FontSize|0
+$--font-size-extra-small: 12px !default;
+/// fontWeight|[主要文字粗细,primary font weight,primary font weight]|FontWeight|1
 $--font-weight-primary: 500 !default;
+/// fontWeight|[次要文字粗细,secondary font weight,secondary font weight]|FontWeight|1
+$--font-weight-secondary: 100 !default;
+/// fontLineHeight|[主要文字行高,primary font line height,primary font line height]|LineHeight|2
 $--font-line-height-primary: 24px !default;
-
+/// fontLineHeight|[次要文字行高,secondary font line height,secondary font line height]|LineHeight|2
+$--font-line-height-secondary: 16px !default;
+$--font-color-disabled-base: #bbb !default;
 /* Size
 -------------------------- */
 $--size-base: 14px !default;
@@ -161,15 +195,22 @@ $--checkbox-button-checked-border-color: $--color-primary !default;
 
 /* Radio
 -------------------------- */
-$--radio-font-size: 14px !default;
+/// fontSize|[]|Font|1
+$--radio-font-size: $--font-size-base !default;
+/// fontWeight|[]|Font|1
 $--radio-font-weight: $--font-weight-primary !default;
-$--radio-color: $--color-text-regular !default;
+/// color|[]|Color|0
+$--radio-font-color: $--color-text-regular !default;
 $--radio-input-height: 14px !default;
 $--radio-input-width: 14px !default;
+/// borderRadius|[]|Border|2
 $--radio-input-border-radius: $--border-radius-circle !default;
-$--radio-input-fill: $--color-white !default;
+/// color|[]|Color|0
+$--radio-input-background-color: $--color-white !default;
 $--radio-input-border: $--border-base !default;
+/// color|[]|Color|0
 $--radio-input-border-color: $--border-color-base !default;
+/// color|[]|Color|0
 $--radio-icon-color: $--color-white !default;
 
 $--radio-disabled-input-border-color: $--disabled-border-base !default;
@@ -180,9 +221,13 @@ $--radio-disabled-checked-input-border-color: $--disabled-border-base !default;
 $--radio-disabled-checked-input-fill: $--disabled-fill-base !default;
 $--radio-disabled-checked-icon-color: $--color-text-placeholder !default;
 
-$--radio-checked-text-color: $--color-primary !default;
+/// color|[]|Color|0
+$--radio-checked-font-color: $--color-primary !default;
+/// color|[]|Color|0
 $--radio-checked-input-border-color: $--color-primary !default;
-$--radio-checked-input-fill: $--color-white !default;
+/// color|[]|Color|0
+$--radio-checked-input-background-color: $--color-white !default;
+/// color|[]|Color|0
 $--radio-checked-icon-color: $--color-primary !default;
 
 $--radio-input-border-color-hover: $--color-primary !default;
@@ -202,9 +247,13 @@ $--radio-bordered-mini-input-height: 12px !default;
 $--radio-bordered-mini-input-width: 12px !default;
 $--radio-bordered-mini-height: 28px !default;
 
+/// fontSize|[]|Font|1
 $--radio-button-font-size: $--font-size-base !default;
-$--radio-button-checked-fill: $--color-primary !default;
-$--radio-button-checked-color: $--color-white !default;
+/// color|[]|Color|0
+$--radio-button-checked-background-color: $--color-primary !default;
+/// color|[]|Color|0
+$--radio-button-checked-font-color: $--color-white !default;
+/// color|[]|Color|0
 $--radio-button-checked-border-color: $--color-primary !default;
 $--radio-button-disabled-checked-fill: $--border-color-extra-light !default;
 
@@ -393,17 +442,23 @@ $--tab-vertical-header-count-fill: $--color-text-secondary !default;
 
 /* Button
 -------------------------- */
-$--button-font-size: 14px !default;
+/// fontSize|[]|Font|1
+$--button-font-size: $--font-size-base !default;
+/// fontWeight|[]|Font|1
 $--button-font-weight: $--font-weight-primary !default;
+/// borderRadius|[]|Border|2
 $--button-border-radius: $--border-radius-base !default;
 $--button-padding-vertical: 12px !default;
 $--button-padding-horizontal: 20px !default;
 
-$--button-medium-font-size: 14px !default;
+/// fontSize|[]|Font|1
+$--button-medium-font-size: $--font-size-base !default;
+/// borderRadius|[]|Border|2
 $--button-medium-border-radius: $--border-radius-base !default;
 $--button-medium-padding-vertical: 10px !default;
 $--button-medium-padding-horizontal: 20px !default;
 
+/// fontSize|[]|Font|1
 $--button-small-font-size: 12px !default;
 $--button-small-border-radius: #{$--border-radius-base - 1} !default;
 $--button-small-padding-vertical: 9px !default;
@@ -414,33 +469,50 @@ $--button-mini-border-radius: #{$--border-radius-base - 1} !default;
 $--button-mini-padding-vertical: 7px !default;
 $--button-mini-padding-horizontal: 15px !default;
 
-$--button-default-color: $--color-text-regular !default;
-$--button-default-fill: $--color-white !default;
-$--button-default-border: $--border-color-base !default;
-
-$--button-disabled-color: $--color-text-placeholder !default;
-$--button-disabled-fill: $--color-white !default;
-$--button-disabled-border: $--border-color-lighter !default;
-
-$--button-primary-border: $--color-primary !default;
-$--button-primary-color: $--color-white !default;
-$--button-primary-fill: $--color-primary !default;
-
-$--button-success-border: $--color-success !default;
-$--button-success-color: $--color-white !default;
-$--button-success-fill: $--color-success !default;
-
-$--button-warning-border: $--color-warning !default;
-$--button-warning-color: $--color-white !default;
-$--button-warning-fill: $--color-warning !default;
-
-$--button-danger-border: $--color-danger !default;
-$--button-danger-color: $--color-white !default;
-$--button-danger-fill: $--color-danger !default;
-
-$--button-info-border: $--color-info !default;
-$--button-info-color: $--color-white !default;
-$--button-info-fill: $--color-info !default;
+/// color|[]|Color|0
+$--button-default-font-color: $--color-text-regular !default;
+/// color|[]|Color|0
+$--button-default-background-color: $--color-white !default;
+/// color|[]|Color|0
+$--button-default-border-color: $--border-color-base !default;
+
+/// color|[]|Color|0
+$--button-disabled-font-color: $--color-text-placeholder !default;
+/// color|[]|Color|0
+$--button-disabled-background-color: $--color-white !default;
+/// color|[]|Color|0
+$--button-disabled-border-color: $--border-color-lighter !default;
+
+/// color|[]|Color|0
+$--button-primary-border-color: $--color-primary !default;
+/// color|[]|Color|0
+$--button-primary-font-color: $--color-white !default;
+/// color|[]|Color|0
+$--button-primary-background-color: $--color-primary !default;
+/// color|[]|Color|0
+$--button-success-border-color: $--color-success !default;
+/// color|[]|Color|0
+$--button-success-font-color: $--color-white !default;
+/// color|[]|Color|0
+$--button-success-background-color: $--color-success !default;
+/// color|[]|Color|0
+$--button-warning-border-color: $--color-warning !default;
+/// color|[]|Color|0
+$--button-warning-font-color: $--color-white !default;
+/// color|[]|Color|0
+$--button-warning-background-color: $--color-warning !default;
+/// color|[]|Color|0
+$--button-danger-border-color: $--color-danger !default;
+/// color|[]|Color|0
+$--button-danger-font-color: $--color-white !default;
+/// color|[]|Color|0
+$--button-danger-background-color: $--color-danger !default;
+/// color|[]|Color|0
+$--button-info-border-color: $--color-info !default;
+/// color|[]|Color|0
+$--button-info-font-color: $--color-white !default;
+/// color|[]|Color|0
+$--button-info-background-color: $--color-info !default;
 
 $--button-hover-tint-percent: 20% !default;
 $--button-active-shade-percent: 10% !default;
@@ -470,7 +542,8 @@ $--dialog-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3) !default;
 $--dialog-close-hover-color: $--color-primary !default;
 $--dialog-title-font-size: $--font-size-large !default;
 $--dialog-font-size: 14px !default;
-$--dialog-line-height: $--font-line-height-primary !default;
+/// fontLineHeight|[]
+$--dialog-font-line-height: $--font-line-height-primary !default;
 $--dialog-padding-primary: 20px !default;
 
 /* Table

+ 1 - 1
packages/theme-chalk/src/dialog.scss

@@ -58,7 +58,7 @@
   }
 
   @include e(title) {
-    line-height: $--dialog-line-height;
+    line-height: $--dialog-font-line-height;
     font-size: $--dialog-title-font-size;
     color: $--color-text-primary;
   }

+ 7 - 7
packages/theme-chalk/src/radio-button.scss

@@ -12,11 +12,11 @@
     line-height: 1;
     white-space: nowrap;
     vertical-align: middle;
-    background: $--button-default-fill;
+    background: $--button-default-background-color;
     border: $--border-base;
     font-weight: $--button-font-weight;
     border-left: 0;
-    color: $--button-default-color;
+    color: $--button-default-font-color;
     -webkit-appearance: none;
     text-align: center;
     box-sizing: border-box;
@@ -57,8 +57,8 @@
 
     &:checked {
       & + .el-radio-button__inner {
-        color: $--radio-button-checked-color;
-        background-color: $--radio-button-checked-fill;
+        color: $--radio-button-checked-font-color;
+        background-color: $--radio-button-checked-background-color;
         border-color: $--radio-button-checked-border-color;
         box-shadow: -1px 0 0 0 $--radio-button-checked-border-color;
       }
@@ -66,11 +66,11 @@
 
     &:disabled {
       & + .el-radio-button__inner {
-        color: $--button-disabled-color;
+        color: $--button-disabled-font-color;
         cursor: not-allowed;
         background-image: none;
-        background-color: $--button-disabled-fill;
-        border-color: $--button-disabled-border;
+        background-color: $--button-disabled-background-color;
+        border-color: $--button-disabled-border-color;
         box-shadow: none;
       }
       &:checked + .el-radio-button__inner {

+ 3 - 3
packages/theme-chalk/src/radio.scss

@@ -4,7 +4,7 @@
 @import "common/var";
 
 @include b(radio) {
-  color: $--radio-color;
+  color: $--radio-font-color;
   font-weight: $--radio-font-weight;
   line-height: 1;
   position: relative;
@@ -135,7 +135,7 @@
       }
 
       & + .el-radio__label {
-        color: $--radio-checked-text-color;
+        color: $--radio-checked-font-color;
       }
     }
 
@@ -150,7 +150,7 @@
     border-radius: $--radio-input-border-radius;
     width: $--radio-input-width;
     height: $--radio-input-height;
-    background-color: $--radio-input-fill;
+    background-color: $--radio-input-background-color;
     position: relative;
     cursor: pointer;
     display: inline-block;