I have a component where I want Vue to ignore the class that was already there and use the inherited class instead. But only if the class is of the same type as the inherited class. Here is an example of what I mean:
I have a button component like this
MyButton.vue
<button >
<slot></slot>
</button>
Then in my project I would like to use MyButton
component but I want to override the justify-center
and text-red-500
classes with my own, for example like this:
<MyButton >click me</MyButton>
The result is this rendered button:
<button >
click me
</button>
The problem is that HTML prioritizes the class justify-center
and justify-start
class is ignored. I would like Vue to be smart enough that it understands Tailwind classes and if it sees that there was justify-center
originally and now I pass in justify-start
then it should remove justify-center
and add justify-start
.
I also want it to do this for all Tailwind classes. If it sees that there was originally a text-....
class then it should remove that and replace it with the inherited class. Same for fonts
and colors
and shadows
etc.
So the result would be like this:
<button >
click me
</button>
CodePudding user response:
For vue, CSS classes are strings, it does not know about which class would replace which (I am not aware of a package that would parse and resolve the classes for you). You will either have to create properties to set classes, i.e.
<MyButton justify="start" textColor="gray">click me</MyButton>
or merge classes from outside yourself through the $attrs
object, i.e. something like:
<button :>
click me
</button>
CodePudding user response:
You can create prop and set classes you want:
const app = Vue.createApp({
data() {
return {
count: 0
}
},
})
app.component('mybutton', {
template: `
<button :>
<slot></slot>
</button>`,
props: {
classes: {
type: String,
default: 'justify-center text-red-500'
}
}
})
app.mount('#demo')
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" integrity="sha512-wnea99uKIC3TJF7v4eKk4Y lMz2Mklv18 r4na2Gn1abDRPPOeef95xTzdwGD9e6zXJBteMIhZ1 68QC5byJZw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<div id="demo">
<mybutton :classes="'justify-start text-green-900'">click me</mybutton>
</div>
CodePudding user response:
In my opinion, you have two options:
- for alignment, color, and ... you can define props with the default classname values:
props: {
alignment: 'start',
// other props
},
and bind these props to the CSS class and use these there.
- You can use vue directives:
Vue.directive('tailwind', {
bind: function(el, binding, vnode) {
let currentClasses = el.classList;
for (let i = 0; i < currentClasses.length; i ) {
let className = currentClasses[i];
if (className.startsWith('text-') || className.startsWith('justify-') || className.startsWith('font-')) {
el.classList.remove(className);
}
}
},
update: function(el, binding, vnode) {
let newClasses = binding.value.split(' ');
for (let i = 0; i < newClasses.length; i ) {
let className = newClasses[i];
if (className.startsWith('text-') || className.startsWith('justify-') || className.startsWith('font-')) {
el.classList.add(className);
}
}
}
});
And you can use it in this way:
<button v-tailwind="class" >
<slot></slot>
</button>
<MyButton >click me</MyButton>
CodePudding user response:
I finally managed to do it by using a 3rd party library tailwind-merge
<template>
<button :>
<slot></slot>
<i >
</i>
</button>
</template>
<script setup lang="ts">
import { computed, useAttrs } from 'vue'
import { twMerge } from 'tailwind-merge'
const attrs = useAttrs()
const buttonClass = computed(() => {
return twMerge('font-bold text-3xl justify-center text-red-500 ', attrs.class)
})
</script>
<script lang="ts">
export default {
inheritAttrs: false
}
</script>