Dynamically adjusting the system theme color (4): Exploration of CssVar and Variant solutions

Image

Dynamic adjustment of system theme color (4): Exploration of CssVar and Variant solutions

  • Dynamically adjusting the system theme color (4): Exploration of CssVar and Variant solutions
    • Preface
    • Introduction and comparison of plans
      • CssVar (CSS variable scheme)
      • Combination of CSS variable scheme and tailwindcss
      • Variant plan
    • Examples of 2 solutions on mini programs
    • Previous articles

Foreword

This is already the fourth article on dynamically adjusting the system theme color. In the blink of an eye, 2 years have passed since the first article was released. As time goes by, the understanding of technology is constantly improving, and solutions are constantly being improved and introduced. This article discusses the 2 solution for the system theme color: CssVar and Variant.

Introduction and comparison of plans

If you have not read the previous three articles, you may not intuitively know what they mean when you first see these 2 words. Let me briefly introduce what they are and their principles.

CssVar (CSS variable scheme)

This plan is very simple. The general idea is that we define in advance how many colors will be used throughout the entire design specification. Then define them as CSS variables, and then change the entire theme by changing the value of the CSS variable.

For example, we can define it like this:

:root{<!-- -->
    --prism-background: #f4f4f4;
}
/*dark*/
html.dark{<!-- -->
    --prism-background: #181818;
}

body{<!-- -->
    background-color: var(--prism-background);
}

This method defines other themes under the :root/html selector. Essentially, it uses the priority of the selector to overwrite the original definition of CSS variables. value to achieve the switching effect. This solution is very versatile. Many component libraries you see use this solution, such as element-plus / vant and so on (antd is not)

CSS variables will look for definitions recursively upward from where they are used, so if we want to overwrite variables defined in :root, we can directly add them to the variable defined in :root. Define a value where used to replace it to achieve the overwriting effect.

Therefore, we can encapsulate a component ThemeProvider, define a div element to wrap a slot, and then dynamically set the CSS variable in this element, thereby achieving the effect of switching local CSS variables in the slot. (Does it feel familiar?)

Combination of CSS variable scheme and tailwindcss

Of course, CSS variable scheme and tailwindcss can be combined very well. We only need to add theme< in tailwind.config.js in advance. Just expand and define variables in advance in /code>.

However, it is a little different from the above method. The CSS variable we define here is a string instead of a direct color. This is to enable a dynamic operation with transparency later. For example, we have multiple themes here, namely light (default), deep, dark, fantasy. We It can be defined like this:

 enum ModeEnum {<!-- -->
      light = 'light',
      deep = 'deep',
      dark = 'dark',
      fantasy = 'fantasy'
    }
    // To be completely dynamically configurable, you can save these values on the server and obtain them dynamically
    // Then write a page on the front end, use the color picker component to set the color, and save it in the database
    // This eliminates the need to hard-code variables on the front end, thereby achieving a more flexible configuration effect.
    const cssVarsMap: Record<ModeEnum, Record<string, string>> = {<!-- -->
      light: {<!-- -->
        '--ice-color-base': '191, 219, 254',
        '--ice-color-primary': '30, 58, 138',
        '--ice-color-primary-content': '255, 255, 255'
      },
      deep: {<!-- -->
        '--ice-color-base': '125, 211, 252',
        '--ice-color-primary': '79, 70, 229',
        '--ice-color-primary-content': '255, 255, 255'
      },
      dark: {<!-- -->
        '--ice-color-base': '2, 132, 199',
        '--ice-color-primary': '165, 180, 252',
        '--ice-color-primary-content': '0,0,0'
      },
      fantasy: {<!-- -->
        '--ice-color-base': '8, 47, 73',
        '--ice-color-primary': '224, 231, 255',
        '--ice-color-primary-content': '0,0,0'
      }
    }

Then dynamically set the values of these variables on html or pass them into ThemeProvider to achieve the purpose of switching.

A simple configuration is as follows:

/** @type {import('tailwindcss').Config} */
module.exports = {<!-- -->
  theme: {<!-- -->
    extend: {<!-- -->
      colors: {<!-- -->
        primary: 'rgba(var(--ice-color-primary), <alpha-value>)',
        'primary-content':
          'rgba(var(--ice-color-primary-content), <alpha-value>)',
        base: 'rgba(var(--ice-color-base), <alpha-value>)'
      }
    }
  },
}

At the same time, since the value we define is a string, we can use this to freely combine the rgba constructor to determine its transparency. That is, using the characteristics of CSS variable strings to combine the effect of rgba(r,g,b,a), this way the writing method is very free, and the same theme color , you can write text-primary directly using the primary color, or you can write text-primary/50 like this 50% The primary color of transparency. (This is the purpose of the placeholder)

In this way, when we define a component, we can use a class name to dynamically reference the variable:

<template>
  <button
    class="bg-primary text-primary-content">
    <slot></slot>
  </button>
</template>

The background color of this component is the main color, and the text is the main color of content.

Variant plan

I don’t know if you have noticed that in the dark mode switch that comes with tailwindcss, the writing method used is text-gray-800 dark:text-gray-200.

This kind of semantics is very intuitive. When you see it, you will know that this element color is text-gray-800 by default. In dark mode, coloris text-gray-200.

Similarly, its generation is also covered by the priority of Selector/Media Selector. The general principle is as follows:

border-white {<!-- -->
    --tw-border-opacity: 1;
    border-color: rgb(255 255 255 / var(--tw-border-opacity));
}

:is(.dark .dark:border-slate-800) {<!-- -->
    --tw-border-opacity: 1;
    border-color: rgb(30 41 59 / var(--tw-border-opacity));
}

Among them, the condition starting with dark: is called Variant. We can create many Variant according to our own needs to define and generate Different CSS blocks. Similarly, we can expand our theme based on this method.

For example, we still have multiple themes here, namely light (default), deep, dark, fantasy , we can define it like this:

const plugin = require('tailwindcss/plugin')

/** @type {import('tailwindcss').Config} */
module.exports = {<!-- -->
  //Dark Variant and .dark css generation blocks will be added by default
  darkMode: 'class',
  plugins: [
    plugin(function ({<!-- --> addVariant, addBase }) {<!-- -->
      addVariant('deep', ':is(.deep & amp;)')
      addVariant('fantasy', ':is(.fantasy & amp;)')
    }),
  ],
}

In this way, if we define a button component as above, we will write it like this:

 <button
    class="
    bg-pink-900
    deep:bg-pink-600
    dark:bg-pink-300
    fantasy:bg-pink-100
    text-white
    deep:text-gray-300
    dark:text-gray-600
    fantasy:text-gray-900">
    <slot></slot>
  </button>

This is obviously very verbose, even if it is still somewhat readable, but it is still flawless, and it is also error-prone to use @apply to extract it.

And there is a fatal problem. This solution requires adding a Variant for each additional theme, which is obviously very inflexible. So when the number of topics is uncertain, this solution is a disaster. The theme color is obviously not a suitable scenario for Variant.

This solution may only be suitable for the special scenario of dark mode. But the CssVar solution can also adapt to Dark Mode.

So just choose CssVar for the theme color scheme.

Examples of two solutions on mini programs

#小program://tailwind/RvdAJVby6DXgZpa

After copying it to WeChat and opening it, the applet code will not be placed here to avoid being judged by the Nuggets as a promotional article.

Or search the tailwind applet on WeChat to view the effect.

Previous articles

  1. Dynamically adjust the web system theme? Just read this article

  2. Dynamically adjust web themes (2) Extraction

  3. Dynamically adjust web themes (3): theme color generation scheme based on tailwindcss plug-in

syntaxbug.com © 2021 All Rights Reserved.