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.
所以,在发现这种工作方式不被推荐,JIT也不支持它之后(感谢慷慨的评论者)。我已经将方法更改为更基于“配置”的方法。
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.
以下是配置的示例。(目前没有打字)和一些更好的重构,但你会有想法的。
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:
然后,我只需像这样应用样式:
<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>
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"
不推荐使用这种编写TailWind CSS类的方式。就连JIT模式也不支持它,引用TailWind CSS文档的话说:“TailWind不包括任何类型的客户端运行时,因此类名需要在构建时是静态可提取的,并且不能依赖于任何类型的随客户端变化的任意动态值。”
EDIT: Better implementation 2022 - https://stackoverflow.com/a/73057959/11614995
编辑:更好地实施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.
顺风css不支持动态类名(参见此处)。然而,仍然有一种方法可以实现这一点。我需要在我的Vue3应用程序中使用动态构建类名称。请参见下面的代码示例。
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.
在构建时,顺风扫描您的应用程序中正在使用的类,并自动清除所有其他类(请参见此处)。然而,有一个savelist功能,可以用来排除类的清除--也就是说,它们永远都会投入生产。
I have created a sample code below, that I use in my production. It combines each color and each color shade (colorValues array
).
我已经创建了下面的示例代码,并在我的生产中使用。它组合了每种颜色和每种颜色底纹(ColorValues数组)。
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.
这个类名数组被传递到安全列表中。请注意,通过实现此功能,您可以将更多的CSS数据交付给生产环境,也可以交付您可能永远不会使用的CSS类。
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'),
]
}
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.
但是,Dynamic Class工作得很好,只要它是一个完整的顺风类名。
its stated here
它在这里说明
this will not work
这是行不通的
<div class="text-{{ error ? 'red' : 'green' }}-600"></div>
but this one works
但这个管用
<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,
TailWind将扫描Tailwind.config.js内的mode.exports.content中指定的所有文件,并查找顺风类,它甚至不必在类属性中,甚至可以添加到注释行中,只要该文件中存在完整的类名并且类名不是动态构造的;TailWind将提取该类的样式。
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
因此,在您的示例中,您所要做的就是将所有可能的动态类的值放入该文件内的完整类名中,如下所示
<button className={ color === 'primary' ? 'bg-primary-500' : 'bg-secondary-500'}>
{children}
</button>
or the method I would prefer
或者我更喜欢的方法
<!-- 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
下面是另一个例子,虽然它是VUE,但是它的思想对于任何JS框架都是一样的
<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
结果会是这样的
<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 -->
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.
如果有人在2022年遇到--我接受了A.Mrózek的回答,做了几个调整,以避免不建议使用的警告和迭代非对象调色板的问题。
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: [],
}
For tailwind JIT
mode or v3 that uses JIT
, you have to ensure that the file where you export the object styles is included in the content option in tailwind.config.js
, e.g.
对于tailwindJIT模式或使用JIT的v3,您必须确保导出对象样式的文件包含在tailwind.js.js的内容选项中,例如。
content: ["./src/styles/**/*.{html,js}"],
Is it recommended to use dynamic class
in tailwind
?
No
Using dynamic classes
in tailwind-css
is usually not recommended because tailwind
uses tree-shaking
i.e any class that wasn't declared in your source files, won't be generated in the output file.
Hence it is always recommended to use full class names
通常不推荐在TailWind-css中使用动态类,因为TailWind使用树抖动,也就是说,任何没有在源文件中声明的类都不会在输出文件中生成。因此,始终建议使用完整的类名
According to Tailwind-css docs
根据Tailind-css文档
If you use string interpolation or concatenate partial class names together, Tailwind will not find them and therefore will not generate the corresponding CSS
Isn't there work around ?
Yes
As a last resort, Tailwind offers Safelisting classes.
作为最后的手段,TailWind提供安全列表课程。
Safelisting is a last-resort, and should only be used in situations where it’s impossible to scan certain content for class names. These situations are rare, and you should almost never need this feature.
In your example,you want to have 100 500 700
shades of colors. You can use regular expressions to include all the colors you want using pattern
and specify the shades accordingly .
在您的示例中,您希望拥有100、500、700种不同的颜色。您可以使用正则表达式来包含您希望使用Pattern的所有颜色,并相应地指定色调。
Note: You can force Tailwind to create variants
as well:
In tailwind.config.js
在Teswind.config.js中
module.exports = {
content: [
'./pages/**/*.{html,js}',
'./components/**/*.{html,js}',
],
safelist: [
{
pattern: /bg-(red|green|blue|orange)-(100|500|700)/, // You can display all the colors that you need
variants: ['lg', 'hover', 'focus', 'lg:hover'], // Optional
},
],
// ...
}
EXTRA: How to automate to have all tailwind colors in the safelist
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
}
// Define all of your desired shades
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}`) <-- You can add different colored text as well
colorSafeList.push(`bg-${colorName}-${shade}`)
}
})
}
}
// tailwind.config.js
module.exports = {
safelist: colorSafeList, // <-- add the safelist here
content: ["{pages,app}/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {
colors: tailwindColors,
},
},
plugins: [],
}
Note: I have tried to summarize the answer in all possible ways, In the combination of all possible answers.Hope it helps
注:我已经尝试用所有可能的方式,结合所有可能的答案来总结答案。希望这能有所帮助
Now could use safeListing
现在可以使用SafeListing
and tailwind-safelist-generator package to "pregenerate" our dynamics styles.
和顺风-安全-生成器包,以“预生成”我们的动态风格。
With tailwind-safelist-generator, you can generate a safelist.txt file for your theme based on a set of patterns.
使用顺风-安全列表生成器,您可以基于一组模式为您的主题生成一个Safelist.txt文件。
Tailwind's JIT mode scans your codebase for class names, and generates CSS based on what it finds. If a class name is not listed explicitly, like text-${error ? 'red' : 'green'}-500, Tailwind won't discover it. To ensure these utilities are generated, you can maintain a file that lists them explicitly, like a safelist.txt file in the root of your project.
顺风的JIT模式扫描您的代码库中的类名,并根据找到的内容生成CSS。如果没有明确列出类名,如Text-${Error?‘red’:‘green’}-500,TailWind不会发现它。为了确保生成这些实用程序,您可以维护一个明确列出它们的文件,就像项目根目录中的Safelist.txt文件一样。
In Nextjs 13.4+ with tailwind you can use combination of both clsx
& twMerge
在带有顺风的Nextjs 13.4+中,您可以同时使用clsx和twMerge
clsx
: For Object
based className
Clsx:用于基于对象的类名称
const [pending, setPending] = useState(false);
<button className={
"px-4",
{
"bg-blue-500":pending, // if pending is true apply blue background
}
}
/>
twMerge
: For effectively merging tailwind classes,
TwMerge:为了有效地合并顺风类,
<button className={twMerge(
"bg-blue-500 px-4",
"bg-black"
)}
/>
Utility Function /lib/utils.ts
实用程序函数/lib/utils.ts
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
Use it as page.tsx
将其用作页面。tsx
import { cn } from "@/lib/utils";
export default function Component(){
return <div className={cn(
"bg-black font-sans",
"bg-white h-full",
{
"px-5":pending, // if pending is true apply padding
}
)}
/>
}
For further refer: cn() - Every Tailwind Coder Needs It (clsx + twMerge)
更多信息请参考:cn()-每个尾风编码器都需要它(clsx+twMerge)
In v3 as Blessing said you can change the content array to support that.
在v3中,正如Bless所说,您可以更改内容数组以支持这一点。
I had this
我有这个
const PokemonTypeMap = {
ghost: {
classes: "bg-purple-900 text-white",
text: "fantasma",
},
normal: {
classes: "bg-gray-500 text-white",
text: "normal",
},
dark: {
classes: "bg-black text-white",
text: "siniestro",
},
psychic: {
classes: "bg-[#fc46aa] text-white",
text: "psíquico",
},
};
function PokemonType(props) {
const pokemonType = PokemonTypeMap[props.type];
return (
<span
className={pokemonType.classes + " p-1 px-3 rounded-3xl leading-6 lowercase text-sm font-['Open_Sans'] italic"}
>
{pokemonType.text}
</span>
);
}
export default PokemonType;
something similar to your approach, then I moved the array to a JSON file, it thought was working fine, but was browser caché... so following Blessing's response, you can add .json like this
类似于您的方法,然后我将数组移动到一个JSON文件,它认为工作正常,但浏览器缓存...因此,按照Bless的响应,您可以像这样添加.json
content: ["./src/**/*.{js,jsx,ts,tsx,json}"],
Finally I have this code, it's better in my view.
最后我有了这段代码,在我看来它更好。
import PokemonTypeMap from "./pokemonTypeMap.json";
function PokemonType(props) {
const pokemonType = PokemonTypeMap[props.type];
return (
<span className={pokemonType.classes + " p-1 px-3 rounded-3xl leading-6 lowercase text-sm font-['Open_Sans']"}>
{pokemonType.text}
</span>
);
}
export default PokemonType;
2023 Update
2023年更新
The TailwindUI library (created by Tailwind) uses a handy little helper function which you can define in your app (no dependencies required):
TailwinUI库(由TailWind创建)使用了一个方便的小助手函数,您可以在应用程序中定义该函数(不需要依赖项):
export function classNames(...classes) {
return classes.filter(Boolean).join(' ')
}
Then in any file you can simply add together strings of classes:
然后,在任何文件中,您都可以简单地将类字符串添加到一起:
className={
classNames(
// This could be a string representing classes passed into the component
"flex flex-col",
primary ? "bg-teal-600" : "bg-white",
active ? 'bg-slate-100' : '',
)
}
I had a similar issue, instead of passing all the possible configs, I just pass the data to the style property of the HTML. This is far more efficient!
我有一个类似的问题,而不是传递所有可能的样式,我只是把数据传递给HTML的样式属性。这样效率更高!
or
或
Pass a class name as a prop and let the user of the package write the styles to that class.
将类名作为道具传递,并让包的用户将样式写入该类。
const CustomComp = ({
keyColGap = 0,
keyRowGap = 0,
className = '',
}: Props) => {
const classNameToRender = (): string => {
return `m-1 flex flex-col ${className}`.trim();
};
const rowStylesToRender = (): React.CSSProperties | undefined => {
const styles: React.CSSProperties | undefined = { gap: `${keyRowGap}rem` };
return styles;
};
const colStylesToRender = (): React.CSSProperties | undefined => {
const styles: React.CSSProperties | undefined = { gap: `${keyColGap}rem` };
return styles;
};
return (
<div className={classNameToRender()} style={rowStylesToRender()}>
{layout.map((row) => {
return (
<div
className={`flex justify-around`}
style={colStylesToRender()}
key={row}
>
/* Some Code */
</div>
);
})}
</div>
}
Tailwind classes with JS template literals
When colors ( styles ) are driven from the config (JS).
当颜色(样式)从配置(JS)驱动时。
The below pattern didn't work ❌ ( Refer to top Cards in the image with no background colors )
下面的图案不适用于❌(指的是图像中没有背景颜色的顶层卡片)
const cardsWithNoColors= [
{
id: '1',
title: 'Card 1',
value: 'Body 1',
cardBg: 'yellow',
},
{
id: '2',
title: 'Card 2',
value: 'Body 2',
cardBg: 'orange',
},
{
id: '3',
title: 'Card 3',
value: 'Body 3',
cardBg: 'indigo',
},
];
<div className={`bg-[${cardBg}]`}>
Fix here
Change ( Refer to Bottom Cards in the image with background colors )
更改(请参阅图像中带有背景颜色的底牌)
cardBg: 'yellow' 👉 cardBg: 'bg-[yellow]'
<div className={`bg-[${cardBg}]`}> 👉 <div className={`${cardBg}`}>
更多回答
It's even possible to just add the tailwind classes (you want to have included for dynamic usage) as comments somewhere in your code. Allows using bg-${color}-100 text-${color}-500
as long as u mention bg-accent-100 text-accent-500
in a comment somewhere for every color that you want to include.
甚至可以只将顺风类(您希望将其包含在代码中以用于动态使用)作为注释添加到您的代码中。允许使用BG-${COLOR}-100 TEXT-${COLOR}-500,只要您在评论中提到BG-ACCENT-100 TEXT-ACCENT-500,就可以为您要包含的每种颜色添加BG-ACCENT-100文本-ACCENT-500。
@morganney This was for a small personal project, I like to educate myself on all the front-end frameworks in how they work and what the caveats are. I still think you can build really flexible and scalable front-ends with TailWind, it's just how you set it up and decide to use it. And as statet below, this isn't really an issue anymore since the latest updates.
@mganney这是一个小的个人项目,我想学习所有的前端框架是如何工作的,以及注意事项是什么。我仍然认为你可以用顺风构建非常灵活和可伸缩的前端,这只是你如何设置它并决定使用它。正如下面的状态,这不再是一个真正的问题,因为最新的更新。
@morganney This is hardly a significant problem
@摩甘妮这算不上什么大问题
@forresthopkinsa It is a significant problem if you try to generate lots of dynamic class strings. Your bundle size will be massive, because you will end up safelisting a large proportion of all tailwindcss classes.
@forresthopkinsa如果您尝试生成大量动态类字符串,这将是一个严重的问题。您的捆绑包大小将是巨大的,因为您最终将安全地列出很大比例的TRANSFINCSS类。
@WesleyJanse I was just doing some more asking around and experimenting with this. It appears, if you are in an SSR context, you can run postcss with the tailwind plugin on rendered HTML. And, the performance hit doesn't appeared to be significant (< 100ms). This allows you to use dynamic classnames, so long as (1) you're using a proper SSR framework like Nuxt and (1.1) Javascript which changes the classnames at browser runtime aren't included in the bundle.
@Wesley Janse我只是做了一些更多的询问和试验。看起来,如果你在一个SSR环境中,你可以在呈现的HTML上用顺风插件运行postcss。而且,性能影响似乎不是很大(<100ms)。这允许您使用动态类名,只要(1)您使用的是正确的SSR框架,如Nuxt和(1.1)在浏览器运行时更改类名的Java脚本不包含在捆绑包中。
I wonder if safelist
is a better alternative then auto-generating all the possible combinations in a helper function and then simply call that function to dynamically look up the classnames. I wrote this gist regarding the topic: gist.github.com/tahesse/345830247456980d1c8ac6e53a2dd879
我想知道Safelist是不是比自动生成帮助器函数中所有可能的组合,然后简单地调用该函数来动态查找类名更好。我写了这个主题的要点:gist.github.com/tahesse/345830247456980d1c8ac6e53a2dd879
@tahesse I like your solution. It could be used as an external script or "plugin" for other devs. My code is more like a hotfix or a quickfix. As you mention in the gist you have to call into account the classes you might not use. But still, you can modify my solution to only use classes that you want and safeList them. I find my solution quite easy as you take advantage of a feature already included in TailWindCSS. So to answer your question. I think it's "cleaner" to use my solution insted of writing CSS classes directly to the file via node fs. But still, it's just my opinion.
@taess se我喜欢你的解决方案。它可以用作外部脚本或其他开发人员的“插件”。我的代码更像是热修复程序或快速修复程序。正如您在要点中提到的,您必须考虑到您可能不会使用的类。但是,您仍然可以修改我的解决方案,使其只使用您想要的类,并安全地列出它们。我发现我的解决方案非常简单,因为您可以利用TailWindCSS中已经包含的一个功能。所以我来回答你的问题。我认为使用我的解决方案是通过节点文件系统将css类直接写到文件中会更“干净”。但尽管如此,这只是我的观点。
this is no longer working in 2022 I believe. Added an updated version in a post below
我认为,这在2022年行不通了。在下面的帖子中添加了更新版本
It's still working in 2022. We use it in our production codebase. I have added a link to your implementation to my original anwer as I find it better. Thanks for tweaking my code. :)
到2022年,它仍在使用。我们在我们的生产代码库中使用它。我已经添加了一个链接到您的实现我的原始答案,因为我觉得它更好。谢谢你修改了我的代码。:)
Correct me if I'm wrong. But shouldn't you use continue instead of break in the for loop when checking, if the colorName is included in the deprecated array? Otherwise upon finding the deprecated color you break the cycle and no more colors are added to the tailwindColors array.
如果我说错了,请纠正我。但是,如果ColorName包含在已弃用的数组中,那么在检查时,您不应该在for循环中使用Continue而不是Break吗?否则,一旦找到不推荐使用的颜色,就会中断循环,不会再向TailwinColors数组添加更多的颜色。
I had the same problem and this is actually what I was missing out : I had my utility classes imported from a local data object stored in a custom folder, so that Tailwind couldn't reference them. I just had to complete the "content" array with my file.
我遇到了同样的问题,这实际上是我遗漏的:我从存储在自定义文件夹中的本地数据对象导入了我的实用程序类,这样TailWind就不能引用它们。我只需要用我的文件完成“Content”数组。
Thanks for letting me know, haven't looked into Tailwind V3. So I don't know what's the better approach right now
谢谢你让我知道,我还没有看过Tailwind V3。所以我不知道现在该怎么做
It's not about tailwind v3 only, it's just the JIT mode.
这不仅仅是顺风v3,这只是JIT模式。
我是一名优秀的程序员,十分优秀!