容器大小查询

CSS 容器查询仍在不断获得关注,我们中的许多人都在尝试使用它们,即使只是进行一些小实验或诸如此类的事情。它们得到了很好的浏览器支持,但还不够全面——足以证明在某些项目中使用它们是合理的,但可能还没有达到我们开始用崭新的容器大小查询取代过去项目中的媒体查询的程度。

不过它们确实很方便!事实上,我已经遇到过一些情况,我真的很想使用它们,但就是无法克服支持要求。如果我能够使用它们,在那些情况下它看起来会是这样的。

案例一:卡片网格

您不得不预料到这种情况,对吧?这是一种常见的模式,我们似乎都会在某个时候遇到它。但事实是,如果我能够使用容器大小查询而不是标准媒体查询,那么容器大小查询将为我节省大量时间并获得更好的结果。

假设你的任务是构建此卡片网格,要求每张卡片保持其 1:1 的宽高比:

 1:1 的宽高比:

一个四乘三的卡片元素网格,作为灰度模型。

它比看起来更难!问题是,根据视口宽度调整组件内容的大小会让您完全受制于组件对视口的响应方式 — 以及任何其他祖先容器对它的响应方式。例如,如果您希望卡片标题的字体大小在卡片达到某个内联大小时减小,则没有可靠的方法可以做到这一点。

我认为,您可以按单位设置字体大小vw,但组件仍与浏览器的视口宽度相关。当卡片网格用于可能没有相同断点的其他上下文中时,这可能会导致问题。

在我的实际项目中,我采用的 JavaScript 方法如下:

  1. 监听调整大小事件。
  2. 计算每张卡片的宽度。
  3. 根据每张卡片的宽度添加内联字体大小。
  4. 使用em单位对里面的所有内容进行样式设置。

看起来工作量很大,对吧?但这是一个稳定的解决方案,可以在不同环境下跨不同屏幕尺寸实现所需的缩放。

容器查询本来会更好,因为它们为我们提供了容器查询单位,例如cqw单位。您可能已经知道了,但1cqw等于1%容器宽度的。我们还有单位cqi,它是容器内联宽度的度量,也是cqb容器块宽度的度量。因此,如果我们有一个宽的卡片容器500px,则50cqw值计算为250px

如果我能够在卡片网格中使用容器查询,我可以将组件设置.card为容器:

.card { 
  container: card / size;
}

然后我可以使用以下单位设置一个按宽度padding缩放的内包装器:10%.cardcqw

.card__inner { 
  padding: 10cqw; 
} 

这是一种很好的方式,无论卡片在什么位置使用,都可以在任何给定的视口宽度下一致地缩放卡片边缘和内容之间的间距。无需媒体查询!

另一个想法?使用cqw单位来表示内部内容的字体大小,然后以em单位形式应用填充:

.card__inner { 
  font-size: 5cqw; 
  padding: 2em;
} 

5cqw10cqw是一个任意值 — 只是我确定的一个值。由于em单位与字体大小有关,因此填充仍然等于.card__inner

你明白了吗? 是2em相对于在同一个容器上5cqw设置的字体大小的。容器的工作方式与我们习惯的不同,因为单位是相对于同一元素的。但我很快注意到,容器查询单位与最近的父级(也是容器)相关。emfont-size value

例如,在此示例中,5cqw不会根据元素的宽度进行缩放:.card

.card { 
  container: card / size; 
  container-name: card; 
  font-size: 5cqw; 
}

相反,它会根据定义为容器的最近父级进行缩放。这就是我设置.card__inner包装器的原因。

案例 2:交替布局

我在另一个项目中还需要另一个卡片组件。这一次,我需要卡片从横向布局过渡到纵向布局……然后回到横向,然后随着屏幕变小再次回到纵向。

显示卡片元素的四种状态,在不同断点处在纵向和横向布局之间变化。

我做了一件脏活,让这个组件在这两个特定的视口范围内变成纵向显示(向新的媒体查询范围语法致敬!),但问题是,它随后被锁定在它上面设置的媒体查询、它的父级以及可能响应视口宽度的任何其他内容上。我们希望它在任何条件下都能正常工作,而不必担心内容会在哪里中断!

由于以下规则,容器查询可以使这变得轻而易举@container

.info-card {
  container-type: inline-size;
  container-name: info-card;
}

@container info-card (max-width: 500px) {
  .info-card__inner {
    flex-direction: column;
  }
}

一次询问,无限流动性:

但请稍等!您可能需要注意一些事项。具体来说,在基于 prop 的设计系统中使用这样的容器查询可能会很困难。例如,此.info-card组件可能包含依赖 prop 来更改其外观的子组件。

这有什么大不了的?卡片的纵向布局可能需要替代样式,但您无法使用 CSS 更改 JavaScript 属性。因此,您可能会重复所需的样式。我实际上在另一篇文章中谈到了这一点以及如何解决它。如果您需要对大量样式使用容器查询,那么您可能需要围绕它们建立整个设计系统,而不是试图将它们强行塞入依赖媒体查询的现有设计系统中。

案例 3:SVG 笔触

这是我最近使用的另一个非常常见的模式,容器大小查询可以产生更精致的产品。假设你有一个锁定在标题中的图标:

即使没有媒体查询,也可以非常直接地根据标题的大小缩放图标。但问题是,SVGstroke-width可能太细,在较小尺寸下不易被注意到,而在较大尺寸下,超粗的描边又可能吸引太多注意力。

我必须创建并应用类到每个图标实例,以确定其大小和笔划宽度。如果图标位于采用固定字体大小样式的标题旁边,那么这样做还行,但当使用不断变化的流体类型时,这样做就不太好。

锁定一个六边形图标和标题,有三种不同的尺寸,从大到小。

标题的字体大小可能基于视口的宽度,因此 SVG 图标需要相应地调整其笔触在任何大小下的工作方式。您可以font-size通过设置单位来使笔触宽度相对于标题的宽度。但如果您需要坚持一组特定的笔触大小,那么这将行不通,因为它会线性缩放 —如果不诉诸视口宽度上的媒体查询,em就无法在某些点将其调整为特定值。stroke-width

但是,如果我当时有容器查询的话,我会这样做:

.icon {
  container: icon / size; 
  width: 1em; 
  height: 1em; 
}

.icon svg {
  width: 100%; 
  height: 100%; 
  fill: none; 
  stroke: #ccc; 
  stroke-width: 0.8; 
}

@container icon (max-width: 70px) {
  .icon svg {
    stroke-width: 1.5; 
  }
}
@container icon (max-width: 35px) {
  .icon svg {
    stroke-width: 3;
  }
}

比较实现并查看容器查询版本如何根据容器的宽度将 SVG 的描边捕捉到我想要的特定宽度。

https://codepen.io/anon/embed/RwJxyro?height=800&theme-id=1&slug-hash=RwJxyro&default-tab=css,result

奖励:其他类型的容器尺寸查询

好吧,我实际上还没有在实际项目中遇到过这种情况。但当我梳理有关集装箱查询的信息时,我注意到我们可以查询与集装箱大小或物理尺寸相关的其他内容。

我见过的大多数例子都查询了、、widthmax-width、、min-width和,正如我在本文中所做的那样。heightblock-sizeinline-size

@container info-card (max-width: 500px) {
  .info-card__inner {
    flex-direction: column;
  }
}

但是MDN 还概述了我们可以查询的另外两件事orientation。第一件事是完全合理的,因为我们在媒体查询中一直使用它。容器查询也一样:

@media screen (orientation: landscape) { 
  .info-card__inner {
    /* Style away! */
  }
} 

@container info-card (orientation: landscape) { 
  .info-card__inner {
    /* Style away! */
  }
} 

另一个呢?aspect-ratio不管你信不信,它是:

@container info-card (aspect-ratio: 3/2) { 
  .info-card__inner {
    /* Style away! */
  }
} 

这是一个可编辑的演示,可以尝试这两个示例:

https://codepen.io/anon/embed/XWYVqEr?height=450&theme-id=1&slug-hash=XWYVqEr&default-tab=css,result&editable=true

我还没有真正找到这两种方法的良好用例。如果您有任何想法或觉得它可以在您的项目中有所帮助。

Author: swing

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注