「对不起,这次破戒了,不是全网首发,但依旧是原创精选」
写在前文
最近我自己也体验了一下 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 只看名称就能唯一确定属性,符合开发直觉。
block
-> display: block
absolute
-> position: absolute
m-auto
-> margin: auto
p-auto
-> padding: auto
mx-2
-> margin-left: 0.5rem; margin-right: 0.5rem
对于一些 css 通用名称,tailwindcss提供了统一的规范。例如 left, right, top, bottom, 分别对应 l, r, t, b。left-right 对应 x, top-bottom 对应 y。 所以产生了下面的类名:
ml-[2px]
-> margin-left: 2px;
mr-[2px]
-> margin-right: 2px;
mx-[2px]
-> margin-left: 2px; margin-right: 2px;
pt-2
-> padding-top: 0.5rem;
pb-2
-> padding-bottom: 0.5rem;
border-r
-> border-right-width: 1px;
border-b
-> border-bottom-width: 1px;
所以如果想写一个上边框黑色,上下外边距 2px, 上内边距10px,可以如下:
<div class="border-t border-black my-[2px] pt-[10px] "/>
其他简写也会符合越常用越简写的原则,来看一些案例
bg-black
-> background: black
bg-[url(/img.png)]
-> background: url(/img.png)
bg-norepeat
-> background: no-repeat
text-[#333]
-> color: #333
w-[200px]
-> width: 200px
h-[200px]
-> height: 200px
whitespace-nowrap
-> white-space: nowrap
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]">
再有一些类名也大致满足如上规则,前期需要对照文档查每个属性的类名,习惯之后可以凭借记忆和规则猜出类名。
在 tailwindcss 中,修改器语法为 修改器:类名 修改器的种类很多且可扩展。内置的修改器如下:
案例 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 对这种情况提供了很好的支持,任何需要自定义值的属性,可以使用方括号来表示。例如
w-full
-> width: 100%
w-[100px]
-> width: 100px
text-[#333]
top-[10px]
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 等方案,推荐各位尝试,相信不会令大家失望。
欢迎点赞、关注、转发~