feat: add official EmDash marketing template
- Marketing landing page with hero, features, testimonials, FAQ, pricing - EmDash CMS with pages collection and marketing blocks - Full seed data with all content sections - Dockerfile with entrypoint for database persistence - Responsive design with CSS variables Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
156
src/layouts/Base.astro
Normal file
156
src/layouts/Base.astro
Normal file
@@ -0,0 +1,156 @@
|
||||
---
|
||||
import { getMenu, getSiteSettings } from "emdash";
|
||||
import { EmDashHead } from "emdash/ui";
|
||||
import { createPublicPageContext } from "emdash/page";
|
||||
import { Font } from "astro:assets";
|
||||
import "../styles/theme.css";
|
||||
|
||||
interface Props {
|
||||
title?: string;
|
||||
description?: string;
|
||||
image?: string;
|
||||
}
|
||||
|
||||
const { title, description, image } = Astro.props;
|
||||
const settings = await getSiteSettings();
|
||||
const siteTitle = settings?.title || "Acme";
|
||||
const fullTitle = title ? `${title} — ${siteTitle}` : siteTitle;
|
||||
const siteDescription = settings?.tagline || "Build products people actually want";
|
||||
const siteLogo = (settings?.logo as any)?.url ? settings.logo as { mediaId: string; alt?: string; url: string } : null;
|
||||
const siteFavicon = (settings?.favicon as any)?.url ?? null;
|
||||
|
||||
const menu = await getMenu("primary");
|
||||
|
||||
const pageCtx = createPublicPageContext({
|
||||
Astro,
|
||||
kind: "custom",
|
||||
pageType: "website",
|
||||
title: fullTitle,
|
||||
pageTitle: title ?? siteTitle,
|
||||
description: description || siteDescription,
|
||||
canonical: Astro.url.href,
|
||||
image,
|
||||
seo: { ogImage: image },
|
||||
siteName: siteTitle,
|
||||
});
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>{fullTitle}</title>
|
||||
{siteFavicon && <link rel="icon" href={siteFavicon} />}
|
||||
{description && <meta name="description" content={description} />}
|
||||
<EmDashHead emdash={Astro.locals.emdash} pageContext={pageCtx} />
|
||||
<Font ...{({ variable: "--font-sans", provider: { type: "google", name: "Inter" } })} />
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
<nav class="container nav">
|
||||
<a href="/" class="logo">
|
||||
{siteLogo ? (
|
||||
<img src={siteLogo.url} alt={siteLogo.alt || siteTitle} width="32" height="32" />
|
||||
) : (
|
||||
<span>{siteTitle}</span>
|
||||
)}
|
||||
</a>
|
||||
<ul class="nav-links">
|
||||
{menu?.items.map((item: any) => (
|
||||
<li>
|
||||
<a href={item.url}>{item.label}</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<a href="/contact" class="btn btn-primary">Get Started</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<slot />
|
||||
</main>
|
||||
|
||||
<footer class="site-footer">
|
||||
<div class="container">
|
||||
<p>© {new Date().getFullYear()} {siteTitle}. All rights reserved.</p>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<style>
|
||||
.site-header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
background: var(--color-bg);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: var(--spacing-md) 0;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-weight: 700;
|
||||
font-size: var(--font-size-xl);
|
||||
text-decoration: none;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
display: flex;
|
||||
gap: var(--spacing-xl);
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.nav-links a {
|
||||
text-decoration: none;
|
||||
color: var(--color-text);
|
||||
font-weight: 500;
|
||||
transition: color var(--transition-fast);
|
||||
}
|
||||
|
||||
.nav-links a:hover {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.site-footer {
|
||||
border-top: 1px solid var(--color-border);
|
||||
padding: var(--spacing-2xl) 0;
|
||||
text-align: center;
|
||||
color: var(--color-muted);
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: var(--spacing-sm) var(--spacing-lg);
|
||||
border-radius: var(--radius);
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--color-primary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: var(--color-primary-dark);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.nav-links {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user