本篇文章为Waxy主题设置中自定义CSS/JS的高级应用。

什么是Canvas?

Canvas 是 HTML5 中一个新特性,开发者绘制一系列图形。通过和JavaScript配合,可以使静态的图形动起来~

当然更加具体的内容,还请您自己学习,这里不做展开~

后面飞扬的花瓣就是基于Canvas技术实现的

如何自定义Canvas背景?

目前Waxy主题支持3种方式自定义Canvas背景,在文章后面,我提供了几个整理好的背景。

1.文件引入(推荐)

首先,您需要将要引入的背景文件,放入网站的目录下,并且保证可以读取。

这里我放在主题的js目录下

然后进入主题设置,自定义JS,中填入下面的代码:

1<script type="text/javascript" src="【此处为该js文件相对与网站的路径地址】"></script>

注意替换src的路径

2.代码追加

打开主题下的/js/waxy-main.js文件。

在文件末尾换行一次或多次,将背景文件内所有内容复制进文件最后。

注意复制全了

3.全量设置

然后进入主题设置,自定义JS,中填入下面的代码:

1<script type="text/javascript">此处为背景文件内所有的内容</script>

注意,一定不要丢 ‘<’ 和 ‘>’

附件

这些背景均搜集自网络,仅修改不兼容Waxy的部分,其他代码均未修改。保留原作者注释及声明

Canvas背景特效 浮动彩球
``` js /* canvas背景特效 浮动彩球 */ (function () { // 1. 配置参数(多套配色,点击带 data-config-nr 属性的元素可切换) // 注:透明背景版已移除原来的渐变背景(colorStart/colorStop 保留但不再绘制), // 只保留彩球,便于叠加在任意主题之上。 const configs = [ { // 0:暖白(默认) colorStart: "rgba(255,244,230,1)", colorStop: "rgba(255,233,228,1)", blur: 1, compose: "source-over", bubbleFunc: () => `hsla(${Math.random() * 50}, 100%, 50%, .3)` }, { // 1:暗红 colorStart: "#111", colorStop: "#422", blur: 4, compose: "lighter", bubbleFunc: () => `hsla(0, 100%, 50%, ${Math.random() * 0.25})` }, { // 2:紫色霓虹 colorStart: "#4c004c", colorStop: "#1a001a", blur: 4, compose: "lighter", bubbleFunc: () => `hsla(${Math.random() * 360}, 100%, 50%, ${Math.random() * 0.25})` }, { // 3:暖橙 colorStart: "#fff4e6", colorStop: "#ffe9e4", blur: 1, compose: "source-over", bubbleFunc: () => `hsla(${Math.random() * 50}, 100%, 50%, .3)` } ];
let canvas, ctx, width, height, balls, currentOpts;

const random = () => Math.random();

// 3. 生成小球数据
function makeBalls(opts) {
    const count = Math.floor(0.02 * (width + height)); // 数量随屏幕尺寸自适应
    const list = [];
    for (let i = 0; i < count; i++) {
        list.push({
            color: opts.bubbleFunc(),          // 颜色
            x: random() * width,               // 初始 x
            y: random() * height,              // 初始 y
            r: 4 + random() * width / 25,      // 半径
            angle: random() * Math.PI * 2,     // 运动角度
            speed: 0.1 + 0.5 * random()        // 速度
        });
    }
    return list;
}

// 4. 初始化画布(透明沉底,直接挂 body,不拦截交互)
function setupCanvas() {
    canvas = document.createElement('canvas');
    ctx = canvas.getContext('2d');
    canvas.style.cssText =
        'display:block;position:fixed;top:0;left:0;width:100%;height:100%;z-index:-1;background:transparent;pointer-events:none;';
    width = canvas.width = window.innerWidth;
    height = canvas.height = window.innerHeight;

    document.body.appendChild(canvas);
}

// 5. 应用一套配置(首次启动 & 点击切换共用)
function applyConfig(opts) {
    currentOpts = opts;
    ctx.shadowColor = opts.shadowColor || "#fff";
    ctx.shadowBlur = opts.blur || 4;
    balls = makeBalls(opts);
}

// 6. 动画主循环
function renderLoop() {
    // 透明清屏(不再铺渐变背景,让网站主题透出)
    ctx.clearRect(0, 0, width, height);

    // 普通叠加绘制小球(透明背景下 source-over 比 lighter 更自然)
    ctx.globalCompositeOperation = "source-over";
    balls.forEach(b => {
        ctx.beginPath();
        ctx.arc(b.x, b.y, b.r, 0, 2 * Math.PI);
        ctx.fillStyle = b.color;
        ctx.fill();

        // 移动
        b.x += Math.cos(b.angle) * b.speed;
        b.y += Math.sin(b.angle) * b.speed;

        // 边界环绕(飞出一侧从另一侧回来)
        if (b.x - b.r > width) b.x = -b.r;
        if (b.x + b.r < 0) b.x = width + b.r;
        if (b.y - b.r > height) b.y = -b.r;
        if (b.y + b.r < 0) b.y = height + b.r;
    });

    requestAnimationFrame(renderLoop);
}

// 7. 窗口尺寸变化(重设阴影,因为改 canvas 尺寸会重置上下文状态)
window.addEventListener('resize', () => {
    width = canvas.width = window.innerWidth;
    height = canvas.height = window.innerHeight;
    ctx.shadowColor = currentOpts.shadowColor || "#fff";
    ctx.shadowBlur = currentOpts.blur || 4;
});

// 8. 点击切换配色(保留原功能)
document.addEventListener('click', function (e) {
    if (e.target.hasAttribute('data-config-nr')) {
        const nr = parseInt(e.target.getAttribute('data-config-nr'), 10) || 0;
        applyConfig(configs[nr] || configs[0]);
    }
});

// 9. 自动启动
setupCanvas();
applyConfig(configs[0]);
renderLoop();

})();

</div></div></div>
</div>


<div class="shrink-box shrink-box--success">
  <div class="shrink-box__title" data-action="shrink-toggle">Canvas背景特效 彩色气泡</div>
  <div class="shrink-box__body"><div class="shrink-box__body-inner"><div class="shrink-box__body-content">
``` js
/* canvas背景特效 彩色气泡 */
(function () {
    // 1. 配置参数(完全私有,外部无法修改)
    const opts = {
        num: 100,               // 气泡最大数量
        start_probability: 0.1, // 每帧添加新气泡的概率
        radius_min: 1,          // 最小半径
        radius_max: 5,          // 最大半径
        radius_add_min: .1,     // 半径膨胀速度最小值
        radius_add_max: .3,     // 半径膨胀速度最大值
        opacity_min: 0.3,       // 初始透明度最小值
        opacity_max: 0.6,       // 初始透明度最大值
        opacity_prev_min: .003, // 透明度衰减速度最小值
        opacity_prev_max: .005, // 透明度衰减速度最大值
        light_min: 40,          // HSL亮度最小值
        light_max: 70,          // HSL亮度最大值
        is_same_color: false    // 是否所有气泡色调一致
    };

    let globalColor = Math.random() * 360;
    const bubbleList = []; // 存放气泡渲染闭包的数组
    let canvas, ctx;

    const random = (a, b) => Math.random() * (b - a) + a;

    // 3. 气泡工厂(核心闭包:为每个气泡锁定独立的状态变量)
    function makeBubble() {
        let x, y, radius, radiusChange, opacity, opacityChange, color;

        function init() {
            const light = random(opts.light_min, opts.light_max);
            x = random(0, canvas.width);
            y = random(0, canvas.height);
            radius = random(opts.radius_min, opts.radius_max);
            radiusChange = random(opts.radius_add_min, opts.radius_add_max);
            opacity = random(opts.opacity_min, opts.opacity_max);
            opacityChange = random(opts.opacity_prev_min, opts.opacity_prev_max);
            color = `hsl(${opts.is_same_color ? globalColor : random(0, 360)}, 100%, ${light}%)`;
        }

        init(); // 首次调用初始化

        // 返回的函数闭包了上方所有的局部变量
        return function updateAndDraw() {
            ctx.fillStyle = color;
            ctx.globalAlpha = opacity;
            ctx.beginPath();
            ctx.arc(x, y, radius, 0, 2 * Math.PI, true);
            ctx.closePath();
            ctx.fill();

            // 状态演变
            opacity -= opacityChange;
            radius += radiusChange;

            // 生命周期结束,原位重置(变量常驻内存,无需重新 new)
            if (opacity <= 0) {
                init();
            }
        };
    }

    // 4. 初始化画布(透明沉底,直接挂 body,不拦截交互)
    function setupCanvas() {
        canvas = document.createElement('canvas');
        ctx = canvas.getContext('2d');
        canvas.style.cssText =
            'position:fixed;top:0;left:0;width:100%;height:100%;z-index:-1;background:transparent;pointer-events:none;';
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
        document.body.appendChild(canvas);

        window.onresize = () => {
            canvas.width = window.innerWidth;
            canvas.height = window.innerHeight;
        };
    }

    // 5. 动画主循环闭包
    function renderLoop() {
        globalColor += 0.1;

        // 擦除上一帧(透明清屏,不铺背景色)
        ctx.globalAlpha = 1;
        ctx.clearRect(0, 0, canvas.width, canvas.height);

        // 渐进式生成新气泡闭包
        if (bubbleList.length < opts.num && Math.random() < opts.start_probability) {
            bubbleList.push(makeBubble());
        }

        // 执行每个气泡的闭包渲染逻辑
        bubbleList.forEach(drawBubble => drawBubble());

        window.requestAnimationFrame(renderLoop);
    }

    // 6. 自动启动
    setupCanvas();
    renderLoop();

})();
Canvas背景特效 动态彩带
``` js /* canvas背景特效 动态彩带 */ (function () { // 1. 配置参数(完全私有,外部无法修改) const opts = { colorSaturation: "100%", // 颜色饱和度 colorBrightness: "60%", // 颜色亮度 colorAlpha: 0.80, // 整体透明度 colorCycleSpeed: 8, // 色相循环速度 verticalPosition: "center", // 起始垂直位置 top/center/bottom horizontalSpeed: 200, // 水平展开速度 ribbonCount: 5, // 同屏彩带数量 strokeSize: 0, // 描边宽度(0 为不描边) parallaxAmount: -0.0, // 滚动视差系数 animateSections: true // 是否让分段轻微浮动 };
// 3. 工具函数:random(max) / random(min,max) / random(array)
const random = function () {
    if (arguments.length === 1) {
        if (Array.isArray(arguments[0])) {
            return arguments[0][Math.round(random(0, arguments[0].length - 1))];
        }
        return random(0, arguments[0]);
    } else if (arguments.length === 2) {
        return Math.random() * (arguments[1] - arguments[0]) + arguments[0];
    }
    return 0;
};

// 屏幕尺寸与滚动信息
const screenInfo = function () {
    const w = window, d = document.documentElement, b = document.body;
    const width = Math.max(0, w.innerWidth || d.clientWidth || b.clientWidth || 0);
    const height = Math.max(0, w.innerHeight || d.clientHeight || b.clientHeight || 0);
    const scrolly = Math.max(0, w.pageYOffset || d.scrollTop || b.scrollTop || 0) - (d.clientTop || 0);
    return { width, height, scrolly };
};

// 4. 二维点(仅保留彩带用得到的方法)
function Point(x, y) {
    this.x = x || 0;
    this.y = y || 0;
}
Point.prototype.copy = function (p) { this.x = p.x || 0; this.y = p.y || 0; return this; };
Point.prototype.add = function (x, y) { this.x += x || 0; this.y += y || 0; return this; };
Point.prototype.subtract = function (x, y) { this.x -= x || 0; this.y -= y || 0; return this; };

let canvas, ctx, width = 0, height = 0, scroll = 0;
const ribbons = []; // 每条彩带 = 若干三角形分段组成的数组

// 5. 初始化画布
function setupCanvas() {
    canvas = document.createElement('canvas');
    canvas.id = 'bg_canvas';
    canvas.style.cssText = 'display:block;position:fixed;margin:0;padding:0;border:0;left:0;top:0;width:100%;height:100%;z-index:-1;background:transparent;pointer-events:none;';
    onResize();
    ctx = canvas.getContext('2d');
    ctx.clearRect(0, 0, width, height);
    ctx.globalAlpha = opts.colorAlpha;

    window.addEventListener('resize', onResize);
    window.addEventListener('scroll', onScroll);

    document.body.appendChild(canvas);
}

function onResize() {
    const s = screenInfo();
    width = s.width;
    height = s.height;
    if (canvas) {
        canvas.width = width;
        canvas.height = height;
        if (ctx) ctx.globalAlpha = opts.colorAlpha;
    }
}

function onScroll() {
    scroll = screenInfo().scrolly;
}

// 6. 新增一条彩带
function addRibbon() {
    const dir = Math.round(random(1, 9)) > 5 ? "right" : "left"; // 展开方向
    const hide = 200;                                            // 屏幕外预留
    const min = 0 - hide, max = width + hide;
    let stop = 1000, movex = 0, movey = 0, delay = 0;
    const startx = dir === "right" ? min : max;
    let starty = Math.round(random(0, height));

    // 起始垂直位置
    if (/^(top|min)$/i.test(opts.verticalPosition)) starty = 0 + hide;
    else if (/^(middle|center)$/i.test(opts.verticalPosition)) starty = height / 2;
    else if (/^(bottom|max)$/i.test(opts.verticalPosition)) starty = height - hide;

    const ribbon = [];
    const point1 = new Point(startx, starty);
    const point2 = new Point(startx, starty);
    let point3 = null;
    let color = Math.round(random(0, 360));

    // 逐段生成,直到走出屏幕或达到上限
    while (true) {
        if (stop <= 0) break;
        stop--;
        movex = Math.round((Math.random() * 1 - 0.2) * opts.horizontalSpeed);
        movey = Math.round((Math.random() * 1 - 0.5) * (height * 0.25));
        point3 = new Point().copy(point2);
        if (dir === "right") {
            point3.add(movex, movey);
            if (point2.x >= max) break;
        } else {
            point3.subtract(movex, movey);
            if (point2.x <= min) break;
        }
        ribbon.push({
            point1: new Point(point1.x, point1.y),
            point2: new Point(point2.x, point2.y),
            point3: point3,
            color: color, delay: delay, dir: dir, alpha: 0, phase: 0
        });
        point1.copy(point2);
        point2.copy(point3);
        delay += 4;
        color += opts.colorCycleSpeed;
    }
    ribbons.push(ribbon);
}

// 7. 绘制单个分段,返回 true 表示该分段生命周期结束
function drawSection(section) {
    if (!section) return false;
    if (section.phase >= 1 && section.alpha <= 0) return true;

    if (section.delay <= 0) {
        // 用 sin 曲线做淡入淡出
        section.phase += 0.02;
        section.alpha = Math.sin(section.phase);
        section.alpha = section.alpha <= 0 ? 0 : (section.alpha >= 1 ? 1 : section.alpha);

        // 分段轻微浮动
        if (opts.animateSections) {
            const mod = Math.sin(1 + section.phase * Math.PI / 2) * 0.1;
            if (section.dir === "right") {
                section.point1.add(mod, 0); section.point2.add(mod, 0); section.point3.add(mod, 0);
            } else {
                section.point1.subtract(mod, 0); section.point2.subtract(mod, 0); section.point3.subtract(mod, 0);
            }
            section.point1.add(0, mod); section.point2.add(0, mod); section.point3.add(0, mod);
        }
    } else {
        section.delay -= 0.5; // 延迟出现
    }

    const c = `hsla(${section.color}, ${opts.colorSaturation}, ${opts.colorBrightness}, ${section.alpha})`;
    ctx.save();
    if (opts.parallaxAmount !== 0) ctx.translate(0, scroll * opts.parallaxAmount);
    ctx.beginPath();
    ctx.moveTo(section.point1.x, section.point1.y);
    ctx.lineTo(section.point2.x, section.point2.y);
    ctx.lineTo(section.point3.x, section.point3.y);
    ctx.fillStyle = c;
    ctx.fill();
    if (opts.strokeSize > 0) {
        ctx.lineWidth = opts.strokeSize;
        ctx.strokeStyle = c;
        ctx.lineCap = "round";
        ctx.stroke();
    }
    ctx.restore();
    return false;
}

// 8. 动画主循环
function renderLoop() {
    ctx.clearRect(0, 0, width, height);

    // 绘制所有彩带,全部分段结束的整条置空
    for (let a = 0; a < ribbons.length; a++) {
        const ribbon = ribbons[a];
        if (!ribbon) continue;
        let done = 0;
        for (let b = 0; b < ribbon.length; b++) {
            if (drawSection(ribbon[b])) done++;
        }
        if (done >= ribbon.length) ribbons[a] = null;
    }

    // 清理已结束的彩带
    for (let i = ribbons.length - 1; i >= 0; i--) {
        if (!ribbons[i]) ribbons.splice(i, 1);
    }

    // 数量不足则补充
    if (ribbons.length < opts.ribbonCount) addRibbon();

    requestAnimationFrame(renderLoop);
}

// 9. 自动启动
setupCanvas();
renderLoop();

})();

</div></div></div>
</div>


<div class="shrink-box shrink-box--success">
  <div class="shrink-box__title" data-action="shrink-toggle">Canvas背景特效 粒子网络</div>
  <div class="shrink-box__body"><div class="shrink-box__body-inner"><div class="shrink-box__body-content">
``` js
/* canvas背景特效 粒子网络 */
(function () {
    // 1. 配置参数(原版从 script 标签属性读取,这里改为内联,完全私有)
    const opts = {
        zIndex: -1,             // canvas 层级
        opacity: 1,             // 整体透明度
        color: "190,189,188",   // 点/连线颜色 RGB
        count: 150,             // 粒子数量
        dist: 6000,             // 粒子间连线的距离阈值(距离平方)
        mouseDist: 20000        // 鼠标吸附/连线的距离阈值(距离平方)
    };

    let canvas, ctx, width, height;
    let particles = [];
    const mouse = { x: null, y: null, max: opts.mouseDist }; // 鼠标作为一个特殊点
    let allPoints;

    const random = Math.random;

    // 3. 初始化画布(透明沉底,直接挂 body,不拦截交互)
    function setupCanvas() {
        canvas = document.createElement('canvas');
        ctx = canvas.getContext('2d');
        canvas.style.cssText =
            `position:fixed;top:0;left:0;width:100%;height:100%;z-index:${opts.zIndex};opacity:${opts.opacity};background:transparent;pointer-events:none;`;
        setSize();

        document.body.appendChild(canvas);

        window.addEventListener('resize', setSize);
    }

    function setSize() {
        width = canvas.width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
        height = canvas.height = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
    }

    // 4. 生成粒子
    function makeParticles() {
        particles = [];
        for (let i = 0; i < opts.count; i++) {
            particles.push({
                x: random() * width,
                y: random() * height,
                xa: 2 * random() - 1,   // x 方向速度
                ya: 2 * random() - 1,   // y 方向速度
                max: opts.dist
            });
        }
        // 把鼠标点拼到末尾,参与连线计算
        allPoints = particles.concat([mouse]);
    }

    // 5. 鼠标交互
    function bindMouse() {
        window.onmousemove = e => {
            e = e || window.event;
            mouse.x = e.clientX;
            mouse.y = e.clientY;
        };
        window.onmouseout = () => {
            mouse.x = null;
            mouse.y = null;
        };
    }

    // 6. 动画主循环
    function renderLoop() {
        ctx.clearRect(0, 0, width, height);
        let target, dx, dy, dist, d;

        particles.forEach((p, idx) => {
            // 移动
            p.x += p.xa;
            p.y += p.ya;
            // 碰到边界反向反弹
            p.xa *= (p.x > width || p.x < 0) ? -1 : 1;
            p.ya *= (p.y > height || p.y < 0) ? -1 : 1;
            // 绘制一个 1px 的点
            ctx.fillRect(p.x - 0.5, p.y - 0.5, 1, 1);

            // 与后续点(含鼠标)连线
            for (let i = idx + 1; i < allPoints.length; i++) {
                target = allPoints[i];
                if (target.x === null || target.y === null) continue;
                dx = p.x - target.x;
                dy = p.y - target.y;
                dist = dx * dx + dy * dy;
                if (dist < target.max) {
                    // 靠近鼠标时被轻微吸引
                    if (target === mouse && dist >= target.max / 2) {
                        p.x -= 0.03 * dx;
                        p.y -= 0.03 * dy;
                    }
                    d = (target.max - dist) / target.max; // 越近越不透明
                    ctx.beginPath();
                    ctx.lineWidth = d / 2;
                    ctx.strokeStyle = `rgba(${opts.color},${d + 0.2})`;
                    ctx.moveTo(p.x, p.y);
                    ctx.lineTo(target.x, target.y);
                    ctx.stroke();
                }
            }
        });

        requestAnimationFrame(renderLoop);
    }

    // 7. 自动启动
    setupCanvas();
    makeParticles();
    bindMouse();
    setTimeout(renderLoop, 100);
})();
Canvas动画背景 漂浮气泡
``` js // Canvas 动画背景 漂浮气泡 (function () { // 创建背景专用 Canvas const bgCanvas = document.createElement('canvas'); const ctx = bgCanvas.getContext('2d'); document.body.appendChild(bgCanvas);

// background: transparent 确保完全透明,完美适应任何网页主题 bgCanvas.style.cssText = ‘position:fixed;top:0;left:0;z-index:-1;width:100%;height:100%;background:transparent;pointer-events:none;’;

function resize() { bgCanvas.width = window.innerWidth; bgCanvas.height = window.innerHeight; } window.addEventListener(‘resize’, resize); resize();

let bubbles = []; const BUBBLE_COUNT = 45; // 气泡数量,可根据需要增减

class Bubble { constructor() { this.init(true); // 首次初始化,随机散落在屏幕各处 }

init(isFirstTime = false) {
  this.radius = Math.random() * 35 + 15; // 气泡半径(15px 到 50px)
  this.x = Math.random() * bgCanvas.width;

  // 首次随机分布全屏,后续则严格从底部(屏幕高度 + 半径)升起
  this.y = isFirstTime ? Math.random() * bgCanvas.height : bgCanvas.height + this.radius;

  this.vy = -(Math.random() * 0.7 + 0.3); // 上升速度
  this.wobbleSpeed = Math.random() * 0.02; // 左右摇摆频率
  this.wobble = Math.random() * Math.PI; // 初始摇摆弧度

  // 七彩随机色:HSL 色相(0~360) 完美覆盖彩虹色系
  // 饱和度 85%(色彩鲜艳)、亮度 65%(柔和不刺眼)、透明度 0.25(半透明,可完美叠在任何主题上)
  this.color = `hsla(${Math.floor(Math.random() * 360)}, 85%, 65%, 0.25)`;
}

update() {
  this.y += this.vy;
  this.wobble += this.wobbleSpeed;
  this.x += Math.sin(this.wobble) * 0.4; // 模拟水流左右晃动

  // 飘出顶部后,重置回底部重新生成新气泡
  if (this.y < -this.radius) {
    this.init(false);
  }
}

draw() {
  ctx.beginPath();
  ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
  ctx.fillStyle = this.color;
  ctx.fill();
}

}

// 初始化气泡 for (let i = 0; i < BUBBLE_COUNT; i++) { bubbles.push(new Bubble()); }

// 统一渲染循环 function animate() { // 每一帧彻底清空画布,保持透明度 ctx.clearRect(0, 0, bgCanvas.width, bgCanvas.height);

bubbles.forEach(b => {
  b.update();
  b.draw();
});

requestAnimationFrame(animate);

}

animate(); })();

</div></div></div>
</div>



<div class="shrink-box shrink-box--success">
  <div class="shrink-box__title" data-action="shrink-toggle">毛玻璃效果</div>
  <div class="shrink-box__body"><div class="shrink-box__body-inner"><div class="shrink-box__body-content">
``` js
//毛玻璃效果
(function () {
  // ── 可调参数 ──────────────────────────
  const BLUR      = 12;    // 模糊半径 px,越大越"磨砂"
  const SATURATE  = 150;   // 提色 %,玻璃后面颜色更鲜活
  const ALPHA_L   = 0.25;  // 浅色模式背景不透明度(越小越透)
  const ALPHA_D   = 0.55;  // 暗色模式背景不透明度
  // ─────────────────────────────────────

  // 需要毛玻璃的容器(与原主题真实类名一致)
  const targets = [
    '.site-header', '.nav__sub',
    '.post', '.sidebar .widget',
    '.widget--toc', '.widget--toc.is-fixed', '.waxy-toc-float-panel',
    '.blog-stats__item', '.post-stats__item',
    '.about-author', '.empty-state',
    '.post-nav__item', '.breadcrumb',
    '.waxy-drawer__nav', '.page-404__card'
  ].join(',\n');

  const css = `
/* ── 毛玻璃质感覆盖(外挂,不改原文件)───────── */
${targets} {
  /* 半透明底色,让 backdrop-filter 有东西可透 */
  background: rgba(255, 255, 255, ${ALPHA_L}) !important;
  -webkit-backdrop-filter: blur(${BLUR}px) saturate(${SATURATE}%);
  backdrop-filter: blur(${BLUR}px) saturate(${SATURATE}%);
  /* 顶部一条极淡高光,模拟玻璃边缘反光 */
  border: 1px solid rgba(255, 255, 255, 0.45);
}

/* 暗色模式:换深色半透明底 + 更弱的高光边 */
html.dark ${targets.split(',\n').join(',\nhtml.dark ')} {
  background: rgba(36, 37, 38, ${ALPHA_D}) !important;
  border: 1px solid rgba(255, 255, 255, 0.08);
}
`;

  const style = document.createElement('style');
  style.id = 'waxy-glass-override';
  style.textContent = css;
  document.head.appendChild(style);
})();