fresh-main #5
			
				
			
		
		
		
	|  | @ -7,7 +7,7 @@ import tailwind from '@astrojs/tailwind'; | ||||||
| 
 | 
 | ||||||
| // https://astro.build/config
 | // https://astro.build/config
 | ||||||
| export default defineConfig({ | export default defineConfig({ | ||||||
|   site: 'https://laforceit.blog', |   site: 'https://laforceit-blog.pages.dev', // Your current Cloudflare site
 | ||||||
|   output: 'static', |   output: 'static', | ||||||
|   // adapter: cloudflare(),  // Commented out for local development
 |   // adapter: cloudflare(),  // Commented out for local development
 | ||||||
|   integrations: [ |   integrations: [ | ||||||
|  | @ -17,10 +17,14 @@ export default defineConfig({ | ||||||
|   ], |   ], | ||||||
|   markdown: { |   markdown: { | ||||||
|     shikiConfig: { |     shikiConfig: { | ||||||
|       theme: 'dracula', |       theme: 'one-dark-pro', | ||||||
|       wrap: true |       wrap: true | ||||||
|     }, |     }, | ||||||
|     remarkPlugins: [], |     remarkPlugins: [], | ||||||
|     rehypePlugins: [] |     rehypePlugins: [] | ||||||
|  |   }, | ||||||
|  |   compressHTML: false, // Disable HTML compression to avoid parsing errors
 | ||||||
|  |   build: { | ||||||
|  |     format: 'file',  // Use 'file' instead of 'directory' format
 | ||||||
|   } |   } | ||||||
| }); | }); | ||||||
|  | @ -379,6 +379,70 @@ const navItems = [ | ||||||
| </style> | </style> | ||||||
| 
 | 
 | ||||||
| <script> | <script> | ||||||
|  | 
 | ||||||
|  |   // Handle mobile menu toggle | ||||||
|  |   document.addEventListener('DOMContentLoaded', () => { | ||||||
|  |     const menuBtn = document.getElementById('mobile-menu-btn'); | ||||||
|  |     const mainNav = document.querySelector('.main-nav'); | ||||||
|  |     const header = document.querySelector('.site-header'); | ||||||
|  |      | ||||||
|  |     if (menuBtn && mainNav) { | ||||||
|  |       menuBtn.addEventListener('click', () => { | ||||||
|  |         mainNav.classList.toggle('active'); | ||||||
|  |         menuBtn.classList.toggle('mobile-menu-active'); | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     // Header scroll effect | ||||||
|  |     window.addEventListener('scroll', () => { | ||||||
|  |       if (window.scrollY > 50) { | ||||||
|  |         header?.classList.add('scrolled'); | ||||||
|  |       } else { | ||||||
|  |         header?.classList.remove('scrolled'); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |      | ||||||
|  |     // Theme toggle functionality | ||||||
|  |     const themeToggle = document.getElementById('theme-toggle'); | ||||||
|  |      | ||||||
|  |     if (themeToggle) { | ||||||
|  |       themeToggle.addEventListener('click', () => { | ||||||
|  |         document.documentElement.classList.toggle('light-mode'); | ||||||
|  |          | ||||||
|  |         // Store preference in localStorage | ||||||
|  |         const isLightMode = document.documentElement.classList.contains('light-mode'); | ||||||
|  |         localStorage.setItem('theme', isLightMode ? 'light' : 'dark'); | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     // Add interactive network nodes animation | ||||||
|  |     const header_el = document.querySelector('.site-header'); | ||||||
|  |      | ||||||
|  |     if (header_el) { | ||||||
|  |       // Create animated nodes | ||||||
|  |       for (let i = 0; i < 5; i++) { | ||||||
|  |         const node = document.createElement('div'); | ||||||
|  |         node.className = 'nav-node'; | ||||||
|  |         node.style.left = `${Math.random() * 100}%`; | ||||||
|  |         node.style.animationDelay = `${Math.random() * 5}s`; | ||||||
|  |         node.style.animationDuration = `${5 + Math.random() * 5}s`; | ||||||
|  |         header_el.appendChild(node); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  | // Theme toggle functionality | ||||||
|  | const themeToggle = document.getElementById('theme-toggle'); | ||||||
|  | 	 | ||||||
|  | if (themeToggle) { | ||||||
|  |   themeToggle.addEventListener('click', () => { | ||||||
|  | 	document.documentElement.classList.toggle('light-mode'); | ||||||
|  | 	 | ||||||
|  | 	// Store preference in localStorage | ||||||
|  | 	const isLightMode = document.documentElement.classList.contains('light-mode'); | ||||||
|  | 	localStorage.setItem('theme', isLightMode ? 'light' : 'dark'); | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|   // Handle mobile menu toggle |   // Handle mobile menu toggle | ||||||
|   document.addEventListener('DOMContentLoaded', () => { |   document.addEventListener('DOMContentLoaded', () => { | ||||||
|     const menuBtn = document.getElementById('mobile-menu-btn'); |     const menuBtn = document.getElementById('mobile-menu-btn'); | ||||||
|  |  | ||||||
|  | @ -0,0 +1,93 @@ | ||||||
|  | --- | ||||||
|  | // ThemeToggler.astro | ||||||
|  | // A component to toggle between light and dark themes | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | <button id="theme-toggle" aria-label="Toggle dark mode" class="theme-toggle"> | ||||||
|  |   <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="sun-icon"> | ||||||
|  |     <circle cx="12" cy="12" r="5"></circle> | ||||||
|  |     <line x1="12" y1="1" x2="12" y2="3"></line> | ||||||
|  |     <line x1="12" y1="21" x2="12" y2="23"></line> | ||||||
|  |     <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line> | ||||||
|  |     <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line> | ||||||
|  |     <line x1="1" y1="12" x2="3" y2="12"></line> | ||||||
|  |     <line x1="21" y1="12" x2="23" y2="12"></line> | ||||||
|  |     <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line> | ||||||
|  |     <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line> | ||||||
|  |   </svg> | ||||||
|  |   <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="moon-icon"> | ||||||
|  |     <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path> | ||||||
|  |   </svg> | ||||||
|  | </button> | ||||||
|  | 
 | ||||||
|  | <style> | ||||||
|  |   .theme-toggle { | ||||||
|  |     background: none; | ||||||
|  |     border: none; | ||||||
|  |     padding: 0.25rem; | ||||||
|  |     border-radius: 50%; | ||||||
|  |     display: flex; | ||||||
|  |     align-items: center; | ||||||
|  |     justify-content: center; | ||||||
|  |     color: var(--text-secondary); | ||||||
|  |     cursor: pointer; | ||||||
|  |     transition: color 0.3s ease, background-color 0.3s ease; | ||||||
|  |     position: relative; | ||||||
|  |     width: 34px; | ||||||
|  |     height: 34px; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .theme-toggle:hover { | ||||||
|  |     color: var(--text-primary); | ||||||
|  |     background: rgba(255, 255, 255, 0.1); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .sun-icon, .moon-icon { | ||||||
|  |     position: absolute; | ||||||
|  |     transition: transform 0.5s ease, opacity 0.5s ease; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   html:not(.dark) .sun-icon { | ||||||
|  |     opacity: 1; | ||||||
|  |     transform: rotate(0); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   html:not(.dark) .moon-icon { | ||||||
|  |     opacity: 0; | ||||||
|  |     transform: rotate(90deg); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   html.dark .sun-icon { | ||||||
|  |     opacity: 0; | ||||||
|  |     transform: rotate(-90deg); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   html.dark .moon-icon { | ||||||
|  |     opacity: 1; | ||||||
|  |     transform: rotate(0); | ||||||
|  |   } | ||||||
|  | </style> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  |   // Theme toggling logic | ||||||
|  |   document.addEventListener('DOMContentLoaded', () => { | ||||||
|  |     const themeToggle = document.getElementById('theme-toggle'); | ||||||
|  |      | ||||||
|  |     // Function to set theme | ||||||
|  |     const setTheme = (isDark) => { | ||||||
|  |       if (isDark) { | ||||||
|  |         document.documentElement.classList.add('dark'); | ||||||
|  |         localStorage.setItem('theme', 'dark'); | ||||||
|  |       } else { | ||||||
|  |         document.documentElement.classList.remove('dark'); | ||||||
|  |         localStorage.setItem('theme', 'light'); | ||||||
|  |       } | ||||||
|  |     }; | ||||||
|  |      | ||||||
|  |     // Theme toggle click handler | ||||||
|  |     themeToggle?.addEventListener('click', () => { | ||||||
|  |       const isDark = document.documentElement.classList.contains('dark'); | ||||||
|  |       setTheme(!isDark); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | </script> | ||||||
|  | @ -23,6 +23,18 @@ const { | ||||||
|     <title>{title}</title> |     <title>{title}</title> | ||||||
|     <meta name="description" content={description} /> |     <meta name="description" content={description} /> | ||||||
|      |      | ||||||
|  |     <!-- Theme initialization - Must be inline --> | ||||||
|  |     <script is:inline> | ||||||
|  |       // Initialize theme before page loads to prevent flash | ||||||
|  |       const savedTheme = localStorage.getItem('theme'); | ||||||
|  |       const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; | ||||||
|  |       if (savedTheme === 'light' || (!savedTheme && !prefersDark)) { | ||||||
|  |         document.documentElement.classList.add('light-mode'); | ||||||
|  |       } else { | ||||||
|  |         document.documentElement.classList.remove('light-mode'); | ||||||
|  |       } | ||||||
|  |     </script> | ||||||
|  |      | ||||||
|     <!-- OpenGraph/Social Media Meta Tags --> |     <!-- OpenGraph/Social Media Meta Tags --> | ||||||
|     <meta property="og:title" content={title} /> |     <meta property="og:title" content={title} /> | ||||||
|     <meta property="og:description" content={description} /> |     <meta property="og:description" content={description} /> | ||||||
|  | @ -44,6 +56,9 @@ const { | ||||||
|     <!-- Favicon --> |     <!-- Favicon --> | ||||||
|     <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> |     <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> | ||||||
|      |      | ||||||
|  |     <!-- Theme CSS --> | ||||||
|  |     <link rel="stylesheet" href="/styles/theme.css" /> | ||||||
|  |      | ||||||
|     <!-- Cytoscape Library for Knowledge Graph --> |     <!-- Cytoscape Library for Knowledge Graph --> | ||||||
|     <script src="https://unpkg.com/cytoscape@3.25.0/dist/cytoscape.min.js" is:inline></script> |     <script src="https://unpkg.com/cytoscape@3.25.0/dist/cytoscape.min.js" is:inline></script> | ||||||
|      |      | ||||||
|  |  | ||||||
|  | @ -1,216 +1,184 @@ | ||||||
| import { getCollection } from 'astro:content'; | --- | ||||||
|  | // src/pages/tag/[tag].astro | ||||||
|  | // Dynamic route for tag pages | ||||||
|  | 
 | ||||||
| import BaseLayout from '../../layouts/BaseLayout.astro'; | import BaseLayout from '../../layouts/BaseLayout.astro'; | ||||||
|  | import { getCollection } from 'astro:content'; | ||||||
| 
 | 
 | ||||||
| export async function getStaticPaths() { | export async function getStaticPaths() { | ||||||
|   const allPosts = await getCollection('posts'); |   const allPosts = await getCollection('blog'); | ||||||
|    |   const uniqueTags = [...new Set(allPosts.map((post) => post.data.tags).flat())]; | ||||||
|   // Get all unique tags from all posts |  | ||||||
|   const uniqueTags = [...new Set(allPosts.flatMap(post => post.data.tags || []))]; |  | ||||||
|    |  | ||||||
|   // Create a page for each tag |  | ||||||
|   return uniqueTags.map(tag => { |  | ||||||
|     // Filter posts that have this tag |  | ||||||
|     const filteredPosts = allPosts.filter(post =>  |  | ||||||
|       post.data.tags && post.data.tags.includes(tag) |  | ||||||
|     ); |  | ||||||
| 
 | 
 | ||||||
|  |   return uniqueTags.map((tag) => { | ||||||
|  |     const filteredPosts = allPosts.filter((post) => post.data.tags.includes(tag)); | ||||||
|     return { |     return { | ||||||
|       params: { tag }, |       params: { tag }, | ||||||
|       props: { posts: filteredPosts, tag }, |       props: { posts: filteredPosts }, | ||||||
|     }; |     }; | ||||||
|   }); |   }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const { posts, tag } = Astro.props; | const { tag } = Astro.params; | ||||||
|  | const { posts } = Astro.props; | ||||||
| 
 | 
 | ||||||
| // Sort posts by date | // Format date | ||||||
|  | const formatDate = (dateStr) => { | ||||||
|  |   const date = new Date(dateStr); | ||||||
|  |   return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Sort posts by date (newest first) | ||||||
| const sortedPosts = posts.sort((a, b) => { | const sortedPosts = posts.sort((a, b) => { | ||||||
|   const dateA = a.data.pubDate ? new Date(a.data.pubDate) : new Date(0); |   const dateA = new Date(a.data.pubDate); | ||||||
|   const dateB = b.data.pubDate ? new Date(b.data.pubDate) : new Date(0); |   const dateB = new Date(b.data.pubDate); | ||||||
|   return dateB.getTime() - dateA.getTime(); |   return dateB.getTime() - dateA.getTime(); | ||||||
| }); | }); | ||||||
| --- | --- | ||||||
| 
 | 
 | ||||||
| <BaseLayout title={`Posts tagged with "${tag}" | LaForce IT Blog`} description={`Articles and guides related to ${tag}`}> | <BaseLayout title={`Posts tagged with "${tag}" | LaForce IT Blog`} description={`Articles and guides related to ${tag}`}> | ||||||
|   <main class="container"> |   <div class="container tag-page"> | ||||||
|     <section class="tag-header"> |     <header class="tag-hero"> | ||||||
|       <h1 class="tag-title">Posts tagged with <span>#{tag}</span></h1> |       <h1>Posts tagged with <span class="tag-highlight">{tag}</span></h1> | ||||||
|       <p class="tag-description"> |       <p>Explore {sortedPosts.length} {sortedPosts.length === 1 ? 'article' : 'articles'} related to {tag}</p> | ||||||
|         Browse all {sortedPosts.length} articles related to this topic |     </header> | ||||||
|       </p> |  | ||||||
|       <a href="/tags" class="tag-link">View all tags</a> |  | ||||||
|     </section> |  | ||||||
| 
 | 
 | ||||||
|     <div class="blog-grid"> |     <div class="posts-grid"> | ||||||
|       {sortedPosts.map((post) => ( |       {sortedPosts.map((post) => ( | ||||||
|         <article class="post-card"> |         <article class="post-card"> | ||||||
|           {/* Temporarily removed conditional image rendering for debugging */} |           <!-- Simplified image rendering that works reliably --> | ||||||
|           <img |           <img | ||||||
|             width={720} |             width={720} | ||||||
|             height={360} |             height={360} | ||||||
|             src="/images/placeholders/default.jpg" |             src={post.data.heroImage || "/images/placeholders/default.jpg"} | ||||||
|             alt="" |             alt="" | ||||||
|             class="post-image" |             class="post-image" | ||||||
|           /> |           /> | ||||||
|           <div class="post-content"> |           <div class="post-content"> | ||||||
|             <div class="post-meta"> |             <time datetime={post.data.pubDate}>{formatDate(post.data.pubDate)}</time> | ||||||
|               <time datetime={post.data.pubDate ? new Date(post.data.pubDate).toISOString() : ''}> |             <h2 class="post-title"> | ||||||
|                 {post.data.pubDate ? new Date(post.data.pubDate).toLocaleDateString('en-us', { |  | ||||||
|                   year: 'numeric', |  | ||||||
|                   month: 'short', |  | ||||||
|                   day: 'numeric', |  | ||||||
|                 }) : 'No date'} |  | ||||||
|               </time> |  | ||||||
|               {post.data.category && ( |  | ||||||
|                 <span class="post-category"> |  | ||||||
|                   {post.data.category} |  | ||||||
|                 </span> |  | ||||||
|               )} |  | ||||||
|             </div> |  | ||||||
|             <h3 class="post-title"> |  | ||||||
|               <a href={`/posts/${post.slug}/`}>{post.data.title}</a> |               <a href={`/posts/${post.slug}/`}>{post.data.title}</a> | ||||||
|               {post.data.draft && <span class="draft-badge">Draft</span>} |             </h2> | ||||||
|             </h3> |  | ||||||
|             <p class="post-excerpt">{post.data.description}</p> |             <p class="post-excerpt">{post.data.description}</p> | ||||||
|             <div class="post-footer"> |             <div class="post-meta"> | ||||||
|               <span class="post-read-time">{post.data.readTime || '5 min read'}</span> |               <span class="reading-time">{post.data.minutesRead || '5 min'} read</span> | ||||||
|               <a href={`/posts/${post.slug}/`} class="read-more">Read More</a> |               <ul class="post-tags"> | ||||||
|  |                 {post.data.tags.map((tagName) => ( | ||||||
|  |                   <li> | ||||||
|  |                     <a href={`/tag/${tagName}`} class={tagName === tag ? 'current-tag' : ''}> | ||||||
|  |                       {tagName} | ||||||
|  |                     </a> | ||||||
|  |                   </li> | ||||||
|  |                 ))} | ||||||
|  |               </ul> | ||||||
|             </div> |             </div> | ||||||
|           </div> |           </div> | ||||||
|         </article> |         </article> | ||||||
|       ))} |       ))} | ||||||
|     </div> |     </div> | ||||||
|   </main> | 
 | ||||||
|  |     <a href="/tags" class="all-tags-link"> | ||||||
|  |       <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | ||||||
|  |         <line x1="19" y1="12" x2="5" y2="12"></line> | ||||||
|  |         <polyline points="12 19 5 12 12 5"></polyline> | ||||||
|  |       </svg> | ||||||
|  |       View all tags | ||||||
|  |     </a> | ||||||
|  |   </div> | ||||||
| </BaseLayout> | </BaseLayout> | ||||||
| 
 | 
 | ||||||
| <style> | <style> | ||||||
| .tag-header { |   .tag-page { | ||||||
|   margin: 3rem 0; |     padding-top: 2rem; | ||||||
|  |     padding-bottom: 4rem; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .tag-hero { | ||||||
|     text-align: center; |     text-align: center; | ||||||
|  |     margin-bottom: 3rem; | ||||||
|  |     animation: fadeIn 0.5s ease-out; | ||||||
|   } |   } | ||||||
|    |    | ||||||
| .tag-title { |   @keyframes fadeIn { | ||||||
|   font-size: clamp(1.8rem, 4vw, 2.5rem); |     from { opacity: 0; transform: translateY(10px); } | ||||||
|   margin-bottom: 1rem; |     to { opacity: 1; transform: translateY(0); } | ||||||
|   } |   } | ||||||
|    |    | ||||||
| .tag-title span { |   .tag-hero h1 { | ||||||
|   background: linear-gradient(90deg, var(--accent-primary), var(--accent-tertiary)); |     font-size: var(--font-size-3xl); | ||||||
|  |     margin-bottom: 0.5rem; | ||||||
|  |     line-height: 1.2; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .tag-highlight { | ||||||
|  |     background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary)); | ||||||
|     -webkit-background-clip: text; |     -webkit-background-clip: text; | ||||||
|  |     -webkit-text-fill-color: transparent; | ||||||
|     background-clip: text; |     background-clip: text; | ||||||
|     color: transparent; |     color: transparent; | ||||||
|   font-family: 'JetBrains Mono', monospace; |     font-weight: 700; | ||||||
|   } |   } | ||||||
|    |    | ||||||
| .tag-description { |   .tag-hero p { | ||||||
|     color: var(--text-secondary); |     color: var(--text-secondary); | ||||||
|   font-size: 1.1rem; |     font-size: var(--font-size-lg); | ||||||
|   margin-bottom: 1rem; |  | ||||||
|   } |   } | ||||||
|    |    | ||||||
| .tag-link { |   .posts-grid { | ||||||
|   display: inline-block; |  | ||||||
|   margin-top: 1rem; |  | ||||||
|   color: var(--accent-primary); |  | ||||||
|   text-decoration: none; |  | ||||||
|   font-family: 'JetBrains Mono', monospace; |  | ||||||
|   font-size: 0.9rem; |  | ||||||
|   border-bottom: 1px dashed var(--accent-primary); |  | ||||||
|   transition: all 0.3s ease; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .tag-link:hover { |  | ||||||
|   border-bottom: 1px solid var(--accent-primary); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .draft-badge { |  | ||||||
|   display: inline-block; |  | ||||||
|   margin-left: 0.5rem; |  | ||||||
|   padding: 0.25rem 0.5rem; |  | ||||||
|   background-color: rgba(226, 232, 240, 0.2); |  | ||||||
|   color: #94a3b8; |  | ||||||
|   font-size: 0.75rem; |  | ||||||
|   border-radius: 0.25rem; |  | ||||||
|   vertical-align: middle; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* Include styles from blog index if needed, like .blog-grid, .post-card etc. */ |  | ||||||
| .blog-grid { |  | ||||||
|     display: grid; |     display: grid; | ||||||
|   grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); |     grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); | ||||||
|     gap: 2rem; |     gap: 2rem; | ||||||
|   margin: 2rem 0 4rem; |     margin-bottom: 3rem; | ||||||
|   } |   } | ||||||
|    |    | ||||||
|   .post-card { |   .post-card { | ||||||
|     background: var(--card-bg); |     background: var(--card-bg); | ||||||
|   border-radius: 10px; |     border-radius: 12px; | ||||||
|   border: 1px solid var(--card-border); |  | ||||||
|     overflow: hidden; |     overflow: hidden; | ||||||
|   transition: all 0.3s ease; |     border: 1px solid var(--border-primary); | ||||||
|   position: relative; |     transition: transform 0.3s ease, box-shadow 0.3s ease; | ||||||
|   z-index: 1; |     animation: fadeIn 0.5s ease-out forwards; | ||||||
|  |     animation-delay: calc(var(--animation-order, 0) * 0.1s); | ||||||
|  |     opacity: 0; | ||||||
|  |     display: flex; | ||||||
|  |     flex-direction: column; | ||||||
|   } |   } | ||||||
|    |    | ||||||
|   .post-card:hover { |   .post-card:hover { | ||||||
|     transform: translateY(-5px); |     transform: translateY(-5px); | ||||||
|   box-shadow: 0 10px 30px rgba(6, 182, 212, 0.1); |     box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); | ||||||
|   border-color: rgba(56, 189, 248, 0.4); |     border-color: var(--accent-primary); | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .post-card::before { |  | ||||||
|   content: ''; |  | ||||||
|   position: absolute; |  | ||||||
|   inset: 0; |  | ||||||
|   background: linear-gradient(135deg, rgba(6, 182, 212, 0.05), rgba(139, 92, 246, 0.05)); |  | ||||||
|   z-index: -1; |  | ||||||
|   opacity: 0; |  | ||||||
|   transition: opacity 0.3s ease; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .post-card:hover::before { |  | ||||||
|   opacity: 1; |  | ||||||
|   } |   } | ||||||
|    |    | ||||||
|   .post-image { |   .post-image { | ||||||
|     width: 100%; |     width: 100%; | ||||||
|     height: 200px; |     height: 200px; | ||||||
|     object-fit: cover; |     object-fit: cover; | ||||||
|   border-bottom: 1px solid var(--card-border); |     border-bottom: 1px solid var(--border-primary); | ||||||
|   } |   } | ||||||
|    |    | ||||||
|   .post-content { |   .post-content { | ||||||
|     padding: 1.5rem; |     padding: 1.5rem; | ||||||
| } |     flex-grow: 1; | ||||||
| 
 |  | ||||||
| .post-meta { |  | ||||||
|     display: flex; |     display: flex; | ||||||
|   justify-content: space-between; |     flex-direction: column; | ||||||
|   align-items: center; |  | ||||||
|   margin-bottom: 1rem; |  | ||||||
|   font-size: 0.85rem; |  | ||||||
|   color: var(--text-secondary); |  | ||||||
|   } |   } | ||||||
|    |    | ||||||
| .post-category { |   .post-content time { | ||||||
|   background: rgba(6, 182, 212, 0.1); |     color: var(--text-tertiary); | ||||||
|   color: var(--accent-primary); |     font-size: var(--font-size-sm); | ||||||
|   padding: 0.25rem 0.5rem; |     font-family: var(--font-mono); | ||||||
|   border-radius: 4px; |  | ||||||
|   font-family: 'JetBrains Mono', monospace; |  | ||||||
|   font-size: 0.75rem; |  | ||||||
|   } |   } | ||||||
|    |    | ||||||
|   .post-title { |   .post-title { | ||||||
|   font-size: 1.25rem; |     font-size: var(--font-size-xl); | ||||||
|   margin-bottom: 0.75rem; |     margin: 0.5rem 0 1rem; | ||||||
|     line-height: 1.3; |     line-height: 1.3; | ||||||
|   } |   } | ||||||
|    |    | ||||||
|   .post-title a { |   .post-title a { | ||||||
|     color: var(--text-primary); |     color: var(--text-primary); | ||||||
|     text-decoration: none; |     text-decoration: none; | ||||||
|   transition: color 0.3s ease; |     transition: color 0.2s ease; | ||||||
|   } |   } | ||||||
|    |    | ||||||
|   .post-title a:hover { |   .post-title a:hover { | ||||||
|  | @ -219,37 +187,60 @@ const sortedPosts = posts.sort((a, b) => { | ||||||
|    |    | ||||||
|   .post-excerpt { |   .post-excerpt { | ||||||
|     color: var(--text-secondary); |     color: var(--text-secondary); | ||||||
|   font-size: 0.9rem; |     font-size: var(--font-size-md); | ||||||
|     margin-bottom: 1.5rem; |     margin-bottom: 1.5rem; | ||||||
|   display: -webkit-box; |     line-height: 1.6; | ||||||
|   -webkit-line-clamp: 3; |     flex-grow: 1; | ||||||
|   -webkit-box-orient: vertical; |  | ||||||
|   overflow: hidden; |  | ||||||
|   } |   } | ||||||
|    |    | ||||||
| .post-footer { |   .post-meta { | ||||||
|     display: flex; |     display: flex; | ||||||
|   justify-content: space-between; |     flex-direction: column; | ||||||
|   align-items: center; |     gap: 0.75rem; | ||||||
|   color: var(--text-secondary); |     margin-top: auto; | ||||||
|   font-size: 0.85rem; |  | ||||||
|   } |   } | ||||||
|    |    | ||||||
| .read-more { |   .reading-time { | ||||||
|  |     color: var(--text-tertiary); | ||||||
|  |     font-size: var(--font-size-sm); | ||||||
|  |     font-family: var(--font-mono); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .post-tags { | ||||||
|  |     display: flex; | ||||||
|  |     flex-wrap: wrap; | ||||||
|  |     gap: 0.5rem; | ||||||
|  |     list-style: none; | ||||||
|  |     padding: 0; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .post-tags li a { | ||||||
|  |     display: block; | ||||||
|  |     padding: 0.25rem 0.75rem; | ||||||
|  |     background: rgba(56, 189, 248, 0.1); | ||||||
|  |     border-radius: 20px; | ||||||
|     color: var(--accent-primary); |     color: var(--accent-primary); | ||||||
|  |     font-size: var(--font-size-xs); | ||||||
|     text-decoration: none; |     text-decoration: none; | ||||||
|   font-weight: 500; |     transition: all 0.2s ease; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .post-tags li a:hover { | ||||||
|  |     background: rgba(56, 189, 248, 0.2); | ||||||
|  |     transform: translateY(-2px); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .post-tags li a.current-tag { | ||||||
|  |     background: var(--accent-primary); | ||||||
|  |     color: var(--bg-primary); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .all-tags-link { | ||||||
|     display: flex; |     display: flex; | ||||||
|     align-items: center; |     align-items: center; | ||||||
|   gap: 0.25rem; |     gap: 0.5rem; | ||||||
|   transition: color 0.3s ease; |     margin: 0 auto; | ||||||
| } |     padding: 0.75rem 1.5rem; | ||||||
| 
 |     background: var(--bg-secondary); | ||||||
| .read-more:hover { |     border: 1px solid var(--border-primary); | ||||||
|   color: var(--accent-secondary); |     border-radius: 30px; | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .read-more::after { |  | ||||||
|   content: '→'; |  | ||||||
| } |  | ||||||
| </style> |  | ||||||
|  | @ -0,0 +1,82 @@ | ||||||
|  | /* Theme Variables - Dark/Light Mode Support */ | ||||||
|  | 
 | ||||||
|  | /* Dark theme (default) */ | ||||||
|  | html { | ||||||
|  |   /* Keep the default dark theme as defined in BaseLayout */ | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Light theme */ | ||||||
|  | html.light-mode { | ||||||
|  |   /* Primary Colors */ | ||||||
|  |   --bg-primary: #f8fafc; | ||||||
|  |   --bg-secondary: #f1f5f9; | ||||||
|  |   --bg-tertiary: #e2e8f0; | ||||||
|  |   --bg-code: #f1f5f9; | ||||||
|  |   --text-primary: #0f172a; | ||||||
|  |   --text-secondary: #334155; | ||||||
|  |   --text-tertiary: #64748b; | ||||||
|  |    | ||||||
|  |   /* Accent Colors remain the same for brand consistency */ | ||||||
|  |    | ||||||
|  |   /* Glow Effects - lighter for light mode */ | ||||||
|  |   --glow-primary: rgba(6, 182, 212, 0.1); | ||||||
|  |   --glow-secondary: rgba(59, 130, 246, 0.1); | ||||||
|  |   --glow-tertiary: rgba(139, 92, 246, 0.1); | ||||||
|  |    | ||||||
|  |   /* Border Colors */ | ||||||
|  |   --border-primary: rgba(0, 0, 0, 0.1); | ||||||
|  |   --border-secondary: rgba(0, 0, 0, 0.05); | ||||||
|  |    | ||||||
|  |   /* Card Background */ | ||||||
|  |   --card-bg: rgba(255, 255, 255, 0.8); | ||||||
|  |   --card-border: rgba(56, 189, 248, 0.3); /* Slightly stronger border */ | ||||||
|  |    | ||||||
|  |   /* UI Element Colors */ | ||||||
|  |   --ui-element: #e2e8f0; | ||||||
|  |   --ui-element-hover: #cbd5e1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Background adjustments for light mode */ | ||||||
|  | html.light-mode body { | ||||||
|  |   background-image:  | ||||||
|  |     radial-gradient(circle at 20% 35%, rgba(6, 182, 212, 0.05) 0%, transparent 50%), | ||||||
|  |     radial-gradient(circle at 75% 15%, rgba(59, 130, 246, 0.05) 0%, transparent 45%), | ||||||
|  |     radial-gradient(circle at 85% 70%, rgba(139, 92, 246, 0.05) 0%, transparent 40%); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Adding light mode grid overlay */ | ||||||
|  | html.light-mode body::before { | ||||||
|  |   background-image:  | ||||||
|  |     linear-gradient(rgba(15, 23, 42, 0.03) 1px, transparent 1px), | ||||||
|  |     linear-gradient(90deg, rgba(15, 23, 42, 0.03) 1px, transparent 1px); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Theme transition for smooth switching */ | ||||||
|  | html, body, * { | ||||||
|  |   transition:  | ||||||
|  |     background-color 0.3s ease, | ||||||
|  |     color 0.3s ease, | ||||||
|  |     border-color 0.3s ease, | ||||||
|  |     box-shadow 0.3s ease; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Knowledge Graph light mode adjustments */ | ||||||
|  | html.light-mode .graph-container { | ||||||
|  |   background: rgba(248, 250, 252, 0.6); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | html.light-mode .graph-loading { | ||||||
|  |   background: rgba(241, 245, 249, 0.7); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | html.light-mode .graph-filters { | ||||||
|  |   background: rgba(241, 245, 249, 0.7); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | html.light-mode .graph-legend { | ||||||
|  |   background: rgba(241, 245, 249, 0.7); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | html.light-mode .node-details { | ||||||
|  |   background: rgba(248, 250, 252, 0.9); | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue