Integrate Concepts Collection into More-About Dynamic Routing
Summary
Implemented a unified dynamic routing system that renders both vocabulary and concepts collections through the same
/more-about
route, with dedicated index pages and consistent styling. Further refactored the implementation to use component-based architecture and semantic CSS classes.Why Care
This enhancement provides a more comprehensive reference library for users, allowing them to access both vocabulary terms and conceptual frameworks through a consistent interface. The implementation follows a modular approach that can be extended to support additional content collections in the future. The component-based refactoring improves code maintainability and establishes patterns for future development.
Implementation
Changes Made
We implemented the integration of the concepts collection into the more-about routing system in four main commits:
- Initial setup and configuration:
text
15af080 - prerun(concepts): add concepts to dyanamic routing. (41 minutes ago)
M src/components/admin/RouteManager.astro
M src/components/basics/Header.astro
M src/content.config.ts
A src/pages/more-about/[content-item].astro
A src/pages/more-about/concepts.astro
A src/pages/more-about/index.astro
A src/pages/more-about/vocabulary.astro
M src/utils/routing/routeManager.ts
- Improved routing with catch-all pattern:
text
0cc76d9 - iterate(concepts): second iteration fixes page names (27 minutes ago)
M src/layouts/Layout.astro
R055 src/pages/more-about/[content-item].astro src/pages/more-about/[...slug].astro
- UI refinements and styling improvements:
text
4ddb89b - works(reference): reference page now working. (10 minutes ago)
M src/pages/more-about/concepts.astro
M src/pages/more-about/index.astro
M src/pages/more-about/vocabulary.astro
- Component refactoring and CSS improvements:
text
f8e2c7d - refactor(components): move functionality to dedicated components (5 minutes ago)
A src/components/reference/ConceptPreviewCard.astro
A src/components/reference/VocabularyPreviewCard.astro
M src/pages/more-about/concepts.astro
M src/pages/more-about/index.astro
M src/pages/more-about/vocabulary.astro
Technical Details
Content Configuration
Added the concepts collection to
src/content.config.ts
to make Astro aware of the content in the /content/concepts/
directory: typescript
// src/content.config.ts
const conceptsCollection = defineCollection({
loader: glob({pattern: "**/*.md", base: "../content/concepts"}),
schema: z.object({
aliases: z.union([
z.string().transform(str => [str]),
z.array(z.string())
]).optional().default([])
}).passthrough().transform((data, context) => {
// Transform logic for generating titles and slugs
})
});
// Add to collections export
export const collections = {
// existing collections...
'concepts': conceptsCollection
};
Dynamic Routing
Implemented a flexible catch-all route handler in
src/pages/more-about/[...slug].astro
that can handle both vocabulary and concepts entries: javascript
// src/pages/more-about/[...slug].astro
// Helper function to convert filename to proper case
function toProperCase(str) {
return str
.replace(/[-_]/g, ' ')
.split(' ')
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
.join(' ');
}
// Function to find an entry by slug in a collection
async function findEntryBySlug(collection, slug) {
const entries = await getCollection(collection);
return entries.find(entry => {
const entrySlug = entry.data.slug || entry.id.replace(/\.md$/, '').toLowerCase().replace(/\s+/g, '-');
return entrySlug === slug;
});
}
// Try to find the entry in both collections
let entry;
let collection;
// First check vocabulary, then check concepts
// ...
Index Pages
Created three index pages with consistent styling and navigation:
- Main index page (
src/pages/more-about/index.astro
) showing both collections - Vocabulary index (
src/pages/more-about/vocabulary.astro
) - Concepts index (
src/pages/more-about/concepts.astro
)
All pages use a consistent card-based layout with compact styling:
javascript
// Styling improvements in all index pages
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{entries.map(entry => (
<div class="bg-gray-800 p-4 rounded-lg">
<h3 class="text-sm font-bold mb-2">
<a href={`/more-about/${entry.data.slug || entry.id.replace(/\.md$/, '').toLowerCase().replace(/\s+/g, '-')}`} class="text-blue-400 hover:underline">
{entry.data.title}
</a>
</h3>
<p class="text-xs text-gray-300">
{entry.data.description || "Learn more about this concept..."}
</p>
</div>
))}
</div>
Component Refactoring
Refactored the implementation to use component-based architecture, introducing dedicated components for concept and vocabulary preview cards:
javascript
// src/components/reference/VocabularyPreviewCard.astro
<div class="vocabulary-card">
<h3 class="vocabulary-card__title">
<a href={url} class="vocabulary-card__link">
{entry.data.title}
</a>
</h3>
{entry.data.aliases && entry.data.aliases.length > 0 && (
<p class="vocabulary-card__aliases">
Also known as: {entry.data.aliases.join(', ')}
</p>
)}
</div>
// src/components/reference/ConceptPreviewCard.astro
<div class="concept-card">
<h3 class="concept-card__title">
<a href={url} class="concept-card__link">
{entry.data.title}
</a>
</h3>
<p class="concept-card__description">
{entry.data.description || "Learn more about this concept..."}
</p>
</div>
CSS Improvements
Improved CSS architecture by replacing Tailwind utility classes with semantic BEM-style classes and leveraging project CSS variables:
css
/* VocabularyPreviewCard.astro <style> section */
.vocabulary-card {
background-color: var(--clr-lossless-primary-dark, #19141D);
padding: 1rem;
border-radius: 0.5rem;
}
.vocabulary-card__title {
font-size: 1rem; /* text-base */
font-weight: 400;
margin-bottom: 0.5rem;
}
.vocabulary-card__link {
color: var(--clr-lossless-accent--brightest, rgb(4, 229, 229));
}
.vocabulary-card__link:hover {
text-decoration: underline;
}
.vocabulary-card__aliases {
font-size: 0.75rem;
font-weight: 300;
color: var(--white--pure--40p, hsla(0, 0%, 100%, .4));
margin-bottom: 0.5rem;
}
This approach:
- Improves code readability with semantic class names
- Establishes a pattern for component-specific styling
- Leverages the project's CSS variable system
- Provides comprehensive comments for future developers
- Creates a single source of truth for component styles
Integration Points
Title Generation
Implemented a robust title generation system that:
- Extracts titles from frontmatter when available
- Falls back to generating titles from filenames using proper case formatting
- Handles hyphens, underscores, and other special characters in filenames
javascript
// Title generation logic in all index pages
if (!entry.data.title) {
const filename = entry.id.replace(/\.md$/, '');
const filenameParts = filename.split('/');
const baseFilename = filenameParts[filenameParts.length - 1];
entry.data.title = toProperCase(baseFilename);
}
URL Generation
Added fallback mechanisms for entries without explicit slugs, using the filename as the basis for the URL path:
javascript
href={`/more-about/${entry.data.slug || entry.id.replace(/\.md$/, '').toLowerCase().replace(/\s+/g, '-')}`}
Type Safety
Used type assertions to handle TypeScript errors until Astro regenerates types for new collections:
javascript
// Type assertion in index.astro
const conceptsEntries = await getCollection('concepts' as any) as any[];
Documentation
The implementation is documented in the updated prompt:
/content/lost-in-public/prompts/render-logic/Integrate-Concepts-into-More-About.md
This prompt now includes:
- Detailed implementation insights
- Code examples for each component
- Data flow diagrams
- Implementation architecture
- Summary of implementation steps