Fork of Chiri for Astro for my blog
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

chore: tweak image handling

the3ash 6f77453e 57b52ca9

+35 -134
+18 -98
src/components/ui/ImageOptimizer.astro
··· 1 - --- 2 - import { Image } from 'astro:assets' 3 - import type { ImageOptimizerProps } from '../../types/component.types' 4 - 5 - const { 6 - src, 7 - alt, 8 - width, 9 - height, 10 - quality = 85, 11 - format = 'webp', 12 - loading = 'lazy', 13 - decoding = 'async', 14 - class: className, 15 - caption, 16 - priority = false 17 - } = Astro.props as ImageOptimizerProps 18 - 19 - // Use eager loading for priority images 20 - const actualLoading = priority ? 'eager' : loading 21 - 22 - // Generate responsive image sizes 23 - const sizes = '(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw' 24 - --- 25 - 26 - <figure class={`optimized-image ${className || ''}`}> 27 - { 28 - typeof src === 'string' ? ( 29 - <img 30 - src={src} 31 - alt={alt} 32 - width={width} 33 - height={height} 34 - loading={actualLoading} 35 - decoding={decoding} 36 - fetchpriority={priority ? 'high' : 'auto'} 37 - sizes={sizes} 38 - class="responsive-image" 39 - /> 40 - ) : ( 41 - <Image 42 - src={src} 43 - alt={alt} 44 - width={width} 45 - height={height} 46 - quality={quality} 47 - format={format} 48 - loading={actualLoading} 49 - decoding={decoding} 50 - fetchpriority={priority ? 'high' : 'auto'} 51 - sizes={sizes} 52 - class="responsive-image" 53 - /> 54 - ) 55 - } 56 - {caption && <figcaption>{caption}</figcaption>} 57 - </figure> 58 - 59 - <style> 60 - .optimized-image { 61 - margin: 1.25em 0; 62 - text-align: center; 63 - } 64 - 65 - .optimized-image .responsive-image { 66 - max-width: 100%; 67 - height: auto; 68 - border-radius: 4px; 69 - transition: opacity 0.3s ease; 70 - background-color: var(--bg-secondary); 71 - } 72 - 73 - .optimized-image figcaption { 74 - color: var(--text-secondary); 75 - font-size: var(--font-size-s); 76 - margin-top: 0.75em; 77 - text-align: center; 78 - } 79 - 80 - /* Loading state */ 81 - .optimized-image .responsive-image[loading='lazy'] { 82 - opacity: 0; 83 - animation: fadeIn 0.3s ease forwards; 84 - } 85 - 86 - @keyframes fadeIn { 87 - from { 88 - opacity: 0; 89 - } 90 - to { 91 - opacity: 1; 92 - } 1 + <script is:inline> 2 + // Global handler: Automatically remove the img-placeholder class after images are loaded 3 + function removeImgPlaceholder() { 4 + document.querySelectorAll('img.img-placeholder').forEach(function (img) { 5 + if (img.complete) { 6 + img.classList.remove('img-placeholder') 7 + } else { 8 + img.addEventListener('load', function () { 9 + img.classList.remove('img-placeholder') 10 + }) 11 + } 12 + }) 93 13 } 94 - 95 - /* Hover effect for interactive images */ 96 - .optimized-image .responsive-image:hover { 97 - transform: scale(1.02); 98 - transition: transform 0.3s ease; 14 + if (document.readyState === 'loading') { 15 + document.addEventListener('DOMContentLoaded', removeImgPlaceholder) 16 + } else { 17 + removeImgPlaceholder() 99 18 } 100 - </style> 19 + document.addEventListener('astro:page-load', removeImgPlaceholder) 20 + </script>
+2
src/layouts/PostLayout.astro
··· 10 10 import GradientMask from '@/components/ui/GradientMask.astro' 11 11 import ImageViewer from '@/components/ui/ImageViewer.astro' 12 12 import GitHubCard from '@/components/ui/GitHubCard.astro' 13 + import ImageOptimizer from '@/components/ui/ImageOptimizer.astro' 13 14 import XPOST from '@/components/ui/XPOST.astro' 14 15 import CopyCode from '@/components/ui/CopyCode.astro' 15 16 import BaseLayout from '@/layouts/BaseLayout.astro' ··· 60 61 <CopyCode /> 61 62 <GitHubCard /> 62 63 <XPOST /> 64 + <ImageOptimizer /> 63 65 {themeConfig.post.imageViewer && <ImageViewer />} 64 66 {themeConfig.general.footer && <Footer />} 65 67 </div>
+2 -1
src/plugins/rehype-image-processor.mjs
··· 47 47 // Add decoding hint for better performance 48 48 decoding: 'async', 49 49 // Add fetchpriority for critical images (first image gets high priority) 50 - fetchpriority: newNodes.length === 0 ? 'high' : 'auto' 50 + fetchpriority: newNodes.length === 0 ? 'high' : 'auto', 51 + class: [...(imgNode.properties.class || []), 'img-placeholder'] 51 52 } 52 53 53 54 if (!alt || alt.includes('_')) {
+3 -3
src/styles/global.css
··· 30 30 --text-tertiary: rgba(0, 0, 0, 0.24); 31 31 --border: rgba(0, 0, 0, 0.1); 32 32 --selection: rgba(0, 0, 0, 0.08); 33 - --code-bg: rgba(0, 0, 0, 0.06); 33 + --code-bg: rgba(0, 0, 0, 0.04); 34 34 --mark: #f3ffc4; 35 35 36 36 /* Syntax Theme */ ··· 55 55 --text-tertiary: rgba(0, 0, 0, 0.24); 56 56 --border: rgba(0, 0, 0, 0.1); 57 57 --selection: rgba(0, 0, 0, 0.08); 58 - --code-bg: rgba(0, 0, 0, 0.06); 58 + --code-bg: rgba(0, 0, 0, 0.04); 59 59 --mark: #f3ffc4; 60 60 61 61 /* Syntax Theme */ ··· 80 80 --text-tertiary: rgba(255, 255, 255, 0.24); 81 81 --border: rgba(255, 255, 255, 0.1); 82 82 --selection: rgba(255, 255, 255, 0.08); 83 - --code-bg: rgba(255, 255, 255, 0.06); 83 + --code-bg: rgba(255, 255, 255, 0.04); 84 84 --mark: #545b37; 85 85 86 86 /* Syntax Theme */
+10 -32
src/styles/post.css
··· 100 100 font-weight: var(--font-weight-bold); 101 101 } 102 102 103 - /* Images with performance optimizations */ 103 + /* Images */ 104 104 .prose img { 105 105 max-width: 100%; 106 106 height: auto; 107 107 display: block; 108 108 margin: 1.25em 0; 109 - background-color: var(--astro-code-background); 110 - transition: opacity 0.3s ease-in-out; 109 + } 110 + 111 + .img-placeholder { 112 + background: var(--code-bg); 113 + display: block; 111 114 } 112 115 113 116 /* Loading state for images */ 114 117 .prose img[loading='lazy'] { 115 118 opacity: 0; 116 - animation: fadeIn 0.3s ease-in-out forwards; 119 + animation: fadeIn 0.3s ease-out forwards; 117 120 } 118 121 119 122 @keyframes fadeIn { ··· 125 128 } 126 129 } 127 130 128 - /* Ensure images don't cause layout shifts */ 129 - .prose img:not([width]):not([height]) { 130 - aspect-ratio: attr(width) / attr(height); 131 - } 132 - 133 - /* Add skeleton loading effect */ 134 - .prose img[loading='lazy']:not([src]) { 135 - background: linear-gradient( 136 - 90deg, 137 - var(--bg-secondary) 25%, 138 - var(--bg-tertiary) 50%, 139 - var(--bg-secondary) 75% 140 - ); 141 - background-size: 200% 100%; 142 - animation: loading 1.5s infinite; 143 - } 144 - 145 - @keyframes loading { 146 - 0% { 147 - background-position: 200% 0; 148 - } 149 - 100% { 150 - background-position: -200% 0; 151 - } 152 - } 153 - 154 131 .prose figure { 155 132 margin-bottom: 1.25em; 156 133 text-align: center; ··· 193 170 194 171 /* Inline code */ 195 172 .prose code { 196 - padding: 3px 4px; 197 - border-radius: 5px; 173 + padding: 3px 5px; 174 + border-radius: 6px; 198 175 background-color: var(--code-bg); 176 + border: 0.5px solid var(--border); 199 177 font-family: var(--font-mono); 200 178 font-size: 0.9em; 201 179 font-feature-settings: