探索Tailwind CSS的设计方案

发表时间: 2023-05-13 09:22

写在前文

最近我自己也体验了一下 tailwindcss,作为一个很喜欢写 CSS 的人,体验完之后,说实话还是觉得有点儿不太顺手,但是我确实因为它不愿意再新建一个 CSS 文件了。这种有点儿难受又有点儿爽的心态让我继续沉迷于 tailwindcss 。恰好昨晚看到我的好朋友在知乎上写了一些关于它的文章,情不自禁转来分享大家。


下面是原文:

经过一段时间的使用,tailwindcss 方案已经能够很流畅的使用了,没有想象中的不适感,整体设计风格很统一,熟练之后能够很流畅地写出复杂样式,和传统的css、less相比,明显的感受是上下文切换变少了,和 css-in-js 相比,敲键盘次数变少了,原以为只是一个 css 工具集,但它的强大超出我的预期,一些复杂的联动效果也能轻松完成。

能点击进来看到本文的应该都多多少少听过 tailwindcss,本文会结合我的使用体验,将tailwindcss方案的核心思想梳理一番。

官方在线 Playground 地址: Tailwind Play(
https://play.tailwindcss.com/

背景

一直以来一个项目的 css 方案都是让前端开发者头痛的问题,相似结构的重复样式导致冗余[1]、样式和内容耦合导致内容变动后样式也对应修改、大量无意义元素取名困难、大型项目容易命名冲突,缺少和JS类似的模块化方案,css语义不清晰阅读困难、缺少嵌套支持等。

传统的方案例如语义化 CSS 追求的是内容优先,例如 CSS 禅意花园: CSS 设计之美(
https://www.csszengarden.com/tr/zh-cn/
) ,这种规范以 HTML 内容优先,HTML无关样式,通过样式表让HTML具有不同的风格,这是早期人们对样式表的理解。优点是内容无关样式,但是缺点同样地导致样式非常和内容相关,内容变动导致样式需要大量修改。

BEM 方案通过给HTML增加大量类名,优点是依然保留的上面的语义化,HTML 可读性好,而且可以避免类名重复,缺点对应的是类名膨胀,尤其是在书写具有相似结构的组件时,会产生大量冗余的类名,在面对一些无意义元素时也需要对其命名。

为了解决BEM, 自定义类名等方案带来的问题,使用工具类能有效地减少css冗余、样式复用等问题,于是作者以工具类优先的原则开发了 tailwindcss。

使用

tailwindcss 本体是一个 Postcss 插件, 支持命令行启动,其核心工作流程如下[2]。

tailwindcss是一个postcss插件,工作流如图 tailwind 会将生成的样式表插入到对应的插槽中。所以我们必须有一个css文件包含下面三句,否则tailwindcss不会产生任何效果。

@tailwind base;@tailwind components;@tailwind utilities;

其中 tailwind base 相当于一份重置样式表,包含了最基础的样式。tailwind components 包含了一些组件类, 组件相当于复合样式,tailwind utilities 包含了工具类,也就是 flex mx-auto 这些内置样式。

这么划分的原因是因为 css 的优先级规则,tailwindcss 全部都是一级样式,在类名权重相等的情况,下面的样式可以覆盖上面的样式,所以工具类优先,组件类次之,基础样式兜底,生成的样式顺序尤为重要,所以 上面三句指令的顺序非必须建议不要修改。

类名规则

tailwindcss 内置了大量工具类名,理论上可以做到100%使用内置方案覆盖,下面看一些具体的命名规则:

tailwindcss 的命名规范很统一,具有唯一性的样式属性会直接作为对应的类名,例如 block, absolute, flex, top-0, overflow-hidden, whitespace-nowrap, border, border-black 只看名称就能唯一确定属性,符合开发直觉。

  1. block -> display: block
  2. absolute -> position: absolute
  3. m-auto -> margin: auto
  4. p-auto -> padding: auto
  5. mx-2 -> margin-left: 0.5rem; margin-right: 0.5rem

对于一些 css 通用名称,tailwindcss提供了统一的规范。例如 left, right, top, bottom, 分别对应 l, r, t, bleft-right 对应 x, top-bottom 对应 y。 所以产生了下面的类名:

  1. ml-[2px] -> margin-left: 2px;
  2. mr-[2px] -> margin-right: 2px;
  3. mx-[2px] -> margin-left: 2px; margin-right: 2px;
  4. pt-2 -> padding-top: 0.5rem;
  5. pb-2 -> padding-bottom: 0.5rem;
  6. border-r -> border-right-width: 1px;
  7. border-b -> border-bottom-width: 1px;

所以如果想写一个上边框黑色,上下外边距 2px, 上内边距10px,可以如下:

<div class="border-t border-black my-[2px] pt-[10px] "/>

其他简写也会符合越常用越简写的原则,来看一些案例

  1. bg-black-> background: black
  2. bg-[url(/img.png)]-> background: url(/img.png)
  3. bg-norepeat -> background: no-repeat
  4. text-[#333] -> color: #333
  5. w-[200px] -> width: 200px
  6. h-[200px] -> height: 200px
  7. whitespace-nowrap -> white-space: nowrap
  8. transition-opacity -> transition-property: opacity;transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);transition-duration: 150ms;

所以我想写一个宽高 200, 黑底白字,过渡效果为背景色,过渡时间为 300ms,可以如下

<div class="w-[200px] h-[200px] bg-black text-white transition-bg duration-[300ms]">

再有一些类名也大致满足如上规则,前期需要对照文档查每个属性的类名,习惯之后可以凭借记忆和规则猜出类名。

修改器 hover, focus, before, after, group, important

在 tailwindcss 中,修改器语法为 修改器:类名 修改器的种类很多且可扩展。内置的修改器如下:

  1. 伪类选择器: hover, focus, active
  2. 子选择器: first, last, even, odd
  3. 父状态选择器: group <- tailwindcss 特有
  4. 伪元素: before, after
  5. !important修改器

案例 1:hover:bg-sky-500 当 hover 时应用此类名

<div class="hover:bg-sky-500 w-[200px] h-[200px] bg-black text-white transition-bg duration-[300ms]">

案例2:first:bg-sky-500 第一个子元素

<div>   <div class="w-[20px] h-[20px] mt-[10px] bg-gray-300 first:bg-sky-500"/>   <div class="w-[20px] h-[20px] mt-[10px] bg-gray-300 first:bg-sky-500"/>  <div class="w-[20px] h-[20px] mt-[10px] bg-gray-300 first:bg-sky-500"/> </div>Ï

案例3: group, group-hover 父状态选择器,当父组件 hover 时,子元素高亮

<div class="group border-b whitespace-nowrap text-gray-300">   <div class="w-[20px] h-[20px] mt-[10px] bg-gray-300"> pick me </div>    <div class="w-[20px] h-[20px] mt-[10px] bg-gray-300 group-hover:text-sky-500">    pick me      </div>     <div class="w-[20px] h-[20px] mt-[10px] bg-gray-300"> pick me</div></div>

案例4: !, important 修改器。

由于权重问题,有时我们的的确确添加了两个相同功能类名,例如 bg-white, bg-black, 此时我们想强制让某一个生效,则可以使用 ! 修改器。这种情况可能出现在你想覆盖某个组件内样式的场景。

<div class="bg-white !bg-black"/>

任意属性值

在 tailwindcss 3.x 之后,tailwindcss 默认开启了JIT模式,该模式能够让我们书写动态 CSS 样式,不再受到静态规则集的限制。除了内置了一些常用属性值, 例如 ml-2 表示 margin-left: 0.5rem , 有时我们需要一些特定的值,tailwindcss 对这种情况提供了很好的支持,任何需要自定义值的属性,可以使用方括号来表示。例如

  1. w-full -> width: 100%
  2. w-[100px] -> width: 100px
  3. text-[#333]
  4. top-[10px]
  5. bg-[#999]

JIT模式的支持解决了传统 class 方案的不灵活的问题,从此再也不需要添加自定义样式。

响应式设计

对于一些需要 media query 实现的响应式样式,tailwindcss 提供了一组默认规则,使用 min-width 实现的断点功能实现响应式方案。默认的几个断点如下

min-width 表示当屏幕尺寸大于等于该值则应用此规则。所以 tailwindcss 的规则是基于 大于等于 做判断, 下图可以直观感受下每个选择器的范围。

这样的特性使得,我们的基础样式必须为小屏幕手机的才可以,所以 tailwindcss 具有移动端优先的特点。

<div className="flex flex-wrap sm:flex-nowrap"/>

上面的样式为:在小屏幕手机上应用 flex flex-wrap, 在大屏幕上应用 flex-nowrap.

自定义样式类

如果想在项目内添加自己实现的类,例如如下

.article {  position: relative;  color: orange;  line-height: 1.2;  font-family: ...;  @apply text-ellipsis;}

这里 tailwind 建议写在主文件(具有tailwind指令的文件)并放在对应的 layer 下,所以需要改成如下

@tailwind base;@tailwind components;@tailwind utilities;@layer components { .article {  position: relative;  color: orange;  line-height: 1.2;  font-family: ...;  @apply text-ellipsis; }}

原因是因为 css 类名都是一级类名,权重相同,只能使用顺序来确保覆盖规则,使用这个指令可以让生成的最终css文件内, article 类名在 utilities之前,这样当我们写如下样式

<div class="article absolute"/>

才能保证工具类名(absolute)正确覆盖组件类名(article)。放在 layer 代码块之内还有一个好处,tailwindcss 会自动帮我们剔除没有用到的类名,如果放在普通 css 代码块内则没有这种效果。

总结

我已经有好几个个人应用完全使用 tailwindcss 了,期间没有写过一行 css,经过实践验证目前的版本已经足够好用了(相比于2.x)。个人感觉该方案的书写便利程度远超 css-in-js, css module 等方案,推荐各位尝试,相信不会令大家失望。

参考

  1. CSS Utility Classes and "Separation of Concerns": https://adamwathan.me/css-utility-classes-and-separation-of-concerns/
  2. https://github.com/tailwindlabs/tailwindcss


作者:龙背上的骑兵

来源:微信公众号:相学长

出处
:https://mp.weixin.qq.com/s/YH3RCYGdvd67jkmN8DPIgg