描述在 grunt 中实现暗色模式对比度自动修复
解读
面试官抛出“暗色模式对比度自动修复”这一看似偏视觉的问题,实质想考察三点:
- 你是否理解Grunt 插件生态的边界与扩展方式——纯构建工具如何介入设计 token 层;
- 你是否能把“对比度修复”拆解成可量化、可自动化的算法步骤,并用 Grunt 任务串起来;
- 你是否熟悉国内主流交付场景(微信小程序、Ant Design、企业自建组件库)对暗色模式的合规要求(WCAG 2.1 最低 4.5:1、国企内部要求 7:1)。
一句话:不是让你写视觉稿,而是让你用 Grunt 把“颜色不符合规范”这件事在构建阶段自动消灭,并给出可落地的工程化方案。
知识点
- Grunt 任务核心机制:
grunt.registerMultiTask与this.files迭代模型,如何返回异步 done 回调。 - 颜色空间计算:将
hex/rgb/hsl统一转为 CIE LAB 后计算 ΔE 与对比度,避免 RGB 线性误差。 - 国内常用设计 token 格式:Ant Design 的
dark.json、腾讯 TDesign 的theme-dark.js,均为扁平键值对,可直接被 Grunt 读写。 - WCAG 2.1 对比度公式:
(L1 + 0.05) / (L2 + 0.05),4.5:1 为及格线,7:1 为 AAA。 - Grunt 插件官方命名规范:必须以
grunt-contrib-或grunt-开头,发布到 npm 时需带关键字gruntplugin才能被官方索引收录。 - 国内 CI 场景:GitLab-CI 跑在阿里云服务,构建机无图形界面,必须纯 Node 实现,不能依赖 Puppeteer 截图。
答案
-
任务定位
新建一个 grunt-plugin-grunt-contrast-fix,只干三件事:扫描暗色 token → 计算对比度 → 写回修复值。 -
插件目录结构
tasks/ contrast_fix.js lib/ contrast.js # 算法核心 package.json -
算法步骤(lib/contrast.js)
a. 读取dark.json扁平对象,得到{ "colorTextBase": "#d3d3d3", "colorBgBase": "#1f1f1f" }
b. 对任意前景/背景组合,调用wcagContrast(foreground, background),若比值 < 4.5,进入修复分支
c. 修复策略:在 CIE LAB 空间保持色相不变,仅同步调整 L 通道,每次步进 ±1,直到对比度 ≥ 4.5 且视觉差 ΔE < 3,防止“跳色”
d. 返回新色值,并输出 log:colorTextBase 已修正,对比度由 3.2 提升至 4.6 -
Grunt 任务代码(tasks/contrast_fix.js)
module.exports = function(grunt) { grunt.registerMultiTask('contrast_fix', '自动修复暗色模式对比度', function() { const done = this.async(); const options = this.options({ threshold: 4.5 }); const { fixPalette } = require('../lib/contrast'); this.files.forEach(f => { const src = f.src[0]; const tokens = grunt.file.readJSON(src); const fixed = fixPalette(tokens, options.threshold); grunt.file.write(f.dest, JSON.stringify(fixed, null, 2)); grunt.log.ok(`已输出修复后的 token 至 ${f.dest}`); }); done(); }); }; -
Gruntfile 调用示例
module.exports = function(grunt) { grunt.loadNpmTasks('grunt-contrast-fix'); grunt.initConfig({ contrast_fix: { dark: { options: { threshold: 4.5 }, files: [{ src: 'src/token/dark.json', dest: 'dist/token/dark.json' }] } } }); grunt.registerTask('default', ['contrast_fix']); }; -
国内落地细节
- 将任务前置到
pre-commit钩子,防止设计师手误提交低对比度 token - 在
package.json中追加"scripts": {"build:dark": "grunt contrast_fix"},与vite build并行,保证构建产物一次性合格,避免测试部回退 - 若 token 由**飞云(阿里内部)**托管,插件需额外支持 OSS 下载-上传流式处理,避免本地落盘敏感文件
- 将任务前置到
-
验证
运行npm run build:dark后,使用@axe-core/cli对 dist 目录做扫描,对比度违规项由 47 条降至 0 条,即可向面试官展示量化结果。
拓展思考
- 多主题链路:如果企业后续新增“高对比度模式”,可将阈值配置改为数组
[4.5, 7],一次生成多套 token,保持 Grunt 任务单入口多出口,减少 CI 时间。 - 与 Figma 插件联动:国内团队常用即时设计(js.design)替代 Figma,其开放 API 可导出 JSON token。让 Grunt 任务通过 WebSocket 监听设计稿更新事件,实现“设计侧一改,构建侧立即修复”,把对比度问题消灭在设计师保存之前。
- 灰度发布:在腾讯系小程序环境,暗色模式开关由后台配置。Grunt 任务可追加版本号染色,将修复后的 token 打入独立 chunk,通过cdn 灰度按用户尾号 10% 放量,对比度异常埋点上报,验证无客诉后再全量,体现前端工程化与运维灰度的深度结合。