A ready-to-use, SEO and accessibility-focused Astro starter template with blog and portfolio functionality.
astro- Framework (Astro 5.x+)accessible-astro-components- Core component librarytailwindcss- Styling (v4.x with Vite plugin)@astrojs/mdx- MDX support@astrojs/sitemap- XML sitemap generationastro-icon- Icon system (using Lucide icon set)- ESLint with
eslint-plugin-jsx-a11y- Accessibility linting - Prettier with Astro and Tailwind plugins - Code formatting
This is the flagship starter theme of the Accessible Astro ecosystem:
- Target Audience: Developers building blogs, portfolios, or content-focused websites
- Features: Blog with pagination, project portfolio, contact forms, MDX support
- Showcase: Demonstrates all components from
accessible-astro-components - WCAG 2.2 AA: Fully compliant with comprehensive accessibility features
- Reference Implementation: Shows best practices for using the component library
-
Install dependencies:
npm install
-
Start dev server:
npm run dev # or npm startServer starts at
http://localhost:4321 -
Build production site:
npm run build
Output:
./dist/ -
Preview production build:
npm run preview
This project can work with locally linked accessible-astro-components:
- The
astro.config.mjsautomatically detects symlinked packages - When symlinks are detected, it enables:
- Auto-reload on component changes
- Filesystem access to parent directories
- Symlink preservation in Vite
- Strict mode enabled
- Path aliases configured for cleaner imports:
@components→./src/components@layouts→./src/layouts@assets→./src/assets@content→./src/content@pages→./src/pages@public→./public@post-images→./public/posts@project-images→./public/projects
- Prettier is configured with:
prettier-plugin-astroprettier-plugin-css-orderprettier-plugin-tailwindcss
- Run format manually:
npx prettier --write .
- ESLint configured with:
@typescript-eslint/eslint-plugineslint-plugin-astroeslint-plugin-jsx-a11y(strict accessibility rules)
- Check manually:
npx eslint .
- Tailwind CSS v4 with Vite plugin
- SCSS with custom utilities in
src/assets/scss/ - Modern OKLCH color system with automatic palette generation
- Use logical properties (e.g.,
inline-startinstead ofleft) - Custom properties for theming
- Atkinson Hyperlegible font for improved readability
This project follows WCAG 2.2 AA standards and serves as a reference implementation.
- Semantic HTML: Use appropriate elements (
<button>,<nav>,<main>, etc.) - Keyboard Navigation: All interactive elements must be keyboard accessible
- Focus Indicators: Never remove focus outlines without accessible alternatives
- Alt Text: Provide meaningful alt text for images (or use
alt=""for decorative images) - Color Contrast: Ensure text has 4.5:1 contrast (3:1 for large text)
- ARIA: Only use ARIA when native HTML is insufficient
- Heading Hierarchy: Maintain proper h1-h6 order
- Forms: Use
<label>, proper input types, and error messages - Motion: Respect
prefers-reduced-motionfor all animations
- Test with keyboard only (no mouse)
- Test with screen readers (VoiceOver, NVDA, JAWS)
- Use ESLint's jsx-a11y rules to catch common issues
- Check color contrast ratios
- Verify
prefers-reduced-motionis respected - Test responsive design on mobile
src/
├── components/ # Custom project-specific components
│ ├── Header.astro
│ ├── Footer.astro
│ ├── Navigation.astro
│ ├── SiteMeta.astro # SEO component
│ ├── ColorContrast.astro # WCAG contrast checker
│ ├── Featured*.astro # Content showcase components
│ └── ... (explore for more)
├── content/ # MDX content collections
│ └── projects/ # Project portfolio items
├── layouts/
│ ├── DefaultLayout.astro # Main layout
│ └── MarkdownLayout.astro # Layout for MDX content
├── pages/ # Route pages (file-based routing)
│ ├── index.astro # Homepage
│ ├── accessible-components.astro # Component showcase
│ ├── blog/
│ │ ├── [...page].astro # Blog pagination (dynamic route)
│ │ └── [post].astro # Individual blog posts (dynamic route)
│ ├── contact.astro # Contact form with validation
│ ├── color-contrast.astro # Interactive contrast checker
│ ├── 404.astro # Custom 404 page
│ └── ... (explore for more pages)
├── assets/
│ ├── images/ # Project images
│ ├── img/ # SVG assets
│ └── scss/ # SCSS utility classes
├── styles/
│ └── tailwind.css # Global Tailwind styles
├── utils/ # Utility functions
├── content.config.ts # Content collections config
└── env.d.ts # Type definitions
public/ # Static assets (served as-is)
├── fonts/ # Atkinson Hyperlegible web fonts
├── posts/ # Blog post images
├── projects/ # Project images
└── favicon.svg
Import from the package:
---
import { Accordion, AccordionItem, Button, Card, DarkMode, Modal } from 'accessible-astro-components'
---Many files throughout this project import from accessible-astro-components. Check the /accessible-components page to see all available components in action.
- Place in
src/components/ - Use semantic HTML
- Add proper ARIA attributes when needed
- Ensure keyboard accessibility
- Follow existing patterns in the codebase
- Import and use components from the package when possible
The /accessible-components page (src/pages/accessible-components.astro) demonstrates:
- All available components from the library
- Usage examples and code snippets
- Accessibility features of each component
- Interactive demos
The project uses Astro Content Collections for structured content:
- Projects:
src/content/projects/(7 MDX files) - Schema defined in
src/content.config.ts - Use frontmatter for metadata
- Query with
getCollection()API - Dynamic routes generate pages from collections
Blog posts are in src/pages/blog/ as Astro files:
- Create a new
.astrofile insrc/pages/blog/ - Add frontmatter with
title,description,publishDate, etc. - The
[...page].astrofile handles pagination automatically - Add post images to
public/posts/
Projects are MDX content in src/content/projects/:
- Create a new
.mdxfile insrc/content/projects/ - Add frontmatter following the schema in
content.config.ts - Write content in MDX format
- Add project images to
public/projects/
- Hero section with CTA
- Featured posts
- Featured projects
- Component showcases
- Pagination support
- Dynamic routes for individual posts
- Breadcrumbs navigation
- Social sharing
- Form validation showcase
- Accessible form components
- Error handling examples
- Success page redirect
- Comprehensive component showcase
- Live interactive examples
- Component documentation
- Accessibility features highlighted
- Interactive WCAG contrast checker
- Real-time contrast ratio calculation
- Pass/fail indicators for AA and AAA levels
All website images must be in WebP format. JPG/JPEG files are not used directly.
When adding or importing images:
- Convert JPG/JPEG files to WebP before placing them in
public/:cwebp input.jpg -o output.webp # or via sips (macOS): sips -s format webp input.jpg --out output.webp - Name the file accordingly (e.g.
ihk_wirpackenmitan.webp) - Reference the
.webppath in frontmatter and templates:featuredImage: /projects/ihk_wirpackenmitan.webp
Applies to:
public/projects/*.jpg→ convert topublic/projects/*.webppublic/posts/*.jpg→ convert topublic/posts/*.webp- Any other static image assets served from
public/
Rationale: WebP provides significantly smaller file sizes compared to JPEG while maintaining visual quality, improving page load performance and Core Web Vitals scores.
Project featured images are used in two contexts with different sizing requirements. Use a single source image and optimize accordingly.
Purpose: Single source image for both homepage carousel and project detail hero
Dimensions: 2400px × 1350px (16:9 aspect ratio, WebP format)
Specifications:
- Aspect Ratio: 16:9 (wide, editorial format)
- Format: WebP
- File Size Target: 250–450 KB (balances quality and load performance)
- Quality Settings: 75–85 quality in WebP compression
- Color Profile: sRGB
Usage:
- Homepage (
src/components/FeaturedProjects.astro): Displayed at 690×426 withobject-covercrop - Project Detail Page (
src/pages/portfolio/[project].astro): Displayed full-width hero withobject-contain(max 820px height)
Best Practices:
- Composition: Leave center content clear; important elements should not occupy extreme edges (safe area ~90% of width on desktop)
- Motif Type: Landscapes, editorial imagery, or high-contrast graphics work best
- Avoid: Pure portraits (vertical), ultra-wide panoramas, or low-contrast imagery
- Text Overlays: Keep minimal; if text is present, ensure sufficient contrast (4.5:1 WCAG AA minimum)
File Naming: {project-slug}-featured.webp
- Example:
drehbuch-12-min-featured.webp - Location:
public/projects/{filename}.webp
Frontmatter Reference:
---
title: 'Project Title'
description: 'Project description'
featuredImage: /projects/drehbuch-12-min-featured.webp
---Generation/Export Checklist:
- Image is 2400×1350 px
- Exported as WebP
- File size is 250–450 KB
- Placed in
public/projects/folder - Filename matches slug pattern
- Added to MDX frontmatter with
/projects/path - Tested on mobile (375px) and desktop (1920px)
- Contrast ratio meets 4.5:1 if text present
This is a static site (SSG) that can be deployed anywhere:
-
Netlify: Drop-in deployment
- Build command:
npm run build - Publish directory:
dist
- Build command:
-
Vercel: Zero-config deployment
- Auto-detects Astro
- Deploys on push
-
GitHub Pages: Free hosting
- Configure base path in
astro.config.mjs - Use GitHub Actions for CI/CD
- Configure base path in
-
Cloudflare Pages: Fast global CDN
- Build command:
npm run build - Output directory:
dist
- Build command:
- Site URL configured in
astro.config.mjs - Sitemap automatically generated via
@astrojs/sitemap - Compressed HTML output for performance
Follow conventional commits format:
type(scope): subject
[optional body]
[optional footer]
Types: feat, fix, docs, style, refactor, test, chore
Examples:
feat(blog): add social sharing componentfix(navigation): resolve mobile menu keyboard trapdocs(readme): update installation instructionsa11y(forms): improve error message announcementsstyle(homepage): update hero section layout
- Title format:
[starter] Brief description - Check before submitting:
- Run
npm run build- no errors - Check
npx eslint .- no accessibility violations - Test keyboard navigation
- Verify no console errors
- Check responsive design on mobile
- Test with screen reader if accessibility-related
- Verify
prefers-reduced-motionrespected
- Run
- Include in PR description:
- What changed and why
- Any accessibility considerations
- Screenshots/videos for UI changes
- Testing steps performed
- Which pages are affected
- Check if symlink is detected: Look for "Workspace detected" message on dev server start
- Verify symlink:
ls -la node_modules/accessible-astro-components - Try restarting dev server
- Clear cache:
rm -rf node_modules/.astro node_modules/.vite
- Clear cache:
rm -rf node_modules/.astro node_modules/.vite - Reinstall:
rm -rf node_modules && npm install - Check Node version compatibility (Node 18+)
- Verify all images exist in
public/directory - Check for TypeScript errors:
npx tsc --noEmit
- Verify schema in
src/content.config.ts - Check frontmatter matches schema
- Restart dev server
- Clear Astro cache:
rm -rf node_modules/.astro
- Check Tailwind config:
tailwind.config.js - Verify Vite plugin in
astro.config.mjs - Clear cache and rebuild
- Check browser console for CSS errors
This is part of the Accessible Astro ecosystem:
- Accessible Astro Components: Component library (dependency)
- Accessible Astro Dashboard: Dashboard theme with auth
- Accessible Astro Docs: Documentation site