首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

nextjs初探之TailwindCSS和PostCSS

  • 24-12-16 16:08
  • 3255
  • 31611
juejin.cn

在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 工作流程

image.png

  1. 写入一个包含字符串到 AST 转换的文件

  2. 将其拆分为词法分析/解析步骤(源字符串 → 标记 → AST)

    1. 这是我们在 PostCSS 中的做法,也是最流行的一种。很多解析器如@babel/parser (Babel 背后的解析器) 、 CSSTree都是这样编写的。将标记化与解析步骤分开的主要原因是性能和抽象复杂性。
    2. 首先,因为字符串到标记步骤比解析步骤花费更多时间。我们对大型源字符串进行操作并逐个字符地处理它,这就是为什么它在性能方面是非常低效的操作,我们应该只执行一次。
    3. 但从 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 文件体积更小。

执行流程:

  1. tailwindcss/nesting:先处理嵌套规则,生成平铺的 CSS。
  2. tailwindcss:解析 Tailwind 的类名,并生成对应的 CSS。
  3. autoprefixer:最后处理生成的 CSS,为需要的属性添加浏览器前缀。

工作整体流程:

less
代码解读
复制代码
@tailwind base; @tailwind components; @tailwind utilities;  .btn { @apply bg-blue-500 text-white px-4 py-2;  &:hover { background-color: blue; } } 
  1. tailwindcss/nesting:
css
代码解读
复制代码
.btn { @apply bg-blue-500 text-white px-4 py-2; } .btn:hover { background-color: blue; } 
  1. 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; }
  1. 添加浏览器前缀
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内置了自己的原子样式,会把这些样式输出。

提供了配置的能力,自定义样式。

具体的配置的能力平时按照文档查阅就行。

注:本文转载自juejin.cn的西部荒野子的文章"https://juejin.cn/post/7448832434387091490"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

104
前端
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top