Personnalisation

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.

src/data/home.ts
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 :

src/app/ma-page/page.tsx
import { type Metadata } from "next";

export const metadata: Metadata = {
  title: "Ma Page",
  description: "Description SEO",
};

export default function MaPage() {
  return <main>...</main>;
}
Pensez à ajouter la nouvelle route dans 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 :

ComposantRôle
HeroSectionSection principale avec titre, sous-titre et CTA
FeaturesSectionGrille de fonctionnalités avec icônes
ServicesSectionCards de services avec liens
StatsSectionCompteurs animés (chiffres clés)
FaqSectionAccordéon de questions-réponses
CtaSectionBloc d'appel à l'action
ContactFormSectionFormulaire 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 :

src/components/blocks/mon-bloc.tsx
"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>
  );
}
Tout composant qui utilise Framer Motion, des hooks React (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 :

Variantes Framer Motion
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 :

Exemple
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.

Ne modifiez pas les fichiers dans src/components/ui/directement. Pour ajouter un nouveau composant shadcn, utilisez la CLI : npx shadcn@latest add dialog

Composants disponibles utiles

ComposantUsage typique
ButtonCTA, actions primaires et secondaires
Card / CardContentMise en avant de contenu, pricing, features
BadgeLabels, tags de catégories
Input / TextareaChamps de formulaire
AccordionFAQ, questions-réponses
Dialog / SheetModals et panneaux latéraux
SeparatorSéparateurs visuels
TabsContenu tabulé
TooltipInfo-bulles sur icônes

Icônes

Toutes les icônes proviennent de lucide-react. Parcourez le catalogue sur lucide.dev et importez-les directement :

Exemple
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) :

Exemple
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 */}
/>
Ajoutez 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 :

src/app/ma-section/layout.tsx
export default function MaSectionLayout({
  children,
}: { children: React.ReactNode }) {
  return (
    <div className="flex gap-8">
      <aside>Sidebar</aside>
      <main>{children}</main>
    </div>
  );
}