news 2026/9/9 23:45:50

airi 项目 UnoCSS 实战:用 transformerDirectives 在 CSS 中原生启用 @apply、@screen、theme() 与 icon()

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
airi 项目 UnoCSS 实战:用 transformerDirectives 在 CSS 中原生启用 @apply、@screen、theme() 与 icon()

airi 项目 UnoCSS 实战:用 transformerDirectives 在 CSS 中原生启用 @apply、@screen、theme() 与 icon()

【免费下载链接】airi💖🧸 Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-sama's altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi

transformerDirectives 是 UnoCSS 提供的一个核心 transformer,它让开发者能够直接在 CSS 中书写@apply@screentheme()icon()四种指令,把工具类(utilities)与主题(theme)能力"搬进"普通 CSS 文件中。在 airi 仓库中,这条能力既被根配置集中启用(uno.config.ts),也被应用到桌面端渲染层(如 spotlight.vue)的复杂渐变与响应式样式编写上。读完本文,你将掌握四种指令的完整语法、别名配置方法,以及如何像 airi 一样在 Vue SFC 的 scoped style 里优雅地混用工具类与原生 CSS。

本文的原始知识卡位于仓库 .agents/skills/unocss/references/transformer-directives.md,下面结合 airi 的真实配置与源码逐层展开。

transformerDirectives 解决的问题

在纯 UnoCSS 工作流中,工具类通常直接写在模板的 class 属性中。但当你需要复用一组工具类、或想在::before/::after等伪元素、@keyframes内部、媒体查询里组合样式时,模板 class 就无能为力了。transformerDirectives 正是为此而生——它扫描 CSS 源码,把指令转换为展开后的工具类声明,使"样式逻辑收敛到 CSS"与"工具类按需生成"两种范式可以共存。

安装与最小配置非常简单,在 UnoCSS 配置文件(通常是uno.config.ts)中加入 transformer 即可:

import { defineConfig, transformerDirectives } from 'unocss' export default defineConfig({ transformers: [ transformerDirectives(), ], })

airi 中的启用方式:从根配置到各应用共享

airi 的根级 uno.config.ts 把共享配置封装为sharedUnoConfig(),其中 transformers 同时启用了 directives 与 variant group 两个转换器,并对别名做了显式限定:

// 根 uno.config.ts(节选) transformers: [ transformerDirectives({ applyVariable: ['--at-apply'], }), transformerVariantGroup(), ],

这里的关键点:显式传入applyVariable: ['--at-apply']后,自定义属性别名只保留--at-apply一个(不再包含默认同义别名,详见下文别名一节)。随后,不同子应用通过mergeConfigs复用这份共享配置——例如 packages/stage-ui/uno.config.ts 直接合并了根配置:

import { defineConfig, mergeConfigs } from 'unocss' import { histoireUnoConfig, sharedUnoConfig } from '../../uno.config' export default mergeConfigs([ sharedUnoConfig(), histoireUnoConfig(), defineConfig({}), ])

也有的应用独立声明完整配置,例如 apps/component-calling/uno.config.ts 中与transformerVariantGroup()并排启用了默认形态的transformerDirectives()(不传参数)。仓库通过 pnpm workspace catalog 统一锁定了unocss版本(见 pnpm-workspace.yaml 中unocss: 66.7.5的 catalog 条目),因此各应用获得的 transformer 行为是一致的。

@apply:在 CSS 中内联工具类

基础用法与带变体的写法

@apply可以把一组工具类合并进任意 CSS 规则中,最直接的应用是封装"类组件化"的样式:

.custom-btn { @apply py-2 px-4 font-semibold rounded-lg; }

当需要混入hover:focus:等**变体(variant)**时,由于变体值中带冒号与空格,必须使用引号包裹整个字符串,否则指令解析会失败:

.custom-btn { @apply 'hover:bg-blue-600 focus:ring-2'; }

CSS 自定义属性替代写法(vanilla CSS 兼容)

@apply是标准 CSS 之外的指令语法,若希望样式文件仍能通过普通 CSS 解析器(例如防止原生 CSS 校验报错,或希望渐进增强),可改用 CSS 自定义属性别名:

.custom-div { --at-apply: text-center my-0 font-medium; }

transformer 支持三个同义别名,配置如下:

transformerDirectives({ applyVariable: ['--at-apply', '--uno-apply', '--uno'], // 或完全关闭别名能力:applyVariable: false })
  • 默认别名是--at-apply
  • 当传入数组时,数组内容即生效别名集合;
  • 设为false可关闭该特性(若你的样式表中恰好有用到这些自定义属性的场景,可避免误展开)。

airi 的真实用法:伪元素 + 复杂渐变 + 深色模式

自定义属性写法在 airi 桌面端被大量使用,因为它能出现在::before/::after这类伪元素规则中,与手写 position、mask-image 等原生属性无缝共存。下面摘自已搜索可见的 apps/stage-tamagotchi/src/renderer/pages/spotlight.vue(舞台 spotlight 输入框的光晕层):

.spotlight-card::before { pointer-events: none; --at-apply: 'bg-gradient-to-r from-primary-500/25 via-primary-500/12 to-transparent dark:from-primary-400/25 dark:via-primary-400/12 dark:to-transparent'; content: ''; position: absolute; inset: 0; z-index: 0; width: 85%; height: 100%; mask-image: linear-gradient(120deg, white 100%); } .spotlight-card::after { pointer-events: none; --at-apply: 'bg-dotted-[primary-300/35] dark:bg-dotted-[primary-200/16]'; position: absolute; inset: 0; z-index: 0; width: 100%; height: 100%; background-size: 10px 10px; content: ''; mask-image: linear-gradient(165deg, white 30%, transparent 55%); }

这个例子至少说明了三点实战要点:

  1. 值必须带引号:多段工具类(含空格与dark:变体)必须写成引号字符串,--at-apply后的值才会被当作完整指令解析;
  2. 可以消费自定义规则与主题色bg-dotted-[primary-300/35]对应根配置 uno.config.ts 中自定义的bg-dotted-[...]规则,而primary-300等色板来自presetChromatic生成的主题——证明--at-apply展开的是与模板 class 完全一致的完整工具类体系;
  3. 原生 CSS 与指令可并存:同一规则内mask-imagebackground-sizeposition等手写声明与--at-apply互不干扰,这让"UI 引擎层样式"保持为可读的 CSS。

另一个更简洁的用法见 apps/stage-tamagotchi/src/renderer/pages/index.vue(加载动效墙):

.wall { --at-apply: text-primary-300; --wall-width: 8px; animation: wall-move 1s linear infinite; background-image: repeating-linear-gradient( 45deg, currentColor, currentColor var(--wall-width), #ff00 var(--wall-width), #ff00 calc(var(--wall-width) * 2) ); }

这里--at-apply: text-primary-300仅为元素注入文字颜色,随后手写的background-image通过currentColor引用它——指令与原生 CSS 通过颜色关键字协同工作,是"工具类用于取主题 token、原生 CSS 负责复杂绘制"的理想分工。

提示:从源码检索看,airi 的 Vue SFC 中统一采用--at-apply别名而非@apply关键字。若团队规约更看重可迁移性,可采用与根配置一致的applyVariable显式列表,避免样式被其他解析器误读。

@screen:把断点写成语义化的媒体查询

@screen <断点名>会被转换为对应的媒体查询,断点名称来自主题breakpoints(presetWind3 / preset-mini 默认提供sm/md/lg/xl/2xl等)。经典响应式栅格示例:

.grid { display: grid; grid-template-columns: repeat(2, 1fr); } @screen sm { .grid { grid-template-columns: repeat(3, 1fr); } } @screen lg { .grid { grid-template-columns: repeat(4, 1fr); } }

相比手写媒体查询,@screen的可读性更强,且断点值始终与主题定义保持单一事实来源。

断点变体:lt- 与 at-

除默认的"大于等于某断点"语义外,还支持两种变体:

/* 小于某断点才生效(max-width 语义) */ @screen lt-sm { .item { display: none; } } /* 仅在该断点区间生效(min/max 组合) */ @screen at-md { .item { width: 50%; } }

这在"移动端隐藏某元素""仅中屏横排"等场景中比默认的向上兼容语义更精确。

theme():在任意 CSS 属性中读取主题令牌

theme('路径点分字符串')允许在 CSS 值里直接读取 UnoCSS 主题配置,避免把colors.blue.500这类十六进制硬编码散落在样式文件中:

.btn-blue { background-color: theme('colors.blue.500'); padding: theme('spacing.4'); border-radius: theme('borderRadius.lg'); }

取值路径采用点分记号,映射到根配置defineConfig({ theme: {...} })的对象结构。airi 根配置扩展了丰富的主题段(见 uno.config.ts),例如theme.fontFamily中定义的cute/cutejp/sans-rounded等字体族、以及theme.animation下一整套 keyframes / durations / timingFns 的入场退场动画配置——这些都可以通过theme('fontFamily.cute')theme('animation.durations.fadeIn')这类写法在 CSS 中引用,与@apply、模板 class 读取的是同一份主题数据。

icon():把图标工具类转成 SVG 背景图

icon()指令能把预设图标工具类(如i-carbon-sun)转换为内联 SVGbackground-image。它依赖 preset-icons 提供图标解析——airi 根配置恰好启用了presetIcons并挂载了多个 Iconify 集合(见 uno.config.ts):

.icon-sun { background-image: icon('i-carbon-sun'); } /* 第二个参数指定自定义颜色 */ .icon-moon { background-image: icon('i-carbon-moon', '#fff'); } /* 颜色参数同样支持 theme() 读取主题色 */ .icon-alert { background-image: icon('i-carbon-warning', 'theme("colors.red.500")'); }

注意两点:颜色的使用方式是覆盖图标原色(本质是为内联 SVG 注入指定色值);而theme("colors.red.500")之所以用双引号,是因为它嵌套在外层单引号之内,且颜色参数本身是一个表达式而非普通字符串。该能力非常适合为那些无法通过text-*染色的背景型图标注入主题色。

完整组合示例:Card 组件

把四种指令放进一个组件级样式,即可看到它们如何协作(完整示例亦保留于原始知识卡):

.card { @apply rounded-lg shadow-md p-4; background-color: theme('colors.white'); } .card-header { @apply 'font-bold text-lg border-b'; padding-bottom: theme('spacing.2'); } @screen md { .card { @apply flex gap-4; } } .card-icon { background-image: icon('i-carbon-document'); @apply w-6 h-6; }

从这份示例可以归纳协作模式:@apply负责排版骨架与可见性,theme()负责取值,@screen负责响应式分段,icon()负责图形资产。在 airi 这类多端(Web / Electron / Capacitor / tamagotchi 桌面应用)monorepo 中,把这份 CSS 放在公共 UI 包(如 packages/stage-ui)内,配合根配置的分层启用策略,即可保证各端获得一致的指令行为。

使用建议与注意事项

  • 作用域内使用:在 Vue SFC 中,<style scoped>内的@apply/--at-apply展开发生在 UnoCSS 构建阶段,Vue 的 scoped 属性处理与其并不冲突,airi 的多个页面即采用这种组合。
  • 带空格与变体务必加引号@apply--at-apply的值若包含hover:/dark:/ 空格分隔的多段类名,必须整体加引号,否则会被当作单一工具类解析失败。
  • 别名的统一管理:既然项目从根配置统一了applyVariable,新增子应用时应优先复用sharedUnoConfig()或与其保持一致,避免不同应用间别名集合漂移。
  • 图标类需存在集合icon()依赖presetIcons及其配置的 collections;如果图标名不在已加载集合内,构建期不会生成对应背景图。
  • 与 extractor 的配合@apply/--at-apply等指令内出现的工具类由 transformer 收集并触发生成,airi 根配置的 content pipeline(见 uno.config.ts)同时覆盖.vue.ts/.js等文件,确保 CSS 中引用的类与模板中用到的类都进入统一扫描范围。

若需进一步了解相关的 preset-icons、preset-wind3、variant-group 与 shortcuts 能力,可继续查阅仓库 .agents/skills/unocss 下的 references 知识卡(如 preset-icons.md、preset-wind3.md、transformer-variant-group.md、core-shortcuts.md),它们与本文共同构成 UnoCSS 在 airi 中的完整使用参考。

【免费下载链接】airi💖🧸 Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-sama's altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 23:41:59

全家游北京一家一团怎么选?2026 北京一家一团服务标准及哪家好不拼陌生人深度指南

对于计划全家一起来北京旅行的家庭来说&#xff0c;选择合适的一家一团服务商是出行规划中最重要的决策。北京一家一团服务标准、北京一家一团哪家好、全家游北京一家一团、北京一家一团不拼陌生人&#xff0c;这四个关键词反映了家庭游客对服务品质、选择标准、全家游需求、团…

作者头像 李华
网站建设 2026/9/9 23:41:35

汉字与字母文字:两套操作系统的架构对比与演化逻辑

“字母文字的焦虑&#xff1a;当汉字成了文明发展的高效操作系统”——光看这个标题&#xff0c;我就知道这不是一个用来做情绪宣泄的选题&#xff0c;而是可以当成一套系统架构来聊的话题。我自己写文章、做产品、处理中英文信息有十几年了&#xff0c;频繁在两种文字系统之间…

作者头像 李华
网站建设 2026/9/9 23:38:47

immersive-translate 云同步怎么设置:换电脑不用重新配翻译配置

immersive-translate 云同步怎么设置&#xff1a;换电脑不用重新配翻译配置 【免费下载链接】immersive-translate 沉浸式双语网页翻译扩展 , 支持输入框翻译&#xff0c; 鼠标悬停翻译&#xff0c; PDF, Epub, 字幕文件, TXT 文件翻译 - Immersive Dual Web Page Translation …

作者头像 李华