为 UnoCSS 扩展无限色阶与主题换色

在 UnoCSS 生态中,颜色系统一直以预设色板(如 rose-500blue-600)为核心,配合 theme.colors 扩展自定义色板。但实际业务场景往往更复杂:

  • 设计系统需要精确到非标准色阶(如 445187),官方固定步长(50,100,...950)无法覆盖;
  • 需要为组件定义局部颜色变量,又不希望污染全局或手写 CSS 变量;
  • 暗黑模式下,同一个语义色(如 primary)需要映射不同色相,且色阶方向需要反转(浅色模式用亮色,暗色模式用暗色);
  • 运行时换色需求(多主题切换)往往需要重新构建整套样式,代价高昂。

Magicolor 预设正是为解决这些问题而生 ------ 它在不替换 UnoCSS 原有颜色系统的前提下,增加了一层 mc- 魔法颜色能力,提供任意数字色阶、局部颜色定义、全局语义别名和运行时更新等特性。

源码链接github.com/nixwai/unoc...


📦 快速开始

安装

使用 npm、pnpm 或 bun 安装预设:

bash 复制代码
# npm
npm install -D unocss-preset-magicolor

# pnpm
pnpm add -D unocss-preset-magicolor

# bun
bun add -d unocss-preset-magicolor

使用本预设前,请先按照官方 UnoCSS 集成指南 安装并配置 UnoCSS。保留项目中已有的 UnoCSS 预设,然后添加 presetMagicolor()

配置

把预设加入 UnoCSS 配置:

ts 复制代码
// uno.config.ts
import { defineConfig, presetWind4 } from 'unocss';
import { presetMagicolor } from 'unocss-preset-magicolor';

export default defineConfig({
  presets: [
    presetWind4(),
    presetMagicolor(),
  ],
});

🎨 基本使用与颜色变量定义

直接使用任意色阶

直接在模板中使用 mc- 颜色工具类:

html 复制代码
<button class="rounded px-4 py-2 bg-mc-rose-445 c-white hover:bg-mc-rose-620">
  Magic color
</button>

不同于 rose-500 这类固定色阶,Magicolor 支持任意数字色阶:

html 复制代码
<div class="bg-mc-rose-445 c-mc-blue-187 border border-mc-neutral-733">
  Arbitrary depth colors
</div>

色阶颜色会从当前 UnoCSS 主题色板,或由 magic-color 生成的 50、100、200...950 色板中解析,然后输出为 oklch(...) 自定义属性。预设只会为扫描到的颜色名和色阶生成变量,未使用的变量不会输出。

颜色来源多样性

Magicolor 工具类可以读取多种颜色来源。

UnoCSS 主题颜色 :UnoCSS 内置颜色和 theme.colors 中的自定义颜色都可以直接使用:

ts 复制代码
// uno.config.ts
export default defineConfig({
  presets: [presetWind4(), presetMagicolor()],
  theme: {
    colors: {
      grape: '#679512',
      wine: { red: '#9c1d1e' },
    },
  },
});
html 复制代码
<button class="border border-mc-wine-red-575 bg-mc-rose-445 px-8 py-4">
  <span class="c-mc-grape-160">Hello World!</span>
</button>

任意 CSS 颜色:使用 UnoCSS 任意值语法可以书写一次性颜色:

html 复制代码
<div class="bg-mc-[rgb(12_22_33)]-220 c-mc-[#789411]-430">
  Arbitrary magic color
</div>

全局语义别名 :对于稳定的语义色,例如 primarybrandsurface,可以使用 presetMagicolor({ colors }) 定义全局别名:

ts 复制代码
// uno.config.ts
import { defineConfig, presetWind4 } from 'unocss';
import { presetMagicolor } from 'unocss-preset-magicolor';

export default defineConfig({
  presets: [
    presetWind4({ dark: 'class' }),
    presetMagicolor({
      colors: {
        primary: { color: 'rose', lightnessReverse: true },
        brand: '#4f7bff',
      },
      dark: {
        primary: 'blue',
        brand: '#8ab4ff',
      },
    }),
  ],
});
html 复制代码
<button class="bg-mc-primary-457 c-white ring ring-mc-brand-630 hover:bg-mc-primary-620">
  Semantic color
</button>

colors 定义亮色模式别名,dark 定义同名暗色模式别名。当存在 presetWind4 时,Magicolor 会跟随它的暗色模式设置,包括 classmedia 和自定义选择器。camelCase 别名也可以通过 kebab-case class 使用,例如 brandPrimary 既可以写成 c-mc-brandPrimary-457,也可以写成 c-mc-brand-primary-457

局部颜色定义(组件级变量)

使用 mc-<name>_<source> 可以在任意 DOM 元素上声明局部颜色源,而无需在全局主题中定义。例如:

html 复制代码
<div class="mc-card_[#9c1d1e]">
  <button class="bg-mc-card-450 px-8 py-4 shadow-mc-card-620 hover:bg-mc-card-575">
    <span class="c-mc-card-610">Card button</span>
  </button>
</div>
  • mc-card_[#9c1d1e] 定义了一个名为 card 的局部颜色源,值为 #9c1d1e
  • 其后代中的 bg-mc-card-450c-mc-card-610 等工具类,会自动基于该源生成对应色阶变量。
  • 颜色源也可以是全局别名、主题颜色或其他任意颜色,甚至可带源色阶(如 mc-badge_rose-620)。

这种局部定义方式让组件样式自包含,适合库开发或微前端场景,同时避免了全局变量的污染。


🌗 暗黑模式下的反向亮度

传统暗黑模式通常通过 dark: 变体单独覆盖颜色,但当语义色在亮暗模式下需要相反亮度倾向时(如亮色模式用深色背景,暗色模式则用浅色背景),逐个写变体显得繁琐。Magicolor 提供了两种优雅的反向亮度机制。

1. 通过预设配置启用反向亮度

为语义别名设置 lightnessReverse: true,使其色阶读取方向自动反转:

ts 复制代码
presetMagicolor({
  colors: {
    primary: { color: 'rose', lightnessReverse: true },
  },
  dark: {
    primary: { color: 'blue', lightnessReverse: true },
  },
})
  • lightnessReverse: true 时,mc-primary-50 实际读取的是 rose 色板的 950 色阶,mc-primary-450 读取 550,而 500 保持 500(因为 500 是中间值)。
  • 这样,在亮色模式下使用浅色(如 bg-mc-primary-50)时,暗色模式会自动变成深色,无需额外编写 dark: 规则。

2. 使用 mc-lr-* 工具类局部反转

你可以在任意 DOM 子树中临时反转一个或多个颜色的色阶,而不修改全局配置:

html 复制代码
<!-- 反转 primary 色阶 -->
<section class="mc-lr-primary">
  <!-- bg-mc-primary-80 实际读取 primary 色板的 950-80=870 色阶 -->
  <button class="bg-mc-primary-80 c-white">Reversed primary</button>
</section>

<!-- 反转当前选区中所有已配置的颜色 -->
<div class="mc-lr">
  <!-- 同时反转 primary 和 rose -->
  <span class="bg-mc-primary-230 c-mc-rose-400">Both reversed</span>
</div>
  • mc-lr-primary 只反转 primary 这个颜色名。
  • 裸写 mc-lr 则会反转当前元素及其后代中所有正在使用的颜色名(包括全局别名、主题颜色和局部定义的颜色),非常适合在暗黑模式容器内一键调整整体亮度方向。

3. 与 UnoCSS 暗黑选择器联动

当使用 presetWind4({ dark: 'class' })dark: 'media' 时,Magicolor 会自动跟随 UnoCSS 的暗黑模式配置。你在 presetMagicolor({ dark: {...} }) 中定义的暗色别名,会在对应的 .dark 类或 @media (prefers-color-scheme: dark) 下生效,覆盖亮色别名。整个切换逻辑纯 CSS 驱动,无需额外 JavaScript,性能极佳。


🔧 兼容性:与 UnoCSS 生态无缝协作

Magicolor 设计之初就强调非侵入式,它不会覆盖或破坏 UnoCSS 原有功能,而是作为增强层存在。

  • 所有 UnoCSS 变体hover:focus:dark:group-hover: 等)和 mc-* 工具类完全兼容。
  • Shortcuts / Safelist / Attributify 模式 :预设内置了 usage scanner,能正确识别 mc-* 类并生成对应变量,无需额外配置。
  • transparentcurrentColorinherit 等特殊颜色关键字会直接透传,不会误生成 magic 变量。
  • presetWind4presetUno 等主流预设协同工作 :主题色板(theme.colors)会被 Magicolor 自动读取。
  • Layer 优先级可控 :默认 mc-* 工具类所在的 layer 排序值为 -100,原生 UnoCSS 工具类优先级更高。如需提高 mc-* 优先级,只需在配置中提升该 layer 的排序值即可。
相关推荐
jiayong231 小时前
第 44 课:任务详情抽屉里的评论输入与操作记录联动
前端
ndsc_d1 小时前
前端实战复盘:用AI直接生成响应式官网落地页与React源码
前端·人工智能·react.js·ui·前端框架·aigc·paico
大家的林语冰2 小时前
✌️ Deno 2.9 来了,ESM 直接导入 CSS,甚至能开发桌面应用?!
前端·javascript·node.js
李顿波2 小时前
前端样式技术发展进程和解析(长期更新版)
前端
不简说2 小时前
JS 代码技巧 vol.1 — 10 个让代码少写 30% 的小套路
前端·javascript·面试
Hilaku2 小时前
为什么业务型前端容易遭遇中年危机?
前端·javascript·程序员
এ慕ོ冬℘゜3 小时前
深度解析 JavaScript:赋予 Web 灵魂的“全栈语言”
开发语言·前端·javascript
iCOD3R3 小时前
Skill - 3秒生成精美GitHub主页
前端·程序员·ai编程
爱勇宝3 小时前
《道德经》第5章:生产环境不相信眼泪
前端·后端·架构