面向未知的设计:响应式布局与CSS技术演进

本文深入探讨了响应式设计的演进历程,从早期基于像素的固定布局到现代CSS技术如Flexbox、Grid和Subgrid的应用。文章详细分析了媒体查询的局限性,展望了容器查询的前景,并介绍了内在设计理念如何通过内容优先的方法创建适应未知设备和场景的灵活组件。

面向未知的设计

你公司的生存能力取决于其产品能否在无法想象的情境下工作,并在尚未问世的设备上运行。 ——Jeffrey Zeldman

我不确定第一次听到这句话是什么时候,但这些年来它一直萦绕在我心头。如何为无法想象的情境创建服务?或者设计能在尚未发明的设备上工作的产品?

Flash、Photoshop与响应式设计

当我刚开始设计网站时,首选软件是Photoshop。我会创建一个960像素的画布,开始设计布局,之后再将内容填充进去。开发阶段则是通过固定宽度、固定高度和绝对定位来实现像素级精确度。

2010年,Ethan Marcotte在An Event Apart的演讲以及随后在A List Apart上发表的文章《响应式网页设计》改变了一切。我一听说响应式设计就被它吸引,但也感到恐惧。我之前引以为豪的那些充满神奇数字的像素级完美设计不再足够好。

我的第一次响应式设计经历并没有缓解这种恐惧。我的第一个项目是将现有的固定宽度网站改造成响应式。我艰难地认识到,你不能在项目最后才添加响应性。要创建流式布局,需要在设计阶段全程规划。

新的设计方式

设计响应式或流式站点始终是关于消除限制,生成可以在任何设备上查看的内容。它依赖于基于百分比的布局,我最初通过原生CSS和工具类实现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
.column-span-6 {
  width: 49%;
  float: left;
  margin-right: 0.5%;
  margin-left: 0.5%;
}

.column-span-4 {
  width: 32%;
  float: left;
  margin-right: 0.5%;
  margin-left: 0.5%;
}

.column-span-3 {
  width: 24%;
  float: left;
  margin-right: 0.5%;
  margin-left: 0.5%;
}

然后使用Sass,这样我就可以利用@includes来重用重复的代码块,并回归更具语义化的标记:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
.logo {
  @include colSpan(6);
}

.search {
  @include colSpan(3);
}

.social-share {
  @include colSpan(3);
}

媒体查询

响应式设计的第二个要素是媒体查询。没有它们,内容会缩小以适应可用空间,无论内容是否保持可读性(采用移动优先方法时出现了完全相反的问题)。

媒体查询通过允许我们添加断点来防止这种情况,设计可以在这些断点处适应。像大多数人一样,我开始时设置了三个断点:一个用于桌面,一个用于平板,一个用于手机。多年来,我增加了更多断点,用于平板手机、宽屏等。

多年来,我愉快地以这种方式工作,并在此过程中提高了设计和前端技能。我遇到的唯一问题是修改内容,因为有了我们的Sass网格系统,网站所有者无法在不修改标记的情况下添加内容——这对小企业主来说可能是个难题。这是因为网格中的每一行都是使用div作为容器定义的。添加内容意味着创建新的行标记,这需要一定的HTML知识。

行标记是早期响应式设计的主要内容,存在于所有广泛使用的框架中,如Bootstrap和Skeleton。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<section class="row">
  <div class="column-span-4">1 of 7</div>
  <div class="column-span-4">2 of 7</div>
  <div class="column-span-4">3 of 7</div>
</section>

<section class="row">
  <div class="column-span-4">4 of 7</div>
  <div class="column-span-4">5 of 7</div>
  <div class="column-span-4">6 of 7</div>
</section>

<section class="row">
  <div class="column-span-4">7 of 7</div>
</section>

当我从为中小型企业构建网站的设计机构转到在内部团队中工作,负责一系列相关网站时,另一个问题出现了。在这些角色中,我开始更多地与可重用组件打交道。

我们对媒体查询的依赖导致组件与常见的视口尺寸绑定。如果组件库的目标是重用,那么这是一个真正的问题,因为你只能在设计的设备与模式库中使用的视口尺寸对应时使用这些组件——在这个过程中并没有真正达到“尚未问世的设备”的目标。

然后是空间的问题。媒体查询允许组件基于视口大小进行适应,但如果我把一个组件放入侧边栏,如下图所示呢?

容器查询:救世主还是虚假的黎明?

容器查询长期以来一直被吹捧为对媒体查询的改进,但在撰写本文时,大多数浏览器尚不支持。有JavaScript的变通方案,但它们可能产生依赖和兼容性问题。容器查询的基本理论是,元素应该根据其父容器的大小而不是视口宽度来改变,如下图所示。

支持容器查询的最大论点之一是,它们帮助我们创建真正可重用的组件或设计模式,因为它们可以被拾取并放置在布局的任何地方。这是朝着基于组件的设计形式迈出的重要一步,该形式可以在任何设备上以任何尺寸工作。

换句话说,用响应式组件取代响应式布局。

容器查询将帮助我们从设计响应浏览器或设备大小的页面,转向设计可以放置在侧边栏或主内容中并相应响应的组件。

我担心的是,我们仍然使用布局来决定设计何时需要适应。这种方法总是有限制性的,因为我们仍然需要预定义的断点。因此,我对容器查询的主要问题是,我们如何决定何时更改组件使用的CSS?

脱离上下文和真实内容的组件库可能不是做出这个决定的最佳场所。

如下图所示,我们可以使用容器查询为特定的容器宽度创建设计,但如果我想基于图像大小或比例更改设计呢?

在这个例子中,容器的尺寸不应该是决定设计的因素;图像才是。

在获得可靠的跨浏览器支持之前,很难确定容器查询是否会成功。响应式组件库肯定会改变我们的设计方式,并提高重用和大规模设计的可能性。但也许我们总是需要调整这些组件以适应我们的内容。

CSS正在改变

当容器查询的争论持续时,CSS已经有了许多进步,改变了我们思考设计的方式。用像素测量的固定宽度元素和用于拼凑布局的浮动div元素的日子早已一去不复返,与表格布局一起成为历史。Flexbox和CSS Grid彻底改变了网页布局。我们现在可以创建在空间不足时换行到新行的元素,而不是在设备改变时。

1
2
3
4
5
.wrapper {
  display: grid;
  grid-template-columns: repeat(auto-fit, 450px);
  gap: 10px;
}

repeat()函数与auto-fit或auto-fill配对,允许我们指定每列应使用多少空间,同时由浏览器决定何时将列溢出到新行。类似的事情可以用Flexbox实现,因为元素可以换行多行并“伸缩”以填充可用空间。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
.wrapper {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
}

.child {
  flex-basis: 32%;
  margin-bottom: 20px;
}

所有这些的最大好处是你不需要将元素包裹在容器行中。没有行,内容不会以同样的方式与页面标记绑定,允许在不进行额外开发的情况下删除或添加内容。

这是在创建允许内容演化的设计方面的一大进步,但灵活设计的真正改变者是CSS Subgrid。

还记得精心制作完美对齐界面的日子吗?结果客户几乎一获得CMS访问权限就添加了一个难以置信的长标题,如下图所示?

Subgrid允许元素响应其自身内容和兄弟元素内容的调整,帮助我们创建更能适应变化的设计。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
.wrapper {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
     grid-template-rows: auto 1fr auto;
  gap: 10px;
}

.sub-grid {
  display: grid;
  grid-row: span 3;
  grid-template-rows: subgrid; /* 将行设置为父网格 */
}

CSS Grid允许我们分离布局和内容,从而实现灵活的设计。同时,Subgrid允许我们创建可以适应变化内容的设计。在撰写本文时,Subgrid仅在Firefox中支持,但上述代码可以通过@supports特性查询实现。

内在布局

我不能不提到内在布局,这是Jen Simmons创造的术语,用于描述用于创建响应可用空间的布局的新旧CSS特性的混合。

响应式布局使用百分比实现灵活列。另一方面,内在布局使用fr单位创建灵活列,这些列永远不会缩小到使内容无法阅读的程度。

fr单位是一种方式,可以说我希望你以这种方式分配额外空间,但……永远不要让它比里面的内容小。 ——Jen Simmons,《设计内在布局》

内在布局还可以利用固定和灵活单位的混合,允许内容决定它占据的空间。

内在设计突出的原因是,它不仅创建了能够经受未来设备考验的设计,而且有助于在不失去灵活性的情况下扩展设计。组件和模式可以被提取和重用,而不需要具有与先前实现相同的断点或相同数量的内容。

我们现在可以创建适应其拥有空间、其中内容以及周围内容的设计。通过内在方法,我们可以构建响应式组件,而不依赖于容器查询。

另一个2010时刻?

在我看来,这种内在方法应该和十年前的响应式网页设计一样具有开创性。对我来说,这是另一个“一切改变”的时刻。

但它似乎没有发展得那么快;尽管有广泛分享的精彩演讲引起了我的注意,但我还没有体验到与响应式设计相同的职业改变时刻。

一个原因可能是我现在在一个大型组织工作,这与我2010年在设计机构的工作截然不同。在我代理机构的日子里,每个新项目都是一张白纸,一个尝试新事物的机会。如今,项目使用现有工具和框架,并且通常是对具有现有代码库的现有网站的改进。

另一个原因可能是我现在对变化准备得更充分。2010年,我对设计整体还是新手;这种转变是可怕的,需要大量学习。而且,内在方法并不完全是全新的;它是关于以不同的方式使用现有技能和现有CSS知识。

你不能用框架解决内容问题

内在设计采用稍慢的另一个原因可能是缺乏可快速启动变化的快速修复框架解决方案。

十年前,响应式网格系统无处不在。使用像Bootstrap或Skeleton这样的框架,你手头就有一个响应式设计模板。

内在设计和框架并不那么契合,因为拥有选择单位的好处在于创建布局模板时是一种障碍。内在设计的美妙之处在于结合不同的单位并尝试技术以为你的内容获得最佳效果。

然后是设计工具。我们可能都在职业生涯的某个阶段使用过桌面、平板和移动设备的Photoshop模板,将设计放入并展示网站在所有三个阶段的外观。

你现在如何做到这一点,每个组件响应内容,布局在需要时灵活变化?这种类型的设计必须在浏览器中进行,我个人非常喜欢这一点。

关于“设计师是否应该编码”的争论是另一个持续多年的问题。在设计数字产品时,我们至少应该为内容的最佳和最差情况设计。在基于图形的软件包中这样做远非理想。在代码中,我们可以添加更长的句子、更多的单选按钮和额外的标签,并实时观察设计如何适应。它仍然有效吗?设计是否过于依赖当前内容?

就个人而言,我期待内在设计成为设计标准的那一天,当设计组件可以真正灵活,适应其空间和内容,而不依赖于设备或容器尺寸。

内容优先

内容不是恒定的。毕竟,要为未知或意外设计,我们需要考虑内容变化,就像我们之前的Subgrid卡片示例,它允许卡片响应其自身内容和兄弟元素内容的调整。

幸运的是,CSS除了布局还有更多,许多属性和值可以帮助我们以内容为先。Subgrid和伪元素如::first-line和::first-letter有助于将设计与标记分离,这样我们就可以创建允许变化的设计。

而不是像这样的旧标记技巧——

1
2
3
<p>
  <span class="first-line">第一行文本具有不同的样式</span>...
</p>

——我们可以基于内容出现的位置来定位它。

1
2
3
4
5
6
7
.element::first-line {
  font-size: 1.4em;
}

.element::first-letter {
  color: red;
}

CSS更大的补充包括逻辑属性,它改变了我们使用逻辑尺寸(开始和结束)而不是物理尺寸(左和右)构建设计的方式,CSS Grid也通过min()、max()和clamp()等函数做到这一点。

这种灵活性允许根据内容进行方向性改变,这是当我们需要以多种语言呈现内容时的常见要求。过去,这通常通过Sass混合宏实现,但通常仅限于从左到右切换到从右到左的方向。

在Sass版本中,需要设置方向变量。

1
2
3
4
5
$direction: rtl;
$opposite-direction: ltr;

$start-direction: right;
$end-direction: left;

这些变量可以用作值——

1
2
3
4
body {
  direction: $direction;
  text-align: $start-direction;
}

——或作为属性。

1
2
margin-#{$end-direction}: 10px;
padding-#{$start-direction}: 10px;

然而,现在我们有了原生逻辑属性,消除了对Sass(或类似工具)和预规划的依赖,预规划需要在整个代码库中使用变量。这些属性也开始打破设计与严格物理尺寸之间的紧密耦合,为语言和方向的变化创造更多灵活性。

1
2
margin-block-end: 10px;
padding-block-start: 10px;

还有像text-align这样的属性的原生开始和结束值,这意味着我们可以用text-align: start替换text-align: right。

像前面的例子一样,这些属性有助于构建不局限于一种语言的设计;设计将反映内容的需求。

固定与流动

我们简要介绍了在内在布局中将固定宽度与流动宽度结合的力量。min()和max()函数是类似的概念,允许你指定一个固定值和一个灵活的替代方案。

对于min(),这意味着设置一个流动的最小值和一个最大的固定值。

1
2
3
.element {
  width: min(50%, 300px);
}

只要元素的宽度不超过300px,上图中的元素将是其容器的50%。

对于max(),我们可以设置一个灵活的最大值和一个最小的固定值。

1
2
3
.element {
  width: max(50%, 300px);
}

现在,只要元素的宽度至少为300px,它将是其容器的50%。这意味着我们可以设置限制,但允许内容响应可用空间。

clamp()函数通过允许我们使用第三个参数设置一个首选值来构建于此。现在我们可以允许元素在需要时收缩或增长,而不会达到无法使用的程度。

1
2
3
.element {
  width: clamp(300px, 50%, 600px);
}

这次,元素的宽度将是其容器的50%(首选值),但永远不会小于300px,也永远不会大于600px。

有了这些技术,我们有了一个内容优先的响应式设计方法。我们可以将内容与标记分离,意味着用户所做的更改不会影响设计。我们可以通过规划语言或方向的意外变化来开始未来验证设计。并且我们可以通过设置期望尺寸 alongside 灵活替代方案来增加灵活性,允许更多或更少的内容正确显示。

情境优先

得益于我们目前讨论的内容,我们可以通过改变我们的方法来覆盖设备灵活性,围绕内容和空间设计,而不是迎合设备。但是Jeffrey Zeldman引用的最后一部分,“……你无法想象的情境”呢?

为坐在台式电脑前的人设计与为在耀眼阳光下穿过拥挤街道使用手机的人设计是非常不同的。情境和环境很难规划或预测,因为它们随着人们应对自己独特的挑战和任务而变化。

这就是为什么选择如此重要。一种尺寸永远不适合所有人,所以我们需要为多种场景设计,为所有用户创造平等的体验。

幸运的是,我们可以做很多事情来提供选择。

负责任的设计

“世界上有些地方移动数据昂贵得令人望而却步,宽带基础设施很少或没有。” Chris Ashton,《我在50MB预算下使用网络一天》

我们做出的最大假设之一是,与我们的设计互动的人拥有良好的wifi连接和宽屏显示器。但在现实世界中,我们的用户可能是通勤者,在火车或其他交通工具上旅行,使用可能经历连接中断的较小移动设备。没有什么比无法加载的网页更令人沮丧了,但我们可以帮助用户使用更少的数据或处理零星的连接。

srcset属性允许浏览器决定提供哪个图像。这意味着我们可以创建更小的“裁剪”图像在移动设备上显示,从而使用更少的带宽和数据。

1
2
3
4
5
6
<img 
  src="image-file.jpg"
  srcset="large.jpg 1024w,
             medium.jpg 640w,
             small.jpg 320w"
     alt="图片替代文本" />

preload属性也可以帮助我们思考媒体如何以及何时下载。它可以用来告诉浏览器任何需要高优先级下载的关键资源,提高感知性能和用户体验。

1
2
<link rel="stylesheet" href="style.css"> <!--标准样式表标记-->
<link rel="preload" href="style.css" as="style"> <!--预加载样式表标记-->

还有原生懒加载,指示仅在需要时才下载的资源。

1
<img src="image.png" loading="lazy" alt="…">

有了srcset、preload和懒加载,我们可以开始根据用户所处的情境定制他们的体验。然而,这些都没有做的是允许用户自己决定他们想要下载什么,因为决定通常由浏览器做出。

那么我们如何让用户控制呢?

媒体查询的回归

媒体查询一直远不止设备尺寸。它们允许内容适应不同的情境,屏幕尺寸只是其中之一。

我们长期以来能够检查媒体类型,如print和speech,以及特性,如hover、resolution和color。这些检查允许我们提供适合多个场景的选项;与其说是一刀切,不如说是提供可适应的内容。

在撰写本文时,媒体查询第5级规范仍在开发中。它引入了一些非常令人兴奋的查询,将来会帮助我们为许多其他意外情境设计。

例如,有一个light-level特性,允许你在用户处于阳光或黑暗时修改样式。与自定义属性配对,这些特性允许我们快速为特定环境创建设计或主题。

1
2
3
4
5
6
7
8
9
@media (light-level: normal) {
  --background-color: #fff;
  --text-color: #0b0c0c;  
}

@media (light-level: dim) {
  --background-color: #efd226;
  --text-color: #0b0c0c;
}

第5级规范的另一个关键特性是个性化。不是为每个人创建相同的设计,用户可以选择适合他们的内容。这是通过使用prefers-reduced-data、prefers-color-scheme和prefers-reduced-motion等特性实现的,后两个已经享有广泛的浏览器支持。这些特性利用通过操作系统或浏览器设置的偏好,这样人们就不必花时间使每个访问的网站更可用。

像这样的媒体查询超越了浏览器做出的选择,将更多控制权授予用户。

期待意外

最后,我们应该始终期待的一件事是事情会变化。设备尤其变化得比我们跟上的速度更快,可折叠屏幕已经上市。

我们不能以同样的方式为这个不断变化的景观设计,但我们可以为内容设计。通过将内容放在首位,并允许该内容适应其周围的任何

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计