-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow multiple classes in class: directive #7170
Comments
I need this in my life. |
cool |
Current alternative to using the tenary operator would be to use an action like the following. It's more or less the same count of characters but can be in some circumstances be more verbose. REPL function clazz(node, props) {
for (let prop of props) {
if (prop[0]) {
node.classList.add(...prop[1].split(" "));
}
}
return {
update(props) {
for (let prop of props) {
if (prop[0]) {
node.classList.add(...prop[1].split(" "));
} else {
node.classList.remove(...prop[1].split(" "));
}
}
},
};
}
|
Hey! If anyone wants to use this feature here is my vite plugin npm i svelte-multicssclass update your vite.config // vite.config.js
import { sveltekit } from '@sveltejs/kit/vite';
import { multicssclass } from 'svelte-multicssclass';
/** @type {import('vite').UserConfig} */
const config = {
plugins: [multicssclass(), sveltekit()],
};
export default config; before: <label
class:text-gray-500="{isValid}"
class:bg-gray-50="{isValid}"
class:border-gray-300="{isValid}"
class:text-red-700="{!isValid}"
class:bg-red-50="{!isValid}"
class:border-red-300="{!isValid}"
>
text
</label>
after: <label
class:text-gray-500;bg-gray-50;border-gray-300;;text-red-700;bg-red-50;border-red-300="{isValid}"
>
text
</label>
<!-- OR -->
<label
class:text-gray-500,bg-gray-50,border-gray-300,,text-red-700,bg-red-50,border-red-300="{isValid}"
>
text
</label>
<!-- OR -->
<label
class:text-gray-500|bg-gray-50|border-gray-300||text-red-700|bg-red-50|border-red-300="{isValid}"
>
text
</label> enjoy 🌌 |
Maybe a simple option to pass an array into the class directive.
Where class1 and class2 would be applied if someBooleanVariable is true. |
I really like the proposed syntax <script lang="ts">
export let color: 'primary' | 'danger' = 'primary';
</script>
<button
on:click
class="px-3 py-2 text-white rounded shadow-lg focus:outline-none focus:ring-2 focus:ring-offset-2"
class:"bg-blue-700 hover:bg-blue-800 ring-blue-400"={color === 'primary'}
class:"bg-red-600 hover:bg-red-700 ring-red-500"={color === 'danger'}
>
<slot />
</button> Because https://github.com/tailwindlabs/prettier-plugin-tailwindcss and https://github.com/tailwindlabs/tailwindcss-intellisense will easily adapt to it too. |
I like this syntax and wholeheartedly support this proposal. |
What's your thoughts @Rich-Harris and @adamwathan? |
export let button_type;
const cls = {
default: 'px-3 py-2 text-white rounded shadow-lg focus:outline-none focus:ring-2 focus:ring-offset-2',
primary: 'bg-blue-500 hover:bg-blue-600 focus:ring-blue-500 focus:ring-offset-blue-200',
danger: 'bg-red-500 hover:bg-red-600 focus:ring-red-500 focus:ring-offset-red-200'
};
const style = cls.default + ' ' + cls[button_type]; Im against adding new syntax to svelte when it can be replicated in simple js. And additionally there are far more suited libraries to handle this type of thing like class-variance-authority |
Svelte should have any solution for that. Adding a lost class statement should be achievable in easier mode: array or something. |
@Rich-Harris It would be fantastic if v5 include this feature. |
Honestly I just settled for clsx |
Interesting, could you provide a code example on how you do this? |
Yes! With clsx the example at the top of this issue could be expressed as: <script lang="ts">
import clsx from "clsx";
export let color: 'primary' | 'danger' = 'primary';
</script>
<button
on:click
class={clsx("px-3 py-2 text-white rounded shadow-lg focus:outline-none focus:ring-2 focus:ring-offset-2", {
"bg-blue-700 hover:bg-blue-800 ring-blue-400": color === 'primary',
"bg-red-600 hover:bg-red-700 ring-red-500": color === 'danger'
})}
>
<slot />
</button> |
@dummdidumm Do you think this can be added to v5 milestone? |
I've added it to the milestone, which isn't a commitment to do it for 5.0, but means it will be considered so that we don't miss the window provided by the semver major. |
Idea:
Since quotes are illegal right now, this could be done in a minor later on. |
I have to admit the <div class:"foo {bar} baz"={value}>...</div> Is the An alternative could be to introduce a <div classes:a,b,c={value}>...</div>
<div classes:a|b|c={value}>...</div> As far as I'm aware neither character is used in standard Tailwind. The comma is probably the better choice since I'm not too worried about accommodating weird characters — as long as there's an escape hatch (which there is) then we don't need to optimise for edge cases. |
@Rich-Harris could we use the same format and just use I don't think the plural form adds much for comprehension of the purpose and use. |
I agree with @Rich-Harris and @bwklein. <div class:a,b,c={value}>...</div> HTML doesn't have/need a plural form for multi-class either so I don't think anyone would be upset if they had the option to append additional class names with a |
Perhaps a straw-man here, but why not just build something like clsx into the <button class={[
'btn',
{
'btn-primary': isPrimary,
'btn-link': isLink,
}
]}>
<slot />
</button> |
This would be amazing!!! |
I mean.. we could allow that to be a dynamic expression, couldn't we? What else irks you / makes it feel like an anomaly? Because it's just the class string syntax, just before the equals sign. Comma-based solutions make the whole thing feel crammed (not breathing room between classes) and I fear tailwind's micro syntax might grab this character at some point, too. |
https://play.tailwindcss.com/a6j1Ed7RF2
Tailwind has had the ability to use CSS inside its classes for quite some time, where |
Both can be used in Tailwind using their arbitrary values feature, and at least the A space character is the logical choice in my opinion. It's already used in normal HTML to separate multiple classes, and I assume it's easier for most third-party tools (formatters, linters, syntax highlighters, etc.) to adapt as well. |
To add to #7170 (comment): #7294 asks for dynamic conditional classes, so just allowing expressions would solve this request, too |
I really like the Angular approach here where you can just do this:
The only "problem" i see is that currently in svelte you cannot combine a class with a dynamic class because attributes need to be unique (might be different in Svelte 5?) |
Why not just use class (attribute) syntax with directives? Either make quotes mandatory or optional. Based on the example above:
|
I like this option a lot as well. |
My preference would be something like this:
I have zero idea if something like this is possible. In words:
Why must it work with components? Because if you can only forego the need of As for the double appearance, let's just say that |
Treating a specific property differently is the kind of magic that causes trouble. |
there are many ways already to add multiple classes with a single condition. Creating a complex syntax to hide these groups in the template is just going to make the template harder to read. abusing? double quotes in the attribute name makes the parser more complex and be a source for confusion. |
I understand, @brunnerh. Still, if it can't work for components, this whole exercise is futile, IMO, because then your need for |
I agree that making But maybe I'm the odd one out here because I actually really dislike how many lightly sugared shortcuts Svelte already has. Eg: how much is something like |
Whatever the solution would be, support for opacity in Tailwind (v4) will be greatly appreciated. Example: |
Describe the problem
Utility-first CSS frameworks like Tailwind use very granular CSS classes (e.g.
bg-red-500
for a red background,shadow-lg
for a large box-shadow, ...). You often want to apply styles conditional with theclass:
directive. Unfortunately, it only works for a single class at the moment which means you have to duplicate it quite often. A very simple example for a Button component with Svelte and Tailwind might look like this:This is very boiler-plate-heavy and annoying to work with. It's also just a very simple example for showcasing and usually gets even uglier in real-world examples. When using a utility-first framework you run into this issue a lot.
Describe the proposed solution
Allow the use of multiple CSS classes in the
class:
directive with aclass:"x y z"={true}
syntax. This would allow the example above to be simplified like this:Alternatives considered
There's been a very similar issue (#3376) which unfortunately has been closed and not been re-opened despite getting a lot of follow-up comments that argue for its usefulness. In this issue, some alternatives have been discussed:
Using Tailwind's
@apply
directiveTailwind does provide a
@apply
directive to extract multiple Tailwind-classes into a custom CSS class. For the example above, this could look like this:While this appears to be a good solution (and is used by many to circumvent the issue), using the
@apply
directive goes against the utility-first workflow. Adam Wathan (the creator of Tailwind) advised against using it (Source):Additionally, there are other Utility-CSS frameworks that usually don't have this feature.
Using the ternary operator
This does work, but obviously also introduces a lot of boilerplate code. The whole point of the
class:
directive is to eliminate this kind of code.Writing a Svelte Preprocessor
I'm not familiar with preprocessors, but this has been a frequent suggestion in the original issue. There even exists one already: https://github.com/paulovieira/svelte-preprocess-class-directive
This might be a viable option but I would much rather prefer support out-of-the-box instead of relying on a third-party library. Besides not being actively maintained, the linked preprocessor uses an alternative, non-ideal syntax like described in the next section "Alternative syntaxes".
Alternative syntaxes
Many other syntaxes have been suggested, e.g.
class:x,y,z={true)
,.x.y.z={true}
,class:{"x y z"}={true}
, ...The problem with most of them is that they either introduce breaking changes (e.g.,
class:x,y,z={true}
is already valid syntax for the classx,y,z
) and/or limit it to a sub-set of CSS-classes because,
and.
are valid characters in CSS class names. While not very common in "classic" CSS classes, they are often used by utility frameworks like Tailwind (e.g.gap-[2.75rem]
,grid-rows-[200px_minmax(900px,_1fr)_100px]
, orrow-[span_16_/_span_16]
).class:{"x y z"}={true}
would work and should be supported as an alternative syntax (just likeclass={"x y z"}
also works) but is also unnecessary (yet small) boilerplate in most cases.Importance
would make my life easier
Final words
As mentioned above, this is technically a duplicate of #3376. However, since there have been no responses from any maintainers (even when pinging them) on the original issue, I've decided to open this issue with a summary of the discussion in the original issue. I would very much appreciate a re-evaluation of the original decision to not support this feature, either in this issue or by re-opening the original one. Thank you for the awesome work on Svelte!
The text was updated successfully, but these errors were encountered: