自从著名设计师 Ethan Marcotte(@beep)在 A List Apart上发表了一篇名为《 Responsive Web Design》的文章之后,响应式网页设计(RWD,即 Responsive Web Design)的身影就出现在了公众面前。自此就有了响应式 Web 设计这个概念。从提出这个概念到今天已经有十多年的时间了。在这十多年来,CSS 也发生了巨大的变化,新增了很多新的特性,近两年尤其如此,近两年尤其如此(详细请参阅《2022 年的 CSS》一文)。这些变化,对于响应式Web设计的开发也有较大的改变。Una Kravets(@Una)大神,在2021的Google I/O 大会上的分享,提出 新的响应式:组件驱动式 Web 设计。Web 生态即将进入响应式 Web 设计的新时代,并转变我们对其含义的看法,也为会Web设计带来新的变化。组件式驱动 Web 设计(或开发)也被称为是下一代响应式 Web 设计(或开发)。如果你对这方面话题感兴趣的话,请继续往下阅读。
文章链接:
《Responsive Web Design》
《2022 年的 CSS》
《新的响应式:组件驱动式 Web 设计》
响应式Web设计的发展历程
既然要聊响应式Web设计,那么我们就花一点篇幅和时间简单地了解一下其发展历程。众所周知,自从 Tim Berners-Lee 创建第一个 Web页面(大约在1991年8月份左右)到90年代末,Web页面都是非常简陋的:
直到90年代末20年代初,Web设计和用户体验随着 CSS 的到来才慢慢地有了美感,Web页面看起来开始像我们今天使用的网站:
正如上图所示,越往后,Web 的 UI 越来越丰富,越来越漂亮。这也让 Web 开发人员不得不在布局、设计和排版等方面花费更多的时间。虽然 Web 开发人员为 Web 布局花费不少时间,但在这个过程中,也积累了很多不同的布局方法。在早期,Web 开发人员主要采用 固定宽度 和 流式布局 两种布局方案来实现 Web 页面的布局。特别是流式布局,自 Glenn Davis提出和推广之后,可谓是轰动一时,并且长期以来,都认为流式布局就是响应式Web布局。流式布局(Liquid Layout)可以调整Web页面尺寸以适应不同的显示器分辨率或浏览器窗口的大小。来看一个流式布局的简单示例:
Liquid Page Layout Example@nickpettitCodePen
但流式布局并不是完美的。使用流式布局的Web页面上,内容可能会溢出,在较小的屏幕上文字可能会换行,在较大的屏幕上可能会有很多不必的空白。
大多数流体布局在 800px x 600px到 1280px宽或更大的屏幕分辨率下看起来还不错。然而,如果我们能把它分割得更细一些,比如为 800px ~ 1024px、1024px ~ 1280px以及1280px以上的分辨率提供不同的定制布局,那效果会更佳。同样,对于 640px ~ 800px、320px ~ 640px、240px ~ 320px 以及 240px 以下的分辨率也可以定制不同的布局。
大约在 2004 年的时候,Cameron Adams(@themaninblue)在他的博文《Resolution dependent layout》提出了基于屏幕分辨率来动态构建Web布局,即 使用JavaScript根据浏览器窗口大小加载不同CSS文件。
注意,在所有 标签上title属性的值分别为 narrow、default 和 wide,并且在 dynamiclayout.js中有一个 DynamicLayout() 函数,将会根据 标签的title属性的值来调用不同的样式表:
function dynamicLayout(){
var browserWidth = getBrowserWidth();
// Narrow CSS rules
if (browserWidth < 640){
changeLayout("narrow");
}
// Normal (default) CSS rules
if ((browserWidth >= 640) && (browserWidth <= 960)){
changeLayout("default");
}
// Wide CSS rules
if (browserWidth > 960){
changeLayout("wide");
}
}
@Kevin Hale 的博文《 Dynamic Resolution Dependent Layouts》详细介绍该技术。
这种布局技术后来就以 Cameron Adams 博客文章标题命名,被称为 分辨率相关的布局。这种布局技术虽然会额外增加开发者(开发者需要为不同定制的布局提供不同样式表)工作量,但在CSS媒体查询流行之前还是很受欢迎的。它也被称为是早期的 CSS 媒体查询(通过JavaScript查询断点)。
虽然这种依赖动态分辩率布局的方案可以在不同的分辨率下提供更佳的体验,但随着 2005年08月10日 Opera 软件公司推出Opera Mini和 2007年01月09是第一台 iPhone手机的出现,市场上不同品牌,不同分辨率的移动端以及品牌商自己的Web浏览器就越来越多。
在这种环境之下,基于动态分辨率加载不同的样式表已不太现实,Web开发者不得不想出其他的方案来适应不同的屏幕尺寸。在很长一段时间,甚至到今日,为了适应不同屏幕的尺寸适配,为移动端单独创建一个网站,即 移动子域名网站。比如 Facebook的桌面版本和移动端版本,采用两个不同的域名来访问:
如此一来,开发人员要开发两个版本,相应的工作量就更大了,特别对于要快速响应和试错的Web应用来说,难度变得更大。
Web开发人员为了能改善这种现象,在 2010 年的时候,Ethan Marcotte(@beep)基于 John Allsopp(@johnallsopp)的 《网页设计的道(A Dao of Web Design)》,提出响应式Web设计思路(《Responsive Web Design》)。从此响应式Web设计(Responsive Web Design,简称 RWD)的身影就出现在了公众面前。
Ethan Marcotte(@beep)在《Responsive Web Design》中提到,响应式这个词源自于建筑学领域,原本指的是建筑物本身会“响应”实际的使用情况,来自我调整。在Web开发领域,“响应式”的意思就变成了,我们开发的Web页面会“响应”用户的设备尺寸而自动调整布局。在这篇文章中提到过,我们可以基于 流体网格(Fluid Grids)、灵活的图片(Flexible Images)和媒体查询(Media Queries) 三种技术来构建一个响应式Web网站或Web应用。
另外,Ethan Marcotte(@beep)构建了第一个具有响应式的Web网页,可以说是响应式Web设计经典案例之一(只可惜现在打不开了):
那通过这个示例,大家对响应式Web设计是什么样的有了一个初步的了解了。其实,Ethan Marcotte 在他的文章中就提到:
未来我们应该这样,随着访问网页的设备增加我们不会为每个设备单独设计,而只会做一份设计,把每个设备作为这份设计要照顾的一个方面。
也就是说,每个设备上都会去追求最佳的用户体验,设计会自动适应各个设备。在过去的时代,设计师精确的知道自己的媒介材质,比如一张 A4 纸张,一个名片,或者一张海报。但是在我们这个多屏时代,Web 设计必须有这样的思维,我们要为“任意尺寸”而去设计。
自 Ethan Marcotte 提出响应式Web设计思路以及基于不同断点(CSS 媒体查询替代JavaScript查询设备屏幕分辨)实现不同终端设备屏幕分辨率布局已有十多年了。大家都说,每十年就会看到一个生态系统迅速的发展,其中 CSS 也不另外。尤其是这两年,CSS 新增了很多新的特性,比如大家最为期待的容器查询特性@container、级联分层@layer 以及 CSS作用域 @scope 等。
正如 Una Kravets (@Una) 在2021的Google I/O 大会上的分享所说:
随着用户偏好查询、容器查询以及其他设备类型查询的出现(CSS新特性),Web社区即将进入响应式 Web 设计的新时代,并改变我们对其含义的看法。
换句话说,下一代响应式Web设计即将到来,或者说已经来到了我们身边。
响应式Web设计的现状
在聊一下代响应式Web设计之前,我们就要对响应式Web设计的现状有所了解。我们分别从两种不同角色(Web设计师和Web开发者)的角度来看响应式 Web 设计的现状。先从 Web 设计师的角色来聊。
时至今日,虽然已是移动终端的天下,但如果要给不同终端提供设计稿的话,Web 设计师还是会依旧为不同的设备终端提供不同的设计稿。比如,为不同的设备视窗尺寸(如手机,平板和桌面端)提供不同的设计稿:
我们来看下简化后的不同版本的设计线框图,如下所示:
在上图中,设计师为卡片(Card)提供了三种不同的 UI 效果。虽然卡片在不同设备视窗下有着不同的UI效果,但他们构成的元素是相同,都有卡片容器、卡片缩略图、卡片标题 和 卡片描述等:
作为Web设计师,你已经使用了多个版本的布局来展示同一个组件三种不同状态下的UI变化。可以说把足够多的信息传递给了Web开发者!到这一步,Web 设计师已给 Web 开发者提供了具有响应式的Web设计稿。
接下来,再从另一种角色(Web开发者)来看响应式Web设计的现状。对于 Web 开发者而言,要实现上图中三种状态下的卡片UI效果非常简单。借助 CSS 媒体查询特性,在不同断点下调整CSS样式规则即可:
/* Mobile First */
.card {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
/* Tablet */
@media (min-width: 700px) {
.card {
gap: 20px
}
}
/* Laptop and Desktop */
@media (min-width: 1024px) {
.card {
position: relative
}
.card__thumb {
position: absolute;
inset: 0;
}
}
这种方式只能适合于同一组件独立存在于不同版本下。就示例的设计稿来看,在桌面端有两种效果的卡片,为了满足该设计效果,我们需要额外添加一些类名,在不同状态下为卡片处理不同的UI效果:
CSS样式代码可能会是像下面这样:
/* Mobile First */
.card {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
/* Tablet */
@media (min-width: 700px) {
.card--vertical {
gap: 20px
}
}
/* Laptop and Desktop */
@media (min-width: 1024px) {
.card--featured {
position: relative
}
.card--featured .card__thumb {
position: absolute;
inset: 0;
}
}
看上去是不错,但问题是,只有当视窗宽度大于一个特定的值时(常指的分辨率断点值),相应的组件变体才会生效,比如当视窗宽度大于 700px时,.card--vertical卡片UI效果才生效;当视窗宽度大于1024px时,.card--featured卡片UI效果才生效。换句话说,如果要在平板端看到.card--featured卡片效果就无法看到,因为它的媒体查询在 1024px 或更大的视窗宽度下才会生效。
不仅如此,Web的内容是动态的,有的时候输出的内容可能和设计预定的卡片数量不相符合,那么在这种情况之下,要么会有一个空的空间,要么卡片会扩展以填补容器的剩余(或可用)空间。比如我们这个示例中,在视窗宽度为 700px 或更大的视窗宽度中,.card--vertical 和 .card--featured 都有可能出现这样的场景。
针对这样的场景,Web设计师可能更希望有额外的UI效果给卡片。这部分我们放到下一节来聊。简单地说,目前的响应式 Web 设计主要方案还是利用 CSS 媒体查询特性在不同的终端上提供布局的切换。虽然他能满足 Web 页面布局大部分场景,但也相对丧失了一些其他能力,比如说将响应式的样式注入到组件本身的能力。换句话说,基于视窗宽度查询构建的响应式Web页面,尤其是响应式组件,其能力是有限的。
CSS 媒体查询的不足
在上一节中我们一起探讨了 Web 开发者可以借助于 CSS 媒体查询特性来查询视窗宽度(或设备其他特性)为 Web 页面在不同终端提供差异化的布局。但也暴露出很多不足之处,甚至是明显的能力不足。就拿前面示例来说,卡片组件有三种差异化的UI效果,这些差异化的UI效果是取决于浏览器视窗宽度,也就意味着卡片组件不能根据其父容器宽度去调整 UI 风格。这就限制了开发者 只能在视窗宽度大于某个特定值时(断点) 使用一个组件的特定样式。例如视窗宽度到达 700px 或大于700px时,卡片组件从默认的.card(水平)状态切换到垂直(.card--vertical)状态。也就是说,如果我们想在小于700px宽度的视窗下,使用垂直状态(.card--vertical)卡片效果是不行的:
另外一点就是当服务端吐出的数据和设计师预设的数量不一致时,最终的Web效果有可能不是设计师期望的。比如只有一张卡片、两张、三张或更多,而设计稿中包含三张,这种情况之下,Web开发的实现的效果可能会像下图这样:
正如上图所示,如果只有一张卡片数据吐出的时候,整个卡片宽度会扩展与容器宽度相等(拉伸)。此时,卡片宽度太宽导致用于卡片上的缩略图被拉抻,有可能会使缩略图变得模糊。事实上呢?这只是Web开发者的一厢情愿,设计师真正的意图可能是像下图这样:
在这种场景之下,使用 CSS 媒体查询特性实现起来会比较棘手,但使用 CSS 容器查询特性,就会容易地多,我们可以通过查询卡片父容器来决定如何显示卡片去解决这些问题。
如果用一然话来概述的话:
虽然 Web 开发者可以使用全局的视窗信息来设置组件的样式,但组件仍然不能拥有自已的样式,而且当我们的设计系统基于组件而不是基于页面时,那么媒体查询就无能为力!
庆幸的是,生态系统正在改变,而且这两年尤其突出。比如说,我们一直期望的容器查询特性就如约而至。如果我们将组件自身的响应式样式思路从查询视窗转换到查询其祖先容器,是不是前面的问题就可以轻易解决。除此之外,早期的CSS媒体查询只能查询视窗宽度和媒体特性,但不能查询用户对设备喜好的设置。不过,这两年CSS媒体查询特性也有巨大的变化,我们除了查询媒介(设备)特性之外,也可以查询用户偏好设置。
正如 Una Kravets 所言:
Web生态再一次让CSS腾飞,这些新的特性将会让响应式Web设计注入新的能力。我们也将进入响应式设计的新时代,并转变我们对其含义的看法。
下一代响应式Web设计
当媒体查询被运用于 CSS 中时,“响应式Web设计”(Responsive Web Design)一词就被 Ethan Marcotte 在 2010年创造出来。从那时起,Web设计师和开发人员就开始使用响应式Web设计的方法来设计和开发一个沉浸式的Web页面或Web应用,以实时适配当今看上去无底洞的终端设备。
今天,当我们提到响应式Web设计时,首先想到的是Ethan Marcotte(@beep)的 《Responsive Web Design》博文中提到过的基于 流体网格(Fluid Grids)、灵活的图片(Flexible Images)和媒体查询(Media Queries) 三种技术来构建一个适应不同屏幕尺寸或不同移动终端设备的 Web 页面。
Web 开发者使用 CSS 媒体查询来改变整个页面的布局,并从上到下调整移动手机、平板电脑和桌面端的设计尺寸。这种方法很有效,而且效果很好,但它有一个明显的缺陷,即 整个屏幕同时响应,或者说整个页面同时响应。虽然该响应方式确实对用户的体验有一些较大的影响,但不能响应个别用户的需求,也缺乏将响应式注入组件本身。
好消息是,生态系统正在改变,而且进步非常迅速。Web 设计师和Web平台的工程师正在开始用一种新的响应式技术方案来构建Web页面,这种方案被称为组件驱动式Web设计(Component-Driven Web Design)。
组件驱动式Web设计
我们今天使用的响应式 Web 设计方法很快就会被认为是过时的,就像我们从90年代最初的基于表格的HTML开发过渡到现在的感觉一样。
Web 设计师现在要克服的挑战是,目前的响应式 Web 设计方法本质上是一种一刀切的方法,把整个页面和用户体验当作一个整体,没有任何个性化。
基于视窗的媒体查询(CSS 媒体查询)给我们提供了许多媒体查询的能力,但缺少为我们的Web设计提供精细度的能力,并创造一个对用户、他们的环境和他们在页面上采取的行动来说是独特的体验。我们也缺乏将响应式样式注入组件本身的能力。
这里所说的组件是Web上的元素,可以由其他设计元素的集合或分组组成。如果我们把组件看成是由积木组成的,并把这个概念应用到像幻灯片、卡片或内容块这样的常见UI元素的构造中,就会更容易理解,在不久的某一天,我们可能会把响应式设计样式注入单个组件或积木中,以定制和调整用户的体验,而不是把一套固定的样式和规则应用于整个页面的布局。
我们可以使用全局视窗信息来控制元素的字体大小和最大宽度等样式,或者调整这些组件的背景图像和布局,但它们仍然无法控制拥有自己的样式。当我们的响应式设计系统是基于组件的,而不是基于页面的时候,这种限制就更难了。
好消息是,全世界的Web设计师和开发人员正在努力改变响应式Web设计的生态系统。尽管为了改变我们对响应式设计的思考方式以及组件如何适应其周围环境,需要进行基本的设计思维过程的改变,但响应式设计专业人员处理响应式Web设计的方式正在快速变化。
现在为创新之火推波助澜的是CSS和灵活布局的快速发展,比如添加了一些新的查询规则、Flexbox 和 Grid布局。这里所取得的进步正在迅速迎来一个新的响应式Web设计的时代,而这个时代就在地平线之外。
CSS生态快速的发展,即将彻底改变响应式 Web 设计的概念!
现在,在我们被引入响应式 Web 设计这个激进的新概念的十多年后,我们又一次见证了响应式设计生态系统的演变,即 CSS新增的特性将直接基于组件而不是基于页面注入样式响应能力。这种能力被称为 组件驱动Web设计(Component-Driven Web Design),基于组件驱动的开发将会成为一种真正流行的开发模式。
为了理解这种开发模式的转变,并为即将到来的变化浪潮做好准备,让我们看看在响应式Web设计运动中我们可以期待的变化,以及这可能会如何改变我们对待响应式设计的概念。
响应用户的需求
你可能对基于视窗可视区域大小的媒体查询(通过 min-width、max-width、min-height、max-height、orientation 和 aspect-ratio 等)比较熟悉,比如:
@media (max-width: 45rem) {
/* 视窗小于 45rem */
}
@media (min-width: 45rem) {
/* 视窗大
这只是 CSS 的 @media 最基础的一部分规则,事实上,@media 规则大约包含了 24 个可供查询的特性,其中大约 19 个查询规则得到较好的支持,详细的可以阅读《图解CSS: CSS媒体查询》一文。在这些新增的查询特性中是用来改善用户体验的,比如 Media Queries Level 5规范中的第十一部分,能够让你根据用户自身的特定偏好和需求来设计 Web 体验。
也意味着这些新增的媒体查询特性允许你根据用户的偏好来调整用户的体验。
现在很多设备提供了一些用户偏好的设置。比如在 Mac 电脑上,用户可以根据自己喜好做一些设置:
CSS媒体查询提供了一些用户喜好的查询特性,这些特性可以识别出用户在系统上的偏好设置,帮助Web开发者构建更加健壮和个性化的 Web 体验,特别是对于那些具有可访问性需求的用户。
prefers-reduced-motion
Web页面或应用难免少不了用一些动效来点缀,但有些用户不喜欢这些动画效果,甚至对于少数用户来说,这些动效会让他们身体不适。这就是为什么现在大多数设备都支持一种方法让用户根据自己的喜好来做设置。
使用prefers-reduced-motion媒体查询用于检测用户的系统是否被开启了动画减弱功能。比如下面的这个示例,将会展示一组令人心烦的动画,不过当你开启了系统的“减少运动”后就能看到动画减弱的效果了。
.pulse {
animation: pulse 2s infinite;
}
@media screen and (prefers-reduced-motion: reduce) {
.pulse {
animation: none;
}
}
示例效果演示的是prefers-reduced-motion媒体特性如何让animation停止,其实CSS的transition也可以实现动画效果,加上并不是所有设备对动效都有一个很好的性能支持(毕竟动效是较耗性能的),因此,我们可以像下面这样来写CSS:
@media screen and (prefers-reduced-motion: reduce), (update: slow) {
* {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
}
}
这段代码强制所有使用动画持续时间或过渡持续时间声明的动画以人眼无法察觉的速度结束。当用户要求减少动画体验,或者设备屏幕刷新率较低时,比如廉价智能手机,它就能工作。
另外,Eric Bailey 在他的文章《Revisiting prefers-reduced-motion, the reduced motion media query》中提出了一个观点:
“并不是每一个可以访问网络的设备都可以呈现动画,或者流畅地呈现动画。”
对于刷新率低的设备来说,可能会导致动画出现问题,比如动画卡顿。这样的话,删除动画可能是更好的选择。我们可以将 prefers-reduced-motion 和 update 结合在一起使用:
@media screen and (prefers-reduced-motion: reduce), (update: slow) {
* {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
}
}
这段代码强制所有使用animation-duration或transition-duration声明的动画以人眼难以察觉的速度结束。当一个人要求减少动效体验,或者设备有一个刷新率较低的屏幕,比如电子墨水或廉价的智能手机,它就能发挥作用。
但需要注意的是,使用动态减弱并不意味着“没有动效”,因为动效在Web页面中传达信息能起到至关重要的作用。相反,你应该使用一个坚实的、去除非必须的动效基础体验去引导这些用户,同时逐步增强没有此项偏好设置的其他用户的体验。
如果你对减弱动效效果这方面技术感兴趣的话,还可以阅读:
Revisiting prefers-reduced-motion, the reduced motion media query
Meeting “2.2.2 Pause, Stop, Hide” with prefers-reduced-motion
Respecting Users’ Motion Preferences
Designing With Reduced Motion For Motion Sensitivities
Accessible Web Animation: The WCAG on Animation Explained
Accessible Animations in React
prefers-color-scheme
你可能知道了,macOS系统和iOS13之后,苹果设备具备Dark Mode效果,就是用户可以根据自己的喜好来选择系统提供的色系:
使用 prefers-color-scheme 查询特性可以让你对用户是否打开了设备上Dark Mode来做出响应。换句话说,给Web页面或应用添加Dark Mode只需要几行代码即可。首先我们默认加载的主题是亮色系,我们可以在 :root 中声明亮色系所需要的颜色,比如:
:root {
--text-color: #444;
--background-color: #f4f4f4;
}
然后通过媒体查询prefers-color-scheme: dark为暗色系重置所需要的颜色:
@media screen and (prefers-color-scheme: dark) {
:root {
--text-color: rgba(255,255,255,.8);
--background-color: #121212;
}
}
使用prefers-color-scheme 来定制不同外观主题时,还可以和theme-color 以及 color-scheme 结合起来使用。这将能控制系统应用的(比如浏览器)主题颜色:
而 color-scheme 这个 CSS 属性和的name为theme-color是相同的。它们都是让开发者更容易根据用户的喜好设置来控制 Web应用或页面的主题,即 允许开发者根据用户喜好设置添加特定的主题样式。其实 color-scheme 属性和 相应的标签与prefers-color-scheme相互作用,它们在一起可以发挥更好的作用。最重要的一点是, color-scheme完全决定了默认的外观,而prefers-color-scheme则决定了可样式化的外观 。
假设你有下面这样的一个简单页面:
Lorem ipsum dolor sit amet, legere ancillae ne vis.
页面上