DEV Community

Cover image for How to build a details drop-down with VueJS and TailwindCSS
Ekure Edem
Ekure Edem

Posted on

How to build a details drop-down with VueJS and TailwindCSS

Introduction

Requires a basic understanding of html, vue and tailwindcss.

It turns out that the default styling of the <details> tag is prety ugly because of this,
most people just decide not to use it and fallback to divs, h1 and ul with some kind
of state management to support it.

It basically looks like this closed without the styling

Image description

And this when open

Image description

But with Tailwind and a bit of vue magic, we can achieve this

Image description
when open.

With no time to waste, let's get started.

Project Structure

To incorporate this with your vue website, you will need not to start a new npm or yarn or pnpm vue app.

I personally used pnpm and vite for this project

pnpx create-vite .
pnpm install . 
pnpm install tailwindcss autoprefixer postcss 
tailwindcss init -p
pnpm run dev
Enter fullscreen mode Exit fullscreen mode

All you need is your component file and tailwindcss enabled. It should look nothing less than this.

Image description

For best results, Add a vector library you like. But in this project, I will be using font-awesome.

I included the vector library by adding this script tag to the index.html

    <script defer src="https://use.fontawesome.com/releases/v5.15.4/js/all.js" integrity="sha384-rOA1PnstxnOBLzCLMcre8ybwbTmemjzdNlILg8O7z1lUkLXozs4DHonlDtnE7fpc" crossorigin="anonymous"></script>
Enter fullscreen mode Exit fullscreen mode

Don't forget to import the index.css file into your main.js file.

App.vue

Nothing much here. Just basic imports and data declaration.

<template>
  <custom-details :title="heading" :data="tools" />
</template>

<script>
import CustomDetails from './components/custom-details.vue';
export default {
  components: { CustomDetails },
  data(){
    return {
      heading: "Tools for building a website",
      tools: ["HTML", "CSS", "JavaScript", "VueJS", "ReactJS", "Vite"]
    }
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

custom-details component

In out custom details component, lets write down some templates.

<template>
  <details>
    <summary>
      <div>
        <h2> {{title}} </h2>
        <i class="fa fa-caret-down"></i>
      </div>
    </summary>
    <ul>
      <li v-for="(detail, index) in detailData" :key="index">
        {{ detail }}
      </li>
    </ul>
  </details>
</template>
Enter fullscreen mode Exit fullscreen mode

In this template, no style is being added to it to keep it readable and easy to modify. All styles will be written in the styles tag.

Time to add some functionality

In our script tag,

<script>
export default {
  props: ["title", "data"],
  data(){
    return {
      detailData: this.data instanceof Array ? [...this.data] : []
    }
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

Explanation

First, we have to define the data our custom-details will need which are a title and some array of strings data by using the props attribute.

  ...
  props: ["title", "data"]
  ...
Enter fullscreen mode Exit fullscreen mode

Then, we have to ensure that the data being provided by the parent component is an Array, so we check if the data is and array, if not, we return an empty array using the tenery operator.

this.data instanceof Array ? [...this.data] : []
Enter fullscreen mode Exit fullscreen mode

Time to add some styles (the fun part :)

To make our code clean, we are using the styles tag.

Default styles

To our style.css which is usually automatically imported by your build tool into the main.js file, add the following to the top.

  @tailwind base;
  @tailwind utilities;
Enter fullscreen mode Exit fullscreen mode

The @tailwind components; was not added since we do not require any more styles.

Component styles

To our styles tag should initially look like this.

  <style scoped>
  </style>
Enter fullscreen mode Exit fullscreen mode

First of, let us add some style to the details tag.

    details{
      @apply border border-gray-300 rounded-md my-2 mx-auto;
    }
Enter fullscreen mode Exit fullscreen mode

Next, remove default markers of the summary tag and style it

    ...
    summary::-webkit-details-marker,
    summary::marker{
      display: none;
    }

    details > summary{
      @apply flex py-2 cursor-pointer;
    }

    details[open] > summary{
      @apply border-b border-gray-300;
    }
    ...
Enter fullscreen mode Exit fullscreen mode

Then, styling the inner summary div

    ...

    summary > div {
      @apply flex items-center gap-x-6 w-full text-gray-700 px-4;
    }

    ...
Enter fullscreen mode Exit fullscreen mode

Finally, we then style the list

    ...

    details > ul {
      @apply flex flex-col divide-y-2 divide-gray-200;
    }

    ul > li {
      @apply flex px-4 hover:bg-gray-50 cursor-pointer text-sm text-gray-500 font-bold py-1.5
    }

Enter fullscreen mode Exit fullscreen mode

Your final CSS should look like this.


<style scoped>

  details{
    @apply border border-gray-300 rounded-md my-2 mx-auto;
  }

  details > summary{
    @apply flex py-2 cursor-pointer;
  }

  details[open] > summary{
    @apply border-b border-gray-300;
  }

  summary::-webkit-details-marker,
  summary::marker{
    display: none;
  }

  summary > div {
    @apply flex items-center gap-x-6 w-full text-gray-700 px-4;
  }

  details > ul {
    @apply flex flex-col divide-y-2 divide-gray-200;
  }

  ul > li {
    @apply flex px-4 hover:bg-gray-50 cursor-pointer text-sm text-gray-500 font-bold py-1.5
  }

</style>
Enter fullscreen mode Exit fullscreen mode

If properly followed, the results should give you this.

Image description

Thanks for reading and have a good day.

Top comments (2)

Collapse
 
equan profile image
Equan P.

Thanks!

Collapse
 
astraldev profile image
Ekure Edem

It works as expected right?