Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
dist/
node_modules/
.vscode/
.github/
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = false
trim_trailing_whitespace = false
2 changes: 1 addition & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- name: Setup npm
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: '20'
node-version: '24'

- name: Setup Python
uses: actions/setup-python@v5.5.0 # v5.5.0
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ pnpm-debug.log*
pnpm-lock.yaml

.astro
*.old
*.old
2 changes: 2 additions & 0 deletions .pnpmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Expose Astro dependencies for `pnpm` users
shamefully-hoist=true
2 changes: 1 addition & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ dist
node_modules
.github
.changeset
docs/*
docs/*
4 changes: 2 additions & 2 deletions .prettierrc.cjs → .prettierrc.mjs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
/** @type {import('prettier').Config} */
module.exports = {
export default {
printWidth: 120,
semi: true,
singleQuote: true,
tabWidth: 2,
trailingComma: 'es5',
useTabs: false,

plugins: [require.resolve('prettier-plugin-astro')],
plugins: ['prettier-plugin-astro'],

overrides: [{ files: '*.astro', options: { parser: 'astro' } }],
};
2 changes: 1 addition & 1 deletion .stackblitzrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
"env": {
"ENABLE_CJS_IMPORTS": true
}
}
}
106 changes: 106 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# AstroWind Agent Instructions

## Project Overview

AstroWind is a free, open-source website template built with **Astro v6** and **Tailwind CSS v4**. It generates a fully static site optimized for performance, SEO, and accessibility.

**Stack:** Astro v6 | Tailwind CSS v4 | TypeScript 5.9 | MDX | Sharp

## Quick Reference

| Command | Purpose |
| ----------------- | ----------------------------------- |
| `npm run dev` | Start dev server at localhost:4321 |
| `npm run build` | Production build to `./dist/` |
| `npm run preview` | Preview production build locally |
| `npm run check` | Run astro check + ESLint + Prettier |
| `npm run fix` | Auto-fix ESLint + Prettier issues |

**Node.js requirement:** >= 22.12.0

## Architecture

### Directory Structure

```
src/
assets/styles/tailwind.css # Tailwind v4 config (themes, utilities, plugins)
components/
common/ # Shared: Image, Metadata, Analytics, ToggleTheme
ui/ # Primitives: Button, Headline, WidgetWrapper, ItemGrid
widgets/ # Page sections: Hero, Features, Pricing, Header, Footer
blog/ # Blog: SinglePost, List, Pagination, Tags
CustomStyles.astro # CSS variables for colors and fonts
content.config.ts # Content Collections schema (Astro v6 location)
data/post/ # Blog posts (.md, .mdx)
layouts/ # Layout.astro, PageLayout.astro, MarkdownLayout.astro
pages/ # File-based routing
utils/ # blog.ts, images.ts, permalinks.ts, frontmatter.ts
config.yaml # Site configuration (loaded as virtual module)
navigation.ts # Navigation structure
types.d.ts # TypeScript type definitions
vendor/integration/ # Custom Astro integration for config loading
```

### Path Aliases

Use `~/` to import from `src/`:

```typescript
import Image from '~/components/common/Image.astro';
import { SITE } from 'astrowind:config';
```

### Configuration System

Site config lives in `src/config.yaml` and is loaded as a Vite virtual module `astrowind:config` by the custom integration in `vendor/integration/`. Exports: `SITE`, `I18N`, `METADATA`, `APP_BLOG`, `UI`, `ANALYTICS`.

## Tailwind CSS v4

Configuration is CSS-first in `src/assets/styles/tailwind.css`:

- **Theme tokens:** `@theme { --color-primary: var(--aw-color-primary); ... }`
- **Custom utilities:** `@utility bg-page { ... }`
- **Dark mode:** Class-based via `@variant dark (&:where(.dark, .dark *))`
- **Plugins:** `@plugin "@tailwindcss/typography"`
- **Custom variant:** `@custom-variant intersect (&:not([no-intersect]))`

CSS variables for colors/fonts are defined in `src/components/CustomStyles.astro` with light/dark theme variants.

The Vite plugin `@tailwindcss/vite` is configured in `astro.config.ts` (not as an Astro integration).

### Class Merging

Components use `twMerge` from `tailwind-merge` v3 for conditional class composition.

## Content Collections

Defined in `src/content.config.ts` using the Astro v6 Content Layer API with `glob()` loader. Posts are in `src/data/post/` as `.md` or `.mdx` files.

Post frontmatter: `title` (required), `publishDate`, `updateDate`, `draft`, `excerpt`, `image`, `category`, `tags`, `author`, `metadata`.

## Component Patterns

- Props extend interfaces from `~/types`
- Use `class:list` for conditional classes
- Use `twMerge()` when accepting className overrides
- Use named slots for layout composition
- Widget components accept standardized props (see `~/types`)

## Image Handling

`src/components/common/Image.astro` supports:

- Local images via `astro:assets` (optimized by Sharp)
- Remote images via Unpic CDN
- Allowed domains (for providers Unpic can't detect, processed by Sharp): `cdn.pixabay.com`

Hero images use `loading="eager"` and `fetchpriority="high"`.

## Verification Checklist

After changes, always verify:

1. `npm run build` succeeds
2. `npm run check` passes (astro check + ESLint + Prettier)
3. Visual check in browser: homepage, blog, dark mode, mobile menu
3 changes: 3 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Claude Code Configuration

See [AGENTS.md](./AGENTS.md) for all project documentation and AI agent instructions.
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ ENV PHP_TZ=UTC

# Multi-stage build: Build static assets
# This allows us to not include Python within the final container
FROM python:3.14-rc-bookworm AS python_builder
FROM python:3.14-trixie AS python_builder

WORKDIR /usr/src/app

Expand All @@ -27,7 +27,7 @@ RUN pip install --no-cache-dir -r requirements.txt && \

# Multi-stage build: Build static assets
# This allows us to not include Node within the final container
FROM node:20 AS node_builder
FROM node:24 AS node_builder

RUN mkdir -p /app/dist

Expand Down
41 changes: 26 additions & 15 deletions astro.config.mjs → astro.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,30 @@ import { fileURLToPath } from 'url';

import { defineConfig } from 'astro/config';

import { unified } from '@astrojs/markdown-remark';

import sitemap from '@astrojs/sitemap';
import tailwind from '@astrojs/tailwind';
import tailwindcss from '@tailwindcss/vite';
import mdx from '@astrojs/mdx';
import partytown from '@astrojs/partytown';
import icon from 'astro-icon';
import compress from 'astro-compress';
import type { AstroIntegration } from 'astro';

import astrowind from './vendor/integration';

import {
readingTimeRemarkPlugin,
responsiveTablesRehypePlugin,
lazyImagesRehypePlugin,
} from './src/utils/frontmatter.mjs';
import { readingTimeRemarkPlugin, responsiveTablesRehypePlugin } from './src/utils/frontmatter';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

const hasExternalScripts = false;
const whenExternalScripts = (items = []) =>
const whenExternalScripts = (items: (() => AstroIntegration) | (() => AstroIntegration)[] = []) =>
hasExternalScripts ? (Array.isArray(items) ? items.map((item) => item()) : [items()]) : [];

export default defineConfig({
output: 'static',
build: {
assets: 'assets/'
},

integrations: [
tailwind({
applyBaseStyles: false,
}),
sitemap(),
mdx(),
icon({
Expand Down Expand Up @@ -76,12 +70,29 @@ export default defineConfig({
}),
],

image: {
// Astro's default Sharp service handles local images.
//
// Most remote CDN images (Unsplash, Cloudinary, Imgix…) are routed by
// src/components/common/Image.astro through `unpic`, which rewrites the
// URL with CDN-side query parameters and serves it straight from the
// provider — Astro never downloads it, so they don't need to be listed.
//
// `domains` only matters for remote URLs that fall through to Astro's
// native <Image /> (i.e. providers Unpic can't detect, like Pixabay).
// Listed entries are authorized to be processed by Sharp.
// domains: ['cdn.pixabay.com'],
},

markdown: {
remarkPlugins: [readingTimeRemarkPlugin],
rehypePlugins: [responsiveTablesRehypePlugin, lazyImagesRehypePlugin],
processor: unified({
remarkPlugins: [readingTimeRemarkPlugin],
rehypePlugins: [responsiveTablesRehypePlugin],
}),
},

vite: {
plugins: [tailwindcss()],
resolve: {
alias: {
'~': path.resolve(__dirname, './src'),
Expand Down
7 changes: 7 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ export default [
'@typescript-eslint/no-non-null-assertion': 'off',
},
},
{
files: ['src/components/common/Analytics.astro', 'src/components/common/Analytics.astro/**'],
rules: {
'prefer-rest-params': 'off',
'no-var': 'off',
},
},
{
ignores: ['dist', 'node_modules', '.github', 'types.generated.d.ts', '.astro'],
},
Expand Down
4 changes: 2 additions & 2 deletions makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ all: assets
.PHONY: assets docs

node_modules:
npm install
npm ci

astro: node_modules
npm run build
Expand Down Expand Up @@ -35,4 +35,4 @@ docker-build:
docker build . -t test-lychee-docker --progress plain

docker-run: docker-build
docker run -p 9999:80 -t test-lychee-docker
docker run -p 9999:80 -t test-lychee-docker
Loading
Loading