Technical Overview: Migrating to Astro
The previous iteration of this site relied on manual HTML maintenance and dynamic imports. While effective for simple layouts, it lacked the type safety and developer experience required for a growing visual archive and blog. This refactor focused on Content Collections, MDX, and component consolidation.
Architectural Paradigm
The core of the migration involved decoupling the layout from the content and standardizing the component interface. By utilizing Astro’s layout system, the global UI is defined in a single location and injected into each page via slots.
---
import { ClientRouter } from "astro:transitions";
import "../styles/global.css";
import Sidebar from "../components/Sidebar.astro";
// ...
<aside>
<Sidebar currentPath={currentPath} />
<main><slot /></main>
</aside> I utilize the ClientRouter component to enable persistent state across page transitions. This allows for smooth navigation without the overhead of a full client-side framework while maintaining the “oldschool” performance profile and providing a modern user experience.
Content Collections
The site now uses Astro’s Content Collections API with MDX for all blog posts and reviews. This provides type-safe frontmatter via Zod schemas and generates TypeScript definitions automatically.
import { defineCollection, z } from 'astro:content';
const reviews = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
date: z.string(),
tags: z.array(z.string()),
poster: z.string().optional(),
type: z.enum(['film', 'music']),
movie: z.string().optional(),
director: z.string().optional(),
artist: z.string().optional(),
// ...
}),
});
export const collections = { blog, reviews }; Content lives in src/content/blog/ and src/content/reviews/ as MDX files with frontmatter declarations. This eliminates the need for manual metadata exports and provides compile-time validation.
Dynamic Routing
Single [slug].astro files handle all blog posts and reviews, automatically generating routes from the content directory.
---
import { getCollection, render } from "astro:content";
import BlogPost from "../../layouts/BlogPost.astro";
export async function getStaticPaths() {
const posts = await getCollection("blog");
return posts.map((post) => ({
params: { slug: post.slug },
props: { post },
}));
}
const { post } = Astro.props;
const { Content } = await render(post);
---
<BlogPost title={post.data.title} date={post.data.date}>
<Content />
</BlogPost> Component Extraction
A significant portion of the refactoring involved extracting repeated patterns into reusable components.
PageHeader
Standardizes page intro headers across the site.
---
interface Props {
title: string;
tagline?: string;
}
const { title, tagline } = Astro.props;
---
<header class="page-intro">
<h1 class="page-title" set:html={title} />
{tagline && <p class="tagline">{tagline}</p>}
</header> InfoGrid
Handles metadata display for reviews with consistent styling.
---
interface Props {
poster: string;
items: { label: string; value: string }[];
tags?: string[];
}
// Renders consistent grid layout for movie/album metadata BlogPost Layout
Wraps all content with breadcrumb, title, and formatted date.
---
interface Props {
title: string;
date: string;
backHref?: string;
backLabel?: string;
}
// Automatically formats date and provides navigation Custom Component Implementation
Specialized components handle site-specific features.
ContextList
Provides depth without cluttering the primary narrative. Uses CSS backdrop-filter and absolute positioning to display metadata lists on hover.
<ContextList
term="Technical Stack"
items={["Astro", "Bun", "TypeScript", "Vanilla CSS"]}
/> This allows for the inclusion of technical details or cultural references, such as my interests in visual media✦ Cinema✦ Literature✦ Photographyand more, without breaking the reader’s flow.
Glossary
For Japanese terminology, I developed a Glossary component. It provides a standardized way to display Kanji while offering the Reading and Translation as a hoverable popup.
---
interface Props {
kanji: string;
hiragana: string;
romaji: string;
translation?: string;
}
// Renders kanji with hoverable popup for reading/translation This ensures that terms like 紫陽花あじさいajisaihydrangea are accessible to all readers without requiring inline parentheticals.
Styling Consolidation
All component-scoped styles were moved to global.css alongside design tokens. This keeps the CSS centralized while Astro’s scoped styles handle component-specific rules.
Build System
The site uses Bun for package management and builds, providing fast install times and a minimal dependency footprint.
{
"dependencies": {
"astro": "^5.16.6",
"@astrojs/mdx": "^4.0.0"
}
} The move to Content Collections has standardized the development workflow. The site now benefits from type-safe content, centralized styling, and a high-performance build process via Bun.
More technical logs will continue to be added as the architecture evolves.