This article is written by BingLee1994, a member of the team, and we have authorized the exclusive use of doodle big front, including but not limited to editing, original annotation and other rights.

Variables are familiar. Why do you need variables in your code? Because the same value is referenced in multiple places, we extract it as a variable, so that only the variable needs to be modified, and all referenced places are modified synchronously.

CSS custom variables

CSS has always been a simple UI description language, unable to use more advanced features like JS. Later, the birth of CSS variables gives CSS more capabilities, enabling me to achieve “one modification, multiple effects”, which not only saves development time, It also allows us to move more UI variable code (global context, state, etc.) from JS to CSS for easy maintenance. Theme peels (dark mode/dark mode/eye protection mode also fall into this category) are one of the CSS variable usage scenarios.

If you haven’t already worked with CSS variables, you can go to the tutorial and learn about them. They are really easy to learn and only take a few minutes.

compatibility

If you’re still hard compatible with old IE11, which unfortunately doesn’t support it, you should not use it, especially if you apply it to layout styles such as margin, Flex, Position, etc., which will cause them to fail and crash the page.

There are no compatibility issues with this variable

Incompatible hardballs cannot be fixed, but fortunately there are no compatibility problems with the CSS variable described below.

Don’t underestimate it, even if it is the only variable that exists, it is still helpful in many use scenarios!

Early experience

This variable is used in many places, but you don’t realize it. The guest officer reads down:

Let’s create two normal hyperlinks. To make things easier for you, let’s create two different styles of underline:

<a href="https://www.tuya.com">Visit the website</a>
<a style="text-decoration-style: wavy;" href="https://developer.tuya.com/cn">Interview developers</a>
Copy the code

The following information is displayed:

We found that the underline and wavy lines looked the same color as the font, so let’s continue experimenting:

We changed the font color of the two hyperlinks to the theme color respectively. Graffiti orange: RGB (255 72 0), and vitality blue: RGB (78 133 254).

<a style="color: rgb(255 72 0);" href="https://www.tuya.com">Visit the website</a>
<a style="color: rgb(78 133 254); text-decoration-style: wavy;" href="https://developer.tuya.com/cn">Interview developers</a>
Copy the code

We were pleasantly surprised to find that both the underline and the wavy line were visually synchronized with the font color,But the lines may not have been drawn in font.

We can even verify this by getting the true color value of the computed style with js:

getComputedStyle(firstLink).textDecorationColor
Copy the code

The final color value applied is the font color itself, confirming our guess. The default textDecorationColor is currentColor

currentColor

CurrentColot = color. This property is used to read and synchronize font colors for other purposes, such as wave colors. The default value of border-color is currentColor as well.

Since color can inherit variables from parent or ancestor elements, it only needs to set the color once for the parent element to penetrate to the bottom element.

Complete real case

Let’s make a color button like this, the default is a hollow button, hover to a solid button.

This is not too difficult for you, so let’s go with the old idea:

<button class="btn">
  <span class="btn-txt">Visit the website</span>
  <span class="btn-icon">
    <! -- Icon (if required) -->
  </span>
</button>
Copy the code
button {
  margin: 0;
  padding: 0;
  outline: none;
  border: none;
  background: none;
} /* reset CSS to clear the button style */

.btn {
  color: rgb(78 133 254); /* Default color */
  height: 30px;
  line-height: 30px; / * * /
  padding: 0 8px; 
  border-radius: 6px; / * rounded corners * /
  border-style: solid;
  border-width: 1px; Bezel / * * /
  cursor: pointer;
}

/* hover */
.btn:hover {
  background-color: rgb(78 133 254);
}

.btn:active {
  opacity:.8;
}

.btn:hover > .btn-txt {
  color: white;
}
Copy the code

It’s easy for you, so don’t get too excited. Read on:

Increase in demand

Now we need to add two additional color themes, green for success and red for warning.

This is also a piece of cake. We can add two feature styles to the base style to cover the font color and hover background color:

/* Leave the source code unchanged and add the following code */
/* Success color */
.btn.btn-success {
  color: #7cb305;
}
.btn.btn-success:hover {
  background-color: #7cb305;
}

/* Warning color */
.btn.btn-warning {
  color: #ff4d4f;
}
.btn.btn-warning:hover {
  background-color: #ff4d4f;
}
Copy the code

Then we can add new styles where we need them:

<! -- Apply warning color -->
<button class="btn btn-warning">
  <span class="btn-txt">Visit the website</span>
  <span class="btn-icon">
    <! -- Icon (if required) -->
  </span>
</button>
Copy the code

If we add one, two or more color themes later, just copy and paste the code above and change the color (advanced ones can be generated using SCSS mixins or loops).

Packaging components

We need to encapsulate it as a React component. Here’s some pseudo-code to demonstrate:

const colorTheme = {
  success: 'btn-success'.warning: 'btn-warning',}// For time reasons, the code is omitted from the whole non-air control judgment, please feel free to add; Please forwardRef consciously.
const Button = (props) = > {
  const { children, theme, className: customerClassName, ... restAsHTMLAttr } = propsconst themeClassName = colorTheme[theme] || ' '
  const classNames = ['btn', themeClassName, customerClassName].join(' ')

  return (
    <button className={classNames} {. restAsHTMLAttr} >
      <span className="btn-txt">{children}</span>
      <span className="btn-icon">{/* icon, if required */}</span>
    </button>)}Copy the code

We can then easily switch the buttons we want using the theme property:

<Button theme="success"> login < / Button ><Button theme="warning">The cancellation of account</Button>
Copy the code

Difficult to upgrade

Now we are required to support custom color values for our components, because we may use different colors for different projects, so we need to customize colors for the basic components and then encapsulate them into business components.

Because we can’t predict how many colors there will be in the future, we can’t configure themeColor by writing styles in the base component. God knows how many colors there will be in the future, so we need the user to pass in the color values directly through props.

Of course, you could do away with this requirement altogether and let the user create custom colors by adding custom class names to hack styles, but this is obviously lazy or evasive behavior. We are professional developers, so we can’t give up.

Accept color attributes

Now we get the custom color value from the color of props and apply it to the color style of the button:

const colorTheme = {
  success: 'btn-success'.warning: 'btn-warning',}const Button = (props) = > {
  const {
    children,
    theme,
    color,
    className: customerClassName,
    style: customerStyle, ... restAsHTMLAttr } = propsconst themeClassName = colorTheme[theme] || ' '
  const classNames = ['btn', themeClassName, customerClassName].join(' ')

  const colorStyle = {
    color, // Accept custom colors. customerStyle, }return (
    <button style={colorStyle} className={classNames} {. restAsHTMLAttr} >
      <span className="btn-txt">{children}</span>
      <span className="btn-icon">{/* icon, if required */}</span>
    </button>)}Copy the code

Support the hover

Currently, we change the color of the button by color, but we need to handle the hover state, but JS does not have the hover event. We can simulate it by two events, such as mouseover and mouseleave, and we need to change the color, we can use state here, the idea is as follows:

// This is all pseudo-code
const { color } = props
const normalStyle = { color }
const hoverStyle = {
  backgroundStyle: color,
}

const[colorStyle, setColorStyle] = useState({ ... normalStyle/ /... The style passed by the user is skipped here, please handle it by yourself
})
Copy the code

Next in the Mouseover event, change the style to the background color:

// Switch to the background color
onMouseOver = () = > {
  setColorStyle(hoverStyle)
  // Other code omitted
}
Copy the code

Finally switch to the font color in mouseleave:

// Switch back to font color
onMouseLeave = () = > {
  setColorStyle(normalStyle)
  // Other code omitted
}
Copy the code

Of course, if you don’t want to use state, you can just grab the DOM in the event callback and change the style by going to event.target.style.xxx = XXX, which is just as unfriendly.

Because writing so many events just to change the background color, the original pure U component becomes not pure, this is disgusting, I really hate to write a lot of code like this, I wish I could move to CSS.

Optimize with currentColor

Notice that? The background color above is just to synchronize the font color, so we’ll use currentColor to optimize and modify our CSS as follows:

.btn {
  color: rgb(78 133 254);
  height: 30px;
  line-height: 30px;
  padding: 0 8px;
  border-radius: 6px;
  border-style: solid;
  border-width: 1px;
  cursor: pointer;
}

.btn:hover {
  background-color: currentColor; /* Synchronize color */
}

.btn:active {
  opacity:.8;
}

.btn:hover > .btn-txt {
  color: white;
}
Copy the code

Then we only need to manipulate the color to synchronize the background color, so we need to delete the previous useless background operation code!

/* Success color */
.btn.btn-success {
  color: #7cb305;
}
/* delete all */
/* .btn.btn-success:hover { background-color: #7cb305; } * /

/* Warning color */
.btn.btn-warning {
  color: #ff4d4f;
}
/* delete all */
/* .btn.btn-warning:hover { background-color: #ff4d4f; } * /
Copy the code

Only two colors are left. We then modified our component to remove all the mouseover, mouseleave, and state stuff, leaving only the code to change color, and finally simplifying it to the following:

const colorTheme = {
  success: 'btn-success'.warning: 'btn-warning',}const Button = (props) = > {
  const {
    children,
    theme,
    color,
    className: customerClassName,
    style: customerStyle, ... restAsHTMLAttr } = propsconst themeClassName = colorTheme[theme] || ' '
  const classNames = ['btn', themeClassName, customerClassName].join(' ')
  const colorStyle = {
    color, // Accept custom colors. customerStyle, }return (
    <button style={colorStyle} className={classNames} {. restAsHTMLAttr} >
      <span className="btn-txt">{children}</span>
      <span className="btn-icon">{/* icon, if required */}</span>
    </button>)}Copy the code

tip

By virtue of the color inheritance feature, the scene card below has a theme color (yellow as shown below) and needs to change according to the illustration. Multiple title and Button colors can inherit the card, we just need to set the color on the card largest container, and then set the Button color to inherit the card color, so there is no need to pass the color layer down in js code or use context.

More scenes

Colors can also be used for box-shadow, and even for SVG’s fill attribute, which automatically synchronizes the SVG icon and font colors, saving us time to modify color values layer by layer through custom styles. Some of antD’s SVG ICONS also use currentColot: