tailwindcss
tailwindcss copied to clipboard
CSS based functional `@utility` with same name results in missing classes
When defining custom functional @utility with the same name for different purposes results in unwanted behavior.
If you want to use the same name, similar to how we can use text-red-500 and text-2xl then the order matters and only the first one "wins" right now.
@utility foo-* {
color: --value(--color- *);
}
@utility foo-* {
font-size: --spacing(--value(number));
}
In this case, foo-red-500 will work, but foo-123 will not.
Play: https://play.tailwindcss.com/1VmWKocy9O?file=css
If you swap the order, then foo-123 will win, but foo-red-500 won't result in anything.
@utility foo-* {
font-size: --spacing(--value(number));
}
@utility foo-* {
color: --value(--color- *);
}
Play: https://play.tailwindcss.com/71oBj8xrI1?file=css
The expected behavior is that both work.
Defining both properties in the same utility works
@utility foo-* {
font-size: --spacing(--value(number));
color: --value(--color- *);
}
Yep you are correct! And that's also what we do internally for core plugins, but I think there is still something nice about separating them.
Play with combination: https://play.tailwindcss.com/OUN74Zyn9S?file=css
I think im having this issue too but in a similar fashion, when i have a utility class with the same name but with extra letters appended - e.g btn-primary + btn-primaryAlt The btn-primaryAlt will be get removed when running the minify script
Adding to this issue to highlight similar issue where generated custom classes override each other if sharing same class prefix with a setup of
:root {
...
--color-base-600: hsl(0, 0%, 42%);
...
}
@theme {
--color-sub-600: var(--color-base-600);
--text-title-h1: 3.5rem;
--text-title-h1--line-height: 4rem;
--text-title-h1--letter-spacing: -0.01em;
--text-title-h1--font-weight: 600;
}
Ideally, I would want to be able to set both text-title-h1 and text-sub-600 classes on an element i.e. on a radix dialog title element
const DialogTitle = React.forwardRef<
React.ComponentRef<typeof DialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
>(({ className, ...rest }, forwardedRef) => {
return (
<DialogPrimitive.Title
ref={forwardedRef}
className={cn("text-title-h1 text-sub-600", className)}
{...rest}
/>
);
});
DialogTitle.displayName = "DialogTitle";
However, upon render only text-sub-600 is applied and if I swap the order of classes text-title-h1 is applied. Following theme documentation as outlined here.
To fix the issue I had to utilise @layer utilities and define classes without --text prefix i.e. --text-title-h1 became
@layer utiltiies {
.title-h1 {
font-size: var(--text-title-h1);
line-height: var(--text-title-h1--line-height);
letter-spacing: var(--text-title-h1--letter-spacing);
font-weight: var(--text-title-h1--font-weight);
}
}
On second thought the utilities approach seems more correct, however, the new documentation can be misleading for defining new font sizes and colors especially given that applying custom color and font size will only apply the last defined class on an element.
@mtthsnc I am not applying the style in Radix dialog but could you check the link https://play.tailwindcss.com/oDMwWmaBEH?file=css
@RobinMalfait While using utility classes are single-purpose classes that apply specific styles, and you can combine them to create complex designs, allowing you to build responsive and consistent user interfaces efficiently. but you cannot use the same name to define the same utility either combine the styles or use a different name for defining the utility.
@yash-k-s-7span thank you for providing an example. I eventually backtracked this this to tailwind-merge being the issue as it would remove duplicate prefix classes i.e. if multiple text-* classes were applied it would always favour the last applied class. So this was not an issue with tailwindcss but with tailwind-merge
Adding to this issue to highlight similar issue where generated custom classes override each other if sharing same class prefix with a setup of
:root { ... --color-base-600: hsl(0, 0%, 42%); ... }
@theme { --color-sub-600: var(--color-base-600); --text-title-h1: 3.5rem; --text-title-h1--line-height: 4rem; --text-title-h1--letter-spacing: -0.01em; --text-title-h1--font-weight: 600; } Ideally, I would want to be able to set both
text-title-h1andtext-sub-600classes on an element i.e. on a radix dialog title elementconst DialogTitle = React.forwardRef< React.ComponentRef<typeof DialogPrimitive.Title>, React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
(({ className, ...rest }, forwardedRef) => { return ( <DialogPrimitive.Title ref={forwardedRef} className={cn("text-title-h1 text-sub-600", className)} {...rest} /> ); }); DialogTitle.displayName = "DialogTitle"; However, upon render only
text-sub-600is applied and if I swap the order of classestext-title-h1is applied. Following theme documentation as outlined here.To fix the issue I had to utilise
@layer utilitiesand define classes without--textprefix i.e.--text-title-h1became@layer utiltiies { .title-h1 { font-size: var(--text-title-h1); line-height: var(--text-title-h1--line-height); letter-spacing: var(--text-title-h1--letter-spacing); font-weight: var(--text-title-h1--font-weight); } } On second thought the utilities approach seems more correct, however, the new documentation can be misleading for defining new font sizes and colors especially given that applying custom color and font size will only apply the last defined class on an element.
I have run into the same issue, though when using the @layer utilities approach, we don't get intelisense for them.