语义组件适配

Web 功能: 容器样式查询

使用样式查询 (@container style()) 触发上下文变体,而无需对产生或响应每个上下文的内容进行硬编码。这可以减少设计体系耦合,并提高可扩展性和组件封装性。

例如,section.featured { --surface: featured } 规则可以标记上下文,着陆页也可能通过 main { --surface: featured } 默认使用该上下文。然后,组件会选择启用对 --surface: featured 的响应:卡片会显示促销徽章,按钮会默认显示其填充变体,等等。

样式查询可让双方保持解耦:精选界面绝不会命名适应它们的元素,组件也绝不会检查产生上下文的内容。任何一端都可以独立扩展(新组件、新功能界面),而不会影响另一端。后代选择器(例如 .featured .button)则恰恰相反:选择器必须对关系的两端进行编码,并且只要其中一端发生变化,选择器就必须随之更改。

何时使用本指南

本指南涵盖行为变化:组件根据其上下文决定要呈现的内容、如何排列自身或呈现哪个变体,例如隐藏或显示标记、切换布局方向或交换按钮变体。

如果您需要进行令牌级更改(例如密度模式、主题或其他高阶令牌,这些令牌会在多个组件中统一更改内边距和颜色等值),请参阅设计令牌响应性。

选择合适的工具

当上下文关系的两端可能独立变化时,请使用样式查询。例如,新元素可能开始产生上下文,或者新组件可能开始对上下文做出反应。这种间接方式在可扩展性和封装性方面物有所值,并且样式查询可以跨越后代选择器无法跨越的 shadow DOM 边界。如果不需要这种灵活性,请首选更简单的工具:

  • 后代选择器 (.toolbar .button):一种固定的、一次性的关系,不可重复使用或扩展。遵循 YAGNI 原则:不要添加不会使用的间接层。
  • 尺寸查询 (@container (min-width: …)):由可用空间(而非语义状态)驱动的布局和密度变化。
  • 样式查询 (@container style(…)):语义状态,即组件在给定上下文中应呈现、排列或展示的内容。

实现指令

  1. 使用容器上的 CSS 自定义属性定义语义上下文标志。
  2. 请勿为样式查询设置 container-type;样式查询会查询最近祖先元素上继承的自定义属性,不需要显式包含上下文。
  3. 强制性:使用 @container style(--property: value) 将条件样式应用于后代元素。样式查询无法匹配设置了属性的元素,只能匹配其后代。
  4. 如果关系必须保持可扩展性或跨组件/影子 DOM 边界,请优先使用样式查询,而不是后代选择器(请参阅上文的选择合适的工具),但对于固定的一次性关系,请保留后代选择器,因为间接性不会带来任何好处。

实现示例

/* 1. Set the context on a layout container */
.featured {
  --surface: featured; /* Semantic context flag for children */
}

/* 2. Component reacts to inherited flag */
.button {
  /* Default: Outlined informational style */
  background: transparent;
  border: 1px solid currentColor;
}

.badge {
  /* Default: Hidden — only revealed in featured surfaces */
  display: none;
}

@container style(--surface: featured) {
  .button {
    /* Automatic switch to Filled promotional style */
    background: var(--brand-accent);
    color: white;
    border: none;
  }

  .badge {
    /* Reveal decorative elements only in featured context */
    display: block;
  }
}

后备策略

在容器样式查询广泛可用之前,请将其作为渐进增强功能进行分层:提供一个可在任何地方运行的基于选择器的默认样式,并在支持时让样式查询替换它。在上下文选择器上使用 :where(),以便样式查询(特异性相同,在源代码顺序中靠后)自动胜出。

/* Default styles — work everywhere */
.button {
  background: transparent;
  border: 1px solid currentColor;
}

.badge {
  display: none;
}

/* Selector-based contextual styles — broad browser support */
:where(.featured) .button {
  background: var(--brand-accent);
  color: white;
  border: none;
}

:where(.featured) .badge {
  display: block;
}

/* Progressive enhancement: style queries override when supported */
@container style(--surface: featured) {
  .button {
    background: var(--brand-accent);
    color: white;
    border: none;
  }

  .badge {
    display: block;
  }
}

选择器后备会将情境样式与类名相关联,从而将组件与其 DOM 放置位置相关联。样式查询得到广泛支持后,即可移除 :where() 规则。