Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions src/components/DocsBreadcrumbs.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
export interface Props {
items: Array<{
label: string;
href?: string;
}>;
}

const { items } = Astro.props;
---

<nav aria-label="Breadcrumb" class="flex items-center space-x-2 text-sm">
{items.map((item, index) => (
<>
{index > 0 && (
<svg class="w-4 h-4 text-gray-400 dark:text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
</svg>
)}
{item.href ? (
<a href={item.href.startsWith('#') ? item.href : import.meta.env.BASE_URL + item.href} class="text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 transition-colors">
{item.label}
</a>
) : (
<span class="text-gray-900 dark:text-white font-medium" aria-current="page">
{item.label}
</span>
)}
</>
))}
</nav>
52 changes: 52 additions & 0 deletions src/components/DocsHeader.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
import DocsBreadcrumbs from './DocsBreadcrumbs.astro';

export interface Props {
currentPage?: string;
breadcrumbs?: Array<{
label: string;
href?: string;
}>;
}

const { currentPage = 'Documentation', breadcrumbs = [] } = Astro.props;
---

<!-- Mobile docs header -->
<div class="sticky top-28 z-30 bg-white/95 dark:bg-slate-900/95 backdrop-blur border-b border-gray-200 dark:border-slate-700 md:hidden" x-data="{ open: false }">
<!-- Header bar -->
<div class="flex items-center justify-between px-4 py-3">
<div class="flex items-center space-x-3">
<button
@click="open = !open"
class="p-2 rounded-lg bg-indigo-100 dark:bg-slate-800/80 backdrop-blur-sm border border-indigo-200 dark:border-indigo-500/30 text-slate-600 dark:text-slate-200 hover:text-purple-600 dark:hover:text-purple-300 hover:bg-indigo-200 dark:hover:bg-slate-700/80 hover:border-purple-400 dark:hover:border-purple-400/50 transition-all duration-300"
>
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" x-show="!open">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" x-show="open">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>

<div class="border-l border-gray-300 dark:border-gray-600 h-6"></div>

<DocsBreadcrumbs items={breadcrumbs.length > 0 ? breadcrumbs : [{label: currentPage}]} />
</div>
</div>

<!-- Mobile menu -->
<div
x-show="open"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 -translate-y-1"
x-transition:enter-end="opacity-100 translate-y-0"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100 translate-y-0"
x-transition:leave-end="opacity-0 -translate-y-1"
class="absolute top-full left-0 right-0 bg-white dark:bg-slate-900 border-b border-gray-200 dark:border-slate-700 shadow-lg max-h-screen overflow-y-auto"
@click.away="open = false"
>
<slot name="mobile-nav" />
</div>
</div>
35 changes: 35 additions & 0 deletions src/components/DocsNavLink.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
export interface Props {
href: string;
label: string;
active?: boolean;
badge?: string;
icon?: string;
nested?: boolean;
}

const { href, label, active = false, badge, icon, nested = false } = Astro.props;
---

<a
href={href.startsWith('#') ? href : import.meta.env.BASE_URL + href}
class={`flex items-center justify-between px-3 py-2 text-sm rounded-lg transition-all duration-200 ${
nested ? 'ml-4' : ''
} ${
active
? 'bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300 font-medium'
: 'text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100 dark:hover:bg-slate-800'
}`}
>
<span class="flex items-center gap-2">
{icon && (
<span class="text-gray-400 dark:text-gray-500" set:html={icon} />
)}
{label}
</span>
{badge && (
<span class="px-2 py-0.5 text-xs font-medium rounded-full bg-purple-200 dark:bg-purple-800 text-purple-700 dark:text-purple-200">
{badge}
</span>
)}
</a>
43 changes: 43 additions & 0 deletions src/components/DocsNavbar.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
export interface Props {
sections: Array<{
title: string;
items: Array<{
label: string;
href: string;
active?: boolean;
}>;
}>;
}

const { sections } = Astro.props;
---

<!-- Desktop sidebar -->
<aside class="hidden md:block fixed left-0 top-0 bottom-0 w-64 bg-white dark:bg-slate-900 border-r border-gray-200 dark:border-slate-700 overflow-y-auto z-10 pt-20">
<nav class="px-6 mt-6 py-6 space-y-8">
{sections.map((section) => (
<div>
<h3 class="px-3 mb-3 text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">
{section.title}
</h3>
<ul class="space-y-1">
{section.items.map((item) => (
<li>
<a
href={item.href.startsWith('#') ? item.href : import.meta.env.BASE_URL + item.href}
class={`block px-3 py-2 text-sm rounded-lg transition-all duration-200 ${
item.active
? 'bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300 font-medium'
: 'text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100 dark:hover:bg-slate-800'
}`}
>
{item.label}
</a>
</li>
))}
</ul>
</div>
))}
</nav>
</aside>
43 changes: 43 additions & 0 deletions src/components/DocsSectionHeader.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
export interface Props {
id?: string;
level?: 'h1' | 'h2' | 'h3' | 'h4';
title: string;
description?: string;
}

const { id, level = 'h2', title, description } = Astro.props;
const Tag = level;

const headingClasses = {
h1: 'text-4xl font-bold text-gray-900 dark:text-white',
h2: 'text-3xl font-semibold text-gray-900 dark:text-white',
h3: 'text-2xl font-semibold text-gray-800 dark:text-gray-100',
h4: 'text-xl font-medium text-gray-800 dark:text-gray-100',
};
---

<div class="mb-8">
<Tag
id={id}
class={`${headingClasses[level]} mb-3 scroll-mt-40 group flex items-center`}
>
{title}
{id && (
<a
href={`#${id}`}
class="ml-2 opacity-0 group-hover:opacity-100 transition-opacity duration-200"
aria-label={`Link to ${title}`}
>
<svg class="w-5 h-5 text-gray-400 dark:text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"></path>
</svg>
</a>
)}
</Tag>
{description && (
<p class="text-gray-600 dark:text-gray-400 text-lg leading-relaxed">
{description}
</p>
)}
</div>
93 changes: 93 additions & 0 deletions src/layouts/docs.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
---
import Navbar from '../components/Navbar.astro';
import DocsNavbar from '../components/DocsNavbar.astro';
import DocsHeader from '../components/DocsHeader.astro';

export interface Props {
title: string;
description?: string;
keywords?: string;
githubUrl?: string;
currentPage?: string;
breadcrumbs?: Array<{
label: string;
href?: string;
}>;
sections: Array<{
title: string;
items: Array<{
label: string;
href: string;
active?: boolean;
}>;
}>;
}

const {
title,
description = "LuxVim Documentation",
keywords = "vim, neovim, Luxvim, documentation",
githubUrl = "https://github.com/LuxVim/LuxVim",
currentPage = "Documentation",
breadcrumbs = [],
sections
} = Astro.props;

const navLinks = [
{ href: 'docs', label: 'Documentation', active: true },
{ href: 'donate', label: 'Donate' },
];
---

<!DOCTYPE html>
<html lang="en" class="scroll-smooth">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content={description} />
<meta name="keywords" content={keywords} />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<title>{title}</title>
</head>
<body class="min-h-screen bg-gradient-to-br from-indigo-50 via-white to-purple-50 dark:from-slate-950 dark:via-slate-900 dark:to-slate-950">
<Navbar githubUrl={githubUrl} links={navLinks} />

<DocsHeader currentPage={currentPage} breadcrumbs={breadcrumbs}>
<nav slot="mobile-nav" class="px-4 py-6 space-y-8">
{sections.map((section) => (
<div>
<h3 class="px-3 mb-3 text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">
{section.title}
</h3>
<ul class="space-y-1">
{section.items.map((item) => (
<li>
<a
href={item.href.startsWith('#') ? item.href : import.meta.env.BASE_URL + item.href}
class={`block px-3 py-2 text-sm rounded-lg transition-all duration-200 ${
item.active
? 'bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300 font-medium'
: 'text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100 dark:hover:bg-slate-800'
}`}
>
{item.label}
</a>
</li>
))}
</ul>
</div>
))}
</nav>
</DocsHeader>

<div class="flex">
<DocsNavbar sections={sections} />

<main class="flex-1 md:ml-2 pt-28 md:pt-32">
<div class="max-w-4xl mx-auto px-4 md:px-8 py-8">
<slot />
</div>
</main>
</div>
</body>
</html>
Loading