Version 1.0 · Last updated: January 1, 2024


Introduction

Neutral is a minimal, fast, and fully-featured blog theme built with Astro, Tailwind CSS v4, and Alpine.js. It is designed for personal blogs, travel journals, and content-first websites.

What’s included

  • Hero slider + category grid on the homepage
  • Blog post pages with fullwidth and sidebar variants
  • Category, tag, and author archive pages with pagination
  • Search page with instant client-side filtering
  • About, Contact, Privacy, Terms, and Cookie pages
  • Reusable sidebar widgets (author, categories, tags, gallery)
  • Footer with dynamic latest and featured posts
  • Back-to-top button, social share, and related posts

Requirements

Before you begin, make sure you have the following installed:

  • Node.js 18.17.1 or higher (LTS recommended) — nodejs.org
  • npm 9+ or pnpm 8+ or yarn 1.22+
  • A code editor — VS Code with the official Astro extension is recommended

Verify your Node version:

node -v

Installation

1. Clone or extract the theme

If you downloaded the theme as a ZIP file, extract it to your project folder:

cd my-blog

2. Install dependencies

npm install

This will install all required packages including:

  • astro — the core framework
  • tailwindcss + @tailwindcss/vite — styling
  • @tailwindcss/typography — prose styling for blog content
  • alpinejs — lightweight interactivity
  • astro-icon + @iconify-json/bi — Bootstrap Icons

3. Start the development server

npm run dev

Open http://localhost:4321 in your browser. The site hot-reloads automatically when you edit files.


Project Structure

neutral-astro/
├── public/
│   └── img/                    # Static images (blog covers, avatar)
│       ├── blogs/
│       └── personal/
├── src/
│   ├── components/             # Reusable Astro components
│   ├── content/
│   │   ├── blog/               # Blog posts (.mdx)
│   │   └── pages/              # Static pages (.mdx)
│   ├── data/                   # Site configuration data
│   │   ├── authorData.ts
│   │   ├── headerData.ts
│   │   ├── footerData.ts
│   │   ├── heroData.ts
│   │   └── aboutData.ts
│   ├── layouts/                # Page layout templates
│   ├── pages/                  # Astro route pages
│   ├── styles/
│   │   └── style.css           # Global styles + Tailwind entry
│   └── utils/
│       └── path.ts             # URL helper
├── astro.config.mjs
├── content.config.ts           # Content collection schemas
└── tsconfig.json

Writing Blog Posts

All blog posts live in src/content/blog/ as .mdx files.

Create a new post

Create a file: src/content/blog/my-first-post.mdx

---
title: "My First Post"
cover: "/img/blogs/my-cover.jpg"
date: "2024-06-01"
category: "Travel"
tags: ["travel", "tips", "adventure"]
readTime: 5
featured: false
trending: false
popular: false
authorId: "jesicca"
excerpt: "A short description shown in post cards and meta tags."
---

Your post content goes here. You can use **bold**, *italic*, and all standard Markdown.

## A Heading

Regular paragraph text.

> A blockquote looks like this.

- List item one
- List item two

Frontmatter fields

FieldTypeRequiredDescription
titlestringPost title
coverstringCover image path from /public/
datestringISO date: "2024-06-01"
categorystringSingle category label
tagsstring[]Array of tag strings
readTimenumberEstimated minutes to read
featuredbooleanShows in hero slider + featured widgets
trendingbooleanShows in gallery widgets
popularbooleanShows in popular post widgets
authorIdstringMust match an id in authorData.ts
excerptstringShort description (150–200 chars)

Post URL

The post URL is derived from the filename. my-first-post.mdx becomes /my-first-post.


Site Configuration

All site-wide data is stored in src/data/. Edit these files to customize the theme.

Header — headerData.ts

Controls the top bar links, navigation menu, social icons, and logo:

export const defaultHeaderData: HeaderData = {
  topMenu: [
    { label: 'Home',    href: '/'        },
    { label: 'About',   href: '/about'   },
    { label: 'Contact', href: '/contact' },
  ],
  logo: {
    src:   '/img/logo.png',
    alt:   'My Blog',
    width: 160,
  },
  navMenu: [
    { label: 'Home',    href: '/'    },
    { label: 'Travel',  href: '/category/travel'  },
    {
      label: 'Pages',
      children: [
        { label: 'About',   href: '/about'   },
        { label: 'Contact', href: '/contact' },
      ],
    },
  ],
  socialLinks: [
    { icon: 'bi:instagram', href: 'https://instagram.com/yourhandle', label: 'Instagram' },
    { icon: 'bi:twitter-x', href: 'https://twitter.com/yourhandle',   label: 'Twitter'   },
  ],
  copyright: 'Copyright 2024',
};

Controls the about text, contact info, and copyright links:

export const defaultFooterData: FooterData = {
  info: {
    title:       'About Us',
    description: 'Your blog description here.',
    address:     'Your city, Country',
    phone:       '+(000) 000-0000',
    email:       'hello@yourdomain.com',
  },
  copyright: {
    links: [
      { label: 'Privacy', href: '/privacy' },
      { label: 'Terms',   href: '/terms'   },
    ],
    text: 'Copyright 2024 Your Name',
  },
};

Author — authorData.ts

Add or edit authors. The id must match the authorId in your MDX posts:

export const authors: AuthorData[] = [
  {
    id:       'your-id',
    name:     'Your Name',
    fullName: 'Your Full Name',
    avatar:   '/img/personal/avatar.jpg',
    bio:      'Short bio shown in sidebar and author page.',
    href:     '/author/your-id',
    website:  'https://yourwebsite.com',
    socials: [
      { icon: 'bi:instagram', href: 'https://instagram.com/yourhandle', label: 'Instagram' },
    ],
  },
];

About page — aboutData.ts

Edit stats, skills, timeline, and gallery for the /about page:

export const defaultAboutData: AboutData = {
  name:    'Your Name',
  tagline: 'Travel Blogger · Photographer',
  avatar:  '/img/personal/avatar.jpg',
  intro:   ['First paragraph...', 'Second paragraph...'],
  stats:   [{ value: '50+', label: 'Countries Visited' }],
  skills:  [{ name: 'Photography', percent: 90 }],
  timeline: [
    { year: '2020', title: 'Started Blogging', description: '...' },
  ],
  gallery: [
    { image: '/img/blogs/1.jpg', alt: 'Photo caption' },
  ],
};

Hero Section

The homepage hero has two parts: a slider and a category grid.

Slider

The slider automatically pulls the 3 most recent posts with featured: true. To change the number of slides:

<!-- src/pages/index.astro -->
<HeroSection sliderLimit={5} autoplayInterval={5000} />

Category grid

Edit the grid items in heroData.ts:

export const defaultHeroData: HeroData = {
  grid: [
    { image: '/img/blogs/3.jpg', imageAlt: 'Travel', category: 'Travel', href: '/category/travel' },
    { image: '/img/blogs/6.jpg', imageAlt: 'Food',   category: 'Food',   href: '/category/food'   },
    { image: '/img/blogs/8.jpg', imageAlt: 'Daily',  category: 'Daily',  href: '/category/daily'  },
  ],
};

PostCard Variants

PostCard supports four layout variants:

<!-- Default: two-column, thumbnail left -->
<PostCard variant="default" ... />

<!-- Grid: thumbnail top, stacked layout -->
<PostCard variant="grid" ... />

<!-- Box: content overlapping thumbnail -->
<PostCard variant="box" ... />

<!-- List: compact thumbnail left (1/3), no button -->
<PostCard variant="list" ... />

Static Pages (MDX)

Pages in src/content/pages/ are used for /privacy, /terms, and /cookies. To add a new page:

1. Create src/content/pages/about-us.mdx:

---
title: "About Us"
label: "About Us"
icon: "bi:info-circle"
date: "2024-01-01"
description: "Learn more about our team."
---

Your page content here.

2. Create src/pages/about-us.astro:

---
import { getCollection, render } from 'astro:content';
import PageLayout from '@src/layouts/PageLayout.astro';

const pages = await getCollection('pages');
const page  = pages.find((p) => p.id === 'about-us');
const { Content } = await render(page);
---
<PageLayout page={page} Content={Content} />

Adding Icons

This theme uses Bootstrap Icons via astro-icon. To add a new icon, first add it to astro.config.mjs:

icon({
  include: {
    bi: [
      // existing icons...
      'star',       // ← add new icon name here
    ],
  },
}),

Then use it in any component:

---
import { Icon } from 'astro-icon/components';
---
<Icon name="bi:star" class="size-4" />

Browse all Bootstrap Icons at icons.getbootstrap.com.


Sub-path Deployment

If your site is deployed to a sub-path (e.g. yourdomain.com/), set BASE_URL in your environment or update src/utils/path.ts:

const BASE = '/blog'; // your sub-path

All href and src attributes in this theme use the url() helper, so every link will automatically pick up the prefix.


Build & Deploy

Build for production

npm run build

Output is generated in dist/. Preview locally with:

npm run preview

Deploy to Netlify

npm install -g netlify-cli
netlify deploy --prod --dir=dist

Deploy to Vercel

npm install -g vercel
vercel --prod

Deploy to GitHub Pages

Add to astro.config.mjs:

export default defineConfig({
  site: 'https://yourusername.github.io',
  base: '/your-repo-name',
  // ...
});

Then push to the gh-pages branch or use the official Astro GitHub Pages guide.


Support & License

For questions contact lightestcode@gmail.com.