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 ➔
npm i vue-collapsed # yarn add vue-collapsed # pnpm add vue-collapsed # bun add vue-collapsedName Description Type Required
when
Value to control collapse boolean ✅ baseHeight
Collapsed height in px, defaults to 0
. number ❌ as
Tag to use instead of div
keyof HTMLElementTagNameMap ❌ Name Description Type @expand
Expand transition start () => void @expanded
Expand transition completed () => void @collapse
Collapse transition start () => void @collapsed
Collapse transition completed () => void
<script setup> import { ref } from 'vue' import { Collapse } from 'vue-collapsed' const isExpanded = 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.
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 */ }
To transition other properties use the attribute data-collapse
:
collapsed
expanding
expanded
Collapse expanded
collapsing
collapsed
.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>
<script setup> import { reactive } from 'vue' import { Collapse } from 'vue-collapsed' const questions = 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 } ]) function handleAccordion(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> <div v-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>
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' const isExpanded = ref(false) const TOGGLE_ID = 'toggle-id' const COLLAPSE_ID = 'collapse-id' const toggleAttrs = computed(() => ({ id: TOGGLE_ID, 'aria-controls': COLLAPSE_ID, 'aria-expanded': isExpanded.value })) const collapseAttrs = { role: 'region', id: COLLAPSE_ID, 'aria-labelledby': TOGGLE_ID } function handleCollapse() { isExpanded.value = !isExpanded.value } </script> <template> <div> <button v-bind="toggleAttrs" @click="handleCollapse">This a panel.</button> <Collapse v-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>
MIT
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4