深入挖掘容器样式查询

源节点: 1765194

我写了一些 关于容器样式查询的早期想法 不久前。 现在还为时尚早。 它们已经在 CSS Containment Module Level 1 规范 (目前处于编辑草案状态)但仍有一些未完成的讨论正在进行。

基本思想是我们可以定义一个容器,然后根据计算出的样式有条件地将样式应用于其后代。

@container ?  {
  /* conditional styles */
}

到目前为止我见过的最好的例子是从类似的东西中删除斜体 , 当它们用于内容已经是斜体的上下文中时:

em, i, q {
  font-style: italic; /* default UA behavior */
}

/* When the container's font-style is italic, remove italics from these elements. */
@container style(font-style: italic) {
  em, i, q {
    font-style: normal;
  }
}

这是一般的想法。 但是,如果您不知道的话,该规范的编辑 Miriam Suzanne 保持着一套持续而全面的 关于容器样式查询的个人笔记 这是公开的。 它是前几天更新的,我在那里花了一些时间试图围绕样式查询的更细微的方面进行思考。 这是非官方的东西,但我想我应该记下一些对我来说很突出的东西。 谁知道? 也许这是我们最终可以期待的东西!

每个元素都是一个样式容器

我们甚至不需要明确地分配一个 container-name or container-type 定义样式容器,因为默认情况下一切都是样式容器。

那么,您看到上面那个删除斜体的例子了吗? 请注意,它不标识容器。 它使用 style() 功能。 那么,查询的是什么容器呢? 这将是 元素的直接父元素 接收应用的样式。 如果不是那样,那就是 下一个最近的相对容器 那优先。

我喜欢。 搜索匹配项的查询非常符合 CSS-y,然后继续冒泡直到找到匹配条件。

我的小脑袋很难理解为什么我们可以摆脱基于样式的隐式容器,但在处理维度查询时却没有那么多,比如 sizeinline-size. Miriam 很好地解释了它:

维度查询需要css 遏制 容器的大小、布局和样式,以防止布局循环。 容器是一种广泛应用的侵入性事物,因此作者仔细控制哪些元素是(或不是)大小容器是很重要的。

基于样式的查询没有相同的限制。 在 CSS 中,后代样式已经无法影响祖先的计算样式。 因此不需要包含,并且在将元素建立为 样式查询容器.

(强调我的)

这一切都归结为后果——就一切都是开箱即用的样式查询容器而言,没有任何后果。

  • 如果找到容器:针对该容器解决条件。
  • 如果多个容器匹配:最近的相对容器优先。
  • 如果找不到匹配项: unknown 回。

那是一样的 其余 CSS 的“宽容”精神.

一个容器可以同时支持维度查询和样式查询

假设我们想要定义一个没有显式样式的查询 container-name:

@container style(font-style: italic) {
  em {
    font-style: normal;
  }
}

这有效,因为 所有元素都是样式容器,无论 container-type. 这就是允许我们隐式查询样式并依赖最近匹配的原因。 这完全没问题,因为在创建样式容器时也没有不利的副作用。

我们必须使用显式 container-type 对于维度查询,但对于样式查询则不是那么多,因为每个元素都是样式查询。 这也意味着这个容器既是一种风格 维度查询:

.card-container {
  container: card / inline-size; /* implictly a style query container as well */
}

从查询中排除容器

也许我们不希望容器参与匹配过程。 那是可以设置的地方 container-type: none 在一个元素上。

.some-element {
  container-type: none;
}

显式样式查询容器提供对查询内容的更多控制

比方说,如果我们要为 padding ,没有可靠的方法来确定最匹配的容器,无论我们是使用明确命名的容器还是最近的直接父容器。 那是因为 padding 不是继承的财产。

所以,在那些情况下,我们应该使用 container-name 明确通知浏览器他们可以从哪些容器中提取。 我们甚至可以给一个容器多个明确的名字,让它匹配更多的条件:

.card {
  container-name: card layout theme;
}

哦, container-name 接受任意数量的可选和 可重复使用 容器的名称! 在帮助浏览器在搜索匹配项时做出选择时,这更加灵活。

.theme {
  container-name: theme;
}
.grid {
  container-name: layout;
}
.card {
  container-name: card layout theme;
}

我有点想知道,如果一个容器被忽略,是否也可以将其视为“后备”。

样式查询可以组合

orand 运算符允许我们组合 wueries 以保持干燥:

@container bubble style(--arrow-position: start start) or style(--arrow-position: end start) {
  .bubble::after {
    border-block-end-color: inherit;
    inset-block-end: 100%;
  }
}

/* is the same as... */
@container bubble style(--arrow-position: start start) {
  /* etc. */
}
@container bubble style(--arrow-position: end start) {
  /* etc. */
}

切换样式

容器样式查询和 正在做的工作是定义一个 toggle() 功能. 例如,我们可以循环两个 font-style 价值观,说 italicnormal:

em, i, q {
  font-style: italic;
}

@container style(font-style: italic) {
  em, i, q {
    font-style: normal;
  }
}

凉爽的。 但是 CSS Toggles 的提议表明 toggle() 功能将是一个更简单的方法:

em, i, q {
  font-style: toggle(italic, normal);
}

但是除了这种二进制用例之外的任何东西都在哪里 toggle() 不太适合。 不过,样式查询很好用。 Miriam 确定了三种样式查询比 toggle():

/* When font-style is italic, apply background color. */
/* Toggles can only handle one property at a time. */
@container style(font-style: italic) {
  em, i, q {
    background: lightpink;
  }
}

/* When font-style is italic and --color-mode equals light */
/* Toggles can only evaluate one condition at a time */
@container style((font-style: italic) and (--color-mode: light)) {
  em, i, q {
    background: lightpink;
  }
}

/* Apply the same query condition to multiple properties */
/* Toggles have to set each one individually as separate toggles */
@container style(font-style: italic) {
  em, i, q {
    /* clipped gradient text */
    background: var(--feature-gradient);
    background-clip: text;
    box-decoration-break: clone;
    color: transparent;
    text-shadow: none;
  }
}

样式查询解决了“Custom Property Toggle Hack”

请注意,样式查询是 “CSS 自定义属性切换技巧”. 在那里,我们设置了一个空的自定义属性(--foo: ;) 并使用逗号分隔的回退方法在自定义属性设置为实际值时“切换”属性的打开和关闭。

button {
  --is-raised: ; /* off by default */
  
  border: 1px solid var(--is-raised, rgb(0 0 0 / 0.1));
  box-shadow: var(
    --is-raised,
    0 1px hsl(0 0% 100% / 0.8) inset,
    0 0.1em 0.1em -0.1em rgb(0 0 0 / 0.2)
  );
  text-shadow: var(--is-raised, 0 -1px 1px rgb(0 0 0 / 0.3));
}

button:active {
  box-shadow: var(--is-raised, 0 1px 0.2em black inset);
}

#foo {
  --is-raised: initial; /* turned on, all fallbacks take effect. */
}

这太酷了,样式容器查询也使很多工作变得微不足道。

样式查询和 CSS 生成的内容

对于由 content 的财产 ::before::after 伪元素,匹配容器是在其上生成内容的元素。

.bubble {
  --arrow-position: end end;
  container: bubble;
  border: medium solid green;
  position: relative;
}

.bubble::after {
  content: "";
  border: 1em solid transparent;
  position: absolute;
}

@container bubble style(--arrow-position: end end) {
  .bubble::after {
    border-block-start-color: inherit;
    inset-block-start: 100%;
    inset-inline-end: 1em;
  }
}

样式查询和 Web 组件

我们可以将一个web component定义为一个容器,通过style进行查询。 首先,我们有 组件的:


  
… …

然后我们使用 :host 伪元素作为容器来设置 container-name,以 container-type,以及上面的一些高级属性:

:host {
  container: media-host / inline-size;
  --media-location: before;
  --media-style: square;
  --theme: light;
}

里面的元素 可以查询的参数 元件:

@container media-host style(--media-style: round) {
  [part='img'] {
    border-radius: 100%;
  }
}

下一步是什么?

同样,我在这里记下的所有内容都是基于 Miriam 的笔记,这些笔记不能替代官方规范。 但它们表明正在讨论的内容以及未来可能发生的事情。 我感谢 Miriam 将一些仍在进行中的杰出讨论联系起来,我们可以关注这些讨论以掌握最新情况:

时间戳记:

更多来自 CSS技巧