When building a web application, I would advise using Chakra UI Vue to speed up your development and have accessibility build in.
But sometimes the project requirements are different, and you have to use Tailwind CSS, for example.
There is no reason to not start from zero, you can use Zag.js components with Vue.js (or React or Solid.js). Those components come with all the logic and accessibility in mind that you need to have a solid foundation.
Let’s go over how we can style Zag.js components using Tailwind CSS as CSS Framework. I will use the accordion component as an example, the purpose of this post is to show how you can set up Tailwind CSS and use it to add styling to the Zag.js components you are using.
A StackBlitz playground can be found here: https://stackblitz.com/edit/vue-zag-tailwind?file=src/App.vue
For those who do not know Zag.js, let’s start there first.
Zag.js
Zag is a toolkit that provides framework-agnostic UI components powered by State Machines. The components are build with accessibility in mind and handle all the logic for you. They are completely headless and unstyled, which gives you the full control to use your favourite styling solution.
Zag.js currently works seamless with React, Vue.js and Solid.js. In this blog, we will use Vue.js.
The Zag.js documentation can be found here: https://zagjs.com/.
Styling solution
As you might have guessed from the title, the styling solution will be Tailwind CSS. Let’s assume you do know about this CSS Framework, documentation can be found here: https://tailwindcss.com/.
Getting started
We will probably start with setting up our Vue.js project, using the Vue CLI and use Vite for example. When this has been done, we can easily add Zag.js to our project. If we scan the documentation, we quickly notice we first will have to pick a component we would like to use and style. Lets for example say the Accordion component. Documentation here: https://zagjs.com/components/vue-sfc/accordion.
We will have to install the machine for this component, which is completely framework-agnostic.
yarn add @zag-js/accordion
Then comes the part to connect this machine with the Vue.js framework. For this, we need to install the adapter.
yarn add @zag-js/vue
Just to play around with the accordion, we will add it to the App.vue
file. For more information about what the Zag.js code does, please take a look at the documentation of Zag.js.
<script setup>
import * as accordion from '@zag-js/accordion'
import { normalizeProps, useMachine } from '@zag-js/vue'
import { computed } from 'vue'
const data = [
{ title: 'Watercraft', content: 'Sample accordion content' },
{ title: 'Automobiles', content: 'Sample accordion content' },
{ title: 'Aircrafts', content: 'Sample accordion content' },
]
const [state, send] = useMachine(accordion.machine({ id: '1' }))
const api = computed(() => accordion.connect(state.value, send, normalizeProps))
</script>
<template>
<div ref="ref" v-bind="api.rootProps">
<div
v-for="item in data"
:key="item.title"
v-bind="api.getItemProps({ value: item.title })"
>
<h3>
<button
v-bind="api.getTriggerProps({ value: item.title })"
>
{{ item.title }}
<svg
data-accordion-icon
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path>
</svg>
</button>
</h3>
<div
v-bind="api.getContentProps({ value: item.title })"
>
{{ item.content }}
</div>
</div>
</div>
</template>
Inside the accordion header, we have added an SVG which is an arrow to indicate the state of the accordion item. Right now we notice our accordion component has any styling to it, the SVG arrow is not rotating or anything. It’s time to add Tailwind CSS to make this all possible.
Adding styling
The Zag.js documentation does show a styling guide on how to style each accordion part with CSS here.
We won’t be using this solution since we would like to use the Tailwind CSS framework.
First, we will have to install Tailwind CSS, and it’s dependencies. Then run the init command to generate the following Tailwind CSS files: tailwind.config.cjs
and postcss.config.cjs
.
yarn add tailwindcss postcss autoprefixer -D
npx tailwindcss init -p
Inside the generated tailwind.config.cjs
file, we add the paths to all our/ template files.
// tailwind.config.cjs
module.exports = {
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
],
...
}
The last thing we need to do before adding Tailwind CSS classes and running our project is adding the @tailwind
directives to our ./src/style.css
file.
<!-- style.css -->
@tailwind base;
@tailwind components;
@tailwind utilities;
Now we can run the project and add some styling!
Add Tailwind CSS styling
We can start with adding some styling to the header of our accordion component by adding the following classes, for example.
<template>
...
<h3>
<button
v-bind="api.getTriggerProps({ value: item.title })"
class="flex justify-between items-center p-5 w-full font-medium text-left text-gray-500 rounded-t-xl border border-b-0 border-gray-200 focus:ring-4 focus:ring-gray-200 hover:bg-gray-100"
>
...
</button>
</h3>
...
</template>
Inside the accordion header we can now add the style logic to have the arrow SVG turn depending on the state. If the Zag machine api value
is the same as the accordions item title
this accordion item is open.
<template>
...
<svg
data-accordion-icon
class="w-6 h-6 shrink-0"
:class="{ 'rotate-180': api.value == item.title }"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path>
</svg>
...
</template>
Some small styling to the accordion content, and we have a nicely styled accordion that is also accessible.
<template>
...
<div
v-bind="api.getContentProps({ value: item.title })"
class="p-5 border border-b-0 border-gray-200"
>
{{ item.content }}
</div>
...
</template>
Slot
That’s it! Not that complicated to set up and a pleasure to use!
I do know I didn’t put much time or thought into the styling itself, feel free to make it better! I would also like to challenge you to make the accordion component reusable.
Note that setting this up for a React project with Vite is very similar.
Sources
StackBlitz playground: https://stackblitz.com/edit/vue-zag-tailwind?file=src/App.vue
Zag.js Documentation: https://zagjs.com/
Tailwind CSS Documentation: https://tailwindcss.com/
Top comments (1)
Great stuff!