Skip to content

🏋️‍♂️ CSS height transition from any to auto and vice versa for Vue and Nuxt. Accordion ready.

License

Notifications You must be signed in to change notification settings

smastrom/vue-collapsed

Repository files navigation

npmdependenciesnpm bundle sizedownloadsGitHub Workflow Status

Vue Collapsed

Dynamic CSS height transition from any to auto and vice versa. Accordion ready.

Examples and Demo - Stackblitz


Check out my other packages for Vue and Nuxt:

🔔 Notivue
Fully-featured notification system for Vue and Nuxt.
Visit repo ➔

🌀 Vue Global Loader
Global loaders made easy for Vue and Nuxt.
Visit repo ➔

👌 Vue Use Active Scroll
Accurate TOC/sidebar links without compromises.
Visit repo ➔

🔥 Vue Use Fixed Header
Turn your boring fixed header into a smart one with three lines of code.
Visit repo ➔


Installation

npm i vue-collapsed # yarn add vue-collapsed# pnpm add vue-collapsed# bun add vue-collapsed

Props

NameDescriptionTypeRequired
whenValue to control collapseboolean
baseHeightCollapsed height in px, defaults to 0.number
asTag to use instead of divkeyof HTMLElementTagNameMap

Emits

NameDescriptionType
@expandExpand transition start() => void
@expandedExpand transition completed() => void
@collapseCollapse transition start() => void
@collapsedCollapse transition completed() => void

Usage

<script setup>import{ref } from'vue'import{Collapse } from'vue-collapsed'constisExpanded=ref(false)</script> <template> <button@click="isExpanded = !isExpanded">Trigger</button> <Collapse:when="isExpanded"> <p>{{'Collapsed '.repeat(100) }}</p> </Collapse> </template>

Automatic transition (default behavior)

By default, if no height transition is specified the following one is automatically added to the Collapse element:

height var(--vc-auto-duration) cubic-bezier(0.33, 1, 0.68, 1)

--vc-auto-duration is calculated in background and corresponds to an optimal transition duration based on your content height.

This is the recommended way to use this package unless you want to customize the transition.

Custom transition

If you prefer to use a custom duration or easing, add a class to Collapse that transitions the height property:

<Collapse :when="isExpanded" class="v-collapse"> <p>{{'Collapsed '.repeat(100) }}</p> </Collapse>
.v-collapse{transition: height 300ms ease-out; /* or transition: height var(--vc-auto-duration) ease-in-out */ }

Multiple transitions

To transition other properties use the attribute data-collapse:

TransitionFromEnterLeave
Expandcollapsedexpandingexpanded
Collapseexpandedcollapsingcollapsed
.v-collapse{--dur-easing:var(--vc-auto-duration) cubic-bezier(0.33,1,0.68,1); transition: height var(--dur-easing), opacity var(--dur-easing)} .v-collapse[data-collapse='expanded'], .v-collapse[data-collapse='expanding']{opacity:1} .v-collapse[data-collapse='collapsed'], .v-collapse[data-collapse='collapsing']{opacity:0}

Or to use different easings/durations for expand and collapse:

.v-collapse[data-collapse='expanding']{transition: height 600ms ease-in-out} .v-collapse[data-collapse='collapsing']{transition: height 300ms ease-out}

Above values can also be accessed using v-slot:

<Collapse :when="isExpanded" class="v-collapse" v-slot="{state }">{{state === 'collapsing' ? 'Collapsing content...' : null }} </Collapse>

Example - Accordion

<script setup>import{reactive } from'vue'import{Collapse } from'vue-collapsed'constquestions=reactive([{ title:'Question one', answer:'Answer one', isExpanded:false// Initial value },{ title:'Question two', answer:'Answer two', isExpanded:false },{ title:'Question three', answer:'Answer three', isExpanded:false }])functionhandleAccordion(selectedIndex){questions.forEach((_, index) =>{ questions[index].isExpanded= index === selectedIndex ?!questions[index].isExpanded:false })}/** * For individual control you might use: * * function handleMultiple(index){ * questions[index].isExpanded = !questions[index].isExpanded * }*/</script> <template> <divv-for="(question, index) in questions":key="question.title"> <button@click="handleAccordion(index)">{{question.title }} </button> <Collapse:when="questions[index].isExpanded"> <p>{{question.answer }} </p> </Collapse> </div> </template>

Accessibility

vue-collapsed automatically detects if users prefer reduced motion and will disable transitions accordingly while keeping the same API behavior (emitting events and post-transition styles).

You should only add aria attributes to the Collapse element according to your use case.

<script setup>import{ref, computed } from'vue'import{Collapse } from'vue-collapsed'constisExpanded=ref(false)constTOGGLE_ID='toggle-id'constCOLLAPSE_ID='collapse-id'consttoggleAttrs=computed(() => ({ id:TOGGLE_ID,'aria-controls':COLLAPSE_ID,'aria-expanded':isExpanded.value}))constcollapseAttrs={ role:'region', id:COLLAPSE_ID,'aria-labelledby':TOGGLE_ID}functionhandleCollapse(){isExpanded.value=!isExpanded.value}</script> <template> <div> <buttonv-bind="toggleAttrs"@click="handleCollapse">This a panel.</button> <Collapsev-bind="collapseAttrs":when="isExpanded"> <p>{{'Collapsed '.repeat(100) }}</p> </Collapse> </div> </template>

Manually disabling transitions

<template> <Collapse:when="isExpanded"class="instant-collapse"> <p>{{'Collapsed '.repeat(100) }}</p> </Collapse> </template> <style>.instant-collapse{transition: none;}</style>

License

MIT

About

🏋️‍♂️ CSS height transition from any to auto and vice versa for Vue and Nuxt. Accordion ready.

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

    Contributors 3

    •  
    •  
    •