Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
September 18, 2021 05:50 am GMT

Using the PostCSS plugin Let your WebApp support dark mode

image

Recently my company needed to add multiple skin functions to multiple WebApps (about 20 +) . The default was white skin, so we started with dark mode to gradually achieve multiple skin functions. This article is a record of the implementation ideas.

Solution for skin-changing

css variables

css variables is the Web standard that implements support for dark patterns,

the following code queries through the CSS Media, the simplest implementation.

:root {    color-scheme: light dark;    background: white;    color: black;}@media (prefers-color-scheme: dark) {    :root {        background: black;        color: white;    }}

Use CSS variables if you have a lot of colors

:root {    color-scheme: light dark;    --nav-bg-color: #F7F7F7;    --content-bg-color: #FFFFFF;    --font-color: rgba(0,0,0,.9);}@media (prefers-color-scheme: dark) {    :root {        --nav-bg-color: #2F2F2F;        --content-bg-color: #2C2C2C;        --font-color: rgba(255, 255, 255, .8);    }}:root {    color: var(--font-color)}.header {    background-color: var(--nav-bg-color);}.content {    background-color: var(--content-bg-color);}

Advantages: the least amount of code, easy to implement;

The downside: Theres a Cross-browser, which is supported by Edge16 + ; older projects are implemented, and CSS needs to be refactored, so its not going to work for us, and if its a new WebApp, I wouldnt hesitate to use it.

Online compilation use less.js

The most typical example of this scenario is the https://antdtheme.com/ , via the less modifyVars method

Enables run-time changes to fewer variables. When called with a new value, fewer files are recompiled without reloading.

<script src="less.js"></script><script>less.modifyVars({ '@text-color': '#fff', '@bg-color': '#000' });</script>

If there are too many color variables to change, or too many style files, it will cause Cottonwood to switch.

Build multiple CSS

Of course, you can also manually build 2 CSS styles

var less = require("less");var fs = require("fs");fs.readFile("./index.less", "utf-8", (err, str) => {  less.render(    str,    {      paths: [".", "./components"], //  the search path for the@import directive      compress: true,       modifyVars: {        "@text-color": "#fff",        "@bg-color": "#000",      },    },    function (e, output) {      console.log(output.css);    }  );});

So you can skin it by dynamically inserting CSS

function changeTheme(theme) {    const styleCss = document.querySelector("#styleCss");    if (styleCss) {        styleCss.href = `/assets/css/${theme}.css`;    } else {        const head = document.getElementsByTagName("head")[0];        const link = document.createElement("link");        link.id = "styleCss";        link.type = "text/css";        link.rel = "stylesheet";        link.dataset.type = "theme";        link.href = `/assets/css/${theme}.css`;        head.appendChild(link);       }    localStorage.setItem("theme", theme);}

One problem with this approach is that it causes the entire page to reorder when you click to switch, so we need to separate out the color-only style files. From this point of view, we are exposed to PostCSS.

PostCSS

The PostCSS core contains a parser that generates a CSS AST (Abstract Syntax Tree), which is a representation of a node tree that parses strings of CSS. When we change something inside the CSS Abstract Syntax Tree, PostCSS will still represent it as a root node but stringify the syntax tree back into a CSS string.

The core process is ** Parse->Transform--> Generate ** Is it like Babel ?

Everyone knows that https://astexplorer.net/ is a site that can be used to write Babel plugins, but have you used any other parsers? Select CSS and PostCSS here so you can parse your CSS into your CSS AST (abstract syntax tree) .

image astexplorer

Purpose

less file image

Currently I have one less style and two color variables, I need to generate the following style:

output image

So I can add and remove the dark class in the HTML root node to do this.

Some of you may be asking, Suddenly, why is it Less? Can PostCSS parse Less? The answer is no.

At the moment, I'am sure your webapp is based on Webpack.

module: {    rules:[        //...        {           test: /\.less$/i,           use: ['style-loader', 'css-loader', 'postcss-loader', 'less-loader'],        },        //...    ]}

Webpacks loader's order of execution is from right to left, Less pass by less-loaderIt becomes CSS

Start writing a PostCSS plugin

We can use postcss-plugin-boilerplate , This scaffolding to create a postcss-plugin It also has jest unit tests configuredYou can create one postcss-plugin project with a few simple commands

image

Of course we can just create a JS file in the project root directory

// test-plugin.jsvar postcss = require("postcss");module.exports = postcss.plugin("pluginname", function (opts) {  opts = opts || {}; // plugin   return function (root, result) {    // Transform the CSS AST  };});

And then, after that, Just bring it in postcss.config.js

module.exports = {  plugins: [    require('./test-plugin'),    require('autoprefixer')  ]};

PostCSS plugin Hello world

Write a plugin that inverts CSS property values

var postcss = require("postcss");module.exports = postcss.plugin("postcss-backwards", function (opts) {  opts = opts || {};  return function (root, result) {    // Iterate over all style nodes    root.walkDecls((declaration) => {      declaration.value = declaration.value.split("").reverse().join("");    });  };});

Of course this plugin, doesnt make any sense, we just use it to learn how to write PostCSS plugin

postcss-multiple-themes

Usage

JS entry file import 2 style files

import "./default-theme.less";import "./dark-theme.less";

component.less

.box{  width: 100px;  height: 100px;  border: 1px solid @border;  background-color: @bg;  color: @color;}

default-theme.less

@import "./component";@border: #333;@color: #000;@bg: #fff;

dark-theme.less

@import "./component";@border: #999;@color: #fff;@bg: #000;

Output css

.box {  width: 100px;  height: 100px;  border: 1px solid #333;  background-color: #fff;  color: #000;}.dark .box {  border: 1px solid #999;  background-color: #000;  color: #fff;}

Source Code

function isEmpty(arr) {  return Array.isArray(arr) && arr.length === 0;}const hasColorProp = (colorProps, declProp) =>  colorProps.some((prop) => declProp.includes(prop));module.exports = (opts = {}) => {  if (!opts.colorProps) {    opts.colorProps = ["color", "background", "border", "box-shadow", "stroke"];  }  return (root) => {    let theme;    const file = root.source.input.file || "";    const matched = file.match(      /(?<theme>[a-zA-Z0-9]+)-theme.(less|css|scss|sass)/    );    if (matched && matched.groups.theme !== "default") {      theme = matched.groups.theme;    } else {      if (process.env.NODE_ENV == "test") {        theme = "test";      }    }    if (theme) {      root.walkRules((rule) => {        rule.walkDecls((decl) => {          if (!hasColorProp(opts.colorProps, decl.prop)) {            decl.remove();          }        });        if (isEmpty(rule.nodes)) {          rule.remove();        } else {          rule.selector = rule.selector            .replace(/
/g, "") .split(",") .map((s) => `.${theme} ${s}`) .join(",
"); } }); } };};

Implementation steps

1Use the file name to determine if a skin style needs to be generated

const file = root.source.input.file || "";const matched = file.match(  /(?<theme>[a-zA-Z0-9]+)-theme.(less|css|scss|sass)/);

2Remove styles that do not contain colors, and leave border-color background-color and the CSS properties that contains colors

["color", "background","border","box-shadow","stroke",]

3If there are no CSS properties in the CSS selector, delete the selector

4In front of the CSS selector .theme class name

Upgrade of old project

The original project may not have color-sensitive variables in a separate style file, and the absolute value of color may be written in the style.

Is it possible to write a tool to help us upgrade

At this time, I have a library that helps mepostcss-less will help us parse the less to AST Then we can configure the rules to replace the ** color with the variable **

configure the rules

module.exports = [  {    prop: ["background-color", "background"],    from: ["#fff", "#ffffff", "@white"],    to: "@component-background",  },  {    prop: ["border", "border-color"],    from: ["#D3D9E4", "#D3D9E2"],    to: "@border-color",  },  {    prop: ["color"],    from: ["#666E79", "#5C6268"],    to: "@text-color",  }];

Transform

const syntax = require("postcss-less");var fs = require("fs");const path = require("path");const rules = require("./rule.js");var glob = require("glob");function log(file, node, to) {  console.log(    "\x1b[32m",    `convert ${file} ${node.source.start.line}:${node.source.start.column}  ${node.parent.selector} ${node.prop} from ${node.value} to ${to}`  );}let codes = {};// options is optionalglob("./src/**/*.less", function (er, files) {    files.forEach((file) => {    var ast = syntax.parse(file);    // traverse AST and modify it    ast.walkDecls(function (node) {        rules.forEach((item) => {          if (item.prop.includes(node.prop) && item.from.includes(node.value)) {              node.value = item.to;              log(file, node, item.to);          }        });    });    fs.writeFileSync(path.resolve(file), syntax.nodeToString(ast));  });});

Main steps

1Read all the less files with glob

2Use postcss-less Convert less to AST

3Iterating over all CSS properties, the decision is replaced with the less variable in the rule

4Convert to less write file

The above code is the simplest, and there are many styles that are not covered

For example border You can write border-color and so on.

Use VSCODE regular query missing color

When the above rules cannot cover all the project code, the developer can enter the rules in VSCODE.

(#[a-fA-F0-9]{3})|(#[a-fA-F0-9]{6})|^rgb

Find out the colors in the code and extract them into less variables one by one.

image

Summary

  1. This article summarizes some common ways of front-end multiple skins. Through the most comparison, I find that it is the most convenient to generate skin style through PostCSS in our project, and it is also the easiest to make your website support dark mode. I opened up the plugin postcss-multiple-themes to Github and released the npm package.

  2. Thinking about how to replace the css color in the old project with variables through PostCSS, when there are more projects, the labor cost is saved to a certain extent.

Last

If you are also doing change skin work for WebApp and are plagued by the problem of multi-skin in the front-end, I hope this article will be helpful to you. You can also exchange your ideas and experiences in the comments area. Welcome to explore the front-end.


Original Link: https://dev.to/maqi1520/using-the-postcss-plugin-let-your-webapp-support-dark-mode-1nnp

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To