Files
mintel.me/src/layouts/BaseLayout.astro
2026-01-13 02:42:03 +01:00

186 lines
5.1 KiB
Plaintext

---
import '../styles/global.css';
import { Footer } from '../components/Footer';
import { Hero } from '../components/Hero';
import Analytics from '../components/Analytics.astro';
interface Props {
title: string;
description?: string;
}
const { title, description = "Technical problem solver's blog - practical insights and learning notes" } = Astro.props;
---
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{title} | Marc Mintel</title>
<meta name="description" content={description} />
<meta name="generator" content={Astro.generator} />
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
</head>
<body>
<!-- Single page container -->
<div class="min-h-screen bg-white">
<!-- Main Content -->
<main class="container">
<slot />
</main>
<!-- Footer -->
<Footer client:load />
<!-- Global Interactive Elements -->
<!-- Reading Progress Bar -->
<div id="global-reading-progress" class="reading-progress-bar" style="display: none;"></div>
<!-- Floating Back to Top Button -->
<button
id="global-back-to-top"
class="floating-back-to-top"
aria-label="Back to top"
style="display: none;"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 10l7-7m0 0l7 7m-7-7v18" />
</svg>
</button>
</div>
<!-- Analytics Component -->
<Analytics />
<!-- Global JavaScript for interactive elements -->
<script>
// Global interactive elements manager
class GlobalInteractive {
readingProgress: HTMLElement | null;
backToTop: HTMLElement | null;
constructor() {
this.readingProgress = document.getElementById('global-reading-progress');
this.backToTop = document.getElementById('global-back-to-top');
this.init();
}
init() {
// Set up event listeners
window.addEventListener('scroll', () => this.handleScroll());
// Back to top click
this.backToTop?.addEventListener('click', () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
});
// Show elements on first interaction
document.addEventListener('click', () => this.showElements(), { once: true });
document.addEventListener('scroll', () => this.showElements(), { once: true });
}
showElements() {
if (this.readingProgress) {
this.readingProgress.style.display = 'block';
}
if (this.backToTop) {
this.backToTop.style.display = 'flex';
}
}
handleScroll() {
const scrollTop = window.scrollY;
const docHeight = document.documentElement.scrollHeight - window.innerHeight;
// Update reading progress
if (this.readingProgress && docHeight > 0) {
const progress = (scrollTop / docHeight) * 100;
this.readingProgress.style.transform = `scaleX(${progress / 100})`;
}
// Show/hide back to top button
if (this.backToTop) {
if (scrollTop > 300) {
this.backToTop.classList.add('visible');
} else {
this.backToTop.classList.remove('visible');
}
}
}
}
// Initialize when DOM is ready
if (typeof document !== 'undefined') {
document.addEventListener('DOMContentLoaded', () => {
new GlobalInteractive();
});
}
</script>
<style>
/* Additional global styles */
/* Smooth scroll behavior */
html {
scroll-behavior: smooth;
}
/* Custom scrollbar */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: #f1f5f9;
}
::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 4px;
transition: background 0.2s ease;
}
::-webkit-scrollbar-thumb:hover {
background: #94a3b8;
}
/* Selection color */
::selection {
background: #bfdbfe;
color: #1e40af;
}
/* Reduced motion support */
@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* Print styles */
@media print {
.floating-back-to-top,
.keyboard-hint,
.reading-progress-bar {
display: none !important;
}
}
</style>
</body>
</html>