Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Romainpetit/1fc41d3040e7ed8b48bba70997dc1b95 to your computer and use it in GitHub Desktop.
Save Romainpetit/1fc41d3040e7ed8b48bba70997dc1b95 to your computer and use it in GitHub Desktop.
Accordion Component for VueJS and GSAP
#app
zz-accordion(v-bind:contents.sync="contents")
Vue.component('zz-accordion', {
props: ['contents'],
data: function() {
return {
}
},
methods: {
expand: function(e, i) {
e.preventDefault();
let el = this.$refs['accordion-body-' + i][0];
if (this.contents[i].active === false) {
this.contents[i].active = true;
TweenLite.to(el, 1, {
height: el.scrollHeight,
ease: Elastic.easeOut.config(1, 0.3)
});
} else {
this.contents[i].active = false;
TweenLite.to(el, 0.5, {
height: 0,
ease: Bounce.easeOut
});
}
}
},
template: `
<div class="accordion">
<div
class="accordion-item"
v-for="content, i in contents"
v-bind:class="{ 'accordion-active': content.active }"
>
<div class="accordion-header">
<a href="#" v-on:click="expand(event, i)">
<div class="accordion-header-div">{{ content.title }}</div>
<div class="accordion-header-div">
<div class="accordion-caret"></div>
</div>
</a>
</div>
<div class="accordion-body" v-bind:ref="'accordion-body-' + i">
<div class="accordion-content">{{ content.description }}</div>
</div>
</div>
</div>
`
});
// ----------
var app = new Vue({
el: '#app',
data: {
contents: [{
title: 'Lorem ipsum dolor sit amet',
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
active: false
}, {
title: 'Ut enim ad minim veniam',
description: 'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
active: false
}, {
title: 'Duis aute irure dolor in reprehenderit',
description: 'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur',
active: false
}, {
title: 'Excepteur sint occaecat cupidatat non proident',
description: 'Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
active: false
}]
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.3/TweenMax.min.js"></script>
$base-color: #727272;
$base-width: 320px;
$base-time: 200ms;
$app-background-color: #82dfff;
$app-padding: 2rem;
$accordion-background-color: #ffffff;
$accordion-max-width: $base-width;
$accordion-padding: 2rem 1rem;
$accordion-border-radius: 0.5rem;
$accordion-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.3);
$accordion-header-font-size: 1.2rem;
$accordion-header-height: 2rem;
$accordion-header-padding: 1rem 1rem 1rem 1rem;
$accordion-header-border-radius: 0.6rem;
$accordion-header-transition: background-color $base-time ease-in-out;
$accordion-header-hover-background-color: #efefef;
$accordion-caret-color: $base-color;
$accordion-caret-background-image: linear-gradient(
to top right,
transparent 50%,
$accordion-caret-color 50%
);
$accordion-caret-width: 0.5rem;
$accordion-caret-height: 0.5rem;
$accordion-caret-transform: rotate(-45deg);
$accordion-caret-transforming: scale(1.5) rotate(45deg);
$accordion-caret-active-transform: rotate(135deg);
$accordion-body-content-padding: 1rem;
@import url("https://fonts.googleapis.com/css?family=Montserrat:400,400i,700");
html, body, #app {
color: $base-color;
font-family: Montserrat, sans-serif;
font-size: 14px;
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
#app,
.accordion,
.accordion-item,
.accordion-header,
.accordion-body {
font-family: inherit;
}
#app {
background-color: $app-background-color;
display: flex;
box-sizing: border-box;
}
.accordion {
background-color: $accordion-background-color;
width: 100%;
max-height: 100%;
max-width: $accordion-max-width;
padding: $accordion-padding;
margin: auto;
box-sizing: border-box;
overflow: auto;
box-shadow: $accordion-box-shadow;
}
.accordion-item.accordion-active {
.accordion-caret {
animation: accordion-is-active $base-time linear forwards;
}
}
.accordion-header {
color: inherit;
font-size: $accordion-header-font-size;
font-weight: bold;
position: relative;
a {
color: inherit;
text-decoration: none;
width: 100%;
height: 100%;
display: flex;
justify-content: space-between;
box-sizing: border-box;
border-radius: $accordion-header-border-radius;
transition: $accordion-header-transition;
&:not(.accordion-active):hover {
background-color: $accordion-header-hover-background-color;
}
}
}
.accordion-header-div {
padding: $accordion-header-padding;
&:last-child {
padding-left: 0;
display: flex;
align-items: center;
justify-content: center;
}
}
.accordion-caret {
background-image: $accordion-caret-background-image;
width: $accordion-caret-width;
height: $accordion-caret-height;
transform: $accordion-caret-transform;
animation: accordion-is-inactive $base-time linear forwards;
}
.accordion-body {
height: 0;
overflow: hidden;
}
.accordion-content {
padding: $accordion-body-content-padding;
}
@keyframes accordion-is-inactive {
0% { transform: $accordion-caret-transform; }
50% { transform: $accordion-caret-transforming; }
100% { transform: $accordion-caret-active-transform; }
}
@keyframes accordion-is-active {
0% { transform: $accordion-caret-active-transform; }
50% { transform: $accordion-caret-transforming; }
100% { transform: $accordion-caret-transform; }
}
@media screen and (min-width: $base-width) {
#app {
padding: $app-padding;
overflow: auto;
}
.accordion {
max-height: none;
border-radius: $accordion-border-radius;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment