feat: implement dark mode with theme toggle
- Add dark mode support across all components and pages - Implement theme toggle button in navbar (desktop and mobile) - Fix desktop theme toggle icon visibility issue - Add dark mode color variants to all text, backgrounds and components - Update Tailwind config to enable class-based dark mode - Add dark mode support to prose styles for blog content - Persist theme preference in localStorage - Update all pages (index, bio, blog) with dark mode colors - Optimize images (me.png and me-baby.jpg)
This commit is contained in:
parent
7caf02fb36
commit
df04844ca2
16 changed files with 392 additions and 203 deletions
Binary file not shown.
|
Before Width: | Height: | Size: 210 KiB After Width: | Height: | Size: 210 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 90 KiB |
|
|
@ -5,29 +5,31 @@ import tailwindIcon from "../assets/tailwind.svg";
|
|||
import githubIcon from "../assets/github.svg";
|
||||
---
|
||||
|
||||
<div class="w-full bg-secondary py-2 px-4">
|
||||
<div class="max-w-6xl mx-auto flex justify-between flex-col-reverse sm:flex-row gap-4 sm:items-end items-center">
|
||||
<div class="text-center sm:text-left text-gray-800 flex flex-col items-center sm:items-start">
|
||||
<div class="flex flex-wrap items-center gap-3 text-sm">
|
||||
<span class="font-medium text-gray-800">Lorenzo Iovino</span>
|
||||
<span class="text-gray-800">•</span>
|
||||
<div class="w-full bg-secondary dark:bg-gray-800 py-8 px-4 transition-colors duration-200">
|
||||
<div
|
||||
class="max-w-6xl mx-auto flex justify-between flex-col-reverse sm:flex-row gap-4 sm:items-end items-center"
|
||||
>
|
||||
<div
|
||||
class="text-center sm:text-left text-gray-800 dark:text-gray-200 flex flex-col items-center sm:items-start"
|
||||
>
|
||||
<div class="flex flex-wrap items-center gap-1 md:gap-3 text-xs">
|
||||
<a
|
||||
href="/rss.xml"
|
||||
class="transition-colors duration-200 inline-flex items-center gap-1"
|
||||
class="transition-colors duration-200 inline-flex items-center gap-1 hover:text-gray-600 dark:hover:text-gray-300"
|
||||
title="RSS Feed"
|
||||
>
|
||||
RSS
|
||||
</a>
|
||||
<span class="text-gray-800">•</span>
|
||||
<span class="text-gray-800 dark:text-gray-400">•</span>
|
||||
<a
|
||||
href="/sitemap-index.xml"
|
||||
class="transition-colors duration-200"
|
||||
class="transition-colors duration-200 hover:text-gray-600 dark:hover:text-gray-300"
|
||||
title="Sitemap">Sitemap</a
|
||||
>
|
||||
<span class="text-gray-800">•</span>
|
||||
<span class="text-gray-800 dark:text-gray-400">•</span>
|
||||
<a
|
||||
href="https://www.iubenda.com/privacy-policy/98687046"
|
||||
class="transition-colors duration-200 iubenda-white iubenda-noiframe iubenda-embed iubenda-noiframe"
|
||||
class="transition-colors duration-200 iubenda-white iubenda-noiframe iubenda-embed iubenda-noiframe hover:text-gray-600 dark:hover:text-gray-300"
|
||||
title="Privacy Policy">Privacy Policy</a
|
||||
>
|
||||
<script is:inline type="text/javascript">
|
||||
|
|
@ -47,11 +49,11 @@ import githubIcon from "../assets/github.svg";
|
|||
}
|
||||
})(window, document);
|
||||
</script>
|
||||
<span class="text-gray-800">•</span>
|
||||
<span class="text-gray-800 dark:text-gray-400">•</span>
|
||||
|
||||
<a
|
||||
href="https://www.iubenda.com/privacy-policy/98687046/cookie-policy"
|
||||
class="transition-colors duration-200 iubenda-white iubenda-noiframe iubenda-embed iubenda-noiframe"
|
||||
class="transition-colors duration-200 iubenda-white iubenda-noiframe iubenda-embed iubenda-noiframe hover:text-gray-600 dark:hover:text-gray-300"
|
||||
title="Cookie Policy">Cookie Policy</a
|
||||
>
|
||||
<script is:inline type="text/javascript">
|
||||
|
|
@ -73,53 +75,14 @@ import githubIcon from "../assets/github.svg";
|
|||
</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center sm:text-right text-gray-800 text-sm">
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="flex items-center justify-center sm:justify-end gap-1 flex-wrap">
|
||||
<span>Made with</span>
|
||||
<a
|
||||
href="https://astro.build/"
|
||||
class="inline-flex items-center transition-all duration-200"
|
||||
aria-label="Astro"
|
||||
>
|
||||
<Image
|
||||
class="h-5 w-5"
|
||||
src={astroIcon}
|
||||
width={20}
|
||||
height={20}
|
||||
loading="lazy"
|
||||
alt=""
|
||||
/>
|
||||
</a>
|
||||
<span>and</span>
|
||||
<a
|
||||
href="https://tailwindcss.com/"
|
||||
class="inline-flex items-center center transition-all duration-200"
|
||||
aria-label="TailwindCSS"
|
||||
>
|
||||
<Image
|
||||
class="h-5 w-5"
|
||||
src={tailwindIcon}
|
||||
width={20}
|
||||
height={20}
|
||||
loading="lazy"
|
||||
alt=""
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<div class="text-center sm:text-right text-gray-800 dark:text-gray-200 text-xs">
|
||||
<div class="flex flex-col">
|
||||
<a
|
||||
href="https://github.com/thisloke/lorenzoiovino.com"
|
||||
class="transition-colors duration-200 inline-flex items-center justify-center sm:justify-end gap-2 font-medium"
|
||||
class="transition-colors duration-200 inline-flex items-center justify-center sm:justify-end gap-2 font-medium hover:text-gray-600 dark:hover:text-gray-300"
|
||||
>
|
||||
<span>Check the source code</span>
|
||||
<Image
|
||||
src={githubIcon}
|
||||
class="h-5 w-5"
|
||||
width={20}
|
||||
height={20}
|
||||
loading="lazy"
|
||||
alt=""
|
||||
/>
|
||||
<Image src={githubIcon} class="h-5 w-5" width={20} height={20} loading="lazy" alt="" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import githubIcon from "../assets/github.svg";
|
|||
<div class="relative px-6 lg:px-8">
|
||||
<div class="mx-auto max-w-5xl">
|
||||
<div
|
||||
class="relative overflow-hidden rounded-3xl bg-white shadow-soft-lg hover:shadow-soft-lg transition-shadow duration-300"
|
||||
class="relative overflow-hidden rounded-3xl bg-white dark:bg-gray-800 shadow-soft-lg hover:shadow-soft-lg transition-all duration-300"
|
||||
>
|
||||
<div class="relative flex flex-col md:flex-row items-center md:items-end">
|
||||
<!-- Photo Section -->
|
||||
|
|
@ -36,23 +36,29 @@ import githubIcon from "../assets/github.svg";
|
|||
<div class="space-y-4 md:space-y-6">
|
||||
<!-- Greeting -->
|
||||
<div class="text-center md:text-left">
|
||||
<h1 class="mb-3">
|
||||
Hey, I'm <span class="text-gray-900 font-extrabold">Lorenzo</span>!
|
||||
<h1 class="mb-3 text-gray-900 dark:text-gray-100">
|
||||
Hey, I'm <span class="text-gray-900 dark:text-gray-100 font-extrabold"
|
||||
>Lorenzo</span
|
||||
>!
|
||||
</h1>
|
||||
<div class="h-1 w-20 bg-secondary rounded-full mx-auto md:mx-0"></div>
|
||||
<div class="h-1 w-20 bg-secondary dark:bg-primary rounded-full mx-auto md:mx-0">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Description -->
|
||||
<div class="space-y-3 md:space-y-4">
|
||||
<p class="text-base md:text-lg text-gray-700">
|
||||
<p class="text-base md:text-lg text-gray-700 dark:text-gray-300">
|
||||
I'm on a quest to uncover life's meaning while diving deep into my passion for
|
||||
Computer Science as a
|
||||
<span class="font-semibold text-gray-900 relative inline-block">
|
||||
<span
|
||||
class="font-semibold text-gray-900 dark:text-gray-100 relative inline-block"
|
||||
>
|
||||
Software Engineer
|
||||
<span class="absolute bottom-0 left-0 w-full h-0.5 bg-secondary"></span>
|
||||
<span class="absolute bottom-0 left-0 w-full h-0.5 bg-secondary dark:bg-primary"
|
||||
></span>
|
||||
</span>
|
||||
</p>
|
||||
<p class="text-sm md:text-lg text-gray-600">
|
||||
<p class="text-sm md:text-lg text-gray-600 dark:text-gray-400">
|
||||
Here, on my personal website, I share my projects and occasional thoughts.
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -61,7 +67,7 @@ import githubIcon from "../assets/github.svg";
|
|||
<div class="flex gap-3 md:gap-4 pt-2 justify-center md:justify-end">
|
||||
<a
|
||||
href="https://www.linkedin.com/in/lorenzoiovino/"
|
||||
class="group relative p-3 bg-gray-50 hover:bg-secondary/10 rounded-xl transition-all duration-200 transform hover:-translate-y-1"
|
||||
class="group relative p-3 bg-gray-50 dark:bg-gray-700 hover:bg-secondary/10 dark:hover:bg-primary/10 rounded-xl transition-all duration-200 transform hover:-translate-y-1"
|
||||
aria-label="LinkedIn"
|
||||
>
|
||||
<Image
|
||||
|
|
@ -75,7 +81,7 @@ import githubIcon from "../assets/github.svg";
|
|||
</a>
|
||||
<a
|
||||
href="https://github.com/thisloke"
|
||||
class="group relative p-3 bg-gray-50 hover:bg-secondary/10 rounded-xl transition-all duration-200 transform hover:-translate-y-1"
|
||||
class="group relative p-3 bg-gray-50 dark:bg-gray-700 hover:bg-secondary/10 dark:hover:bg-primary/10 rounded-xl transition-all duration-200 transform hover:-translate-y-1"
|
||||
aria-label="GitHub"
|
||||
>
|
||||
<Image
|
||||
|
|
|
|||
|
|
@ -2,23 +2,55 @@
|
|||
const currentPath = Astro.url.pathname;
|
||||
---
|
||||
|
||||
<nav class="fixed top-0 left-0 right-0 z-50 bg-secondary shadow-sm">
|
||||
<nav
|
||||
class="fixed top-0 left-0 right-0 z-50 bg-secondary dark:bg-gray-800 shadow-sm transition-colors duration-200"
|
||||
>
|
||||
<div class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex justify-between items-center h-16">
|
||||
<!-- Logo/Brand -->
|
||||
<a
|
||||
href="/"
|
||||
class="text-xl font-bold text-gray-800 hover:text-gray-200 transition-colors duration-200"
|
||||
class="text-xl font-bold text-gray-800 dark:text-gray-100 hover:text-gray-600 dark:hover:text-gray-300 transition-colors duration-200"
|
||||
>
|
||||
Lorenzo Iovino
|
||||
</a>
|
||||
|
||||
<!-- Desktop Navigation -->
|
||||
<div class="hidden md:flex items-center space-x-8">
|
||||
<!-- Dark Mode Toggle -->
|
||||
<button
|
||||
id="theme-toggle"
|
||||
class="p-2 rounded-lg text-gray-800 dark:text-gray-200 hover:bg-white/20 dark:hover:bg-white/10 transition-colors duration-200"
|
||||
aria-label="Toggle dark mode"
|
||||
>
|
||||
<svg
|
||||
id="theme-toggle-dark-icon"
|
||||
class="theme-toggle-dark-icon w-5 h-5 hidden"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"></path>
|
||||
</svg>
|
||||
<svg
|
||||
id="theme-toggle-light-icon"
|
||||
class="theme-toggle-light-icon w-5 h-5 hidden"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<a
|
||||
href="/"
|
||||
class={`text-sm font-medium transition-colors duration-200 ${
|
||||
currentPath === "/" ? "text-gray-800 font-semibold" : "text-gray-800/80 hover:text-gray-800"
|
||||
currentPath === "/"
|
||||
? "text-gray-800 dark:text-gray-100 font-semibold"
|
||||
: "text-gray-800/80 dark:text-gray-300 hover:text-gray-800 dark:hover:text-gray-100"
|
||||
}`}
|
||||
>
|
||||
Home
|
||||
|
|
@ -26,7 +58,9 @@ const currentPath = Astro.url.pathname;
|
|||
<a
|
||||
href="/bio"
|
||||
class={`text-sm font-medium transition-colors duration-200 ${
|
||||
currentPath === "/bio" ? "text-gray-800 font-semibold" : "text-gray-800/80 hover:text-gray-800"
|
||||
currentPath === "/bio"
|
||||
? "text-gray-800 dark:text-gray-100 font-semibold"
|
||||
: "text-gray-800/80 dark:text-gray-300 hover:text-gray-800 dark:hover:text-gray-100"
|
||||
}`}
|
||||
>
|
||||
Bio
|
||||
|
|
@ -34,17 +68,48 @@ const currentPath = Astro.url.pathname;
|
|||
<a
|
||||
href="/blog"
|
||||
class={`text-sm font-medium transition-colors duration-200 ${
|
||||
currentPath === "/blog" ? "text-gray-800 font-semibold" : "text-gray-800/80 hover:text-gray-800"
|
||||
currentPath === "/blog"
|
||||
? "text-gray-800 dark:text-gray-100 font-semibold"
|
||||
: "text-gray-800/80 dark:text-gray-300 hover:text-gray-800 dark:hover:text-gray-100"
|
||||
}`}
|
||||
>
|
||||
Blog
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Mobile controls (theme toggle + menu button) -->
|
||||
<div class="md:hidden flex items-center space-x-2">
|
||||
<!-- Dark Mode Toggle (Mobile) -->
|
||||
<button
|
||||
id="theme-toggle-mobile"
|
||||
class="p-2 rounded-lg text-gray-800 dark:text-gray-200 hover:bg-white/20 dark:hover:bg-white/10 transition-colors duration-200"
|
||||
aria-label="Toggle dark mode"
|
||||
>
|
||||
<svg
|
||||
class="theme-toggle-dark-icon w-5 h-5 hidden"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"></path>
|
||||
</svg>
|
||||
<svg
|
||||
class="theme-toggle-light-icon w-5 h-5 hidden"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- Mobile menu button -->
|
||||
<button
|
||||
id="mobile-menu-button"
|
||||
class="md:hidden p-2 rounded-lg text-gray-800 hover:bg-white/10 focus:outline-none focus:ring-2 focus:ring-white/50"
|
||||
class="p-2 rounded-lg text-gray-800 dark:text-gray-200 hover:bg-white/10 dark:hover:bg-white/5 focus:outline-none focus:ring-2 focus:ring-white/50"
|
||||
aria-label="Toggle menu"
|
||||
>
|
||||
<svg
|
||||
|
|
@ -63,16 +128,20 @@ const currentPath = Astro.url.pathname;
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Navigation -->
|
||||
<div id="mobile-menu" class="hidden md:hidden bg-secondary">
|
||||
<div
|
||||
id="mobile-menu"
|
||||
class="hidden md:hidden bg-secondary dark:bg-gray-800 transition-colors duration-200"
|
||||
>
|
||||
<div class="px-4 py-3 space-y-3">
|
||||
<a
|
||||
href="/"
|
||||
class={`block px-3 py-2 rounded-lg text-base font-medium transition-colors duration-200 ${
|
||||
currentPath === "/"
|
||||
? "bg-white/20 text-gray-800 font-semibold"
|
||||
: "text-gray-800/80 hover:bg-white/10 hover:text-gray-800"
|
||||
? "bg-white/20 dark:bg-white/10 text-gray-800 dark:text-gray-100 font-semibold"
|
||||
: "text-gray-800/80 dark:text-gray-300 hover:bg-white/10 dark:hover:bg-white/5 hover:text-gray-800 dark:hover:text-gray-100"
|
||||
}`}
|
||||
>
|
||||
Home
|
||||
|
|
@ -81,8 +150,8 @@ const currentPath = Astro.url.pathname;
|
|||
href="/bio"
|
||||
class={`block px-3 py-2 rounded-lg text-base font-medium transition-colors duration-200 ${
|
||||
currentPath === "/bio"
|
||||
? "bg-white/20 text-gray-800 font-semibold"
|
||||
: "text-gray-800/80 hover:bg-white/10 hover:text-gray-800"
|
||||
? "bg-white/20 dark:bg-white/10 text-gray-800 dark:text-gray-100 font-semibold"
|
||||
: "text-gray-800/80 dark:text-gray-300 hover:bg-white/10 dark:hover:bg-white/5 hover:text-gray-800 dark:hover:text-gray-100"
|
||||
}`}
|
||||
>
|
||||
Bio
|
||||
|
|
@ -91,8 +160,8 @@ const currentPath = Astro.url.pathname;
|
|||
href="/blog"
|
||||
class={`block px-3 py-2 rounded-lg text-base font-medium transition-colors duration-200 ${
|
||||
currentPath === "/blog"
|
||||
? "bg-white/20 text-gray-800 font-semibold"
|
||||
: "text-gray-800/80 hover:bg-white/10 hover:text-gray-800"
|
||||
? "bg-white/20 dark:bg-white/10 text-gray-800 dark:text-gray-100 font-semibold"
|
||||
: "text-gray-800/80 dark:text-gray-300 hover:bg-white/10 dark:hover:bg-white/5 hover:text-gray-800 dark:hover:text-gray-100"
|
||||
}`}
|
||||
>
|
||||
Blog
|
||||
|
|
@ -102,10 +171,51 @@ const currentPath = Astro.url.pathname;
|
|||
</nav>
|
||||
|
||||
<script>
|
||||
// Mobile menu toggle
|
||||
const mobileMenuButton = document.getElementById("mobile-menu-button");
|
||||
const mobileMenu = document.getElementById("mobile-menu");
|
||||
|
||||
mobileMenuButton?.addEventListener("click", () => {
|
||||
mobileMenu?.classList.toggle("hidden");
|
||||
});
|
||||
|
||||
// Theme toggle functionality
|
||||
const themeToggleBtns = [
|
||||
document.getElementById("theme-toggle"),
|
||||
document.getElementById("theme-toggle-mobile"),
|
||||
];
|
||||
const themeToggleDarkIcons = document.querySelectorAll(".theme-toggle-dark-icon");
|
||||
const themeToggleLightIcons = document.querySelectorAll(".theme-toggle-light-icon");
|
||||
|
||||
// Check for saved theme preference or default to 'light' mode
|
||||
const currentTheme = localStorage.getItem("theme") || "light";
|
||||
|
||||
// Set initial theme and icons
|
||||
if (currentTheme === "dark") {
|
||||
document.documentElement.classList.add("dark");
|
||||
themeToggleLightIcons.forEach((icon) => icon.classList.remove("hidden"));
|
||||
themeToggleDarkIcons.forEach((icon) => icon.classList.add("hidden"));
|
||||
} else {
|
||||
document.documentElement.classList.remove("dark");
|
||||
themeToggleDarkIcons.forEach((icon) => icon.classList.remove("hidden"));
|
||||
themeToggleLightIcons.forEach((icon) => icon.classList.add("hidden"));
|
||||
}
|
||||
|
||||
// Toggle theme on button clicks
|
||||
themeToggleBtns.forEach((btn) => {
|
||||
btn?.addEventListener("click", () => {
|
||||
document.documentElement.classList.toggle("dark");
|
||||
|
||||
// Swap icons
|
||||
themeToggleDarkIcons.forEach((icon) => icon.classList.toggle("hidden"));
|
||||
themeToggleLightIcons.forEach((icon) => icon.classList.toggle("hidden"));
|
||||
|
||||
// Save theme preference
|
||||
if (document.documentElement.classList.contains("dark")) {
|
||||
localStorage.setItem("theme", "dark");
|
||||
} else {
|
||||
localStorage.setItem("theme", "light");
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -15,19 +15,21 @@ const {
|
|||
noHeight = false,
|
||||
} = Astro.props;
|
||||
|
||||
const bgClass = backgroundColor === "light" ? "bg-white" : "bg-secondary";
|
||||
const titleColorClass = titleColor === "light" ? "text-white" : "text-secondary-700";
|
||||
const bgClass =
|
||||
backgroundColor === "light" ? "bg-white dark:bg-gray-800" : "bg-secondary dark:bg-gray-900";
|
||||
const titleColorClass =
|
||||
titleColor === "light" ? "text-white dark:text-gray-100" : "text-gray-600 dark:text-gray-300";
|
||||
const heightClass = noHeight ? "h-[0px]" : "h-[150px]";
|
||||
---
|
||||
|
||||
<div
|
||||
class={`bg-center bg-no-repeat ${heightClass} ${bgClass}`}
|
||||
class={`bg-center bg-no-repeat ${heightClass} ${bgClass} transition-colors duration-200`}
|
||||
style={backgroundImageUrl ? `background-image: url(${backgroundImageUrl})` : ""}
|
||||
>
|
||||
{
|
||||
title && (
|
||||
<h2
|
||||
class={`mx-24 pt-8 text-6xl drop-shadow-2xl shadow-black text-gray-600 mr-6 font-extrabold ${titleColorClass}`}
|
||||
class={`mx-24 pt-8 text-6xl drop-shadow-2xl shadow-black mr-6 font-extrabold ${titleColorClass}`}
|
||||
>
|
||||
{title}
|
||||
</h2>
|
||||
|
|
@ -35,7 +37,7 @@ const heightClass = noHeight ? "h-[0px]" : "h-[150px]";
|
|||
}
|
||||
</div>
|
||||
<section
|
||||
class="max-w-4xl min-h-full py-4 px-6 mx-auto text-gray-700 flex flex-col items-center justify-center"
|
||||
class="max-w-4xl min-h-full py-4 px-6 mx-auto text-gray-700 dark:text-gray-300 flex flex-col items-center justify-center transition-colors duration-200"
|
||||
>
|
||||
<slot />
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
title: "Welcome to My Blog!"
|
||||
description: "First commit"
|
||||
pubDate: 2024-01-15
|
||||
pubDate: 2026-01-08
|
||||
heroImage: "../../assets/photos/remote.jpg"
|
||||
tags: ["personal", "welcome"]
|
||||
---
|
||||
|
|
@ -9,7 +9,7 @@ tags: ["personal", "welcome"]
|
|||
Hi, I’m Lorenzo.
|
||||
|
||||
I’ve been thinking about writing for a long time, and I finally decided to publish this blog.
|
||||
Not to be “a creator” or something like that — just to keep track of what I learn and share it with whoever finds it useful.
|
||||
Not to be “a creator” or something like that, just to keep track of what I learn and share it with whoever finds it useful.
|
||||
|
||||
## What you will find here
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,9 @@ const {
|
|||
const siteUrl = "https://lorenzoiovino.com";
|
||||
const defaultImage = mePhoto.src;
|
||||
const fullImageUrl = image
|
||||
? (image.startsWith("http") ? image : `${siteUrl}${image}`)
|
||||
? image.startsWith("http")
|
||||
? image
|
||||
: `${siteUrl}${image}`
|
||||
: `${siteUrl}${defaultImage}`;
|
||||
const fullCanonicalUrl = canonicalUrl || `${siteUrl}${Astro.url.pathname}`;
|
||||
---
|
||||
|
|
@ -171,12 +173,16 @@ const fullCanonicalUrl = canonicalUrl || `${siteUrl}${Astro.url.pathname}`;
|
|||
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-family: "Inter";
|
||||
font-style: normal;
|
||||
font-weight: 400 800;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v13/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZ9hiA.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v13/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZ9hiA.woff2)
|
||||
format("woff2");
|
||||
unicode-range:
|
||||
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308,
|
||||
U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
|
||||
U+FFFD;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
@ -184,7 +190,7 @@ const fullCanonicalUrl = canonicalUrl || `${siteUrl}${Astro.url.pathname}`;
|
|||
<link rel="manifest" href="/manifest.json" />
|
||||
<title>{title}</title>
|
||||
</head>
|
||||
<body class="bg-secondary min-h-screen">
|
||||
<body class="bg-white dark:bg-gray-900 min-h-screen transition-colors duration-200">
|
||||
<slot />
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -20,33 +20,34 @@ if (!bioEntry) {
|
|||
const { Content } = await bioEntry.render();
|
||||
---
|
||||
|
||||
<BaseLayout
|
||||
title="Lorenzo Iovino >> Bio"
|
||||
description="Biography and life story of Lorenzo Iovino"
|
||||
>
|
||||
<BaseLayout title="Lorenzo Iovino >> Bio" description="Biography and life story of Lorenzo Iovino">
|
||||
<Navbar />
|
||||
<div class="min-h-screen pt-16 bg-secondary">
|
||||
<div class="min-h-screen pt-16 bg-secondary dark:bg-gray-900 transition-colors duration-200">
|
||||
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-20">
|
||||
<div class="mb-16 text-center">
|
||||
<h1 class="text-gray-800 mb-4">{bioEntry.data.title}</h1>
|
||||
<p class="text-xl text-gray-700 max-w-2xl mx-auto">
|
||||
<h1 class="text-gray-800 dark:text-gray-100 mb-4">{bioEntry.data.title}</h1>
|
||||
<p class="text-xl text-gray-700 dark:text-gray-300 max-w-2xl mx-auto">
|
||||
{bioEntry.data.description}
|
||||
</p>
|
||||
<div class="h-1 w-20 bg-gray-600 rounded-full mx-auto mt-6"></div>
|
||||
<div class="h-1 w-20 bg-gray-600 dark:bg-primary rounded-full mx-auto mt-6"></div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-2xl shadow-soft-lg p-8 md:p-12 lg:p-16">
|
||||
<div
|
||||
class="bg-white dark:bg-gray-800 rounded-2xl shadow-soft-lg p-8 md:p-12 lg:p-16 transition-colors duration-200"
|
||||
>
|
||||
<div class="prose prose-xl max-w-none">
|
||||
<Content />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="mt-16 bg-gradient-to-br from-white to-gray-50 rounded-2xl shadow-soft-lg p-10 md:p-14 lg:p-16 text-center border border-gray-100"
|
||||
class="mt-16 bg-gradient-to-br from-white to-gray-50 dark:from-gray-800 dark:to-gray-700 rounded-2xl shadow-soft-lg p-10 md:p-14 lg:p-16 text-center border border-gray-100 dark:border-gray-600 transition-colors duration-200"
|
||||
>
|
||||
<h3 class="text-3xl md:text-4xl font-bold text-gray-900 mb-6">Let's Connect!</h3>
|
||||
<h3 class="text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100 mb-6">
|
||||
Let's Connect!
|
||||
</h3>
|
||||
<p
|
||||
class="text-gray-700 text-xl md:text-2xl leading-relaxed mb-10 max-w-3xl mx-auto font-light"
|
||||
class="text-gray-700 dark:text-gray-300 text-xl md:text-2xl leading-relaxed mb-10 max-w-3xl mx-auto font-light"
|
||||
>
|
||||
I'm always excited to connect with fellow developers, discuss interesting projects, or
|
||||
just have a chat about technology and life. Feel free to reach out!
|
||||
|
|
@ -56,7 +57,7 @@ const { Content } = await bioEntry.render();
|
|||
href="https://www.linkedin.com/in/lorenzoiovino/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="inline-flex items-center px-10 py-5 bg-white hover:bg-gray-50 text-gray-700 font-semibold text-lg rounded-xl transition-all duration-200 shadow-lg hover:shadow-xl border-2 border-gray-200 hover:border-gray-300 transform hover:-translate-y-1 hover:scale-105"
|
||||
class="inline-flex items-center px-10 py-5 bg-white dark:bg-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-200 font-semibold text-lg rounded-xl transition-all duration-200 shadow-lg hover:shadow-xl border-2 border-gray-200 dark:border-gray-600 hover:border-gray-300 dark:hover:border-gray-500 transform hover:-translate-y-1 hover:scale-105"
|
||||
>
|
||||
<Image
|
||||
src={linkedinIcon}
|
||||
|
|
@ -71,15 +72,9 @@ const { Content } = await bioEntry.render();
|
|||
href="https://github.com/thisloke"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="inline-flex items-center px-10 py-5 bg-white hover:bg-gray-50 text-gray-700 font-semibold text-lg rounded-xl transition-all duration-200 shadow-lg hover:shadow-xl border-2 border-gray-200 hover:border-gray-300 transform hover:-translate-y-1 hover:scale-105"
|
||||
class="inline-flex items-center px-10 py-5 bg-white dark:bg-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-200 font-semibold text-lg rounded-xl transition-all duration-200 shadow-lg hover:shadow-xl border-2 border-gray-200 dark:border-gray-600 hover:border-gray-300 dark:hover:border-gray-500 transform hover:-translate-y-1 hover:scale-105"
|
||||
>
|
||||
<Image
|
||||
src={githubIcon}
|
||||
alt=""
|
||||
class="h-6 w-6 mr-3 opacity-80"
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
<Image src={githubIcon} alt="" class="h-6 w-6 mr-3 opacity-80" width={24} height={24} />
|
||||
GitHub
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ const allTags: Record<string, number> = sortedPosts.reduce(
|
|||
});
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, number>,
|
||||
{} as Record<string, number>
|
||||
);
|
||||
|
||||
const sortedTags: TagCount[] = Object.entries(allTags)
|
||||
|
|
@ -31,31 +31,30 @@ const sortedTags: TagCount[] = Object.entries(allTags)
|
|||
const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
|
||||
---
|
||||
|
||||
<BaseLayout
|
||||
title="Lorenzo Iovino >> Blog"
|
||||
description="Blog posts and articles by Lorenzo Iovino"
|
||||
>
|
||||
<BaseLayout title="Lorenzo Iovino >> Blog" description="Blog posts and articles by Lorenzo Iovino">
|
||||
<Navbar />
|
||||
<div class="min-h-screen pt-16 bg-secondary">
|
||||
<div class="min-h-screen pt-16 bg-secondary dark:bg-gray-900 transition-colors duration-200">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-20">
|
||||
<div class="mb-16 text-center">
|
||||
<h1 class="text-gray-800 mb-4">Blog</h1>
|
||||
<p class="text-xl text-gray-800/90 max-w-2xl mx-auto">
|
||||
<h1 class="text-gray-800 dark:text-gray-100 mb-4">Blog</h1>
|
||||
<p class="text-xl text-gray-800/90 dark:text-gray-300 max-w-2xl mx-auto">
|
||||
Thoughts, experiences, and insights about software engineering, technology, and life
|
||||
</p>
|
||||
<div class="h-1 w-20 bg-gray-800 rounded-full mx-auto mt-6"></div>
|
||||
<div class="h-1 w-20 bg-gray-800 dark:bg-primary rounded-full mx-auto mt-6"></div>
|
||||
</div>
|
||||
|
||||
<div class="mb-12">
|
||||
<div class="bg-white rounded-2xl shadow-soft-lg overflow-hidden">
|
||||
<div
|
||||
class="bg-white dark:bg-gray-800 rounded-2xl shadow-soft-lg overflow-hidden transition-colors duration-200"
|
||||
>
|
||||
<button
|
||||
id="toggle-tags"
|
||||
class="w-full p-6 flex items-center justify-between hover:bg-gray-50 transition-colors duration-200"
|
||||
class="w-full p-6 flex items-center justify-between hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors duration-200"
|
||||
>
|
||||
<div class="flex items-center gap-3">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6 text-secondary-700"
|
||||
class="h-6 w-6 text-secondary-700 dark:text-primary"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
|
|
@ -67,12 +66,12 @@ const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
|
|||
d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"
|
||||
></path>
|
||||
</svg>
|
||||
<h2 class="text-xl font-bold text-gray-900">Filter by Topic</h2>
|
||||
<h2 class="text-xl font-bold text-gray-900 dark:text-gray-100">Filter by Topic</h2>
|
||||
</div>
|
||||
<svg
|
||||
id="chevron-icon"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6 text-gray-400 transition-transform duration-200"
|
||||
class="h-6 w-6 text-gray-400 dark:text-gray-500 transition-transform duration-200"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
|
|
@ -88,7 +87,7 @@ const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
|
|||
<div class="flex flex-wrap gap-2">
|
||||
<a
|
||||
href="/blog"
|
||||
class="inline-block px-4 py-2 bg-secondary text-white rounded-lg font-medium transition-all duration-200 hover:bg-secondary/90"
|
||||
class="inline-block px-4 py-2 bg-secondary dark:bg-primary text-white dark:text-gray-900 rounded-lg font-medium transition-all duration-200 hover:bg-secondary/90 dark:hover:bg-primary/90"
|
||||
>
|
||||
All Posts ({sortedPosts.length})
|
||||
</a>
|
||||
|
|
@ -96,7 +95,7 @@ const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
|
|||
sortedTags.map(({ tag, count }: TagCount) => (
|
||||
<a
|
||||
href={`/blog/tag/${tag}`}
|
||||
class="inline-block px-4 py-2 bg-gray-100 text-gray-700 rounded-lg font-medium transition-all duration-200 hover:bg-secondary hover:text-white"
|
||||
class="inline-block px-4 py-2 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-200 rounded-lg font-medium transition-all duration-200 hover:bg-secondary hover:text-white dark:hover:bg-primary dark:hover:text-gray-900"
|
||||
>
|
||||
{tag} ({count})
|
||||
</a>
|
||||
|
|
@ -110,7 +109,7 @@ const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
|
|||
<div id="posts-container" class="space-y-8">
|
||||
{
|
||||
initialPosts.map((post: BlogPost) => (
|
||||
<article class="bg-white rounded-2xl shadow-soft-lg overflow-hidden hover:shadow-soft-lg transition-shadow duration-300">
|
||||
<article class="bg-white dark:bg-gray-800 rounded-2xl shadow-soft-lg overflow-hidden hover:shadow-soft-lg transition-all duration-300">
|
||||
<div class="block">
|
||||
{post.data.heroImage && (
|
||||
<a href={`/blog/${post.slug}`} class="block">
|
||||
|
|
@ -129,7 +128,7 @@ const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
|
|||
</a>
|
||||
)}
|
||||
<div class="p-8">
|
||||
<div class="flex items-center gap-3 mb-4 text-sm text-gray-500">
|
||||
<div class="flex items-center gap-3 mb-4 text-sm text-gray-500 dark:text-gray-400">
|
||||
<time datetime={post.data.pubDate.toISOString()}>
|
||||
{post.data.pubDate.toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
|
|
@ -144,7 +143,7 @@ const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
|
|||
{post.data.tags.slice(0, 3).map((tag: string) => (
|
||||
<a
|
||||
href={`/blog/tag/${tag}`}
|
||||
class="px-2 py-1 bg-secondary/10 text-gray-800 text-xs rounded-lg font-medium hover:bg-secondary hover:text-white transition-all duration-200"
|
||||
class="px-2 py-1 bg-secondary/10 dark:bg-primary/20 text-gray-800 dark:text-gray-200 text-xs rounded-lg font-medium hover:bg-secondary hover:text-white dark:hover:bg-primary dark:hover:text-gray-900 transition-all duration-200"
|
||||
>
|
||||
{tag}
|
||||
</a>
|
||||
|
|
@ -154,13 +153,13 @@ const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
|
|||
)}
|
||||
</div>
|
||||
<a href={`/blog/${post.slug}`} class="block group">
|
||||
<h2 class="text-2xl md:text-3xl font-bold text-gray-900 mb-3 group-hover:text-secondary-700 transition-colors">
|
||||
<h2 class="text-2xl md:text-3xl font-bold text-gray-900 dark:text-gray-100 mb-3 group-hover:text-secondary-700 dark:group-hover:text-primary transition-colors">
|
||||
{post.data.title}
|
||||
</h2>
|
||||
<p class="text-gray-700 text-lg leading-relaxed mb-4">
|
||||
<p class="text-gray-700 dark:text-gray-300 text-lg leading-relaxed mb-4">
|
||||
{post.data.description}
|
||||
</p>
|
||||
<div class="flex items-center text-secondary-800 font-medium">
|
||||
<div class="flex items-center text-secondary-800 dark:text-primary font-medium">
|
||||
Read more
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
@ -185,14 +184,14 @@ const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
|
|||
|
||||
<div id="loading" class="hidden text-center py-8">
|
||||
<div
|
||||
class="inline-block animate-spin rounded-full h-12 w-12 border-4 border-white border-t-transparent"
|
||||
class="inline-block animate-spin rounded-full h-12 w-12 border-4 border-gray-800 dark:border-gray-200 border-t-transparent"
|
||||
>
|
||||
</div>
|
||||
<p class="text-white mt-4">Loading more posts...</p>
|
||||
<p class="text-gray-800 dark:text-gray-200 mt-4">Loading more posts...</p>
|
||||
</div>
|
||||
|
||||
<div id="end-message" class="hidden text-center py-8">
|
||||
<p class="text-white/80 text-lg">You've reached the end! 🎉</p>
|
||||
<p class="text-gray-800/80 dark:text-gray-200/80 text-lg">You've reached the end! 🎉</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -211,7 +210,7 @@ const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
|
|||
function createPostElement(post) {
|
||||
const article = document.createElement("article");
|
||||
article.className =
|
||||
"bg-white rounded-2xl shadow-soft-lg overflow-hidden hover:shadow-soft-lg transition-shadow duration-300";
|
||||
"bg-white dark:bg-gray-800 rounded-2xl shadow-soft-lg overflow-hidden hover:shadow-soft-lg transition-all duration-300";
|
||||
|
||||
const heroImageHTML = post.data.heroImage
|
||||
? `
|
||||
|
|
@ -237,7 +236,7 @@ const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
|
|||
.slice(0, 3)
|
||||
.map(
|
||||
(tag) =>
|
||||
`<a href="/blog/tag/${tag}" class="px-2 py-1 bg-secondary/10 text-secondary-700 text-xs rounded-lg font-medium hover:bg-secondary hover:text-white transition-all duration-200">${tag}</a>`
|
||||
`<a href="/blog/tag/${tag}" class="px-2 py-1 bg-secondary/10 dark:bg-primary/20 text-gray-800 dark:text-gray-200 text-xs rounded-lg font-medium hover:bg-secondary hover:text-white dark:hover:bg-primary dark:hover:text-gray-900 transition-all duration-200">${tag}</a>`
|
||||
)
|
||||
.join("")}
|
||||
</div>
|
||||
|
|
@ -255,20 +254,20 @@ const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
|
|||
<div class="block">
|
||||
${heroImageHTML}
|
||||
<div class="p-8">
|
||||
<div class="flex items-center gap-3 mb-4 text-sm text-gray-500">
|
||||
<div class="flex items-center gap-3 mb-4 text-sm text-gray-500 dark:text-gray-400">
|
||||
<time datetime="${pubDate.toISOString()}">
|
||||
${formattedDate}
|
||||
</time>
|
||||
${tagsHTML}
|
||||
</div>
|
||||
<a href="/blog/${post.slug}" class="block group">
|
||||
<h2 class="text-2xl md:text-3xl font-bold text-gray-900 mb-3 group-hover:text-secondary-700 transition-colors">
|
||||
<h2 class="text-2xl md:text-3xl font-bold text-gray-900 dark:text-gray-100 mb-3 group-hover:text-secondary-700 dark:group-hover:text-primary transition-colors">
|
||||
${post.data.title}
|
||||
</h2>
|
||||
<p class="text-gray-700 text-lg leading-relaxed mb-4">
|
||||
<p class="text-gray-700 dark:text-gray-300 text-lg leading-relaxed mb-4">
|
||||
${post.data.description}
|
||||
</p>
|
||||
<div class="flex items-center text-secondary font-medium">
|
||||
<div class="flex items-center text-secondary dark:text-primary font-medium">
|
||||
Read more
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
|
|||
|
|
@ -35,10 +35,13 @@ const heroImageSrc = entry.data.heroImage?.src || mePhoto.src;
|
|||
tags={entry.data.tags}
|
||||
>
|
||||
<Navbar />
|
||||
<div class="min-h-screen pt-16 bg-gray-50">
|
||||
<div class="min-h-screen pt-16 bg-gray-50 dark:bg-gray-900 transition-colors duration-200">
|
||||
<article class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8 md:py-12">
|
||||
<header class="mb-8 md:mb-12">
|
||||
<time datetime={entry.data.pubDate.toISOString()} class="block mb-3 text-sm text-gray-600">
|
||||
<time
|
||||
datetime={entry.data.pubDate.toISOString()}
|
||||
class="block mb-3 text-sm text-gray-600 dark:text-gray-400"
|
||||
>
|
||||
{
|
||||
entry.data.pubDate.toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
|
|
@ -53,7 +56,7 @@ const heroImageSrc = entry.data.heroImage?.src || mePhoto.src;
|
|||
{entry.data.tags.map((tag: string) => (
|
||||
<a
|
||||
href={`/blog/tag/${tag}`}
|
||||
class="px-3 py-1.5 bg-gray-100 text-gray-700 text-xs rounded-lg font-medium hover:bg-gray-900 hover:text-white transition-all duration-200"
|
||||
class="px-3 py-1.5 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-200 text-xs rounded-lg font-medium hover:bg-gray-900 hover:text-white dark:hover:bg-primary dark:hover:text-gray-900 transition-all duration-200"
|
||||
>
|
||||
{tag}
|
||||
</a>
|
||||
|
|
@ -61,10 +64,12 @@ const heroImageSrc = entry.data.heroImage?.src || mePhoto.src;
|
|||
</div>
|
||||
)
|
||||
}
|
||||
<h1 class="text-gray-900 mb-4 text-3xl md:text-4xl lg:text-5xl font-bold leading-tight">
|
||||
<h1
|
||||
class="text-gray-900 dark:text-gray-100 mb-4 text-3xl md:text-4xl lg:text-5xl font-bold leading-tight"
|
||||
>
|
||||
{entry.data.title}
|
||||
</h1>
|
||||
<p class="text-lg md:text-xl text-gray-600 leading-relaxed mb-6">
|
||||
<p class="text-lg md:text-xl text-gray-600 dark:text-gray-400 leading-relaxed mb-6">
|
||||
{entry.data.description}
|
||||
</p>
|
||||
</header>
|
||||
|
|
@ -87,13 +92,17 @@ const heroImageSrc = entry.data.heroImage?.src || mePhoto.src;
|
|||
)
|
||||
}
|
||||
|
||||
<div class="bg-white rounded-xl md:rounded-2xl shadow-xl p-6 sm:p-8 md:p-12 lg:p-16">
|
||||
<div
|
||||
class="bg-white dark:bg-gray-800 rounded-xl md:rounded-2xl shadow-xl p-6 sm:p-8 md:p-12 lg:p-16 transition-colors duration-200"
|
||||
>
|
||||
<div class="prose prose-xl max-w-none">
|
||||
<Content />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 md:mt-12 bg-white rounded-xl md:rounded-2xl shadow-lg p-6 sm:p-8">
|
||||
<div
|
||||
class="mt-8 md:mt-12 bg-white dark:bg-gray-800 rounded-xl md:rounded-2xl shadow-lg p-6 sm:p-8 transition-colors duration-200"
|
||||
>
|
||||
<div class="flex items-start gap-6">
|
||||
<Image
|
||||
src={mePhoto}
|
||||
|
|
@ -105,8 +114,10 @@ const heroImageSrc = entry.data.heroImage?.src || mePhoto.src;
|
|||
format="webp"
|
||||
/>
|
||||
<div>
|
||||
<h3 class="text-lg md:text-xl font-bold text-gray-900 mb-2">Lorenzo Iovino</h3>
|
||||
<p class="text-sm md:text-base text-gray-700 mb-4">
|
||||
<h3 class="text-lg md:text-xl font-bold text-gray-900 dark:text-gray-100 mb-2">
|
||||
Lorenzo Iovino
|
||||
</h3>
|
||||
<p class="text-sm md:text-base text-gray-700 dark:text-gray-300 mb-4">
|
||||
Software Engineer based in Sicily, passionate about technology, remote work, and life
|
||||
balance. When not coding, you'll find me working on my vineyard or exploring the
|
||||
beautiful Sicilian countryside.
|
||||
|
|
@ -116,7 +127,7 @@ const heroImageSrc = entry.data.heroImage?.src || mePhoto.src;
|
|||
href="https://www.linkedin.com/in/lorenzoiovino/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-gray-600 hover:text-gray-900 transition-colors"
|
||||
class="text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200 transition-colors"
|
||||
aria-label="Visit Lorenzo Iovino's LinkedIn profile"
|
||||
>
|
||||
<svg class="w-5 h-5 md:w-6 md:h-6" fill="currentColor" viewBox="0 0 24 24">
|
||||
|
|
@ -129,7 +140,7 @@ const heroImageSrc = entry.data.heroImage?.src || mePhoto.src;
|
|||
href="https://github.com/thisloke"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-gray-600 hover:text-gray-900 transition-colors"
|
||||
class="text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200 transition-colors"
|
||||
aria-label="Visit Lorenzo Iovino's GitHub profile"
|
||||
>
|
||||
<svg class="w-5 h-5 md:w-6 md:h-6" fill="currentColor" viewBox="0 0 24 24">
|
||||
|
|
@ -146,7 +157,7 @@ const heroImageSrc = entry.data.heroImage?.src || mePhoto.src;
|
|||
<div class="mt-8 md:mt-12 text-center">
|
||||
<a
|
||||
href="/blog"
|
||||
class="inline-flex items-center px-5 py-2.5 md:px-6 md:py-3 bg-white hover:bg-gray-50 text-gray-900 font-medium rounded-lg md:rounded-xl transition-all duration-200 shadow-md hover:shadow-lg border border-gray-200"
|
||||
class="inline-flex items-center px-5 py-2.5 md:px-6 md:py-3 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-900 dark:text-gray-100 font-medium rounded-lg md:rounded-xl transition-all duration-200 shadow-md hover:shadow-lg border border-gray-200 dark:border-gray-700"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
|
|||
|
|
@ -41,12 +41,12 @@ const displayTag: string = tag.charAt(0).toUpperCase() + tag.slice(1);
|
|||
description={`Browse all blog posts about ${displayTag}. ${posts.length} article${posts.length !== 1 ? "s" : ""} found.`}
|
||||
>
|
||||
<Navbar />
|
||||
<div class="min-h-screen pt-16 bg-secondary">
|
||||
<div class="min-h-screen pt-16 bg-secondary dark:bg-gray-900 transition-colors duration-200">
|
||||
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-20">
|
||||
<div class="mb-16 text-center">
|
||||
<a
|
||||
href="/blog"
|
||||
class="inline-flex items-center text-white/80 hover:text-white transition-colors mb-6"
|
||||
class="inline-flex items-center text-gray-800/80 dark:text-gray-200/80 hover:text-gray-800 dark:hover:text-gray-200 transition-colors mb-6"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
@ -64,7 +64,7 @@ const displayTag: string = tag.charAt(0).toUpperCase() + tag.slice(1);
|
|||
<div class="flex items-center justify-center gap-3 mb-4">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-8 w-8 text-white"
|
||||
class="h-8 w-8 text-gray-800 dark:text-gray-200"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
|
|
@ -76,18 +76,18 @@ const displayTag: string = tag.charAt(0).toUpperCase() + tag.slice(1);
|
|||
d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"
|
||||
></path>
|
||||
</svg>
|
||||
<h1 class="text-white mb-0">{displayTag}</h1>
|
||||
<h1 class="text-gray-800 dark:text-gray-100 mb-0">{displayTag}</h1>
|
||||
</div>
|
||||
<p class="text-xl text-white/90 max-w-2xl mx-auto">
|
||||
<p class="text-xl text-gray-800/90 dark:text-gray-300 max-w-2xl mx-auto">
|
||||
{posts.length} article{posts.length !== 1 ? "s" : ""} about {displayTag.toLowerCase()}
|
||||
</p>
|
||||
<div class="h-1 w-20 bg-white rounded-full mx-auto mt-6"></div>
|
||||
<div class="h-1 w-20 bg-gray-800 dark:bg-primary rounded-full mx-auto mt-6"></div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-8">
|
||||
{
|
||||
posts.map((post: BlogPost) => (
|
||||
<article class="bg-white rounded-2xl shadow-soft-lg overflow-hidden hover:shadow-soft-lg transition-shadow duration-300">
|
||||
<article class="bg-white dark:bg-gray-800 rounded-2xl shadow-soft-lg overflow-hidden hover:shadow-soft-lg transition-all duration-300">
|
||||
<div class="block">
|
||||
{post.data.heroImage && (
|
||||
<a href={`/blog/${post.slug}`} class="block">
|
||||
|
|
@ -106,7 +106,7 @@ const displayTag: string = tag.charAt(0).toUpperCase() + tag.slice(1);
|
|||
</a>
|
||||
)}
|
||||
<div class="p-8">
|
||||
<div class="flex items-center gap-3 mb-4 text-sm text-gray-500">
|
||||
<div class="flex items-center gap-3 mb-4 text-sm text-gray-500 dark:text-gray-400">
|
||||
<time datetime={post.data.pubDate.toISOString()}>
|
||||
{post.data.pubDate.toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
|
|
@ -135,13 +135,13 @@ const displayTag: string = tag.charAt(0).toUpperCase() + tag.slice(1);
|
|||
)}
|
||||
</div>
|
||||
<a href={`/blog/${post.slug}`} class="block group">
|
||||
<h2 class="text-2xl md:text-3xl font-bold text-gray-900 mb-3 group-hover:text-secondary-700 transition-colors">
|
||||
<h2 class="text-2xl md:text-3xl font-bold text-gray-900 dark:text-gray-100 mb-3 group-hover:text-secondary-700 dark:group-hover:text-primary transition-colors">
|
||||
{post.data.title}
|
||||
</h2>
|
||||
<p class="text-gray-700 text-lg leading-relaxed mb-4">
|
||||
<p class="text-gray-700 dark:text-gray-300 text-lg leading-relaxed mb-4">
|
||||
{post.data.description}
|
||||
</p>
|
||||
<div class="flex items-center text-secondary-700 font-medium">
|
||||
<div class="flex items-center text-secondary-700 dark:text-primary font-medium">
|
||||
Read more
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
@ -166,10 +166,10 @@ const displayTag: string = tag.charAt(0).toUpperCase() + tag.slice(1);
|
|||
|
||||
{
|
||||
posts.length === 0 && (
|
||||
<div class="bg-white rounded-2xl shadow-soft-lg p-12 text-center">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-2xl shadow-soft-lg p-12 text-center transition-colors duration-200">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-16 w-16 mx-auto text-gray-300 mb-4"
|
||||
class="h-16 w-16 mx-auto text-gray-300 dark:text-gray-600 mb-4"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
|
|
@ -181,7 +181,7 @@ const displayTag: string = tag.charAt(0).toUpperCase() + tag.slice(1);
|
|||
d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
<p class="text-gray-600 text-lg">No posts found with this tag.</p>
|
||||
<p class="text-gray-600 dark:text-gray-400 text-lg">No posts found with this tag.</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,19 +6,18 @@ import Navbar from "../components/Navbar.astro";
|
|||
import BaseLayout from "../layouts/BaseLayout.astro";
|
||||
---
|
||||
|
||||
<BaseLayout
|
||||
title="Lorenzo Iovino >> Homepage "
|
||||
description="Lorenzo Iovino - Software Engineer"
|
||||
>
|
||||
<BaseLayout title="Lorenzo Iovino >> Homepage " description="Lorenzo Iovino - Software Engineer">
|
||||
<Navbar />
|
||||
<div class="bg-secondary min-h-screen flex flex-col relative pt-16">
|
||||
<div
|
||||
class="bg-secondary dark:bg-gray-900 min-h-screen flex flex-col relative pt-16 transition-colors duration-200"
|
||||
>
|
||||
<div class="flex-grow flex items-center justify-center relative z-10 py-12">
|
||||
<Hero />
|
||||
</div>
|
||||
<div class="relative">
|
||||
<div
|
||||
class="absolute bottom-0 left-0 right-0 pointer-events-none z-20"
|
||||
style="height: 100px; margin-bottom: 0;"
|
||||
style="height: 110px; margin-bottom: 0;"
|
||||
>
|
||||
<Fish />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -12,8 +12,11 @@
|
|||
|
||||
/* Typography base */
|
||||
body {
|
||||
@apply font-sans text-base text-gray-900;
|
||||
font-feature-settings: "kern" 1, "liga" 1, "calt" 1;
|
||||
@apply font-sans text-base text-gray-900 dark:text-gray-100;
|
||||
font-feature-settings:
|
||||
"kern" 1,
|
||||
"liga" 1,
|
||||
"calt" 1;
|
||||
}
|
||||
|
||||
/* Headings ottimizzati */
|
||||
|
|
@ -23,8 +26,12 @@
|
|||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
@apply font-bold;
|
||||
font-feature-settings: "kern" 1, "liga" 1, "calt" 1, "ss01" 1;
|
||||
@apply font-bold text-gray-900 dark:text-gray-100;
|
||||
font-feature-settings:
|
||||
"kern" 1,
|
||||
"liga" 1,
|
||||
"calt" 1,
|
||||
"ss01" 1;
|
||||
}
|
||||
|
||||
h1 {
|
||||
|
|
@ -50,7 +57,11 @@
|
|||
|
||||
/* Link ottimizzati */
|
||||
a {
|
||||
@apply transition-colors duration-200;
|
||||
@apply transition-colors duration-200 text-gray-800 dark:text-gray-200;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
@apply text-gray-600 dark:text-gray-400;
|
||||
}
|
||||
|
||||
/* Strong e emphasis */
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@
|
|||
line-height: 1.875 !important;
|
||||
}
|
||||
|
||||
.dark .prose {
|
||||
color: #d1d5db !important;
|
||||
}
|
||||
|
||||
.prose h2 {
|
||||
font-size: 2.25rem !important;
|
||||
font-weight: 700 !important;
|
||||
|
|
@ -16,11 +20,14 @@
|
|||
padding-bottom: 1rem !important;
|
||||
}
|
||||
|
||||
.dark .prose h2 {
|
||||
color: #f3f4f6 !important;
|
||||
}
|
||||
|
||||
.prose > h2:first-child {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
|
||||
.prose h2::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
|
|
@ -32,6 +39,10 @@
|
|||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.dark .prose h2::after {
|
||||
background: linear-gradient(90deg, #f9bc2e 0%, #fbd46c 100%);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.prose h2 {
|
||||
font-size: 3rem !important;
|
||||
|
|
@ -48,6 +59,10 @@
|
|||
letter-spacing: -0.01em !important;
|
||||
}
|
||||
|
||||
.dark .prose h3 {
|
||||
color: #f3f4f6 !important;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.prose h3 {
|
||||
font-size: 1.875rem !important;
|
||||
|
|
@ -63,6 +78,10 @@
|
|||
line-height: 1.4 !important;
|
||||
}
|
||||
|
||||
.dark .prose h4 {
|
||||
color: #f3f4f6 !important;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.prose h4 {
|
||||
font-size: 1.5rem !important;
|
||||
|
|
@ -76,6 +95,10 @@
|
|||
font-weight: 400 !important;
|
||||
}
|
||||
|
||||
.dark .prose p {
|
||||
color: #d1d5db !important;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.prose p {
|
||||
font-size: 1.1rem !important;
|
||||
|
|
@ -83,8 +106,7 @@
|
|||
}
|
||||
|
||||
.prose > p:first-of-type {
|
||||
|
||||
margin-top: 1.6rem
|
||||
margin-top: 1.6rem;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
|
|
@ -102,21 +124,38 @@
|
|||
transition: all 0.2s ease !important;
|
||||
}
|
||||
|
||||
.dark .prose a {
|
||||
color: #d1d5db !important;
|
||||
}
|
||||
|
||||
.prose a:hover {
|
||||
color: #111827 !important;
|
||||
text-decoration-color: #111827 !important;
|
||||
}
|
||||
|
||||
.dark .prose a:hover {
|
||||
color: #f3f4f6 !important;
|
||||
text-decoration-color: #f9bc2e !important;
|
||||
}
|
||||
|
||||
.prose strong {
|
||||
font-weight: 700 !important;
|
||||
color: #111827 !important;
|
||||
}
|
||||
|
||||
.dark .prose strong {
|
||||
color: #f3f4f6 !important;
|
||||
}
|
||||
|
||||
.prose em {
|
||||
font-style: italic !important;
|
||||
color: #4b5563 !important;
|
||||
}
|
||||
|
||||
.dark .prose em {
|
||||
color: #9ca3af !important;
|
||||
}
|
||||
|
||||
.prose ul,
|
||||
.prose ol {
|
||||
margin-top: 2rem !important;
|
||||
|
|
@ -140,6 +179,10 @@
|
|||
margin-bottom: 0.75rem !important;
|
||||
}
|
||||
|
||||
.dark .prose li {
|
||||
color: #d1d5db !important;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.prose li {
|
||||
font-size: 1.1rem !important;
|
||||
|
|
@ -151,6 +194,10 @@
|
|||
font-weight: 600 !important;
|
||||
}
|
||||
|
||||
.dark .prose li::marker {
|
||||
color: #9ca3af !important;
|
||||
}
|
||||
|
||||
.prose blockquote {
|
||||
border-left: 4px solid #6b7280 !important;
|
||||
background-color: #f9fafb !important;
|
||||
|
|
@ -163,6 +210,12 @@
|
|||
line-height: 1.6 !important;
|
||||
}
|
||||
|
||||
.dark .prose blockquote {
|
||||
border-left-color: #9ca3af !important;
|
||||
background-color: #374151 !important;
|
||||
color: #d1d5db !important;
|
||||
}
|
||||
|
||||
.prose code {
|
||||
background-color: #f3f4f6 !important;
|
||||
color: #374151 !important;
|
||||
|
|
@ -170,8 +223,14 @@
|
|||
border-radius: 0.375rem !important;
|
||||
font-size: 1rem !important;
|
||||
font-weight: 600 !important;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
|
||||
"Courier New", monospace !important;
|
||||
font-family:
|
||||
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
|
||||
monospace !important;
|
||||
}
|
||||
|
||||
.dark .prose code {
|
||||
background-color: #374151 !important;
|
||||
color: #f3f4f6 !important;
|
||||
}
|
||||
|
||||
.prose pre {
|
||||
|
|
@ -197,6 +256,10 @@
|
|||
border-width: 2px !important;
|
||||
}
|
||||
|
||||
.dark .prose hr {
|
||||
border-color: #4b5563 !important;
|
||||
}
|
||||
|
||||
.prose img {
|
||||
border-radius: 1rem !important;
|
||||
margin-top: 2.5rem !important;
|
||||
|
|
@ -237,6 +300,10 @@
|
|||
border-bottom: 2px solid #e5e7eb !important;
|
||||
}
|
||||
|
||||
.dark .prose thead {
|
||||
border-bottom-color: #4b5563 !important;
|
||||
}
|
||||
|
||||
.prose th {
|
||||
padding: 0.75rem 1rem !important;
|
||||
text-align: left !important;
|
||||
|
|
@ -244,16 +311,29 @@
|
|||
color: #111827 !important;
|
||||
}
|
||||
|
||||
.dark .prose th {
|
||||
color: #f3f4f6 !important;
|
||||
}
|
||||
|
||||
.prose td {
|
||||
padding: 0.75rem 1rem !important;
|
||||
border-bottom: 1px solid #e5e7eb !important;
|
||||
color: #374151 !important;
|
||||
}
|
||||
|
||||
.dark .prose td {
|
||||
border-bottom-color: #4b5563 !important;
|
||||
color: #d1d5db !important;
|
||||
}
|
||||
|
||||
.prose tbody tr:hover {
|
||||
background-color: #f9fafb !important;
|
||||
}
|
||||
|
||||
.dark .prose tbody tr:hover {
|
||||
background-color: #374151 !important;
|
||||
}
|
||||
|
||||
/* Image Utility Classes */
|
||||
|
||||
/* Float classes */
|
||||
|
|
@ -363,6 +443,12 @@
|
|||
word-wrap: break-word !important;
|
||||
}
|
||||
|
||||
.dark .prose div.float-left em,
|
||||
.dark .prose div.float-right em,
|
||||
.dark .prose div.w-full em {
|
||||
color: #9ca3af !important;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.prose div.float-right em {
|
||||
margin-left: 2rem !important;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"],
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
colors: {
|
||||
white: "#ffffff",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue