Retour au catalogue
Developer Docs Preview
Preview de documentation avec sidebar de navigation, barre de recherche, contenu principal et exemples de code. Style developer portal.
developercomplex Both Responsive a11y
minimalcorporatesaasuniversalsplit
Theme
"use client";
import { motion } from "framer-motion";
import { Book, ChevronRight, Search, ExternalLink } from "lucide-react";
import { useState } from "react";
interface NavItem {
label: string;
items: string[];
}
interface DocSection {
id: string;
title: string;
content: string;
codeExample?: { text: string; color?: string }[];
}
interface DeveloperDocsPreviewProps {
title?: string;
description?: string;
navItems?: NavItem[];
sections?: DocSection[];
ctaLabel?: string;
ctaUrl?: string;
}
const EASE = [0.16, 1, 0.3, 1] as const;
const TOKEN_COLORS: Record<string, string> = {
keyword: "#ce93d8",
string: "#a5d6a7",
comment: "var(--color-foreground-light)",
accent: "var(--color-accent)",
muted: "var(--color-foreground-muted)",
};
export default function DeveloperDocsPreview({
title = "Documentation",
description = "Tout ce qu'il faut pour integrer notre plateforme.",
navItems = [],
sections = [],
ctaLabel = "Ouvrir la documentation",
ctaUrl = "#docs",
}: DeveloperDocsPreviewProps) {
const [activeSection, setActiveSection] = useState(sections[0]?.id ?? "");
const currentSection = sections.find((s) => s.id === activeSection) ?? sections[0];
return (
<section
style={{
padding: "var(--section-padding-y-lg) 0",
background: "var(--color-background)",
}}
>
<div
style={{
maxWidth: "var(--container-max-width)",
margin: "0 auto",
padding: "0 var(--container-padding-x)",
}}
>
{/* Header */}
<motion.div
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, ease: EASE }}
style={{ textAlign: "center", marginBottom: "2.5rem" }}
>
<div
style={{
display: "inline-flex",
alignItems: "center",
gap: "8px",
padding: "0.375rem 1rem",
borderRadius: "var(--radius-full)",
border: "1px solid var(--color-accent-border)",
background: "var(--color-accent-subtle)",
fontSize: "0.8125rem",
color: "var(--color-foreground-muted)",
marginBottom: "1.25rem",
}}
>
<Book style={{ width: 14, height: 14, color: "var(--color-accent)" }} />
{title}
</div>
<h2
style={{
fontFamily: "var(--font-sans)",
fontSize: "clamp(1.75rem, 3vw, 2.5rem)",
fontWeight: 700,
color: "var(--color-foreground)",
marginBottom: "0.75rem",
letterSpacing: "-0.02em",
}}
>
{description}
</h2>
</motion.div>
{/* Docs preview frame */}
<motion.div
initial={{ opacity: 0, y: 24 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.1, ease: EASE }}
style={{
maxWidth: "960px",
margin: "0 auto",
borderRadius: "var(--radius-lg)",
border: "1px solid var(--color-border)",
background: "var(--color-background-alt)",
overflow: "hidden",
display: "grid",
gridTemplateColumns: "220px 1fr",
minHeight: "420px",
}}
>
{/* Sidebar */}
<div
style={{
borderRight: "1px solid var(--color-border)",
padding: "1.25rem 0",
background: "var(--color-background-card)",
overflow: "auto",
}}
>
{/* Search */}
<div
style={{
display: "flex",
alignItems: "center",
gap: "8px",
margin: "0 1rem 1rem",
padding: "0.5rem 0.75rem",
borderRadius: "var(--radius-md)",
border: "1px solid var(--color-border)",
fontSize: "0.75rem",
color: "var(--color-foreground-light)",
}}
>
<Search style={{ width: 14, height: 14 }} />
Rechercher...
</div>
{/* Nav groups */}
{navItems.map((group) => (
<div key={group.label} style={{ marginBottom: "1rem" }}>
<div
style={{
padding: "0.375rem 1.25rem",
fontSize: "0.6875rem",
fontWeight: 600,
textTransform: "uppercase",
letterSpacing: "0.08em",
color: "var(--color-foreground-light)",
}}
>
{group.label}
</div>
{group.items.map((item) => {
const sectionMatch = sections.find(
(s) => s.title.toLowerCase() === item.toLowerCase()
);
const isActive = sectionMatch?.id === activeSection;
return (
<button
key={item}
onClick={() => sectionMatch && setActiveSection(sectionMatch.id)}
style={{
display: "flex",
alignItems: "center",
gap: "6px",
width: "100%",
padding: "0.375rem 1.25rem",
fontSize: "0.8125rem",
fontWeight: isActive ? 600 : 400,
color: isActive
? "var(--color-accent)"
: "var(--color-foreground-muted)",
background: isActive
? "var(--color-accent-subtle)"
: "transparent",
border: "none",
cursor: "pointer",
textAlign: "left",
borderLeft: isActive
? "2px solid var(--color-accent)"
: "2px solid transparent",
}}
>
<ChevronRight style={{ width: 12, height: 12, opacity: isActive ? 1 : 0.3 }} />
{item}
</button>
);
})}
</div>
))}
</div>
{/* Content */}
<div style={{ padding: "2rem 2.5rem", overflow: "auto" }}>
{currentSection && (
<div>
<h3
style={{
fontFamily: "var(--font-sans)",
fontSize: "1.5rem",
fontWeight: 700,
color: "var(--color-foreground)",
marginBottom: "1rem",
letterSpacing: "-0.01em",
}}
>
{currentSection.title}
</h3>
<p
style={{
fontSize: "0.9375rem",
lineHeight: 1.7,
color: "var(--color-foreground-muted)",
marginBottom: "1.5rem",
}}
>
{currentSection.content}
</p>
{currentSection.codeExample && currentSection.codeExample.length > 0 && (
<div
style={{
padding: "1rem 1.25rem",
borderRadius: "var(--radius-md)",
border: "1px solid var(--color-border)",
background: "var(--color-background)",
fontFamily: "var(--font-mono, monospace)",
fontSize: "0.8125rem",
lineHeight: 1.85,
}}
>
{currentSection.codeExample.map((line, i) => (
<div key={i}>
<span
style={{
color: line.color
? TOKEN_COLORS[line.color] ?? "var(--color-foreground-muted)"
: "var(--color-foreground-muted)",
}}
>
{line.text || "\u00A0"}
</span>
</div>
))}
</div>
)}
</div>
)}
</div>
</motion.div>
{/* CTA */}
<motion.div
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.25, ease: EASE }}
style={{ textAlign: "center", marginTop: "2rem" }}
>
<a
href={ctaUrl}
style={{
display: "inline-flex",
alignItems: "center",
gap: "8px",
padding: "0.75rem 1.75rem",
borderRadius: "var(--radius-full)",
background: "var(--color-accent)",
color: "var(--color-foreground)",
fontWeight: 600,
fontSize: "0.9375rem",
textDecoration: "none",
transition: "background var(--duration-normal) var(--ease-out)",
}}
>
{ctaLabel}
<ExternalLink style={{ width: 16, height: 16 }} />
</a>
</motion.div>
</div>
</section>
);
}