Créer une landing page SEO avec Claude Code en une session

J'avais un SIRET, une idée de service, et zéro landing page. 4 heures plus tard, j'avais une page en production avec SEO, formulaire, chatbox, et des cercles SVG animés. Voici exactement comment ça s'est passé.

Pas de Webflow. Pas de WordPress. Pas de template à 97€. Un terminal, Claude Code, et une idée assez claire de ce que je voulais. Le reste s'est construit en itérant à voix haute dans une conversation.

Le besoin

Le service : automatisation de tâches répétitives pour entreprises et particuliers. Scripts Python/Go sur mesure, intégrations API, scraping structuré, consolidation Excel, chaînes email automatisées. La cible : les TPE/PME qui perdent des heures sur des tâches que du code résoudrait en 5 minutes.

Le besoin côté marketing était simple : une page qui explique ce que je fais, qui inspire confiance, et qui convertit. Avec un formulaire qui envoie un vrai email. Sans abonnement SaaS à 49€/mois pour ça.

Contraintes techniques :

  • Sous-dossier du site existant (/automatisation/), pas un nouveau domaine
  • PHP pur, zéro framework frontend
  • Formulaire qui envoie des emails (SMTP, déjà configuré pour le blog)
  • Anti-spam sérieux — pas juste un if (empty($_POST['email']))

La stratégie domaine et SEO

Premier réflexe : créer un nouveau domaine. automatisation-freelance.fr est libre. Tentant. Mauvaise idée.

web-developpeur.com existe depuis plusieurs années, a des backlinks, un historique dans l'index Google. Un sous-dossier hérite de tout ça. Un nouveau domaine repart de zéro — et mettre 6 mois à remonter dans les SERPs alors qu'on essaie de décrocher ses premiers clients, c'est la mauvaise stratégie.

J'ai demandé à Claude d'analyser la concurrence sur les requêtes cibles. Ingecode.fr, des agences no-code positionnées sur "automatisation Zapier", des consultants Malt aux profils généralistes. Le créneau libre : développeur freelance spécialisé automatisation technique, avec du code réel et des exemples concrets.

Mots-clés retenus :

  • Principal : "automatisation tâches entreprise"
  • Secondaires : "développeur automatisation freelance", "automatiser Excel", "script python automatisation"
  • Longue traîne : "automatiser rapport Excel mensuel", "script scraping données web"

Pour les rich snippets, deux schemas Schema.org : Service pour la page principale, et FAQPage pour la section FAQ — un signal fort à Google pour potentiellement décrocher un encart en position 0.

L'architecture en 4 fichiers

Structure finale du dossier :

automatisation/
├── index.php           # La landing page
├── contact-handler.php # Traitement formulaire (PHPMailer, CSRF, honeypot, rate limiting)
├── merci.php           # Page de confirmation post-soumission
└── assets/
    └── auto.css        # CSS standalone — pas Bootstrap 3

L'absence de Bootstrap 3 est intentionnelle. Le CV principal l'utilise, et c'est bien pour un CV avec sidebar droite et grille. Mais Bootstrap 3 c'est 2013. Pour une landing page de service en 2026, du CSS custom avec clamp(), text-wrap: balance et Grid natif, c'est plus propre, plus rapide à charger, et infiniment plus facile à maintenir que de surcharger les classes Bootstrap.

Le fichier contact-handler.php suit le même pattern PRG (Post-Redirect-Get) que le système de commentaires du blog. Ça évite le double envoi sur F5, et c'est une convention HTTP correcte.

Le design itératif

V1 : fonctionnel, pas beau

La première version avait la structure correcte — hero, cas d'usage, tarifs, FAQ, formulaire — mais les boutons CTA n'avaient pas de style. Raison classique : quand on délègue la génération du HTML et du CSS à deux agents en parallèle (ou dans deux tours de conversation séparés), les noms de classes peuvent diverger silencieusement. Le HTML utilisait .btn-cta, le CSS définissait .cta-button. Visuellement : bouton blanc sur fond blanc.

Ce type d'erreur se détecte immédiatement en ouvrant la page dans un navigateur. Il se corrige en 30 secondes avec Claude Code : "les boutons CTA sont invisibles, aligne les classes". C'est là que l'itération rapide fait la différence — pas besoin de creuser dans le CSS manuellement.

V2 : refonte visuelle

Icônes SVG expressives 64px pour les cas d'usage. Typographie responsive avec clamp(). text-wrap: balance sur les titres pour éviter les mots orphelins en bout de ligne.

Extrait de la feuille de style :

:root {
    --color-primary:   #27ae60;
    --color-dark:      #1a1a2e;
    --color-surface:   #f8fffe;
    --radius:          12px;
    --transition:      0.25s ease;
}

h1, h2 {
    text-wrap: balance;
}

.hero-title {
    font-size: clamp(2rem, 5vw, 3.5rem);
    line-height: 1.15;
    letter-spacing: -0.02em;
}

/* Stagger animations sur les cartes de cas d'usage */
.use-case-card:nth-child(1) { transition-delay: 0s; }
.use-case-card:nth-child(2) { transition-delay: 0.08s; }
.use-case-card:nth-child(3) { transition-delay: 0.16s; }
.use-case-card:nth-child(4) { transition-delay: 0.24s; }
.use-case-card:nth-child(5) { transition-delay: 0.32s; }
.use-case-card:nth-child(6) { transition-delay: 0.40s; }

V3 : les détails qui convertissent

Ajout d'un dashboard sombre avec des cercles SVG progressifs pour illustrer les gains clients (temps économisé, taux d'erreur, etc.). Chatbox flottante. Compteur de missions. Garantie "satisfait ou remboursé". Section FAQ avec prix d'appel.

Le formulaire et la chatbox

Le formulaire principal collecte nom, email, entreprise, type de besoin (select), description et budget. La chatbox est un second formulaire minimaliste, flottant en bas à droite, qui poste vers le même contact-handler.php avec type_besoin=chatbox. Un seul handler, deux points d'entrée.

Voici le handler simplifié :

<?php
declare(strict_types=1);

if (session_status() === PHP_SESSION_NONE) session_start();
require_once __DIR__ . '/../config.php';

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    header('Location: /automatisation/');
    exit;
}

$name        = trim($_POST['name']        ?? '');
$email       = trim($_POST['email']       ?? '');
$description = trim($_POST['description'] ?? '');
$website     = $_POST['website'] ?? ''; // honeypot

// Honeypot : les bots remplissent ce champ, pas les humains
if ($website !== '') {
    header('Location: /automatisation/merci');
    exit; // rejet silencieux
}

// CSRF
$csrfToken = $_POST['csrf_token'] ?? '';
if (!hash_equals($_SESSION['csrf_token'] ?? '', $csrfToken)) {
    header('Location: /automatisation/?error=' . urlencode('Session expirée.') . '#contact');
    exit;
}

// Validation
if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
    header('Location: /automatisation/?error=' . urlencode('Email invalide.') . '#contact');
    exit;
}

if (mb_strlen($description) < 20 || mb_strlen($description) > 2000) {
    header('Location: /automatisation/?error=' . urlencode('Description trop courte ou trop longue.') . '#contact');
    exit;
}

// Rate limiting par préfixe IP (hashé, RGPD-friendly)
$ip       = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
$octets   = explode('.', $ip, 3);
$ipPrefix = ($octets[0] ?? '0') . '.' . ($octets[1] ?? '0');
$ipHash   = substr(hash('sha256', $ipPrefix), 0, 16);

$rateLimitFile = __DIR__ . '/rate-limit.json';
$limits        = file_exists($rateLimitFile) ? json_decode(file_get_contents($rateLimitFile), true) : [];
$now           = time();
$windowStart   = $now - 3600;

$limits[$ipHash] = array_filter($limits[$ipHash] ?? [], fn($ts) => $ts > $windowStart);

if (count($limits[$ipHash]) >= 3) {
    header('Location: /automatisation/?error=' . urlencode('Trop de messages, réessayez dans une heure.') . '#contact');
    exit;
}

$limits[$ipHash][] = $now;
file_put_contents($rateLimitFile, json_encode($limits), LOCK_EX);

// Envoi email (PHPMailer, déjà configuré pour le blog)
// ... PHPMailer setup ...

header('Location: /automatisation/merci');
exit;

PHPMailer était déjà configuré pour les notifications de commentaires du blog. Réutiliser la même configuration SMTP, c'est 10 lignes à copier et adapter. Pas besoin de Mailgun, pas besoin de SendGrid, pas besoin d'un abonnement supplémentaire.

Les détails qui font la différence

SVG progressifs dans les cas clients

Le dashboard sombre utilise des cercles SVG avec stroke-dasharray et stroke-dashoffset pour afficher des métriques clients de manière visuelle. L'animation se déclenche à l'entrée dans le viewport via IntersectionObserver :

.progress-ring__circle {
    stroke-dasharray: 251.2; /* 2π × r, r=40 */
    stroke-dashoffset: 251.2; /* commence à 0% */
    transition: stroke-dashoffset 1.2s cubic-bezier(0.4, 0, 0.2, 1);
    transform: rotate(-90deg);
    transform-origin: 50% 50%;
}

/* JS set --offset via style.strokeDashoffset = 251.2 × (1 - percent/100) */

FAQ en HTML natif

La section FAQ utilise <details>/<summary> natifs — zéro JavaScript, accessible par défaut, et compatible avec le Schema.org FAQPage injecté en JSON-LD dans le <head>. Les accordéons custom JavaScript pour une FAQ, c'est du sur-ingénierie.

Prix d'appel dans la FAQ

Une question dans la FAQ : "Combien ça coûte ?". Réponse : "à partir de 150€ pour un script ponctuel". Ce prix est intentionnellement bas pour débloquer les visiteurs hésitants. La vraie valeur se discute en entretien. Mais sans ancre tarifaire visible, beaucoup de prospects ne contactent pas du tout — ils supposent que "développeur freelance" rime avec "budget prohibitif".

Schema.org complet

$schema = [
    '@context' => 'https://schema.org',
    '@graph'   => [
        [
            '@type'       => 'Service',
            'name'        => 'Automatisation de tâches — Odilon Hugonnot',
            'provider'    => [
                '@type' => 'Person',
                'name'  => 'Odilon Hugonnot',
                'url'   => 'https://www.web-developpeur.com',
            ],
            'serviceType' => 'Développement de scripts d\'automatisation',
            'areaServed'  => 'FR',
        ],
        [
            '@type'      => 'FAQPage',
            'mainEntity' => $faqItems, // tableau de Question/Answer
        ],
    ],
];
echo '<script type="application/ld+json">' . json_encode($schema) . '</script>';

Conclusion

En une session de travail — pas une nuit blanche, pas une semaine de sprint — la page était en production : SEO structuré, formulaire anti-spam, chatbox, design propre, responsive. Le tout sans dépendance externe payante.

Le vrai game changer de Claude Code dans ce type de projet n'est pas la génération de code. C'est la vitesse d'itération. "Les boutons sont invisibles" → fix en 30 secondes. "La FAQ manque d'une question sur les délais" → ajout immédiat. "Le hero title se casse mal sur mobile" → clamp() et text-wrap ajustés en une instruction. Le cycle conception → implémentation → test visuel se réduit à la vitesse de frappe.

Ce que Claude Code ne résout pas : la distribution. La page est là, le SEO est en place, mais Google met du temps à indexer et ranker. En attendant, le réseau LinkedIn, les articles de blog ciblés sur les cas d'usage techniques (automatiser Excel, scripts Python, etc.), et la preuve sociale (premiers clients, premiers témoignages) font le travail. La landing page est un prérequis nécessaire, pas une solution suffisante.

Le service : web-developpeur.com/automatisation.

Commentaires (0)