546 lines
No EOL
22 KiB
HTML
546 lines
No EOL
22 KiB
HTML
|
|
<!DOCTYPE html>
|
|
<!-- The 'dark' class will be managed by JS -->
|
|
<html lang="pl" class="">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Paulina i Paweł Orzech</title>
|
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🌰</text></svg>">
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<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=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
|
|
|
|
<script>
|
|
// Set Tailwind to use class-based dark mode
|
|
tailwind.config = {
|
|
darkMode: 'class'
|
|
}
|
|
|
|
// Run this script before the body renders to prevent theme flickering
|
|
const applyTheme = () => {
|
|
const theme = localStorage.getItem('theme') || 'system';
|
|
const themeSwitcher = document.getElementById('theme-switcher');
|
|
|
|
if (theme === 'dark' || (theme === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
|
document.documentElement.classList.add('dark');
|
|
document.body.classList.remove('light-mode-animated');
|
|
} else {
|
|
document.documentElement.classList.remove('dark');
|
|
document.body.classList.add('light-mode-animated');
|
|
}
|
|
};
|
|
applyTheme();
|
|
</script>
|
|
|
|
<style>
|
|
body {
|
|
font-family: 'Poppins', sans-serif;
|
|
background-size: 200% 200%;
|
|
}
|
|
|
|
@keyframes wind-breeze {
|
|
0% { background-position: 0% 50%; }
|
|
25% { background-position: 100% 0%; }
|
|
50% { background-position: 0% 100%; }
|
|
75% { background-position: 100% 50%; }
|
|
100% { background-position: 0% 50%; }
|
|
}
|
|
|
|
body.light-mode-animated {
|
|
animation: wind-breeze 30s ease infinite;
|
|
background-size: 400% 400%; /* Increase background size to allow more movement */
|
|
}
|
|
.animate-on-load {
|
|
opacity: 0;
|
|
transform: translateY(20px);
|
|
transition: opacity 0.8s ease-out, transform 0.8s ease-out;
|
|
}
|
|
.animate-in {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
.switcher button {
|
|
cursor: pointer;
|
|
transition: all 0.2s ease-in-out;
|
|
filter: grayscale(50%);
|
|
border-radius: 9999px; /* Make them round */
|
|
padding: 0.25rem; /* Add some padding */
|
|
}
|
|
.switcher button:hover {
|
|
transform: scale(1.25); /* Slightly larger on hover */
|
|
filter: grayscale(0); /* Full color on hover */
|
|
box-shadow: 0 4px 12px rgba(0,0,0,0.1); /* Subtle shadow on hover */
|
|
}
|
|
.switcher .active {
|
|
transform: scale(1.15);
|
|
filter: grayscale(0);
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.15); /* More prominent shadow for active */
|
|
}
|
|
|
|
/* Fade animation for language change */
|
|
.fade-out {
|
|
opacity: 0;
|
|
transition: opacity 0.3s ease-out;
|
|
}
|
|
.fade-in {
|
|
opacity: 1;
|
|
transition: opacity 0.3s ease-in;
|
|
}
|
|
|
|
/* Star animation for dark mode */
|
|
@keyframes twinkle {
|
|
0%, 100% { opacity: 0; transform: scale(0.5); }
|
|
50% { opacity: 1; transform: scale(1); }
|
|
}
|
|
|
|
.star {
|
|
position: absolute;
|
|
background-color: rgba(255, 255, 255, 0.8);
|
|
border-radius: 50%;
|
|
animation: twinkle 2s infinite ease-in-out alternate;
|
|
pointer-events: none;
|
|
}
|
|
|
|
/* Shooting star animation */
|
|
@keyframes shoot {
|
|
0% {
|
|
transform: translate(0, 0) scale(0.5);
|
|
opacity: 0;
|
|
}
|
|
10% {
|
|
opacity: 1;
|
|
}
|
|
100% {
|
|
transform: translate(200px, 200px) scale(1);
|
|
opacity: 0;
|
|
}
|
|
}
|
|
|
|
.shooting-star {
|
|
position: absolute;
|
|
width: 4px;
|
|
height: 4px;
|
|
background: linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.8));
|
|
border-radius: 50%;
|
|
filter: blur(1px);
|
|
animation: shoot 2s linear forwards;
|
|
pointer-events: none;
|
|
transform-origin: 0% 0%;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Sun animation for light mode */
|
|
@keyframes sun-glow {
|
|
0%, 100% { box-shadow: 0 0 20px 8px rgba(255, 255, 200, 0.4); }
|
|
50% { box-shadow: 0 0 40px 15px rgba(255, 255, 200, 0.7); }
|
|
}
|
|
|
|
@keyframes sun-drift {
|
|
0% { transform: translate(-50%, -50%); }
|
|
25% { transform: translate(-48%, -49%); }
|
|
50% { transform: translate(-52%, -51%); }
|
|
75% { transform: translate(-49%, -48%); }
|
|
100% { transform: translate(-50%, -50%); }
|
|
}
|
|
|
|
.sun {
|
|
position: absolute;
|
|
top: 10%;
|
|
left: 50%;
|
|
width: 180px; /* Slightly larger for more presence */
|
|
height: 180px;
|
|
background: radial-gradient(circle, #FFFACD, #FFEFD5); /* Pastel yellow to peach */
|
|
border-radius: 50%;
|
|
animation: sun-glow 6s infinite alternate, sun-drift 20s infinite ease-in-out;
|
|
pointer-events: none;
|
|
opacity: 0;
|
|
transition: opacity 0.5s ease-in-out;
|
|
}
|
|
|
|
.sun.visible {
|
|
opacity: 1;
|
|
}
|
|
|
|
/* Moon animation for dark mode */
|
|
@keyframes moon-glow {
|
|
0%, 100% { box-shadow: 0 0 20px 8px rgba(200, 200, 255, 0.2); }
|
|
50% { box-shadow: 0 0 40px 15px rgba(200, 200, 255, 0.4); }
|
|
}
|
|
|
|
@keyframes moon-drift {
|
|
0% { transform: translate(-50%, -50%); }
|
|
25% { transform: translate(-48%, -49%); }
|
|
50% { transform: translate(-52%, -51%); }
|
|
75% { transform: translate(-49%, -48%); }
|
|
100% { transform: translate(-50%, -50%); }
|
|
}
|
|
|
|
.moon {
|
|
position: absolute;
|
|
top: 10%;
|
|
left: 50%;
|
|
width: 150px; /* Slightly smaller than sun */
|
|
height: 150px;
|
|
background: radial-gradient(circle, #E0E0E0, #B0B0B0); /* Greyish white */
|
|
border-radius: 50%;
|
|
animation: moon-glow 6s infinite alternate, moon-drift 20s infinite ease-in-out;
|
|
pointer-events: none;
|
|
opacity: 0;
|
|
transition: opacity 0.5s ease-in-out;
|
|
}
|
|
|
|
.moon.visible {
|
|
opacity: 1;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body class="bg-gradient-to-r from-blue-50 to-blue-100 dark:bg-gradient-to-r dark:from-gray-900 dark:to-gray-800 text-gray-800 flex items-center justify-center min-h-screen transition-colors duration-300">
|
|
|
|
<div id="star-container" class="absolute inset-0 overflow-hidden pointer-events-none"></div>
|
|
<div id="sun-container" class="absolute top-0 left-0 w-full h-1/2 overflow-hidden pointer-events-none"></div>
|
|
<div id="moon-container" class="absolute top-0 left-0 w-full h-1/2 overflow-hidden pointer-events-none"></div>
|
|
|
|
<div id="theme-switcher" class="switcher absolute top-4 left-4 flex space-x-3">
|
|
<button id="theme-light" class="text-2xl">☀️</button>
|
|
<button id="theme-dark" class="text-2xl">🌙</button>
|
|
<button id="theme-system" class="text-2xl">💻</button>
|
|
</div>
|
|
|
|
<div class="switcher absolute top-4 right-4 flex space-x-3">
|
|
<button id="lang-pl" class="text-2xl">🇵🇱</button>
|
|
<button id="lang-en" class="text-2xl">🇬🇧</button>
|
|
</div>
|
|
|
|
<main id="content" class="animate-on-load text-center p-8 space-y-10">
|
|
|
|
<h1 data-translate-key="title" class="text-5xl md:text-7xl font-bold tracking-tight text-blue-900 dark:text-blue-200">
|
|
Paulina i Paweł Orzech
|
|
</h1>
|
|
<p id="current-time" class="text-lg md:text-xl text-gray-600 dark:text-gray-300 mb-4"></p>
|
|
|
|
<div class="flex flex-col md:flex-row items-center justify-center gap-4 md:gap-8 text-lg text-blue-800 dark:text-blue-300">
|
|
<a href="mailto:paulina@orzech.me" class="hover:text-blue-600 dark:hover:text-blue-400 hover:scale-105 transition-all duration-300">
|
|
paulina@orzech.me
|
|
</a>
|
|
<a href="mailto:pawel@orzech.me" class="hover:text-blue-600 dark:hover:text-blue-400 hover:scale-105 transition-all duration-300">
|
|
pawel@orzech.me
|
|
</a>
|
|
</div>
|
|
|
|
<div class="flex flex-col md:flex-row items-center justify-center gap-6 md:gap-8 text-lg pt-6">
|
|
<a href="https://pawel.orzech.me" target="_blank" rel="noopener noreferrer"
|
|
class="bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 px-8 py-4 rounded-2xl shadow-lg hover:shadow-xl dark:hover:bg-blue-800 hover:bg-blue-200 hover:scale-105 transition-all duration-300 font-semibold">
|
|
<span data-translate-key="pawel_blog">Blog Pawła</span>
|
|
</a>
|
|
<div class="bg-gray-200 dark:bg-gray-800 text-gray-500 dark:text-gray-400 px-8 py-4 rounded-2xl cursor-not-allowed shadow-md">
|
|
<span data-translate-key="paulina_page">Strona Pauliny</span>
|
|
</div>
|
|
</div>
|
|
|
|
</main>
|
|
|
|
<footer id="footer" class="animate-on-load absolute bottom-4 text-center w-full text-sm text-gray-500 dark:text-gray-400">
|
|
<p data-translate-key="copyright">© 2025 Paulina i Paweł Orzech. Wszelkie prawa zastrzeżone.</p>
|
|
</footer>
|
|
|
|
<script>
|
|
// --- TRANSLATION LOGIC ---
|
|
const translations = {
|
|
pl: {
|
|
title: "Paulina i Paweł Orzech",
|
|
pawel_blog: "Blog Pawła",
|
|
paulina_page: "Strona Pauliny",
|
|
copyright: "© 2025 Paulina i Paweł Orzech. Wszelkie prawa zastrzeżone.",
|
|
page_title: "Paulina i Paweł Orzech",
|
|
time_format: "HH:mm",
|
|
time_prefix: "Hej, aktualnie jest"
|
|
},
|
|
en: {
|
|
title: "Paulina & Paweł Orzech",
|
|
pawel_blog: "Paweł's Blog",
|
|
paulina_page: "Paulina's Page",
|
|
copyright: "© 2025 Paulina & Paweł Orzech. All rights reserved.",
|
|
page_title: "Paulina & Paweł Orzech",
|
|
time_format: "h:mm A",
|
|
time_prefix: "Hey, it's currently"
|
|
}
|
|
};
|
|
|
|
const updateTimeDisplay = () => {
|
|
const now = new Date();
|
|
const currentLang = document.documentElement.lang;
|
|
const timeFormat = translations[currentLang].time_format;
|
|
const timePrefix = translations[currentLang].time_prefix;
|
|
|
|
const options = {
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
hour12: timeFormat.includes('A') // Use 12-hour format if 'A' is in time_format
|
|
};
|
|
|
|
let formattedTime = now.toLocaleTimeString(currentLang, options);
|
|
|
|
// Determine emoji based on hour
|
|
const hour = now.getHours();
|
|
let emoji;
|
|
if (hour >= 6 && hour < 18) { // 6 AM to 5:59 PM
|
|
emoji = '☀️';
|
|
} else if (hour >= 18 && hour < 20) { // 6 PM to 7:59 PM (sunset)
|
|
emoji = '🌇';
|
|
} else { // 8 PM to 5:59 AM
|
|
emoji = '🌙';
|
|
}
|
|
|
|
document.getElementById('current-time').textContent = `${timePrefix} ${formattedTime} ${emoji}`;
|
|
};
|
|
|
|
const setLanguage = (lang) => {
|
|
document.documentElement.lang = lang;
|
|
document.title = translations[lang].page_title;
|
|
|
|
const elementsToTranslate = document.querySelectorAll('[data-translate-key]');
|
|
|
|
elementsToTranslate.forEach(el => {
|
|
const key = el.getAttribute('data-translate-key');
|
|
const newText = translations[lang][key];
|
|
|
|
if (el.innerHTML === newText) {
|
|
return; // No change, no animation needed
|
|
}
|
|
|
|
el.classList.add('fade-out');
|
|
setTimeout(() => {
|
|
el.innerHTML = newText;
|
|
el.classList.remove('fade-out');
|
|
el.classList.add('fade-in');
|
|
setTimeout(() => {
|
|
el.classList.remove('fade-in');
|
|
}, 300); // Remove fade-in class after animation
|
|
}, 300); // Wait for fade-out to complete
|
|
});
|
|
|
|
// Update time display when language changes
|
|
updateTimeDisplay();
|
|
|
|
document.getElementById('lang-pl').classList.toggle('active', lang === 'pl');
|
|
document.getElementById('lang-en').classList.toggle('active', lang === 'en');
|
|
localStorage.setItem('language', lang);
|
|
};
|
|
|
|
// --- THEME LOGIC ---
|
|
let shootingStarInterval; // Declare interval variable
|
|
|
|
const updateThemeButtons = (theme) => {
|
|
document.getElementById('theme-light').classList.toggle('active', theme === 'light');
|
|
document.getElementById('theme-dark').classList.toggle('active', theme === 'dark');
|
|
document.getElementById('theme-system').classList.toggle('active', theme === 'system');
|
|
};
|
|
|
|
const isNightTime = () => {
|
|
const hour = new Date().getHours();
|
|
return hour >= 20 || hour < 6; // 8 PM to 5:59 AM
|
|
};
|
|
|
|
const setTheme = (theme) => {
|
|
localStorage.setItem('theme', theme);
|
|
updateThemeButtons(theme);
|
|
applyTheme(); // Re-apply the theme logic
|
|
|
|
const isDarkMode = document.documentElement.classList.contains('dark');
|
|
const isCurrentlyNight = isNightTime();
|
|
|
|
if (isDarkMode && isCurrentlyNight) {
|
|
createStars();
|
|
startShootingStars();
|
|
toggleCelestialBodies(false, true);
|
|
} else if (!isDarkMode) {
|
|
removeStars();
|
|
stopShootingStars();
|
|
toggleCelestialBodies(true, false);
|
|
} else {
|
|
removeStars();
|
|
stopShootingStars();
|
|
toggleCelestialBodies(false, false);
|
|
}
|
|
};
|
|
|
|
// --- SUN LOGIC ---
|
|
const toggleCelestialBodies = (showSun, showMoon) => {
|
|
const sunContainer = document.getElementById('sun-container');
|
|
let sunElement = document.getElementById('animated-sun');
|
|
const moonContainer = document.getElementById('moon-container');
|
|
let moonElement = document.getElementById('animated-moon');
|
|
|
|
if (showSun) {
|
|
if (!sunElement) {
|
|
sunElement = document.createElement('div');
|
|
sunElement.id = 'animated-sun';
|
|
sunElement.className = 'sun';
|
|
sunContainer.appendChild(sunElement);
|
|
}
|
|
sunElement.classList.add('visible');
|
|
} else {
|
|
if (sunElement) {
|
|
sunElement.classList.remove('visible');
|
|
}
|
|
}
|
|
|
|
if (showMoon) {
|
|
if (!moonElement) {
|
|
moonElement = document.createElement('div');
|
|
moonElement.id = 'animated-moon';
|
|
moonElement.className = 'moon';
|
|
moonContainer.appendChild(moonElement);
|
|
}
|
|
moonElement.classList.add('visible');
|
|
} else {
|
|
if (moonElement) {
|
|
moonElement.classList.remove('visible');
|
|
}
|
|
}
|
|
};
|
|
|
|
// --- STAR ANIMATION LOGIC ---
|
|
const createStars = () => {
|
|
const starContainer = document.getElementById('star-container');
|
|
starContainer.innerHTML = ''; // Clear existing stars
|
|
const numStars = 25; // Further reduced number of stars
|
|
|
|
for (let i = 0; i < numStars; i++) {
|
|
const star = document.createElement('div');
|
|
star.className = 'star';
|
|
const size = Math.random() * 1.5 + 0.5; // 0.5 to 2px, even smaller
|
|
star.style.width = `${size}px`;
|
|
star.style.height = `${size}px`;
|
|
star.style.left = `${Math.random() * 100}%`;
|
|
star.style.top = `${Math.random() * 100}%`;
|
|
star.style.animationDelay = `${Math.random() * 8}s`; // Even longer random delay
|
|
star.style.animationDuration = `${Math.random() * 6 + 3}s`; // Even slower random duration
|
|
starContainer.appendChild(star);
|
|
}
|
|
};
|
|
|
|
const removeStars = () => {
|
|
const starContainer = document.getElementById('star-container');
|
|
starContainer.innerHTML = ''; // Remove all stars
|
|
};
|
|
|
|
// --- SHOOTING STAR LOGIC ---
|
|
const createShootingStar = () => {
|
|
const starContainer = document.getElementById('star-container');
|
|
const shootingStar = document.createElement('div');
|
|
shootingStar.className = 'shooting-star';
|
|
|
|
// Random start position (top-left quadrant for diagonal movement)
|
|
const startX = Math.random() * window.innerWidth * 0.7; // Up to 70% of width
|
|
const startY = Math.random() * window.innerHeight * 0.3; // Up to 30% of height
|
|
shootingStar.style.left = `${startX}px`;
|
|
shootingStar.style.top = `${startY}px`;
|
|
|
|
// Random duration for animation
|
|
shootingStar.style.animationDuration = `${Math.random() * 1.5 + 1}s`; // 1 to 2.5 seconds
|
|
shootingStar.style.animationDelay = `${Math.random() * 0.5}s`; // Small random delay
|
|
|
|
starContainer.appendChild(shootingStar);
|
|
|
|
// Remove the shooting star after its animation to prevent DOM clutter
|
|
shootingStar.addEventListener('animationend', () => {
|
|
shootingStar.remove();
|
|
});
|
|
};
|
|
|
|
const startShootingStars = () => {
|
|
if (!shootingStarInterval) {
|
|
shootingStarInterval = setInterval(() => {
|
|
if (document.documentElement.classList.contains('dark') && isNightTime()) {
|
|
createShootingStar();
|
|
} else {
|
|
stopShootingStars(); // Stop if conditions are no longer met
|
|
}
|
|
}, 30000); // Every 30 seconds
|
|
}
|
|
};
|
|
|
|
const stopShootingStars = () => {
|
|
if (shootingStarInterval) {
|
|
clearInterval(shootingStarInterval);
|
|
shootingStarInterval = null;
|
|
}
|
|
};
|
|
|
|
// --- ON LOAD INITIALIZATION ---
|
|
window.addEventListener('load', () => {
|
|
// Animate content
|
|
const content = document.getElementById('content');
|
|
const footer = document.getElementById('footer');
|
|
setTimeout(() => content.classList.add('animate-in'), 100);
|
|
setTimeout(() => footer.classList.add('animate-in'), 400);
|
|
|
|
// Initialize Language
|
|
const savedLang = localStorage.getItem('language') || 'pl';
|
|
setLanguage(savedLang);
|
|
|
|
// Initialize Theme
|
|
const savedTheme = localStorage.getItem('theme') || 'system';
|
|
updateThemeButtons(savedTheme);
|
|
// Call applyTheme and createStars/ShootingStars initially based on savedTheme and time
|
|
applyTheme();
|
|
// Initial check for stars, shooting stars, and sun based on current theme and time
|
|
const isDarkModeInitial = document.documentElement.classList.contains('dark');
|
|
const isCurrentlyNightInitial = isNightTime();
|
|
|
|
if (isDarkModeInitial && isCurrentlyNightInitial) {
|
|
createStars();
|
|
startShootingStars();
|
|
toggleCelestialBodies(false, true);
|
|
} else if (!isDarkModeInitial) {
|
|
removeStars();
|
|
stopShootingStars();
|
|
toggleCelestialBodies(true, false);
|
|
} else {
|
|
removeStars();
|
|
stopShootingStars();
|
|
toggleCelestialBodies(false, false);
|
|
}
|
|
|
|
// Add event listeners
|
|
document.getElementById('lang-pl').addEventListener('click', () => setLanguage('pl'));
|
|
document.getElementById('lang-en').addEventListener('click', () => setLanguage('en'));
|
|
document.getElementById('theme-light').addEventListener('click', () => setTheme('light'));
|
|
document.getElementById('theme-dark').addEventListener('click', () => setTheme('dark'));
|
|
document.getElementById('theme-system').addEventListener('click', () => setTheme('system'));
|
|
|
|
// Listen for system theme changes
|
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
|
|
if (localStorage.getItem('theme') === 'system') {
|
|
applyTheme();
|
|
const isDarkMode = document.documentElement.classList.contains('dark');
|
|
const isCurrentlyNight = isNightTime();
|
|
if (isDarkMode && isCurrentlyNight) {
|
|
createStars();
|
|
startShootingStars();
|
|
toggleCelestialBodies(false, true);
|
|
} else if (!isDarkMode) {
|
|
removeStars();
|
|
stopShootingStars();
|
|
toggleCelestialBodies(true, false);
|
|
} else {
|
|
removeStars();
|
|
stopShootingStars();
|
|
toggleCelestialBodies(false, false);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Update time every minute
|
|
setInterval(updateTimeDisplay, 60000);
|
|
});
|
|
</script>
|
|
|
|
</body>
|
|
</html> |