A couple of days ago my sister asked for a favor. She wanted to create a small contact/promo page for the kindergartner’s that she is running. Nothing special and fancy, just a regular landing page with some event graphics and the contact form. Weekend job.
Well, sounds like a very easy and fast task to do and help my sibling in need. First thought was Vue, the next one was Tailwind. After some further thinking turns out this is a great opportunity to finally use Vite … with the real-life project.
So I do have my tools for UI/frontend part, what's left was the form handler and app deployment, serving platform. And as I can get all of this from Netlify … the decision was made.
Installing and running the Vite was super easy. The performance of this tool is incredible, it's extremely fast and powerful. Turns out that I can play a bit with the script setup concept as well. It "forces" kinda new writing approach but eventually it's very convenient and aligned with the Composition API flow.
OK, my development environment is ready now I just need to start creating actual components. As I'm using Tailwind I need to set some initial config with my default style values. You know, font sizes, colors, and other stuff. Fine.
Create containers with some grid classes, great, nothing special here. Time for some global, reusable components like buttons. And here we go... 🤕 Tailwind utility classes madness. To define a simple button I had to use like a' twenty-plus classes. Flashback - every time when I'm using Tailwind I'm dealing with these super long line markups - terrible. Nightmare right?
This must be ended! At least for my Vue apps. 😃
I know, I can use apply handler and define all the classes in the style section. But this is not solving the problem at all. This is also not so global, "reuse" friendly. So what I need is to define some global variants for my global components like buttons, inputs, blocks. But how to use them with Tailwind and Vue? Easy, as long I can bind my styles inside the Vue templates I can define my styles as an object and use them everywhere. And of course, while I'm using Composition API I can create some fancy composable. That is how the vue-use-variant package was created.
The main goal was to create the globally accessible object or objects with some style definitions, components variants. Let's do that. For the banner.
import { ref } from 'vue'
export const buttonVariants = {
button: 'font-bold rounded border-0 bg-blue hover:opacity-80',
buttonPrimary: 'p-4 text-lg',
buttonSecondary: 'p-2 text-md',
}
export const buttonVariantsRef = ref(buttonVariants)
Great. 👍 We have some overall button styles and some variant definitions — for primary and secondary. Typical one. 😅
OK, now the composable and how I’m using it with the above variants.
<template>
<button :class="buttonVariant" type="button" />
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
import { buttonVariants, buttonVariantsRef } from './variants.ts'
import { useVariant, UseVariant } from 'vue-use-variant'
export default defineComponent({
name: 'Button',
setup() {
const { defineVariant } = useVariant() as UseVariant
return {
buttonVariant: defineVariant(
ref({
button: true,
buttonPrimary: true,
}),
buttonVariantsRef,
),
}
},
})
</script>
Your final result will look something like this.
font-bold rounded border-0 bg-blue hover:opacity-80 p-4 text-lg
That’s it. Awesome right? It’s clean, readable, convenient, fast, and globally available. The class madness has been ended. 🎉 You can use it with Ref objects, with props, and straight away inside the component. Finally, you can use it with any other framework. It was built for the Vue ecosystem but it’s not stopping you to use it with React if you want.
Create your variants for all the reusable components and use them across your application. This way you’ll always get one source of truth and your UI components will always come with the same shape and visual representation.
Find the technical specification and some guidelines here. You can install it from npm or yarn. You can play with the very basic demo provided inside the repository. It was created with Vite, so that might be the additional stimulant. And of course, any suggestions, issue reports are very warmly welcome.
Thanks for reading. Enjoy! ✋
Top comments (8)
Is the world going mad or am I?
Not to discredit your work by the way. You ran into a problem and developed a solution. It's just that regular CSS is a far better match for every single one of your listed requirements!
This works, but with tailwind, you use the @layer and @apply directives to create such components.
This ensures that the classes are above the utility classes in the compiled css file and as such properties can be overridden using tailwind utility classes.
Checkout this Tailwind Playground
Also, I do not understand what you mean here @lukasborawski .
Yes, I've mentioned that you can use it and define it like that, but still this is just CSS. I can't type it, can't easily extend it and mix with some additional logic, can't use it with props. Get your point but still, it's very useful and versatile for the Vue.js app.
Wait. This is great, but you're writing here your own, regular classes (assume that on top of BEM) and CSS. This is something different. The tools was created to avoid mass with the Tailwind utility classes.
What I was trying to convey is that you haven't actually solved the underlying problem statement from your original post, only repackaged it. The long list of classes is still there, just moved elsewhere by this additional piece of code that you now also get to maintain.
That CSS snippet I posted is the output of your Tailwind button classes taken straight off the Tailwind docs. Same outcome for less effort and unbeatable performance, and most of Tailwind's utility classes map one-on-one with CSS properties anyway.
Well. You can look at it that way. But as I'm using a set of utility classes anyway and I want to use them globally, create one source of truth of my UI and not struggle with the messy templates I can set one place where I'm defining my variants and use them across the app.
Or use Bootstrap 5, and you also can customize a component and reuse it anywhere ;)
I guess with bootstrap no matter which version you’ll face the same issue.