Files
arboretum/packages/site/src/components/FaqSection.vue
Johan LEROY 3b251c8174 feat: Add packages/site — landing vitrine (Vue 3 + Tailwind 4)
- Scaffold packages/site with 11 Vue components: hero (animated terminal), features, showcase, work groups, how-it-works, security, FAQ accordion, footer
- i18n EN/FR via vue-i18n with 82+ keys, autodetect navigator.language, toggle without reload
- Animated timeline (terminal, dialog, toast) ported from design, scroll-reveal, copy buttons (2 instances)
- Self-hosted JetBrains Mono fonts (@fontsource), optimized assets (mark 28KB, og-cover 1200×630)
- SEO: canonical, og:image, theme-color, robots.txt, sitemap.xml for git-arboretum.com
- Monorepo integration (decoupled from npm release): build:site / dev:site / preview:site scripts
- CI: add build:site step to ci.yml for early detection; create .gitea/workflows/prod.yml for FTPS deploy (lftp mirror) to git-arboretum.com
- Validated builds ✓ and UI rendering (EN/FR headless test) ✓
2026-06-19 10:02:22 +02:00

53 lines
2.2 KiB
Vue

<script setup lang="ts">
import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { FAQS } from '../i18n/content';
import type { AppLocale } from '../i18n';
const { t, locale } = useI18n();
const open = ref(0);
const items = computed(() => FAQS[locale.value as AppLocale]);
function toggle(i: number): void {
open.value = open.value === i ? -1 : i;
}
function panelStyle(i: number) {
return {
maxHeight: open.value === i ? '300px' : '0px',
opacity: open.value === i ? 1 : 0,
overflow: 'hidden',
transition: 'max-height .4s ease, opacity .35s ease',
} as const;
}
</script>
<template>
<section id="faq" class="mx-auto max-w-[780px] scroll-mt-[84px] px-6 pb-[100px]">
<div v-reveal class="mb-[38px] text-center">
<div class="mb-3.5 font-mono text-xs uppercase tracking-[0.14em] text-emerald-400">{{ t('navFaq') }}</div>
<h2 class="m-0 text-[clamp(28px,3.4vw,40px)] font-semibold tracking-[-0.025em] text-zinc-50">{{ t('faqTitle') }}</h2>
</div>
<div v-reveal>
<div v-for="(f, i) in items" :key="i" class="border-t" :class="open === i ? 'border-emerald-400/40' : 'border-zinc-800'">
<button
type="button"
:aria-expanded="open === i"
:aria-controls="`faq-panel-${i}`"
class="flex w-full cursor-pointer items-center justify-between gap-4 border-none bg-transparent px-1 py-5 text-left text-[clamp(16px,2vw,18px)] font-medium text-zinc-50 focus-visible:rounded-md focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-emerald-500/70"
@click="toggle(i)"
>
<span>{{ f.q }}</span>
<span class="flex flex-none text-emerald-400 transition-transform duration-300" :class="open === i ? 'rotate-180' : ''">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#34d399" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>
</span>
</button>
<div :id="`faq-panel-${i}`" :style="panelStyle(i)" :inert="open !== i" :aria-hidden="open !== i">
<p class="m-0 max-w-[640px] px-1 pb-5 text-[15.5px] leading-[1.6] text-zinc-400">{{ f.a }}</p>
</div>
</div>
</div>
</section>
</template>