有趣的事实:无需任何媒体查询就可以创建响应式组件。 当然,如果我们有 容器查询,它们将对组件级响应式设计非常有用。 但我们没有。 尽管如此,无论是否有容器查询,我们都可以做一些事情来使我们的组件变得非常响应。 我们将使用来自 内在 Web 设计 的概念,由 Jen Simmons 为我们带来。
让我们一起深入研究下面描述的用例,解决方案 关于 CSS 的实际状态,以及一些我将给您的 其他技巧。
一个响应式的“烹饪食谱”卡片
我最近 发布推文 一个响应式卡片演示的视频和 Pen,我使用披萨食谱作为示例构建的。(这对于这里使用的技术并不重要,但我放弃了 食谱 在最后,因为它美味且无麸质。)
无需媒体查询的响应式披萨食谱组件。 https://127.0.0.1/upft4Vpkp1
— Geoffrey Crofte 🔥 (@geoffreycrofte) 2020 年 7 月 18 日
基于 @WalterStephanie 的设计进行中。 享受调整浏览器窗口大小的乐趣 😊 pic.twitter.com/FHK2ghMb91
这里的演示是根据 Stéphanie Walter 的演讲中的一个概念进行的首次尝试。 这段视频将向您展示卡片将如何表现
如果您现在想玩玩它,这里是 Pen。
让我们定义响应式布局
规划的关键是了解您正在处理的实际内容以及这些细节的重要性。 并不是说我们应该在任何时候隐藏内容,而是出于布局和设计原因,了解需要首先传达什么内容以及等等,这一点很重要。 无论布局的大小或形状如何,我们都将显示相同的内容。
让我们以移动优先的心态来想象内容,以帮助我们专注于最重要的内容。 然后,当屏幕更大时,例如在台式机上,我们可以使用额外的空间来做一些事情,例如辉煌的空白和更大的排版。 通常情况下,像这样的优先级设置就足以确保在任何和所有视窗大小下都需要哪些内容。
让我们以烹饪食谱预告为例

在她的演讲中,Stéphanie 已经完成了工作,并为我们的卡片 优先考虑了内容。 以下是她概述的内容,按重要性排序
- 图像:因为它是食谱,所以您是用眼睛吃的!
- 标题:确保您要烹饪什么。
- 关键词:以便在第一眼就能看到关键信息。
- 评分信息:用于社会证明。
- 简短描述:对于那些阅读的人。
- 号召性用语:您希望用户在卡片上执行的操作。
这似乎很多,但我们可以将所有这些内容都放到一个智能卡片布局中!
不可缩放的排版
我要向您展示的技巧之一的限制是,您将无法获得基于容器宽度的可缩放排版。 可缩放排版(例如 “流体类型”)通常使用视窗宽度 (vw
) 单位完成,该单位基于视窗,而不是父元素。
因此,虽然我们可能很想将流体类型作为卡片中内容的无媒体查询解决方案,但不幸的是,我们无法根据容器宽度或元素宽度本身的百分比使用流体类型。 然而,这并不会阻止我们实现目标!
关于“像素完美”的快速说明
让我们谈谈双方的观点……
设计师:像素完美非常理想,我们当然可以在组件级别精确。 但在布局级别必须有一些权衡。 也就是说,您将必须提供一些变体,但允许中间部分灵活。 响应式布局中的事物会发生变化,在每个可能的屏幕宽度下实现精确度都是一项艰巨的任务。 但是我们仍然可以使所有比例看起来都很好!
开发人员:您必须能够填补已指定设计布局之间的空白,以使内容在这些状态之间可读且一致。 作为一项良好的实践,我还建议尽量保持尽可能多的 自然流程。
您还可以阅读 Ahmad 关于 像素完美状态 的精彩文章。
零媒体查询的食谱
请记住,我们努力的不仅仅是一个响应式卡片,而是一个不依赖于任何媒体查询的卡片。 并不是说应该避免使用媒体查询; 更多的是关于 CSS 足够强大且灵活,使我们能够使用其他选项。
为了构建我们的响应式卡片,我想知道是否 弹性盒 足够,还是我需要使用 CSS 网格 代替。 事实证明,这次弹性盒对我们来说确实足够,使用了 flex-wrap
和 flex-basis
属性在 CSS 中的行为和魔力。
flex-wrap
的要点是,当内容空间变得太紧时,它允许元素换行。 您可以在此演示中看到不换行和换行弹性盒之间的区别
flex-basis
值为 200px 更像是对浏览器的指示,而不是建议,但如果容器没有提供足够的空間,元素将自动换行。 列之间的边距甚至会强制初始换行。
我使用此换行逻辑来创建卡片的基础。 Adam Argyle 也在以下演示中使用了它,其中演示了四个表单布局,仅用 10 行 CSS 代码。
在他的示例中,Adam 使用了 flex-basis 和 flex-grow(在 flex 简写属性中一起使用))以允许电子邮件输入占用姓名输入或按钮占用空间的三倍。 当浏览器估计没有足够的空间在同一行上显示所有内容时,布局会自动换行,而无需我们管理媒体查询中的更改。
我还使用了 clamp()
函数 来添加更多灵活性。 此函数有点神奇。 它允许我们在单个函数中解析min()
和 max()
计算。 语法如下
clamp(MIN, VALUE, MAX)
就像解析max()
和 min()
函数的组合一样
max(MIN, min(VAL, MAX))
您可以将其用于涵盖以下所有类型的属性: <length>
、<frequency>
、<angle>
、<time>
、<percentage>
、<number>
或 <integer>
。
“无媒体查询响应式卡片”演示
借助所有这些新式的 CSS 功能,我创建了一个灵活的响应式卡片,无需任何媒体查询。最好在一个新标签页中查看 此演示,或者在下面嵌入的代码中使用 0.5x 选项。
您需要立即注意的一点是,两张卡片的 HTML 代码完全相同,唯一的区别是第一张卡片位于一个 65% 宽度的容器中,而第二张卡片位于一个 35% 宽度的容器中。您也可以调整窗口的尺寸来测试其响应能力。
演示中代码的重要部分在这些选择器上
.recipe
是父级 flex 容器。.pizza-box
是一个 flex 项目,是卡片图像的容器。.recipe-content
是第二个 flex 项目,是卡片内容的容器。
现在我们已经了解了 flex-wrap
的工作原理,以及 flex-basis
和 flex-grow
如何影响元素的大小,我们只需要快速解释一下 clamp()
函数,因为我用它来进行响应式字体大小调整,代替了我们通常可能使用的流体类型。
我想要使用 calc()
和自定义属性根据父容器的宽度来计算字体大小,但我没有找到方法,因为 100% 的值根据上下文有不同的解释。我把它保留在我的 clamp()
函数的中间值,但最终结果是过度设计,并没有像我希望的那样工作。
/* No need, really */
font-size: clamp(1.4em, calc(.5em * 2.1vw), 2.1em);
我最终选择了以下方法
font-size: clamp(1.4em, 2.1vw, 2.1em);
这就是我让卡片标题的大小根据屏幕尺寸调整的方法,但正如我们在前面讨论流体类型时所提到的,我们无法根据父容器的宽度调整文本大小。
相反,我们基本上用这一行 CSS 代码表示
我希望
font-size
等于 2.1vw(视窗宽度的 2.1%),但请不要让它小于 1.4em 或大于 2.1em。
这样可以保持标题优先级的突出性,使其比其他内容更大,同时保持可读性。而且,它仍然可以随着屏幕尺寸的改变而增大和缩小!
当然,我们不能忘记响应式图像,内容要求表明图像是最重要的部分,所以我们一定要考虑它,并确保它在所有屏幕尺寸下看起来都很棒。现在,您可能想要尝试以下方法,并以此为结束
max-width: 100%;
height: auto;
但这并不总是能产生最佳的图像渲染效果。相反,我们有 object-fit
属性,它不仅能响应图像内容盒的高度和宽度,而且还能让我们裁剪图像,并使用 object-position
属性来控制图像在盒子里伸展的方式。
img {
max-width: 100%;
min-height: 100%;
width: auto;
height: auto;
object-fit: cover;
object-position: 50% 50%;
}
如您所见,这里有许多属性需要写下来。这是因为 HTML <img>
代码中有明确的宽度和高度属性。如果您删除了 HTML 部分(我不建议这样做,因为 性能原因),您可以保留 CSS 中的 object-*
属性,并删除其他属性。
另一种无需媒体查询的方案
另一种技术是将 flex-grow
用作基于单位的增长值,并将 flex-basis
设置为一个非常大的值。这个想法直接来自于 Heydon Pickering 的精彩的 “Holy Albatross” 演示。
代码中有趣的部分是
/* Container */
.recipe {
--modifier: calc(70ch - 100%);
display: flex;
flex-wrap: wrap;
}
/* Image dimension */
.pizza-box {
flex-grow: 3;
flex-shrink: 1;
flex-basis: calc(var(--modifier) * 999);
}
/* Text content dimension */
.recipe-content {
flex-grow: 4;
flex-shrink: 1;
flex-basis: calc(var(--modifier) * 999);
}
flex-grow
创建比例尺寸,而 flex-basis
尺寸可以是无效的或极高的。当 calc(70ch - 100%)
(即 --modifier
的值)达到正值时,该值会变得极高。当这些值极高时,它们中的每一个都会填充空间,从而创建一个列布局;当这些值无效时,它们会以行内布局方式排列。
70ch 的值就像配方组件中的“断点”(几乎就像 容器查询)。根据您的需要进行更改。
让我们再次分解这些要素
以下是我们用于无媒体查询的卡片组件的 CSS 要素
clamp()
函数有助于解决“首选”与“最小”与“最大”值之间的冲突。flex-basis
属性使用负值来决定布局何时分成多行。flex-grow
属性用作比例增长的单位值。vw
单位有助于实现响应式排版。object-fit
属性提供了更精细的卡片图像响应能力,因为它允许我们改变图像的尺寸,而不会扭曲图像。
深入了解数量查询
我还有一个技巧要告诉你:我们可以根据容器中项目的数量调整布局。这不是容器尺寸带来的响应能力,而是内容所在上下文的响应能力。
实际上没有用于项目数量的实际媒体查询。这是一个 CSS 小技巧,可以反向计数项目数量,并相应地应用样式修改。
演示使用以下选择器
.container > :nth-last-child(n+3),
.container > :nth-last-child(n+3) ~ * {
flex-direction: column;
}
看起来很复杂,对吧?这个选择器允许我们从最后一个子元素及其所有兄弟元素开始应用样式。太棒了!
Una Kravets 解释了这个概念,非常清晰。我们可以这样翻译这个特定用法
.container > :nth-last-child(n+3)
:从该组中最后一个 .container 开始,第三个或更多的 .container 元素。.container > :nth-last-child(n+3) ~ *
:与上一个完全相同,但会选择最后一个 .container 之后的任何 .container 元素。这有助于处理我们添加的任何其他卡片。
Kitty Giraudel 的 “Selectors Explained” 工具 可以将复杂的选择器翻译成简洁的英文,如果您想了解这些选择器如何工作的另一种解释。
在 CSS 中获得“数量”容器的另一种方法是使用 二元条件。但语法并不容易,而且看起来有点像 hack。如果您需要讨论这个问题,或者其他任何关于 CSS 或设计的技巧,可以联系我在 Twitter 上。
这未来证明吗?
我在这里介绍的所有技术都可以在生产环境中使用。它们得到了很好的支持,并提供了优雅降级的机会。
最坏的情况是什么?某些不支持的浏览器,比如 Internet Explorer 9,不会根据我们指定的条件更改布局,但内容仍然可以阅读。所以,它是受支持的,但可能不会针对理想的体验进行“优化”。
也许有一天,我们终于能够看到 容器查询 的实际应用。希望我们在这里使用的 内在 Web 设计 模式能够与您产生共鸣,并帮助您在此期间构建灵活的“内在响应式”组件。
让我们来谈谈这篇文章的“真正”原因……披萨!🍕
无麸质平底锅披萨食谱
您可以选择配料。重要的是面团,以下是面团的制作方法
配料
- 3¼ 杯(455 克)无麸质面粉
- 1 汤匙加 1 茶匙(29 克)红糖
- 2 茶匙犹太盐
- 1/2 块酵母
- 2½ 杯(400 毫升)全杏仁奶
- 4 汤匙融化的人造黄油
- 1 汤匙玉米淀粉
制作方法
- 将所有干性配料混合在一起。
- 加入液体。
- 让它在温暖的地方发酵 2 个小时。建议在面团碗上放一条湿毛巾,并将碗放在温暖的地方(但不要太热,因为我们不想让它现在就煮熟)。
- 把它放在有油的锅里。让它膨胀到原来的两倍,大约需要 1 小时。
- 在 250 度的烤箱中烤 20 分钟。
谢谢 Stéphanie 的食谱 😁
我只想说,你的卡片样式太棒了。
你好 Chris :)
感谢你的反馈!
感谢你的技巧和视频!我必须说,我喜欢这个比萨食谱!赞赏!
这也能用 CSS Grid 做吗?
喜欢这个
谢谢你的比萨食谱
不客气
每当我想到这个技巧,我都不禁希望有一种方法(在 CSS 中)可以查询一个元素与其前面元素的视觉关系。所以你可以说,如果
.recipe-content
与.pizza-box
在同一行,就这样做。或者,如果.recipe-content
(视觉上)与.pizza-box
有块级关系,就应用这些样式。这感觉很像 CSS,并且会非常强大。我完全同意 Sam 的观点
…并且不可访问。它不是基于用户的浏览器文本大小或任何其他自定义设置。请避免使用它。
你好 Bobby,
感谢你对可访问性的关注!我同意
vw
不应该直接使用。这就是我使用
clamp()
函数来使其更加灵活和“缩放友好”的原因。我不确定,但通过将最大值和最小值都设置为em
单位,字体与用户字体大小设置兼容。我刚刚在 Chrome 和 Firefox 上使用文本缩放和界面缩放进行了测试。一切正常。那 IE 呢?大多数公司使用 Windows,并且相当多的用户和公司仍在使用 IE10 或 IE11,它将无法正常工作。
我创建了无媒体查询的卡片和页面,但如果它对我要覆盖的半数人无效,那就毫无用处。
当然,不得不生成适用于两者都有效的 css 绝非易事,我仍在努力使它正常工作。
你好 Rita,
抱歉你仍然需要支持 IE10 和 IE11。我认为这是公司的决定,而不是 CSS 决定,但我不会冒这个险。
你可以使用 @supports 来使用我提出的解决方案,并覆盖基于浮动元素的 IE 解决方案。我们曾经采用了一种针对响应式电子邮件的浮动表格技术,它应该适用于任何类型的元素。另一种解决方案是依赖 JavaScript 来根据组件父级定义精确的行为。
不幸的是,当你试图用过时且不安全的工具解决现代问题时,没有神奇的解决方案。
不错的解决方案,感谢。但是,我在我的实现中遇到了问题,使用从你的第一个解决方案直接迁移的样式——当我不指定
.pizza-box
的宽度和高度为 100% 时,图像没有完全覆盖其区域——看起来max-width
和max-height
没有作用。你知道是什么导致了这种行为吗?你好 Miloš,
如果没有你的代码片段,我无法确定答案,因为即使是 HTML 也会影响这里的行为。事实上,如果你没有在图像上设置宽度和高度属性,渲染可能会有所不同。
同样重要的是要注意:我没有在图像比例上投入太多精力,如果第一个 HTML 解决方案不起作用,你可能需要调整一下 CSS。
玩得开心 :)
如何包装列
1 2 3
4 5 6
在移动视图中,它应该像这样
1 2
4 5
3 6
嗨 Rakesh,
你应该尝试使用 CSS Grid 布局来满足这种需求。在 CSS Tricks 上搜索一下。