---
title: Getting Started with Astro-Theme-Aither
date: "2026-03-14T16:00:00+08:00"
category: Tutorial
description: Everything you need to install, configure, customize, and deploy Astro-Theme-Aither — in one page.
tags: [Guide, Astro]
pinned: true
---

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

- [Node.js](https://nodejs.org/) 22 LTS or later
- [pnpm](https://pnpm.io/) 10 or later
- A GitHub account
- A [Cloudflare](https://cloudflare.com/) account (for deployment)

## Installation

```bash
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.

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

Key sections:

| Section | What it controls |
|---------|-----------------|
| `name` | Site title in navbar and footer |
| `social` | Social links in footer (GitHub, X, Discord, Email, RSS) |
| `nav` | Navigation bar items |
| `footer.sections` | Footer column links |
| `ui.defaultMode` | Default color mode: `'light'`, `'dark'`, or `'system'` |
| `ui.defaultStyle` | Default custom style: `'default'` or any built-in style key |
| `ui.showMoreThemesMenu` | Show or hide the `More Themes` custom theme picker |
| `blog.paginationSize` | Posts per page (default: 20) |
| `sections` | Custom content sections (see below) |

### Environment Variables

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

```bash
cp .env.example .env
```

| Variable | Service | Required |
|----------|---------|----------|
| `PUBLIC_GA_ID` | Google Analytics | No |
| `PUBLIC_CRISP_WEBSITE_ID` | Crisp Chat | No |
| `PUBLIC_GISCUS_REPO` | Giscus Comments | No |
| `PUBLIC_GISCUS_REPO_ID` | Giscus Comments | No |
| `PUBLIC_GISCUS_CATEGORY` | Giscus Comments | No |
| `PUBLIC_GISCUS_CATEGORY_ID` | Giscus Comments | No |

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`:

```javascript
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/`:

```markdown
---
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

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `title` | string | Yes | — | Post title |
| `date` | date | Yes | — | Publish timestamp (`2026-03-14T16:00:00+08:00`) |
| `category` | string | No | `General` | Single category |
| `description` | string | No | — | SEO meta description |
| `tags` | string[] | No | — | Tag list |
| `pinned` | boolean | No | `false` | Pin to top of list |
| `image` | string | No | — | Cover image path |

### MDX Components

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

```mdx
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`:

```typescript
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`:

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

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

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

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

```markdown
---
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`:

```typescript
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:

```css
: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:

| Feature | URL | Description |
|---------|-----|-------------|
| Sitemap | `/sitemap-index.xml` | Auto-generated |
| RSS Feed | `/rss.xml` | Auto-generated |
| robots.txt | `/robots.txt` | Welcomes AI crawlers |
| llms.txt | `/llms.txt` | AI content index |
| llms-full.txt | `/llms-full.txt` | Full-text for AI |
| Markdown export | `/posts/slug.md` | Raw Markdown per post |
| JSON-LD | Every page | Article structured data |
| Open Graph | Every page | Social preview cards |
| OG Images | `/og/slug.png` | Auto-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:

| Secret | Where to get it |
|--------|----------------|
| `CLOUDFLARE_API_TOKEN` | Cloudflare dashboard → API Tokens |
| `CLOUDFLARE_ACCOUNT_ID` | Cloudflare 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

| Layer | Technology |
|-------|-----------|
| Framework | Astro 6 (SSG) |
| UI Islands | React 19 |
| Styling | Tailwind CSS v4 |
| Components | Custom Astro components + small React islands |
| Content | MDX with Content Collections |
| OG Images | Satori + resvg-js |
| i18n | 11 locales |
| Deployment | Cloudflare Pages |
| Package Manager | pnpm 10 |
| Language | TypeScript 5 |

## Useful Commands

| Command | Description |
|---------|-------------|
| `pnpm dev` | Start dev server at `localhost:4321` |
| `pnpm validate` | Run the full pre-push validation suite |
| `pnpm build` | Build for production |
| `pnpm preview` | Preview production build locally |

## Version Scheme

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