All checks were successful
Deploy production / build-deploy (push) Successful in 24s
Stack & icônes - grille unique enrichie (+24 technos) : React, TypeORM, Apache, Gitea, GitLab, GitHub Actions, Vite, pnpm, Socket.io, Chart.js, Discord.js, Prisma, Sequelize, IntelliJ IDEA, VS Code, Windows, HubSpot, HTML5/CSS3… - nouvelles catégories « Bases de données & ORM » et « Tests & Qualité » - 21 icônes simple-icons + tracés monochromes custom VS Code, Windows, Playwright ; support du fill-rule dans TechIcon - mise en page masonry (CSS columns) + titres et puces centrés Contenu & UI - Hero : retrait du badge « ouvert aux opportunités » - About / Contact : reformulation orientée projets - ProjectCard : statut « en développement » - typographie : tirets cadratins → virgules (titres, OG, aria-labels) - ajout du visuel public/images/logo/couverture.png
100 lines
3.7 KiB
Plaintext
100 lines
3.7 KiB
Plaintext
---
|
|
import "../styles/global.css";
|
|
import { social, profile } from "../data/content";
|
|
|
|
interface Props {
|
|
title?: string;
|
|
description?: string;
|
|
}
|
|
const {
|
|
title = "Johan Leroy, Développeur fullstack",
|
|
description = "Johan Leroy, développeur fullstack basé à Nantes. TypeScript, NestJS, Nuxt/Vue, Next.js. Conception d'applications web performantes, du front à la mise en production.",
|
|
} = Astro.props;
|
|
|
|
const siteUrl = Astro.site?.href ?? "https://johanleroy.fr/";
|
|
const canonical = new URL(Astro.url.pathname, siteUrl).href;
|
|
const ogImage = new URL("/images/og.png", siteUrl).href;
|
|
const ogDescription =
|
|
"Développeur fullstack à Nantes. Applications web performantes, propres et scalables.";
|
|
|
|
// Données structurées (schema.org Person) — aide les moteurs à comprendre la page.
|
|
const jsonLd = {
|
|
"@context": "https://schema.org",
|
|
"@type": "Person",
|
|
name: profile.name,
|
|
jobTitle: profile.role,
|
|
url: siteUrl,
|
|
image: ogImage,
|
|
email: social.email,
|
|
address: { "@type": "PostalAddress", addressLocality: "Nantes", addressCountry: "FR" },
|
|
sameAs: [social.github, social.linkedin, social.gitea, social.soundcloud],
|
|
knowsAbout: ["TypeScript", "NestJS", "Nuxt", "Next.js", "Vue.js", "Astro", "Docker", "AWS", "Terraform"],
|
|
};
|
|
---
|
|
<!doctype html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title>{title}</title>
|
|
<meta name="description" content={description} />
|
|
<meta name="author" content="Johan Leroy" />
|
|
<link rel="canonical" href={canonical} />
|
|
|
|
{/* Open Graph */}
|
|
<meta property="og:type" content="website" />
|
|
<meta property="og:site_name" content="Johan Leroy" />
|
|
<meta property="og:locale" content="fr_FR" />
|
|
<meta property="og:title" content={title} />
|
|
<meta property="og:description" content={ogDescription} />
|
|
<meta property="og:url" content={canonical} />
|
|
<meta property="og:image" content={ogImage} />
|
|
<meta property="og:image:width" content="1200" />
|
|
<meta property="og:image:height" content="630" />
|
|
<meta property="og:image:alt" content="Johan Leroy, Développeur fullstack" />
|
|
|
|
{/* Twitter */}
|
|
<meta name="twitter:card" content="summary_large_image" />
|
|
<meta name="twitter:title" content={title} />
|
|
<meta name="twitter:description" content={ogDescription} />
|
|
<meta name="twitter:image" content={ogImage} />
|
|
|
|
<meta name="theme-color" content="#09090b" />
|
|
<link rel="icon" type="image/png" sizes="32x32" href="/images/logo/favicon-32x32.png" />
|
|
<link rel="icon" type="image/png" sizes="16x16" href="/images/logo/favicon-16x16.png" />
|
|
<link rel="icon" href="/favicon.ico" sizes="any" />
|
|
<link rel="apple-touch-icon" href="/images/logo/apple-touch-icon.png" />
|
|
<link rel="sitemap" type="application/xml" href="/sitemap.xml" />
|
|
|
|
<script type="application/ld+json" is:inline set:html={JSON.stringify(jsonLd)} />
|
|
|
|
{/* Thème posé AVANT le premier paint : aucun flash au chargement. */}
|
|
<script is:inline>
|
|
(function () {
|
|
try {
|
|
var saved = localStorage.getItem("jl-theme");
|
|
var light = saved
|
|
? saved === "light"
|
|
: window.matchMedia("(prefers-color-scheme: light)").matches;
|
|
if (light) {
|
|
document.documentElement.setAttribute("data-theme", "light");
|
|
var m = document.querySelector('meta[name="theme-color"]');
|
|
if (m) m.setAttribute("content", "#fafafa");
|
|
}
|
|
} catch (e) {}
|
|
})();
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<div class="halo" aria-hidden="true"></div>
|
|
<div class="grain" aria-hidden="true"></div>
|
|
<div class="page">
|
|
<slot />
|
|
</div>
|
|
|
|
<script>
|
|
import "../scripts/app";
|
|
</script>
|
|
</body>
|
|
</html>
|