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>
  );
}

Avis

Developer Docs Preview — React Developer Section — Incubator