Override Tailwind classes of a reusable Vue component without using props
Published: 2022-11-25 14:52:17


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`


```vue
<!-- 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:


```vue
<!-- 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


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/od73vdmzxihh6jefe47j.png)


---



## 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](https://developer.mozilla.org/en-US/docs/Web/CSS/:where), :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:

```css
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:
```javascript
  //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:


```vue
<!-- AppButton.vue -->
<template>
  <div class="initial:w-8 initial:font-extrabold"> ... </div>
</template>
```

**It works now**
We will find that the classes are overridden:
 
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mi5tkhi4eoxbqef675f9.png)


---



## Solution (2)

#### Use Props

This solution is define the needed props "weight and width": 
```vue
<!-- 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:

```vue
<!-- 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`:

```html
<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!