A clean, minimalist blog theme built with Astro and vanilla CSS. Perfect for developers, writers, and content creators who want a fast, accessible, and beautiful blog.
π¨ Vibe-coded and inspired by Obsidian Minimal Theme - bringing the clean, focused aesthetic of Obsidian to the web.
- β‘ Lightning Fast - Static site generation with Astro (100/100 Lighthouse scores)
- π¨ Beautiful Design - Clean, minimal interface focused on readability
- π Dark/Light Theme - System preference detection with manual toggle
- π± Fully Responsive - Mobile-first design that works on all devices
- π Full-text Search - Fast client-side search (coming soon)
- π Reading Progress - Visual progress bar and reading time estimates
- π Markdown Support - Write in Markdown with full frontmatter support
- π― SEO Optimized - Meta tags, Open Graph, Twitter Cards, sitemap, robots.txt
- π Table of Contents - Auto-generated with scroll spy navigation
- π Smart Linking - Related posts and internal link suggestions
- π·οΈ Tag System - Organize and filter content by tags
- πΌοΈ Image Optimization - Responsive images with lazy loading and lightbox
- π οΈ TypeScript - Full type safety throughout the codebase
- π― Focus Mode - Distraction-free reading experience
- βΏ Accessible - WCAG 2.1 AA compliant with full keyboard navigation
- π Performance - Optimized for Core Web Vitals
- π¦ Component Based - Modular architecture for easy customization
- π§ Easy Configuration - Central config file for all settings
- Node.js 18.14.1 or higher
- npm or yarn package manager
-
Use this template (recommended)
# Clone the repository git clone https://github.com/yourusername/minimalpress.git my-blog cd my-blog # Remove git history and start fresh rm -rf .git git init
-
Install dependencies
npm install # or yarn install
-
Start development server
npm run dev # or yarn dev
Open http://localhost:4321 to see your blog.
-
Build for production
npm run build npm run preview # Preview the production build
minimalpress/
βββ src/
β βββ components/ # Reusable Astro components
β β βββ Breadcrumb.astro
β β βββ ImageGallery.astro
β β βββ MobileMenu.astro
β β βββ OptimizedImage.astro
β β βββ RelatedPosts.astro
β β βββ Sidebar.astro
β β βββ TableOfContents.astro
β β βββ ThemeToggle.astro
β β βββ ...
β βββ content/
β β βββ config.ts # Content collection schema
β β βββ blog/ # Blog posts (Markdown/MDX)
β β βββ getting-started.md
β β βββ guides/
β β βββ configuration.md
β βββ layouts/ # Page layouts
β β βββ BaseLayout.astro # Base HTML structure
β β βββ BlogLayout.astro # Blog post layout
β βββ pages/ # File-based routing
β β βββ index.astro # Homepage/blog listing
β β βββ about.astro # About page
β β βββ blog/
β β βββ [...slug].astro # Dynamic blog routes
β βββ styles/
β β βββ global.css # Global styles & CSS variables
β βββ utils/ # Utility functions
β β βββ navigation.ts # Navigation helpers
β β βββ seo.ts # SEO utilities
β βββ config.ts # Site configuration
βββ public/ # Static assets (images, fonts)
βββ astro.config.mjs # Astro configuration
βββ package.json
βββ tsconfig.json # TypeScript configuration
Create new posts in src/content/blog/
:
---
title: "Getting Started with MinimalPress"
description: "Learn how to create your first blog post"
date: 2024-01-20
tags: ["tutorial", "astro", "markdown"]
image: "/images/getting-started.jpg" # Optional cover image
author: "Your Name" # Optional, defaults to config
draft: false # Set to true to hide from production
canonical: "https://original-post-url.com" # Optional canonical URL
---
Your content here in **Markdown** format.
## Table of contents is automatically generated
Code blocks with syntax highlighting:
β```javascript
const greeting = "Hello, MinimalPress!";
console.log(greeting);
β```
You can organize posts in folders:
src/content/blog/
βββ getting-started.md
βββ tutorials/
β βββ astro-basics.md
β βββ markdown-guide.md
βββ thoughts/
βββ why-minimal-design.md
Folders become part of the URL: /blog/tutorials/astro-basics
Field | Type | Required | Description |
---|---|---|---|
title |
string | Yes | Post title |
description |
string | Yes | Short description (meta description) |
date |
date | Yes | Publication date (YYYY-MM-DD) |
tags |
string[] | No | Array of tags |
image |
string | No | Cover image URL |
author |
string | No | Override default author |
draft |
boolean | No | Hide from production build |
updated |
date | No | Last update date |
canonical |
string | No | Canonical URL for cross-posted content |
noindex |
boolean | No | Exclude from search engines |
Edit src/config.ts
to customize your site:
export const siteConfig = {
name: "My Blog",
description: "My personal blog about web development",
url: "https://myblog.com",
author: {
name: "Your Name",
email: "[email protected]",
twitter: "@yourhandle",
github: "yourusername",
linkedin: "yourprofile",
},
// Navigation menu
navigation: [
{ name: "Home", href: "/" },
{ name: "Blog", href: "/blog" },
{ name: "About", href: "/about" },
{ name: "Contact", href: "/contact" },
],
// Features
features: {
darkMode: true,
search: true,
tableOfContents: true,
readingTime: true,
},
// Blog settings
blog: {
postsPerPage: 10,
sortBy: "date", // "date" or "title"
sortOrder: "desc", // "asc" or "desc"
}
};
MinimalPress uses vanilla CSS with CSS variables for a lightweight, customizable styling system.
Edit CSS variables in src/styles/global.css
:
:root {
/* Light theme */
--bg: #ffffff;
--text: #333333;
--accent: #007acc;
/* ... more variables */
}
.dark {
/* Dark theme */
--bg: #1a1a1a;
--text: #e4e4e7;
--accent: #4facfe;
/* ... more variables */
}
Customize typography in src/styles/global.css
:
.markdown-content {
font-size: 18px;
line-height: 1.8;
letter-spacing: 0.01em;
}
.markdown-content h1 {
font-size: 36px;
font-weight: 700;
margin: 48px 0 24px 0;
}
Create new pages in src/pages/
:
---
// src/pages/contact.astro
import BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout title="Contact" description="Get in touch">
<h1>Contact Me</h1>
<p>Your content here...</p>
</BaseLayout>
Create reusable components in src/components/
:
---
// src/components/MyComponent.astro
export interface Props {
title: string;
variant?: 'primary' | 'secondary';
}
const { title, variant = 'primary' } = Astro.props;
---
<div class={`component ${variant}`}>
<h3>{title}</h3>
<slot />
</div>
npm run build
-
Build the site
npm run build
-
Deploy the
dist/
folder to your hosting provider:- Vercel:
vercel --prod
- Netlify:
netlify deploy --prod
- GitHub Pages: Use GitHub Actions
- Any static host: Upload
dist/
contents
- Vercel:
Create a .env
file for local development:
# Analytics (optional)
PUBLIC_GA_ID=G-XXXXXXXXXX
PUBLIC_PLAUSIBLE_DOMAIN=yourdomain.com
# Comments (optional)
PUBLIC_GISCUS_REPO=username/repo
PUBLIC_GISCUS_REPO_ID=XXX
PUBLIC_GISCUS_CATEGORY=Comments
Command | Action |
---|---|
npm run dev |
Start development server at localhost:4321 |
npm run build |
Build production site to ./dist/ |
npm run preview |
Preview production build locally |
npm run astro ... |
Run Astro CLI commands |
npm run astro add |
Add integrations (e.g., astro add sitemap ) |
npm run astro check |
Check for TypeScript errors |
# Add sitemap
npm run astro add sitemap
# Add RSS feed
npm run astro add rss
# Add image optimization
npm run astro add image
Add fonts to src/layouts/BaseLayout.astro
:
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" rel="stylesheet">
Update CSS in src/styles/global.css
:
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
Add analytics in src/layouts/BaseLayout.astro
:
<!-- Google Analytics -->
{import.meta.env.PUBLIC_GA_ID && (
<script async src={`https://www.googletagmanager.com/gtag/js?id=${import.meta.env.PUBLIC_GA_ID}`}></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', import.meta.env.PUBLIC_GA_ID);
</script>
)}
Build fails with "Cannot find module"
rm -rf node_modules package-lock.json
npm install
Images not loading
- Place images in
public/
folder - Reference as
/image.jpg
(not./image.jpg
)
Styles not updating
- Clear browser cache
- Restart dev server
- Check CSS variables in global.css
404 on deployed site
- Check
base
inastro.config.mjs
if deploying to subdirectory - Ensure all routes are generated during build
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Built with Astro
- Styled with vanilla CSS and CSS variables
- Icons from Heroicons
- Inspired by minimal design principles
Made with β€οΈ by the MinimalPress community