The one with styling Zag.js components with Tailwind CSS
Tags: vue, zagjs, tailwindcss, webdev
Published: 2022-11-29 16:38:36

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](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/](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/](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](https://zagjs.com/components/vue-sfc/accordion).

We will have to install the machine for this component, which is completely framework-agnostic. 

```jsx
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.

```jsx
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.

```jsx
<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.


![Unstyled Zagjs Accordion component](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0412h35g9ilc3etn7qom.png)

### Adding styling

The Zag.js documentation does show a styling guide on how to style each accordion part with CSS [here]([https://zagjs.com/components/react/accordion#styling-guide](https://zagjs.com/components/react/accordion#styling-guide)).
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`.

```jsx
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.

```jsx
// 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.

```css
<!-- 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.

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

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

```html
<template>
	...
	<div
		v-bind="api.getContentProps({ value: item.title })"
		class="p-5 border border-b-0 border-gray-200"
		>
		{{ item.content }}
	</div>
...
</template>
```

![Styled Zagjs Accordion component with Tailwind CSS](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7m0pfgj5qzgum1hsg9jg.png)

## 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](https://stackblitz.com/edit/vue-zag-tailwind?file=src/App.vue)

Zag.js Documentation: [https://zagjs.com/](https://zagjs.com/)

Tailwind CSS Documentation: [https://tailwindcss.com/](https://tailwindcss.com/)