formatting and layout
This commit is contained in:
		| @@ -1,5 +1,5 @@ | ||||
| --- | ||||
| // Background.astro - Dot pattern and ambient glow background with smooth theme transitions | ||||
|  | ||||
| --- | ||||
|  | ||||
| <div class="theme-transition-all fixed inset-0 -z-10 overflow-hidden"> | ||||
|   | ||||
| @@ -8,10 +8,10 @@ const links = await directus.request(readSingleton('links')); | ||||
| const currentYear = new Date().getFullYear(); | ||||
|  | ||||
| const navLinks = [ | ||||
|   { text: 'About', href: '/about' }, | ||||
|   { text: 'Home', href: '/' }, | ||||
|   { text: 'Blog', href: '/blog' }, | ||||
|   { text: 'Topics', href: '/topics' }, | ||||
|   { text: 'RSS', href: '/rss.xml' }, | ||||
|   { text: 'About', href: '/about' }, | ||||
| ]; | ||||
|  | ||||
| const socialLinks = [ | ||||
| @@ -53,7 +53,6 @@ const socialLinks = [ | ||||
|  | ||||
|   <div class="relative px-4 pt-16 pb-12 sm:px-6"> | ||||
|     <div class="mx-auto max-w-4xl"> | ||||
|       <!-- Main footer content --> | ||||
|       <div class="grid grid-cols-1 gap-10 md:grid-cols-12"> | ||||
|         <!-- Brand section --> | ||||
|         <div class="col-span-1 md:col-span-3"> | ||||
|   | ||||
| @@ -86,7 +86,7 @@ try { | ||||
| </Layout> | ||||
|  | ||||
| <style> | ||||
|   /* Enhanced hero image styling */ | ||||
|   /* Hero image styling */ | ||||
|   article img:first-of-type { | ||||
|     border-radius: 1rem; | ||||
|     box-shadow: | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
| import Navigation from '../components/Navigation.astro'; | ||||
| import Footer from '../components/Footer.astro'; | ||||
| import Background from '../components/Background.astro'; | ||||
|  | ||||
| import '../styles/global.css'; | ||||
|  | ||||
| interface Props { | ||||
| @@ -51,7 +52,6 @@ const { title, description } = Astro.props; | ||||
|     class="flex min-h-screen flex-col bg-white text-zinc-900 dark:bg-zinc-900 dark:text-zinc-100" | ||||
|   > | ||||
|  | ||||
|     <!-- Background component with dot pattern and ambient glow --> | ||||
|     <Background /> | ||||
|  | ||||
|     <div class="mx-auto w-full max-w-3xl grow px-4 sm:px-6"> | ||||
|   | ||||
| @@ -1,25 +0,0 @@ | ||||
| --- | ||||
| import { ClientRouter } from 'astro:transitions'; | ||||
| import BaseLayout from './BaseLayout.astro'; | ||||
|  | ||||
| const { title, description } = Astro.props; | ||||
| --- | ||||
|  | ||||
| <BaseLayout title={title} description={description}> | ||||
|   <ClientRouter fallback="swap" /> | ||||
|  | ||||
|   <div transition:animate="slide"> | ||||
|     <slot /> | ||||
|   </div> | ||||
| </BaseLayout> | ||||
|  | ||||
| <style> | ||||
|   /* Custom transition styles */ | ||||
|   ::view-transition-old(root) { | ||||
|     animation: 0.5s cubic-bezier(0.76, 0, 0.24, 1) both slide-to-left; | ||||
|   } | ||||
|  | ||||
|   ::view-transition-new(root) { | ||||
|     animation: 0.5s cubic-bezier(0.76, 0, 0.24, 1) both slide-from-right; | ||||
|   } | ||||
| </style> | ||||
| @@ -1,6 +1,6 @@ | ||||
| --- | ||||
| import BaseLayout from '../layouts/BaseLayout.astro'; | ||||
| import DynamicIcon from '../components/DynamicIcon.tsx'; | ||||
| import DynamicIcon from '../utils/DynamicIcon.tsx'; | ||||
|  | ||||
| import directus from '../../lib/directus'; | ||||
| import { readSingleton, readItems } from '@directus/sdk'; | ||||
| @@ -106,7 +106,7 @@ const skills = await directus.request( | ||||
|       </div> | ||||
|     </div> | ||||
|  | ||||
|     <!-- Skills Section - Improved for mobile --> | ||||
|     <!-- Skills Section --> | ||||
|     <div class="theme-transition-all mb-16 sm:mb-20 md:mb-24"> | ||||
|       <h2 | ||||
|         class="theme-transition-color mb-8 text-center text-2xl font-bold text-zinc-900 sm:mb-12 sm:text-3xl dark:text-zinc-100" | ||||
| @@ -274,7 +274,7 @@ const skills = await directus.request( | ||||
|     z-index: 10; | ||||
|   } | ||||
|  | ||||
|   /* Reduce animation complexity on mobile for better performance */ | ||||
|   /* Reduce animation complexity on mobile */ | ||||
|   @media (max-width: 640px) { | ||||
|     .skill-card { | ||||
|       transition: | ||||
| @@ -334,7 +334,7 @@ const skills = await directus.request( | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /* Improved touch targets for mobile */ | ||||
|   /* Touch targets for mobile */ | ||||
|   @media (max-width: 640px) { | ||||
|     a, | ||||
|     button { | ||||
| @@ -366,7 +366,6 @@ const skills = await directus.request( | ||||
| </style> | ||||
|  | ||||
| <script> | ||||
|   // Wait for the DOM to be fully loaded | ||||
|   document.addEventListener('DOMContentLoaded', () => { | ||||
|     const sliderTrack = document.querySelector('.slider-track'); | ||||
|  | ||||
| @@ -375,9 +374,6 @@ const skills = await directus.request( | ||||
|       const cards = document.querySelectorAll('.skill-card'); | ||||
|       if (!cards.length) return; | ||||
|  | ||||
|       // Clone the first set of cards and append to create seamless loop | ||||
|       const firstSetCount = cards.length / 3; // We have 3 sets in the markup | ||||
|  | ||||
|       // Set proper animation based on screen size | ||||
|       function updateScrollAnimation() { | ||||
|         if (window.innerWidth >= 640) { | ||||
| @@ -462,9 +458,7 @@ const skills = await directus.request( | ||||
|  | ||||
|     // Handle theme transition | ||||
|     document.addEventListener('themeChange', () => { | ||||
|       // Add special effects during theme transition | ||||
|       cards.forEach((card, index) => { | ||||
|         // Add staggered animation delay | ||||
|         setTimeout(() => { | ||||
|           card.classList.add('theme-changing'); | ||||
|           setTimeout(() => { | ||||
|   | ||||
| @@ -116,8 +116,6 @@ const { post, nextPost, prevPost } = Astro.props; | ||||
| </BlogPost> | ||||
|  | ||||
| <script> | ||||
|   // Removing TOC-related functions | ||||
|  | ||||
|   // Add copy buttons to code blocks | ||||
|   function initializeCodeCopyButtons() { | ||||
|     const codeBlocks = document.querySelectorAll('pre'); | ||||
| @@ -185,7 +183,6 @@ const { post, nextPost, prevPost } = Astro.props; | ||||
|  | ||||
|   // Main initialization function | ||||
|   function initializeBlogPost() { | ||||
|     // Initialize remaining components | ||||
|     initializeCodeCopyButtons(); | ||||
|  | ||||
|     // Scroll to hash if present in URL | ||||
| @@ -207,8 +204,6 @@ const { post, nextPost, prevPost } = Astro.props; | ||||
| </script> | ||||
|  | ||||
| <style> | ||||
|   /* Removing TOC-related styles */ | ||||
|  | ||||
|   /* Language badge styling */ | ||||
|   .language-badge { | ||||
|     font-family: | ||||
|   | ||||
| @@ -22,19 +22,11 @@ const postsByYear = sortedPosts.reduce((acc, post) => { | ||||
| }, {}); | ||||
|  | ||||
| const years = Object.keys(postsByYear).sort((a, b) => b - a); | ||||
|  | ||||
| // Get total post count | ||||
| const totalPosts = sortedPosts.length; | ||||
|  | ||||
| // Get unique tags for search suggestions | ||||
| const allTags = [...new Set(sortedPosts.flatMap((post) => post.tags || []))]; | ||||
| --- | ||||
|  | ||||
| <BaseLayout title="Blog"> | ||||
|   <div class="mx-auto w-full max-w-6xl px-4 py-10 sm:px-6 sm:py-16"> | ||||
|     <!-- Header with search  --> | ||||
|     <div class="relative mb-12 sm:mb-20"> | ||||
|       <!-- Decorative elements --> | ||||
|       <div | ||||
|         class="animate-blob absolute -top-10 -left-10 h-48 w-48 rounded-full bg-zinc-100 opacity-30 blur-3xl sm:-top-20 sm:-left-20 sm:h-72 sm:w-72 dark:bg-zinc-800/30" | ||||
|       > | ||||
| @@ -124,7 +116,7 @@ const allTags = [...new Set(sortedPosts.flatMap((post) => post.tags || []))]; | ||||
|         ) | ||||
|       } | ||||
|  | ||||
|       <!-- Improved sidebar for mobile --> | ||||
|       <!-- Sidebar for mobile --> | ||||
|       <div class="relative md:col-span-3"> | ||||
|         <div class="mb-8 space-y-4 md:sticky md:top-24 md:mb-0"> | ||||
|           <h3 | ||||
| @@ -156,7 +148,7 @@ const allTags = [...new Set(sortedPosts.flatMap((post) => post.tags || []))]; | ||||
|         </div> | ||||
|       </div> | ||||
|  | ||||
|       <!-- Improved post grid for mobile --> | ||||
|       <!-- Post grid for mobile --> | ||||
|       <div class="md:col-span-9"> | ||||
|         { | ||||
|           years.map((year) => ( | ||||
| @@ -168,7 +160,7 @@ const allTags = [...new Set(sortedPosts.flatMap((post) => post.tags || []))]; | ||||
|               <div | ||||
|                 class={`grid grid-cols-1 ${postsByYear[year].length >= 2 ? 'md:grid-cols-2' : 'md:grid-cols-1'} gap-8 sm:gap-12`} | ||||
|               > | ||||
|                 {postsByYear[year].map((post, index) => ( | ||||
|                 {postsByYear[year].map((post) => ( | ||||
|                   <article class="group relative mx-auto flex h-full w-full max-w-sm flex-col sm:max-w-md md:mx-0"> | ||||
|                     {post.image && ( | ||||
|                       <div class="mb-4 h-48 overflow-hidden rounded-lg sm:h-56"> | ||||
| @@ -303,7 +295,7 @@ const allTags = [...new Set(sortedPosts.flatMap((post) => post.tags || []))]; | ||||
|     overflow: hidden; | ||||
|   } | ||||
|  | ||||
|   /* Improved touch targets for mobile */ | ||||
|   /* Touch targets for mobile */ | ||||
|   @media (max-width: 640px) { | ||||
|     a, | ||||
|     button { | ||||
| @@ -315,7 +307,6 @@ const allTags = [...new Set(sortedPosts.flatMap((post) => post.tags || []))]; | ||||
| </style> | ||||
|  | ||||
| <script> | ||||
|   // Script không thay đổi - giữ nguyên chức năng | ||||
|   document.addEventListener('DOMContentLoaded', () => { | ||||
|     const backToTopButton = document.getElementById('back-to-top'); | ||||
|  | ||||
| @@ -341,7 +332,7 @@ const allTags = [...new Set(sortedPosts.flatMap((post) => post.tags || []))]; | ||||
|  | ||||
|       // Check scroll position | ||||
|       window.addEventListener('scroll', toggleBackToTopButton); | ||||
|       toggleBackToTopButton(); // Initial check | ||||
|       toggleBackToTopButton(); | ||||
|     } | ||||
|  | ||||
|     // Add smooth scrolling to year links | ||||
|   | ||||
| @@ -22,10 +22,10 @@ const allTags = [...new Set(posts.flatMap((post) => post.tags || []))].slice(0, | ||||
| --- | ||||
|  | ||||
| <Layout title=`Home | ${global.name}`> | ||||
|   <!-- Hero Section with improved mobile responsiveness --> | ||||
|   <!-- Hero Section with mobile responsiveness --> | ||||
|   <section class="theme-transition-all px-4 py-10 sm:px-6 sm:py-16 md:py-20"> | ||||
|     <div class="relative mx-auto max-w-2xl"> | ||||
|       <!-- Adjusted blob positions and sizes for better mobile appearance --> | ||||
|       <!-- Adjusted blob positions and sizes for mobile appearance --> | ||||
|       <div | ||||
|         class="animate-blob theme-transition-bg absolute -top-10 -left-10 h-40 w-40 rounded-full bg-zinc-100 opacity-50 blur-3xl sm:-top-20 sm:-left-20 sm:h-64 sm:w-64 dark:bg-zinc-800/50" | ||||
|       > | ||||
| @@ -85,7 +85,7 @@ const allTags = [...new Set(posts.flatMap((post) => post.tags || []))].slice(0, | ||||
|     </div> | ||||
|   </section> | ||||
|  | ||||
|   <!-- Featured Post Section - Improved for mobile --> | ||||
|   <!-- Featured post section --> | ||||
|   <section | ||||
|     class="theme-transition-all border-t border-zinc-100 px-4 py-10 sm:px-6 sm:py-12 md:py-16 dark:border-zinc-800" | ||||
|   > | ||||
| @@ -124,7 +124,7 @@ const allTags = [...new Set(posts.flatMap((post) => post.tags || []))].slice(0, | ||||
|         </a> | ||||
|       </div> | ||||
|  | ||||
|       <!-- Improved grid for better mobile layout --> | ||||
|       <!-- Grid for mobile layout --> | ||||
|       <div class="grid grid-cols-1 gap-6 sm:grid-cols-2 sm:gap-8 md:gap-12 lg:grid-cols-3"> | ||||
|         { | ||||
|           recentPosts.map((post, index) => ( | ||||
| @@ -215,7 +215,7 @@ const allTags = [...new Set(posts.flatMap((post) => post.tags || []))].slice(0, | ||||
|     </div> | ||||
|   </section> | ||||
|  | ||||
|   <!-- Topics/Tags Section - Improved for mobile --> | ||||
|   <!-- Topics section --> | ||||
|   { | ||||
|     allTags.length > 0 && ( | ||||
|       <section class="theme-transition-all border-t border-zinc-100 px-4 py-10 sm:px-6 sm:py-12 md:py-16 dark:border-zinc-800"> | ||||
| @@ -279,7 +279,6 @@ const allTags = [...new Set(posts.flatMap((post) => post.tags || []))].slice(0, | ||||
| <script> | ||||
|   // Add hover effect for cards on touch devices | ||||
|   document.addEventListener('DOMContentLoaded', () => { | ||||
|     // Check if it's a touch device | ||||
|     const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0; | ||||
|  | ||||
|     if (isTouchDevice) { | ||||
| @@ -297,11 +296,11 @@ const allTags = [...new Set(posts.flatMap((post) => post.tags || []))].slice(0, | ||||
|         }); | ||||
|       }); | ||||
|  | ||||
|       // Disable hover animations on touch devices for better performance | ||||
|       // Disable hover animations on touch devices | ||||
|       document.documentElement.classList.add('touch-device'); | ||||
|     } | ||||
|  | ||||
|     // Improved viewport height fix for mobile browsers | ||||
|     // Viewport height fix for mobile browsers | ||||
|     const setVh = () => { | ||||
|       const vh = window.innerHeight * 0.01; | ||||
|       document.documentElement.style.setProperty('--vh', `${vh}px`); | ||||
| @@ -339,7 +338,7 @@ const allTags = [...new Set(posts.flatMap((post) => post.tags || []))].slice(0, | ||||
|       }); | ||||
|     } | ||||
|  | ||||
|     // Improved theme change handler that preserves scroll position and provides smoother transitions | ||||
|     // Theme change handler that preserves scroll position and provides smoother transitions | ||||
|     document.addEventListener('themeChanged', () => { | ||||
|       // Store current scroll position | ||||
|       const scrollPosition = window.scrollY; | ||||
|   | ||||
| @@ -14,7 +14,6 @@ export async function getStaticPaths() { | ||||
|     }) | ||||
|   ); | ||||
|  | ||||
|   // Get all unique tags | ||||
|   const uniqueTags = [...new Set(posts.flatMap((post) => post.tags || []))]; | ||||
|  | ||||
|   // Create a path for each tag | ||||
| @@ -41,7 +40,6 @@ const sortedPosts = | ||||
|     : []; | ||||
| console.log(`Sorted posts length: ${sortedPosts.length}`); | ||||
|  | ||||
| const tagHue = Math.abs(tag.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0) % 360); | ||||
| const relatedTags = [ | ||||
|   ...new Set(sortedPosts.flatMap((post) => post.tags || []).filter((t) => t !== tag)), | ||||
| ].slice(0, 5); | ||||
| @@ -49,7 +47,6 @@ const relatedTags = [ | ||||
|  | ||||
| <BaseLayout title={`Posts tagged with "${tag}"`}> | ||||
|   <div class="mx-auto max-w-5xl px-4 py-10 sm:py-16"> | ||||
|     <!-- Header section --> | ||||
|     <div class="relative mb-10 sm:mb-16"> | ||||
|       <div | ||||
|         class="animate-blob absolute -top-20 -left-20 h-48 w-48 rounded-full bg-zinc-100 opacity-30 blur-3xl sm:h-64 sm:w-64 dark:bg-zinc-900/30" | ||||
| @@ -277,7 +274,7 @@ const relatedTags = [ | ||||
|       </div> | ||||
|     </div> | ||||
|  | ||||
|     <!-- Empty state với màu zinc --> | ||||
|     <!-- Empty state --> | ||||
|     { | ||||
|       sortedPosts.length === 0 && ( | ||||
|         <div class="py-12 text-center sm:py-20"> | ||||
|   | ||||
| @@ -31,7 +31,6 @@ const sortedTags = [...tagObjects].sort((a, b) => b.count - a.count); | ||||
|  | ||||
| <BaseLayout title="Explore Tags"> | ||||
|   <div class="theme-transition-all mx-auto w-full px-3 py-6 sm:px-6 sm:py-12 md:py-16"> | ||||
|     <!-- Enhanced header section with animated elements - improved for mobile --> | ||||
|     <div class="theme-transition-element relative mb-8 text-center sm:mb-12 md:mb-16"> | ||||
|       <div | ||||
|         class="animate-blob theme-transition-bg absolute -top-16 -left-16 h-36 w-36 rounded-full bg-zinc-100 opacity-50 blur-3xl sm:h-48 sm:w-48 md:h-72 md:w-72 dark:bg-zinc-800/50" | ||||
| @@ -146,9 +145,7 @@ const sortedTags = [...tagObjects].sort((a, b) => b.count - a.count); | ||||
| </BaseLayout> | ||||
|  | ||||
| <script> | ||||
|   // Ultra-reliable responsiveness handling | ||||
|   document.addEventListener('DOMContentLoaded', () => { | ||||
|     // Fix viewport width issues on mobile | ||||
|     const fixViewportWidth = () => { | ||||
|       // Force the viewport to be exactly the width of the device | ||||
|       const viewport = document.querySelector('meta[name="viewport"]'); | ||||
| @@ -378,7 +375,6 @@ const sortedTags = [...tagObjects].sort((a, b) => b.count - a.count); | ||||
|     width: 100% !important; | ||||
|   } | ||||
|  | ||||
|   /* Ultra-responsive breakpoints for extreme reliability */ | ||||
|   /* Micro screens (below 240px) */ | ||||
|   @media (max-width: 239px) { | ||||
|     .tag-cloud { | ||||
| @@ -545,7 +541,7 @@ const sortedTags = [...tagObjects].sort((a, b) => b.count - a.count); | ||||
|     hyphens: auto; | ||||
|   } | ||||
|  | ||||
|   /* Improved shadow for dark mode */ | ||||
|   /* Shadow for dark mode */ | ||||
|   :global(.dark) .tag-cloud { | ||||
|     box-shadow: | ||||
|       0 0 0 1px rgba(255, 255, 255, 0.05), | ||||
|   | ||||
| @@ -1,4 +1,3 @@ | ||||
| /* Remove all the complex mobile menu styles and keep only what's necessary */ | ||||
| @import 'tailwindcss'; | ||||
|  | ||||
| /* Dark mode support for Tailwind CSS v4 */ | ||||
| @@ -43,7 +42,7 @@ | ||||
|     scroll-padding-top: 4rem; | ||||
|   } | ||||
|  | ||||
|   /* Better touch targets on mobile */ | ||||
|   /* Touch targets on mobile */ | ||||
|   button, | ||||
|   a { | ||||
|     @apply min-h-[44px]; | ||||
|   | ||||
| @@ -9,7 +9,7 @@ import * as FiIcons from 'react-icons/fi'; | ||||
| import * as LuIcons from 'react-icons/lu'; | ||||
| import * as SiIcons from 'react-icons/si'; | ||||
| 
 | ||||
| // DynamicIcon.tsx - Load React Icon library dynamically from attributes from Directus
 | ||||
| // Load React Icon library dynamically from attributes in Directus
 | ||||
| 
 | ||||
| const iconSets = { | ||||
|   fa: FaIcons, | ||||
		Reference in New Issue
	
	Block a user