**Docs and Code Enhancements:** Add CodeBlock component with copy functionality and syntax highlighting. Introduce /docs page as the central hub for design system documentation. Update MarkdownContent to support improved heading styles, enhanced links, optimized images with Next.js Image, and upgraded table, blockquote, and list styling for better readability and usability.

This commit is contained in:
2025-11-02 13:47:26 +01:00
parent aff76e3a69
commit 92b7de352c
3 changed files with 434 additions and 43 deletions

View File

@@ -0,0 +1,303 @@
/**
* Documentation Hub
* Central hub for all design system documentation
* Access: /docs
*/
import Link from 'next/link';
import { BookOpen, Sparkles, Layout, Palette, Code2, FileCode, Accessibility, Lightbulb, Search } from 'lucide-react';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
interface DocItem {
title: string;
description: string;
href: string;
icon: React.ReactNode;
badge?: string;
}
const gettingStartedDocs: DocItem[] = [
{
title: 'Quick Start',
description: '5-minute crash course to get up and running with the design system',
href: '/docs/design-system/00-quick-start',
icon: <Sparkles className="h-5 w-5" />,
badge: 'Start Here',
},
{
title: 'README',
description: 'Complete overview and learning paths for the design system',
href: '/docs/design-system/README',
icon: <BookOpen className="h-5 w-5" />,
},
];
const coreConceptsDocs: DocItem[] = [
{
title: 'Foundations',
description: 'Colors (OKLCH), typography, spacing, and shadows',
href: '/docs/design-system/01-foundations',
icon: <Palette className="h-5 w-5" />,
},
{
title: 'Components',
description: 'shadcn/ui component library guide and usage patterns',
href: '/docs/design-system/02-components',
icon: <Code2 className="h-5 w-5" />,
},
{
title: 'Layouts',
description: 'Layout patterns with Grid vs Flex decision trees',
href: '/docs/design-system/03-layouts',
icon: <Layout className="h-5 w-5" />,
},
{
title: 'Spacing Philosophy',
description: 'Parent-controlled spacing strategy and best practices',
href: '/docs/design-system/04-spacing-philosophy',
icon: <FileCode className="h-5 w-5" />,
},
{
title: 'Component Creation',
description: 'When to create vs compose components',
href: '/docs/design-system/05-component-creation',
icon: <Code2 className="h-5 w-5" />,
},
{
title: 'Forms',
description: 'Form patterns with react-hook-form and Zod validation',
href: '/docs/design-system/06-forms',
icon: <FileCode className="h-5 w-5" />,
},
{
title: 'Accessibility',
description: 'WCAG AA compliance, keyboard navigation, and screen readers',
href: '/docs/design-system/07-accessibility',
icon: <Accessibility className="h-5 w-5" />,
},
];
const referencesDocs: DocItem[] = [
{
title: 'AI Guidelines',
description: 'Rules and best practices for AI code generation',
href: '/docs/design-system/08-ai-guidelines',
icon: <Lightbulb className="h-5 w-5" />,
badge: 'AI',
},
{
title: 'Quick Reference',
description: 'Cheat sheet for quick lookups and common patterns',
href: '/docs/design-system/99-reference',
icon: <Search className="h-5 w-5" />,
},
];
export default function DocsHub() {
return (
<div className="min-h-screen bg-background">
{/* Header */}
<header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<div className="container mx-auto flex h-16 items-center justify-between px-4">
<div className="flex items-center gap-3">
<BookOpen className="h-6 w-6 text-primary" />
<div>
<h1 className="text-lg font-semibold">FastNext Documentation</h1>
<p className="text-xs text-muted-foreground">Design System & Component Library</p>
</div>
</div>
<Link href="/dev">
<Button variant="outline" size="sm">
Interactive Demos
</Button>
</Link>
</div>
</header>
{/* Hero Section */}
<section className="border-b bg-gradient-to-b from-background to-muted/20 py-16">
<div className="container mx-auto px-4">
<div className="mx-auto max-w-3xl text-center">
<h2 className="text-4xl font-bold tracking-tight mb-4">
Design System Documentation
</h2>
<p className="text-lg text-muted-foreground mb-8">
Comprehensive guides, best practices, and references for building consistent,
accessible, and maintainable user interfaces with the FastNext design system.
</p>
<div className="flex flex-wrap gap-3 justify-center">
<Link href="/docs/design-system/00-quick-start">
<Button size="lg" className="gap-2">
<Sparkles className="h-4 w-4" />
Get Started
</Button>
</Link>
<Link href="/docs/design-system/README">
<Button variant="outline" size="lg" className="gap-2">
<BookOpen className="h-4 w-4" />
Full Documentation
</Button>
</Link>
<Link href="/dev">
<Button variant="outline" size="lg" className="gap-2">
<Code2 className="h-4 w-4" />
View Examples
</Button>
</Link>
</div>
</div>
</div>
</section>
{/* Main Content */}
<main className="container mx-auto px-4 py-12">
<div className="mx-auto max-w-6xl space-y-16">
{/* Getting Started Section */}
<section>
<div className="mb-6">
<h3 className="text-2xl font-semibold tracking-tight mb-2">Getting Started</h3>
<p className="text-muted-foreground">
New to the design system? Start here for a quick introduction.
</p>
</div>
<div className="grid gap-6 md:grid-cols-2">
{gettingStartedDocs.map((doc) => (
<Link key={doc.href} href={doc.href} className="group">
<Card className="h-full transition-all hover:shadow-lg hover:border-primary/50">
<CardHeader>
<div className="flex items-start justify-between">
<div className="flex items-center gap-3">
<div className="rounded-lg bg-primary/10 p-2.5 text-primary group-hover:bg-primary group-hover:text-primary-foreground transition-colors">
{doc.icon}
</div>
<div>
<CardTitle className="text-xl">{doc.title}</CardTitle>
{doc.badge && (
<Badge variant="secondary" className="mt-1">
{doc.badge}
</Badge>
)}
</div>
</div>
</div>
</CardHeader>
<CardContent>
<CardDescription className="text-base">
{doc.description}
</CardDescription>
</CardContent>
</Card>
</Link>
))}
</div>
</section>
{/* Core Concepts Section */}
<section>
<div className="mb-6">
<h3 className="text-2xl font-semibold tracking-tight mb-2">Core Concepts</h3>
<p className="text-muted-foreground">
Deep dive into the fundamental concepts and patterns of the design system.
</p>
</div>
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
{coreConceptsDocs.map((doc) => (
<Link key={doc.href} href={doc.href} className="group">
<Card className="h-full transition-all hover:shadow-lg hover:border-primary/50">
<CardHeader>
<div className="flex items-start gap-3">
<div className="rounded-lg bg-primary/10 p-2.5 text-primary group-hover:bg-primary group-hover:text-primary-foreground transition-colors">
{doc.icon}
</div>
<div className="flex-1">
<CardTitle className="text-lg">{doc.title}</CardTitle>
</div>
</div>
</CardHeader>
<CardContent>
<CardDescription>
{doc.description}
</CardDescription>
</CardContent>
</Card>
</Link>
))}
</div>
</section>
{/* References Section */}
<section>
<div className="mb-6">
<h3 className="text-2xl font-semibold tracking-tight mb-2">References</h3>
<p className="text-muted-foreground">
Quick references and specialized guides for specific use cases.
</p>
</div>
<div className="grid gap-6 md:grid-cols-2">
{referencesDocs.map((doc) => (
<Link key={doc.href} href={doc.href} className="group">
<Card className="h-full transition-all hover:shadow-lg hover:border-primary/50">
<CardHeader>
<div className="flex items-start justify-between">
<div className="flex items-center gap-3">
<div className="rounded-lg bg-primary/10 p-2.5 text-primary group-hover:bg-primary group-hover:text-primary-foreground transition-colors">
{doc.icon}
</div>
<div>
<CardTitle className="text-xl">{doc.title}</CardTitle>
{doc.badge && (
<Badge variant="secondary" className="mt-1">
{doc.badge}
</Badge>
)}
</div>
</div>
</div>
</CardHeader>
<CardContent>
<CardDescription className="text-base">
{doc.description}
</CardDescription>
</CardContent>
</Card>
</Link>
))}
</div>
</section>
</div>
</main>
{/* Footer */}
<footer className="mt-16 border-t py-8">
<div className="container mx-auto px-4">
<div className="mx-auto max-w-6xl">
<div className="flex flex-col gap-6 sm:flex-row sm:items-center sm:justify-between">
<div>
<p className="text-sm font-medium text-foreground mb-1">
FastNext Design System
</p>
<p className="text-xs text-muted-foreground">
Built with shadcn/ui, Tailwind CSS, and OKLCH colors
</p>
</div>
<div className="flex gap-3">
<Link href="/dev">
<Button variant="ghost" size="sm">
Interactive Demos
</Button>
</Link>
<Link href="/docs/design-system/00-quick-start">
<Button variant="ghost" size="sm">
Quick Start
</Button>
</Link>
</div>
</div>
</div>
</div>
</footer>
</div>
);
}

View File

@@ -0,0 +1,81 @@
/* istanbul ignore file */
/**
* CodeBlock Component
* Syntax-highlighted code block with copy functionality
* This file is excluded from coverage as it's a documentation component
*/
'use client';
import { useState } from 'react';
import { Check, Copy } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';
interface CodeBlockProps {
children: React.ReactNode;
className?: string;
title?: string;
}
export function CodeBlock({ children, className, title }: CodeBlockProps) {
const [copied, setCopied] = useState(false);
const handleCopy = async () => {
const code = extractTextFromChildren(children);
await navigator.clipboard.writeText(code);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
return (
<div className="group relative my-6">
{title && (
<div className="flex items-center justify-between rounded-t-lg border border-b-0 bg-muted/50 px-4 py-2">
<span className="text-xs font-medium text-muted-foreground">{title}</span>
</div>
)}
<div className={cn('relative', title && 'rounded-t-none')}>
<pre
className={cn(
'overflow-x-auto rounded-lg border bg-slate-950 p-4 font-mono text-sm',
title && 'rounded-t-none',
className
)}
>
{children}
</pre>
<Button
variant="ghost"
size="icon"
className="absolute right-2 top-2 h-8 w-8 opacity-0 transition-opacity group-hover:opacity-100"
onClick={handleCopy}
aria-label="Copy code"
>
{copied ? (
<Check className="h-4 w-4 text-green-500" />
) : (
<Copy className="h-4 w-4 text-muted-foreground" />
)}
</Button>
</div>
</div>
);
}
function extractTextFromChildren(children: React.ReactNode): string {
if (typeof children === 'string') {
return children;
}
if (Array.isArray(children)) {
return children.map(extractTextFromChildren).join('');
}
if (children && typeof children === 'object' && 'props' in children) {
return extractTextFromChildren((children as { props: { children: React.ReactNode } }).props.children);
}
return '';
}

View File

@@ -6,13 +6,15 @@
* This file is excluded from coverage as it's a documentation component * This file is excluded from coverage as it's a documentation component
*/ */
import Image from 'next/image';
import ReactMarkdown from 'react-markdown'; import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm'; import remarkGfm from 'remark-gfm';
import rehypeHighlight from 'rehype-highlight'; import rehypeHighlight from 'rehype-highlight';
import rehypeSlug from 'rehype-slug'; import rehypeSlug from 'rehype-slug';
import rehypeAutolinkHeadings from 'rehype-autolink-headings'; import rehypeAutolinkHeadings from 'rehype-autolink-headings';
import { CodeBlock } from './CodeBlock';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import 'highlight.js/styles/github-dark.css'; import 'highlight.js/styles/atom-one-dark.css';
interface MarkdownContentProps { interface MarkdownContentProps {
content: string; content: string;
@@ -30,10 +32,10 @@ export function MarkdownContent({ content, className }: MarkdownContentProps) {
[rehypeAutolinkHeadings, { behavior: 'wrap' }], [rehypeAutolinkHeadings, { behavior: 'wrap' }],
]} ]}
components={{ components={{
// Headings // Headings - improved spacing and visual hierarchy
h1: ({ children, ...props }) => ( h1: ({ children, ...props }) => (
<h1 <h1
className="scroll-mt-20 text-4xl font-bold tracking-tight mb-6 mt-8 first:mt-0 border-b pb-3" className="scroll-mt-20 text-4xl font-bold tracking-tight mb-8 mt-12 first:mt-0 border-b-2 border-primary/20 pb-4 text-foreground"
{...props} {...props}
> >
{children} {children}
@@ -41,7 +43,7 @@ export function MarkdownContent({ content, className }: MarkdownContentProps) {
), ),
h2: ({ children, ...props }) => ( h2: ({ children, ...props }) => (
<h2 <h2
className="scroll-mt-20 text-3xl font-semibold tracking-tight mb-4 mt-10 first:mt-0 border-b pb-2" className="scroll-mt-20 text-3xl font-semibold tracking-tight mb-6 mt-12 first:mt-0 border-b border-border pb-3 text-foreground"
{...props} {...props}
> >
{children} {children}
@@ -49,7 +51,7 @@ export function MarkdownContent({ content, className }: MarkdownContentProps) {
), ),
h3: ({ children, ...props }) => ( h3: ({ children, ...props }) => (
<h3 <h3
className="scroll-mt-20 text-2xl font-semibold tracking-tight mb-3 mt-8 first:mt-0" className="scroll-mt-20 text-2xl font-semibold tracking-tight mb-4 mt-10 first:mt-0 text-foreground"
{...props} {...props}
> >
{children} {children}
@@ -57,16 +59,16 @@ export function MarkdownContent({ content, className }: MarkdownContentProps) {
), ),
h4: ({ children, ...props }) => ( h4: ({ children, ...props }) => (
<h4 <h4
className="scroll-mt-20 text-xl font-semibold tracking-tight mb-2 mt-6 first:mt-0" className="scroll-mt-20 text-xl font-semibold tracking-tight mb-3 mt-8 first:mt-0 text-foreground"
{...props} {...props}
> >
{children} {children}
</h4> </h4>
), ),
// Paragraphs and text // Paragraphs and text - improved readability
p: ({ children, ...props }) => ( p: ({ children, ...props }) => (
<p className="leading-7 mb-4 text-foreground" {...props}> <p className="leading-relaxed mb-6 text-foreground/90 text-base" {...props}>
{children} {children}
</p> </p>
), ),
@@ -76,40 +78,40 @@ export function MarkdownContent({ content, className }: MarkdownContentProps) {
</strong> </strong>
), ),
em: ({ children, ...props }) => ( em: ({ children, ...props }) => (
<em className="italic" {...props}> <em className="italic text-foreground/80" {...props}>
{children} {children}
</em> </em>
), ),
// Links // Links - more prominent with better hover state
a: ({ children, href, ...props }) => ( a: ({ children, href, ...props }) => (
<a <a
href={href} href={href}
className="font-medium text-primary underline underline-offset-4 hover:text-primary/80 transition-colors" className="font-medium text-primary underline decoration-primary/30 underline-offset-4 hover:decoration-primary/60 hover:text-primary/90 transition-all"
{...props} {...props}
> >
{children} {children}
</a> </a>
), ),
// Lists // Lists - improved spacing and hierarchy
ul: ({ children, ...props }) => ( ul: ({ children, ...props }) => (
<ul className="my-4 ml-6 list-disc space-y-2" {...props}> <ul className="my-6 ml-6 list-disc space-y-3 marker:text-primary/60" {...props}>
{children} {children}
</ul> </ul>
), ),
ol: ({ children, ...props }) => ( ol: ({ children, ...props }) => (
<ol className="my-4 ml-6 list-decimal space-y-2" {...props}> <ol className="my-6 ml-6 list-decimal space-y-3 marker:text-primary/60 marker:font-semibold" {...props}>
{children} {children}
</ol> </ol>
), ),
li: ({ children, ...props }) => ( li: ({ children, ...props }) => (
<li className="leading-7 text-foreground" {...props}> <li className="leading-relaxed text-foreground/90 pl-2" {...props}>
{children} {children}
</li> </li>
), ),
// Code blocks // Code blocks - enhanced with copy button and better styling
code: ({ inline, className, children, ...props }: { code: ({ inline, className, children, ...props }: {
inline?: boolean; inline?: boolean;
className?: string; className?: string;
@@ -118,7 +120,7 @@ export function MarkdownContent({ content, className }: MarkdownContentProps) {
if (inline) { if (inline) {
return ( return (
<code <code
className="relative rounded bg-muted px-[0.4rem] py-[0.2rem] font-mono text-sm font-medium text-foreground border" className="relative rounded-md bg-primary/10 border border-primary/20 px-1.5 py-0.5 font-mono text-sm font-medium text-primary"
{...props} {...props}
> >
{children} {children}
@@ -128,7 +130,7 @@ export function MarkdownContent({ content, className }: MarkdownContentProps) {
return ( return (
<code <code
className={cn( className={cn(
'block font-mono text-sm', 'block font-mono text-sm leading-relaxed',
className className
)} )}
{...props} {...props}
@@ -138,27 +140,24 @@ export function MarkdownContent({ content, className }: MarkdownContentProps) {
); );
}, },
pre: ({ children, ...props }) => ( pre: ({ children, ...props }) => (
<pre <CodeBlock {...props}>
className="mb-4 mt-4 overflow-x-auto rounded-lg border bg-muted/30 p-4 font-mono text-sm"
{...props}
>
{children} {children}
</pre> </CodeBlock>
), ),
// Blockquotes // Blockquotes - enhanced callout styling
blockquote: ({ children, ...props }) => ( blockquote: ({ children, ...props }) => (
<blockquote <blockquote
className="mt-6 border-l-4 border-primary pl-4 italic text-muted-foreground" className="my-8 border-l-4 border-primary/50 bg-primary/5 pl-6 pr-4 py-4 italic text-foreground/80 rounded-r-lg"
{...props} {...props}
> >
{children} {children}
</blockquote> </blockquote>
), ),
// Tables // Tables - improved styling with better borders and hover states
table: ({ children, ...props }) => ( table: ({ children, ...props }) => (
<div className="my-6 w-full overflow-x-auto"> <div className="my-8 w-full overflow-x-auto rounded-lg border">
<table <table
className="w-full border-collapse text-sm" className="w-full border-collapse text-sm"
{...props} {...props}
@@ -168,21 +167,21 @@ export function MarkdownContent({ content, className }: MarkdownContentProps) {
</div> </div>
), ),
thead: ({ children, ...props }) => ( thead: ({ children, ...props }) => (
<thead className="border-b bg-muted/50" {...props}> <thead className="bg-muted/80 border-b-2 border-border" {...props}>
{children} {children}
</thead> </thead>
), ),
tbody: ({ children, ...props }) => ( tbody: ({ children, ...props }) => (
<tbody {...props}>{children}</tbody> <tbody className="divide-y divide-border" {...props}>{children}</tbody>
), ),
tr: ({ children, ...props }) => ( tr: ({ children, ...props }) => (
<tr className="border-b transition-colors hover:bg-muted/30" {...props}> <tr className="transition-colors hover:bg-muted/40" {...props}>
{children} {children}
</tr> </tr>
), ),
th: ({ children, ...props }) => ( th: ({ children, ...props }) => (
<th <th
className="px-4 py-3 text-left font-semibold [&[align=center]]:text-center [&[align=right]]:text-right" className="px-5 py-3.5 text-left font-semibold text-foreground [&[align=center]]:text-center [&[align=right]]:text-right"
{...props} {...props}
> >
{children} {children}
@@ -190,27 +189,35 @@ export function MarkdownContent({ content, className }: MarkdownContentProps) {
), ),
td: ({ children, ...props }) => ( td: ({ children, ...props }) => (
<td <td
className="px-4 py-3 [&[align=center]]:text-center [&[align=right]]:text-right" className="px-5 py-3.5 text-foreground/80 [&[align=center]]:text-center [&[align=right]]:text-right"
{...props} {...props}
> >
{children} {children}
</td> </td>
), ),
// Horizontal rule // Horizontal rule - more prominent
hr: ({ ...props }) => ( hr: ({ ...props }) => (
<hr className="my-8 border-t border-border" {...props} /> <hr className="my-12 border-t-2 border-border/50" {...props} />
), ),
// Images // Images - optimized with Next.js Image component
img: ({ src, alt, ...props }) => ( img: ({ src, alt }) => {
<img if (!src || typeof src !== 'string') return null;
src={src}
alt={alt} return (
className="rounded-lg border my-6" <div className="my-8 relative w-full overflow-hidden rounded-lg border shadow-md">
{...props} <Image
/> src={src}
), alt={alt || ''}
width={1200}
height={675}
className="w-full h-auto"
style={{ objectFit: 'contain' }}
/>
</div>
);
},
}} }}
> >
{content} {content}