Template
Initial react editor website template
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { Menu, X } from 'lucide-react';
|
||||
import { Logo } from '@/components/logo';
|
||||
import { ThemeToggle } from '@/components/theme-toggle';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
export type NavigationLink = { label: string; url: string };
|
||||
|
||||
export type HeaderProps = {
|
||||
siteName?: string;
|
||||
logoUrl?: string;
|
||||
links?: NavigationLink[];
|
||||
ctaLabel?: string;
|
||||
ctaUrl?: string;
|
||||
showThemeToggle?: 'yes' | 'no';
|
||||
sticky?: 'yes' | 'no';
|
||||
};
|
||||
|
||||
export default function Header({
|
||||
siteName = 'Your Company',
|
||||
logoUrl = '',
|
||||
links = [],
|
||||
ctaLabel = 'Get started',
|
||||
ctaUrl = '/#pricing',
|
||||
showThemeToggle = 'yes',
|
||||
sticky = 'yes',
|
||||
}: HeaderProps) {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<header className={`${sticky === 'yes' ? 'sticky top-0' : 'relative'} z-50 bg-background`}>
|
||||
<div className="section-shell flex h-16 items-center justify-between">
|
||||
<Logo name={siteName} logoUrl={logoUrl} />
|
||||
|
||||
<nav aria-label="Primary navigation" className="hidden items-center gap-7 md:flex">
|
||||
{links.map((link) => (
|
||||
<Link key={`${link.label}-${link.url}`} href={link.url} className="text-sm text-muted-foreground transition-colors hover:text-foreground">
|
||||
{link.label}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
<div className="flex items-center gap-1.5">
|
||||
{showThemeToggle === 'yes' && <ThemeToggle />}
|
||||
{ctaLabel && (
|
||||
<Button asChild size="sm" className="hidden sm:inline-flex">
|
||||
<Link href={ctaUrl}>{ctaLabel}</Link>
|
||||
</Button>
|
||||
)}
|
||||
<Button type="button" variant="ghost" size="icon" className="md:hidden" aria-label={open ? 'Close navigation' : 'Open navigation'} aria-expanded={open} onClick={() => setOpen((value) => !value)}>
|
||||
{open ? <X aria-hidden="true" /> : <Menu aria-hidden="true" />}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{open && (
|
||||
<nav aria-label="Mobile navigation" className="bg-background px-4 py-4 md:hidden">
|
||||
<div className="section-shell flex flex-col gap-1">
|
||||
{links.map((link) => (
|
||||
<Link key={`${link.label}-${link.url}`} href={link.url} onClick={() => setOpen(false)} className="rounded-md px-3 py-2.5 text-sm hover:bg-muted">
|
||||
{link.label}
|
||||
</Link>
|
||||
))}
|
||||
{ctaLabel && (
|
||||
<Button asChild className="mt-2 w-full">
|
||||
<Link href={ctaUrl} onClick={() => setOpen(false)}>{ctaLabel}</Link>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</nav>
|
||||
)}
|
||||
</header>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user