upgrade to different layout
This commit is contained in:
		
							
								
								
									
										648
									
								
								src/pages/topics/index.astro
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										648
									
								
								src/pages/topics/index.astro
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,648 @@
 | 
			
		||||
---
 | 
			
		||||
import BaseLayout from '../../layouts/BaseLayout.astro';
 | 
			
		||||
 | 
			
		||||
import directus from "../../../lib/directus"
 | 
			
		||||
import { readItems } from "@directus/sdk";
 | 
			
		||||
 | 
			
		||||
const posts = await directus.request(
 | 
			
		||||
  readItems("posts", {
 | 
			
		||||
    fields: ['*'],
 | 
			
		||||
    sort: ["-published_date"],
 | 
			
		||||
  })
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const tags = [...new Set(posts.flatMap(post => post.tags || []))].sort();
 | 
			
		||||
 | 
			
		||||
// Count posts for each tag and create tag objects with additional data
 | 
			
		||||
const tagObjects = tags.map(tag => {
 | 
			
		||||
  const count = posts.filter(post => post.tags?.includes(tag)).length;
 | 
			
		||||
  // Generate a consistent but random-looking hue for each tag
 | 
			
		||||
  const hue = Math.abs(tag.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0) % 360);
 | 
			
		||||
  return { 
 | 
			
		||||
    name: tag, 
 | 
			
		||||
    count,
 | 
			
		||||
    size: Math.max(1, Math.min(3, Math.floor(count / 2) + 1)), // Size 1-3 based on count
 | 
			
		||||
    hue
 | 
			
		||||
  };
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const sortedTags = [...tagObjects].sort((a, b) => b.count - a.count);
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
<BaseLayout title="Explore Tags">
 | 
			
		||||
  <div class="w-full mx-auto px-3 sm:px-6 py-6 sm:py-12 md:py-16 theme-transition-all">
 | 
			
		||||
    <!-- Enhanced header section with animated elements - improved for mobile -->
 | 
			
		||||
    <div class="relative mb-8 sm:mb-12 md:mb-16 text-center theme-transition-element">
 | 
			
		||||
      <div class="absolute -top-16 -left-16 w-36 sm:w-48 md:w-72 h-36 sm:h-48 md:h-72 bg-zinc-100 dark:bg-zinc-800/50 rounded-full blur-3xl opacity-50 animate-blob theme-transition-bg"></div>
 | 
			
		||||
      <div class="absolute -bottom-16 -right-16 w-36 sm:w-48 md:w-72 h-36 sm:h-48 md:h-72 bg-zinc-200 dark:bg-zinc-800/30 rounded-full blur-3xl opacity-30 animate-blob animation-delay-2000 theme-transition-bg"></div>
 | 
			
		||||
      <div class="absolute top-8 right-8 w-24 sm:w-32 md:w-40 h-24 sm:h-32 md:h-40 bg-zinc-100/30 dark:bg-zinc-700/20 rounded-full blur-2xl opacity-40 animate-blob animation-delay-4000 theme-transition-bg"></div>
 | 
			
		||||
      
 | 
			
		||||
      <h1 class="relative text-3xl sm:text-4xl md:text-5xl lg:text-6xl font-bold tracking-tight text-zinc-900 dark:text-zinc-100 mb-3 sm:mb-4 md:mb-6 theme-transition-color">
 | 
			
		||||
        <span class="inline-block relative">
 | 
			
		||||
          <span class="relative inline-block">
 | 
			
		||||
            <span class="absolute -inset-1 rounded-lg bg-gradient-to-r from-zinc-200/50 to-zinc-300/50 dark:from-zinc-800/50 dark:to-zinc-700/50 blur-sm theme-transition-bg"></span>
 | 
			
		||||
            <span class="relative">Explore</span>
 | 
			
		||||
          </span>
 | 
			
		||||
          {" "}
 | 
			
		||||
          <span class="relative inline-block">
 | 
			
		||||
            Topics
 | 
			
		||||
            <span class="absolute -bottom-1 sm:-bottom-2 left-0 w-full h-0.5 sm:h-1 bg-gradient-to-r from-zinc-400 to-zinc-600 dark:from-zinc-600 dark:to-zinc-400 transform origin-left animate-underline theme-transition-bg"></span>
 | 
			
		||||
          </span>
 | 
			
		||||
        </span>
 | 
			
		||||
      </h1>
 | 
			
		||||
      <p class="relative text-sm sm:text-base md:text-lg lg:text-xl text-zinc-600 dark:text-zinc-400 max-w-2xl mx-auto theme-transition-color">
 | 
			
		||||
        Discover content organized by your interests
 | 
			
		||||
      </p>
 | 
			
		||||
    </div>
 | 
			
		||||
    
 | 
			
		||||
    {tags.length === 0 ? (
 | 
			
		||||
      <div class="text-center py-8 sm:py-12 md:py-16 theme-transition-element">
 | 
			
		||||
        <div class="inline-flex items-center justify-center w-16 sm:w-20 md:w-24 h-16 sm:h-20 md:h-24 rounded-full bg-zinc-100 dark:bg-zinc-800 mb-3 sm:mb-4 md:mb-6 shadow-inner theme-transition-bg">
 | 
			
		||||
          <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-8 sm:w-10 md:w-12 h-8 sm:h-10 md:h-12 text-zinc-500 dark:text-zinc-400 theme-transition-color">
 | 
			
		||||
            <path stroke-linecap="round" stroke-linejoin="round" d="M9.568 3H5.25A2.25 2.25 0 003 5.25v4.318c0 .597.237 1.17.659 1.591l9.581 9.581c.699.699 1.78.872 2.607.33a18.095 18.095 0 005.223-5.223c.542-.827.369-1.908-.33-2.607L11.16 3.66A2.25 2.25 0 009.568 3z" />
 | 
			
		||||
            <path stroke-linecap="round" stroke-linejoin="round" d="M6 6h.008v.008H6V6z" />
 | 
			
		||||
          </svg>
 | 
			
		||||
        </div>
 | 
			
		||||
        <p class="text-lg sm:text-xl md:text-2xl font-medium text-zinc-800 dark:text-zinc-200 theme-transition-color">No tags found yet.</p>
 | 
			
		||||
        <p class="mt-2 text-xs sm:text-sm md:text-base text-zinc-500 dark:text-zinc-500 theme-transition-color">Check back later for categorized content.</p>
 | 
			
		||||
      </div>
 | 
			
		||||
    ) : (
 | 
			
		||||
      <div class="flex justify-center w-full">
 | 
			
		||||
        <!-- Featured Tags Section - ultra-responsive design -->
 | 
			
		||||
        <div class="tag-cloud relative p-3 sm:p-4 md:p-6 lg:p-8 rounded-lg sm:rounded-xl md:rounded-2xl lg:rounded-3xl border border-zinc-100 dark:border-zinc-800 bg-white/50 dark:bg-zinc-900/50 backdrop-blur-sm hover-3d glass theme-transition-all w-full">
 | 
			
		||||
          <div class="absolute inset-0 bg-grid-pattern opacity-5 dark:opacity-10 theme-transition-bg"></div>
 | 
			
		||||
          <div class="absolute -top-8 -right-8 w-20 sm:w-24 md:w-32 lg:w-40 h-20 sm:h-24 md:h-32 lg:h-40 bg-gradient-to-br from-zinc-200/30 to-zinc-300/20 dark:from-zinc-700/20 dark:to-zinc-800/10 rounded-full blur-xl theme-transition-bg"></div>
 | 
			
		||||
          <div class="absolute -bottom-8 -left-8 w-20 sm:w-24 md:w-32 lg:w-40 h-20 sm:h-24 md:h-32 lg:h-40 bg-gradient-to-tl from-zinc-200/30 to-zinc-300/20 dark:from-zinc-700/20 dark:to-zinc-800/10 rounded-full blur-xl theme-transition-bg"></div>
 | 
			
		||||
          
 | 
			
		||||
          <h2 class="text-lg sm:text-xl md:text-2xl lg:text-3xl font-bold text-zinc-900 dark:text-zinc-100 mb-3 sm:mb-4 md:mb-6 lg:mb-8 text-center theme-transition-color">Popular Topics</h2>
 | 
			
		||||
          
 | 
			
		||||
          <!-- Ultra-responsive grid layout with fallbacks -->
 | 
			
		||||
          <div class="grid grid-cols-2 xxxs:grid-cols-2 xxs:grid-cols-2 xs:grid-cols-3 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-1.5 xxxs:gap-2 xxs:gap-2 xs:gap-2 sm:gap-3 md:gap-4 w-full">
 | 
			
		||||
            {sortedTags.map((tag) => (
 | 
			
		||||
              <a 
 | 
			
		||||
                href={`/topics/${tag.name}`}
 | 
			
		||||
                class="group relative overflow-hidden rounded-md sm:rounded-lg md:rounded-xl border border-zinc-200 dark:border-zinc-800 transition-all duration-300 hover:shadow-md sm:hover:shadow-lg hover:scale-[1.03] hover:border-zinc-300 dark:hover:border-zinc-700 active:scale-95 theme-transition-element theme-ripple flex-grow min-w-0"
 | 
			
		||||
                style={`--tag-hue: ${tag.hue};`}
 | 
			
		||||
              >
 | 
			
		||||
                <div class="absolute inset-0 bg-gradient-to-br from-zinc-50/90 to-zinc-100/90 dark:from-zinc-800/90 dark:to-zinc-900/90 opacity-100 group-hover:opacity-95 transition-opacity theme-transition-bg"></div>
 | 
			
		||||
                
 | 
			
		||||
                <div class="relative px-1.5 xxxs:px-2 xxs:px-2 xs:px-2 sm:px-3 md:px-4 py-1.5 xxxs:py-2 xxs:py-2 xs:py-2 sm:py-3 md:py-4 flex items-center gap-1.5 xxs:gap-2 w-full">
 | 
			
		||||
                  <div class="flex-shrink-0 flex items-center justify-center w-5 h-5 xxxs:w-6 xxxs:h-6 xxs:w-6 xxs:h-6 xs:w-7 xs:h-7 sm:w-8 sm:h-8 md:w-10 md:h-10 rounded-full bg-zinc-100 dark:bg-zinc-800 text-zinc-700 dark:text-zinc-300 group-hover:bg-accent/20 dark:group-hover:bg-accent/20 group-hover:text-accent-dark dark:group-hover:text-accent-light transition-all duration-300 shadow-sm theme-transition-all">
 | 
			
		||||
                    <span class="text-xs xxxs:text-xs xxs:text-xs xs:text-sm sm:text-base md:text-lg font-semibold">#</span>
 | 
			
		||||
                  </div>
 | 
			
		||||
                  
 | 
			
		||||
                  <div class="flex-1 min-w-0 overflow-hidden">
 | 
			
		||||
                    <h3 class="text-[10px] xxxs:text-xs xxs:text-xs xs:text-xs sm:text-sm md:text-base font-bold text-zinc-900 dark:text-zinc-100 group-hover:text-zinc-700 dark:group-hover:text-zinc-300 transition-colors theme-transition-color break-words hyphens-auto truncate">
 | 
			
		||||
                      {tag.name}
 | 
			
		||||
                    </h3>
 | 
			
		||||
                    <p class="text-[8px] xxxs:text-[9px] xxs:text-[9px] xs:text-[10px] sm:text-xs md:text-xs text-zinc-500 dark:text-zinc-400 theme-transition-color truncate">{tag.count} article{tag.count !== 1 ? 's' : ''}</p>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </div>
 | 
			
		||||
              </a>
 | 
			
		||||
            ))}
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    )}
 | 
			
		||||
  </div>
 | 
			
		||||
</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"]');
 | 
			
		||||
      if (!viewport) {
 | 
			
		||||
        const meta = document.createElement('meta');
 | 
			
		||||
        meta.name = 'viewport';
 | 
			
		||||
        meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no';
 | 
			
		||||
        document.getElementsByTagName('head')[0].appendChild(meta);
 | 
			
		||||
      } else {
 | 
			
		||||
        viewport.setAttribute('content', 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no');
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      // Fix for horizontal overflow
 | 
			
		||||
      document.body.style.overflowX = 'hidden';
 | 
			
		||||
      document.documentElement.style.overflowX = 'hidden';
 | 
			
		||||
      document.documentElement.style.width = '100%';
 | 
			
		||||
      document.body.style.width = '100%';
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    fixViewportWidth();
 | 
			
		||||
    
 | 
			
		||||
    // Adjust tag items based on screen size with extreme precision
 | 
			
		||||
    const adjustTagItems = () => {
 | 
			
		||||
      const tagItems = document.querySelectorAll('.theme-ripple');
 | 
			
		||||
      const width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
 | 
			
		||||
      const isVerySmall = width < 360;
 | 
			
		||||
      const isExtremelySmall = width < 280;
 | 
			
		||||
      const isMicroScreen = width < 240;
 | 
			
		||||
      
 | 
			
		||||
      // Fix container width to match viewport exactly
 | 
			
		||||
      const container = document.querySelector('.tag-cloud');
 | 
			
		||||
      if (container) {
 | 
			
		||||
        container.style.maxWidth = '100vw';
 | 
			
		||||
        container.style.width = '100%';
 | 
			
		||||
        container.style.boxSizing = 'border-box';
 | 
			
		||||
        
 | 
			
		||||
        // Remove any margins that might cause overflow
 | 
			
		||||
        container.style.marginLeft = '0';
 | 
			
		||||
        container.style.marginRight = '0';
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      // Fix grid width
 | 
			
		||||
      const grid = document.querySelector('.grid');
 | 
			
		||||
      if (grid) {
 | 
			
		||||
        grid.style.width = '100%';
 | 
			
		||||
        grid.style.maxWidth = '100%';
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      tagItems.forEach(item => {
 | 
			
		||||
        // Set appropriate classes based on screen size
 | 
			
		||||
        if (isMicroScreen) {
 | 
			
		||||
          item.classList.add('micro-screen');
 | 
			
		||||
          item.classList.remove('extremely-small-screen', 'very-small-screen');
 | 
			
		||||
        } else if (isExtremelySmall) {
 | 
			
		||||
          item.classList.add('extremely-small-screen');
 | 
			
		||||
          item.classList.remove('very-small-screen', 'micro-screen');
 | 
			
		||||
        } else if (isVerySmall) {
 | 
			
		||||
          item.classList.add('very-small-screen');
 | 
			
		||||
          item.classList.remove('extremely-small-screen', 'micro-screen');
 | 
			
		||||
        } else {
 | 
			
		||||
          item.classList.remove('very-small-screen', 'extremely-small-screen', 'micro-screen');
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Ensure text doesn't overflow on small screens
 | 
			
		||||
        const tagName = item.querySelector('h3');
 | 
			
		||||
        const tagCount = item.querySelector('p');
 | 
			
		||||
        
 | 
			
		||||
        if (tagName) {
 | 
			
		||||
          // Set title for accessibility
 | 
			
		||||
          tagName.title = tagName.textContent.trim();
 | 
			
		||||
          
 | 
			
		||||
          // Adjust text length based on screen size
 | 
			
		||||
          if (isMicroScreen && tagName.textContent.length > 6) {
 | 
			
		||||
            tagName.dataset.fullText = tagName.textContent;
 | 
			
		||||
            tagName.textContent = tagName.textContent.substring(0, 6) + '...';
 | 
			
		||||
          } else if (isExtremelySmall && tagName.textContent.length > 8) {
 | 
			
		||||
            tagName.dataset.fullText = tagName.textContent;
 | 
			
		||||
            tagName.textContent = tagName.textContent.substring(0, 8) + '...';
 | 
			
		||||
          } else if (isVerySmall && tagName.textContent.length > 12) {
 | 
			
		||||
            tagName.dataset.fullText = tagName.textContent;
 | 
			
		||||
            tagName.textContent = tagName.textContent.substring(0, 12) + '...';
 | 
			
		||||
          } else if (tagName.dataset.fullText) {
 | 
			
		||||
            tagName.textContent = tagName.dataset.fullText;
 | 
			
		||||
            delete tagName.dataset.fullText;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Set the tag hue for hover effects
 | 
			
		||||
        const hue = item.style.getPropertyValue('--tag-hue');
 | 
			
		||||
        item.style.setProperty('--hover-hue', hue);
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    // Run on load
 | 
			
		||||
    adjustTagItems();
 | 
			
		||||
    
 | 
			
		||||
    // Run on resize with optimized debounce
 | 
			
		||||
    let resizeTimer;
 | 
			
		||||
    const handleResize = () => {
 | 
			
		||||
      if (resizeTimer) {
 | 
			
		||||
        window.cancelAnimationFrame(resizeTimer);
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      resizeTimer = window.requestAnimationFrame(() => {
 | 
			
		||||
        fixViewportWidth();
 | 
			
		||||
        adjustTagItems();
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    window.addEventListener('resize', handleResize);
 | 
			
		||||
    window.addEventListener('orientationchange', handleResize);
 | 
			
		||||
    
 | 
			
		||||
    // Ensure layout is recalculated after page is fully loaded
 | 
			
		||||
    window.addEventListener('load', () => {
 | 
			
		||||
      fixViewportWidth();
 | 
			
		||||
      adjustTagItems();
 | 
			
		||||
      // Force recalculation after images and fonts are loaded
 | 
			
		||||
      setTimeout(() => {
 | 
			
		||||
        fixViewportWidth();
 | 
			
		||||
        adjustTagItems();
 | 
			
		||||
      }, 500);
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    // Fix for iOS Safari and other mobile browsers
 | 
			
		||||
    if (/iPhone|iPad|iPod|Android/.test(navigator.userAgent)) {
 | 
			
		||||
      document.documentElement.style.setProperty('--safe-area-inset-bottom', 'env(safe-area-inset-bottom)');
 | 
			
		||||
      
 | 
			
		||||
      // Fix for mobile viewport height issues
 | 
			
		||||
      const setVh = () => {
 | 
			
		||||
        const vh = window.innerHeight * 0.01;
 | 
			
		||||
        document.documentElement.style.setProperty('--vh', `${vh}px`);
 | 
			
		||||
      };
 | 
			
		||||
      
 | 
			
		||||
      setVh();
 | 
			
		||||
      window.addEventListener('resize', setVh);
 | 
			
		||||
      window.addEventListener('orientationchange', () => {
 | 
			
		||||
        // Wait for orientation change to complete
 | 
			
		||||
        setTimeout(() => {
 | 
			
		||||
          setVh();
 | 
			
		||||
          fixViewportWidth();
 | 
			
		||||
        }, 100);
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Add touch support for mobile devices
 | 
			
		||||
    const addTouchSupport = () => {
 | 
			
		||||
      const tagItems = document.querySelectorAll('.theme-ripple');
 | 
			
		||||
      
 | 
			
		||||
      tagItems.forEach(item => {
 | 
			
		||||
        item.addEventListener('touchstart', () => {
 | 
			
		||||
          item.classList.add('touch-active');
 | 
			
		||||
        }, { passive: true });
 | 
			
		||||
        
 | 
			
		||||
        item.addEventListener('touchend', () => {
 | 
			
		||||
          setTimeout(() => {
 | 
			
		||||
            item.classList.remove('touch-active');
 | 
			
		||||
          }, 150);
 | 
			
		||||
        }, { passive: true });
 | 
			
		||||
        
 | 
			
		||||
        // Cancel active state if touch moves away
 | 
			
		||||
        item.addEventListener('touchmove', (e) => {
 | 
			
		||||
          const touch = e.touches[0];
 | 
			
		||||
          const rect = item.getBoundingClientRect();
 | 
			
		||||
          
 | 
			
		||||
          if (
 | 
			
		||||
            touch.clientX < rect.left || 
 | 
			
		||||
            touch.clientX > rect.right || 
 | 
			
		||||
            touch.clientY < rect.top || 
 | 
			
		||||
            touch.clientY > rect.bottom
 | 
			
		||||
          ) {
 | 
			
		||||
            item.classList.remove('touch-active');
 | 
			
		||||
          }
 | 
			
		||||
        }, { passive: true });
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    addTouchSupport();
 | 
			
		||||
  });
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style>
 | 
			
		||||
  /* Base styles */
 | 
			
		||||
  .tag-cloud {
 | 
			
		||||
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.03), 
 | 
			
		||||
                0 2px 4px rgba(0, 0, 0, 0.03), 
 | 
			
		||||
                0 4px 8px rgba(0, 0, 0, 0.05);
 | 
			
		||||
    transform-style: preserve-3d;
 | 
			
		||||
    perspective: 1000px;
 | 
			
		||||
    transition: all var(--theme-transition);
 | 
			
		||||
    width: 100% !important;
 | 
			
		||||
    max-width: 100% !important;
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
    margin-left: 0 !important;
 | 
			
		||||
    margin-right: 0 !important;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  /* Fix for horizontal overflow */
 | 
			
		||||
  :global(html), :global(body) {
 | 
			
		||||
    overflow-x: hidden;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    max-width: 100%;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  :global(.max-w-6xl) {
 | 
			
		||||
    max-width: 100% !important;
 | 
			
		||||
    width: 100% !important;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  /* Ultra-responsive breakpoints for extreme reliability */
 | 
			
		||||
  /* Micro screens (below 240px) */
 | 
			
		||||
  @media (max-width: 239px) {
 | 
			
		||||
    .tag-cloud {
 | 
			
		||||
      padding: 0.5rem !important;
 | 
			
		||||
      margin: 0 !important;
 | 
			
		||||
      border-radius: 0.25rem !important;
 | 
			
		||||
      width: 100% !important;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .tag-cloud h2 {
 | 
			
		||||
      font-size: 0.875rem !important;
 | 
			
		||||
      margin-bottom: 0.375rem !important;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .grid {
 | 
			
		||||
      grid-template-columns: repeat(1, minmax(0, 1fr)) !important;
 | 
			
		||||
      gap: 0.375rem !important;
 | 
			
		||||
      width: 100% !important;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .micro-screen .flex {
 | 
			
		||||
      padding: 0.25rem !important;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .micro-screen h3 {
 | 
			
		||||
      font-size: 0.625rem !important;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .micro-screen p {
 | 
			
		||||
      font-size: 0.5rem !important;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  /* Extra extra extra small screens (240px-279px) */
 | 
			
		||||
  @media (min-width: 240px) and (max-width: 279px) {
 | 
			
		||||
    .xxxs\:grid-cols-2 {
 | 
			
		||||
      grid-template-columns: repeat(2, minmax(0, 1fr));
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .xxxs\:px-2 {
 | 
			
		||||
      padding-left: 0.5rem;
 | 
			
		||||
      padding-right: 0.5rem;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .xxxs\:py-2 {
 | 
			
		||||
      padding-top: 0.5rem;
 | 
			
		||||
      padding-bottom: 0.5rem;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .xxxs\:w-6 {
 | 
			
		||||
      width: 1.5rem;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .xxxs\:h-6 {
 | 
			
		||||
      height: 1.5rem;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .xxxs\:text-xs {
 | 
			
		||||
      font-size: 0.75rem;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .xxxs\:gap-2 {
 | 
			
		||||
      gap: 0.5rem;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .xxxs\:text-\[9px\] {
 | 
			
		||||
      font-size: 9px;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  /* Extra extra small screens (280px-359px) */
 | 
			
		||||
  @media (min-width: 280px) {
 | 
			
		||||
    .xxs\:grid-cols-2 {
 | 
			
		||||
      grid-template-columns: repeat(2, minmax(0, 1fr));
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .xxs\:px-2 {
 | 
			
		||||
      padding-left: 0.5rem;
 | 
			
		||||
      padding-right: 0.5rem;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .xxs\:py-2 {
 | 
			
		||||
      padding-top: 0.5rem;
 | 
			
		||||
      padding-bottom: 0.5rem;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .xxs\:w-6 {
 | 
			
		||||
      width: 1.5rem;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .xxs\:h-6 {
 | 
			
		||||
      height: 1.5rem;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .xxs\:text-xs {
 | 
			
		||||
      font-size: 0.75rem;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .xxs\:gap-2 {
 | 
			
		||||
      gap: 0.5rem;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .xxs\:text-\[9px\] {
 | 
			
		||||
      font-size: 9px;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  /* Extra small screens (360px-639px) */
 | 
			
		||||
  @media (min-width: 360px) {
 | 
			
		||||
    .xs\:grid-cols-3 {
 | 
			
		||||
      grid-template-columns: repeat(3, minmax(0, 1fr));
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .xs\:px-2 {
 | 
			
		||||
      padding-left: 0.5rem;
 | 
			
		||||
      padding-right: 0.5rem;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .xs\:py-2 {
 | 
			
		||||
      padding-top: 0.5rem;
 | 
			
		||||
      padding-bottom: 0.5rem;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .xs\:w-7 {
 | 
			
		||||
      width: 1.75rem;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .xs\:h-7 {
 | 
			
		||||
      height: 1.75rem;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .xs\:text-xs {
 | 
			
		||||
      font-size: 0.75rem;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .xs\:text-sm {
 | 
			
		||||
      font-size: 0.875rem;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .xs\:gap-2 {
 | 
			
		||||
      gap: 0.5rem;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .xs\:text-\[10px\] {
 | 
			
		||||
      font-size: 10px;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  /* Ensure text doesn't overflow on small screens */
 | 
			
		||||
  .truncate {
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
    text-overflow: ellipsis;
 | 
			
		||||
    white-space: nowrap;
 | 
			
		||||
    max-width: 100%;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  /* Ensure proper word breaking for long tag names */
 | 
			
		||||
  .break-words {
 | 
			
		||||
    word-break: break-word;
 | 
			
		||||
    overflow-wrap: break-word;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .hyphens-auto {
 | 
			
		||||
    hyphens: auto;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  /* Improved shadow for dark mode */
 | 
			
		||||
  :global(.dark) .tag-cloud {
 | 
			
		||||
    box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.05),
 | 
			
		||||
                0 2px 4px rgba(0, 0, 0, 0.1),
 | 
			
		||||
                0 4px 8px rgba(0, 0, 0, 0.15);
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  /* Prevent layout shifts */
 | 
			
		||||
  .flex-grow {
 | 
			
		||||
    flex-grow: 1;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  .min-w-0 {
 | 
			
		||||
    min-width: 0;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  /* Ensure container doesn't overflow */
 | 
			
		||||
  .overflow-hidden {
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  /* Touch support for mobile */
 | 
			
		||||
  .touch-active {
 | 
			
		||||
    transform: scale(0.97) !important;
 | 
			
		||||
    opacity: 0.9;
 | 
			
		||||
    transition: transform 0.15s ease-in-out, opacity 0.15s ease-in-out !important;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  /* Animation for blob */
 | 
			
		||||
  @keyframes blob {
 | 
			
		||||
    0%, 100% {
 | 
			
		||||
      transform: translate(0, 0) scale(1);
 | 
			
		||||
    }
 | 
			
		||||
    25% {
 | 
			
		||||
      transform: translate(10px, -10px) scale(1.05);
 | 
			
		||||
    }
 | 
			
		||||
    50% {
 | 
			
		||||
      transform: translate(0, 20px) scale(0.95);
 | 
			
		||||
    }
 | 
			
		||||
    75% {
 | 
			
		||||
      transform: translate(-10px, -10px) scale(1.05);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  .animate-blob {
 | 
			
		||||
    animation: blob 20s infinite ease-in-out;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  .animation-delay-2000 {
 | 
			
		||||
    animation-delay: 2s;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  .animation-delay-4000 {
 | 
			
		||||
    animation-delay: 4s;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  /* Animation for underline */
 | 
			
		||||
  @keyframes underline {
 | 
			
		||||
    0% {
 | 
			
		||||
      transform: scaleX(0);
 | 
			
		||||
    }
 | 
			
		||||
    100% {
 | 
			
		||||
      transform: scaleX(1);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  .animate-underline {
 | 
			
		||||
    animation: underline 1.5s ease-out forwards;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  /* Fix for iOS Safari notch */
 | 
			
		||||
  @supports (padding: max(0px)) {
 | 
			
		||||
    .tag-cloud {
 | 
			
		||||
      padding-left: max(0.75rem, env(safe-area-inset-left));
 | 
			
		||||
      padding-right: max(0.75rem, env(safe-area-inset-right));
 | 
			
		||||
      padding-bottom: max(0.75rem, env(safe-area-inset-bottom));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
</style>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
  // Handle SPA transitions for tags index page
 | 
			
		||||
  function setupSPATransitions() {
 | 
			
		||||
    // Handle all internal links for SPA transitions
 | 
			
		||||
    document.querySelectorAll('a[href^="/"]').forEach(link => {
 | 
			
		||||
      // Skip links that are anchor links, external links, or already processed
 | 
			
		||||
      if (link.getAttribute('href').includes('#') || 
 | 
			
		||||
          link.getAttribute('target') === '_blank' || 
 | 
			
		||||
          link.hasAttribute('data-spa-handled')) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      // Mark as handled to avoid duplicate listeners
 | 
			
		||||
      link.setAttribute('data-spa-handled', 'true');
 | 
			
		||||
      
 | 
			
		||||
      link.addEventListener('click', (e) => {
 | 
			
		||||
        // Don't handle if modifier keys are pressed (for opening in new tab, etc.)
 | 
			
		||||
        if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) {
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        e.preventDefault();
 | 
			
		||||
        const targetHref = link.getAttribute('href');
 | 
			
		||||
        
 | 
			
		||||
        // Trigger page transition animation
 | 
			
		||||
        const pageTransition = document.getElementById('page-transition');
 | 
			
		||||
        if (pageTransition) {
 | 
			
		||||
          pageTransition.classList.remove('opacity-0');
 | 
			
		||||
          pageTransition.classList.add('opacity-100');
 | 
			
		||||
          
 | 
			
		||||
          // Navigate after transition effect
 | 
			
		||||
          setTimeout(() => {
 | 
			
		||||
            window.location.href = targetHref;
 | 
			
		||||
          }, 300);
 | 
			
		||||
        } else {
 | 
			
		||||
          // Fallback if transition element doesn't exist
 | 
			
		||||
          window.location.href = targetHref;
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    // Add hover effect for tag cards on touch devices
 | 
			
		||||
    const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
 | 
			
		||||
    
 | 
			
		||||
    if (isTouchDevice) {
 | 
			
		||||
      const tagCards = document.querySelectorAll('.tag-cloud a');
 | 
			
		||||
      
 | 
			
		||||
      tagCards.forEach(card => {
 | 
			
		||||
        card.addEventListener('touchstart', () => {
 | 
			
		||||
          card.classList.add('is-touched');
 | 
			
		||||
        });
 | 
			
		||||
        
 | 
			
		||||
        card.addEventListener('touchend', () => {
 | 
			
		||||
          setTimeout(() => {
 | 
			
		||||
            card.classList.remove('is-touched');
 | 
			
		||||
          }, 300);
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Animate tag cards with staggered delay
 | 
			
		||||
    const tagCards = document.querySelectorAll('.tag-cloud a');
 | 
			
		||||
    tagCards.forEach((card, index) => {
 | 
			
		||||
      setTimeout(() => {
 | 
			
		||||
        card.classList.add('animate-reveal');
 | 
			
		||||
      }, 100 + (index * 50));
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  // Initialize on first load
 | 
			
		||||
  document.addEventListener('DOMContentLoaded', setupSPATransitions);
 | 
			
		||||
  
 | 
			
		||||
  // Re-initialize when content changes via Astro's view transitions
 | 
			
		||||
  document.addEventListener('astro:page-load', setupSPATransitions);
 | 
			
		||||
  
 | 
			
		||||
  // For compatibility with custom transition system
 | 
			
		||||
  document.addEventListener('page-transition-complete', setupSPATransitions);
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user