tailwindcss 学习笔记


tailwindcss

tailwindcss 这东西,并不能将当作替代 css 的东西来看。其主要作用统一项目 css 样式,不搞太多的自定义样式,在需要自定义的场景下大可以直接使用 css 来写,这俩是不冲突的。

原谅我一直都接受不了 tailwindcss 这个东西,目前只能作为规范化工具来使用了。

编译机制

有个常见误区,也就是 tail 是内置了大量的原子类用来实现现有的工作方式。其实并不是这样,tail 工作原理是在构建时扫描代码中的字符串,来生成对应的 css
因此对于如下情况,tail 并不会工作

1
<div :class="`columns-${ columnNum }`"></div>

tail 无法识别动态的字符串,你必须要在代码中使用完全静态的类名,例如下面这种 tail 是可以正常识别的:

1
2
3
4
5
<div :class="[
columnNum === 2 && 'columns-2',
columnNum === 3 && 'columns-3',
columnNum === 4 && 'columns-4'
]"></div>

父子节点

tail 支持通过 group 类名来指定父节点,然后便可以通过诸如 group-hover:xxx 的方式来在特定场景下控制子节点:

1
2
3
<div class="group">
<img class="group-hover:scale-110" />
</div>

上面这个案例意思就是,当 div 被 hover 时,放大 img 标签为 1.1 倍

命名 group

group 也可以命名,格式为 group/<命名>,对应的触发类名格式也相应变为 group-hover/<命名>。例如:

1
2
3
<div class="group/img">
<img class="group-hover/img:scale-110" />
</div>

不建议在整合样式里使用 group,有一定可能会出现样式不生效的情况。建议照常使用 css 的 hover

整合样式

把类名全都一股脑塞在 html 里显然是极度恶心的,tail 提供了类名整合的方式,通过 @apply 即可实现。例如现在有如下 vue 代码:

1
2
3
4
5
6
7
8
<template>
<div class="
pointer-events-none absolute inset-x-0 bottom-0
translate-y-full opacity-0
bg-black/55 px-3 py-2
transition-all duration-300 ease-out;
"></div>
</template>

使用 @apply 整合后的效果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
<div class="my-item"></div>
</template>

<style scope>
@reference "tailwindcss";

.my-item {
@apply pointer-events-none absolute inset-x-0 bottom-0
translate-y-full opacity-0
bg-black/55 px-3 py-2
transition-all duration-300 ease-out;
}
</style>

新版 tail 使用 apply 时必须要手动引入 tail 样式库,即 @reference "tailwindcss"; 这一行不能省略,否则会报错
且该行后面的 ; 千万不要省略,否则会导致后面紧跟的第一段样式无法被 tail 正确解析。造成一种“只有第一段无效,其他都有效”的诡异现象。

spacing 宽度

tail 通常使用一些简写+数字来设置 css 样式宽度,如

1
2
3
w-4 // width
p-2 // padding
mt-1 // margin-top

这套是 tailwindcss 默认的 spacing 方案,该方案的数字一般指代 0.25rem 的倍数关系,因此上述的三个样式可以翻译为:

1
2
3
width: 1rem;
padding: 0.5rem;
margin-top: 0.25rem;

又因为 rem 一般等于 16px,因此上述三个样式也可以翻译为:

1
2
3
width: 16px;
padding: 8px;
margin-top: 4px;

这样看下来,只要把 spacing 数字乘以 4 就可以得到对应的 px 值了。

任意值语法

如果无法接受 tail 的预设方案,也存在任意值写法:

1
w-[10px] // width: 10px;

不能出现的字符

任意值语法中,只有空格是不能出现的,需要使用 _ 特殊符号代替,其他的都可以直接写

1
shadow-[0_2px_10px_rgba(0,0,0,0.2)] // box-shadow: 0 2px 10px rgba(0,0,0,0.2);

使用 css 变量

1
bg-[var(--ui-bg)] // background-color: var(--ui-bg);

带上样式名

不想记 tail 类名的话,也可以直接把样式名写到任意值语法里

1
[column-fill:_balance] // column-fill: balance

css 选择器

任意值语法也可以控制 css 选择器:

1
<ul class="[&_li]:bg-red-500"></ul>

_ 代表空格,& 代表当前元素。上面这种写法等价于:

1
2
3
ul li {
background-color: var(--color-red-500)
}

类似还有其他选择器写法,参考表格:

写法 含义
[&_li] 所有后代 li
[&>li] 直接子 li
[&_p] 所有后代 p
[&>*] 所有直接子元素
[&_a:hover] 所有 a:hover

important 权重

在类名后追加 ! 即可提升样式权重,效果就是 css 的 !important,例如:

1
relative! // position: relative !important;

一些复合类名,添加 ! 后他下面的所有样式都会被追加 !important,例如 truncate

1
truncate!

渲染结果

1
2
3
4
5
.truncate\! {
overflow: hidden !important;
text-overflow: ellipsis !important;
white-space: nowrap !important;
}

四值类名

一些像 margin、padding 这种支持四个方向的样式,tail 提供的类名也有对应规律:

1
2
3
4
5
6
m-4 // margin: 1rem;
mx-4 // margin-left: 1rem; margin-right: 1rem;
my-4 // margin-top: 1rem; margin-bottom: 1rem;
mt-4 // margin-top: 1rem;
ml-4 // margin-left: 1rem;
-m-4 // margin: -1rem;

即:x 表示左右,y 表示上下,t 表示上,r 表示右,b 表示下,l 表示左,负号表示取反。

阴影/描边

tail 中控制 box-shadow 的类名为 shadow-xxx。但细心的话可以看到:当指着 shadow 类名时,编辑器给出的提示是 --tw-shadow:

这是因为 tail 实际上对 box-shadow 的操作是下面的 css 代码:

1
2
3
4
5
box-shadow: var(--tw-inset-shadow),
var(--tw-inset-ring-shadow),
var(--tw-ring-offset-shadow),
var(--tw-ring-shadow),
var(--tw-shadow);

tail 实现的一些样式会用到相同的 css 属性,为了避免互相覆盖,便使用了这种变量名的办法,即这里的 shadow 只负责控制 tw-shadow 变量。

描边

在这里与 shadow 公用 box-shadow 的是 ring,即外发光描边。这是一个 tail 自创的属性,旨在实现轮廓的效果。
之所以通过 box-shadow 来操作,是因为其他可实现类似效果的属性有各自的痛点:

  • border: 会影响盒模型宽高,影响布局,导致页面跳动,我们通常都不得追加一条 box-sizing: border-box;,但未显示设置宽高的盒子依然会有这个问题。
  • outline: 不能设置圆角、不支持偏移控制,无法实现复杂效果

ring 存在两种模式,这两种可以共存,即常规offset:

  • 常规:即最外层描边,宽度使用 ring-数字(单位 px)设置,颜色使用 ring-颜色-颜色比重 设置
  • offset: 内层描边,宽度使用 ring-offset-数字(单位 px)设置,颜色使用 ring-offset-颜色-颜色比重 设置

当两者共存时,结果就是常规描边在最外层,内层是 offset,如下面代码效果就是:8px 厚度的淡黄色描边 + 外层 2px 厚度的橙色描边

1
<div class="ring-2 ring-yellow-600 ring-offset-8 ring-offset-yellow-200"></div>

一些常用类名

space-y-4

等价为如下 css(不严谨,因为这个的实现其实比较麻烦),即相邻子元素之间存在 1rem 的间距(除第一个元素)

1
2
3
& > * + * {
margin-top: 1rem;
}

横向版本即 space-x-4

truncate

起到单行省略作用,即如下 css

1
2
3
4
5
.truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

line-clamp-2

起到多行省略作用,即如下 css

1
2
3
4
5
6
.line-clamp-2 {
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}