BAccordion is a simple Vue component that created an expanding and collapsing block. It uses the vue transition expand and can be paired with the Sass mixin accordion().

Vue Component

BpAccordion

Props

  • block - the block class (defaults to ‘accordion’)
  • headingElement - the heading element (defaults to ‘heading’)
  • iconElement - the icon element (defaults to ‘icon’)
  • contentElement - the content element (defaults to ‘content’)
  • contentWrapperElement - the content wrapper element (defaults to ‘contentWrapper’)
  • id - A unique string to identify an accordion item for accessibility purposes (defaults to generating a random number)

Sass Mixin

@mixin accordion()

Parameters

None.

<bp-accordion class="accordion" :id="'content1'">
    <template #heading>
        <h3 class="accordion__heading">Content 1</h3>
    </template>
    <p>
        Amet consectetur dignissimos at sequi officiis? Debitis accusamus veritatis voluptatem quas quis? Optio velit maxime ipsam temporibus ratione, temporibus. Fugiat eligendi officiis <a href="#">impedit</a> voluptate recusandae. Veniam expedita velit aut ad.
    </p>
</bp-accordion>
<bp-accordion class="accordion" :id="'content2'">
    <template #heading>
        <h3 class="accordion__heading">Content 2</h3>
    </template>
    <p>
        Elit laboriosam ipsum dolorem earum quisquam consequuntur? Voluptatibus qui sed!
    </p>
</bp-accordion>
<bp-accordion class="accordion">
    <template #heading>
        <h3 class="accordion__heading">Content 3</h3>
    </template>
    <p>
        Ipsum voluptatem provident sit ab odio magni, repudiandae veritatis. Beatae nisi reprehenderit earum voluptates fugit nam. Explicabo laboriosam corporis sed est iusto veniam error. Sequi corrupti iusto sed itaque repudiandae saepe voluptas ipsa Ut et ab vitae temporibus repellat Cumque omnis autem culpa accusantium voluptatibus est? Molestiae voluptatem eaque vero?
    </p>
</bp-accordion>
<bp-accordion class="accordion" :id="'content4'">
    <template #heading>
        <h3 class="accordion__heading">Content 4</h3>
    </template>
    <p>
        Dolor reprehenderit ut fugiat necessitatibus debitis veniam in Tenetur deleniti suscipit dignissimos corporis expedita Vitae eum quia quia in modi adipisci voluptate nihil Ullam delectus assumenda consequatur aperiam nam explicabo!
    </p>
</bp-accordion>
<bp-accordion class="accordion" :id="'content5'">
    <template #heading>
        <h3 class="accordion__heading">Content 5</h3>
    </template>
    <p>
        Dolor quibusdam beatae sint provident nihil. Dolorem minus consectetur cumque voluptas fuga Soluta eaque quos aspernatur aliquam rerum. Exercitationem illum vel quod nemo fugiat totam. Optio et ducimus consequatur at
    </p>
</bp-accordion>
  • Content:
    <template>
        <div :class="block">
            <button
                :id="`${id}-control`"
                :class="`${block}__${headerElement}`"
                :aria-expanded="`${isOpen}`"
                :aria-controls="id"
                @click="open"
            >
                <slot name="heading" />
                <slot name="icon">
                    <svg
                        :class="[`${block}__${iconElement}`, { '-open': isOpen }]"
                        width="24"
                        height="24"
                        viewBox="0 0 24 24"
                        stroke-width="2"
                        stroke="currentColor"
                        fill="none"
                    >
                        <polyline points="6 9 12 15 18 9" />
                    </svg>
                </slot>
            </button>
            <Transition
                name="accordion__transition"
                @enter="startTransition"
                @leave="endTransition"
            >
                <div
                    v-if="isOpen"
                    :id="id"
                    ref="contentWrapper"
                    :class="`${block}__${contentWrapperElement}`"
                >
                    <div
                        ref="content"
                        :class="`${block}__${contentElement}`"
                        :aria-labelledby="`${id}-control`"
                    >
                        <slot />
                    </div>
                </div>
            </Transition>
        </div>
    </template>
    
    <script setup>
    import { ref, nextTick } from 'vue'
    
    defineProps({
        block: { type: String, default: 'accordion' },
        headerElement: { type: String, default: 'header' },
        iconElement: { type: String, default: 'icon' },
        contentElement: { type: String, default: 'content' },
        contentWrapperElement: { type: String, default: 'contentWrapper' },
        id: { type: String, default: () => Math.random().toString(36).substr(2) },
    })
    
    const isOpen = ref(false)
    const content = ref(null)
    const contentWrapper = ref(null)
    
    const open = () => {
        isOpen.value = !isOpen.value
    }
    
    const startTransition = async (el) => {
        await nextTick()
        el.style.height = `${content.value.scrollHeight}px`
    }
    
    const endTransition = (el) => {
        el.removeAttribute('style')
    }
    
    addEventListener('resize', () => {
        if (!content.value) {
            return
        }
    
        const newHeight = `${content.value.scrollHeight}px`
    
        requestAnimationFrame(() => {
            contentWrapper.value.style.height = newHeight
        })
    })
    </script>
    
  • URL: /components/raw/accordion/BpAccordion.vue
  • Filesystem Path: resources/styles/molecules/accordion/BpAccordion.vue
  • Size: 2.4 KB
  • Content:
    @use "/resources/styles/config";
    
    @mixin accordion() {
        --accordion-color-hover: var(--color-hover, #{config.$primary-hover});
    
        $b: &;
    
        box-shadow: config.$med-shadow;
        margin-bottom: 1.5rem;
        display: block;
    
        &__header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            text-align: left;
            padding: 1rem;
            transition: background-color var(--duration-fast) config.$fade-easing;
            width: 100%;
    
            &:hover {
                background-color: var(--accordion-color-hover);
            }
        }
    
        &__heading {
            margin-bottom: 0;
        }
    
        &__content {
            border-top: 1px solid config.$gray-100;
            margin: 0 1rem;
            padding: .5rem 0;
        }
    
        &__contentWrapper {
            overflow: hidden;
            height: 0;
        }
    
        &__icon {
            flex-shrink: 0;
            margin-left: 1rem;
            transition: transform var(--duration-fast) config.$ease-out-quint;
    
            &.-open {
                transform: rotate(-180deg);
            }
        }
    
        &__transition {
            &-enter-from,
            &-enter-active,
            &-leave-active,
            &-leave-to {
                transition: height config.$slow config.$entrance-easing;
            }
        }
    }
    
    .accordion {
        @include accordion;
    }
    
  • URL: /components/raw/accordion/_index.scss
  • Filesystem Path: resources/styles/molecules/accordion/_index.scss
  • Size: 1.3 KB