Featured image of post 为未知而设计:响应式与内在式布局的CSS演进

为未知而设计:响应式与内在式布局的CSS演进

本文探讨了从固定宽度设计到响应式设计的演变,详细介绍了CSS Grid、Flexbox、容器查询和内在式布局等现代CSS技术,如何帮助创建适应未知设备和内容的灵活设计,实现真正的组件复用和未来验证。

为未知而设计

你公司的生存取决于其产品在你未曾想象的情境中和尚未存在的设备上工作的能力。 — Jeffrey Zeldman

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

Flash、Photoshop和响应式设计

当我刚开始设计网站时,首选软件是Photoshop。我创建一个960px的画布,开始设计布局,之后再将内容放入。开发阶段是关于使用固定宽度、固定高度和绝对定位来实现像素级完美精度。

Ethan Marcotte在2010年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>

放置在Sass网格行中的组件

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

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

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

组件通过媒体查询响应视口宽度

容器查询:我们的救星还是虚假的黎明?

容器查询长期以来一直被吹捧为对媒体查询的改进,但在撰写本文时,大多数浏览器都不支持。有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;
}

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

没有通常行容器的传统Grid布局

在创建允许内容演进的设计方面,这是一个巨大的进步,但灵活设计的真正改变者是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, “Designing Intrinsic Layouts”

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

Jen Simmons的"Designing Intrinsic Layouts"幻灯片

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

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

另一个2010时刻?

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

但它似乎没有移动得那么快;我还没有像响应式设计那样有那种改变职业生涯的时刻,尽管广泛分享的精彩演讲引起了我的注意。

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

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

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

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

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

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

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

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

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

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

内容优先

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

幸运的是,CSS不仅仅是布局,许多属性和值可以帮助我们将内容放在首位。Subgrid和伪元素如::first-line和::first-letter有助于将设计与标记分离,这样我们就可以创建允许更改的设计。

而不是像这样的旧标记黑客—

1
2
3
<p>
  <span class="first-line">First line of text with different styling</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 mixins实现,但通常仅限于从左到右切换到从右到左方向。

在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);
}

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

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

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

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

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

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

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

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

情境优先

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

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

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

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

负责任的设计

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

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

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

1
2
3
4
5
6
<img 
  src="image-file.jpg"
  srcset="large.jpg 1024w,
             medium.jpg 640w,
             small.jpg 320w"
     alt="Image alt text" />

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。这些检查允许我们提供适合多个场景的选项;它 less about one-size-fits-all and more about serving adaptable content。

在撰写本文时,媒体查询第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 设计