preface
Online preview address:Dynamic-Antd-Theme-Demo If you know how to use it, I’m sure you’ll think I’m not a clicker.
// how to use import DynamicAntdTheme from 'dynamic-antd-theme'; render() { ... <DynamicAntdTheme /> ... }Copy the code
Yes, it is that simple, for your convenience, I have made a plug and play component ~ welcome everyone star + issue + PR. dynamic-antd-theme
Why do ant-Design skin change scheme
Here’s what I’d like to say: I’m not arrogant or greedy enough to implement a perfectly rational ANTD dynamic peeler solution. For example, antD-theme-webpack-plugin and ANTD-Theme-Generator in the open source community are very mature schemes, which use webpack to realize dynamic skin in the page by introducing less. Js, which is completely changed.
So why am I doing a dynamic peeler? The reasons are as follows:
- First of all, the above two schemes can not cover all the scenes, he needs the entry file
index.html
As a NextJS party, I couldn’t find it anywhere, so it didn’t work in my NextJS scaffolding project. - Secondly, I think configuration and use is a little tedious and introduces too much extra content. The HTML file must import the less.js file so that the theme can be dynamically changed using window.less. There is also a variety of CONFIGURATION CSS files are not clear meaning.
- And last but not least. There was a question in my post about next-antd-scaffold. If you are interested check out my previous post. Small partners have no configuration success, and no ideas, how to do, as an enthusiastic author, must help solve ah ~
Next- ANTD-scaffold has been basically developed. I am also using this Scaffold to write projects. If you are interested in Nextjs, please join the wechat group at the bottom of this article to communicate with us. I try my best to solve the problem, 😄
To sum up, I spent my weekend coming up with a plug-and-play ANTD peeler solution for one-click installation and direct use. Of course, since it is so simple, there must be drawbacks, after all, my topic is the simplest ANTD skin change program rather than the most perfect ANTD skin change program, listen to me in detail.
The implementation process
I will explain the specific implementation process from the perspectives of ideas, solutions, difficulties in details and drawbacks of the project.
All of my examples here are written with next-antd-scaffold
Train of thought
First, let’s talk about the idea. Since I want to be innovative and the simplest skin changing scheme of ANTD, there must be no complicated configuration process and unclear documentation. Of course, to be honest, UNTIL I read the source code and webpack mechanism in depth, I was not able to write the level of the above two plug-ins, ha ha 😄.
So, we have to find another way. My idea is, can the system run in the process, through the way of class overwrite to dynamically change the color, because CSS loading mechanism is top to bottom, the same name will overwrite the corresponding attributes, use this feature to simply try.
Take the button button as an example, you can see that we set @primarycolor = #524c1a and the class name is.ant-btn-primary, so let’s override it.
// Const styleDom = document.createElement('style'); styleDom.innerHTML = ` .ant-btn-primary { border-color: #0000ff; background-color: #0000ff; } `; document.getElementsByTagName('head')[0].appendChild(styleDom);Copy the code
As you can see, the buttons change color without refreshing the page. So, that’s a good idea, and then we have to worry about the details.
plan
Through the above practice, we determined the train of thought, here to determine the feasibility of the program. To tell the truth, most of the front-end development of class name cover this problem should be able to think of, there should also be some people in use, after all, the need for skin is the basic needs of many background systems, but even if the class name cover different people have different practices, and the difficulty of class name cover is — covering the basic color is good, If you cover every pseudo-class element such as hover :active :focus with an appropriate color, not only implementation is difficult, but also a lot of work. I think here is, I will give you to achieve a class name coverage of the universal scheme, you no longer need to tedious a page a page to achieve, or all kinds of modification of CSS files, just need to introduce this plug-in, automatically all classes are covered.
Concrete implementation process
-
Extract all color related classes of ANTD
I downloaded antD-V3.19.0 CSS file to the local, about 2W+ lines of code, from which I carefully and patiently extract all @primaryColor related colors (including :hover: Focus :active). Because only the color-related attributes under the class are retained, it’s reduced to about 900 lines of code.
Here is a simple screenshot, not to show you, if you want to see the address in the theme. CSS.
The point here is,
:root{ ... }
The following colorVar is also a design scheme, because in this way, I only need to get the color set by the user, and then generate the relevant color to replace the colorVar variable. Otherwise I need to write a re that matches all colors, which is not as efficient as this one -
Get the user color dynamically and then replace it
Here is actually very simple, is to modify the content of the style tag we want to insert, the specific code is as follows:
const cssVar = ` :root { --primary-color: ${primaryColor}; --primary-hover-color: ${hoverColor}; --primary-active-color: ${activeColor}; --primary-shadow-color: ${shadowColor}; } `; <style> let styleNode = document.getelementByid ('dynamic_antd_theme_custom_style'); if (! styleNode) { // avoid repeat insertion styleNode = document.createElement('style'); styleNode.id = 'dynamic_antd_theme_custom_style'; styleNode.innerHTML = `${cssVar}\n${cssContent}`; document.getElementsByTagName('head')[0].appendChild(styleNode); } else { styleNode.innerHTML = `${cssVar}\n${cssContent}`; }Copy the code
I used the react-color plugin to dynamically obtain the color, and then, since it is a skin change, it should definitely save the user’s choice, so it is matched with localStorage client cache, the final effect is like this:
Some people may say, you said a long time @primarycolor related color, to now there is only a @primarycolor, is not in this pull off, 😄 don’t worry, I said is the most important place, is certainly put in the difficult.
The difficulties in
It’s worth sharing here that the challenge isn’t to extract all the attributes associated with @primaryColor from 20,000 lines of code, Instead, you need to dynamically generate a light-colored @primaryHoverColor and a dark-colored @primaryActivecolor from the selected @primaryColor.
Used by the people here should know what I mean, antd button button, a label, etc., : hover / : focus / : active / : visited, etc. These properties have their own color, some is compared with @ primaryColor more shallow, Some are darker than @primaryColor — see the GIF below for details, hover is lighter and active darker
You can’t just unify all the color related attributes into one color, although that would be very simple, but in terms of user experience, it would be better not to do skin change. So let’s talk about this implementation in more detail.
To see the source of ANTD can be found, it is not related to :hover :active :focus color detailed Settings, but all colors are converted by @primarycolor.
As you can see, @primarycolor is divided into a color level with Level6 as the dividing line. <6 will be lighter than @primarycolor, suitable for hover, and >6 will be darker than @primarycolor, suitable for active. All colors are generated from the method colorPalette, so this is the one we’ll be talking about in detail.
In advance, I tried my best to understand and try, but finally I realized only four colors, instead of 10 levels like the original, do not spray, welcome to mention PR interested, do better and better ~
/** * The following code represents my own prior process and capability level * I did not read the official implementation carefully, Function getHoverColor (color, index = 5) {return tinycolor.mix('# FFFFFF ', color, currentEasing(index) * 100 / primaryEasing ).toHexString(); } function getActiveColor (color, index = 7) {return tinycolor.mix('#333333', color, (1 - (currentEasing(index) - primaryEasing) / (1 - primaryEasing)) * 100 ).toHexString(); }Copy the code
There are two functions above, one to get light color, one to get dark color. Both functions are called internally by a method called tinycolor.mix, and it’s easy to understand if we look at the parameters. The mix method actually makes our @primarycolor merge with another color, such as # FFFFFF. If you have learned to draw, you will know that if you mix a color with a white color, the color will become lighter, but it will not change to any other color, i.e. blue -> light blue, red -> light red, etc. The other color will also be darker when combined with #000000 and other black and gray colors. Let’s look at the mix function:
/* tinycolor-mix */
tinycolor.mix = function(color1, color2, amount) {
amount = (amount === 0) ? 0 : (amount || 50);
var rgb1 = tinycolor(color1).toRgb();
var rgb2 = tinycolor(color2).toRgb();
var p = amount / 100;
var rgba = {
r: ((rgb2.r - rgb1.r) * p) + rgb1.r,
g: ((rgb2.g - rgb1.g) * p) + rgb1.g,
b: ((rgb2.b - rgb1.b) * p) + rgb1.b,
a: ((rgb2.a - rgb1.a) * p) + rgb1.a
};
return tinycolor(rgba);
};
Copy the code
This is to input two colors and a weight, and finally output a color value for RGBA. Tinycolor here is a color related plug-in, interested can go to see.
Ok, and then what is the weight ah, here to tell the truth, I am not very good at math, I really do not understand, anyway, it is a Bayesian curve, is to make our color transformation more smooth ~ other article explanation is probably like this, and there is a picture:
Here is more in-depth I don’t say, also don’t understand, anyway I am a copy of the tiger draw out. It should be emphasized that the curve needs to select a baseline, and the baseline of ANTD is as follows:
/* basic-easiing */ const baseEasing = BezierEasing(0.26, 0.09, 0.37, 0.18); Const primaryEasing = baseEasing(0.6); // The baseline of the fusion color const currentEasing = index => baseEasing(index * 0.1);Copy the code
I don’t know if I made it clear, but after a series of complicated operations above, you can generate the corresponding color values of the main color system based on your input, and then perform cssVar replacement
Other features
You can use it directly by:
npm install dynamic-antd-theme
or
yarn add dynamic-antd-theme
Copy the code
The following attributes can be set for the component:
The component is actually the easiest ever to use, and the result is actually better than I originally thought.
Drawbacks and legacy problems
- Ability energy is limited, just do
@primaryColor
Related coverage - Other overrides are still required :global or intra-component overrides
- The effect test of all components has not been done. There may be some deviation in the effect of some scenes. The issue will be solved in time
- Will insert a paragraph of about 900 lines into the project
<style>
The label
Emergency update
Color calculation feedback
After this article was published, ant Financial’s partner appeared in the comments section, giving out their already open source color computing plug-in. Haha, I spent a lot of time and effort, so I thought IT was my own research. Later, it may be replaced with the official color calculated by Ant-Design, in order to be closer to the original
Partial limitation problem
The point here is that, for example, if I set the theme color, the THEME color of the ANTD component style does change, but if it is my native CSS style, it is not changed. For example, if I have a Header component, I initialize the background color to be the same as the theme color, but when I change the theme color, I only change the theme color of antD component, but I do not change the theme color of my Header component, so it looks very obtruding.
For more details, please see the comments below. Thanks @myran for your comments
After learning about this scenario, I came up with a solution to dynamically add a property: themeChangeCallback, pass in a function that takes the changed theme color, and do something to override our local style
Document.getelementbyid ('sys_name').style.backgroundcolor = color; }... <DynamicAntdTheme style={themeStyle} placement='bottomLeft' themeChangeCallback={this.themeChangeCallback} />Copy the code
Well, this looks much better. I still hope everyone can give more comments. If you have ideas, you can PR, and if you don’t, you can comment or issue directly. It’s all ok
Compatibility issues
Because the usual development is not compatible with IE, so this component does not support IE browser, but some friends in the group said that their project still needs to be compatible with IE, there is no way, update it, compatible with IE.
The latest version is V0.2.4.
conclusion
Some people may feel that there is no technical content, is to do a lot of repeated a lot of CSS operations to cover the name of the class, to tell the truth, I also admit, but I think there is a famous saying: there is no way in the world, walk more people, become a way. The same goes for this solution. Everyone is willing to do a lot of complex class substitutions in their own projects, but they don’t want to bother with a generic ANTD override class file, and I’m just willing to show you the way, that’s all. It’s not a technical plugin, I admit it, lol 😄.
In addition, I hope you can give more star and issue, why? Because if you want to do a good job, a person must have limited ability, it is impossible to adapt all components and all scenes. If you can tell me which version and which component has the wrong effect when using it, I can timely modify and put it online. In addition, if you are interested, you can also mention MORE PR and maintain together. The current version supports most component effects of ANTD Version <= 3.19.0.
If you have any questions, please feel free to contact us, thanks 🙏. Click star not lost dynamic-antd-theme
Next. Js small exchange group address: