[CSS]还在用媒体查询?要不尝鲜下Container Queries

1,609

随着设备种类越来越多,前端响应式成为了一个基本要求。虽然很多组件框架都会帮忙完成一部分工作,但是对于一些需要自己实现的组件,这就成为了一件麻烦事了。
现在的 @media 针对的是整个页面的显示大小,然而对于一些特定页面结构中的组件(例如在左右分栏的页面结构中的卡片),明明我们只是想根据组件的大小来调整布局,然而却得考虑整个网页的布局,以推测在不同页面大小下这个组件能够拥有的空间。
现在,随着CSSWG决定将 CSS Container Queries 加入到 CSS Containment Module level 3 中,我们可以期待一下一种新的自适应组织方式了。

参考
本文内容来源自Say Hello To CSS Container Queries(一个十分生动形象的博客介绍,更推荐直接看这个)
CSS Container Queries-MDN 这个是一个粗略的介绍,适合简单了解 Container Queries: a Quick Start Guide这里以一些例子介绍了容器查询的代码规则
Container Query Proposal & Explainer

快速介绍

@media针对的是整个视窗的大小,然而随着组件化的发展,如果能够把逻辑放在组件之内就会方便许多。因此@container提供了一种可以只针对组件空间大小的查询方式。

与媒体查询的对比

如何使用

目前只支持在chrome 开发者/金丝雀版本使用,在 chrome://flags/ 搜索 Container Queries ,选择开启即可。
在chrome中开启 以一个需求为例:如果文章卡片的父元素宽度大于400px则采用新的样式,则代码如下

<div class="o-grid">
  <div class="o-grid__item">
    <article class="c-article">
      <!-- content -->
    </article>
  </div>
  <div class="o-grid__item">
    <article class="c-article">
      <!-- content -->
    </article>
  </div>
</div>
.o-grid__item {
  contain: layout inline-size;
  /*需要给容器添加contain属性*/
  /*inline-size表示其只会变化宽度*/
  /*略显悲催的是,因为inline-size已经**
      **能适应大多使用场景了,所以block-size的支持就被推后了*/
}

.c-article {
  /* 默认样式 */
}

@container (min-width: 400px) {
/*当文章的父元素宽度大于400px时,修改子元素样式*/
  .c-article {
    /* 例如可以让文章以水平样式展示**
        ** 而不必挤在一个垂直卡片内 */
  }
}

总结起来,其实也就是分两步:

  1. 给父元素增减contain属性
  2. 像写媒体查询一样用 @container 来给子元素添加容器查询,写法一样

这是什么

DEMO

样例

Snipaste_2021-04-16_09-22-35.png
在CODEPEN上查看更多实现

Container

CSS Containment-MDN
CSS Containment 主要是通过允许开发者将某些子树从页面中独立出来,从而提高页面的性能。如果浏览器知道页面中的某部分是独立的,就能够优化渲染(也就是只重新渲染容器)并获得性能提升。

none
表示元素将正常渲染,没有包含规则。
strict
表示除了 style 外的所有的包含规则应用于这个元素。等价于 contain: size layout paint
content
表示这个元素上有除了 sizestyle 外的所有包含规则。等价于 contain: layout paint
size
表示这个元素的尺寸计算不依赖于它的子孙元素的尺寸。
layout
表示元素外部无法影响元素内部的布局,反之亦然。
style
即便有些样式会对除了对自己或子孙的元素产生影响,都不会让效果离开容器。当前处于 at-risk ,不一定被所有浏览器支持
Indicates that, for properties that can have effects on more than just an element and its descendants, those effects don't escape the containing element. Note that this value is marked "at-risk" in the spec and may not be supported everywhere.
paint
表示这个元素的子孙节点不会在它边缘外显示。如果一个元素在视窗外或因其他原因导致不可见,则同样保证它的子孙节点不会被显示。
Indicates that descendants of the element don't display outside its bounds. If the containing box is offscreen, the browser does not need to paint its contained elements — these must also be offscreen as they are contained completely by that box. And if a descendant overflows the containing element's bounds, then that descendant will be clipped to the containing element's border-box.

通过使用 contain: layout 你可以告诉浏览器,它只会影响到该节点内部的布局,所有内部的改变都不会影响外部页面的布局,这个容器建立了一个独立的格式化上下文。
通过使用 contain: size 可以让浏览器知道你会明确指定容器的大小。
因为我们明确要使用容器来进行查询,并且也要使用容器的大小不同来改变样式,所以我们可以作为容器的结点增加如下的样式。

.o-grid__item {
  contain: layout inline-size;
  /*inline-size先被支持,其他的,例如block-size日后应该也会支持*/
}

Container Queries

CSS Container Queries-MDN
在响应式设计的时候,虽然大多使用的是媒体查询,但是对于一些元素(例如用户信息卡片),可能不是根据视窗的大小来修改样式,而是根据其放置的位置来改变样式,也就是说,如果可以查询到组件的父元素(设置为 container )的样式,就可以以一种比较方便的方式进行样式的切换。

为啥要用

以前的方案-CSS Grid auto-fit

根据整个窗口大小调整元素大小的媒体查询暂且按下不表,我们来看看如何让容器自动调整子组件的样式。
以卡片列表为例,我们通常会使用 grid-template-columns 来调整,但是其各有缺点
对于auto-fill,子元素并不会在容器有剩余空间的时候自动调整大小。
auto-fit-fill的对比 但是对于auto-fit,子元素虽然会获得更加多的空间,但是我们有时候希望其能在不同大小的时候以不同的样式组织方式进行展示,而不是单纯的进行一个特别长的拉伸。

.o-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  grid-gap: 1rem;
}

use-case-1-2.png
这时候,我们就可以使用容器查询轻松的实现这种效果,其会在显示空间较小的时候展示为卡片,而在空间较大的时候以封面展示

use-case-1-3.png 效果请访问 codepen,代码如下

.o-grid__item {
  contain: layout inline-size;
}

@container (min-width: 400px) {
  .c-article {
    display: flex;
    flex-wrap: wrap;
  }
}
@container (min-width: 700px) {
  .c-article {
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 350px;
  }

  .card__thumb {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
}

不用js就能解决,科技的发展真厉害

还有什么使用场景

最后搬运一下Say Hello To CSS Container Queries中提到的使用场景,文档结构和代码敬请在原文查看

  • 在侧边栏以及主界面

use-case-1

  • 分页工具条

use-case-2

  • 个人信息卡片

use-case-3