sveltejs / svelte

web development for the rest of us
https://svelte.dev
MIT License
80.25k stars 4.27k forks source link

Dynamic conditional CSS classes #7294

Open janosh opened 2 years ago

janosh commented 2 years ago

Describe the problem

In components that want to support use with CSS frameworks like Tailwind, it would be nice to allow users to pass in custom class names as props which are applied depending on an internal condition in the component:

Describe the proposed solution

MyComponent.svelte:

export let dynamicClass = ''

let cond = false

<button class:{dynamicClass}={cond} on:click={() => cond = true}>click me</button>

Alternatives considered

This already works

<button class={cond ? dynamicClass : ''} on:click={() => cond = true}>click me</button>

but it looks cleaner here than in the real world where you might already have class attr in which case you need to manually combine classes before applying.

Importance

nice to have

windyclory commented 2 years ago
class={cond && dynamicClass}
janosh commented 2 years ago

@windyclory Gives class="false" if !cond.

adiguba commented 2 years ago

What about using a reactive-variable ?

<script>
export let dynamicClass = ''
let cond = false

$: theDynamicClass = cond ? dynamicClass : '';
</script>

<button class="my-class-name {theDynamicClass}" on:click={() => cond = !cond}>click me</button>
janosh commented 2 years ago

@adiguba Sure that's what I'm using now and it works but I think the syntax could be improved. That's what I meant with "looks cleaner here than in the real world where [...] you need to manually combine classes".

ankarhem commented 2 years ago

I'm interested in this as well. I haven't touched svelte source before but I tried to make an initial assessment. Maybe someone else could chime in on an idea on how to implement it. But by playing around with the parser I got the impression we would need a new node type to solve this problem.

If someone with deeper knowledge about the parser could give some insight into how the AST could be structured it would probably be easier for someone to pick this one up.

tytusplanck-8451 commented 2 years ago

Something more nuanced would be great.. even using the class directive like this class:{applyLogicAndReturnClass()} would work pretty good.

lightyaer commented 2 years ago

I think I have a similar issue, I am building components from tailwind and daisyui classes, and need to set conditional classes from a long list of props.

Wanted to know if something like this is doable.

 export const classes = {
    'btn-primary': color === 'primary',
    'btn-secondary': color === 'secondary',
    'btn-accent': color === 'accent',
    'btn-ghost': color === 'ghost',
    'btn-link': color === 'link',
    'btn-square': shape === 'square',
    'btn-circle': shape === 'circle',
    'btn-active': active,
    'btn-outline': outline,
    'btn-disabled': disabled,
    'btn-loading': loading,
}

and then set class={classes} to apple classes.

These are some of the props here, but the actual list is quite long.

Current work-around is building a string by iterating through classes object and set class={classString}

Wonder if this could be integrated into svelte itself. Btw, the proposed approach works in React, Vue and even SolidJS.

KyleFontenot commented 1 year ago

Ran into a use-case for this as well. I think syntactically, it would be nice to have it similar to how SolidJS uses computed values in classList:

<div class:[dynamicprop]> 

where bracket notation denotes a computed value inside an object like {[styles.fancydiv] : true}