下一代 CSS: @container

Avatar of Una Kravets
Una Kravets

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 200 美元的免费积分!

Chrome 正在试验 @container,这是 CSS 工作组 包含级别 3 规范 中的一个属性,由 Miriam Suzanne(来自 Oddbird)和一组跨网络平台的工程师推动。 @container 使我们能够根据元素的父容器大小来设置元素的样式

@container API 尚未稳定,语法可能会发生变化。 如果您自行尝试,可能会遇到一些错误。 请将这些错误报告给相应的浏览器引擎!

错误: Chrome | Firefox | Safari

您可以将这些视为媒体查询(@media),但不同的是,它们不依赖于视口来调整样式,而是依赖于您正在定位的元素的父容器来调整这些样式。

容器查询将是自 CSS3 以来 Web 样式方面最大的变化,改变了我们对“响应式设计”的理解。

视口和用户代理不再是我们创建响应式布局和 UI 样式的唯一目标。 借助容器查询,元素将能够定位自己的父级并相应地应用自己的样式。 这意味着,位于侧边栏、主体或英雄区域中的同一个元素,根据其可用大小和动态特性,可能会呈现完全不同的外观。

@container 的作用

在这个例子中,我在一个父级中使用了两个卡片,其标记如下

<div class="card-container">
  <div class="card">
    <figure> ... </figure>
    <div>
      <div class="meta">
        <h2>...</h2>
        <span class="time">...</span>
      </div>
      <div class="notes">
        <p class="desc">...</p>
        <div class="links">...</div>
      </div>
      <button>...</button>
    </div>
  </div>
</div>

然后,我在要查询容器样式的父级(.card-container)上设置包含(container-type 属性)。 我还在 .card-container 的父级上设置了相对网格布局,因此它的 inline-size 将根据该网格进行更改。 这就是我使用 @container 查询的内容

.card-container {
  container-type: inline-size;
  width: 100%;
}

现在,我可以查询容器样式来调整样式! 这与您使用基于宽度的媒体查询设置样式的方式非常相似,使用 max-width 在元素小于某个大小时设置样式,使用 min-width 在元素大于某个大小时设置样式。

/* when the parent container is smaller than 850px, 
remove the .links div and decrease the font size on 
the episode time marker */

@container (max-width: 850px) {
  .links {
    display: none;
  }

  .time {
    font-size: 1.25rem;
  }

  /* ... */
}

/* when the parent container is smaller than 650px, 
decrease the .card element's grid gap to 1rem */

@container (max-width: 650px) {
  .card {
    gap: 1rem;
  }

  /* ... */
}

容器查询 + 媒体查询

容器查询最好的功能之一是能够将微布局宏布局分开。 您可以使用容器查询对单个元素进行样式设置,创建细致入微的微布局,并使用媒体查询对整个页面布局进行样式设置,从而创建宏布局。 这创建了一个新的控制级别,可以实现更具响应性的界面。

以下是一个例子,它展示了使用媒体查询进行宏布局(例如,日历从单面板变为多面板)和微布局(例如,日期布局/大小和事件边距/大小发生变化)的强大功能,以创建美妙的查询交响乐。

容器查询 + CSS 网格

我个人最喜欢的一个展示容器查询效果的方法是观察它们在网格中的工作方式。 以下是一个植物商务 UI 的例子

此网站完全不使用媒体查询。 相反,我们只使用容器查询和 CSS 网格来在不同视图中显示购物卡片组件。

在产品网格中,布局使用 grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); 创建。 这创建了一个布局,告诉卡片使用可用的分数空间,直到它们的大小达到 230px,然后流到下一行。 更多网格技巧请访问 1linelayouts.com

然后,我们有一个容器查询,它将卡片的样式设置为在它们宽度小于 350px 时采用垂直块布局,并在应用 display: flex 时(默认情况下具有内联流)切换到水平内联布局。

@container (min-width: 350px) {
  .product-container {
    padding: 0.5rem 0 0;
    display: flex;
  }

  /* ... */
}

这意味着每张卡片都拥有自己的响应式样式。 这是另一个您可以使用产品网格创建宏布局,并使用产品卡片创建微布局的例子。 非常酷!

用法

要使用 @container,您首先需要创建一个具有 包含性 的父元素。 为此,您需要在父元素上设置 contain: layout inline-size。 您可以使用 inline-size,因为我们目前只能将容器查询应用于内联轴。 这将防止您的布局在块方向上断裂。

设置 contain: layout inline-size 将创建一个新的 包含块 和新的 块格式化上下文,让浏览器将其与布局的其余部分分离。 现在,我们可以进行查询!

局限性

目前,您无法使用基于高度的容器查询,只能使用块轴。 为了让网格子元素与 @container 一起使用,您需要添加一个包装元素。 尽管如此,添加包装器仍然可以实现您想要的效果。

试一试

您可以在今天的 Chromium 中试验 @container 属性,方法是在 Chrome Canary 中导航到:chrome://flags 并打开#experimental-container-queries 标志。