feat: Implement light theme, mobile menu, font and image fallback

This commit is contained in:
2026-02-17 18:54:09 +01:00
parent a781249c1a
commit 64f48f82c0
8 changed files with 141 additions and 27 deletions

View File

@@ -1,6 +1,16 @@
@import "tailwindcss"; @import "tailwindcss";
@theme { @theme {
/* Default Custom Properties (Dark Theme) */
--bg-primary: var(--color-gray-900);
--bg-secondary: var(--color-gray-800);
--bg-tertiary: var(--color-gray-700);
--text-primary: var(--color-gray-100);
--text-secondary: var(--color-gray-400);
--text-tertiary: var(--color-gray-300);
--brand-primary: var(--color-indigo-600);
--brand-secondary: var(--color-indigo-700);
--color-gray-900: #1a1a1a; --color-gray-900: #1a1a1a;
--color-gray-800: #2b2b2b; --color-gray-800: #2b2b2b;
--color-gray-700: #3c3c3c; --color-gray-700: #3c3c3c;
@@ -10,12 +20,31 @@
--color-gray-100: #f5f5f5; --color-gray-100: #f5f5f5;
--color-indigo-600: #4f46e5; --color-indigo-600: #4f46e5;
--color-indigo-700: #4338ca; --color-indigo-700: #4338ca;
--font-family-sans: "Inter", sans-serif;
/* Font Families */
--font-family-body: "Montserrat", sans-serif;
--font-family-heading: "Playfair Display", serif;
}
/* Light Theme Overrides (outside @theme block) */
html[data-theme='light'] {
--bg-primary: var(--color-gray-100);
--bg-secondary: var(--color-gray-200);
--bg-tertiary: var(--color-gray-300);
--text-primary: var(--color-gray-900);
--text-secondary: var(--color-gray-600);
--text-tertiary: var(--color-gray-700);
} }
@layer base { @layer base {
body { body {
@apply bg-gray-900 text-gray-100; background-color: var(--bg-primary);
color: var(--text-primary);
font-family: var(--font-family-body); /* Apply default body font */
}
h1, h2, h3, h4, h5, h6 {
font-family: var(--font-family-heading); /* Apply heading font */
} }
} }
@@ -46,3 +75,4 @@
#lightbox.show { #lightbox.show {
animation: fadeIn 0.3s ease-out forwards; animation: fadeIn 0.3s ease-out forwards;
} }

View File

@@ -2,6 +2,10 @@
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
console.log('Palina theme loaded!'); console.log('Palina theme loaded!');
const htmlElement = document.documentElement;
// Remove 'hidden' class from html to prevent FOUC
htmlElement.classList.remove('hidden');
// Staggered fade-in animation for post grid // Staggered fade-in animation for post grid
const gridItems = document.querySelectorAll('.post-grid-item'); const gridItems = document.querySelectorAll('.post-grid-item');
gridItems.forEach((item, index) => { gridItems.forEach((item, index) => {
@@ -40,4 +44,50 @@ document.addEventListener('DOMContentLoaded', () => {
} }
}); });
} }
// Theme Switcher
const themeToggle = document.getElementById('theme-toggle');
// const htmlElement = document.documentElement; // Already defined above
const currentTheme = localStorage.getItem('theme');
if (currentTheme) {
htmlElement.setAttribute('data-theme', currentTheme);
} else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) {
// Default to light theme if system preference is light
htmlElement.setAttribute('data-theme', 'light');
}
if (themeToggle) {
themeToggle.addEventListener('click', () => {
let newTheme = htmlElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
htmlElement.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
});
}
// Mobile Menu
const mobileMenuToggle = document.getElementById('mobile-menu-toggle');
const mobileMenuClose = document.getElementById('mobile-menu-close');
const mobileMenu = document.getElementById('mobile-menu');
if (mobileMenuToggle && mobileMenu && mobileMenuClose) {
mobileMenuToggle.addEventListener('click', () => {
mobileMenu.classList.remove('-translate-x-full');
mobileMenu.classList.add('translate-x-0');
});
mobileMenuClose.addEventListener('click', () => {
mobileMenu.classList.remove('translate-x-0');
mobileMenu.classList.add('-translate-x-full');
});
// Close menu if a link is clicked
const mobileNavLinks = mobileMenu.querySelectorAll('a');
mobileNavLinks.forEach(link => {
link.addEventListener('click', () => {
mobileMenu.classList.remove('translate-x-0');
mobileMenu.classList.add('-translate-x-full');
});
});
}
}); });

View File

@@ -1,13 +1,14 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="{{@site.lang}}"> <html lang="{{@site.lang}}" data-theme="dark" class="hidden">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{meta_title}}</title> <title>{{meta_title}}</title>
<link rel="stylesheet" href="{{asset "built/screen.css"}}"> <link rel="stylesheet" href="{{asset "built/screen.css"}}">
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;700&family=Playfair+Display:wght@700&display=swap" rel="stylesheet">
{{ghost_head}} {{ghost_head}}
</head> </head>
<body class="{{body_class}} font-sans bg-gray-900 text-gray-100 antialiased"> <body class="{{body_class}} font-sans antialiased">
<div class="min-h-screen flex flex-col"> <div class="min-h-screen flex flex-col">
{{> header}} {{> header}}
@@ -19,6 +20,8 @@
{{> footer}} {{> footer}}
</div> </div>
{{> mobile-menu}}
<div id="lightbox" class="hidden fixed inset-0 bg-black bg-opacity-80 z-50 flex items-center justify-center"> <div id="lightbox" class="hidden fixed inset-0 bg-black bg-opacity-80 z-50 flex items-center justify-center">
<button id="lightbox-close" class="absolute top-4 right-4 text-white text-3xl">&times;</button> <button id="lightbox-close" class="absolute top-4 right-4 text-white text-3xl">&times;</button>
<img id="lightbox-image" src="" alt="Lightbox image" class="max-w-full max-h-full"> <img id="lightbox-image" src="" alt="Lightbox image" class="max-w-full max-h-full">

View File

@@ -1,15 +1,15 @@
{{!< default}} {{!< default}}
<header class="container mx-auto px-4 py-8 text-center"> <header class="container mx-auto px-4 py-8 text-center">
<h1 class="text-5xl font-bold tracking-tight text-white">Palina Photo Blog</h1> <h1 class="text-5xl font-bold tracking-tight text-[var(--text-primary)]">Palina Photo Blog</h1>
<p class="mt-2 text-xl text-gray-400">{{@site.description}}</p> <p class="mt-2 text-xl text-[var(--text-secondary)]">{{@site.description}}</p>
</header> </header>
<div class="container mx-auto px-4"> <div class="container mx-auto px-4">
{{#if posts}} {{#if posts}}
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6"> <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
{{#foreach posts}} {{#foreach posts}}
<article class="post-grid-item opacity-0 relative bg-gray-800 rounded-lg shadow-lg overflow-hidden group"> <article class="post-grid-item opacity-0 relative bg-[var(--bg-secondary)] rounded-lg shadow-lg overflow-hidden group">
<a href="{{url}}" class="block"> <a href="{{url}}" class="block">
{{#if feature_image}} {{#if feature_image}}
<img <img
@@ -24,10 +24,10 @@
loading="lazy" loading="lazy"
> >
{{else}} {{else}}
<div class="w-full h-72 flex items-center justify-center bg-gray-700 text-gray-300 text-2xl">No Image</div> <div class="w-full h-72 flex items-center justify-center bg-[var(--bg-tertiary)] text-[var(--text-tertiary)] text-2xl">No Image</div>
{{/if}} {{/if}}
<div class="absolute inset-0 bg-black bg-opacity-50 flex items-end p-4 opacity-0 group-hover:opacity-100 transition-opacity duration-300 ease-in-out"> <div class="absolute inset-0 bg-black bg-opacity-50 flex items-end p-4 opacity-0 group-hover:opacity-100 transition-opacity duration-300 ease-in-out">
<h2 class="text-white text-xl font-semibold leading-tight">{{title}}</h2> <h2 class="text-[var(--text-primary)] text-xl font-semibold leading-tight">{{title}}</h2>
</div> </div>
</a> </a>
</article> </article>
@@ -36,6 +36,6 @@
{{pagination}} {{pagination}}
{{else}} {{else}}
<p class="text-center text-gray-400 text-2xl py-20">No posts found.</p> <p class="text-center text-[var(--text-secondary)] text-2xl py-20">No posts found.</p>
{{/if}} {{/if}}
</div> </div>

View File

@@ -1,21 +1,21 @@
<footer class="text-gray-400 bg-gray-900 body-font"> <footer class="text-[var(--text-secondary)] bg-[var(--bg-primary)] body-font">
<div class="container px-5 py-8 mx-auto flex items-center sm:flex-row flex-col"> <div class="container px-5 py-8 mx-auto flex items-center sm:flex-row flex-col">
<a href="{{@site.url}}" class="flex title-font font-medium items-center md:justify-start justify-center text-white"> <a href="{{@site.url}}" class="flex title-font font-medium items-center md:justify-start justify-center text-[var(--text-primary)]">
<span class="ml-3 text-xl">{{@site.title}}</span> <span class="ml-3 text-xl">{{@site.title}}</span>
</a> </a>
<p class="text-sm text-gray-500 sm:ml-4 sm:pl-4 sm:border-l-2 sm:border-gray-800 sm:py-2 sm:mt-0 mt-4"> <p class="text-sm text-[var(--text-secondary)] sm:ml-4 sm:pl-4 sm:border-l-2 sm:border-[var(--bg-secondary)] sm:py-2 sm:mt-0 mt-4">
© {{date format="YYYY"}} {{@site.title}} — Powered by Ghost © {{date format="YYYY"}} {{@site.title}} — Powered by Ghost
</p> </p>
<span class="inline-flex sm:ml-auto sm:mt-0 mt-4 justify-center sm:justify-start"> <span class="inline-flex sm:ml-auto sm:mt-0 mt-4 justify-center sm:justify-start">
{{#if @site.facebook}} {{#if @site.facebook}}
<a href="{{@site.facebook_url}}" class="text-gray-500 hover:text-white transition duration-200"> <a href="{{@site.facebook_url}}" class="text-[var(--text-secondary)] hover:text-[var(--text-primary)] transition duration-200">
<svg fill="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="w-5 h-5" viewBox="0 0 24 24"> <svg fill="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="w-5 h-5" viewBox="0 0 24 24">
<path d="M18 2h-3a5 5 0 00-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 011-1h3z"></path> <path d="M18 2h-3a5 5 0 00-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 011-1h3z"></path>
</svg> </svg>
</a> </a>
{{/if}} {{/if}}
{{#if @site.twitter}} {{#if @site.twitter}}
<a href="{{@site.twitter_url}}" class="ml-3 text-gray-500 hover:text-white transition duration-200"> <a href="{{@site.twitter_url}}" class="ml-3 text-[var(--text-secondary)] hover:text-[var(--text-primary)] transition duration-200">
<svg fill="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="w-5 h-5" viewBox="0 0 24 24"> <svg fill="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="w-5 h-5" viewBox="0 0 24 24">
<path d="M23 3a10.9 10.9 0 01-3.14 1.53 4.48 4.48 0 00-7.86 3v1A10.66 10.66 0 013 4s-4 9 5 13a11.64 11.64 0 01-7 2c9 5 20 0 20-11.5a4.5 4.5 0 00-.08-.83A7.72 7.72 0 0023 3z"></path> <path d="M23 3a10.9 10.9 0 01-3.14 1.53 4.48 4.48 0 00-7.86 3v1A10.66 10.66 0 013 4s-4 9 5 13a11.64 11.64 0 01-7 2c9 5 20 0 20-11.5a4.5 4.5 0 00-.08-.83A7.72 7.72 0 0023 3z"></path>
</svg> </svg>

View File

@@ -1,10 +1,25 @@
<header class="text-gray-100 body-font shadow-lg bg-gray-900"> <header class="text-[var(--text-primary)] body-font shadow-lg bg-[var(--bg-primary)]">
<div class="container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center"> <div class="container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center justify-between">
<a href="{{@site.url}}" class="flex title-font font-medium items-center text-white mb-4 md:mb-0"> <a href="{{@site.url}}" class="flex title-font font-medium items-center text-[var(--text-primary)] mb-4 md:mb-0">
<span class="ml-3 text-xl">{{@site.title}}</span> <span class="ml-3 text-xl">{{@site.title}}</span>
</a> </a>
<nav class="md:ml-auto flex flex-wrap items-center text-base justify-center">
<div class="flex items-center">
<nav class="hidden md:flex flex-wrap items-center text-base justify-center">
{{navigation type="primary"}} {{navigation type="primary"}}
<button id="theme-toggle" class="ml-4 p-2 rounded-full bg-[var(--bg-secondary)] text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)] transition-colors duration-200">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-5 h-5">
<path fill-rule="evenodd" d="M9.528 1.718a.75.75 0 01.178 1.043l-4.243 4.243a.75.75 0 01-1.061 0L.952 2.761a.75.75 0 011.06-1.06l3.52 3.52 3.712-3.712a.75.75 0 011.042-.178zM16.292 7.625a.75.75 0 01.132 1.056l-3.903 4.171c.466 1.102.615 2.373.13 3.673-.393 1.077-.665 2.164-.707 2.296-.135.347-.216.593-.216.593H12.75a.75.75 0 010-1.5h.084c.007-.022.036-.094.09-.253.307-.872.502-1.78.544-2.701.328-1.55-.145-2.915-.756-3.83A6.064 6.064 0 0015.6 9a.75.75 0 01.692-.375zM12.75 22.5c-2.935 0-5.696-1.07-7.795-2.997a.75.75 0 011.061-1.061 9 9 0 0013.868 0 .75.75 0 011.06-1.061C18.446 21.43 15.685 22.5 12.75 22.5z" clip-rule="evenodd" />
<path fill-rule="evenodd" d="M15.6 13.5a3.6 3.6 0 11-7.2 0 3.6 3.6 0 017.2 0z" clip-rule="evenodd" />
</svg>
</button>
</nav> </nav>
<button id="mobile-menu-toggle" class="md:hidden p-2 rounded-full bg-[var(--bg-secondary)] text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)] transition-colors duration-200">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
</svg>
</button>
</div>
</div> </div>
</header> </header>

12
partials/mobile-menu.hbs Normal file
View File

@@ -0,0 +1,12 @@
<div id="mobile-menu" class="fixed inset-0 z-40 bg-[var(--bg-primary)] transform -translate-x-full transition-transform duration-300 ease-in-out md:hidden">
<div class="flex justify-end p-5">
<button id="mobile-menu-close" class="p-2 rounded-full bg-[var(--bg-secondary)] text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)] transition-colors duration-200">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<nav class="flex flex-col items-center justify-center h-full space-y-8 text-2xl">
{{navigation type="primary"}}
</nav>
</div>

View File

@@ -2,12 +2,12 @@
<article class="container mx-auto px-4 py-12"> <article class="container mx-auto px-4 py-12">
<header class="text-center mb-8"> <header class="text-center mb-8">
<h1 class="text-6xl font-bold tracking-tight text-white mb-4">{{title}}</h1> <h1 class="text-6xl font-bold tracking-tight text-[var(--text-primary)] mb-4">{{title}}</h1>
<section class="post-meta text-gray-400 text-lg"> <section class="post-meta text-[var(--text-secondary)] text-lg">
<time datetime="{{date format="YYYY-MM-DD"}}">{{date format="MMMM DD, YYYY"}}</time> <time datetime="{{date format="YYYY-MM-DD"}}">{{date format="MMMM DD, YYYY"}}</time>
{{#if primary_tag}} {{#if primary_tag}}
<span class="mx-2">&bull;</span> <span class="mx-2">&bull;</span>
<a href="{{primary_tag.url}}" class="text-gray-400 hover:text-white transition duration-200">{{primary_tag.name}}</a> <a href="{{primary_tag.url}}" class="text-[var(--text-secondary)] hover:text-[var(--text-primary)] transition duration-200">{{primary_tag.name}}</a>
{{/if}} {{/if}}
</section> </section>
</header> </header>
@@ -23,14 +23,18 @@
alt="{{title}}" alt="{{title}}"
> >
</figure> </figure>
{{else}}
<div class="mb-12 rounded-lg overflow-hidden shadow-xl w-full h-96 flex items-center justify-center bg-[var(--bg-tertiary)] text-[var(--text-tertiary)] text-3xl">
No Feature Image
</div>
{{/if}} {{/if}}
<section class="post-content max-w-3xl mx-auto text-gray-200 leading-relaxed text-lg prose prose-invert"> <section class="post-content max-w-3xl mx-auto text-[var(--text-primary)] leading-relaxed text-lg prose prose-invert">
{{content}} {{content}}
</section> </section>
<footer class="mt-12 pt-8 border-t border-gray-700 text-center"> <footer class="mt-12 pt-8 border-t border-[var(--bg-tertiary)] text-center">
<a href="/" class="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 transition duration-200"> <a href="/" class="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-md shadow-sm text-[var(--text-primary)] bg-[var(--brand-primary)] hover:bg-[var(--brand-secondary)] focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[var(--brand-primary)] transition duration-200">
&larr; Back to Home &larr; Back to Home
</a> </a>
</footer> </footer>