Pages & Routes
Onyx utilise l'App Router de Next.js. Chaque dossier dans src/app/ correspond à une route. Modifier, ajouter ou supprimer une page est aussi simple qu'agir sur les fichiers.
Page d'accueil
Le contenu de la page d'accueil est centralisé dans src/data/home.ts. Ce fichier exporte un objet structuré par sections : hero, features, services, stats, FAQ, CTA. Modifiez les textes, titres et liens ici sans toucher aux composants.
export const homePageData = { hero: { title: "Votre titre principal", subtitle: "Sous-titre accrocheur", ctaLabel: "Commencer", ctaHref: "/contact", }, // features, stats, faq, cta... }
Ajouter une page
Créez un dossier dans src/app/ avec un fichier page.tsx :
import { type Metadata } from "next"; export const metadata: Metadata = { title: "Ma Page", description: "Description SEO", }; export default function MaPage() { return <main>...</main>; }
src/app/sitemap.ts pour qu'elle soit incluse dans le sitemap XML généré automatiquement.Blocs réutilisables
Les sections animées disponibles sont dans src/components/blocks/. Vous pouvez les composer librement dans n'importe quelle page :
| Composant | Rôle |
|---|---|
HeroSection | Section principale avec titre, sous-titre et CTA |
FeaturesSection | Grille de fonctionnalités avec icônes |
ServicesSection | Cards de services avec liens |
StatsSection | Compteurs animés (chiffres clés) |
FaqSection | Accordéon de questions-réponses |
CtaSection | Bloc d'appel à l'action |
ContactFormSection | Formulaire de contact avec validation |
Créer un bloc personnalisé
Si les blocs existants ne couvrent pas votre besoin, créez le vôtre dans src/components/blocks/. Voici la structure type d'un bloc animé avec Framer Motion :
"use client"; import { motion } from "framer-motion"; interface MonBlocProps { title: string; items: { label: string; description: string }[]; } export function MonBloc({ title, items }: MonBlocProps) { return ( <section className="py-16"> <motion.h2 initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} > {title} </motion.h2> </section> ); }
useState,useEffect…) ou des event handlers doit avoir la directive "use client" en première ligne.Animation au scroll avec variantes
Pour des animations cohérentes sur plusieurs éléments, définissez les variantes en dehors du composant. Les valeurs ease dans les variantes d'objet nécessitent as const pour satisfaire TypeScript :
const cardVariants = { hidden: { opacity: 0, y: 24 }, visible: (i: number) => ({ opacity: 1, y: 0, transition: { delay: i * 0.1, ease: "easeOut" as const, } }), }; // Dans le JSX : {items.map((item, i) => ( <motion.div variants={cardVariants} initial="hidden" whileInView="visible" viewport={{ once: true }} custom={i} /> ))}
AnimatedContainer — fade-in simple
Pour les animations de fade-in au scroll sans configuration particulière, le wrapper AnimatedContainer suffit. Il accepte un délai optionnel :
import { AnimatedContainer } from "@/components/shared/animated-container"; <AnimatedContainer delay={0.2}> <p>Apparaît en fondu au scroll</p> </AnimatedContainer>
Composants UI (shadcn)
Les composants de base (Button, Card, Badge, Input, Dialog…) viennent de shadcn/ui et sont dans src/components/ui/. Ils lisent directement les tokens CSS définis dans globals.css — changer --primarysuffit pour recolorer tous les boutons primaires.
src/components/ui/directement. Pour ajouter un nouveau composant shadcn, utilisez la CLI : npx shadcn@latest add dialogComposants disponibles utiles
| Composant | Usage typique |
|---|---|
Button | CTA, actions primaires et secondaires |
Card / CardContent | Mise en avant de contenu, pricing, features |
Badge | Labels, tags de catégories |
Input / Textarea | Champs de formulaire |
Accordion | FAQ, questions-réponses |
Dialog / Sheet | Modals et panneaux latéraux |
Separator | Séparateurs visuels |
Tabs | Contenu tabulé |
Tooltip | Info-bulles sur icônes |
Icônes
Toutes les icônes proviennent de lucide-react. Parcourez le catalogue sur lucide.dev et importez-les directement :
import { Zap, Shield, Globe } from "lucide-react"; <Zap className="h-5 w-5 text-primary" />
Images & Assets
Placez vos images statiques dans public/images/. Utilisez le composant next/image pour bénéficier de l'optimisation automatique (formats WebP/AVIF, lazy loading, dimensions réservées) :
import Image from "next/image"; <Image src="/images/mon-image.jpg" alt="Description de l'image" width={1200} height={630} priority {/* pour les images above the fold */} />
priority sur les images visibles immédiatement (logo, hero). Pour les images en dessous de la fold, omettez-le pour activer le lazy loading.Modifier le layout
Le layout global (header, footer, providers) est dans src/app/layout.tsx. Pour créer un layout spécifique à une section (ex. : documentation, espace membre), ajoutez un fichier layout.tsx dans le dossier de la section :
export default function MaSectionLayout({ children, }: { children: React.ReactNode }) { return ( <div className="flex gap-8"> <aside>Sidebar</aside> <main>{children}</main> </div> ); }