Tailwind CSS 在 Typecho 中的完整教程


📚 教程目录

  1. Tailwind CSS 是什么?
  2. 环境准备与安装
  3. 基础配置
  4. 在主题中使用
  5. 响应式设计
  6. 暗黑模式
  7. 与 project_rules.md 结合
  8. 常见问题

一、Tailwind CSS 是什么?

Tailwind CSS 是一个实用优先(Utility-first)的 CSS 框架。和传统框架(如 Bootstrap)提供预定义组件(.btn.card)不同,Tailwind 提供的是原子化的工具类:

/* 传统写法 */
.btn-primary {
  background: #20a53a;
  color: white;
  padding: 10px 20px;
  border-radius: 4px;
}

/* Tailwind 写法 - 直接在 HTML 中使用工具类组合 */
<button class="bg-green-600 text-white px-4 py-2 rounded">
  按钮
</button>

✨ 核心优势

特性说明
开发效率无需写 CSS,直接在 HTML 中组合类名
体积小构建时自动删除未使用的类,最终 CSS 文件极小
无命名冲突不需要担心类名重复
响应式内置md:lg: 前缀轻松实现响应式
可定制性强通过配置文件自定义主题

二、环境准备与安装

2.1 两种安装方式对比

方式适用场景优点缺点
CDN 方式快速测试、小工具无需配置,直接可用体积大(~3MB),无法定制
构建方式正式主题、长期维护按需编译,可定制,体积小需要 Node.js 环境

推荐:正式项目使用构建方式,符合 project_rules.md 的规范要求。

2.2 构建方式安装步骤

步骤1:进入主题目录

cd usr/themes/your-theme-name

步骤2:初始化 npm 项目

npm init -y

步骤3:安装 Tailwind CSS 及依赖

npm install -D tailwindcss postcss autoprefixer cssnano
注:postcssautoprefixer 是 Tailwind 的必备依赖

步骤4:初始化 Tailwind 配置

npx tailwindcss init -p

这个命令会创建两个文件:

  • tailwind.config.js - Tailwind 主配置文件
  • postcss.config.js - PostCSS 配置文件

三、基础配置

3.1 配置 tailwind.config.js

/** @type {import('tailwindcss').Config} */
module.exports = {
  // 告诉 Tailwind 扫描哪些文件中的类名
  content: [
    './*.php',
    './templates/**/*.php',
    './assets/js/**/*.js',
    '../../plugins/**/*.php',  // 如果需要扫描插件
  ],
  theme: {
    extend: {
      // 扩展主题色 - 适配您的宝塔绿色调
      colors: {
        primary: {
          DEFAULT: '#20a53a',
          50: '#f0fdf4',
          100: '#dcfce7',
          500: '#20a53a',
          600: '#18802c',
          700: '#166b34',
        },
      },
      // 扩展间距,匹配 project_rules.md 中的 mt-1,2,3
      spacing: {
        '18': '4.5rem',
        '88': '22rem',
      },
    },
  },
  plugins: [],
  // 暗黑模式支持(后面会详细讲)
  darkMode: 'class',
}

3.2 创建主 CSS 文件

在主题目录下创建 style.css(如果已存在则修改):

/* style.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

/* 自定义组件层 - 复用 project_rules.md 的工具类 */
@layer components {
  /* 卡片组件 */
  .card {
    @apply bg-white rounded-lg shadow-md p-6;
    @apply dark:bg-gray-800 dark:text-white;
  }
  
  .card__header {
    @apply border-b border-gray-200 pb-4 mb-4;
    @apply dark:border-gray-700;
  }
  
  .card__title {
    @apply text-xl font-bold text-gray-900;
    @apply dark:text-gray-100;
  }
  
  /* 按钮组件 */
  .btn-primary {
    @apply px-4 py-2 bg-primary text-white rounded-md;
    @apply hover:bg-primary-600 transition-colors;
    @apply focus:outline-none focus:ring-2 focus:ring-primary-500;
  }
  
  /* 复用 project_rules.md 的工具类 */
  .text-muted {
    @apply text-gray-500 dark:text-gray-400;
  }
  
  .mt-1 {
    @apply mt-1;
  }
  
  .mt-2 {
    @apply mt-2;
  }
  
  .mt-3 {
    @apply mt-3;
  }
}

3.3 配置 package.json 脚本

{
  "scripts": {
    "dev": "npx tailwindcss -i ./style.css -o ./assets/css/style.min.css --watch",
    "build": "npx tailwindcss -i ./style.css -o ./assets/css/style.min.css --minify"
  }
}

3.4 开发流程

# 开发时:监听文件变化,实时编译
npm run dev

# 生产环境:编译并压缩
npm run build

3.5 在主题中引用

header.php 中添加:

<!-- 引用编译后的 CSS 文件 -->
<link rel="stylesheet" href="<?php $this->options->themeUrl('assets/css/style.min.css'); ?>">

四、在主题中使用

4.1 基础用法示例

文章列表卡片

<!-- index.php -->
<div class="container mx-auto px-4 py-8">
  <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
    <?php while($this->next()): ?>
      <article class="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow">
        <!-- 文章缩略图 -->
        <?php if ($this->fields->thumbnail): ?>
          <img src="<?php $this->fields->thumbnail(); ?>" 
               class="w-full h-48 object-cover">
        <?php endif; ?>
        
        <div class="p-6">
          <!-- 标题 -->
          <h2 class="text-xl font-bold mb-2">
            <a href="<?php $this->permalink() ?>" 
               class="hover:text-primary-600 transition-colors">
              <?php $this->title() ?>
            </a>
          </h2>
          
          <!-- 元信息 -->
          <div class="flex items-center text-sm text-muted mb-4">
            <span class="mr-4">
              <svg class="w-4 h-4 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" 
                      d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
              </svg>
              <?php $this->date('Y-m-d'); ?>
            </span>
            <span><?php $this->category(','); ?></span>
          </div>
          
          <!-- 摘要 -->
          <p class="text-muted mb-4 line-clamp-3">
            <?php $this->excerpt(100, '...'); ?>
          </p>
          
          <!-- 阅读更多 -->
          <a href="<?php $this->permalink() ?>" 
             class="inline-flex items-center text-primary hover:text-primary-600">
            阅读更多
            <svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" 
                    d="M9 5l7 7-7 7" />
            </svg>
          </a>
        </div>
      </article>
    <?php endwhile; ?>
  </div>
</div>

分页导航

<!-- 分页部分 -->
<div class="flex justify-center mt-8">
  <div class="flex space-x-2">
    <?php $this->pageNav('«', '»', 1, '...', [
      'wrapTag' => 'div',
      'wrapClass' => 'flex space-x-2',
      'itemTag' => 'span',
      'currentClass' => 'px-3 py-2 bg-primary text-white rounded',
      'prevClass' => 'px-3 py-2 border border-gray-300 rounded hover:bg-gray-100',
      'nextClass' => 'px-3 py-2 border border-gray-300 rounded hover:bg-gray-100',
    ]); ?>
  </div>
</div>

评论列表

<!-- comments.php -->
<div class="mt-8">
  <h3 class="text-lg font-bold mb-4">评论</h3>
  
  <?php $this->comments()->to($comments); ?>
  <?php while($comments->next()): ?>
    <div class="flex space-x-4 mb-6 p-4 bg-gray-50 rounded-lg">
      <div class="flex-shrink-0">
        <img src="<?php $comments->gravatar(40); ?>" 
             class="w-10 h-10 rounded-full">
      </div>
      <div class="flex-1">
        <div class="flex items-center justify-between mb-2">
          <span class="font-medium"><?php $comments->author(); ?></span>
          <span class="text-sm text-muted"><?php $comments->date('Y-m-d H:i'); ?></span>
        </div>
        <div class="text-gray-700">
          <?php $comments->content(); ?>
        </div>
      </div>
    </div>
  <?php endwhile; ?>
  
  <!-- 评论表单 -->
  <?php if($this->allow('comment')): ?>
    <form method="post" action="<?php $this->commentUrl() ?>" class="mt-6">
      <h4 class="font-medium mb-4">发表评论</h4>
      
      <?php if(!$this->user->hasLogin()): ?>
        <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
          <input type="text" name="author" placeholder="昵称 *" required
                 class="form-control w-full px-3 py-2 border border-gray-300 rounded-md">
          <input type="email" name="mail" placeholder="邮箱 *" required
                 class="form-control w-full px-3 py-2 border border-gray-300 rounded-md">
          <input type="url" name="url" placeholder="网站"
                 class="form-control w-full px-3 py-2 border border-gray-300 rounded-md">
        </div>
      <?php endif; ?>
      
      <textarea name="text" rows="4" placeholder="评论内容..." required
                class="form-control w-full px-3 py-2 border border-gray-300 rounded-md mb-4"></textarea>
      
      <button type="submit" class="btn-primary">
        提交评论
      </button>
    </form>
  <?php endif; ?>
</div>

4.2 常用工具类速查表

类别Tailwind 类作用
布局container mx-auto居中容器
flexgrid弹性/网格布局
grid-cols-33列网格
gap-4网格间距
间距p-4内边距 1rem
px-4左右内边距
mt-2上外边距 0.5rem
space-x-4子元素水平间距
排版text-xl字体大小 1.25rem
font-bold粗体
text-center居中
line-clamp-3最多3行后省略
颜色text-primary主色文字
bg-gray-100浅灰背景
border-gray-300灰色边框
交互hover:bg-primary-600悬停变深
transition-shadow阴影过渡
cursor-pointer指针样式

五、响应式设计

Tailwind 采用 移动优先(Mobile First) 的响应式策略。断点前缀表示 min-width

前缀断点说明
sm:640px小屏幕
md:768px中等屏幕
lg:1024px大屏幕
xl:1280px超大屏幕
2xl:1536px巨幕

5.1 响应式示例

<div class="
  <!-- 手机:单列,小字体 -->
  grid grid-cols-1 text-sm
  <!-- 平板:两列,中等字体 -->
  md:grid-cols-2 md:text-base
  <!-- 桌面:三列,大字体 -->
  lg:grid-cols-3 lg:text-lg
  <!-- 间距也响应 -->
  gap-4 md:gap-6 lg:gap-8
">
  <!-- 内容 -->
</div>

5.2 复杂响应式示例

<!-- 侧边栏布局:手机垂直,桌面水平 -->
<div class="flex flex-col md:flex-row">
  <!-- 侧边栏:手机占满,桌面固定宽度 -->
  <aside class="
    w-full md:w-64
    bg-gray-50 p-4
    md:bg-transparent
  ">
    侧边栏内容
  </aside>
  
  <!-- 主内容:自适应 -->
  <main class="flex-1 p-4">
    主内容
  </main>
</div>

六、暗黑模式

6.1 启用暗黑模式

tailwind.config.js 中配置:

module.exports = {
  darkMode: 'class',  // 或 'media'(跟随系统)
  // ...
}

6.2 创建暗黑模式切换脚本

在主题中创建 assets/js/theme.js

// theme.js
(function() {
  // 获取保存的主题或跟随系统
  const getTheme = () => {
    const saved = localStorage.getItem('theme');
    if (saved) return saved;
    
    return window.matchMedia('(prefers-color-scheme: dark)').matches 
      ? 'dark' 
      : 'light';
  };
  
  // 应用主题
  const applyTheme = (theme) => {
    if (theme === 'dark') {
      document.documentElement.classList.add('dark');
    } else {
      document.documentElement.classList.remove('dark');
    }
    localStorage.setItem('theme', theme);
  };
  
  // 初始化
  applyTheme(getTheme());
  
  // 切换函数
  window.toggleTheme = () => {
    const isDark = document.documentElement.classList.contains('dark');
    applyTheme(isDark ? 'light' : 'dark');
  };
})();

footer.php 中引入:

<script src="<?php $this->options->themeUrl('assets/js/theme.js'); ?>"></script>

6.3 暗黑模式切换按钮

<button onclick="toggleTheme()" 
        class="p-2 rounded-lg bg-gray-200 dark:bg-gray-700 
               text-gray-900 dark:text-gray-100
               hover:bg-gray-300 dark:hover:bg-gray-600
               transition-colors">
  <!-- 太阳/月亮图标 -->
  <svg class="w-5 h-5 dark:hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" 
          d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" />
  </svg>
  <svg class="w-5 h-5 hidden dark:block" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" 
          d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />
  </svg>
</button>

6.4 暗黑模式样式示例

/* 在 @layer components 中 */
@layer components {
  .card {
    @apply bg-white text-gray-900;
    @apply dark:bg-gray-800 dark:text-gray-100;
  }
  
  .nav-link {
    @apply text-gray-700 hover:text-primary;
    @apply dark:text-gray-300 dark:hover:text-primary-400;
  }
  
  .border-default {
    @apply border-gray-200;
    @apply dark:border-gray-700;
  }
}

七、与 project_rules.md 结合

7.1 迁移现有工具类

project_rules.md 中的工具类用 Tailwind 实现:

project_rules.md 类Tailwind 实现说明
.text-mutedtext-gray-500 dark:text-gray-400灰色文字
.text-successtext-green-600成功绿色
.text-dangertext-red-600危险红色
.text-centertext-center居中
.mt-1mt-1上边距 0.25rem
.mt-2mt-2上边距 0.5rem
.mt-3mt-3上边距 0.75rem
.d-flexflex弹性布局
.justify-betweenjustify-between两端对齐
.cardbg-white rounded-lg shadow p-4卡片容器

7.2 保留兼容层

style.css 中添加兼容类,确保旧代码也能工作:

/* 兼容 project_rules.md 旧类名 */
@layer utilities {
  .text-muted {
    @apply text-gray-500 dark:text-gray-400;
  }
  
  .text-success {
    @apply text-green-600;
  }
  
  .text-danger {
    @apply text-red-600;
  }
  
  .btn-sm {
    @apply px-3 py-1.5 text-sm;
  }
  
  .btn-lg {
    @apply px-6 py-3 text-lg;
  }
}

7.3 资源加载顺序

根据 project_rules.md 的要求:

<!-- header.php -->
<!-- 1. 基础样式 -->
<link rel="stylesheet" href="/tools/common/css/base.css">

<!-- 2. Tailwind 生成的样式 -->
<link rel="stylesheet" href="<?php $this->options->themeUrl('assets/css/style.min.css'); ?>">

<!-- 3. 主题私有样式(如有需要) -->
<link rel="stylesheet" href="<?php $this->options->themeUrl('style.css'); ?>">

八、常见问题

Q1: 为什么我添加的 Tailwind 类不起作用?

A: 检查以下几点:

  • tailwind.config.js 中的 content 配置是否正确包含你的 PHP 文件
  • 是否运行了 npm run buildnpm run dev
  • 是否正确引入了编译后的 CSS 文件

Q2: 如何添加自定义颜色?

A: 在 tailwind.config.jstheme.extend.colors 中添加:

module.exports = {
  theme: {
    extend: {
      colors: {
        brand: {
          light: '#3abff8',
          DEFAULT: '#0284c7',
          dark: '#0b5e8c',
        },
      },
    },
  },
}

使用:text-brandbg-brand-light

Q3: 如何使用任意值(如特殊宽度)?

A: 使用方括号语法:

<div class="w-[139px] h-[77px] bg-[#165DFF]">
  自定义尺寸和颜色
</div>

Q4: 如何复用重复的类组合?

A: 使用 @apply 在 CSS 中提取组件:

@layer components {
  .blog-card {
    @apply bg-white rounded-lg shadow-md p-6 hover:shadow-lg transition-shadow;
  }
}

Q5: 生产环境 CSS 文件太大怎么办?

A: Tailwind 在生产模式 (npm run build) 会自动清除未使用的类。确保 content 配置正确即可。

Q6: 可以在插件中使用 Tailwind 吗?

A: 可以。两种方式:

  1. 独立编译:插件自带 Tailwind 配置,独立生成 CSS
  2. 共用主题:在主题的 tailwind.config.jscontent 中添加插件路径
content: [
  './**/*.php',
  '../../plugins/PluginName/**/*.php',  // 扫描插件
]

📝 总结

通过本教程,您已经掌握了:

  1. ✅ Tailwind CSS 的核心概念和优势
  2. ✅ 在 Typecho 主题中的完整安装配置流程
  3. ✅ 响应式设计和暗黑模式的实现方法
  4. ✅ 如何与现有的 project_rules.md 规范结合

推荐的开发流程

# 1. 进入主题目录
cd usr/themes/your-theme

# 2. 安装依赖(只需一次)
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

# 3. 开发时监听
npm run dev

# 4. 开发完成,编译生产版本
npm run build

已有 33 条评论

    1. ChrisH ChrisH

      The note about autoprefixer being required - saved me from a potential headache. Would have forgotten that dependency.

    2. 小丸子 小丸子

      最喜欢那个卡片组件的写法,@apply把一堆类名打包成一个,模板里看着清爽多了。打算把我所有的组件都这样重构一遍。

    3. MichaelT MichaelT

      This tutorial bridges the gap between "Tailwind for modern frameworks" and "Tailwind for traditional PHP CMS". Much needed resource.

    4. 阿强 阿强

      Typecho的插件开发也能用上这个思路,把content指向插件目录就行。准备用这个给客户写个自定义插件,样式统一又轻量。

    5. NinaL NinaL

      The hover and focus variants in the btn-primary example show good accessibility practices. Not just styling for idle states.