Retour au catalogue
Developer API Showcase
Showcase API avec liste d'endpoints, badges de features et code snippets switchables. Layout split deux colonnes.
developermedium Both Responsive a11y
minimalcorporatesaasuniversalsplit
Theme
"use client";
import { motion } from "framer-motion";
import { Zap, ArrowRight, Lock, Globe } from "lucide-react";
import { useState } from "react";
interface EndpointInfo {
method: "GET" | "POST" | "PUT" | "DELETE";
path: string;
description: string;
}
interface CodeSnippet {
label: string;
lines: { text: string; indent?: number; color?: string }[];
}
interface DeveloperApiShowcaseProps {
title?: string;
titleAccent?: string;
description?: string;
endpoints?: EndpointInfo[];
snippets?: CodeSnippet[];
features?: { icon: string; label: string; detail: string }[];
}
const EASE = [0.16, 1, 0.3, 1] as const;
const METHOD_COLORS: Record<string, string> = {
GET: "#a5d6a7",
POST: "#90caf9",
PUT: "#ffe082",
DELETE: "#ef9a9a",
};
const TOKEN_COLORS: Record<string, string> = {
keyword: "#ce93d8",
string: "#a5d6a7",
comment: "var(--color-foreground-light)",
accent: "var(--color-accent)",
muted: "var(--color-foreground-muted)",
attr: "#90caf9",
};
const ICON_MAP: Record<string, React.ComponentType<{ style?: React.CSSProperties }>> = {
zap: Zap,
lock: Lock,
globe: Globe,
};
export default function DeveloperApiShowcase({
title = "Une API pensee pour les",
titleAccent = "developpeurs",
description = "Endpoints RESTful, authentification OAuth2, reponses JSON normalisees.",
endpoints = [],
snippets = [],
features = [],
}: DeveloperApiShowcaseProps) {
const [activeSnippet, setActiveSnippet] = useState(0);
const currentSnippet = snippets[activeSnippet];
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: "3rem" }}
>
<h2
style={{
fontFamily: "var(--font-sans)",
fontSize: "clamp(2rem, 4vw, 3rem)",
fontWeight: 700,
color: "var(--color-foreground)",
marginBottom: "1rem",
letterSpacing: "-0.02em",
lineHeight: 1.15,
}}
>
{title}{" "}
<span style={{ color: "var(--color-accent)" }}>{titleAccent}</span>
</h2>
<p
style={{
fontSize: "1.0625rem",
color: "var(--color-foreground-muted)",
maxWidth: "560px",
margin: "0 auto",
lineHeight: 1.6,
}}
>
{description}
</p>
</motion.div>
{/* Two-column layout */}
<div
style={{
display: "grid",
gridTemplateColumns: "1fr 1fr",
gap: "2.5rem",
alignItems: "start",
}}
>
{/* Left — Endpoints + features */}
<motion.div
initial={{ opacity: 0, x: -16 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6, delay: 0.1, ease: EASE }}
>
{/* Endpoints list */}
<div style={{ marginBottom: "2rem" }}>
<div
style={{
fontSize: "0.75rem",
fontWeight: 600,
textTransform: "uppercase",
letterSpacing: "0.08em",
color: "var(--color-foreground-light)",
marginBottom: "1rem",
}}
>
Endpoints
</div>
<div style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
{endpoints.map((ep, i) => (
<div
key={i}
style={{
display: "flex",
alignItems: "center",
gap: "12px",
padding: "0.625rem 1rem",
borderRadius: "var(--radius-md)",
border: "1px solid var(--color-border)",
background: "var(--color-background-card)",
}}
>
<span
style={{
padding: "2px 8px",
borderRadius: "var(--radius-sm)",
fontSize: "0.625rem",
fontWeight: 700,
fontFamily: "var(--font-mono, monospace)",
background: METHOD_COLORS[ep.method] ?? "var(--color-accent)",
color: "#111",
letterSpacing: "0.04em",
}}
>
{ep.method}
</span>
<code
style={{
fontFamily: "var(--font-mono, monospace)",
fontSize: "0.8125rem",
color: "var(--color-foreground)",
fontWeight: 500,
}}
>
{ep.path}
</code>
<span
style={{
marginLeft: "auto",
fontSize: "0.75rem",
color: "var(--color-foreground-muted)",
}}
>
{ep.description}
</span>
</div>
))}
</div>
</div>
{/* Feature badges */}
<div style={{ display: "flex", flexWrap: "wrap", gap: "10px" }}>
{features.map((f, i) => {
const Icon = ICON_MAP[f.icon] ?? Zap;
return (
<div
key={i}
style={{
display: "inline-flex",
alignItems: "center",
gap: "8px",
padding: "0.5rem 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)",
}}
>
<Icon style={{ width: 14, height: 14, color: "var(--color-accent)" }} />
{f.label}
</div>
);
})}
</div>
</motion.div>
{/* Right — Code snippet */}
<motion.div
initial={{ opacity: 0, x: 16 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6, delay: 0.15, ease: EASE }}
style={{
borderRadius: "var(--radius-lg)",
border: "1px solid var(--color-border)",
background: "var(--color-background-alt)",
overflow: "hidden",
}}
>
{/* Snippet tabs */}
<div
style={{
display: "flex",
borderBottom: "1px solid var(--color-border)",
background: "var(--color-background-card)",
}}
>
{snippets.map((s, i) => (
<button
key={s.label}
onClick={() => setActiveSnippet(i)}
style={{
padding: "0.625rem 1.25rem",
fontSize: "0.8125rem",
fontFamily: "var(--font-mono, monospace)",
fontWeight: activeSnippet === i ? 600 : 400,
color:
activeSnippet === i
? "var(--color-accent)"
: "var(--color-foreground-muted)",
background: "transparent",
border: "none",
borderBottom:
activeSnippet === i
? "2px solid var(--color-accent)"
: "2px solid transparent",
cursor: "pointer",
}}
>
{s.label}
</button>
))}
</div>
{/* Code */}
<div
style={{
padding: "1.25rem 1.5rem",
fontFamily: "var(--font-mono, monospace)",
fontSize: "0.8125rem",
lineHeight: 1.85,
minHeight: "220px",
overflowX: "auto",
}}
>
{currentSnippet?.lines.map((line, i) => (
<div
key={i}
style={{ paddingLeft: `${(line.indent ?? 0) * 1.25}rem` }}
>
<span
style={{
color: line.color
? TOKEN_COLORS[line.color] ?? "var(--color-foreground-muted)"
: "var(--color-foreground-muted)",
}}
>
{line.text || "\u00A0"}
</span>
</div>
))}
</div>
{/* Footer link */}
<div
style={{
padding: "0.75rem 1.5rem",
borderTop: "1px solid var(--color-border)",
}}
>
<a
href="#docs"
style={{
display: "inline-flex",
alignItems: "center",
gap: "6px",
fontSize: "0.8125rem",
color: "var(--color-accent)",
textDecoration: "none",
fontWeight: 500,
}}
>
Voir la documentation complete
<ArrowRight style={{ width: 14, height: 14 }} />
</a>
</div>
</motion.div>
</div>
</div>
</section>
);
}