乐闻世界logo
搜索文章和话题

How to dynamically build classnames in tailwindcss

6 个月前提问
2 个月前修改
浏览次数398

6个答案

1
2
3
4
5
6

Tailwind CSS 是一个实用工具优先(utility-first)的 CSS 框架,它通过提供数以千计的小型类(如 text-centermt-4 等)来帮助开发者快速构建用户界面。在默认情况下,Tailwind 生成的类名是静态的,并且包含在生成的样式表中。然而,开发者可能需要根据程序的状态动态构建这些类名。

在使用 Tailwind CSS 时,可以通过几种方式来动态地构建类名:

  1. JavaScript 模板字符串: 如果你正在使用JavaScript来动态生成HTML,或者使用现代的前端框架,比如React、Vue或Angular,你可以使用模板字符串(Template Literals)来根据条件拼接类名。

    例如,在React中:

    jsx
    function Button({ primary }) { const btnClass = `px-4 py-2 border ${ primary ? 'bg-blue-500 text-white' : 'bg-transparent text-blue-500' }`; return <button className={btnClass}>Click me</button>; }

    在这个例子中,根据 primary 属性的值,按钮的类名会动态变化。

  2. 计算属性: 在像Vue这样的框架中,可以使用计算属性(computed properties)来动态生成类名。

    例如,在Vue中:

    vue
    <template> <button :class="buttonClass">Click me</button> </template> <script> export default { props: ['primary'], computed: { buttonClass() { return { 'px-4 py-2 border': true, 'bg-blue-500 text-white': this.primary, 'bg-transparent text-blue-500': !this.primary, }; } }, }; </script>

    在这个例子中,buttonClass 计算属性会返回一个对象,其中包含应该应用于按钮的类名。

  3. 类名函数: 有时,你可能会写一个函数来生成类名,这在任何 JavaScript 环境中都是可行的。

    例如:

    javascript
    function createButtonClass(size) { return `px-4 py-2 text-${size} border`; } const smallButtonClass = createButtonClass('sm'); const largeButtonClass = createButtonClass('lg');
  4. Tailwind 插件: Tailwind CSS 允许通过插件扩展其功能。你可以创建自定义插件来根据需要动态生成样式,尽管这通常是在构建过程中完成的,而不是在客户端运行时。

总结一下,虽然你不能在浏览器中直接让 Tailwind 动态产生未在构建时生成的新类名,但你可以使用JavaScript的逻辑来动态地组合已经存在的类名,并根据应用的状态来切换这些类名。这些方法可以让开发者使用 Tailwind 的实用性而不牺牲动态性。

2024年6月29日 12:07 回复

So after finding out that this way of working is not recommended and that JIT doesn't support it (Thanks to the generous commenters). I have changed the approach to a more 'config' based approach.

Basically I define a const with the basic configuration for the different props and apply those to the component. It's a bit more maintenance work but it does the job.

Here is the example of a config. (Currently without typing) and up for some better refactoring but you'll get the idea.

shell
const buttonConfig = { // Colors primary: { bgColor: 'bg-primary-500', color: 'text-white', outline: 'border-primary-500 text-primary-500 bg-opacity-0 hover:bg-opacity-10', }, secondary: { bgColor: 'bg-secondary-500', color: 'text-white', outline: 'border-secondary-500 text-secondary-500 bg-opacity-0 hover:bg-opacity-10', }, // Sizes small: 'px-3 py-2', medium: 'px-4 py-2', large: 'px-5 py-2', };

Then I just apply the styling like so:

shell
<motion.button whileTap={{ scale: 0.98 }} className={` rounded-lg font-bold transition-all duration-100 border-2 focus:outline-none ${buttonConfig[size]} ${outlined && buttonConfig[color].outline} ${buttonConfig[color].bgColor} ${buttonConfig[color].color}`} onClick={onClick} type="button" tabIndex={0} > {children} </motion.button>
2024年6月29日 12:07 回复

this way of writing Tailwind CSS classes is not recommended. Even JIT mode doesn't support it, to quote Tailwind CSS docs: "Tailwind doesn’t include any sort of client-side runtime, so class names need to be statically extractable at build-time, and can’t depend on any sort of arbitrary dynamic values that change on the client"

2024年6月29日 12:07 回复

this might be a bit late, but for the people bumping this thread.

the simplest explaination for this is;

Dynamic Class Name does not work unless you configured Safelisting for the Dynamic class name,

BUT, Dynamic Class works fine so long as its a full tailwind class name.

its stated here

this will not work

shell
<div class="text-{{ error ? 'red' : 'green' }}-600"></div>

but this one works

shell
<div class="{{ error ? 'text-red-600' : 'text-green-600' }}"></div>

its states;

As long as you always use complete class names in your code, Tailwind will generate all of your CSS perfectly every time.

the longer explanation;

Tailwind will scan all the files specified in module.exports.content inside tailwind.config.js and look for tailwind classes, it does not even have to be in a class attribute and can even be added in commented lines, so long as the full class name is present in that file and class name is not dynamically constructed; Tailwind will pull the styling for that class,

so in your case, all you have to do is put in the full class name inside that file for all the possible values of your dynamic class something like this

shell
<button className={ color === 'primary' ? 'bg-primary-500' : 'bg-secondary-500'}> {children} </button>

or the method I would prefer

shell
<!-- bg-primary-500 bg-secondary-500 --> <button className={`bg-${color}-500 `}> {children} </button>

here's another example, although its Vue, the idea would be the same for any JS framework

shell
<template> <div :class="`bg-${color}-100 border-${color}-500 text-${color}-700 border-l-4 p-4`" role="alert"> test </div> </template> <script> /* all supported classes for color props bg-red-100 border-red-500 text-red-700 bg-orange-100 border-orange-500 text-orange-700 bg-green-100 border-green-500 text-green-700 bg-blue-100 border-blue-500 text-blue-700 */ export default { name: 'Alert', props: { color: {type: String, default: 'red'} } } </script>

and the result would be this

shell
<Alert color="red"></Alert> <!-- this will have color related styling--> <Alert color="orange"></Alert> <!-- this will have color related styling--> <Alert color="green"></Alert> <!-- this will have color related styling--> <Alert color="blue"></Alert> <!-- this will have color related styling--> <Alert color="purple"></Alert> <!-- this will NOT have color related styling as the generated classes are not pre-specified inside the file -->
2024年6月29日 12:07 回复

EDIT: Better implementation 2022 - https://stackoverflow.com/a/73057959/11614995

Tailwind CSS does not support dynamic class names (see here). However, there's still a way to accomplish this. I needed to use dynamically build class names in my Vue3 application. See the code example below.

Upon build tailwind scanes your application for classes that are in use and automatically purges all other classes (see here). There is however a savelist feature that you can use to exclude classes from purging - aka they will always make it to production.

I have created a sample code below, that I use in my production. It combines each color and each color shade (colorValues array).

This array of class names is passed into the safelist. Please note, that by implementing this feature you ship more css data to production as well as ship css classes you may never use.

shell
const colors = require('./node_modules/tailwindcss/colors'); const colorSaveList = []; const extendedColors = {}; const colorValues = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900]; for (const key in colors) { // To avoid tailWind "Color deprecated" warning if (!['lightBlue', 'warmGray', 'trueGray', 'coolGray', 'blueGray'].includes(key)) { extendedColors[key] = colors[key]; for(const colorValue in colorValues) { colorSaveList.push(`text-${key}-${colorValue}`); colorSaveList.push(`bg-${key}-${colorValue}`); } } } module.exports = { content: [ "./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}" ], safelist: colorSaveList, theme: { extend: { colors: extendedColors } }, plugins: [ require('tailwind-scrollbar'), ] }
2024年6月29日 12:07 回复

If someone comes across in 2022 - I took A. Mrózek's answer and made a couple of tweaks to avoid deprecated warnings and an issue with iterating non-object pallettes.

shell
const tailwindColors = require("./node_modules/tailwindcss/colors") const colorSafeList = [] // Skip these to avoid a load of deprecated warnings when tailwind starts up const deprecated = ["lightBlue", "warmGray", "trueGray", "coolGray", "blueGray"] for (const colorName in tailwindColors) { if (deprecated.includes(colorName)) { continue } const shades = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900] const pallette = tailwindColors[colorName] if (typeof pallette === "object") { shades.forEach((shade) => { if (shade in pallette) { colorSafeList.push(`text-${colorName}-${shade}`) colorSafeList.push(`bg-${colorName}-${shade}`) } }) } } // tailwind.config.js module.exports = { safelist: colorSafeList, content: ["{pages,app}/**/*.{js,ts,jsx,tsx}"], theme: { extend: { colors: tailwindColors, }, }, plugins: [], }
2024年6月29日 12:07 回复

你的答案