Skip to content
W WRNexusJS
core component

FAQAccordion

Reusable faqaccordion component.

@wrnexus/ui 0.2.6713 props0 slots1 events

Usage

Components are auto-discovered. Use the component directly in any .wrn view:

<FAQAccordion
  eyebrow="eyebrow"
  title="Frequently asked questions"
  description="description"
  items="[]"
  centered="true"
/>

Legacy mount name: data-component="FAQAccordion".

Props and attributes

PropTypeRequirementDefault
classstringOptional""
eyebrowstringOptional""
titlestringOptional"Frequently asked questions"
descriptionstringOptional""
itemsstringOptional[]
centeredbooleanOptionaltrue
compactbooleanOptionalfalse
allowMultiplebooleanOptionalfalse
defaultOpenIndexnumberOptional0
showContactbooleanOptionalfalse
contactTextstringOptional"Still have questions?"
contactLabelstringOptional"Contact support"
contactHrefstringOptional"/support"

Slots

This component does not declare a slot.

Events

  • click

Source contract

Installed source: components/FAQAccordion.wrn

component FAQAccordion {
    props {
        class = ""
        eyebrow = ""
        title = "Frequently asked questions"
        description = ""

        items = []

        centered = true
        compact = false

        allowMultiple = false
        defaultOpenIndex = 0

        showContact = false
        contactText = "Still have questions?"
        contactLabel = "Contact support"
        contactHref = "/support"
    }

    state openIndexes = defaultOpenIndex >= 0 ? [defaultOpenIndex] : []

    functions {
        function isOpen(index) {
            return openIndexes.includes(index)
        }

        function toggleItem(index) {
            if (isOpen(index)) {
                openIndexes = openIndexes.filter(
                (itemIndex) => itemIndex !== index
                )
            } else if (allowMultiple) {
                openIndexes = [...openIndexes, index]
            } else {
                openIndexes = [index]
            }
        }
    }

    view {
        <section
            aria-labelledby="faq-accordion-title"
            class="w-full px-5 sm:px-8 lg:px-10 xl:px-12 {class}"
            class:py-10="compact"
            class:sm:py-12="compact"
            class:py-14="!compact"
            class:sm:py-16="!compact"
            class:lg:py-20="!compact"
            class:hidden="items.length === 0"
        >
        <div
                class="mx-auto w-full"
                class:max-w-5xl="centered"
                class:max-w-7xl="!centered"
            >
        <div
                    class="gap-10"
                    class:flex="centered"
                    class:flex-col="centered"
                    class:grid="!centered"
                    class:lg:grid-cols-[minmax(260px,0.7fr)_minmax(0,1.3fr)]="!centered"
                    class:lg:items-start="!centered"
                    class:lg:gap-14="!centered"
                >
        <!-- Heading -->
        <div
                        class="min-w-0"
                        class:mx-auto="centered"
                        class:max-w-3xl="centered"
                        class:text-center="centered"
                        class:max-w-xl="!centered"
                        class:lg:sticky="!centered"
                        class:lg:top-24="!centered"
                    >
        <!-- Eyebrow -->
        <p
                            class="text-xs font-bold uppercase tracking-[0.18em] text-indigo-600 dark:text-indigo-400"
                            class:hidden="eyebrow === ''"
                        >
                            {eyebrow}
                        </p>
        <!-- Title -->
        <h2
                            id="faq-accordion-title"
                            class="font-bold leading-tight tracking-tight text-slate-950 dark:text-white"
                            class:mt-3="eyebrow !== ''"
                            class:text-2xl="compact"
                            class:sm:text-3xl="compact"
                            class:text-3xl="!compact"
                            class:sm:text-4xl="!compact"
                        >
                            {title}
                        </h2>
        <!-- Description -->
        <p
                            class="mt-4 text-sm leading-7 text-slate-600 dark:text-slate-400"
                            class:hidden="description === ''"
                            class:sm:text-base="!compact"
                            class:mx-auto="centered"
                            class:max-w-2xl="centered"
                            class:max-w-xl="!centered"
                        >
                            {description}
                        </p>
        <!-- Contact card -->
        <div
                            class="mt-7 rounded-2xl border border-indigo-200/80 bg-linear-to-br from-indigo-50 via-white to-violet-50 p-5 shadow-sm dark:border-indigo-500/20 dark:from-indigo-500/10 dark:via-white/[0.03] dark:to-violet-500/10"
                            class:hidden="!showContact"
                        >
        <div
                                class="flex items-center gap-4"
                                class:justify-center="centered"
                            >
        <span
                                    class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-white text-indigo-600 shadow-sm ring-1 ring-inset ring-indigo-200 dark:bg-white/5 dark:text-indigo-300 dark:ring-indigo-500/20"
                                >
        <span class="icon-[lucide--messages-square] h-5 w-5" aria-hidden="true" >
        </span>
        </span>
        <div
                                    class="min-w-0"
                                    class:text-left="centered"
                                >
        <p class="text-sm font-semibold text-slate-950 dark:text-white">
                                        {contactText}
                                    </p>

                                    {#if contactLabel !== '' && contactHref !== ''}
                                    <a
                                        href="{contactHref}"
                                        class="mt-1 inline-flex items-center gap-1.5 text-sm font-bold text-indigo-600 transition-colors duration-200 hover:text-indigo-500 dark:text-indigo-400 dark:hover:text-indigo-300"
                                    >
                                        {contactLabel}

                                        <span class="icon-[lucide--arrow-right] h-4 w-4" aria-hidden="true" >
        </span>
        </a>
                                    {/if}
                                </div>
        </div>
        </div>
        </div>
        <!-- Accordion -->
        <div class="min-w-0">
        <div class="space-y-3">
                            {#each items as item, index}
                            <article
                                class=" overflow-hidden rounded-2xl border transition-[border-color,background-color,box-shadow,transform] duration-300 ease-out {isOpen(index) ? 'border-indigo-200 bg-indigo-50/60 shadow-md shadow-indigo-950/5 dark:border-indigo-500/20 dark:bg-indigo-500/10' : 'border-slate-200 bg-white shadow-sm hover:-translate-y-0.5 hover:border-indigo-200 hover:bg-slate-50 dark:border-white/10 dark:bg-white/[0.03] dark:hover:border-indigo-500/20 dark:hover:bg-white/5' } "
                            >
        <h3>
        <button
                                        type="button"
                                        @click="toggleItem(index)"
                                        aria-expanded="{isOpen(index)}"
                                        aria-controls="faq-answer-{index}"
                                        class="flex w-full items-center justify-between gap-5 text-left focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-indigo-500"
                                        class:px-4="compact"
                                        class:py-4="compact"
                                        class:px-5="!compact"
                                        class:py-5="!compact"
                                        class:sm:px-6="!compact"
                                    >
        <span class="flex min-w-0 items-start gap-3">
        <!-- Number -->
        <span
                                                class=" mt-0.5 grid h-7 w-7 shrink-0 place-items-center rounded-lg text-xs font-bold transition-[background-color,color,transform] duration-300 ease-out {isOpen(index) ? 'bg-indigo-600 text-white' : 'bg-slate-100 text-slate-500 dark:bg-white/5 dark:text-slate-400' } "
                                            >
                                                {index + 1}
                                            </span>
        <!-- Question -->
        <span
                                                class="font-semibold leading-6 text-slate-950 dark:text-white"
                                                class:text-sm="compact"
                                                class:text-base="!compact"
                                            >
                                                {item.question}
                                            </span>
        </span>
        <!-- Toggle icon -->
        <span
                                            class=" grid h-8 w-8 shrink-0 place-items-center rounded-lg transition-[background-color,color,transform] duration-300 ease-out {isOpen(index) ? 'bg-indigo-100 text-indigo-700 dark:bg-indigo-500/15 dark:text-indigo-300' : 'bg-slate-100 text-slate-500 dark:bg-white/5 dark:text-slate-400' } "
                                        >
        <span class=" icon-[lucide--chevron-down] h-4 w-4 transition-transform duration-300 ease-out {isOpen(index) ? 'rotate-180' : 'rotate-0'} " aria-hidden="true" >
        </span>
        </span>
        </button>
        </h3>
        <div
                                    id="faq-answer-{index}"
                                    role="region"
                                    aria-hidden="{!isOpen(index)}"
                                    class=" grid overflow-hidden transition-[grid-template-rows,opacity] duration-300 ease-out {isOpen(index) ? 'grid-rows-[1fr] opacity-100' : 'grid-rows-[0fr] opacity-0' } "
                                >
        <div class="min-h-0 overflow-hidden">
        <div class="border-t border-indigo-200/60 dark:border-indigo-500/15">
        <div
                                                class=" text-sm leading-7 text-slate-600 transition-[transform,opacity] duration-300 ease-out dark:text-slate-400  {compact ? 'px-4 pb-4 pt-3' : 'px-5 pb-5 pt-4 sm:px-6 sm:pb-6 sm:pt-5' }  {isOpen(index) ? 'translate-y-0 opacity-100' : '-translate-y-2 opacity-0' } "
                                            >
        <p class="m-0">
                                                    {item.answer}
                                                </p>

                                                {#if item.linkLabel !== '' && item.href !== ''}
                                                <a
                                                    href="{item.href}"
                                                    class=" group inline-flex items-center gap-2 text-sm font-bold text-indigo-600 transition-colors duration-200 hover:text-indigo-500 dark:text-indigo-400 dark:hover:text-indigo-300 "
                                                >
                                                    {item.linkLabel}

                                                    <span class=" icon-[lucide--arrow-up-right] h-4 w-4 transition-transform duration-200 group-hover:translate-x-0.5 group-hover:-translate-y-0.5 " aria-hidden="true" >
        </span>
        </a>
                                                {/if}
                                            </div>
        </div>
        </div>
        </div>
        </article>
                            {/each}
                        </div>
        </div>
        </div>
        </div>
        <!-- Tailwind safelist for utilities used by reactive class directives -->
        <span class=" hidden flex flex-col grid justify-center lg:gap-14 lg:grid-cols-[minmax(260px,0.7fr)_minmax(0,1.3fr)] lg:items-start lg:py-20 lg:sticky lg:top-24 max-w-2xl max-w-3xl max-w-5xl max-w-7xl max-w-xl mt-3 mx-auto px-4 px-5 py-4 py-5 py-10 py-14 sm:px-6 sm:py-12 sm:py-16 sm:text-base sm:text-3xl sm:text-4xl text-base text-center text-left text-sm text-2xl text-3xl grid-rows-[0fr] grid-rows-[1fr] opacity-0 opacity-100 -translate-y-2 translate-y-0 rotate-0 rotate-180 " >
        </span>
        </section>
    }
}