Tutorial #Guide#Astro

Getting Started with Astro-Theme-Aither

2026-03-14 · 926 words · 4 min

Everything you need to install, configure, customize, and deploy Astro-Theme-Aither — in one page.

This guide covers everything you need to go from zero to a fully deployed, multilingual blog. Read it start to finish, or jump to the section you need.

Prerequisites

Installation

Terminal windowBash
git clone https://github.com/YOUR_USERNAME/YOUR_REPO.git
cd YOUR_REPO
corepack enable
pnpm install
pnpm validate
pnpm dev

Open http://localhost:4321 in your browser. You should see the default blog.

Project Structure

src/
├── components/ # Astro & React components
├── config/site.ts # Site name, nav, footer, social links
├── content/posts/ # Blog posts organized by locale
│ ├── en/ # English posts
│ ├── zh-hans/ # Simplified Chinese posts
│ └── .../ # Other locales
├── i18n/ # Translations for UI text
├── layouts/ # Page layout (Layout.astro)
├── lib/ # Utilities (posts, formatter, etc.)
├── pages/ # Route pages
└── styles/global.css # Tailwind v4 theme tokens

Configuration

Site Config

Edit src/config/site.ts — this is the single source of truth for your site.

export const siteConfig = {
name: 'Your Blog Name',
title: 'Your tagline here.',
description: 'Your site description for SEO.',
author: {
name: 'Your Name',
avatar: '',
},
// ...
};

Key sections:

SectionWhat it controls
nameSite title in navbar and footer
socialSocial links in footer (GitHub, X, Discord, Email, RSS)
navNavigation bar items
footer.sectionsFooter column links
ui.defaultModeDefault color mode: 'light', 'dark', or 'system'
ui.defaultStyleDefault custom style: 'default' or any built-in style key
ui.showMoreThemesMenuShow or hide the More Themes custom theme picker
blog.paginationSizePosts per page (default: 20)
sectionsCustom content sections (see below)

Environment Variables

Copy .env.example to .env and fill in your values:

Terminal windowBash
cp .env.example .env
VariableServiceRequired
PUBLIC_GA_IDGoogle AnalyticsNo
PUBLIC_CRISP_WEBSITE_IDCrisp ChatNo
PUBLIC_GISCUS_REPOGiscus CommentsNo
PUBLIC_GISCUS_REPO_IDGiscus CommentsNo
PUBLIC_GISCUS_CATEGORYGiscus CommentsNo
PUBLIC_GISCUS_CATEGORY_IDGiscus CommentsNo

Leave any variable empty to disable that service. No code changes needed.

Site URL

The site URL is configured in one place — astro.config.mjs:

import { defineConfig } from 'astro/config';
import aither from '@aither/astro';
export default defineConfig({
site: 'https://your-domain.pages.dev',
integrations: [aither()],
});

All components read this value via import.meta.env.SITE. No need to configure it elsewhere.

Writing Posts

Create a Post

Create a new .mdx file in src/content/posts/en/:

---
title: My First Post
date: "2026-03-14T16:00:00+08:00"
category: General
description: A brief summary for SEO and social previews.
tags: [Topic, Another]
pinned: false
---
Your content starts here. Write in standard Markdown.

Frontmatter Reference

FieldTypeRequiredDefaultDescription
titlestringYesPost title
datedateYesPublish timestamp (2026-03-14T16:00:00+08:00)
categorystringNoGeneralSingle category
descriptionstringNoSEO meta description
tagsstring[]NoTag list
pinnedbooleanNofalsePin to top of list
imagestringNoCover image path

MDX Components

Since all posts use .mdx format, you can import and use React components directly in your content:

import MyChart from '@/components/MyChart'
Here is an interactive chart:
<MyChart data={[10, 20, 30]} />

This is optional — standard Markdown works exactly the same in .mdx files.

Pinning Posts

Set pinned: true in the frontmatter. Pinned posts appear at the top of the list with a pin icon, sorted by date among themselves.

Internationalization (i18n)

The theme supports 11 locales out of the box: English, Simplified Chinese, Traditional Chinese, Korean, French, German, Italian, Spanish, Russian, Indonesian, and Brazilian Portuguese.

Adding a Translated Post

Create the same filename in the target locale directory:

src/content/posts/en/my-post.mdx # English original
src/content/posts/zh-hans/my-post.mdx # Chinese translation
src/content/posts/fr/my-post.mdx # French translation

Each file is independent — translate the frontmatter fields too.

Translating UI Text

UI strings (navigation, footer, buttons) live in src/i18n/messages/. Each locale has its own file:

src/i18n/messages/
├── en.ts
├── zh-hans.ts
├── fr.ts
└── ...

Edit the relevant file to change any UI text.

Browser Language Detection

The theme detects the visitor’s browser language and shows a banner suggesting to switch locale. The preference is saved in localStorage.

Custom Content Sections

Beyond blog posts, you can create custom content sections (translations, notes, tutorials, etc.) that get their own list page and detail pages, with a separate navigation entry.

Adding a Section

Step 1 — Register the collection in src/content.config.ts:

const translations = defineCollection({
loader: glob({ pattern: '**/*.mdx', base: './src/content/translations' }),
schema: contentSchema,
});
export const collections = { posts, translations };

Step 2 — Add section config in src/config/site.ts:

sections: [
{ id: 'translations', labelKey: 'translations' },
],

Step 3 — Add the nav label to each locale file in src/i18n/messages/:

nav: {
blog: 'Blog',
translations: 'Translations', // add this line
about: 'About',
},

Step 4 — Create content in src/content/translations/en/:

---
title: My Translation
date: "2026-03-14T16:00:00+08:00"
category: General
---
Your translated content here.

The list page (/translations/), detail pages (/translations/slug/), and navigation entry are generated automatically for all 11 locales.

Theming

Modes and Styles

The theme uses two layers:

  • Color mode: Light, Dark, or System (follows OS preference)
  • Theme style: default or any built-in custom style such as evolution

The defaults live in src/config/site.ts:

ui: {
defaultMode: 'system', // 'light' | 'dark' | 'system'
defaultStyle: 'default', // 'default' | custom style key
showMoreThemesMenu: true, // true to show the custom theme picker
},

Picking Light, Dark, or System resets back to the main modes. Picking a custom style keeps your mode preference stored for later. Theme switching uses the View Transitions API for a smooth circular reveal animation.

If you want a simpler theme UI, keep mode switching enabled and set showMoreThemesMenu: false. This hides the custom style list while preserving the Light / Dark / System controls.

Theme labels are managed separately from theme keys:

  • Theme keys such as evolution or threebody stay stable and are used only for state and CSS hooks
  • User-facing labels are centralized in src/config/themes.ts
  • Localized labels should translate generic descriptors, while brand names or strong IP names can stay partially untranslated when that reads better in the target locale

This keeps the theme system predictable in code while allowing locale-specific naming on the UI.

Colors

Edit CSS custom properties in src/styles/global.css. The theme uses Tailwind CSS v4 @theme tokens:

:root {
--color-link: ...;
--color-link-hover: ...;
--color-nav-text: ...;
/* etc. */
}

Both light and dark modes have their own set of color tokens.

Fonts

The theme uses a system sans-serif font stack by default, following Apple Human Interface Guidelines typography parameters (17px / 1.47 / -0.022em). To use a custom font, update the font-family values in the Tailwind theme configuration.

SEO and AI

Every page is optimized for search engines and AI agents:

FeatureURLDescription
Sitemap/sitemap-index.xmlAuto-generated
RSS Feed/rss.xmlAuto-generated
robots.txt/robots.txtWelcomes AI crawlers
llms.txt/llms.txtAI content index
llms-full.txt/llms-full.txtFull-text for AI
Markdown export/posts/slug.mdRaw Markdown per post
JSON-LDEvery pageArticle structured data
Open GraphEvery pageSocial preview cards
OG Images/og/slug.pngAuto-generated with Satori

Deployment

Cloudflare Pages

Best practice: create the Pages project first. The workflow uses your repository name by default, or CLOUDFLARE_PAGES_PROJECT_NAME if you need to override it.

Set these GitHub Secrets:

SecretWhere to get it
CLOUDFLARE_API_TOKENCloudflare dashboard → API Tokens
CLOUDFLARE_ACCOUNT_IDCloudflare dashboard → Account Home

Then run pnpm validate and push to main.

Custom Domain

Add a custom domain in the Cloudflare Pages dashboard under your project → Custom domains.

Tech Stack

LayerTechnology
FrameworkAstro 6 (SSG)
UI IslandsReact 19
StylingTailwind CSS v4
ComponentsCustom Astro components + small React islands
ContentMDX with Content Collections
OG ImagesSatori + resvg-js
i18n11 locales
DeploymentCloudflare Pages
Package Managerpnpm 10
LanguageTypeScript 5

Useful Commands

CommandDescription
pnpm devStart dev server at localhost:4321
pnpm validateRun the full pre-push validation suite
pnpm buildBuild for production
pnpm previewPreview production build locally

Version Scheme

Public release tags follow CalVer-style names such as v2026.04.08.

End · Thanks for reading

Comments