ok,经过学习Tailwindcss我决定将此专栏建设成为一个Tailwindcss实战专栏,我将在专栏内完成
5050
挑战:50天50个Tailwindcss练习项目
,欢迎大家订阅!!!
Tailwind CSS v4 带来了更强大的主题定制能力,给了前端开发者更加灵活的设计空间。
今天,我们将来学习 Tailwind CSS v4 的主题相关特性,并且给出一套实践模板 🚀
主题相关核心 API 介绍
Tailwind v4 主题使用 @theme
定义,并支持直接使用 CSS 变量来简化主题设计。下面是官方文档链接:
下面介绍集中常见的主题相关的指令以及自定义变体。
1. @theme
元素
Tailwind v4 引入 @theme
用来定义 CSS 变量,与v3版本需要在 tailwind.config.js
里配置相比,更为简洁统一。
css
@theme {
--color-primary: var(--primary);
--radius-md: calc(var(--radius) - 2px);
}
2. @custom-variant
Tailwind v4 支持自定义优先级,实现更灵活的 dark mode 、 sidebar mode 等 UI 体验的切换。
css
@custom-variant dark (&:is(.dark *));
定义了一个名为 dark 的自定义变体,它会匹配所有带有 .dark 类的元素内部的所有元素。
3. @layer
虽然 Tailwind 能满足大部分样式需求,但有时候还是需要编写纯 CSS。这时,@layer
指令就登场了!
- 使用
@layer
指令告诉 Tailwind 一组自定义样式属于哪个bucket
。有效图层为base
、components
和utilities
。- 三个图层的生效顺序是不一致的,作用在同一元素上的相同的样式属性,生效优先级顺序是:
utilities
>components
>base
- 三个图层的生效顺序是不一致的,作用在同一元素上的相同的样式属性,生效优先级顺序是:
- 使用
@apply
将任何现有工具类内联到你自己的自定义 CSS 中 - 你仍然可以在你的css文件中写出任何原生的css样式,但是通常不建议这么做,这会增加维护难度。
css
@layer base {
body {
@apply bg-background text-foreground;
}
}
@layer components {
card {
@apply bg-card text-card-foreground;
}
}
@layer utilities {
.heading {
@apply text-4xl font-bold;
}
}
利用Theme
实现主题切换
我们可以通过 :root
和 .dark
样式规则来分别定义不同主题模式下的 变量 值,然后配合 @theme
以及 自定义变体
展开 Tailwind 内部设计。
css
@custom-variant dark (&:is(.dark *));
@theme {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
}
:root {
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--radius: 0.625rem;
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
}
.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.145 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.145 0 0);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.985 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.396 0.141 25.723);
--destructive-foreground: oklch(0.637 0.237 25.331);
--border: oklch(0.269 0 0);
--input: oklch(0.269 0 0);
--ring: oklch(0.439 0 0);
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.205 0 0);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(0.269 0 0);
--sidebar-ring: oklch(0.439 0 0);
}
... 其他主题样式
最佳实践:切换主题按钮组件
下面是我创建的用于切换主题的按钮组件,大家可以参考一下:
html
<template>
<button
@click="toggleTheme"
class="flex items-center gap-2 rounded bg-gray-200 px-4 py-2 text-gray-800 transition-colors duration-300 hover:bg-gray-300 dark:bg-gray-700 dark:text-gray-100 dark:hover:bg-gray-600">
<span v-if="isDark">🌙 暗黑</span>
<span v-else>☀️ 亮色</span>
</button>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const isDark = ref(false)
const toggleTheme = () => {
isDark.value = !isDark.value
document.documentElement.classList.toggle('dark')
localStorage.setItem('theme', isDark.value ? 'dark' : 'light')
}
onMounted(() => {
const theme = localStorage.getItem('theme')
isDark.value = theme === 'dark'
if (isDark.value) {
document.documentElement.classList.add('dark')
}
})
</script>
其他
当然tailwindcss v4支持丰富的 自定义样式
复杂样式
以及 自定义变体
,今天们探讨一些常用的主题配置和规则。
Tailwind CSS v4 主题相关引入,让你从分散的属性处理升级到全局统一。