Tailwind CSS

与Boostrap等框架的主要区别是, Tailwind的级别更低, 它的类名几乎等同于CSS样式.
由于Tailwind这种方式, 导致它使用了很多 简写 以缩短类名长度,
这也使得学习Tailwind变得困难, 因为基于样式的CSS类名实在是太多太杂了.
编写时经常需要 依赖编辑器的智能感应.
断点是用于响应式设计的尺寸前缀, 使用这些前缀的类名只在满足相关媒体查询条件时生效.
<img class="w-16 md:w-32 lg:w-48">
前缀 最小宽度
sm (small) 640px
md (medium) 768px
lg (large) 1024px
xl (extra large) 1280px
2xl 1536px
Tailwind提倡 优先使用无前缀的类名, 然后根据屏幕尺寸进一步调整细节, 而不是直接使用有前缀的类名.
breakpoint的缺陷在于它的容器大小是通过硬编码max-width设定的,
除了无媒体查询的情况(宽度小于最小的breakpoint), 没有办法让容器匹配窗口宽度:
如果用户需要在平板设备上增加元素的边距, 但又想使用与手机一样的动态宽度, 则无法通过breakpoint做到.
https://github.com/tailwindlabs/tailwindcss/issues/1102#issuecomment-663754366
移动优先的设计看似很好, 但实际使用时常常遇到问题:
因为移动设备的样式是最需要被特别对待的,
这就导致经常需要先设置一个无前缀的移动样式, 然后再在更高的断点处取消这些样式.
这种先设置再取消的设计方式让人觉得非常愚蠢, 并且对那些在大屏幕上内容更丰富的设计相当不利.
与响应式断点类似, 可以通过前缀给元素增加样式.
混合使用前缀时, 响应式前缀应放在最前.
出于文件大小方面的考虑, Tailwind在默认情况下并非所有类名都支持状态前缀, 默认值需要查询官方文档:
https://tailwindcss.com/docs/hover-focus-and-other-states
如果tailwind的变体不够使用, 用户可以通过编写插件的方式自定义变体.
需要注意的是, 在哲学层面上, Tailwind不鼓励使用伪元素,
建议在创建DOM时生成真实的元素来替代伪元素(元素需要设置 aria-hidden="true").
前缀 意义
hover 悬停时
focus 元素获得焦点时(典型例子是input元素获得焦点)
focus-within 在子元素获得焦点时
focus-visible 在使用键盘获得焦点时(浏览器支持不全面, 需要polyfill)
active 元素处于活动状态时(典型例子是处于被按下状态的按钮)
disabled 元素被禁用的情况下
visited 链接已访问过的情况下
checked 单选框或复选框被选中的情况下
first 该元素是父元素的第一个子元素的情况下
last 该元素是父元素的最后一个子元素的情况下
odd 该元素是父元素的奇数子元素的情况下
even 该元素是父元素的偶数子元素的情况下
group-hover 在子元素上使用此前缀, 在具有group类名的父元素悬停时生效
group-focus 在子元素上使用此前缀, 在具有group类名的父元素获得焦点时生效
motion-safe 这是一个与媒体查询特性prefers-reduced-motion(减少运动动画效果)有关的前缀,
其目的是标记那些不容易造成眩晕的运动动画, 可以与其他前缀配合使用.
motion-reduce 与motion-safe类似, 其目的是标记那些容易造成眩晕的运动动画,
被标记的动画会在用户设置减少动画效果的情况下被禁用.
dark 黑暗模式(如果混用前缀, 则该前缀需要紧跟在响应式前缀之后, 在其他状态前缀之前)
Tailwind会设置一层被称作Preflight的默认样式, 它相当于modern-normalize加上一些自以为是的默认样式.
用户可以通过@layer指令或编写插件以添加默认样式.
Tailwind内置了一套人工定义的调色板.
出于一些需要, 比如支持切换主题, 或者已经有完整的颜色系统时, 也可以自定义颜色名.
自定义颜色的最佳实践是覆盖内置的颜色名, 这能防止那些依赖Tailwind颜色的插件/功能/样式意外失效.
存在客观原因阻止开发人员使用自定义的颜色名:
  • Tailwind的一些核心功能依赖内置颜色名, 因此如果使用自定义颜色名代替内置颜色名,
    将导致那些依赖内置颜色名的样式无法正常工作.
    举例来说, preflight会为元素设置border-color, 设置的值是gray.DEFAULT,
    因此要求颜色主题包含 gray.DEFAULT 这个颜色.
  • 制作颜色系统很难, 需要高度专业的技能, 如果没有成熟稳定的颜色系统, 则建议使用内置颜色名.
Tailwind目前缺乏对before, after伪元素的内置支持.
https://github.com/tailwindlabs/tailwindcss/discussions/2119#discussioncomment-200587
https://github.com/croutonn/tailwindcss-pseudo-elements
在JIT模式下, 可以使用before, after等伪元素.
与JIT模式使用相同语法的fallback:
https://github.com/shimyshack/tailwindcss-pseudo-element-plugin
m{t|r|b|l}-{size}
mx-5 x轴上的margin, 单位长度5.
my-5 y轴上的margin, 单位长度5.
-m{side?}-{size}
space是一种为子元素设置margin的快捷方式.
https://tailwindcss.com/docs/space
space-x-{amount}
w-auto 自动宽度
w-screen 视口宽度, 即100hw.
w-{number} 根据设计系统决定的固定宽度, 例如8, 12, 16, 24
w-{}/{} 由分数来决定宽度, 可以任意分母分子, 例如 w-1/2 是全宽度的一半(50%),
w-full 全宽度, 即100%.
min-w-{number}
min-w-full
min-w-min min-width: min-content
min-w-max max-width: max-content
max-w-{number}
max-w-none max-width: none
max-w-xs
max-w-sm
max-w-md
max-w-lg
max-w-xl
max-w-2xl
max-w-full
max-w-prose Tailwind认为的最佳阅读宽度(65ch, ch是字符0的宽度)
基本与width一致, 只是 w 换成了 h.
Preflight取消了很多元素的默认样式, 比如h1, h2等标题元素,
这导致对于那些类名不可控制的元素来说(比如由CMS输出的HTML), 这些元素的样式是缺失的.
为了解决此问题, Tailwind提供了一套名为 @tailwindcss/typography 的样式, 以实现这种排版需求.
该项目本质上是一个有上千行样式的主题文件.
这个库的自定义功能非常低级, 以至于很难相信它在未来是可维护的.
直接编写CSS进行排版显然会更有可读性.
该插件为元素提供固定的高宽比.
自Tailwind CSS 3.0开始, 核心已经支持CSS原生的aspect-ratio, 但出于浏览器兼容性考虑, 仍然建议使用此插件.
该插件为表单提供样式.
bg-fixed 相对视口固定背景图片
bg-local
bg-scroll
bg-clip-border 背景覆盖至border
bg-clip-padding 背景覆盖至padding
bg-clip-content 背景只覆盖content
bg-clip-text 背景只覆盖文本内容, 可与透明文本配合制作出渐变色的文本
bg-opacity-{amount}
amount的取值范围为[0, 100], 步进5.
bg-auto
bg-cover
bg-contain
bg-{key}
需要手动配置 theme.extend.backgroundImage 来设定key的值, 以引入图片资源.
bg-none
bg-graident-{direction} 使用渐变色作为背景, 具体的渐变色是通过特定的CSS变量配置的.
这些渐变色CSS变量不应该手动设置, 而是使用graident color stops间接配置.
这是一组与渐变色有关的CSS变量的配置类名, 需要与 bg-graident-{direction} 一同使用.
https://tailwindcss.com/docs/border-radius
rounded-none
rounded-sm
rounded
rounded-md
rounded-lg
rounded-xl
rounded-2xl
rounded-3xl
rounded-full 圆形
rounded-{t|r|b|l}{-size?} 单独设置每个面(相邻的两个角)
rounded-{tl|tr|br|bl}{-size?} 单独设置每个角
border-opacity-{amount}
amount的取值范围为[0, 100], 步进5.
border-solid
border-dashed
border-dotted
border-double
border-none
divide用于在子元素之间添加边框.
divide-{x/y}-{width}
divide-{x/y}
divide-{x/y}-reverse 用于倒序排列的子元素(比如有 flex-row-reverse)
https://github.com/tailwindlabs/tailwindcss/discussions/2466
建议手动在子元素上设置padding, 而不是使用space.
盒阴影的颜色固定为黑色, 仅可调整阴影大小.
Tailwind的轮廓是通过不模糊的 box-shadow 实现的.
可以与 shaodw-{size} 系列一起使用.
ring-{width}
ring
ring-inset
ring-{color}-{amount}
ring-opacity-{amount}
带有偏移的轮廓会向外延伸.
ring-offset-{width}
可以修改偏移的颜色.
ring-offset-{color}-{amount}
Tailwind有三个默认的font-family类名, 但都只有英文字体:
  • font-sans
  • font-serif
  • font-mono
通常需要在 theme.fontFmaily 里配置中文字体.
italic 斜体
not-italic 非斜体
text-transparent
text-current 等同于 color: currentColor.
似乎没有意义, currentColor关键字通常用在 color 以外的属性上, 以引用 color 的值.
text-black
text-white
text-{color}-{amount}
color的可选值:
  • gray
  • red
  • yellow
  • green
  • blue
  • indigo
  • purple
  • pink
text-opacity-{amount}
amount的取值范围为[0, 100], 步进5.
此属性需要搭配文本颜色类名使用, 不应该单独使用或依赖于继承值.
truncate 强制单行显示, 在文本溢出时显示省略号, 相当于:
  • overflow: hidden
  • text-overflow: ellipsis
  • white-space: nowrap.
overflow-ellipsis 在文本溢出时显示省略号.
overflow-clip 在文本溢出时截断(text-overflow 的默认值).
  • break-words (即 overflow-wrap: break-word) 对于超长单词会在任意处插入分词符以使其强制换行.
    这种换行在大多数情况下都有效.
    比较奇葩的是在Chromium浏览器下它跟已经被淘汰的 word-break: break-word 行为不一致, 以至于存在一些不会被正确断行的边缘案例.
    因此仍然推荐使用 word-break: break-word 而不是 overflow-wrap: break-word.
  • break-all 对任何超出当前行显示范围的单词都插入分词符来强制换行而不是将单词放到下一行.
    这通常不会被考虑, 因为强制将单词换行后, 文本的可读性很差.
whitespace-normal 默认, 换行符和空格会被折叠成一行, 在显示时根据容器宽度换行.
whitespace-nowrap 常用, 换行符和空格会被折叠成一行, 在显示时不换行.
whitespace-pre 保留换行符与空格, 在显示时不换行, 通常用于显示具有缩进的代码.
whitespace-pre-line 保留换行符但不保留空格, 在显示时根据容器宽度换行.
whitespace-pre-wrap 保留换行符与空格, 在显示时换行.
Tailwind没有提供较新的 white-space: break-spaces, 该值的行为与pre-wrap类似,
区别在于它会在空白字符中间换行.
Tailwind作为英语世界的框架, 不支持CJK专用的line-break.
通常需要将此值设置为anywhere来让中文的符号能够被强制换行: line-break: anywhere;
尽管在大多数情况下都不推荐使用此方法.
Tailwind社区有一个可怕的倾向: 将组件作为可以复制粘贴的模板分享.
有相当比例的与Tailwind强关联的组件库采用了这种"封装"方式, 个人认为这种做法完全是Bootstrap的翻版.
https://tailwindcss.com/docs/adding-custom-styles#adding-base-styles
可以通过 @apply 指令直接在CSS里为HTML常见元素设置基本样式.
它并不能真正替代其他形式的重用, 因为即使是常见元素, 使用相同的基本样式的可能性仍然很低.
这使得我们的CSS类名退化到传统的语义化方案.
接受Tailwind原子化理念的用户通常会天然抵触这种解决方案.
在渲染后, 组件类会隐藏掉原子化类名, 这使得调试变得困难.
Tailwind建议使用组件而不是类名进行抽象.
为此需要写类似的样板代码:
import { forwardRef } from 'react'
import { twMerge } from 'tailwind-merge'
// 不带有ref转发
export function Input(props: React.ComponentPropsWithoutRef<'input'>) {
return (
<input
{...props}
className={twMerge('default_classnames', props.className)}
/>
)
}
// 带有ref转发
export const Input = forwardRef(function Input(
props: React.ComponentPropsWithoutRef<'input'>
, ref: React.ForwardedRef<HTMLInputElement>
) {
return (
<input
{...props}
ref={ref}
className={twMerge('default_classnames', props.className)}
/>
)
})
https://github.com/ben-rogerson/twin.macro
基本上算是Tailwind版本的styled-components了.
它的工作原理是在编译时将类名转换为对应的CSS样式对象, 这实际上会影响对Tailwind的调试.
https://github.com/dcastil/tailwind-merge
该库提供了一个twMerge函数, 可以有效地用靠后的样式覆盖掉靠前的同类样式.
这解决了使用classnames等方案动态生成样式时的主要痛点: 难以控制样式的优先级.
该库同时提供了一个twJoin函数, 它是classnames/clsx的替代品.
const className = `mx-auto ${isHidden ? 'hidden' : ''}`
Tailwind使用tailwind.config.js作为配置文件, 用户的配置会被合并进默认配置.
默认配置:
https://github.com/tailwindlabs/tailwindcss/blob/master/stubs/defaultConfig.stub.js
自Tailwind 3.0开始成为默认选项的编译模式, 具有诸多优点:
  • 快得多的编译速度.
  • 支持使用任意值, 样式可以临时脱离设计系统.
  • 样式按需生成, 无需配置tailwind.config.js就可以支持所有变体.
  • 支持important修饰符, 可以在类名最前加上!开启 !important.
在Next.js里使用JIT模式时, 必须删除 .next 目录, 否则一些类名将无法正常工作.
  • https://github.com/ikcb/animated-tailwindcss