用户的浏览环境是不可预测的。告诉其他开发者,看在上帝的份上,告诉你的设计师。让我们学习如何通过使用自适应、上下文的间隔技术与这种不可预测性共存。
2018年,Jen Simmons在她的演讲"Everything You Know About Web Design Just Changed"中介绍了 "内在网页设计 "这一术语。 她还分享了内在网页设计的原则,我们将以此为指导:
- 收缩和扩展--我们考虑我们的设计将如何适应可用空间的变化的方式
- 灵活性--主要使用flexbox和grid与较新的单元和功能相结合的方式,使我们的布局能够以不同的速度适应可用的空间
- 视口--能够使用视口的所有四边以及利用视口单元。
使用自适应布局技术是设计者、开发者和浏览器之间的一项信任工作。
本质设计的属性和功能
让我们先来回顾一下创建内在尺寸元素的基础要领。
夹子
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 - 3rem 和80ch 之间选择最小的值:
min(100vw - 3rem, 80ch)
这导致当视口<80ch ,100vw - 3rem 被选中,而当视口>80ch ,80ch 被选中。
如果我们采用这个规则并添加逻辑属性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;
}
要理解何时使用min 和max ,可能会有点棘手。
让我们考虑一下max 的以下数值:
max(2rem, 4vh)
当4vh 的计算值变得小于2rem 时,max 函数将选择2rem 。
因此,有效地,正在发生的事情是,选择的2rem 是你将允许这个规则的最小值。
现在让我们翻到min() 。
min(100%, 60ch)
min() 的这个规则意味着60ch实际上是这个元素的最大允许值。
如果你想知道为什么我们要使用min 和max ,而不是单独列出相应的属性,那是因为我们可以在任何允许有数值的地方使用它们,而不仅仅是在尺寸上。
例如,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-content 和max-content ,所以让我们扩大文本值。
fit-content会增长但不会溢出
max-content有溢出的可能
说实话,我还没有发现min-content 或max-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() 的改进的演示。
我们对文章元素和卡片都使用了相同的填充规则。在文章中的技巧是在60ch 和100% 之间进行切换,前者适用于大的内联空间,后者在窄的内联空间中没有额外的 "沟槽 "空间。
另一个受到这些规则积极影响的情况是《网络内容可及性指南》的回流成功标准,该标准定义了对浏览器放大到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-start 是margin-top 的逻辑伴侣。
在min() 中,我们提供了4rem 和8vh 的值。所以你可以把4rem 看成是静态值,把8vh 看成是动态值。
下面是该规则在大与小的背景下的效果。从技术上讲,这些值的计算余地只在 "移动 "版本上节省了大约13px ,这可能看起来没有太大的影响:
然而,再一次,在我们的缩放背景下,启用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 是在元素之间应用的,如阴影区域所示。
顺便说一句--自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() ,这将为基于实际元素配置创建上下文间距提供更多可能性。
开始使用上下文间距技术
设计系统和框架的(必要的)僵化与内在的网页设计是相悖的。
为了行动,我要求你们每个人都尝试在你能做到的地方做出调整,并教育其他人了解这种可能性。花点时间多考虑一下,做一些实验,并超越你目前的舒适区。让我知道你是如何使用和改进这套入门技术的。