容器查询给设计带来的变化

发布于 大漠

前段时间我们花了几篇篇幅的内容来介绍 CSS 容器查询特性的使用,并且介绍了现代 Web 布局技术让我们离 CSS 容器查询更近一步,但这些内容都只是和大家聊的是如何使用,以及其对 Web 开发者带来的变化。在介绍 CSS 容器查询特性的时候,也提到过,CSS 容器查询特性除了给 Web 开发者带来变化的同时也给 Web 设计者带来变化。那么,今天我们来和大家一起聊聊 CSS 容器查询给设计将带来什么样的变化。感兴趣的同学,请继续往下阅读。

Web 设计的演进

1990的12月20日,欧洲核子研究组织的科学家蒂娒.伯纳斯.李在瑞士的研究中心给人类创建了第一个 Web 页面至今有 31 年了。在这些年当中,Web 的布局从无任何布局形式到表格布局、再到浮动布局,以及到现在主流的 Flexbox,Grid 布局,经历了多次的演变。对于 Web 设计也是一样的,在不同的时期也在不断的变化:

  • 一维布局:设计元素大多数是按顺序排列
  • 二维布局:单元格中有放置元素的网格,具有更多的自由性
  • 一个“新的维度”,可以像平面设计工具一样的自由地定位元素、重叠

事实上,这个演变还在或将会随着时代的不同继续演变。比如十年前的响应式设计和今天要聊的容器查询特性给 Web 设计带来的变化。

响应式设计的现状

要聊容器查询将会给 Web 设计带来什么样的变化,就绕不开响应式设计。那我们就从响应式设计开始聊起。

自从 2010 年 @Ethan Marcotte 首次提出 Web 响应式设计(RWD)的设计理念,Web 设计就进入现代 Web 布局时代。随之 CSS 媒体查询特性的到来,Web 开发者可以通过视窗宽度、设备特性、用户喜好等为用户呈现不同的 UI 效果。

特别是随着 CSS 的 FlexboxGridShapes 等特性的到来,Web 开发者和Web设计可发挥的空间越来越大,比如构建带有响应式的不规则的Web布局杂志、报刊等布局):

抛开这些不规则的布局不说,回到那种规规矩矩的方块布局中。Web 设计师还是会因为不同的设备终端(特别是在当今移动端为先的年代)提供不同的设计,比如为不同的设备视窗尺寸(如手机、平板和PC端)提供不同的设计稿:

针对上图的三种不同展示方式,Web 开发者使用 CSS 媒体查询特性可以轻易实现这三种不同的布局。

就上图的效果,不难发现,这是同一个组件在不同断点下的三种不同 UI 设计,作为一名 Web 设计师,上面的设计稿已经将表达清楚了它的多种布局。对于 Web 开发者来说,需要针对该组件创建三种不同的样式风格,并且每一种都是独一无二的。

/* Mobile First */
.card {

}

.card--hero {

}

/* Tablet */
@media (min-width: 700px) {
    .card {

    }

    .card--hero {

    }

    .card--vertical {

    }
}

/* Laptop and Desktop */
@media (min-width: 1024px) {
    .card {

    }

    .card--hero {
        
    }

    .card--vertical {
        
    }
}

详细代码请在CodePen上查看:

改变视窗大小,你将看到的效果如下:

上面的Demo展示了三种不同风格的卡片效果(基于同一个组件),但这三种风格差异是取决于浏览器视窗宽度(媒体查询),意味着卡片组件不能根据其父容器宽度去调整 UI 风格。这就限制了开发者 只能在视窗宽度大于某个特定值时(断点) 使用一个组件的特定样式。例如视窗宽度到达 700px 或大于700px时,卡片组件从默认的.card(水平)状态切换到垂直(.card__vertical)状态。也就是说,如果我们想在小于700px宽度的视窗下,使用垂直状态(.card-vertical)卡片效果是不行的:

不过,现实总是残酷的。服务端吐出的数据(内容)比设计师预期的少,比如只有一张、两张、三张或更多,而设计稿中包含三张,这种情况之下,Web开发的实现的效果可能会像下图这样:

正如上图所示,如果只有一张卡片数据吐出的时候,整个卡片宽度会扩展与容器宽度相等(拉伸)。此时,卡片宽度太宽导致用于卡片上的缩略图被拉抻,有可能会使缩略图变得模糊。

有关于这方面的探讨,曾在《现代Web技术让我们离容器查询更近一步》一文中有详细阐述过!

事实上呢?这只是Web开发者的一厢情愿,设计师真正的意图可能是像下图这样:

在这种场景之下,使用 CSS 媒体查询特性实现起来会比较棘手,但使用 CSS 容器查询特性,就会容易地多,我们可以通过查询卡片父容器来决定如何显示卡片去解决这些问题。

因此,我们将解决思路转移到容器查询特性上!

什么是容器查询

首先,把卡片放到一个容器元素中,比如.card__container

<!-- HTML -->
<div class="card__container">
    <div class="card">
        <img src="https://picsum.photos/2568/600?random=1" width="2568" height="600" alt="" class="card__thumbnail" />
        <div class="card__badge">Must Try</div>
        <h3 class="card__title">Best Brownies in Town</h3>
        <p class="card__describe">High quality ingredients and best in-class chef. Light, tender, and easy to make~</p>
        <button class="card__button">Order now</button>
    </div>
</div>

也就是说,当卡片组件被放在一个容器中时,代表着它被包含在该容器中,比如上面代码中的.card__container。这也意味着,我们可以使用 CSS 的 container来查询.card__container 的宽度,并在@container.card 设置不同的样式规则。从而达到设计师真正的意图:

比如,容器宽度分别在 >400px>550px>700px时为.card设置不同样式:

代码可能像下面这样:

/* Default */
.card {
    // ...
}

/* CSS Container Queries*/

.card__container {
    contain: layout inline-size style;
}

/* container's width > 400px*/

@container (min-width: 400px) {
    .card {
        // ...
    }
}

/* container's width > 550px*/

@container (min-width: 550px) {
    .card {
        // ...
    }
}

/* container's width > 700px*/

@container (min-width: 700px) {
    .card {
        // ...
    }
}

详细代码请在Codepen上查看:

效果如下:

有关于CSS容器查询特性更详细的介绍,可以移步阅读下面几篇文章:

媒体查询 vs. 容器查询

正如前面两个示例所示,媒体查询查询的是浏览器视窗宽度,而容器查询查询的是组件容器的宽度。这个容器可以是组件的父元素,也可以是其祖先元素。也就是说,如果需要的话,可以在组件顶层容器上进行查询。用下图来可以很清晰的阐述媒体查询和容器查询的差异:

注意,CSS 媒体查询不仅仅是查询视窗宽度,还可以查询其他的,比如媒体设备用户偏好等;同样的,CSS容器查询也不仅仅查询容器尺寸,他也可以查询容器的样式和状态,只不过目前为止仅支持查询容器尺寸!

容器查询解决的是什么问题?

众所周知,响应式设计的概念的核心是 CSS 媒体查询的出现,它允许开发者根据浏览器视窗的尺寸来设置各种样式规则。也正因此,响应式设计和CSS媒体查询开启了更多的 Web 布局解决方案,以及多年来围绕响应视窗尺寸创建的最佳实践。而且,近些年来,设计系统和组件库也得到了更广泛的普及。对于更多开发者而言,更大的期望是:

一次建成,随地部署

这也意味着一个单独开发的 Web 组件可以在任何情况下工作,以使建立复杂的界面更加有效和一致。只不过,这些组件会组合在一起,形成一个Web页面或Web应用界面。目前,在只有媒体查询的情况下,往往需要额外的一层来协调跨视窗大小变化的组件的突变。在这些情况下,你可能不得不在更多的断点下使用更多的类名来设置不同的样式规则。甚至更惨的是,即使这样做仍然很多情况之下也无法达到最理想的UI表面。

很多时候,响应式Web设计不是关于浏览器视窗尺寸而是关于容器的尺寸大小,比如:

庆幸的是,CSS容器查询的出现,使我们超越了只考虑浏览器视窗尺寸的范围,并允许任何组件或元素对定义的容器尺寸做出响应。因此,虽然你可能仍然使用响应式来给Web页面布局,但Web页面的任何一个组件都可能通过容器查询来定义自己的样式变化。然后,它可以根据它是在一个窄的还是宽的容器中显示,来调整它的样式。

容器查询使我们不再只考虑浏览器视窗尺寸大小,而是允许任何组件或元素对定义的容器尺寸做出响应

也就是说,有了CSS容器查询,你就能以一种非常精确和可预测的方式定义一个组件的全部样式。

设计时考虑容器查询

虽然响应式设计给Web设计师带来了更多的可有性,但响应式设计还是有很多的局限性。对于Web设计师而言,更期待的是能够根据组件容器尺寸来提供不同的设计风格。依旧拿卡片组件来举例:

也就是说,CSS容器查询特性来了之后,作为一名Web设计师,在设计Web页面(或组件)时,就需要基于容器尺寸考虑如何设计。这样一来,可以向Web开发人员提供组件的细节和变化,Web开发人员也可以基于这些细节进行编码(进行开发)。

不过,这并不意味着容器查询特性之后响应式设计是就失去了意义。在未来,容器查询和响应式设计是共存的,简单地说,Web设计师在设计组件时可能会将组件分为以下几个部分:

  • 基于视窗(CSS媒体查询)
  • 基于容器(CSS容器查询)
  • 通用型(不受影响的组件)

比如:

在未来,Web设计师给Web开发者投喂的设计稿可能就会像下图这样了:

正确理解设计师的意图

继续拿前面提到的卡片组件为例,Web设计师可能在未来的设计中会提供向下图的卡片组件设计:

作为Web开发人员,看到上图设计效果,需要改变以往对设计图意图的理解,不能继续执着于基于视窗尺寸来调整组件UI。

上图是基于视窗的一种开发模式,需要为卡片组件设置不同的类名,并且基于视窗尺寸,在相应的类名下调整卡片组件UI。有了容器特性时,我们可以基于现代的Web布局技术,比如FlexboxGrid布局,让卡片组件基于其容器来调整其UI:

正如上图所示,可以基于视窗大小采用CSS媒体查询特性FlexboxGrid布局等技术改变卡片容器.card__container的大小,从而让卡片组件根据其容器尺寸大小做出相应响应。

拥有一个能根据其父容器尺寸做出响应(UI调整)的组件是非常有用的,正如你看到的,我们可以只构建一个组件,就可以满足不同视窗布局下的设计诉求!

容器查询不应该让组件变得复杂化

组件是有由很多个元素组合在一起构成的:

虽然容器查询特性到来,可以让组件根据其容器尺寸来做出响应,但要记住的是,做出响应变化应该要有一个度。如果过度设计的话,对于Web开发人员而言,与其使用容器查询特性来实现UI响应,还不如重新构建一个独立的全新组件。拿用户信息组件(UserProfile)为例,组件内部结构保持不变,或者至少不会增加新的结构,只需稍加调整,比如调整布局就可以实现不同的UI效果,或者让内部元素显示隐藏切换等。在这种情景之中,采用容器查询特性才能显现其魅力:

容器查询可用于哪些场景

通过这几篇文章,我们对CSS容器查询有了一定的了解:

接下来,我们来看看实际业务中,我们在哪些场景下使用CSS容器查询可以帮助我们快速构建,提高组件扩展性。

搜索表单

搜索表单在一些业务场景很常见,它会根据容器的宽度有不同的状态,这样的搜索组件就非常适用于CSS容器查询:

具体代码如下面Demo所示:

效果如下:

导航栏

Web页面导航栏是常见的一个组件,在宽屏幕和窄屏的时候,他会向用户呈现不同的 UI效果,如下图所示:

小站导航栏也有类似的效果,只不过是使用CSS媒体查询实现的,现在我们使用 CSS 容器查询来实现:

具体代码如下所示:

调整容器尺寸时,效果如下:

分页器

分页器组件(Pagination)类似于导航栏,也很适合于使用容器查询:

代码如下:

效果如下:

侧边栏

在一些Web应用的侧边栏(比如Gitlab的侧边栏,Facebook 聊天界面,其实Web版本的微信群也有点类似于Facebook聊天室)像下图这样的模式:

像上图这样的效果,我们可以使用 CSS 容器查询来实现。当有足够的空间时,侧边栏的列表会菜开,如果没有足够空间时,侧边栏只会展示Icon图标(或用户头像)。我们来实现一个简单的聊天室界面。

代码如下:

效果如下:

其他案例

适合于 CSS 容器查询的场景还有很多,比如下图所示:

这也是这篇文章留给大家的,感兴趣的同学可以尝试着使用CSS容器查询实现上图的效果。