到目前为止,我们在本书中看到的所有例子都有一个共同点:它们是为电脑屏幕设计的,并不是为了在小屏幕上看起来很好,比如在智能手机或平板电脑上。制作一个能在多种尺寸的屏幕上使用的 CSS 设计的过程被称为响应式设计。

在普通的 CSS 中,响应式设计可能是一个复杂的 CSS 类和 @media 标签的纠结。Tailwind 提供了修改器,可以应用于任何 Tailwind 工具类,以控制屏幕尺寸的集合。

Tailwind 并没有消除响应式设计的所有复杂性;当你为多种尺寸设计时,你仍然需要考虑许多因素。例如,你需要考虑当用户在较小的屏幕上看时,你的网站的哪些元素是最重要的,需要被强调。但是,Tailwind 确实让你更容易在不同尺寸下实验不同的设计,并且能够一目了然地看到所有不同尺寸的行为。也就是说,Tailwind 中的响应式设计可能会导致极长的 CSS 声明,而这些声明可能很难阅读。

在这一章中,我们将看一下 Tailwind 中的响应式工具类,以及应用它们的常见模式。

屏幕宽度和断点

在 CSS 中,各种属性可以根据屏幕的宽度有条件地应用。这些条件是用 @media 标签管理的。设计发生变化的特定屏幕宽度,通常被称为断点。在 Tailwind 中,您可以在任何 Tailwind 工具上放置一个响应式修改器,以指定该工具应被应用的最小屏幕宽度。

Tailwind 的响应行为与你可能习惯的其他框架有些不同。一些需要注意的重要行为包括:

  • 任何响应的修改器都会使该工具在指定的屏幕宽度或任何更大的屏幕宽度上生效。
  • Tailwind 工具定义了一个生效的最小宽度,但没有定义最大宽度。
  • 如果没有使用修改器,默认的最小宽度是 0 —— 该工具总是生效的。

如果你将某件事定义为适用于小屏幕宽度,Tailwind 就会将该行为一直应用到小、中、大以及更大的屏幕。如果你只想在小屏幕上有行为,你就定义小屏幕行为,而不使用修改器,用中屏幕或宽屏幕修改器来取消行为。

Tailwind 默认定义了五个屏幕宽度。对于这五个屏幕宽度,像素宽度是屏幕的逻辑宽度。在拥有视网膜屏幕的设备上,一个逻辑像素将由多个物理像素组成,我们仍然使用逻辑屏幕。例如,iPhone 13 是 390 个逻辑像素宽,尽管它有 1170 个物理像素宽。

五种屏幕宽度是:

  • 小(sm):640 像素及以上
  • 中(md):768 像素及以上
  • 大(lg):1024 像素及以上
  • 超大(xl):1280 像素及以上
  • 特大(2xl):1536 像素及以上

下表中提供了现有设备宽度的部分列表:
image.png
关键的一点是,如果你把某样东西定义为 sm:(例如,sm:m-2),那么这个 m-2 的效用就被定义为所有尺寸为 640 像素及以上的屏幕。如果你想在更大的屏幕上改变这个边距,你可以定义一个具有更大修饰符的工具 —— 你可以保证大修饰符将优先于小修饰符。所以你可以用 sm:m-2 md:m-4 lg:m-8 来让你的边距随着你的屏幕宽度增加而逐渐变宽。

处理这些模式的一般方法是这样的:没有修改器的实用程序应该描述你在最小的屏幕上看到的行为,然后你引入修改过的实用程序来调整屏幕变大时的行为。我们的想法是,你首先为移动设备定义你的设计,然后使用修改器来为大屏幕调整设计。

在整本书中,我一直努力保持一致,指出否定或默认的效用,即使在不清楚它们可能被使用的地方。响应式实用程序就是使用这些否定式实用程序的地方。在 Tailwind 中,宽度修改器适用于它们的大小和向上。如果你想在一个更宽的宽度上取消一个实用程序,你需要在更大的宽度上明确地否定它。例如,像 sm:shadow-xl md:shadow-none 这样的东西使用 .shadow-none 重置工具来撤销 .shadow-xl。在一个元素上同时使用这两样东西会给你一个宽度在 640 和 768 之间的盒状阴影(如果出于某种原因你想这么做)。

值得一提的是,你可以将屏幕尺寸与其他修改器结合起来:md:hover:font-bold lg:hover:font-black 是完全合法的。

按尺寸隐藏

使你的应用程序适合在较小的屏幕上使用的一种方法是在较小的屏幕上隐藏用户界面的一部分。在这种情况下,因为最小的屏幕行为被隐藏,所以未修改的属性也被隐藏。在更大的屏幕上,你可能想让这个项目显示出来,所以你加入 lg:block(或者任何你想开始看到这个项目的断点),最后变成 class="hidden lg:block"

有时你可能想反其道而行之,以较小的尺寸显示一个元素,但不显示较大的尺寸。让一个汉堡包菜单的元素在小尺寸上取代导航栏是很常见的,但在一个大到可以显示所有导航的设备上就会消失。在这种情况下,小尺寸的行为是显示,这是默认的,而你把隐藏行为作为一个断点加入,如 class="lg:hidden"

同样地,在小尺寸设备上降低标题文本的尺寸也很常见。较小的尺寸是在较小的宽度上显示的,所以产生的 DOM 类会看起来像 class="text-xl md:text-2xl lg:text-4xl"

在小型设备上减少网格列

一般来说,很多响应式设计的目标是同时让信息在小尺寸时堆叠起来,在大尺寸时空间存在时散开成行。确切地说,你想如何做到这一点取决于你的目标。

一种可能性是,你有一组类似卡片的元素,就像新闻网站上的特色文章一样,其中的数据实际上不是一个表格,而是一系列排成一排的项目。

在这种情况下,你可能希望这些项目能填满整个屏幕的宽度,但项目的数量要根据屏幕的大小而变化。在智能手机上,你可能希望整个屏幕上只有一个项目;在桌面上,可能是四个。所以你可以使用这样的东西:

  1. <div class="grid items-stretch
  2. md:grid-cols-2 md:gap-4
  3. lg:grid-cols-4 lg:gap-4">
  4. <div class="mb-6 lg:mb-0"></div>
  5. ...
  6. </div>

这里有几件事情要做。父 div 在所有宽度下都是一个网格,但在最窄的宽度下默认的网格大小是 1,在中等屏幕上增长到 2,在大屏幕上增长到 4。 items-stretch 意味着每个单独的子元素将被拉伸以填充其宽度部分,这意味着它们将随着屏幕变大而变大,直到下一个断点,然后每行将被添加更多项目。随着屏幕变大,我们也会增加项目之间的间隙。对于子项目,当行中只有一个元素时,我们的底部边距 mb-6,这样就有了一些间距,但当屏幕变大时,底部边距就会消失,lg:mb-0

在大设备上保持弹性

另一种在不同尺寸之间进行调整的方法是,让一个元素在较小的设备上使用默认的块状间距,然后在较大的设备上转换为柔性间距。小设备上的块状间距确保项目保持在一列,即使有些项目很窄,而大尺寸时的弹性间距则将它们分散成一排。

这里的常见模式是,在大尺寸时,导航栏会分布在页面的顶部。但在较小的尺寸下,它是一个菜单栏,一般在点击菜单按钮之前是隐藏的。

这里有一个简单的例子:

  1. <div class="w-full hidden lg:flex lg:flex-grow, lg:items-center lg:width-auto
  2. divide-black divide-y lg:divide-y-0"
  3. id="navbar-menu">
  4. <a class="block lg:mr-4 p-2 hover:bg-gray-200">Blog</a>
  5. ...
  6. </div>

这里的外层 div 在小屏幕上开始是隐藏的。这通常是与 JavaScript 搭配(我们一会儿会做),使其不被隐藏。在小屏幕上,当它不被隐藏时,它将使用默认的块显示模式,也就是列显示。在大屏幕上,lg:flex 覆盖了隐藏的功能,显示方式是 flexflex-grow,这意味着项目将适合整个屏幕。我还在小规模的项目之间添加了一条分割线,即 divide-y,但在大尺寸时,这条分割线就消失了。 lg:divide-y-0 使小列中的项目更容易区分。

内部项目在大尺寸时有更多的右边距,在悬停时它们的背景会变成灰色。它们需要被明确地设置为块状,因为 a 标签默认是内联的。如果我把这些 div 标签做成了,就不需要屏蔽了。

为了使其作为导航条工作,你需要一点 JavaScript。下面的片段是没有框架的普通 JavaScript,假设你有三个元素。一个是导航条本身,我们之前讨论过,但 DOM ID 为 navbar-menu。另外两个元素隐藏在相同的间距内,是汉堡包菜单 navbar-burger 和一个 X 形关闭元素 navbar-close:

  1. <nav class="flex items-center font-bold text-grey=600">
  2. <div class="block lg:hidden self-start">
  3. <button id="navbar-burger"
  4. class="px-3 py-2
  5. border rounded border-grey-400
  6. hover:border-black">
  7. <svg xmlns="http://www.w3.org/2000/svg"
  8. fill="none" viewBox="0 0 24 24"
  9. stroke="currentColor">
  10. <path stroke-linecap="round"
  11. stroke-linejoin="round"
  12. stroke-width="2"
  13. d="M4 6h16M4 12h16M4 18h16" />
  14. </svg>
  15. </button>
  16. <button id="navbar-close"
  17. class="px-3 py-2
  18. border rounded border-grey-400
  19. hover:border-black">
  20. <svg xmlns="http://www.w3.org/2000/svg"
  21. fill="none"
  22. viewBox="0 0 24 24"
  23. stroke="currentColor">
  24. <path stroke-linecap="round"
  25. stroke-linejoin="round"
  26. stroke-width="2"
  27. d="M6 18L18 6M6 6l12 12" />
  28. </svg>
  29. </button>
  30. </div>
  31. <div class="w-full hidden
  32. lg:flex lg:flex-grow,
  33. lg:items-center lg:width-auto
  34. divide-black divide-y
  35. lg:divide-y-0"
  36. id="navbar-menu">
  37. <a class="block lg:mr-4 p-2 hover:bg-gray-200">Blog</a>
  38. AND SO ON
  39. </div>
  40. </div>
  41. </nav>

“汉堡包”和“关闭”图标的SVG来自 https://heroicons.com,这是一套小型的 SVG 图标,也来自 Tailwind CSS 的制作者。

接下来,我们为导航栏的汉堡和导航栏的关闭添加一个事件监听器。汉堡的监听器会隐藏汉堡,并显示关闭按钮和菜单。关闭监听器隐藏关闭按钮和菜单并显示汉堡:

  1. document.addEventListener('DOMContentLoaded', () => {
  2. const $navbarBurger = document.querySelector('#navbar-burger')
  3. const $navbarClose = document.querySelector('#navbar-close')
  4. const $navbarMenu = document.querySelector('#navbar-menu')
  5. $navbarBurger.addEventListener('click', () => {
  6. $navbarMenu.classList.remove("hidden")
  7. $navbarBurger.classList.add("hidden")
  8. $navbarClose.classList.remove("hidden")
  9. });
  10. $navbarClose.addEventListener('click', () => {
  11. $navbarMenu.classList.add("hidden")
  12. $navbarBurger.classList.remove("hidden")
  13. $navbarClose.classList.add("hidden")
  14. });
  15. })

这就给了你一个基本的响应式导航系统。接下来,让我们谈谈如何定制 Tailwind。