All checks were successful
Deploy site (production) / build-and-deploy (push) Successful in 48s
- 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) ✓
53 lines
1.5 KiB
TypeScript
53 lines
1.5 KiB
TypeScript
import type { Directive } from 'vue';
|
|
|
|
// Directive `v-reveal` : révèle un bloc quand il entre dans le viewport.
|
|
// Reprend l'intention du setupReveal() du design (seuil ~92% de la hauteur),
|
|
// via un IntersectionObserver partagé. Respecte prefers-reduced-motion.
|
|
let reducedMotion = false;
|
|
try {
|
|
reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
} catch {
|
|
/* matchMedia indisponible */
|
|
}
|
|
|
|
let observer: IntersectionObserver | null = null;
|
|
|
|
function getObserver(): IntersectionObserver | null {
|
|
if (typeof IntersectionObserver === 'undefined') return null;
|
|
if (!observer) {
|
|
observer = new IntersectionObserver(
|
|
(entries) => {
|
|
for (const entry of entries) {
|
|
if (entry.isIntersecting) {
|
|
entry.target.classList.add('is-visible');
|
|
observer?.unobserve(entry.target);
|
|
}
|
|
}
|
|
},
|
|
// -8% en bas ≈ déclenche quand le haut du bloc franchit ~92% du viewport.
|
|
{ threshold: 0, rootMargin: '0px 0px -8% 0px' },
|
|
);
|
|
}
|
|
return observer;
|
|
}
|
|
|
|
export const reveal: Directive<HTMLElement> = {
|
|
mounted(el) {
|
|
el.classList.add('reveal');
|
|
if (reducedMotion) {
|
|
el.classList.add('is-visible');
|
|
return;
|
|
}
|
|
const io = getObserver();
|
|
if (!io) {
|
|
// Pas d'IntersectionObserver : on révèle immédiatement (pas de contenu caché).
|
|
el.classList.add('is-visible');
|
|
return;
|
|
}
|
|
io.observe(el);
|
|
},
|
|
unmounted(el) {
|
|
observer?.unobserve(el);
|
|
},
|
|
};
|