Retour au catalogue
Code Block API
Reference API avec endpoint, badge de methode HTTP (GET/POST), liste de parametres et exemple de reponse JSON formate.
code-blockmedium Both Responsive a11y
minimalcorporatesaasuniversalcentered
Theme
"use client";
import { motion } from "framer-motion";
import { Copy, Check, ChevronRight } from "lucide-react";
import { useState } from "react";
interface ApiParam {
name: string;
type: string;
required: boolean;
description: string;
}
interface CodeBlockApiProps {
title?: string;
description?: string;
method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
endpoint?: string;
params?: ApiParam[];
responseLines?: { text: string; indent?: number; color?: string }[];
}
const EASE = [0.16, 1, 0.3, 1] as const;
const METHOD_COLORS: Record<string, string> = {
GET: "#a5d6a7",
POST: "#90caf9",
PUT: "#ffe082",
PATCH: "#ce93d8",
DELETE: "#ef9a9a",
};
const TOKEN_COLORS: Record<string, string> = {
key: "#90caf9",
string: "#a5d6a7",
number: "#ffe082",
muted: "var(--color-foreground-muted)",
accent: "var(--color-accent)",
};
export default function CodeBlockApi({
title = "Reference API",
description = "Documentation complete de nos endpoints.",
method = "GET",
endpoint = "/api/v1/users",
params = [],
responseLines = [],
}: CodeBlockApiProps) {
const [copied, setCopied] = useState(false);
function handleCopy() {
navigator.clipboard.writeText(
`curl -X ${method} https://api.acme.dev${endpoint}`
);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}
return (
<section
style={{
padding: "var(--section-padding-y) 0",
background: "var(--color-background)",
}}
>
<div
style={{
maxWidth: "var(--container-max-width)",
margin: "0 auto",
padding: "0 var(--container-padding-x)",
}}
>
<motion.div
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, ease: EASE }}
style={{ textAlign: "center", marginBottom: "2.5rem" }}
>
<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",
}}
>
{title}
</h2>
<p
style={{
fontSize: "1.0625rem",
color: "var(--color-foreground-muted)",
maxWidth: "520px",
margin: "0 auto",
lineHeight: 1.6,
}}
>
{description}
</p>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 24 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.1, ease: EASE }}
style={{
maxWidth: "780px",
margin: "0 auto",
borderRadius: "var(--radius-lg)",
border: "1px solid var(--color-border)",
background: "var(--color-background-alt)",
overflow: "hidden",
}}
>
{/* Endpoint bar */}
<div
style={{
display: "flex",
alignItems: "center",
gap: "12px",
padding: "0.875rem 1.25rem",
borderBottom: "1px solid var(--color-border)",
background: "var(--color-background-card)",
}}
>
<span
style={{
display: "inline-block",
padding: "3px 10px",
borderRadius: "var(--radius-sm)",
fontSize: "0.6875rem",
fontWeight: 700,
fontFamily: "var(--font-mono, monospace)",
color: "#111",
background: METHOD_COLORS[method] ?? "var(--color-accent)",
letterSpacing: "0.04em",
}}
>
{method}
</span>
<code
style={{
flex: 1,
fontFamily: "var(--font-mono, monospace)",
fontSize: "0.875rem",
color: "var(--color-foreground)",
}}
>
{endpoint}
</code>
<button
onClick={handleCopy}
style={{
display: "inline-flex",
alignItems: "center",
gap: "4px",
padding: "4px 10px",
borderRadius: "var(--radius-sm)",
border: "1px solid var(--color-border)",
background: "transparent",
color: "var(--color-foreground-muted)",
fontSize: "0.75rem",
cursor: "pointer",
}}
>
{copied ? (
<Check style={{ width: 14, height: 14, color: "var(--color-accent)" }} />
) : (
<Copy style={{ width: 14, height: 14 }} />
)}
</button>
</div>
{/* Parameters */}
{params.length > 0 && (
<div
style={{
padding: "1rem 1.25rem",
borderBottom: "1px solid var(--color-border)",
}}
>
<div
style={{
fontSize: "0.6875rem",
fontWeight: 600,
textTransform: "uppercase",
letterSpacing: "0.08em",
color: "var(--color-foreground-light)",
marginBottom: "0.75rem",
}}
>
Parametres
</div>
<div style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
{params.map((p) => (
<div
key={p.name}
style={{
display: "flex",
alignItems: "baseline",
gap: "10px",
fontSize: "0.8125rem",
}}
>
<ChevronRight
style={{
width: 12,
height: 12,
color: "var(--color-accent)",
flexShrink: 0,
marginTop: "3px",
}}
/>
<code
style={{
fontFamily: "var(--font-mono, monospace)",
fontWeight: 600,
color: "var(--color-foreground)",
}}
>
{p.name}
</code>
<span
style={{
fontSize: "0.6875rem",
padding: "1px 6px",
borderRadius: "var(--radius-sm)",
background: "var(--color-accent-subtle)",
color: "var(--color-foreground-muted)",
fontFamily: "var(--font-mono, monospace)",
}}
>
{p.type}
</span>
{p.required && (
<span
style={{
fontSize: "0.625rem",
fontWeight: 600,
textTransform: "uppercase",
color: "#ef9a9a",
letterSpacing: "0.05em",
}}
>
requis
</span>
)}
<span
style={{
color: "var(--color-foreground-muted)",
fontSize: "0.8125rem",
}}
>
{p.description}
</span>
</div>
))}
</div>
</div>
)}
{/* Response */}
{responseLines.length > 0 && (
<div style={{ padding: "1rem 1.25rem" }}>
<div
style={{
fontSize: "0.6875rem",
fontWeight: 600,
textTransform: "uppercase",
letterSpacing: "0.08em",
color: "var(--color-foreground-light)",
marginBottom: "0.75rem",
}}
>
Exemple de reponse
</div>
<div
style={{
fontFamily: "var(--font-mono, monospace)",
fontSize: "0.8125rem",
lineHeight: 1.85,
}}
>
{responseLines.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>
</div>
)}
</motion.div>
</div>
</section>
);
}