npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

change-theme-loader-utils

v0.0.1

Published

implementation for less-loader or sass-loader. Compiles Less or sass to CSS.

Downloads

2

Readme

change-theme-loader-utils

提供了

  • getLess,本质上不是针对 less-loader 的扩展,而是less 包的扩展
  • getSass,本质上不是针对 sass-loader 的扩展,而是sass 包的扩展

让你轻松实现基于lesssass的 web 应用在线动态主题切换。

动态主题模式预设主题模式

特点:

  • 使用成本很低
  • 不限 ui 框架,Element-ui、iview、Ant-design 等等等(只要基于 less/sass)
  • 不依赖 css3 vars
  • 浏览器兼容性良好(IE9+ ?)

demo repositories

动态主题模式

v1.4.0+支持

可用颜色板选择任意颜色切换相关的梯度颜色,这里以 scss 为例

one inline demo

one demo repository

效果图

在 webpack 中使用

# use npm or pnpm
npm install [email protected] change-theme-loader-utils @zougt/theme-css-extract-webpack-plugin -D
# use yarn
yarn add [email protected] change-theme-loader-utils @zougt/theme-css-extract-webpack-plugin -D

webpack.config.js

const path = require('path');

const { getSass } = require('change-theme-loader-utils');

const ThemeCssExtractWebpackPlugin = require('@zougt/theme-css-extract-webpack-plugin');

const multipleScopeVars = [
    {
        // 必需,任意名称
        scopeName: 'theme-vars',
        // path和varsContent选一个
        path: path.resolve('src/theme/theme-vars.scss'),
        // varsContent:`@--color-primary:#9c26b;`
    },
];

module.exports = {
    module: {
        rules: [
            {
                // 添加 setCustomTheme 的热更新loader
                test: /setCustomTheme\.js$/,
                enforce: 'pre',
                loader: require.resolve(
                    '@zougt/theme-css-extract-webpack-plugin/dist/hot-loader/index.js'
                ),
            },
            {
                test: /\.(scss|sass)$/i,
                // 请确保支持 implementation 属性的 sass-loader版本,webpack4 => sass-loader v10.x,webpack5 => sass-loader v12.x,请安装sass, 非 node-sass
                loader: 'sass-loader',
                options: {
                    implementation: getSass({
                        // getMultipleScopeVars优先于 sassOptions.multipleScopeVars
                        getMultipleScopeVars: (lessOptions) =>
                            multipleScopeVars,
                        // 可选项
                        // implementation:sass
                    }),
                },
            },
        ],
    },
    plugins: [
        new ThemeCssExtractWebpackPlugin({
            // 启用动态主题模式
            arbitraryMode: true,
            // 默认主题色,与"src/theme/theme-vars.scss"的@--color-primary主题色相同
            defaultPrimaryColor: '#512da7',
            multipleScopeVars,
            // 【注意】includeStyleWithColors作用: css中不是由主题色变量生成的颜色,也让它抽取到主题css内,可以提高权重
            includeStyleWithColors: [
                {
                    // color也可以是array,如 ["#ffffff","#000"]
                    color: '#ffffff',
                    // 排除属性,如 不提取背景色的#ffffff
                    // excludeCssProps:["background","background-color"]
                    // 排除选择器,如 不提取以下选择器的 #ffffff
                    // excludeSelectors: [
                    //   ".ant-btn-link:hover, .ant-btn-link:focus, .ant-btn-link:active",
                    // ],
                },
            ],
            // 是否在html默认添加主题的style标签
            InjectDefaultStyleTagToHtml: true,
            // setCustomTheme.js的一个依赖的生成路径,默认是 @zougt/theme-css-extract-webpack-plugin/dist/hot-loader/setCustomThemeContent.js
            customThemeOutputPath: '',
            // 调整色相值偏差,某些颜色值是由主题色通过mix等函数转化后,两者色相值不相等,无法确认是梯度颜色,可以调整low和high,允许偏差范围, 例如 hueDiffControls:{low: 2,high:2}
            // hueDiffControls: {
            //     low: 0,
            //     high: 0,
            // },
        }),
    ],
};

src/theme/theme-vars.scss

/*说明:此文件不应该被其他@import,此文件的变量并不是来设置项目的主题(当然,你可以作为加载时的默认主题),主要作用是,这里的变量值只要与项目的原变量值有差别,编译后就会抽取跟随主题色梯度变化的css*/

/*注意(重要):此文件的内容一旦固定下来就不需要改,在线动态切换主题,调用setCustomTheme方法即可*/

/*注意(强调):变量值改动会影响 gradientReplacer 和 targetValueReplacer 的可用属性的变化,所以内容一旦固定下来就不需要改(强调)*/

/*主题色,通常与  插件的 defaultPrimaryColor 相同, 使用setCustomTheme({primaryColor})切换*/

$--color-primary: #512da7;

/*与此颜色对应的样式,默认情况也会跟主色变化的,要切换它对应的梯度颜色,使用setCustomTheme({gradientReplacer:{"#F7D06B":"#F7D06B"}})切换 */
$--color-success: #f7d06b;

// /*圆角值,尽量与原值差别大一点,方便分析 targetValueReplacer 的可用属性,非颜色值的切换,可以使用 setCustomTheme({targetValueReplacer:{"6px"}}) 精准替换*/
// @border-radius-base:6px;

在线切换主题

动态主题切换必须使用的 "setCustomTheme" 模块,会自动处理项目中包括组件库涉及的梯度颜色替换

// color@4 使用了Numeric separators,如需良好兼容性应该安装 color@3
import Color from 'color';
// setCustomTheme的参数必须提供Color模块,至于为什么不把 Color 直接依赖进去是有原因的
import setCustomTheme from '@zougt/theme-css-extract-webpack-plugin/dist/setCustomTheme';
// 设置任意主题色既可
setCustomTheme({
    Color,
    primaryColor: '#FF005A',
    //gradientReplacer:{},
    //targetValueReplacer:{}
});

setCustomTheme 的可选参数 gradientReplacer 与 targetValueReplacer 的可用属性会跟随 .scss 内容变化的,所以整个项目动态主题的模型应该最开始固化下来

# npm run dev 之后
# 可以在终端使用 z-theme 命令查看  gradientReplacer 与 targetValueReplacer 的可用属性
npx z-theme inspect

预设主题模式

只预设多种可选主题,这里以less为例

one inline demo

one demo repository

效果图

在 webpack 中使用

# use npm or pnpm
npm install change-theme-loader-utils @zougt/theme-css-extract-webpack-plugin -D
# use yarn
yarn add change-theme-loader-utils @zougt/theme-css-extract-webpack-plugin -D

webpack.config.js

const path = require('path');
const webpack = require('webpack');

const { getLess } = require('change-theme-loader-utils');

const ThemeCssExtractWebpackPlugin = require('@zougt/theme-css-extract-webpack-plugin');

const multipleScopeVars = [
    {
        // 必需
        scopeName: 'theme-default',
        // path 和 varsContent 必选一个
        path: path.resolve('src/theme/theme-default.less'),
        // varsContent参数等效于 path文件的内容
        // varsContent:`@primary-color:${defaultPrimaryColor};`
    },

    {
        scopeName: 'theme-red',
        path: path.resolve('src/theme/theme-red.less'),
    },
];
const extract = process.env.NODE_ENV === 'production';
const publicPath = '/';
const assetsDir = 'assets';
const extractCssOutputDir = `${assetsDir}/css`;

module.exports = {
    output: {
        publicPath,
    },
    module: {
        rules: [
            {
                test: /\.less$/i,
                // webpack4 => less-loader v7.x , webpack5 => less-loader v10.x
                loader: 'less-loader',
                options: {
                    lessOptions: {
                        javascriptEnabled: true,
                    },
                    implementation: getLess({
                        // getMultipleScopeVars优先于 lessOptions.multipleScopeVars
                        getMultipleScopeVars: (lessOptions) =>
                            multipleScopeVars,
                        // 可选项
                        // implementation:less
                    }),
                },
            },
        ],
    },
    plugins: [
        // 添加参数到浏览器端
        new webpack.DefinePlugin({
            'env.themeConfig': {
                multipleScopeVars: JSON.stringify(multipleScopeVars),
                extract: JSON.stringify(extract),
                publicPath: JSON.stringify(publicPath),
                extractCssOutputDir: JSON.stringify(extractCssOutputDir),
            },
        }),

        new ThemeCssExtractWebpackPlugin({
            multipleScopeVars,
            // 【注意】includeStyleWithColors作用: css中不是由主题色变量生成的颜色,也让它抽取到主题css内,可以提高权重
            includeStyleWithColors: [
                {
                    // color也可以是array,如 ["#ffffff","#000"]
                    color: '#ffffff',
                    // 排除属性,如 不提取背景色的#ffffff
                    // excludeCssProps:["background","background-color"]
                    // 排除选择器,如 不提取以下选择器的 #ffffff
                    // excludeSelectors: [
                    //   ".ant-btn-link:hover, .ant-btn-link:focus, .ant-btn-link:active",
                    // ],
                },
                {
                    color: ['transparent', 'none'],
                },
            ],
            // 默认使用哪份主题,默认取 multipleScopeVars[0].scopeName
            defaultScopeName: '',
            // 在生产模式是否抽取独立的主题css文件,extract为true以下属性有效
            extract,
            // 独立主题css文件的输出路径
            outputDir: extractCssOutputDir,
            // 会选取defaultScopeName对应的主题css文件在html添加link
            themeLinkTagId: 'theme-link-tag',
            // 是否对抽取的css文件内对应scopeName的权重类名移除
            removeCssScopeName: false,
        }),
    ],
};

在线切换主题

预设主题切换,需要做的事情

1、开发时只需,html 标签的 calss 添加对应的 scopeName,移除上个 scopeName 2、打包后,如果开启 extract: true,需要切换对应的 link 标签的 href

可以选择使用如下封装好的方法

import { toggleTheme } from '@zougt/theme-css-extract-webpack-plugin/dist/toggleTheme';
// env.themeConfig 来源 (webpack.DefinePlugin)
const themeConfig = env.themeConfig;
toggleTheme({
    scopeName,
    multipleScopeVars: themeConfig.multipleScopeVars,
    extract: themeConfig.extract,
    publicPath: themeConfig.publicPath,
    outputDir: themeConfig.extractCssOutputDir,
    // customLinkHref: (href) => href,
    // themeLinkTagId: "theme-link-tag",
    // removeCssScopeName: false,
    // loading: {
    //   show: () => {},
    //   hide: () => {},
    // },
});

预设多主题编译原理示例(以 sass 为例)

主题包含的可能不只是颜色部分

//src/theme/default-vars.scss
/**
*此scss变量文件作为multipleScopeVars去编译时,会自动移除!default以达到变量提升
*同时此scss变量文件作为默认主题变量文件,被其他.scss通过 @import 时,必需 !default
*/
$primary-color: #0081ff !default;
$--border-radius-base: 4px !default;
//src/theme/mauve-vars.scss
$primary-color: #9c26b0 !default;
$--border-radius-base: 8px !default;
//src/components/Button/style.scss
@import '../../theme/default-vars';
.un-btn {
    position: relative;
    display: inline-block;
    font-weight: 400;
    white-space: nowrap;
    text-align: center;
    border: 1px solid transparent;
    background-color: $primary-color;
    border-radius: $--border-radius-base;
    .anticon {
        line-height: 1;
    }
}

编译之后

src/components/Button/style.css

.un-btn {
    position: relative;
    display: inline-block;
    font-weight: 400;
    white-space: nowrap;
    text-align: center;
    border: 1px solid transparent;
}
.theme-default .un-btn {
    background-color: #0081ff;
    border-radius: 4px;
}
.theme-mauve .un-btn {
    background-color: #9c26b0;
    border-radius: 8px;
}
.un-btn .anticon {
    line-height: 1;
}

html中改变 classname 切换主题,只作用于 html 标签 :

<!DOCTYPE html>
<html lang="zh" class="theme-default">
    <head>
        <meta charset="utf-8" />
        <title>title</title>
    </head>
    <body>
        <div id="app"></div>
        <!-- built files will be auto injected -->
    </body>
</html>
document.documentElement.className = 'theme-mauve';

使用 Css Modules

如果是模块化的 scss,得到的 css 类似:

.src-components-Button-style_theme-default-3CPvz
    .src-components-Button-style_un-btn-1n85E {
    background-color: #0081ff;
}
.src-components-Button-style_theme-mauve-3yajX
    .src-components-Button-style_un-btn-1n85E {
    background-color: #9c26b0;
}

实际需要的结果应该是这样:

.theme-default .src-components-Button-style_un-btn-1n85E {
    background-color: #0081ff;
}
.theme-mauve .src-components-Button-style_un-btn-1n85E {
    background-color: #9c26b0;
}

在 webpack.config.js 需要对css-loader (v4.0+) 的 modules 属性添加 getLocalIdent:

const path = require('path');
// const sass = require("sass");
const { getSass } = require('change-theme-loader-utils');
const { interpolateName } = require('loader-utils');
function normalizePath(file) {
    return path.sep === '\\' ? file.replace(/\\/g, '/') : file;
}
const multipleScopeVars = [
    {
        scopeName: 'theme-default',
        path: path.resolve('src/theme/default-vars.scss'),
    },
    {
        scopeName: 'theme-mauve',
        path: path.resolve('src/theme/mauve-vars.scss'),
    },
];
module.exports = {
    module: {
        rules: [
            {
                test: /\.module.scss$/i,
                use: [
                    {
                        loader: 'css-loader',
                        options: {
                            importLoaders: 1,
                            modules: {
                                localIdentName:
                                    process.env.NODE_ENV === 'production'
                                        ? '[hash:base64:5]'
                                        : '[path][name]_[local]-[hash:base64:5]',
                                //使用 getLocalIdent 自定义模块化名称 , css-loader v4.0+
                                getLocalIdent: (
                                    loaderContext,
                                    localIdentName,
                                    localName,
                                    options
                                ) => {
                                    if (
                                        multipleScopeVars.some(
                                            (item) =>
                                                item.scopeName === localName
                                        )
                                    ) {
                                        //localName 属于 multipleScopeVars 的不用模块化
                                        return localName;
                                    }
                                    const { context, hashPrefix } = options;
                                    const { resourcePath } = loaderContext;
                                    const request = normalizePath(
                                        path.relative(context, resourcePath)
                                    );
                                    // eslint-disable-next-line no-param-reassign
                                    options.content = `${
                                        hashPrefix + request
                                    }\x00${localName}`;
                                    const inname = interpolateName(
                                        loaderContext,
                                        localIdentName,
                                        options
                                    );

                                    return inname.replace(
                                        /\\?\[local\\?]/gi,
                                        localName
                                    );
                                },
                            },
                        },
                    },
                    {
                        loader: 'sass-loader',
                        options: {
                            implementation: getSass({
                                // getMultipleScopeVars优先于 sassOptions.multipleScopeVars
                                getMultipleScopeVars: (sassOptions) =>
                                    multipleScopeVars,
                                // 可选项
                                // implementation:sass
                            }),
                        },
                    },
                ],
            },
        ],
    },
};

以上是基于 webpack 的多主题的编译方案实现,如需 vite 版本的请看 vite 插件@zougt/vite-plugin-theme-preprocessor

multipleScopeVars

必需的

当 multipleScopeVars 只有一项时, scopeName 就没有意义,但是 path 可以起到 变量提升的作用

Type object[]

multipleScopeVars[].scopeName

Type string

multipleScopeVars[].path

必需的,变量文件的绝对路径

Type string || string[]

const multipleScopeVars = [
    {
        scopeName: 'theme-default',
        path: path.resolve('src/theme/default-vars.less'),
    },
    {
        scopeName: 'theme-mauve',
        path: path.resolve('src/theme/mauve-vars.less'),
    },
];

multipleScopeVars[].includeStyles

v1.3.0 支持 includeStyles,只在预设主题模式有效

Type: Object

当存在以下情况时,可以用这个属性处理

.theme-blue .el-button:focus,
.theme-blue .el-button:hover {
    /*这里的color值由 $primary-color 编译得来的,所以选择器前面加了 .theme-blue 提高了权重*/
    color: #0281ff;
    border-color: #b3d9ff;
    background-color: #e6f2ff;
}
.el-button--primary:focus,
.el-button--primary:hover {
    /*这里的color值不是由 变量 编译得来的,这时就会被上面那个 color 覆盖了, 实际上这里的color才是需要的效果*/
    color: #fff;
}
const includeStyles = {
    '.el-button--primary:hover, .el-button--primary:focus': {
        color: '#FFFFFF',
    },
};
const multipleScopeVars = [
    {
        scopeName: 'theme-default',
        path: path.resolve('src/theme/default-vars.less'),
        includeStyles,
    },
    {
        scopeName: 'theme-mauve',
        path: path.resolve('src/theme/mauve-vars.less'),
        includeStyles,
    },
];

得到

.theme-blue .el-button:focus,
.theme-blue .el-button:hover {
    /*这里的color值由 $primary-color 编译得来的,所以选择器前面加了 .theme-blue 提高了权重*/
    color: #0281ff;
    border-color: #b3d9ff;
    background-color: #e6f2ff;
}
.theme-blue .el-button--primary:focus,
.theme-blue .el-button--primary:hover {
    /*这里的color值不是由 变量 编译得来的,通过includeStyles也提高了权重得到实际的效果*/
    color: #ffffff;
}

出现权重问题效果图

includeStyles

使用了 includeStyles 的效果图

includeStyles