How I Made My Blog Faster and More Feature-Rich
Recently, I’ve been feeling that my blog loads slowly.
Especially with image-heavy articles, it takes time to display, which can be stressful when reading.
So I decided to take the plunge and work on improving my blog.
What I Implemented
Performance Improvements
The first thing I tackled was improving loading speed.
Image Optimization
Problems
- Images were being served as PNG or JPEG, with large file sizes
- Always loading the same size images regardless of screen size
- Lazy loading wasn’t implemented
Solutions
I created a component called OptimizedImage.astro.
<Picture
src={imageSrc}
alt={alt}
formats={['webp', 'jpeg']}
widths={[400, 800, 1200]}
sizes="(max-width: 400px) 400px, (max-width: 800px) 800px, 1200px"
loading={loading}
/>
Using Astro’s Picture component, I automatically convert to WebP format and generate multiple sizes.
Results
- Average 60-70% reduction in image file sizes
- Now serving smaller images on mobile and larger on desktop, reducing unnecessary data transfer
- Noticeably faster image display
Critical CSS Implementation
Problems
- Loading all CSS at once made initial display slow
- Unused styles were loaded from the start
Solutions
I implemented Critical CSS in MainLayout.astro.
I embedded only the minimal CSS needed for the first view inline, and loaded the rest asynchronously.
<style is:inline>
/* Critical CSS */
body { margin: 0; font-family: sans-serif; }
.container { max-width: 1200px; margin: 0 auto; }
/* ... */
</style>
Results
- First Contentful Paint (FCP) improved by about 30%
- Visibly faster initial page display
Service Worker Cache Strategy
Problems
- Fetching the same resources from the server repeatedly
- Couldn’t display anything offline
Solutions
I created sw.js and implemented a cache-first strategy.
// Cache-first strategy
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request).then((fetchResponse) => {
return caches.open(CACHE_NAME).then((cache) => {
cache.put(event.request, fetchResponse.clone());
return fetchResponse;
});
});
})
);
});
Results
- Blazing fast access from the second visit (served instantly from cache)
- Previously viewed pages can be displayed even offline
Featured Posts Carousel Improvements
Problems
- Black spaces next to images looked bad
- Couldn’t tell what articles were about at all
Solutions
I significantly revised FeaturedPostsCarousel.astro.
First, I created a function to get excerpts from article content:
function getExcerpt(body: string, maxLength: number = 150): string {
if (!body) return '';
const plainText = body
.replace(/^#+\s+/gm, '') // Remove headings
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') // Convert links to text
.replace(/[*_~`]/g, '') // Remove markdown formatting
.trim();
return plainText.length > maxLength
? plainText.substring(0, maxLength) + '...'
: plainText;
}
Then changed the layout to overlay text on images:
.carousel-text-overlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: linear-gradient(to top, rgba(0,0,0,0.9), transparent);
color: white !important;
}
Results
- Article content is now visible at a glance
- Modern and stylish design
- Text is readable in both light and dark modes
New Feature Additions
Besides performance improvements, I also added features to improve usability.
PWA Support
Problems
- Only accessible through browser
- Just a shortcut when added to home screen
Solutions
Created manifest.json:
{
"name": "semiramisu blog",
"short_name": "semiramisu",
"start_url": "/",
"display": "standalone",
"theme_color": "#0ea5e9",
"background_color": "#ffffff",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
}
]
}
Combined with Service Worker to function as a complete PWA.
Results
- When added to smartphone home screen, looks like a native app
- Address bar hidden for an immersive experience
- Basic functionality works even offline
Comment Feature (Giscus)
Problems
- No way to receive feedback on articles
- Can’t communicate with readers
Solutions
Created Comments.astro component and integrated Giscus:
<script src="https://giscus.app/client.js"
data-repo="semiramisu/semiramisu.github.io"
data-repo-id="YOUR_REPO_ID"
data-category="Comments"
data-category-id="YOUR_CATEGORY_ID"
data-mapping="pathname"
data-theme={theme}
async>
</script>
Also implemented dark mode support, so the comment section theme automatically changes when the theme switches.
Results
- Users can log in with GitHub account and comment
- GitHub handles spam prevention, so it’s secure
- Conversations can continue in discussion format
Enhanced Search Functionality
Problems
- Search function wasn’t working at all
- Pagefind script wasn’t loading correctly
Solutions
Fixed SearchBar.astro and properly initialized Pagefind:
// Dynamically load Pagefind script
const script = document.createElement('script');
script.src = '/pagefind/pagefind.js';
document.head.appendChild(script);
// Execute search
const search = await pagefind.search(query);
Also improved search result display to show just title and tags simply:
<a href="${data.url}" class="search-result-item">
<div class="search-result-title">${title}</div>
<div class="search-result-tags">${tagsHtml}</div>
</a>
Results
- Fast search even in Japanese
- Search results are clear and easy to click
- Real-time results display with incremental search
Related Posts Display Improvements
Problems
- Displayed large at the end of articles, requiring long scrolls after reading
- When moved to sidebar, images and titles were too small to see
Solutions
Added compact mode to RelatedPosts.astro:
{isCompact ? (
<div class="related-posts-list">
{finalPosts.map((post) => (
<a href={`/posts/${IdToSlug(post.id)}/`} class="related-post-item">
<h3 class="related-post-title">{post.data.title}</h3>
<div class="related-post-tags">
{post.data.tags.slice(0, 3).map(tag => (
<span class="related-post-tag">{tag}</span>
))}
</div>
</a>
))}
</div>
) : (
// Normal display
)}
Results
- Readable display even in sidebar
- Removed images to focus on text information
- By placing under table of contents, users can check related articles while reading
Other Minor Improvements
RSS Feed Improvements
- Problem: Only titles, no content included
- Fix: Used MarkdownIt and sanitize-html to convert Markdown to HTML before distribution
- Result: Article content now readable in feed readers
Table of Contents Improvements
- Problem: Table of contents gets in the way for long articles
- Fix: Added collapse feature and reading progress indicator
- Result: Can expand only when needed, shows reading progress
Enhanced Mobile Support
- Problem: Poor mobile usability
- Fix: Implemented pull-to-refresh, swipe navigation, bottom navigation
- Result: Comfortable operation on smartphones
Problems Encountered During Implementation and Solutions
Netlify Build Error
Build errors occurred because pnpm-lock.yaml was outdated.
The error message showed dependency inconsistencies were the cause.
Resolved by running pnpm install
to update the lockfile.
Tailwind CSS Circular Reference
Circular reference error occurred when using @apply visible
.
Tailwind’s @apply internally uses CSS custom properties, causing issues when combined with visibility property.
Resolved by directly using visibility: visible
.
Navigation Layout Breaking
Navigation broke when adding bookmark feature.
The cause was adding too many elements to the navigation bar, breaking responsive design.
Eventually decided the bookmark feature wasn’t worth the complexity and removed all related code.
Search Function Not Working
Pagefind integration wasn’t working properly.
The causes were Pagefind script not loading at the right time and incorrect API usage.
Resolved by fixing dynamic script loading and using the correct API methods.
Summary
With these improvements, blog loading speed has significantly improved, and I think usability has gotten better too.
Image optimization in particular had a big effect, with noticeable differences even in perceived performance.
Including technical details, this became quite a large-scale renovation, but by solving problems one by one, I feel the blog has become better.
There still seem to be areas for improvement, but this feels like a good stopping point for now.
I’d like to continue nurturing this blog little by little.
Chat
Actually, Claude helped me with these improvements.
I don’t think I could have done such large-scale renovations alone.
It was especially helpful to get specific code suggestions for implementing each feature.
Developing with AI is quite enjoyable.
I wonder what I should improve next…
(This text was also created by Claude. Only this notice was written by a human)
コメント