我个人认为tailwind的基础概念有三部分:
我的分类并不科学,这三部分其实有些是互相重叠的,这篇文章就过一下这三部分内容。。当然不会事无巨细的过,更多详情还是要参考官方文档。
五个关键字,sm
, md
, lg
, xl
, 2xl
。分别对应640, 768, 1024, 1280, 1536三个横向尺寸。这五个关键字可以前缀冒号在所有的tailwind类名上。
这些关键字的意思是:屏幕宽度至少到达对应的标准,后续的样式才会被应用上。
比如md:w-32
的意思是:在屏幕尺寸超过768px时,才应用w-32
这个样式。
而没有带断点前缀的类,则会生效在任何宽度的屏幕上。比如我直接写个w-32
,它会在任何尺寸的屏幕上都生效,即便屏幕宽度只有40px。
所以在设计的时候,我觉得一个比较好的方式是先确定你的应用要使用几个断点。比如我们开发的应用准备支持三种形态:宽度小于640的极小屏幕,宽度在640~1024之间的中型屏幕,然后宽度大于1024的统一认为是大屏幕。那么实际上我们用到的断点只有两个:sm
和lg
。那么我们实际上要为页面写三套样式,分别是:
样式前缀 | 应用的屏幕尺寸 |
---|---|
无前缀 | 0~640px |
sm: 前缀 |
640px~1024px |
lg: 前缀 |
1024px~ |
这稍微有一点不直观,反直觉。tailwind还支持另一种响应式声明:我们上面说明白了,sm/md/lg/xl/2xl
这样的断点关键字指的是屏幕尺寸至少达到断点标准。还有max-sm/max-md/max-lg/max-xl/max-2xl
这样的断点关键字指的是屏幕尺寸至多达到断点标准。
所以如果你不嫌麻烦,我们也可以如下表达上面的例子:
样式前缀 | 应用的屏幕尺寸 |
---|---|
max-sm: |
0~640px |
sm:max-lg: |
640px~1024px |
lg: |
1024px~ |
如果你还是觉得别扭,tailwind支持你自定义断点定义
下面就是一个在tailwind.config.js
中自定义断点来实现上例需求的例子:
module.exports = {
theme: {
screens: {
"phone": {
"max": "639px"
},
"tablet": {
"min": "640px",
"max": "1023px"
},
"pc": {
"min": "1024px"
}
}
}
}
如果你采用了如上的配置,那么使用起来就比较符合直觉了:
样式前缀 | 应用的屏幕尺寸 |
---|---|
phone: |
0~640px |
tablet: |
640px~1024px |
pc: |
1024px~ |
另外虽然说响应式设计主要针对的是屏幕宽度进行,但你也可以在断点定义中自定义其它的屏幕属性,比如高度什么的。。不是很常用,这里就不提了。
CSS有个media query叫prefers-color-scheme
,它一般用来查询用户的agent或系统设置里是否开启了夜间模式。开启夜间模式的话,值是dark
,否则是light
。
所以一般支持夜间模式的App会如下这样写它的css
<style>
.box {
width:300px;
height:300px;
}
.theme-a {
background: #dca;
color: #731;
}
@media (prefers-color-scheme: dark) {
.theme-a.adaptive {
background: #753;
color: #dcb;
outline: 5px dashed #000;
}
}
</style>
<div class="box theme-a adaptive">Theme A (changed if dark preferred)</div>
简单来说就是先写一个基础样式,然后再使用@media(prefers-color-scheme:dark)
把夜间模式的样式写上。这样如果系统开启了夜间模式,.theme-a.adaptive
里的样式就会把.theme-a
里的样式覆盖掉。原理就是这么个原理。
上面这段代码在非夜间模式下的渲染结果如下:
在夜间模式开启的情况下的渲染结果如下:
在夜间模式下,浏览器是如下采用样式的:
tailwind里要做这个事情就简单了,给样式加dark:
前缀就行,比如在tailwind里,上面的例子就可以如下写:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
sapling: {
300: '#dca'
},
pueblo: {
900: '#731'
},
copper: {
700: '#753'
},
bone: {
200: '#dcb'
}
}
}
}
}
</script>
<title>Document</title>
</head>
<body>
<div class="w-[300px] h-[300px] bg-sapling-300 text-pueblo-900 dark:bg-copper-700 dark:text-bone-200 dark:outline-dashed dark:outline-black dark:outline-5">Theme A (changed if dark preferred)</div>
</body>
</html>
说到这里,推荐一个颜色分级生成工具:Color Generator
说这个东西之前,就需要先介绍下tailwind中的指令,有四个,分别是@tailwind
, @layer
, @apply
和@config
。其中@config
是个很无聊的指令,它是用来显式指定tailwind配置文件的路径的,这里就不提了,主要说另外三个。
@tailwind
这个指令是用来声明“你要用tailwind的哪部分功能的”,官方教程里的初始CSS文件里(即传递给tailwindcss
命令行的输入文件),让你加以下三行:
@tailwind base;
@tailwind components;
@tailwind utilities;
它们的意思如下:
指令 | 语义 |
---|---|
@tailwind base; |
生成基础样式类。基础样式是一些覆盖Agent默认行为的样式集合,比如取消默认marin,unstyle所有heading,list,将图片设定为block-level之类的基础样式 |
@tailwind components; |
生成组件样式类。目前我所知道唯一的组件样式类,就是container 。不过用户可以在这里面扩充。所谓的组件样式类,是对工具类的聚合和打包 |
@tailwind utilities; |
99%的tailwind知识点都在这里,几乎所有的自带的样式类都属于工具类。比如bg-xxx ,w-xx 之类的 |
换句话说,tailwind引擎为你生成的每一个类名,都归属于base
,components
或utilities
之一。这么说或许不准确,准确的说是:
base
container
以及用户自行扩充的自定义类名,属于components
utilities
那这三个兄弟,base
, components
, utilities
是个什么东西呢?它们叫“layer”。
你知道了“layer”的概念,下面我们就能比较合理的介绍@layer
这个指令:它就是用来扩充对应的“layer”的定义的
比如,你想自定义基础样式类。因为tailwind自带生成的基础样式类中,所有的<hx>
标签都是去除所有样式的,你不满意,你觉得还是要把<h1>
和<h2>
的字体调大一点,你就可以在你的tailwind样式文件中添加以下内容进行自定义:
@layer base {
h1 {
font-size: xx-large;
}
h2 {
font-size: x-large;
}
}
你正常写一个蓝色的按钮,需要如下写:
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Press Me</button>
你觉得太麻烦了,想把它的样式“封装”一下,你就可以在components
layer添加一个你自定义的组件样式集合,如下:
@layer components {
.btn-blue {
@apply bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded;
}
}
诶,出现新知识点了啊!这个@apply
又是什么意思呢?在上面我们扩充base
layer的时候,使用的是原生的CSS来扩充的。需要我们一个一个写样式。使用@apply
的意思就是:把标准utilities
layer里的工具类的样式,应用到这里来。
当然,同样的照猫画虎,你可以扩充utilities
层的类定义:更改现有工具类的定义,或者新增你自己的工具类,都行。
不过有两个有关@apply
的小坑需要注意一下:
使用@apply
生成的样式,默认是不带!important
声明的。如果需要,你要自己在尾巴后面把!important
写上,如下:
.btn {
@apply font-bold py-2 px-4 rounded !important;
}
像Vue或React这样的前端框架,都支持组件样式隔离:即每个组件都可以有自己独立的CSS代码。咱们这里不讨论具体框架,只说这么个意思:比如你的项目,有一个全局样式文件global.css
,然后有两个组件C1
和C2
。
这时,比如你在global.css
中扩充components
layer,自定义了一个组件类,如下:
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.card {
background-color: theme(colors.white);
border-radius: theme(borderRadius.lg);
padding: theme(spacing.6);
box-shadow: theme(boxShadow.xl);
}
}
然后你想在你的组件中使用这个组件类,但并不是以内联的形式使用它的,而是试图以如下的方式使用它(以下是伪码,既不是React也不是Vue,就说那么个意思):
<style scoped>
div {
@apply card;
}
</style>
<template>
<div>...</div>
</template>
你工具链是会报错的。这里报错的原因在于:多数前端框架在做打包整合时,处理组件的隔离样式,是独立且并行进行的。也就是说,C1
和C2
的编译,以及global.css
的编译是独立进行的,在编译过程中,tailwindcss
引擎是不知道card
这个类的定义到底在哪的,它就没法把这个@apply
翻译成正确合法的CSS代码。
如果你用内联的方式,把这个card
直接写在<div class="card">...</div>
里,倒还没有问题,因为这样写的话,编译打包工具链和tailwindcss引擎都不需要处理这个class
,也就不需要在编译打包的时候知道card
的具体定义到底在哪。
但你使用@apply
的话,就不行了:tailwindcss
必须知道card
的定义到底在哪里。
解决办法有两个:一个上面已经说了,内联的去使用自定义的类名。
另一个,就是通过tailwind插件的方式去扩展Layer定义,在tailwind配置文件里进行扩充,如下:
const plugin = require('tailwindcss/plugin')
module.exports = {
// ...
plugins: [
plugin(function ({ addComponents, theme }) {
addComponents({
'.card': {
backgroundColor: theme('colors.white'),
borderRadius: theme('borderRadius.lg'),
padding: theme('spacing.6'),
boxShadow: theme('boxShadow.xl'),
}
})
})
]
}
除了断点前缀sm/md/lg/xl/2xl
和夜间模式前缀dark
外,tailwind中还有巨多的前缀。巨灵活无比。这里简单的过一下这些前缀
这类前缀一般用来处理交互响应,典型的就是hover
,focus
和active
这三个前缀。它们基本都能与CSS原生的某个伪类选择器对上。这里就不一一列举了,简单的把它们分为三类:
分类 | 常用前缀 |
---|---|
处理交互 | hover , focus , active , visited , focus-within , focus-visible |
在列表或表格中选择元素,常用于<li> 和<tr> 上 |
first , last , odd , even |
表单相关 | required , invalid , disabled , read-only , indeterminate , checked |
伪类前缀还有两个特殊用法:一个是与父元素联动,一个是与兄弟元素联动
在父元素上应用group
类,然后在子元素上使用group[xxx]
来查询父元素的状态,从而有条件的在子元素上应用样式,如下示例:
<a href="#" class="group block max-w-xs mx-auto rounded-lg p-6 bg-white ring-1 ring-slate-900/5 shadow-lg space-y-3 hover:bg-sky-500 hover:ring-sky-500">
<div class="flex items-center space-x-3">
<svg class="h-6 w-6 stroke-sky-500 group-hover:stroke-white" fill="none" viewBox="0 0 24 24"><!-- ... --></svg>
<h3 class="text-slate-900 group-hover:text-white text-sm font-semibold">New project</h3>
</div>
<p class="text-slate-500 group-hover:text-white text-sm">Create a new project from a variety of starting templates.</p>
</a>
最外层的<a>
上应用了group
,里面两个孙子一个儿子都使用group-hover:stroke-white
或group-hover:text-white
来在父元素被hover时更改颜色,这个效果如下所示: