网站导航

Directives

工具类与混入

定义按需生成的工具类、可复用的混入以及使用层叠层的原生样式。

本页内容

@utility 注册按需生成的类。@mixin 为可复用 CSS 主体命名;@apply 在调用处展开它。原生选择器仍为常规 CSS。复用样式说明如何选择形式;本页定义其语法与替换规则。

@utility

在顶层显式声明类形式:

@utility card-layout {  display: grid;  gap: var(--spacing-md);  &:hover {    outline: 1px solid currentColor;  }}@utility col-gap-(--spacing) {  column-gap: var(--spacing);}@utility grid-cols(--cols <integer>) {  display: grid;  grid-template-columns: repeat(var(--cols), minmax(0, 1fr));}

在标记中使用 card-layout、col-gap-sm 和 grid-cols(3)@sm。头部决定定义注册的是固定类、令牌模式还是函数。每种形式仅注册该形式,令牌模式不会同时创建函数调用。添加声明或支持的嵌套规则不会改变注册形式。

工具类名称和令牌前缀不能包含 _,因为 _ 在类中开始后代选择器;此类定义会在源位置报告错误。令牌键可以含 _:它仍是可供 var() 使用的有效变量,但不会生成裸令牌类或补全。混入名称、自定义属性及原生样式表类仍可使用 _。

注册与预设

主题提供令牌,工具类定义决定这些令牌能产生哪些类。导入 "@master/css" 获取完整预设;显式组合样式表时,也可搭配自己的令牌加载 "@master/css/utilities.css"。

家族索引提供完整令牌与属性映射。固定工具类和直接数值列出各自的注册。它们均使用本页所述的相同定义形式。

令牌模式

模式有一个末尾占位符。参数指定令牌命名空间,固定部分指定类前缀。无类型占位符绑定令牌值:

@utility p-(--spacing) {  padding: var(--spacing);}@utility m-(--spacing) {  margin: var(--spacing);}@utility fg-(--color) {  color: var(--color);}@utility size-(--spacing) {  width: var(--spacing);  height: var(--spacing);}

p-md 绑定 var(--spacing-md);size-md 在两条声明中使用该引用。常规令牌保留由浏览器求值的引用,inline 令牌则将声明值代入一次。未知令牌不会创建类。值模式接受数字和 Unicode 键,包括包含 -- 的键。

<string> 占位符绑定令牌键,可用于显式引用伴随值:

@theme {  --text-hero: var(--font-size-5xl);  --text-hero--line-height: 1.15;  --text-hero--letter-spacing: -.03em;}@utility text-(--text <string>) {  font-size: var(ident("--text-" var(--text)));  line-height: var(ident("--text-" var(--text) "--line-height"), normal);  letter-spacing: var(ident("--text-" var(--text) "--letter-spacing"), normal);}

text-hero 绑定字符串 "hero",且必须存在对应的主要令牌。含 -- 的伴随键不创建类;缺少伴随值时使用原有回退值。ident() 静态折叠为常规 CSS 标识符。命名空间来自参数,因此 @utility type-(--text <string>) 使用相同令牌,搭配不同类前缀。令牌占位符不接受默认值或其他类型。

共享前缀可以显式提供多个命名空间:

@utility font-(--font-family) {  font-family: var(--font-family);}@utility font-(--font-size) {  font-size: var(--font-size);}@utility font-(--font-weight) {  font-weight: var(--font-weight);}

font-sans、font-sm 和 font-bold 各自选择唯一包含该键的命名空间。键若存在于多个分支,使用时报告 AMBIGUOUS_TOKEN 且不产生 CSS。请改用不同键,或 (font-size:var(--font-size-brand)) 这类原生声明。此规则也适用于字符串键配方和多声明主体。负值与不透明度仅在选定唯一分支后才检查。

固定类优先于模式:保留的是完整类名拼写,而非令牌键。因此即使存在 --text-center,text-center 仍设置 text-align;fg-solid、b-solid/.5 和 -mx-auto 则仍可使用名为 solid 或 auto 的令牌。模式采用最长前缀;所选家族内若没有令牌,就报告错误,不再尝试较短前缀。参数命名空间不同的令牌模式可共用前缀。较后模式完整替换解码后前缀与命名空间均相同的定义,包括参数模式、主体、来源与依赖。静态和函数注册则各自独立替换同形式、同名称的定义。

负值与不透明度适用于直接值子集:一个无类型占位符,以及一条完整值恰为 var(--parameter) 的原生声明。-m-md 将数值间距令牌转为负值;-p-md 无效,因为 padding 不支持负值。fg-red/.5 为颜色令牌应用不透明度,范围为零到一。这些能力不扩展到常规多声明或字符串键配方。

两个匹配的命名空间不会产生两条声明

此自定义主题中两个令牌键均存在。Master CSS 必须先选出唯一命名空间,才会生成类。

文字说明
  1. 存在 --font-family-brand: monospace 和 --font-size-brand: 2rem 时,font-brand 匹配两个预设 font- 家族。
  2. 结果为 AMBIGUOUS_TOKEN,且该类不产生 CSS;分支不会合并。
  3. 请用不同键,或 (font-size:var(--font-size-brand)) 这类显式原生声明。

静态参数

函数工具类与混入接受位置参数:无类型、<integer>、<number>、<string> 或 <custom-ident>。静态默认值用冒号指定,例如 --cols <integer>: 2。数字必须是数值字面量;网格数量与跨度必须为正整数。字符串参数必须加引号。

@utility pair(--size) {  width: var(--size);  height: var(--size);}

使用 pair(2rem)。常规函数参数必须能在静态阶段确定。若函数或混入恰有一个无类型、无默认值的参数,且仅有一条完整值恰为 var(--parameter) 的原生声明,也接受符号式 var() 表达式。调用方引用原样保留,交由浏览器求值。其他函数拒绝外部 var()、attr() 及依赖浏览器的参数。已知的外层参数可通过 var(--parameter) 转发。

A parameter utility

@utility pair(--size) { width: var(--size); height: var(--size); } @safelist 'pair(2rem)';
生成的 CSS
@layer utilities {  .pair\(2rem\) {    width: 2rem;    height: 2rem  }}

静态配方参数不同于实时 CSS 值

图中的 pair 工具类有两条声明。另一项单参数、单声明的符号式引用例外不适用于此。

文字说明
  1. @utility pair(--size) { width: var(--size); height: var(--size); } 接受 pair(2rem) 的已知参数,并绑定到两条声明。
  2. 此配方拒绝 pair(var(--size)),因为参数依赖浏览器。需要保持实时变化的值,请用显式原生声明类。
  3. (width:var(--size)) 为浏览器保留变量引用,仅改变宽度。若也需要高度,请另加高度声明。

@mixin 与 @apply

Master 编译实验性 CSS Mixins 规范的静态子集。定义采用双连字符名称,且必须是不带条件的样式表级规则。混入提供 CSS 复用,不注册 Master 类。

@mixin --caption {  font-size: var(--font-size-sm);  line-height: 1.5;}.caption {  color: currentColor;  @apply --caption;  font-weight: 600;}@utility caption {  @apply --caption;}

@apply 在原生规则、工具类或另一个混入内接受一个混入调用,不接受工具类名称或类列表。展开位于原有声明位置,并保留重复声明、重要性、选择器、条件与 URL 所有权。

参数可用于应用元素上,包括 &:hover 与条件包装。跨元素使用参数、重新赋值、选择器或条件插值、递归定义、重载、@private 及不支持的类型均报告编译错误。该静态子集未实现依赖浏览器的参数求值、私有属性或通用函数。

混入在调用位置展开

插入位置前后的相同行保持对齐。选择器、声明顺序和外层的层叠层仍由调用方拥有。

文字说明
  1. --caption 混入提供 font-size: 14px 和 line-height: 1.5。
  2. 在 .caption 内,@apply --caption 展开于 color: currentColor 与 font-weight: 600 之间。
  3. 展开不会注册类,也不会将原生规则移至生成层。重复声明和编写顺序均保留。

内容与类包装

使用 @contents 标记调用方的声明或嵌套规则插入可复用包装的位置。

@mixin --card-wide() {  @container card (width >= 40rem) {    @contents {      display: grid;    }  }}.card {  @apply --card-wide {    gap: 1rem;  }}

@contents; 插入提供的内容。@contents { … } 仅在调用省略块时提供回退内容。显式空 @apply --card-wide {} 会抑制回退。每个 contents 位置插入一次块,保留声明、重复值与嵌套规则。没有 @contents 的混入忽略提供的块。

省略 contents 块与空块不同

混入包含 @contents { display: grid; }。调用处的花括号区分刻意留空与省略块。

文字说明
  1. @apply --panel; 省略 contents 块,使用 display: grid 回退内容。
  2. @apply --panel {} 提供空块,抑制回退,因此本例不添加声明。
  3. @apply --panel { display: flex; } 提供自身内容替换回退内容,两条 display 声明不会合并。

提供的内容保留调用方参数绑定,即使经嵌套应用转发也一样。回退内容使用定义混入的绑定。常规自定义属性保留原生 CSS 层叠行为。允许有限的嵌套调用;递归定义及超过展开深度或节点上限均为错误。

@mixin --hocus {  &:is(:hover, :focus-visible) {    @contents;  }}@utility emphasis {  color: red;  display: block;}
<button class="(color:red)@apply(--hocus)">Save</button><div class="grid-cols(3)@apply(--card-wide)"></div><div class="emphasis@apply(--hocus)!"></div>

@apply(--name(...)) 接受静态参数。多个 @ 包装从左到右嵌套:(color:red)@apply(--a)@apply(--b) 将 --a 放在 --b 外层。工具类将所有声明作为一个块提供,因此包装自身的声明每个类仅输出一次。选择器后缀保留原有位置;末尾 ! 将每条展开声明标为重要。类头部与除 @apply 外的条件后缀决定类排序:每个外层条件分支都是独立排序的一条规则;混入内部选择器和条件留在输出文本中,不会让类移到较后排序级别,展开也保留原始顺序。若展开未添加声明、选择器或条件,就与单声明工具类一起排序;若头部为 (property:value),则与原生声明一起排序。其他展开按配方排序。检查与冲突分析使用展开后的规则。

未使用定义、丢弃的 contents 和未使用回退块不会保留令牌或关键帧。已交付原生 @apply 仍通过 emitted-globals 契约作为资源根节点。声明位置与相对 URL 保留其定义或调用文件。

包装决定调用方声明的位置

高亮插槽与插入声明位于选择器边界内的同一位置。包装控制声明的应用位置。

文字说明
  1. @mixin --hocus { &:is(:hover, :focus-visible) { @contents; } } 定义包含显式 contents 插槽的选择器包装。
  2. underline@apply(--hocus) 将 text-decoration-line: underline 提供给插槽。输出规则在任一状态下均可匹配类所属元素。
  3. 图示结果以 & 简写转义后的类所属元素,使位置易于阅读;生成 CSS 仍保留完整所属元素。

完整定义替换

较后的定义替换同一注册的完整主体,不合并声明。同名静态工具类与函数工具类属于独立注册。令牌模式仅替换解码后前缀与命名空间均相同的分支。

@utility badge {  color: red;  padding: 1rem;}@utility badge {  color: blue;}

badge 现在仅输出 color: blue。之前的 padding 及其依赖已移除。

较后定义完整替换注册

两个定义注册相同的静态工具类。第二个完整替换第一个,原有的 padding 也不再保留。这是生成前的定义替换,并非两条 CSS 规则在层叠中竞争。

文字说明
  1. 第一个静态 badge 定义包含 color: red 和 padding: 1rem。
  2. 较后的静态 badge 定义仅含 color: blue,并完整替换第一个定义。
  3. 使用 badge 生成一条包含 color: blue 的规则。之前的 padding 及其依赖不会保留。
  4. 静态与函数注册仍相互独立。令牌模式仅替换前缀与命名空间相同的分支;混入则替换完整命名定义。

混入名称仅有一个最终定义,包括参数列表、主体与依赖。空定义仍保持注册。前向嵌套调用依据最终注册表解析;不提供参数重载。

静态名称与函数独立注册

路径表示注册身份。名称相同不会使两个定义成为重载或合并主体。

文字说明
  1. @utility badge { display: inline-block; } 注册静态 badge 类。@utility badge(--inset) { padding: var(--inset); } 注册独立函数。
  2. 将静态定义改为 display: block 会改变 badge,但 badge(8px) 仍写入 padding: 8px。
  3. 替换影响的是同形式、同名称的完整主体。函数不是静态类的回退。

定义作用域与导入

@theme、@utility、@mixin、@custom-media 和 @custom-container 是顶层定义,在层或条件包装中注册会产生错误。混入主体可包含声明、嵌套选择器、支持的原生条件、嵌套 @apply 与 @contents。层位置由调用方决定。

源扫描、安全列表、引擎 API 和运行时 DOM 变更中的类均为使用根节点。已交付原生 CSS 中的 @apply 也是根节点;未使用工具类或混入内、或未交付 @reference 样式表内的则不是。依赖收集在参数绑定与 ident() 折叠后进行。已使用令牌保留有序声明和传递依赖。静态令牌即使没有引用方也保持根节点。移除最后一个根节点会释放资源;原生 CSS 资源则通过 emittedGlobals 保持固定。

运行时 manifest 保留未使用定义,供未来 DOM 类使用。按需生成针对 CSS 与资源输出。原生 CSS 裁剪仍须显式启用;关键帧与其声明文件共用原生保留和裁剪策略。带限定的导入只能包含原生 CSS。定义请通过不带限定的导入或引用加载。

原生属性优先顺序

原生 (property:value) 类的 CSS 属性含义独立于工具类注册。在相同条件下,单声明工具类与原生声明类可以覆盖配方的一部分。完整排序和覆盖规则见声明与值。

原生默认样式

将原生默认样式写在 @layer defaults 中。

原生组件

将原生组件选择器写在 @layer components 中。

@layer components {  .button {    display: inline-flex;    padding: var(--spacing-md);    &:focus-visible {      outline: 2px solid currentColor;    }  }}

原生规则按样式表导入图交付。工具类定义注册类名;混入定义通过 @apply 提供 CSS 复用。两者均在编译期间使用,仅被使用时才输出 CSS。

Sass 互操作

使用 Sass 时,请将 CSS 混入定义放在 .css 文件中。当前 Dart Sass 为未来 CSS 支持保留 @mixin --name,并拒绝将其视为 Sass 混入;请见 Sass 兼容性公告。SCSS 文件可在 Master CSS 编译前,通过插值输出原生 at-rule:

@#{'mixin'} --card {  padding: 1rem;}.card { @apply --card; }

这是 Sass 插值,并非额外的 Master CSS 语法。Sass @include 与 CSS @apply 仍是独立结构。

主题选择器包装

原生选择器和查询激活主题覆盖。混入可以提供对应的类条件:

@mixin --ocean {  &:where([data-theme="ocean"], [data-theme="ocean"] *) {    @contents;  }}

使用 (color:white)@apply(--ocean)。指向 shadow host 时,请显式添加 :host(...) 选择器。