Published on

译: 值得了解的Tailwind CSS技巧

Authors
  • avatar
    Name
    Deng Hua
    Twitter

本文翻译至: Tailwind CSS Tips and Tricks Worth Knowing

目录

动态类名

Tailwind 通过移除未使用的class来保证代码在拥有一定样式功能的同时也能控制好CSS bundle的大小。因此,如果要使用动态类名,则需要写入完整的类名。这是为了让 Tailwind 能够静态分析您的代码。

例如,这样的代码是行不通的:

const DoesntWork = () => {
  const colors = ['red', 'green', 'yellow'];
  const [color, setColor] = React.useState(colors[0]);
  const changeColor = () => {
    setColor('green');
  };
  return (
    <>
      <div className={`w-40 h-40 border bg-${color}-500`}></div>
      <select
        value={color}
        className={`bg-${color}-500`}
        onChange={(e) => setColor(e.target.value)}
      >
        <option value="">choose</option>
        {colors.map((c) => (
          <option key={c} value={c}>
            {c}
          </option>
        ))}
      </select>
      <button onClick={changeColor}>Change color</button>
    </>
  );
};

因为 Tailwind 无法静态地找到它的类。需要bg-${color}-500在运行时进行计算。如果我们使用了完整的类名,Tailwind 编译器就可以正常工作:

const Works = () => {
  const colors = ['bg-red-500', 'bg-green-500', 'bg-yellow-500'];
  const [color, setColor] = React.useState(colors[0]);
  const changeColor = () => {
    setColor('bg-green-500');
  };
  return (
    <>
      <div className={`w-40 h-40 border ${color}`}></div>
      <select
        value={color}
        className={`${color}`}
        onChange={(e) => setColor(e.target.value)}
      >
        <option value="">choose</option>
        {colors.map((c) => (
          <option key={c} value={c}>
            {c}
          </option>
        ))}
      </select>
      <button onClick={changeColor}>Change color</button>
    </>
  );
};

在CSS中使用Tailwind

有些我们需要使用CSS。例如,使用第三方库时。

我们可以通过使用@apply指令或theme函数来使用 Tailwind的颜色样式。让我们看一个代码示例:

.__some-external-class {
  /* 使用 @apply 可以混入任何Tailwind */
  @apply text-blue-300 bg-gray-300 py-2 px-6 rounded-lg uppercase;

  /* 或者使用 theme() 函数混入 */

  color: theme('colors.blue.300');
  background-color: theme('colors.gray.300');
  padding: theme('padding.2') theme('padding.6');
  border-radius: theme('borderRadius.lg');
  text-transform: uppercase;
}

注意:@applytheme()不能公共使用,只能二选一。

任意值

在 Tailwind 中编写纯 CSS 的另一种方法是使用括号 ([])。 这就是所谓的“任意值”。您可以执行如下操作:

<div class="w-[100vw] bg-[rebbecapurple]"></div>

您还可以使用theme函数:

<div class="grid grid-cols-[fit-content(theme(spacing.32))]">
  <!-- ... -->
</div>

从v3.3开始,如果你想引用一个CSS定制属性,就不需要使用var关键词。您可以简单地将 CSS 变量作为任意值传入:

/* style.css */
:root {
  --myColor: rgb(168, 198, 31);
}
<h3 class="text-[--myColor]">h3</h3>

类的编组与同级标注

Tailwind 允许我们根据元素的状态使用帮助类(如 :hover:checked:disabled:focus等)更改元素的样式(您可以在此处找到它们)。因此,我们很容易做到这样的事情:

<button class="
    bg-purple-500
    border
    border-blue-500
    text-white
    text-2xl
    uppercase
    p-6
    rounded-md
    m-4
    transition-colors
    hover:bg-purple-800
    hover:border-blue-200
    hover:text-gray-200"
>
  Click me!
</button>

效果是这样

如果我们想根据另一个元素的状态更改样式怎么办?这就是 PeerGroup 实用类派上用场的地方。

基于父状态的样式

例如,当在父元素上悬停时,我们可以通过将父元素转换为组并使用groupgroup-hover:实用程序类来更改子元素的样式:

  <div class="relative rounded-xl overflow-auto p-8">
    <a
      href="#"
      class="
          group
          block
          max-w-xs
          mx-auto
          rounded-lg
          p-4
          bg-white
          ring-1
          ring-slate-900/5
          shadow-lg
          space-y-3
        hover:bg-sky-500
        hover:ring-sky-500"
    >
      <div class="flex items-center space-x-3">
        <svg class="h-6 w-6 stroke-sky-500 group-hover:stroke-white" fill="none" viewBox="0 0 24 24">
          <!-- ... -->
        </svg>
        <h3 class="text-sm text-slate-900 font-semibold group-hover:text-white">New project</h3>
      </div>
      <p class="text-sm text-slate-500 group-hover:text-white">
        Create a new project from a variety of starting templates.
      </p>
    </a>
  </div>

效果如下:

有更多的帮助程序类来修改子元素,这几乎适用于每个伪类修饰符(这里是完整列表)。

基于同级状态的样式

peer类修饰符可用于根据元素的同级状态设置元素的样式。您可以使用 peer-{modifier},其中{modifier} 可以是任何伪类修饰符。

下面是一个简单的示例:

<div class="flex flex-col items-center gap-20 p-10 bg-pink-400">
  <p class="peer cursor-pointer">I am sibling 1</p>
  <p class="peer-hover:text-white">I am sibling 2</p>
</div>

当我们将鼠标悬停在sibling1上时,sibling 2文本颜色将更改:

sibling2

这在设置一些列表元素的active状态时将非常有用。

您可以命名名称

使用grouppeer ,你可以提供唯一的名称来区分组方和同级方。

这是通过添加到/{name}任一帮助程序类来完成的,例如:

<div class="group/main w-[30vw] bg-purple-300">
<div class="group/hello peer/second h-20 w-full flex flex-col items-center justify-around">
  <p class="group-hover/hello:text-white">Hello Wolrd!</p>
  <p>All this code belogs to us</p>
</div>
<div class="peer-hover/second:bg-red-400 w-[200px] h-[200px] bg-blue-300">
</div>
<div class="group-hover/main:bg-green-200 peer-hover/main:bg-red-400 w-[200px] h-[200px] bg-orange-300">
</div>

动画程序类

Tailwind 有一些非常有用且易于使用的动画程序类。例如,我们可以添加过渡颜色类并设置 300 毫秒的持续时间,以在悬停时创建平滑的颜色变化效果。我们还可以传递动画曲线和动画的延迟:

<div class="... hover:bg-gray-300 transition-colors duration-300 ease-in-out" ></div>

几乎任何可动画属性都可供您使用(有关完整列表,请参阅此处)。

除此之外,还有一些预制动画可用,例如:animate-spinanimate-pinganimate-bounceanimate-pulse

响应式

Tailwind 是一个移动端优先框架,这意味着un-prefixed(无前缀)的功能类对所有屏幕尺寸都有效,而带前缀的实用程序会覆盖特定断点及以上断点处的样式。这有助于首先编写 CSS 移动设备,因为您需要从小屏幕到大屏幕进行定义。

假设我们想要一个图片或视频网格。我们希望我们的设计在移动设备上为一列,然后在较大的屏幕上为 2 列,在桌面设备上为 3 列,如下所示:

可以这样写:

<div class="grid grid-cols-1 gap-10 p-5 sm:grid-cols-2 md:grid-cols-3">
  <div class="w-full aspect-video bg-cyan-500"></div>
  <div class="w-full aspect-video bg-cyan-500"></div>
  <div class="w-full aspect-video bg-cyan-500"></div>
  <div class="w-full aspect-video bg-cyan-500"></div>
  <div class="w-full aspect-video bg-cyan-500"></div>
  <div class="w-full aspect-video bg-cyan-500"></div>
</div>

创建自定义程序类

我们可以使用 Tailwind 配置文件来创建我们自己的自定义程序类。如果我们想在应用程序的多个位置使用特定样式,这非常有用。

比如,如果我们想添加另一个盒子阴影类,那么我们可以这样做:

// tailwind.config.js

module.exports = {
  content: ['./src/**/*.{html,js}'],
  theme: {
    extend: {
      boxShadow: {
        // Note that we can use the theme function in here as well
        neon: "0 0 5px theme('colors.purple.200'), 0 0 20px theme('colors.purple.700')"
      }
    }
  },
}

然后我们可以在我们的代码中使用它:

<div class="w-20 h-10 rounded shadow-neon"></div>

Tailwind 中的任何内容都可以扩展或覆盖。

创建自定义的Tailwind 插件

如果我们希望能够通过传递颜色来选择自定义实用程序的颜色,我们需要制作自己的自定义 Tailwind 插件。这有点意料之外,但它允许我们创建非常灵活且可重用的实用程序。

让我们重用我们的霓虹灯阴影示例。要添加该功能,我们可以转到 tailwind.config.js 并定义我们的插件:

// tailwind.config.js

const plugin = require('tailwindcss/plugin');

module.exports = {
  content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
  theme: { /* ... our previous config */ },
  plugins: [
    // to get the colors we can use the "theme" property
    plugin(({ theme, addUtilities }) => {
      const neonUtilities = {};
      const colors = theme('colors');

      // loop through the colors
      for (const color in colors) {
        // Check if color is an object as some colors in
        // Tailwind are objects and some are strings
        if (typeof colors[color] === 'object') {
          // we opt in to use 2 colors
          const color1 = colors[color]['500'];
          const color2 = colors[color]['700'];

          // Here we build the actual class name
          neonUtilities[`.neon-${color}`] = {
            boxShadow: `0 0 5px ${color1}, 0 0 20px ${color2}`,
          };
        }
      }
      // this adds the utility classes to Tailwind
      addUtilities(neonUtilities);
    }),
  ],
}

然后我们可以直接在 HTML(或 JSX)中使用新创建的实用程序类:

<div class="m-20 w-20 h-10 rounded-lg neon-blue"></div>

提示: 我们可以在 Tailwind 调色板中更改为我们想要的任何颜色:

custom-class

将 Tailwind 颜色导入为对象

我们可以将 Tailwind 颜色作为 JavaScript 中的对象导入。如果我们想使用 Tailwind 颜色来创建我们自己的自定义主题,或者向调色板添加另一个名称,这将非常有用。

例如,我们可以创建一个 primary 颜色并将其添加为类:

// import all colors from Tailwind
const colors = require('tailwindcss/colors');

module.exports = {
  content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
  theme: {
    extend: {
      colors: {
        // set "primary" class name to desired color + set default.
        primary: { ...colors.sky, DEFAULT: colors.sky['600'] },
      },
    },
  },
  plugins: [],
};

然后使用:

  <h1 class="text-primary-600">h1</h1>
  <h2 class="bg-primary-300">h2</h2>

使用 variants

构建UI组件时的一个常见用例是具有某种基本或默认样式,可以通过传递类或props来覆盖这些样式。

tailwind-merge软件包对于处理此问题非常有用。它允许我们将基础类作为第一个参数传递,将类作为第二个参数传递,确保我们的类名覆盖默认类(有关其工作原理的更深入探讨,请参阅此视频)。

下面是一个示例:

import { twMerge } from 'tailwind-merge'

const SpecialButton: React.FC<{ className?: string }> = ({ className }) => {
  return (
    <button className={
      twMerge('px-2 py-1 bg-red-500 hover:bg-red-800', className)}
    >
      Click me!
    </button>
  )
}

// Then we can override the style like so:

// some-other-component.js
const Component = () => {
  <div>
    <h1>Hello!</h1>
    <SpecialButton className="bg-blue-500 hover:bg-blue-800" />
  </div>
}

tailwind-merge配合clsx

实际开发中,tailwind-merge包一般还会配合clsx包一起使用。

可以写一个工具函数:

import { clsx } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs) {
  return twMerge(clsx(inputs))
}

就可以像这样使用了:

<p
  className={cn(
    'text-sm text-muted-foreground/80',
    condition1 && "last:block",
    conditoin2 === 0 && "hidden"
  )}
>
  {/* ... */}
</p>

tailwind-merge配合cva

CVA 是一个帮助您以更优雅的方式创建variants的包。为了使其与 tailwind-merge 一起工作,我们可以执行以下操作:

import { cva } from "class-variance-authority";
import { twMerge } from "tailwind-merge";

const buttonVariants = cva(["your", "base", "classes"], {
  variants: {
    intent: {
      primary: ["your", "primary", "classes"],
    },
  },
  defaultVariants: {
    intent: "primary",
  },
});

export const buttonClasses = (variants: ButtonVariants) => twMerge(buttonVariants(variants));

详情请看:cva


轻松的使用渐变

您可以使用渐变色标创建复杂的渐变。为此,我们可以使用该bg-gradient-to-类并将其与t(顶部)、r(右)、 b (下)和l(左)组合。我们还可以使用tr(右上角)、 bl (左下角)等来表示角。

然后我们可以组合:fromtovia做一些令人惊叹的渐变。

让我们看一些例子:

<div class="bg-gradient-to-r from-indigo-500 ...">

渲染的输出将是一个渐变,从靛蓝开始,然后淡入透明:

要设置结尾,我们可以使用 to-:

<div class="bg-gradient-to-r from-indigo-500 to-pink-400 ...">

要添加花样,我们可以通过在之间使用 via 来控制中间的颜色:

<div class="bg-gradient-to-r from-indigo-500 via-green-400 to-pink-400 ...">

轻松的截断文本

另一个好用的实用类是 line-clamp,它允许您通过简单地添加一个数字来截断多行文本,例如line-clamp-3:

<article class="border border-slate-300 rounded-md p-4 ml-6 text-white w-60">
  <p class="line-clamp-3">
    Nulla dolor velit adipisicing duis excepteur esse in duis nostrud
    occaecat mollit incididunt deserunt sunt. Ut ut sunt laborum ex
    occaecat eu tempor labore enim adipisicing minim ad. Est in quis eu
    dolore occaecat excepteur fugiat dolore nisi aliqua fugiat enim ut
    cillum. Labore enim duis nostrud eu. Est ut eiusmod consequat irure
    quis deserunt ex. Enim laboris dolor magna pariatur. Dolor et ad sint
    voluptate sunt elit mollit officia ad enim sit consectetur enim.
  </p>
</article>

渲染结果将在第 3 行文字后加上省略号:

Nulla dolor velit adipisicing duis excepteur esse in duis nostrud occaecat mollit incididunt deserunt sunt. Ut ut sunt laborum ex occaecat eu tempor labore enim adipisicing minim ad. Est in quis eu dolore occaecat excepteur fugiat dolore nisi aliqua fugiat enim ut cillum. Labore enim duis nostrud eu. Est ut eiusmod consequat irure quis deserunt ex. Enim laboris dolor magna pariatur. Dolor et ad sint voluptate sunt elit mollit officia ad enim sit consectetur enim.

设计 un-styleable 样式

<input type="checkbox"> 这样的样式一直以来都很难处理。有了 accent-{color} 修饰符,再也不是问题:

<label>
  <input type="checkbox" checked > Browser default
</label>
<label>
  <input type="checkbox" class="accent-pink-500" checked> Customized
</label>
checkbox

Container查询

令很多人感到兴奋的新功能之一。它们允许根据元素本身的大小应用样式。现在在 Tailwind 中有一个插件可以开始使用它们,称为 @tailwindcss/container-queries

将此插件添加到您的项目后,可以标记@container一个元素,子元素可以使用variants,例如 @sm@md

安装插件:

npm install @tailwindcss/container-queries

添加到tailwind.config.js中:

// tailwind.config.js
module.exports = {
  theme: {
    // ...
  },
  plugins: [
    require('@tailwindcss/container-queries'),
    // ...
  ],
}

首先使用 @container 类将元素标记为容器,然后使用 @md:@lg:@xl 等容器变体,根据该容器的大小来应用样式。

<div class="@container">
  <div class="@lg:text-sky-400">
    <!-- ... -->
  </div>
</div>

End.