Sometimes we face a case where we need to override a reusable' component base classes from outside, without the headache of defining tons of props. Instead we will be using the "initial" variant.
Main Problem
We can't just pass the Tailwind classes to the component and it automatically will be take the priority and override the base classes
So, let's say we have this "AppButton.vue" component with these base classes: w-8 font-extrabold
<!-- AppButton.vue -->
<template>
<div class="w-8 font-extrabold"> ... </div>
</template>
and we wish to pass different font weight and width, we will be intuitively doing this in our home page:
<!-- Home.vue -->
<template>
<AppButton class="w-20 font-light"/>
</template>
But that won't work always, why?
It depends on the order of those classes when they get compiled, in the above example, the base classes w-8 font-extrabold
have higher order in the compiled stylesheet file which means they have higher specificity
Solution (1):
Decrease the CSS specificity of the base classes
If we can guarantee that our base classes w-8 font-extrabold
have less specificity, we can ensure that the passed classes from outside will override them
how?
To use the :where()
pseudo selector!
According to MDN, :where() is a CSS functional pseudo-class selector that takes in a list of selectors as an argument and applies the given styles to any element from that list. :where() is very useful for making a long selector list shorter
How can we benefit of :where()
?
If we can have our base classes to be compiled to something similar to this:
html :where(.initial .w-8, .initial .font-light)
The above code can be translated to:
Every item that is prefixed with the .initial
class will take zero specificity, and by this our passed classes to the AppButton
can override those zero specificity base classes
So how can we do this using Tailwind?
Add a new variant called initial
to the Tailwind plugins array:
//tailwind.config.js
module.exports = {
plugins: [
function ({ addVariant }) {
addVariant("initial", "html :where(&)");
}
]
}
Now we are able to prefix the base classes with initial:*
so the AppButton.vue will be as changed to:
<!-- AppButton.vue -->
<template>
<div class="initial:w-8 initial:font-extrabold"> ... </div>
</template>
It works now
We will find that the classes are overridden:
Solution (2)
Use Props
This solution is define the needed props "weight and width":
<!-- AppButton.vue -->
<template>
<div :class="[weight, width]"> ... </div>
</template>
<script>
export default {
props:{
weight:{
type:'String',
default:'font-extrabold'
},
width:{
type:'String',
default:'w-8'
}
}
}
</script>
And in our home template, we'll be passing them:
<!-- Home.vue -->
<template>
<AppButton width="w-20" weight="font-light"/>
</template>
I don't prefer this solution, as we need to add a prop for every new default base class.
Why to bloat the code with props when we can use the first solution.
Solution (3)
Is to pass the classes as !important
:
<template>
<div class="!w-20 !font-light"> ... </div>
</template>
but I don't recommend it as it will override all the attached styles to that base class like lg:font-semibold
Thank you!
Top comments (0)