内涵式网页设计的上下文间距(附代码示例)

167 阅读14分钟

用户的浏览环境是不可预测的。告诉其他开发者,看在上帝的份上,告诉你的设计师。让我们学习如何通过使用自适应、上下文的间隔技术与这种不可预测性共存。

2018年,Jen Simmons在她的演讲"Everything You Know About Web Design Just Changed"中介绍了 "内在网页设计 "这一术语。 她还分享了内在网页设计的原则,我们将以此为指导:

  1. 收缩和扩展--我们考虑我们的设计将如何适应可用空间的变化的方式
  2. 灵活性--主要使用flexbox和grid与较新的单元和功能相结合的方式,使我们的布局能够以不同的速度适应可用的空间
  3. 视口--能够使用视口的所有四边以及利用视口单元。

使用自适应布局技术是设计者、开发者和浏览器之间的一项信任工作。

本质设计的属性和功能

让我们先来回顾一下创建内在尺寸元素的基础要领。

夹子

clamp() ,这是一个通用的CSS函数,是内在网页设计的关键,它从2020年3月开始得到稳定的支持

Clamp接受三个值:最小值、理想值和最大值。这有效地让你提供灵活的约束。

clamp() 的诀窍在于那个理想值,其中必须使用动态单位(如视图宽度)来触发最小和最大之间的过渡。

你可能已经在流体排版的保护伞下遇到过clamp() ,它依赖于视口单位。这里有一组数值的例子。

font-size: clamp(1rem, 4vw, 3rem);

基于当前的计算值4vw,font-size 将随着视口的增长和缩小而调整。但它永远不会小于1rem 或大于3rem

我们将在后面回顾clamp 的更多用例,以了解我们的间距技术。

那么,我们如何去使用clamp() 进行内在的设计呢?我的建议是:

  • 设计者提供最小和最大值
  • 开发人员决定动态尺寸

最小最大值可以由设计令牌提供,如果你来自设计系统或使用提供尺寸斜坡的框架,这可能是一个熟悉的概念。当我们探索更多的技术时,我将继续指出设计标记的机会,因为它们是将我们的约束条件映射到设计和线框的极好方法。

最小和最大函数

min()max() 函数使我们能够提供与环境相关的选项。这些函数的支持程度与clamp() 基本上相同。

min()max() 都接受两个或多个值。对于min() 函数,浏览器将使用最小的计算值,反之,max() ,浏览器将使用最大的计算值。

min,max, 和clamp 的另一个特点是,我们可以进行额外的计算,而不需要嵌套的calc() 函数。所以对于下面的定义,我们要求浏览器在100vw - 3rem80ch 之间选择最小的值:

min(100vw - 3rem, 80ch)

这导致当视口<80ch100vw - 3rem 被选中,而当视口>80ch80ch 被选中。

如果我们采用这个规则并添加逻辑属性margin-inline ,设置为auto ,那么我们就有了一个真正现代的容器类。

.container {
  width: min(100vw - 3rem, 80ch);
  margin-inline: auto;
}

我们可以把我们的容器类更进一步,设置一个可选的自定义属性container-max ,使用80ch 作为回退。所以现在我们有一个现代的、超灵活的规则:

.container {
  width: min(100vw - 3rem, var(--container-max, 80ch) );
  margin-inline: auto;
}

要理解何时使用minmax ,可能会有点棘手。

让我们考虑一下max 的以下数值:

max(2rem, 4vh)

4vh 的计算值变得小于2rem 时,max 函数将选择2rem

因此,有效地,正在发生的事情是,选择的2rem 是你将允许这个规则的最小值。

现在让我们翻到min()

min(100%, 60ch)

min() 的这个规则意味着60ch实际上是这个元素的最大允许值。

如果你想知道为什么我们要使用minmax ,而不是单独列出相应的属性,那是因为我们可以在任何允许有数值的地方使用它们,而不仅仅是在尺寸上。

例如,background-size ,就像我们在CSS数学函数的实际应用中所探讨的那样,我们对clamp()min() 、和max()

合适/最小/最大-内容

接下来,我们有fit-content,min-content, 和max-content ,它们允许内在的大小。

对这些关键词的支持最好与width 属性搭配,而且你可能会发现需要使用前缀,这取决于你的受众。

让我们来比较一下这些尺寸关键字在应用于width 属性时对文本内容的呈现。

"内在关键词比较 "的CSS

.fit-content {
  width: fit-content;
}
.min-content {
  width: min-content;
}
.max-content {
  width: max-content;
}

宽度设置为适合内容

宽度设置为最小内容

宽度设置为最大内容

  • fit-content 增长到足以容纳其内容的程度
  • min-content 只增长到与最长的字的宽度相匹配,并将应用软包装
  • max-content 将继续增长到其内容所需的大小

乍一看,可能很难区分fit-contentmax-content ,所以让我们扩大文本值。

fit-content会增长但不会溢出

max-content有溢出的可能

说实话,我还没有发现min-contentmax-content 的很多用例。但fit-content 是一个顶级的属性。

在这个演示中,"警报 "有width: fit-content 。注意,它只增长到相当于它的max-content ,这比可用的内联空间要窄。

fit-content 的神奇之处在于,在不改变display 的值的情况下实现了内容相对的宽度,这意味着我们可以保存display 属性。而在这个流程背景下,警报元素可以继续使用块状行为,这意味着边距继续发挥作用。

网格单位和功能

伙计们--我们从2017年春天开始就有了CSS网格支持

CSS网格是在约束条件下实现灵活性的完美工具集。

在内在网页设计的主题下,我们将回顾我对网格的两大用途。

以下是最神奇的CSS定义,因为它创建了一个内在大小的布局网格。

grid-template-columns: repeat(auto-fit, minmax(30ch, 1fr));

我已经在其他几篇Modern CSS文章中写过了,但这里是摘要。除了ch 单位,这一行的所有内容都是CSS网格特有的:

  • repeat() 定义了一个循环模式,用于指定的网格轨道
  • auto-fit 是指根据定义的下一部分,创建尽可能多的轨道
  • minmax() 是一个CSS网格函数,定义了一个数值范围,用来计算每个轨道的大小,其中第一个数值是最小值,第二个数值是最大值。

下面是这个规则的操作。

"内在网格布局 "的CSS

.grid-layout {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--grid-min, 20ch)), 1fr));
}

在较大的尺寸下,会创建三个轨道,随着内联空间的减少,轨道空间也会减少。一旦轨道尺寸将元素挤压到低于30ch 的最小值,元素就会下降到被添加到或创建一个新的行。

在演示中,我们还增强了规则,以包括一个嵌套的min() 函数,并将100% 作为其中一个选项。这减少了溢出的机会,当空间需要更窄时,允许元素收缩到30ch 以下。我们还通过添加--grid-min 的可选自定义属性来控制 "断点",从而使这一规则能够缩放。

在下一条规则中,我们将使用fit-content() 的纯网格函数版本。使用fit-content() 函数意味着我们可以提供我们自己喜欢的最大值,如演示中的侧边栏,它的最大值是20ch

"fit-content()网格布局 "的CSS

.sidebar-layout {
  display: grid;
  grid-template-columns: fit-content(20ch) minmax(50%, 1fr);
}

总的来说,这个规则所产生的行为是,文章元素将减少,直到它达到50%的最小值。在这一点上,边栏将最终开始压缩,直到它达到相当于min-content

我们可以通过再次加入一个自定义属性来提高这个规则的灵活性。我喜欢这个定义,因为它可以容纳其他内容类型,比如这张图片。

fit-content() grid layout with image" 的CSS

.sidebar-layout {
  display: grid;
  grid-template-columns: fit-content(var(--sidebar-max, 20ch)) minmax(50%, 1fr);
}

只要记住,图片没有最小内容值这种东西,所以它将根据剩余的分配空间继续缩小。

内在的、上下文的间距

到目前为止,我们所谈到的是如何影响元素的空间占用和大小。现在终于到了谈论影响元素之间和周围的间距的时候了,这将帮助我们从内在的网页设计中获得最大的收益。

尽管除了像素之外还有很多单位可以使用,但我们仍然几乎只用像素来定义间距,也许有时会用雷姆。

像素是创建内在布局的最不灵活的单位。那么,如果我们能够为浏览器提供一个更好的标准来确定间距的大小呢?如果我们能在不使用媒体查询的情况下做到这一点呢?

关于间距,首先要承认的是,涉及间距的属性--间隙、填充和边距--有不同的目的。因此,让我们学习如何使用更合适的单位,并创建自适应的方法来处理元素之间和周围的间距。

填充

填充是用来处理单个盒子的间距的。我们的升级技术将使用clamp() 与百分数和rem

提醒一下,用于填充的百分比是相对于元素的内联尺寸计算的,所以对于clamp() ,我们可以使用百分比作为一个相对于元素的动态值。

下面是两种移动体验的比较--左边的使用像素来定义margin和padding,右边的使用clamp()

对于使用clamp() 的版本,注意到内联空间的增加,这导致了更舒适的阅读体验。

像素通常被设计成适用于 "桌面 "或宽屏环境。而更新它们的值将意味着创建一系列媒体查询控制的断点。因此,这里是我们使用clamp()min() 的改进的演示。

我们对文章元素和卡片都使用了相同的填充规则。在文章中的技巧是在60ch100% 之间进行切换,前者适用于大的内联空间,后者在窄的内联空间中没有额外的 "沟槽 "空间。

另一个受到这些规则积极影响的情况是《网络内容可及性指南》的回流成功标准,该标准定义了对浏览器放大到400%的期望。在这一点上,屏幕的计算宽度被假定为320px左右。不幸的是,我们没有一个缩放媒体查询,但任何影响接近320px的视口的规则都会影响这个上下文。因此,狭窄的布局将在高缩放下显示,并被允许有更理想的阅读线长。

我建议尝试创建padding自定义属性:

:root {
  --padding-sm: clamp(1rem,    3%, 1.5rem);
  --padding-md: clamp(1.5rem,  6%,   3rem);
  --padding-lg: clamp(3rem,   12%,   6rem);
}

中间的百分比值可能看起来有点神奇--而且,确实如此--但我发现一个不错的起点是将最大值的两倍作为百分比使用。而且,还有一些更多的设计象征性的机会

保证金

接下来是页边距,为了我们的目的,我们将明确地把它用于块状布局的间距,我指的是垂直间距。

我们将使用min() ,用视口单位和rem 。视口单位将允许创建上下文的间距。

这里是我们对页边距的基线规则:

.block-flow {
  margin-block-start: min(4rem, 8vh);
}

我从Andy Bell的流动规则中借用了 "流动 "一词,而 "块 "是因为该规则是使用margin-block-start 的逻辑属性来应用的。如果你还不熟悉逻辑属性,margin-block-startmargin-top 的逻辑伴侣。

min() 中,我们提供了4rem8vh 的值。所以你可以把4rem 看成是静态值,把8vh 看成是动态值。

下面是该规则在大与小的背景下的效果。从技术上讲,这些值的计算余地只在 "移动 "版本上节省了大约13px ,这可能看起来没有太大的影响:

the .block-flow rule as viewed on a laptop vs. a mobile phone

然而,再一次,在我们的缩放背景下,启用8vh 选项所带来的差异是相当积极的:

在这种情况下,减少不必要的空间是可取的。这个演示还强调了变焦环境和移动环境的区别:横向方向。相比之下,通常情况下,我们最关心的是在移动环境下的纵向设计。

因此,这里是我们的块流自定义属性的一个起点,在这里,再次简单地将静态的rem 值加倍,以产生vh 值,效果相当好。正如我们所探讨的,真正的移动和桌面缩放都是测试和验证vh 值的关键环境。静态的rem 值是另一个设计代币的机会。

由于我需要保持品牌,这里有你的超现代CSS规则来设置块流:

:is(body, .block-flow) > * + * {
  margin-block-start:
    var(- -block-flow, var(- -block-flow-md) );
}

使用:is() ,我们定义了body 的直接子节点和应用block-flow 类时的直接子节点将默认为具有我们的中等顶部边距。如果包括body 对于你的环境来说太笼统了,你当然可以把这个规则减少到只有block-flow

下面是block-flow 自定义属性的起始集:

:root {
  --block-flow-sm: min(2rem,  4vh);
  --block-flow-md: min(4rem,  8vh);
  --block-flow-lg: min(8rem, 16vh);
}

间距

我们的最后一个间距属性是gap ,我们将考虑用于布局组件的间距,就像为我们之前设置的内在网格提供数值一样。我们将再次使用clamp() ,但将其切换为使用vmax 作为此上下文的动态值。

作为提醒,gap 是在元素之间应用的,如阴影区域所示。

three text elements with row and column gap space indicated by a pink shaded area

顺便说一句--自2021年4月起,我们已经在flexbox中对gap 进行了跨浏览器支持!

这里有一个布局的基准规则gap

gap: clamp(1.5rem, 6vmax, 3rem);

vmax 单位要求浏览器使用最大的一个:视口宽度或高度。

我们使用vmax 作为动态单位,而不是百分数,因为百分数,如应用于gap ,是根据gap 的方向计算的。因此,像这样统一应用gap ,使用百分数,可能会使行gap 的值小于列gap 的。相反,使用vmax 创建动态的、有背景的空间,将均匀地应用于行和列gap

而且--你可能猜到了--我们对clamp() 的最小和最大值又一次成为了潜在的设计标记。

我将gap 与grid一起使用,并将flex用于表单字段等原子学。所以我想明确地将这组自定义属性命名为layout-gap ,因为它们依赖于vmax ,并且是为网格等布局组件准备的。同样,你可以看到翻倍的策略在起作用,以计算出一个起始的vmax

:root {
  --layout-gap-sm: clamp(1rem,   3vmax, 1.5rem)
  --layout-gap-md: clamp(1.5rem, 6vmax,   3rem);
  --layout-gap-lg: clamp(3rem,   8vmax,   4rem);
}

为什么不使用媒体查询?

我们刚刚回顾了更合适的技术,以确保元素的上下文相关的、内在的大小和间距。与视口相关的媒体查询并不适合所有的环境,而且也不再是最好的工具了。

此外,我们很快就会对容器查询提供全面支持。一旦我们能够访问容器单元,在某些地方它们可能会成为更好的选择,我在这些例子中仍然选择了视口单元。

我们还将获得 "父级 "选择器--:has() ,这将为基于实际元素配置创建上下文间距提供更多可能性。

开始使用上下文间距技术

设计系统和框架的(必要的)僵化与内在的网页设计是相悖的。

为了行动,我要求你们每个人都尝试在你能做到的地方做出调整,并教育其他人了解这种可能性。花点时间多考虑一下,做一些实验,并超越你目前的舒适区。让我知道你是如何使用和改进这套入门技术的。