在nextjs下选择UI库有必要了解一下Tailwindcss(tailwindcss.com/docs/instal…)。
nextjs一些推荐的UI库都是基于Tailwindcss的。
官网给的例子
普通的css
TailwindCSS
javascript 代码解读复制代码class="chat-notification">
<div class="chat-notification-logo-wrapper">
<img class="chat-notification-logo" src="/img/logo.svg" alt="ChitChat Logo">
div>
<div class="chat-notification-content">
<h4 class="chat-notification-title">ChitChath4>
<p class="chat-notification-message">You have a new message!p>
div>
div>
<style>
.chat-notification {
display: flex;
align-items: center;
max-width: 24rem;
margin: 0 auto;
padding: 1.5rem;
border-radius: 0.5rem;
background-color: #fff;
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
.chat-notification-logo-wrapper {
flex-shrink: 0;
}
.chat-notification-logo {
height: 3rem;
width: 3rem;
}
.chat-notification-content {
margin-left: 1.5rem;
}
.chat-notification-title {
color: #1a202c;
font-size: 1.25rem;
line-height: 1.25;
}
.chat-notification-message {
color: #718096;
font-size: 1rem;
line-height: 1.5;
}
style>
css 代码解读复制代码<div class="p-6 max-w-sm mx-auto bg-white rounded-xl shadow-lg flex items-center gap-x-4">
<div class="shrink-0">
<img class="size-12" src="/img/logo.svg" alt="ChitChat Logo">
div>
<div>
<div class="text-xl font-medium text-black">ChitChatdiv>
<p class="text-slate-500">You have a new message!p>
div>
div>
带来的好处
- 不会浪费精力发明类名
- CSS 停止增长
- 做出改变感觉更安全
为什么不直接使用内联样式呢
- 有约束的设计。使用内联样式,每个值都是一个神奇的数字。使用实用程序,您可以从预定义的设计系统中选择样式,这使得构建视觉上一致的 UI 变得更加容易。
- 响应式设计。
- 悬停、焦点等状态。内联样式无法针对悬停或焦点等状态,但 Tailwind 的状态变体可以轻松地使用实用程序类来设置这些状态的样式。
缺点(文档没提)
个人有限的认知内觉得:
- 更多的学习成本,开发需要查阅文档。
- 在设计系统混乱的时候依旧需要大量的自定义样式来满足UI/UE需求。
- 平时使用的很多UI库并不是基于Tailwind CSS的,结合Tailwind CSS使用起来并不方便。
假设开始看tailwindcss.com/docs/config… 文档来写样式后,咱们还要了解TailwindCSS是如何生成的样式的。
- tailwind.config.ts
- postcss.config.js
- 全局的样式文件里的
less 代码解读复制代码@tailwind base;
@tailwind components;
@tailwind utilities;
PostCSS
- 不是 像
Sass
或Less
这样的样式预处理器。
- 是CSS语法转换的工具, 它允许您定义自定义 CSS 之类的语法,这些语法可以被插件理解和转换。
- 是 CSS 生态系统的重要参与者
Workflow 工作流程
-
写入一个包含字符串到 AST 转换的文件
-
将其拆分为词法分析/解析步骤(源字符串 → 标记 → AST)
- 这是我们在 PostCSS 中的做法,也是最流行的一种。很多解析器如@babel/parser (Babel 背后的解析器) 、 CSSTree都是这样编写的。将标记化与解析步骤分开的主要原因是性能和抽象复杂性。
- 首先,因为字符串到标记步骤比解析步骤花费更多时间。我们对大型源字符串进行操作并逐个字符地处理它,这就是为什么它在性能方面是非常低效的操作,我们应该只执行一次。
- 但从 token 到 AST 的转换在逻辑上更加复杂,因此通过这种分离,我们可以编写非常快的 tokenizer(但有时难以阅读代码)和易于阅读(但速度慢)的解析器。
分词器lib/tokenize.js
它接受 CSS 字符串并返回标记列表。
css 代码解读复制代码.className { color: #FFF; }
PostCSS 的相应标记将是
css 代码解读复制代码[ ["word", ".className", 1, 1, 1, 10]
["space", " "]
["{", "{", 1, 12]
["space", " "]
["word", "color", 1, 14, 1, 18]
[":", ":", 1, 19]
["space", " "]
["word", "#FFF" , 1, 21, 1, 23]
[";", ";", 1, 24]
["space", " "]
["}", "}", 1, 26]
]
让我们更仔细地看看像word
这样的单个标记。正如所说,每个token都代表一个列表并遵循这种模式。
arduino 代码解读复制代码const token = [
// represents token type
'word',
// represents matched word
'.className',
// This two numbers represent start position of token.
// It is optional value as we saw in the example above,
// tokens like `space` don't have such information.
// Here the first number is line number and the second one is corresponding column.
1, 1,
// Next two numbers also optional and represent end position for multichar tokens like this one. Numbers follow same rule as was described above
1, 10
]
解析器lib/parse.js , lib/parser.js
解析器生成一个称为抽象语法树(AST)的结构,稍后可以通过插件对其进行转换。
处理器lib/processor.js
处理器是一个非常简单的结构,用于初始化插件并运行语法转换
Stringifier lib/stringify.js , lib/stringifier.js
Stringifier 是一个基类,它将修改后的 AST 转换为纯 CSS 字符串。 Stringifier 从提供的 Node 开始遍历 AST,并调用相应的方法生成它的原始字符串表示形式。
PostCSS插件
TailwindCSS本质上是一个PostCSS插件,这里看下plugin.
修改PostCSS给我们的token.
css 代码解读复制代码const plugin = () => {
return {
postcssPlugin: 'to-red',
Rule (rule) {
console.log(rule.toString())
},
Declaration (decl) {
console.log(decl.toString())
decl.value = 'red'
}
}
}
plugin.postcss = true
await postcss([plugin]).process('a { color: black }', { from })
// => a { color: black }
// => color: black
// => a { color: red }
// => color: red
postcss.config.js
java 代码解读复制代码/** @type {import('postcss-load-config').Config} */
module.exports = {
plugins: {
'tailwindcss/nesting': {},
'tailwindcss': {},
'autoprefixer': {},
},
};
- /** @type {import('postcss-load-config').Config} */
类型注释:使用 TypeScript 提供类型检查,帮助开发者在 IDE 中获得更好的代码补全和提示。
导入三个插件
-
tailwindcss/nesting
- 支持 CSS 嵌套规则,类似 SCSS 中的嵌套语法。
- 将嵌套的 CSS 转换为标准的平铺样式,便于浏览器解析。
css 代码解读复制代码.btn {
color: white;
&:hover {
color: blue;
}
}
// 转换后
.btn {
color: white;
}
.btn:hover {
color: blue;
}
-
tailwindcss
-
核心插件,负责解析 Tailwind CSS 的类名(如 text-center
, bg-red-500
)并生成相应的 CSS 样式。
- 扫描配置文件
tailwind.config.js
中的配置,获取颜色、字体、间距等设计系统的自定义信息
-
提取类名:从 content
指定的文件中提取所有使用的 Tailwind 类名
-
生成 CSS
-
未被使用的类名会被剔除,生成的 CSS 文件体积更小。
执行流程:
-
tailwindcss/nesting
:先处理嵌套规则,生成平铺的 CSS。
-
tailwindcss
:解析 Tailwind 的类名,并生成对应的 CSS。
-
autoprefixer
:最后处理生成的 CSS,为需要的属性添加浏览器前缀。
工作整体流程:
less 代码解读复制代码@tailwind base;
@tailwind components;
@tailwind utilities;
.btn {
@apply bg-blue-500 text-white px-4 py-2;
&:hover {
background-color: blue;
}
}
-
tailwindcss/nesting
:
css 代码解读复制代码.btn {
@apply bg-blue-500 text-white px-4 py-2;
}
.btn:hover {
background-color: blue;
}
- tailwindcss
css 代码解读复制代码.bg-blue-500 {
background-color: #3b82f6;
}
.text-white {
color: #ffffff;
}
.px-4 {
padding-left: 1rem;
padding-right: 1rem;
}
.py-2 {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
}
.btn {
background-color: #3b82f6;
color: #ffffff;
padding-left: 1rem;
padding-right: 1rem;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
}
.btn:hover {
background-color: blue;
}
- 添加浏览器前缀
css 代码解读复制代码.btn:hover {
-webkit-background-color: blue;
background-color: blue;
}
小结:TailwindCSS作为一个PostCss的插件,将TailwindCSS定义的样式输出
tailwindcss.config.js
css 代码解读复制代码import type { Config } from 'tailwindcss';
// import { fontFamily } from 'tailwindcss/defaultTheme';
const config = {
darkMode: 'class',
content: [
'./app/**/*.{js,ts,jsx,tsx,mdx}', // Note the addition of the `app` directory.
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
// Or if using `src` directory:
'./src/**/*.{js,ts,jsx,tsx,mdx}',
],
prefix: '',
theme: {
container: {
center: true,
padding: '2rem',
screens: {
'2xl': '1400px',
},
},
extend: {
fontFamily: {
inter: ['var(--font-inter)'],
lexend: ['var(--font-lexend)'],
lexendBold: ['var(--font-lexend-bold)'],
},
backgroundImage: {
'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
'gradient-conic':
'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
},
colors: {
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))',
},
},
borderRadius: {
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)',
},
keyframes: {
'accordion-down': {
from: { height: '0' },
to: { height: 'var(--radix-accordion-content-height)' },
},
'accordion-up': {
from: { height: 'var(--radix-accordion-content-height)' },
to: { height: '0' },
},
},
animation: {
'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out',
},
typography: {
DEFAULT: {
css: {
'h2, h3, h4, h5, ul, ol': {
'margin-top': '1em',
'margin-bottom': '0.6em',
},
},
},
},
},
},
plugins: [
require('tailwindcss-animate'),
require('@tailwindcss/typography'),
require('daisyui'),
],
variants: {
extend: {
display: ['group-hover'],
},
},
} satisfies Config;
export default config;
darkMode: 'class'
-
作用:启用暗黑模式,基于类名切换(class
)。
-
默认情况下,Tailwind 支持的值为:
-
'media'
:根据系统偏好自动切换。
-
'class'
:需要手动添加 dark
类名来启用暗黑模式
content
css 代码解读复制代码content: [
'./app/**/*.{js,ts,jsx,tsx,mdx}',
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
'./src/**/*.{js,ts,jsx,tsx,mdx}',
],
指定 Tailwind 的内容扫描范围,告诉其从这些文件中提取使用的类名,以便生成对应的 CSS。
prefix
设置 Tailwind 的类名前缀
若有冲突,可自定义前缀。例如
vbnet 代码解读复制代码prefix: 'tw-',
使用时需写成 tw-bg-red-500
。
theme.container
yaml 代码解读复制代码container: {
center: true,
padding: '2rem',
screens: {
'2xl': '1400px',
},
},
作用:配置 Tailwind 的 .container
类。
-
center: true
:让容器居中对齐。
-
padding: '2rem'
:为容器内内容添加 2rem 的内边距。
-
screens
:设置不同屏幕大小下容器的宽度,示例中限制了 2xl
的最大宽度为 1400px。
plugins
css 代码解读复制代码plugins: [
require('tailwindcss-animate'),
require('@tailwindcss/typography'),
require('daisyui'),
],
作用:添加 Tailwind 的插件扩展。
-
tailwindcss-animate
:提供预定义的动画类。
-
@tailwindcss/typography
:为富文本内容(如 Markdown)提供默认样式。
-
daisyui
:一个组件库,提供丰富的 Tailwind 风格化组件。
一些基于Tailwindcss的UI库也会往这个配置文件输入内容,比如ui.shadcn.com/docs/compon…
比如安装accordion
sql 代码解读复制代码pnpm dlx shadcn@latest add accordion
1.会把accordion组件的源代码输入到components/ui文件夹下
javascript 代码解读复制代码"use client"
import * as React from "react"
import * as AccordionPrimitive from "@radix-ui/react-accordion"
import { ChevronDown } from "lucide-react"
import { cn } from "~/lib/utils"
const Accordion = AccordionPrimitive.Root
const AccordionItem = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
>(({ className, ...props }, ref) => (
<AccordionPrimitive.Item
ref={ref}
className={cn("border-b", className)}
{...props}
/>
))
AccordionItem.displayName = "AccordionItem"
const AccordionTrigger = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Header className="flex">
<AccordionPrimitive.Trigger
ref={ref}
className={cn(
"flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline text-left [&[data-state=open]>svg]:rotate-180",
className
)}
{...props}
>
{children}
<ChevronDown className="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" />
AccordionPrimitive.Trigger>
AccordionPrimitive.Header>
))
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
const AccordionContent = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Content
ref={ref}
className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
{...props}
>
<div className={cn("pb-4 pt-0", className)}>{children}div>
AccordionPrimitive.Content>
))
AccordionContent.displayName = AccordionPrimitive.Content.displayName
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
2.会把tailwindcss需要的样式输入到
tailwindcss.config.js
css 代码解读复制代码
keyframes: {
'accordion-down': {
from: {
height: '0',
},
to: {
height: 'var(--radix-accordion-content-height)',
},
},
'accordion-up': {
from: {
height: 'var(--radix-accordion-content-height)',
},
to: {
height: '0',
},
},
},
animation: {
'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out',
},
global.css
less 代码解读复制代码@tailwind base;
@tailwind components;
@tailwind utilities;
最后看看这三句
@tailwind
是 Tailwind CSS 提供的内置指令,用于在样式表中插入 Tailwind 的预定义样式。
@tailwind base;
-
作用:插入 Tailwind 的全局基础样式(Base Styles)。
- 包括现代浏览器的
CSS reset
(即消除浏览器默认样式的影响)。
- 提供一致的基础样式和排版规则(如字体大小、行高等)。
@tailwind components;
-
作用:插入 Tailwind 的预定义组件样式(Component Styles)。
- 提供可复用的、半抽象的样式(例如按钮样式、表单样式等)。
- 这些样式的复杂度介于基础样式和工具类之间。
@tailwind utilities;
-
作用:插入 Tailwind 的工具类样式(Utility Classes)。
- 工具类 是 Tailwind 的核心,例如
text-center
、bg-red-500
、flex
等。
- 这些类的职责单一,通常只修改某个具体的 CSS 属性值。
总结
TailwindCSS作为一个PostCSS的插件,使用PostCss生成css的能力:将className对应的样式生成为css,生成的过程中提供配置的能力。
TailwindCSS内置了自己的原子样式,会把这些样式输出。
提供了配置的能力,自定义样式。
具体的配置的能力平时按照文档查阅就行。
评论记录:
回复评论: