Files
Cozy-Den/src/layouts/BaseLayout.astro
T
Latte 7fbe9a3e43
CI / ci (push) Successful in 28s
Add Nav, About/Projects pages and nginx hardening
Harden nginx: add CSP, HSTS, Referrer-Policy and Permissions-Policy;
include image/svg+xml in gzip types; set X-Content-Type-Options on
static assets; change try_files to return =404.

Add Nav component and wire into BaseLayout; add About and Projects
pages with projects.json, an initial blog post, and small layout/padding
adjustments (removed redundant back links).
2026-03-04 19:53:13 +01:00

207 lines
5.4 KiB
Plaintext

---
interface Props {
title: string;
description?: string;
ogImage?: string;
canonicalURL?: string;
}
import Nav from "../components/Nav.astro";
const {
title,
description = "Hidden Den Cafe - A cozy, self-hosted corner of the internet. Privacy-focused, furry-friendly, and built with love.",
ogImage = "/og-image.png",
canonicalURL = Astro.url.pathname,
} = Astro.props;
const fullCanonicalURL = new URL(canonicalURL, Astro.site).href;
const fullOgImage = new URL(ogImage, Astro.site).href;
---
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="generator" content={Astro.generator} />
<!-- Primary Meta Tags -->
<title>{title}</title>
<meta name="title" content={title} />
<meta name="description" content={description} />
<link rel="canonical" href={fullCanonicalURL} />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content={fullCanonicalURL} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={fullOgImage} />
<meta property="og:site_name" content="Hidden Den Cafe" />
<meta property="og:locale" content="en_US" />
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:url" content={fullCanonicalURL} />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content={fullOgImage} />
<!-- Additional Meta Tags -->
<meta name="author" content="Latte" />
<meta
name="keywords"
content="self-hosted, privacy, open-source, furry, developer, cozy, hidden den"
/>
<meta name="theme-color" content="#cba6f7" />
<meta name="color-scheme" content="dark" />
<!-- Favicons -->
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/favicon-16x16.png"
/>
<link
rel="apple-touch-icon"
sizes="180x180"
href="/apple-touch-icon.png"
/>
<link rel="manifest" href="/site.webmanifest" />
<!-- Preconnect for performance (if needed for future external resources) -->
<!-- <link rel="preconnect" href="https://example.com" crossorigin /> -->
</head>
<body>
<Nav />
<slot />
</body>
</html>
<style is:global>
:root {
/* Catppuccin Mocha Color Palette */
--color-bg: #1e1e2e;
--color-bg-light: #313244;
--color-surface: #45475a;
--color-text: #cdd6f4;
--color-text-dim: #a6adc8;
--color-accent: #cba6f7;
--color-accent-bright: #f5c2e7;
--color-warm: #f38ba8;
--color-blue: #89b4fa;
--color-green: #a6e3a1;
--color-peach: #fab387;
/* Spacing */
--space-xs: 0.5rem;
--space-sm: 1rem;
--space-md: 1.5rem;
--space-lg: 2rem;
--space-xl: 3rem;
/* Typography */
--font-body: "JetBrains Mono", "Fira Code", "SF Mono", Consolas, monospace;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
font-family: var(--font-body);
background: var(--color-bg);
color: var(--color-text);
scroll-behavior: smooth;
}
body {
min-height: 100vh;
line-height: 1.6;
}
h1,
h2,
h3,
h4,
h5,
h6 {
color: var(--color-accent-bright);
line-height: 1.2;
}
a {
color: var(--color-accent-bright);
text-decoration: none;
transition: color 0.2s ease;
}
a:hover {
color: var(--color-accent);
}
a:focus-visible {
outline: 2px solid var(--color-accent);
outline-offset: 2px;
border-radius: 2px;
}
/* Smooth animations */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fade-in {
animation: fadeIn 0.6s ease-out;
}
/* Respect user's motion preferences */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
/* Improve focus visibility for keyboard navigation */
*:focus-visible {
outline: 2px solid var(--color-accent);
outline-offset: 2px;
}
/* Screen reader only content */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
</style>