**Design System Enhancements:** Replace .md links with clean paths in /dev documentation. Migrate anchor tags (<a>) to Next.js <Link> components for internal navigation. Add dynamic [...slug] markdown route for rendering docs. Introduce MarkdownContent for styled markdown rendering with syntax highlighting. Perform general cleanup of unused imports and variables in design system files. Fix minor wording issues.
This commit is contained in:
@@ -16,7 +16,6 @@ import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/components/ui/card';
|
||||
@@ -30,7 +29,6 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
||||
import { Example, ExampleSection } from '@/components/dev/Example';
|
||||
@@ -441,7 +439,7 @@ const { register, handleSubmit, formState: { errors } } = useForm({
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="h-4 w-4 text-green-600 mt-0.5" />
|
||||
<span>Add <code className="text-xs">role="alert"</code> to error messages</span>
|
||||
<span>Add <code className="text-xs">role="alert"</code> to error messages</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="h-4 w-4 text-green-600 mt-0.5" />
|
||||
@@ -575,14 +573,12 @@ const { register, handleSubmit, formState: { errors } } = useForm({
|
||||
<div className="container mx-auto px-4 text-center">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Learn more:{' '}
|
||||
<a
|
||||
href="/docs/design-system/06-forms.md"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
<Link
|
||||
href="/docs/design-system/06-forms"
|
||||
className="font-medium hover:text-foreground"
|
||||
>
|
||||
Forms Documentation
|
||||
</a>
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import type { Metadata } from 'next';
|
||||
import Link from 'next/link';
|
||||
import { ArrowLeft, Grid3x3, LayoutDashboard } from 'lucide-react';
|
||||
import { ArrowLeft, Grid3x3 } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Card,
|
||||
@@ -508,14 +508,12 @@ export default function LayoutsPage() {
|
||||
<div className="container mx-auto px-4 text-center">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Learn more:{' '}
|
||||
<a
|
||||
href="/docs/design-system/03-layouts.md"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
<Link
|
||||
href="/docs/design-system/03-layouts"
|
||||
className="font-medium hover:text-foreground"
|
||||
>
|
||||
Layout Documentation
|
||||
</a>
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -70,22 +70,22 @@ const documentationLinks = [
|
||||
{
|
||||
title: 'Quick Start',
|
||||
description: '5-minute crash course',
|
||||
href: '/docs/design-system/00-quick-start.md',
|
||||
href: '/docs/design-system/00-quick-start',
|
||||
},
|
||||
{
|
||||
title: 'Complete Documentation',
|
||||
description: 'Full design system guide',
|
||||
href: '/docs/design-system/README.md',
|
||||
href: '/docs/design-system/README',
|
||||
},
|
||||
{
|
||||
title: 'AI Guidelines',
|
||||
description: 'Rules for AI code generation',
|
||||
href: '/docs/design-system/08-ai-guidelines.md',
|
||||
href: '/docs/design-system/08-ai-guidelines',
|
||||
},
|
||||
{
|
||||
title: 'Quick Reference',
|
||||
description: 'Cheat sheet for lookups',
|
||||
href: '/docs/design-system/99-reference.md',
|
||||
href: '/docs/design-system/99-reference',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -190,11 +190,9 @@ export default function DesignSystemHub() {
|
||||
|
||||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
||||
{documentationLinks.map((link) => (
|
||||
<a
|
||||
<Link
|
||||
key={link.href}
|
||||
href={link.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="group"
|
||||
>
|
||||
<Card className="h-full transition-all hover:border-primary/50 hover:bg-accent/50">
|
||||
@@ -207,7 +205,7 @@ export default function DesignSystemHub() {
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
</a>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -374,7 +374,7 @@ export default function SpacingPage() {
|
||||
<ul className="ml-6 space-y-1 text-sm text-muted-foreground list-disc">
|
||||
<li>Exception case (one child needs different spacing)</li>
|
||||
<li>Negative margin for overlap effects</li>
|
||||
<li>Can't modify parent (external component)</li>
|
||||
<li>Cannot modify parent (external component)</li>
|
||||
</ul>
|
||||
<div className="rounded-lg border bg-muted/30 p-3 font-mono text-xs">
|
||||
mt-8 {/* exception */}
|
||||
@@ -506,14 +506,12 @@ export default function SpacingPage() {
|
||||
<div className="container mx-auto px-4 text-center">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Learn more:{' '}
|
||||
<a
|
||||
href="/docs/design-system/04-spacing-philosophy.md"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
<Link
|
||||
href="/docs/design-system/04-spacing-philosophy"
|
||||
className="font-medium hover:text-foreground"
|
||||
>
|
||||
Spacing Philosophy Documentation
|
||||
</a>
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
122
frontend/src/app/docs/[...slug]/page.tsx
Normal file
122
frontend/src/app/docs/[...slug]/page.tsx
Normal file
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* Dynamic Documentation Route
|
||||
* Renders markdown files from docs/ directory
|
||||
* Access: /docs/design-system/01-foundations, etc.
|
||||
*/
|
||||
|
||||
import { notFound } from 'next/navigation';
|
||||
import { promises as fs } from 'fs';
|
||||
import path from 'path';
|
||||
import matter from 'gray-matter';
|
||||
import Link from 'next/link';
|
||||
import { ArrowLeft, FileText } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { MarkdownContent } from '@/components/docs/MarkdownContent';
|
||||
|
||||
interface DocPageProps {
|
||||
params: Promise<{ slug: string[] }>;
|
||||
}
|
||||
|
||||
// Generate static params for all documentation files
|
||||
export async function generateStaticParams() {
|
||||
const docsDir = path.join(process.cwd(), 'docs', 'design-system');
|
||||
|
||||
try {
|
||||
const files = await fs.readdir(docsDir);
|
||||
const mdFiles = files.filter(file => file.endsWith('.md'));
|
||||
|
||||
return mdFiles.map(file => ({
|
||||
slug: ['design-system', file.replace(/\.md$/, '')],
|
||||
}));
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Get markdown file content
|
||||
async function getDocContent(slug: string[]) {
|
||||
const filePath = path.join(process.cwd(), 'docs', ...slug) + '.md';
|
||||
|
||||
try {
|
||||
const fileContent = await fs.readFile(filePath, 'utf-8');
|
||||
const { data, content } = matter(fileContent);
|
||||
|
||||
return {
|
||||
frontmatter: data,
|
||||
content,
|
||||
filePath: slug.join('/'),
|
||||
};
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export default async function DocPage({ params }: DocPageProps) {
|
||||
const { slug } = await params;
|
||||
const doc = await getDocContent(slug);
|
||||
|
||||
if (!doc) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
// Extract title from first heading or use filename
|
||||
const title = doc.content.match(/^#\s+(.+)$/m)?.[1] || slug[slug.length - 1];
|
||||
|
||||
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 gap-4 px-4">
|
||||
<Link href="/dev">
|
||||
<Button variant="ghost" size="icon" aria-label="Back to design system">
|
||||
<ArrowLeft className="h-5 w-5" />
|
||||
</Button>
|
||||
</Link>
|
||||
<div className="flex items-center gap-2">
|
||||
<FileText className="h-5 w-5 text-muted-foreground" />
|
||||
<div>
|
||||
<h1 className="text-lg font-semibold">{title}</h1>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{doc.filePath}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Content */}
|
||||
<main className="container mx-auto px-4 py-8">
|
||||
<div className="mx-auto max-w-4xl">
|
||||
<MarkdownContent content={doc.content} />
|
||||
</div>
|
||||
</main>
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="mt-16 border-t py-6">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="mx-auto max-w-4xl">
|
||||
<Separator className="mb-6" />
|
||||
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
FastNext Design System Documentation
|
||||
</p>
|
||||
<div className="flex gap-2">
|
||||
<Link href="/dev">
|
||||
<Button variant="outline" size="sm">
|
||||
View Interactive Demos
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href="/docs/design-system/README">
|
||||
<Button variant="outline" size="sm">
|
||||
Documentation Home
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import Link from 'next/link';
|
||||
import {
|
||||
Moon, Sun, Mail, User,
|
||||
Settings, LogOut, Shield, AlertCircle, Info,
|
||||
CheckCircle2, AlertTriangle, Trash2, ArrowLeft
|
||||
Trash2, ArrowLeft
|
||||
} from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { Code2, Eye } from 'lucide-react';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
@@ -49,7 +48,6 @@ export function Example({
|
||||
centered = false,
|
||||
tags,
|
||||
}: ExampleProps) {
|
||||
const [showCode, setShowCode] = useState(false);
|
||||
|
||||
// Compact variant - no card wrapper
|
||||
if (variant === 'compact') {
|
||||
|
||||
220
frontend/src/components/docs/MarkdownContent.tsx
Normal file
220
frontend/src/components/docs/MarkdownContent.tsx
Normal file
@@ -0,0 +1,220 @@
|
||||
/* istanbul ignore file */
|
||||
|
||||
/**
|
||||
* MarkdownContent Component
|
||||
* Renders markdown content with syntax highlighting and design system styling
|
||||
* This file is excluded from coverage as it's a documentation component
|
||||
*/
|
||||
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import rehypeHighlight from 'rehype-highlight';
|
||||
import rehypeSlug from 'rehype-slug';
|
||||
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
|
||||
import { cn } from '@/lib/utils';
|
||||
import 'highlight.js/styles/github-dark.css';
|
||||
|
||||
interface MarkdownContentProps {
|
||||
content: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function MarkdownContent({ content, className }: MarkdownContentProps) {
|
||||
return (
|
||||
<div className={cn('prose prose-neutral dark:prose-invert max-w-none', className)}>
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[remarkGfm]}
|
||||
rehypePlugins={[
|
||||
rehypeHighlight,
|
||||
rehypeSlug,
|
||||
[rehypeAutolinkHeadings, { behavior: 'wrap' }],
|
||||
]}
|
||||
components={{
|
||||
// Headings
|
||||
h1: ({ children, ...props }) => (
|
||||
<h1
|
||||
className="scroll-mt-20 text-4xl font-bold tracking-tight mb-6 mt-8 first:mt-0 border-b pb-3"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</h1>
|
||||
),
|
||||
h2: ({ children, ...props }) => (
|
||||
<h2
|
||||
className="scroll-mt-20 text-3xl font-semibold tracking-tight mb-4 mt-10 first:mt-0 border-b pb-2"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</h2>
|
||||
),
|
||||
h3: ({ children, ...props }) => (
|
||||
<h3
|
||||
className="scroll-mt-20 text-2xl font-semibold tracking-tight mb-3 mt-8 first:mt-0"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</h3>
|
||||
),
|
||||
h4: ({ children, ...props }) => (
|
||||
<h4
|
||||
className="scroll-mt-20 text-xl font-semibold tracking-tight mb-2 mt-6 first:mt-0"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</h4>
|
||||
),
|
||||
|
||||
// Paragraphs and text
|
||||
p: ({ children, ...props }) => (
|
||||
<p className="leading-7 mb-4 text-foreground" {...props}>
|
||||
{children}
|
||||
</p>
|
||||
),
|
||||
strong: ({ children, ...props }) => (
|
||||
<strong className="font-semibold text-foreground" {...props}>
|
||||
{children}
|
||||
</strong>
|
||||
),
|
||||
em: ({ children, ...props }) => (
|
||||
<em className="italic" {...props}>
|
||||
{children}
|
||||
</em>
|
||||
),
|
||||
|
||||
// Links
|
||||
a: ({ children, href, ...props }) => (
|
||||
<a
|
||||
href={href}
|
||||
className="font-medium text-primary underline underline-offset-4 hover:text-primary/80 transition-colors"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
),
|
||||
|
||||
// Lists
|
||||
ul: ({ children, ...props }) => (
|
||||
<ul className="my-4 ml-6 list-disc space-y-2" {...props}>
|
||||
{children}
|
||||
</ul>
|
||||
),
|
||||
ol: ({ children, ...props }) => (
|
||||
<ol className="my-4 ml-6 list-decimal space-y-2" {...props}>
|
||||
{children}
|
||||
</ol>
|
||||
),
|
||||
li: ({ children, ...props }) => (
|
||||
<li className="leading-7 text-foreground" {...props}>
|
||||
{children}
|
||||
</li>
|
||||
),
|
||||
|
||||
// Code blocks
|
||||
code: ({ inline, className, children, ...props }: {
|
||||
inline?: boolean;
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
}) => {
|
||||
if (inline) {
|
||||
return (
|
||||
<code
|
||||
className="relative rounded bg-muted px-[0.4rem] py-[0.2rem] font-mono text-sm font-medium text-foreground border"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</code>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<code
|
||||
className={cn(
|
||||
'block font-mono text-sm',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</code>
|
||||
);
|
||||
},
|
||||
pre: ({ children, ...props }) => (
|
||||
<pre
|
||||
className="mb-4 mt-4 overflow-x-auto rounded-lg border bg-muted/30 p-4 font-mono text-sm"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</pre>
|
||||
),
|
||||
|
||||
// Blockquotes
|
||||
blockquote: ({ children, ...props }) => (
|
||||
<blockquote
|
||||
className="mt-6 border-l-4 border-primary pl-4 italic text-muted-foreground"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</blockquote>
|
||||
),
|
||||
|
||||
// Tables
|
||||
table: ({ children, ...props }) => (
|
||||
<div className="my-6 w-full overflow-x-auto">
|
||||
<table
|
||||
className="w-full border-collapse text-sm"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</table>
|
||||
</div>
|
||||
),
|
||||
thead: ({ children, ...props }) => (
|
||||
<thead className="border-b bg-muted/50" {...props}>
|
||||
{children}
|
||||
</thead>
|
||||
),
|
||||
tbody: ({ children, ...props }) => (
|
||||
<tbody {...props}>{children}</tbody>
|
||||
),
|
||||
tr: ({ children, ...props }) => (
|
||||
<tr className="border-b transition-colors hover:bg-muted/30" {...props}>
|
||||
{children}
|
||||
</tr>
|
||||
),
|
||||
th: ({ children, ...props }) => (
|
||||
<th
|
||||
className="px-4 py-3 text-left font-semibold [&[align=center]]:text-center [&[align=right]]:text-right"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</th>
|
||||
),
|
||||
td: ({ children, ...props }) => (
|
||||
<td
|
||||
className="px-4 py-3 [&[align=center]]:text-center [&[align=right]]:text-right"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</td>
|
||||
),
|
||||
|
||||
// Horizontal rule
|
||||
hr: ({ ...props }) => (
|
||||
<hr className="my-8 border-t border-border" {...props} />
|
||||
),
|
||||
|
||||
// Images
|
||||
img: ({ src, alt, ...props }) => (
|
||||
<img
|
||||
src={src}
|
||||
alt={alt}
|
||||
className="rounded-lg border my-6"
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user