186 lines
5.1 KiB
Plaintext
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>
|