Added initial Astro starter kit with font setup, linting/formatting,SEO/Sitemap TailwindCSS, Alpine.js, and Dockerize

This commit is contained in:
Pooriya Karami
2026-04-15 22:32:15 +03:30
parent e3d9728906
commit da585a0c34
17 changed files with 11338 additions and 43 deletions

2
.env.example Normal file
View File

@@ -0,0 +1,2 @@
CONTAINER_NAME=astro-docker-kit
SITE_URL=https://example.com

3
.prettierignore Normal file
View File

@@ -0,0 +1,3 @@
node_modules
dist
.astro

21
.prettierrc.mjs Normal file
View File

@@ -0,0 +1,21 @@
/** @type {import("prettier").Config} */
export default {
// تنظیمات دلخواه Prettier شما
semi: true,
singleQuote: true,
tabWidth: 2,
trailingComma: 'es5',
// معرفی پلاگین‌ها (ترتیب اهمیت دارد)
plugins: ['prettier-plugin-astro', 'prettier-plugin-tailwindcss'],
// تنظیمات اختصاصی برای فایل‌های Astro
overrides: [
{
files: '*.astro',
options: {
parser: 'astro',
},
},
],
};

17
Dockerfile Normal file
View File

@@ -0,0 +1,17 @@
FROM docker.arvancloud.ir/node:20-alpine AS build
RUN npm config set registry https://package-mirror.liara.ir/repository/npm/ --global
RUN echo "https://mirror.arvancloud.ir/alpine/v3.20/main" > /etc/apk/repositories && \
echo "https://mirror.arvancloud.ir/alpine/v3.20/community" >> /etc/apk/repositories
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
FROM docker.arvancloud.ir/nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
# افزودن gzip و cache برای بهترین پرفورمنس
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80

278
README.md
View File

@@ -1,43 +1,273 @@
# Astro Starter Kit: Minimal
<div align="center">
```sh
npm create astro@latest -- --template minimal
# 🚀 Astro Docker Kit
</div>
## 🚀 Quick Start
```bash
git clone https://github.com/advento-ir/astro-docker-kit
cd astro-docker-kit
npm install
npm run dev
```
> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun!
Visit `http://localhost:4321`.
## 🚀 Project Structure
---
Inside of your Astro project, you'll see the following folders and files:
## 🐳 Deploy with Docker
```bash
cp .env.example .env
docker network create private-net # required once
docker compose up -d
```
---
## 📁 Project Structure
```text
/
├── public/
├── public/fonts/iran-yekan-x/ # IRANYekanX woff2 files
├── src/
── pages/
── index.astro
└── package.json
── components/
── blocks/ # Content block components
│ │ ├── core/ # Base UI components
│ │ ├── layout/ # Header, footer, sidebar
│ │ └── meta/SEO.astro # SEO component
│ ├── layouts/BaseLayout.astro # RTL/FA base layout
│ ├── pages/index.astro # Homepage
│ └── styles/
│ ├── fonts.css # Persian font definitions
│ └── global.css # Global styles + Tailwind
├── Dockerfile
├── docker-compose.yml
└── nginx.conf
```
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
---
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
## 🔍 SEO Component
Any static assets, like images, can be placed in the `public/` directory.
`src/components/meta/SEO.astro` is a full-featured, drop-in SEO component used inside `BaseLayout.astro`. It handles every layer of modern SEO — from core meta tags to structured data — with zero external dependencies.
### Features at a glance
| Category | What's included |
| --- | --- |
| Core meta | `<title>`, `description`, `keywords`, `author`, `robots`, `theme-color`, `color-scheme`, `generator` |
| Canonical | Auto-detected from `Astro.url.href` or manually overridden |
| Open Graph | `og:type`, `og:title`, `og:description`, `og:image` (+ width/height/alt), `og:locale`, `og:site_name` |
| OG Article | `article:published_time`, `article:modified_time`, `article:section`, `article:tag`, `article:author` |
| Twitter Card | `twitter:card`, `twitter:title`, `twitter:description`, `twitter:image`, `twitter:site`, `twitter:creator` |
| JSON-LD | Auto-generated `WebSite`, `WebPage` / `Article`, `BreadcrumbList` schemas + custom schema injection |
| hreflang | Alternate locale `<link>` tags for multilingual sites |
| Resource hints | `preconnect` and `dns-prefetch` link tags |
| Favicon | SVG icon, ICO fallback, Apple touch icon, Web App Manifest |
---
### Basic usage
`BaseLayout.astro` already wires the component up — just pass props when using the layout:
```astro
---
import BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout
title="My Page"
description="A short description of this page."
/>
```
All other props are optional and tree-shake to nothing when unused.
---
### All props
#### Core
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `title` | `string` | **required** | Page title (also used as OG/Twitter fallback) |
| `titleTemplate` | `string` | `'%s'` | Wraps the title — `%s` is replaced with `title`. E.g. `'%s \| Advento'` |
| `description` | `string` | **required** | Meta description (also used as OG/Twitter fallback) |
| `keywords` | `string[]` | — | Comma-joined into `<meta name="keywords">` |
| `author` | `string` | — | `<meta name="author">` |
| `themeColor` | `string` | `'#7c3aed'` | `<meta name="theme-color">` |
#### Canonical & Robots
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `canonical` | `string` | `Astro.url.href` | Absolute canonical URL |
| `noindex` | `boolean` | `false` | Add `noindex` to the robots directive |
| `nofollow` | `boolean` | `false` | Add `nofollow` to the robots directive |
#### Open Graph
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `ogType` | `'website' \| 'article' \| 'profile'` | `'website'` | OG object type |
| `ogTitle` | `string` | resolved `title` | Override OG title |
| `ogDescription` | `string` | `description` | Override OG description |
| `ogImage` | `string` | — | Absolute URL to the OG image (1200×630 recommended) |
| `ogImageAlt` | `string` | — | Alt text for the OG image |
| `ogImageWidth` | `number` | `1200` | OG image width in pixels |
| `ogImageHeight` | `number` | `630` | OG image height in pixels |
| `ogLocale` | `string` | `'fa_IR'` | OG locale string |
| `ogSiteName` | `string` | — | `og:site_name` |
#### Twitter Card
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `twitterCard` | `'summary' \| 'summary_large_image' \| 'app' \| 'player'` | `'summary_large_image'` | Card type |
| `twitterSite` | `string` | — | Site Twitter handle (e.g. `@advento_ir`) |
| `twitterCreator` | `string` | — | Author Twitter handle |
| `twitterTitle` | `string` | OG title | Override Twitter title |
| `twitterDescription` | `string` | OG description | Override Twitter description |
| `twitterImage` | `string` | OG image | Override Twitter image URL |
| `twitterImageAlt` | `string` | — | Alt text for the Twitter image |
#### Article (requires `ogType="article"`)
Pass an `article` object with the following shape:
```ts
interface ArticleMeta {
publishedTime: string; // ISO 8601, e.g. "2025-01-15T10:00:00Z"
modifiedTime?: string;
authors?: string[]; // names or profile URLs
section?: string;
tags?: string[];
}
```
#### JSON-LD & Breadcrumbs
| Prop | Type | Description |
| --- | --- | --- |
| `breadcrumbs` | `BreadcrumbItem[]` | Generates a `BreadcrumbList` schema automatically |
| `jsonLd` | `Record<string, unknown> \| Record<string, unknown>[]` | Inject one or more custom schema.org objects |
```ts
interface BreadcrumbItem {
name: string;
url: string; // absolute URL
}
```
#### Alternates & Resource Hints
| Prop | Type | Description |
| --- | --- | --- |
| `alternateLocales` | `AlternateLocale[]` | Emits `<link rel="alternate" hreflang="...">` for each entry |
| `preconnect` | `string[]` | Emits `<link rel="preconnect">` for each origin |
| `dnsPrefetch` | `string[]` | Emits `<link rel="dns-prefetch">` for each origin |
```ts
interface AlternateLocale {
hreflang: string; // e.g. "en", "fa", "x-default"
href: string; // absolute URL
}
```
#### Favicon & Manifest
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `faviconSvg` | `string` | `'/favicon.svg'` | Path to SVG favicon |
| `faviconIco` | `string` | — | Path to `.ico` favicon (legacy browsers) |
| `appleTouchIcon` | `string` | — | Path to Apple touch icon (180×180 PNG) |
| `manifestHref` | `string` | — | Path to `manifest.json` / `manifest.webmanifest` |
---
### Full example — article page
```astro
---
import BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout
title="How We Built This Kit"
titleTemplate="%s | Advento"
description="A deep-dive into our Astro + Docker starter setup."
keywords={['Astro', 'Docker', 'Tailwind', 'RTL']}
author="Advento Team"
canonical="https://advento.ir/blog/how-we-built-this-kit"
ogType="article"
ogImage="https://advento.ir/og/how-we-built-this-kit.png"
ogImageAlt="Screenshot of the Astro Docker Kit starter"
ogSiteName="Advento"
twitterSite="@advento_ir"
twitterCreator="@advento_ir"
article={{
publishedTime: '2025-01-15T10:00:00Z',
modifiedTime: '2025-06-01T08:00:00Z',
authors: ['Advento Team'],
section: 'Engineering',
tags: ['Astro', 'Docker', 'SEO'],
}}
breadcrumbs={[
{ name: 'Home', url: 'https://advento.ir' },
{ name: 'Blog', url: 'https://advento.ir/blog' },
{ name: 'How We Built This Kit', url: 'https://advento.ir/blog/how-we-built-this-kit' },
]}
alternateLocales={[
{ hreflang: 'fa', href: 'https://advento.ir/fa/blog/how-we-built-this-kit' },
{ hreflang: 'x-default', href: 'https://advento.ir/blog/how-we-built-this-kit' },
]}
preconnect={['https://fonts.gstatic.com']}
/>
```
---
## 🧞 Commands
All commands are run from the root of the project, from a terminal:
| Command | Action |
| ---------------------- | ------------------------------------ |
| `npm run dev` | Start dev server at `localhost:4321` |
| `npm run build` | Build production site to `./dist/` |
| `npm run preview` | Preview the production build locally |
| `npm run format` | Format code with Prettier |
| `npm run lint` | Lint with ESLint |
| `docker compose up -d` | Run container in background |
| Command | Action |
| :------------------------ | :----------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:4321` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
| `npm run astro -- --help` | Get help using the Astro CLI |
---
## 👀 Want to learn more?
## 🤝 Contributing
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).
1. Fork the repo
2. Create a branch: `git checkout -b feature/your-feature`
3. Commit: `git commit -m 'feat: add your feature'`
4. Push and open a Pull Request
---
## 🌌 About Advento
At [Advento](https://advento.ir), we are redefining the digital marketing landscape. By harnessing the absolute edge of Artificial Intelligence, we deliver highly scalable, data-driven, and innovative solutions. This repository is part of our commitment to pushing the boundaries of what's possible in digital marketing technology.
---
## 📄 License
MIT — see [LICENSE](LICENSE) for details.
---
<div align="center">
Made with ❤️ by [**Advento**](https://advento.ir)
</div>

View File

@@ -1,5 +1,14 @@
// @ts-check
import { defineConfig } from 'astro/config';
import tailwindcss from '@tailwindcss/vite';
import alpinejs from '@astrojs/alpinejs';
import icon from 'astro-icon';
import sitemap from '@astrojs/sitemap';
// https://astro.build/config
export default defineConfig({});
export default defineConfig({
integrations: [alpinejs(), icon(), sitemap()],
site: import.meta.env.SITE_URL,
vite: {
plugins: [tailwindcss()],
},
});

15
docker-compose.yml Normal file
View File

@@ -0,0 +1,15 @@
version: '3.9'
services:
astro:
build:
context: .
dockerfile: Dockerfile
container_name: ${CONTAINER_NAME}
networks:
- private-net
restart: always
networks:
private-net:
external: true

16
eslint.config.mjs Normal file
View File

@@ -0,0 +1,16 @@
import eslintPluginAstro from 'eslint-plugin-astro';
export default [
{
ignores: ['.astro/**', 'dist/**', 'node_modules/**'],
},
// اضافه کردن تنظیمات پیشنهادی (Recommended) برای Astro
...eslintPluginAstro.configs.recommended,
...eslintPluginAstro.configs['jsx-a11y-recommended'],
{
// اگر قوانین خاصی مد نظرتان است می‌توانید اینجا اضافه کنید
rules: {
// "astro/no-set-html-directive": "error"
},
},
];

12
nginx.conf Normal file
View File

@@ -0,0 +1,12 @@
server {
listen 80;
root /usr/share/nginx/html;
location / {
try_files $uri $uri/ =404;
}
gzip on;
gzip_types text/css application/javascript image/svg+xml;
gzip_min_length 1024;
}

10170
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -6,9 +6,28 @@
"dev": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
"astro": "astro",
"format": "prettier --write .",
"lint": "eslint ."
},
"dependencies": {
"astro": "^5.16.6"
"@astrojs/alpinejs": "^0.5.0",
"@astrojs/sitemap": "^3.7.2",
"@fontsource/vazirmatn": "^5.2.8",
"@iconify-json/ph": "^1.2.2",
"alpinejs": "^3.15.11",
"astro": "^5.16.6",
"astro-icon": "^1.1.5",
"vazirmatn": "^33.0.3"
},
"devDependencies": {
"@tailwindcss/vite": "^4.2.2",
"eslint": "^9.39.4",
"eslint-plugin-astro": "^1.7.0",
"eslint-plugin-jsx-a11y": "^6.10.2",
"prettier": "^3.8.2",
"prettier-plugin-astro": "^0.14.1",
"prettier-plugin-tailwindcss": "^0.7.2",
"tailwindcss": "^4.2.2"
}
}

View File

@@ -0,0 +1,307 @@
---
/**
* SEO Component — Full-featured, modern SEO for Astro
*
* Features:
* - Core meta tags (title, description, robots, keywords, author, theme-color)
* - Canonical URL (auto-detected or manual override)
* - Open Graph (website + article support)
* - Twitter Card (summary_large_image / summary)
* - JSON-LD structured data (WebSite, WebPage, Article, BreadcrumbList)
* - Alternate language links (hreflang)
* - Preconnect / DNS-prefetch resource hints
* - Favicon + Apple touch icon + Web manifest
*/
export interface AlternateLocale {
hreflang: string;
href: string;
}
export interface BreadcrumbItem {
name: string;
url: string;
}
export interface ArticleMeta {
publishedTime: string;
modifiedTime?: string;
authors?: string[];
section?: string;
tags?: string[];
}
export interface Props {
// ─── Core ─────────────────────────────────────────────────────────────────
title: string;
/** e.g. "%s | Advento" — %s is replaced with title */
titleTemplate?: string;
description: string;
keywords?: string[];
author?: string;
themeColor?: string;
// ─── Canonical / Robots ───────────────────────────────────────────────────
/** Defaults to Astro.url.href */
canonical?: string;
noindex?: boolean;
nofollow?: boolean;
// ─── Open Graph ───────────────────────────────────────────────────────────
ogTitle?: string;
ogDescription?: string;
/** Absolute URL to OG image (1200×630 recommended) */
ogImage?: string;
ogImageAlt?: string;
ogImageWidth?: number;
ogImageHeight?: number;
ogType?: 'website' | 'article' | 'profile';
/** e.g. "fa_IR" */
ogLocale?: string;
ogSiteName?: string;
// ─── Twitter ──────────────────────────────────────────────────────────────
twitterCard?: 'summary' | 'summary_large_image' | 'app' | 'player';
twitterSite?: string;
twitterCreator?: string;
twitterTitle?: string;
twitterDescription?: string;
twitterImage?: string;
twitterImageAlt?: string;
// ─── Article ──────────────────────────────────────────────────────────────
article?: ArticleMeta;
// ─── JSON-LD ──────────────────────────────────────────────────────────────
jsonLd?: Record<string, unknown> | Record<string, unknown>[];
breadcrumbs?: BreadcrumbItem[];
// ─── Alternates ───────────────────────────────────────────────────────────
alternateLocales?: AlternateLocale[];
// ─── Resource hints ───────────────────────────────────────────────────────
preconnect?: string[];
dnsPrefetch?: string[];
// ─── Favicon ──────────────────────────────────────────────────────────────
faviconSvg?: string;
faviconIco?: string;
appleTouchIcon?: string;
manifestHref?: string;
}
const {
title,
titleTemplate = '%s',
description,
keywords,
author,
themeColor = '#7c3aed',
canonical,
noindex = false,
nofollow = false,
ogTitle,
ogDescription,
ogImage,
ogImageAlt,
ogImageWidth = 1200,
ogImageHeight = 630,
ogType = 'website',
ogLocale = 'fa_IR',
ogSiteName,
twitterCard = 'summary_large_image',
twitterSite,
twitterCreator,
twitterTitle,
twitterDescription,
twitterImage,
twitterImageAlt,
article,
jsonLd,
breadcrumbs,
alternateLocales,
preconnect = [],
dnsPrefetch = [],
faviconSvg = '/favicon.svg',
faviconIco,
appleTouchIcon,
manifestHref,
} = Astro.props;
// ─── Derived values ───────────────────────────────────────────────────────────
const resolvedTitle = titleTemplate.includes('%s')
? titleTemplate.replace('%s', title)
: title;
const resolvedCanonical = canonical ?? Astro.url.href;
const resolvedOgTitle = ogTitle ?? resolvedTitle;
const resolvedOgDescription = ogDescription ?? description;
const resolvedOgImage = ogImage ?? null;
const resolvedTwitterTitle = twitterTitle ?? resolvedOgTitle;
const resolvedTwitterDescription = twitterDescription ?? resolvedOgDescription;
const resolvedTwitterImage = twitterImage ?? resolvedOgImage;
const robotsContent = [
noindex ? 'noindex' : 'index',
nofollow ? 'nofollow' : 'follow',
'max-snippet:-1',
'max-image-preview:large',
'max-video-preview:-1',
].join(', ');
// ─── JSON-LD schemas ──────────────────────────────────────────────────────────
const websiteSchema: Record<string, unknown> = {
'@context': 'https://schema.org',
'@type': 'WebSite',
name: ogSiteName ?? resolvedTitle,
url: new URL('/', resolvedCanonical).href,
};
const webPageSchema: Record<string, unknown> = {
'@context': 'https://schema.org',
'@type': ogType === 'article' ? 'Article' : 'WebPage',
name: resolvedTitle,
description,
url: resolvedCanonical,
...(resolvedOgImage ? { image: resolvedOgImage } : {}),
...(ogType === 'article' && article
? {
datePublished: article.publishedTime,
...(article.modifiedTime ? { dateModified: article.modifiedTime } : {}),
...(article.authors?.length
? {
author: article.authors.map((a) => ({
'@type': 'Person',
name: a,
})),
}
: {}),
...(article.section ? { articleSection: article.section } : {}),
...(article.tags?.length ? { keywords: article.tags.join(', ') } : {}),
}
: {}),
};
const breadcrumbSchema: Record<string, unknown> | null =
breadcrumbs && breadcrumbs.length > 0
? {
'@context': 'https://schema.org',
'@type': 'BreadcrumbList',
itemListElement: breadcrumbs.map((item, index) => ({
'@type': 'ListItem',
position: index + 1,
name: item.name,
item: item.url,
})),
}
: null;
const allJsonLd: Record<string, unknown>[] = [
websiteSchema,
webPageSchema,
...(breadcrumbSchema ? [breadcrumbSchema] : []),
...(Array.isArray(jsonLd) ? jsonLd : jsonLd ? [jsonLd] : []),
];
---
<!-- ─── Charset & Viewport ─────────────────────────────────────────────────── -->
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- ─── Core Meta ──────────────────────────────────────────────────────────── -->
<title>{resolvedTitle}</title>
<meta name="description" content={description} />
<meta name="robots" content={robotsContent} />
{keywords && keywords.length > 0 && (
<meta name="keywords" content={keywords.join(', ')} />
)}
{author && <meta name="author" content={author} />}
<meta name="theme-color" content={themeColor} />
<meta name="color-scheme" content="dark light" />
<meta name="generator" content={Astro.generator} />
<!-- ─── Canonical ─────────────────────────────────────────────────────────── -->
<link rel="canonical" href={resolvedCanonical} />
<!-- ─── Alternate Locales ─────────────────────────────────────────────────── -->
{alternateLocales?.map((alt) => (
<link rel="alternate" hreflang={alt.hreflang} href={alt.href} />
))}
<!-- ─── Open Graph ────────────────────────────────────────────────────────── -->
<meta property="og:type" content={ogType} />
<meta property="og:title" content={resolvedOgTitle} />
<meta property="og:description" content={resolvedOgDescription} />
<meta property="og:url" content={resolvedCanonical} />
<meta property="og:locale" content={ogLocale} />
{ogSiteName && <meta property="og:site_name" content={ogSiteName} />}
{resolvedOgImage && (
<>
<meta property="og:image" content={resolvedOgImage} />
<meta property="og:image:width" content={String(ogImageWidth)} />
<meta property="og:image:height" content={String(ogImageHeight)} />
<meta property="og:image:type" content="image/png" />
{ogImageAlt && <meta property="og:image:alt" content={ogImageAlt} />}
</>
)}
<!-- ─── OG Article ────────────────────────────────────────────────────────── -->
{ogType === 'article' && article && (
<>
<meta property="article:published_time" content={article.publishedTime} />
{article.modifiedTime && (
<meta property="article:modified_time" content={article.modifiedTime} />
)}
{article.section && (
<meta property="article:section" content={article.section} />
)}
{article.tags?.map((tag) => (
<meta property="article:tag" content={tag} />
))}
{article.authors?.map((a) => (
<meta property="article:author" content={a} />
))}
</>
)}
<!-- ─── Twitter Card ──────────────────────────────────────────────────────── -->
<meta name="twitter:card" content={twitterCard} />
<meta name="twitter:title" content={resolvedTwitterTitle} />
<meta name="twitter:description" content={resolvedTwitterDescription} />
{twitterSite && <meta name="twitter:site" content={twitterSite} />}
{twitterCreator && <meta name="twitter:creator" content={twitterCreator} />}
{resolvedTwitterImage && (
<>
<meta name="twitter:image" content={resolvedTwitterImage} />
{twitterImageAlt && (
<meta name="twitter:image:alt" content={twitterImageAlt} />
)}
</>
)}
<!-- ─── Favicon & Manifest ────────────────────────────────────────────────── -->
{faviconSvg && <link rel="icon" type="image/svg+xml" href={faviconSvg} />}
{faviconIco && <link rel="icon" type="image/x-icon" href={faviconIco} sizes="any" />}
{appleTouchIcon && <link rel="apple-touch-icon" href={appleTouchIcon} />}
{manifestHref && <link rel="manifest" href={manifestHref} />}
<!-- ─── Resource Hints ────────────────────────────────────────────────────── -->
{preconnect.map((url) => (
<link rel="preconnect" href={url} crossorigin="anonymous" />
))}
{dnsPrefetch.map((url) => (
<link rel="dns-prefetch" href={url} />
))}
<!-- ─── JSON-LD Structured Data ──────────────────────────────────────────── -->
{allJsonLd.map((schema) => (
<script type="application/ld+json" set:html={JSON.stringify(schema)} />
))}

View File

@@ -0,0 +1,19 @@
---
import '../styles/global.css';
import SEO from '../components/meta/SEO.astro';
import type { Props as SEOProps } from '../components/meta/SEO.astro';
export type Props = SEOProps;
const seoProps = Astro.props;
---
<!doctype html>
<html lang="fa" dir="rtl">
<head>
<SEO {...seoProps} />
</head>
<body class="bg-gray-950 font-sans text-gray-100 antialiased">
<slot />
</body>
</html>

View File

@@ -1,16 +1,430 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
import { Icon } from 'astro-icon/components';
const siteUrl = 'https://advento.ir';
const features = [
{
icon: 'ph:rocket-launch-duotone',
title: 'Astro 5',
desc: 'آخرین نسخه Astro با معماری Islands و بهینه‌سازی خودکار برای بالاترین سرعت.',
color: 'from-violet-500 to-purple-600',
},
{
icon: 'ph:paint-brush-duotone',
title: 'Tailwind CSS v4',
desc: 'سیستم طراحی utility-first با پشتیبانی کامل از RTL و پیکربندی فانت فارسی.',
color: 'from-sky-500 to-cyan-600',
},
{
icon: 'ph:lightning-duotone',
title: 'Alpine.js',
desc: 'تعاملات سبک و واکنش‌گرا بدون نیاز به bundle سنگین — مستقیم در markup.',
color: 'from-emerald-500 to-teal-600',
},
{
icon: 'ph:cube-duotone',
title: 'Docker Ready',
desc: 'Dockerfile چند مرحله‌ای و docker-compose آماده برای deploy فوری در هر محیطی.',
color: 'from-orange-500 to-amber-600',
},
{
icon: 'ph:globe-duotone',
title: 'Nginx بهینه',
desc: 'سرور Nginx با پشتیبانی از gzip و cache header برای بالاترین امتیاز Core Web Vitals.',
color: 'from-pink-500 to-rose-600',
},
{
icon: 'ph:magnifying-glass-duotone',
title: 'SEO پیشرفته',
desc: 'کامپوننت SEO با پشتیبانی از Open Graph، Twitter Card و تگ‌های canonical.',
color: 'from-indigo-500 to-blue-600',
},
{
icon: 'ph:text-aa-duotone',
title: 'فونت وزیر',
desc: 'فونت فارسی حرفه‌ای با تمام وزن‌ها و bswap بهینه برای بارگذاری سریع.',
color: 'from-fuchsia-500 to-purple-600',
},
{
icon: 'ph:code-duotone',
title: 'TypeScript & ESLint',
desc: 'پیکربندی آماده TypeScript، ESLint و Prettier برای کدنویسی تمیز و خطاناپذیر.',
color: 'from-lime-500 to-green-600',
},
];
const steps = [
{
num: '۱',
label: 'Clone',
code: 'git clone https://github.com/ooriyap/astro-docker-kit',
},
{ num: '۲', label: 'نصب', code: 'npm install' },
{ num: '۳', label: 'اجرا', code: 'npm run dev' },
{ num: '۴', label: 'Deploy', code: 'docker compose up -d' },
];
---
---
<BaseLayout
title="استارتر کیت حرفه‌ای"
titleTemplate="%s | Astro Docker Kit"
description="استارتر کیت مدرن برای ساخت سایت‌های فارسی با Astro، Tailwind CSS v4، Alpine.js و Docker"
keywords={['Astro', 'Tailwind CSS', 'Alpine.js']}
author="Advento"
canonical={siteUrl}
ogType="website"
ogSiteName="Astro Docker Kit"
ogLocale="fa_IR"
breadcrumbs={[{ name: 'خانه', url: siteUrl }]}
>
<!-- Navbar -->
<header
class="sticky top-0 z-50 border-b border-white/5 bg-gray-950/80 backdrop-blur-md"
>
<div class="mx-auto flex max-w-6xl items-center justify-between px-6 py-4">
<div class="flex items-center gap-3">
<div
class="flex h-9 w-9 items-center justify-center rounded-xl bg-gradient-to-br from-violet-500 to-purple-700 shadow-lg shadow-violet-500/30"
>
<Icon name="ph:rocket-launch-duotone" class="h-5 w-5 text-white" />
</div>
<span class="text-lg font-bold tracking-tight">Astro Docker Kit</span>
</div>
<nav class="hidden items-center gap-6 text-sm text-gray-400 md:flex">
<a href="#features" class="transition hover:text-white">امکانات</a>
<a href="#quickstart" class="transition hover:text-white">شروع سریع</a>
<a href="#stack" class="transition hover:text-white">تکنولوژی‌ها</a>
<a
href="https://advento.ir"
target="_blank"
rel="noopener noreferrer"
class="flex items-center gap-1.5 rounded-lg bg-violet-600 px-4 py-1.5 font-medium text-white transition hover:bg-violet-500"
>
<Icon name="ph:arrow-square-out" class="h-4 w-4" />
Advento
</a>
</nav>
</div>
</header>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>Astro</title>
</head>
<body>
<h1>Astro</h1>
</body>
</html>
<main>
<!-- Hero -->
<section class="relative overflow-hidden px-6 py-28 text-center">
<!-- Background glow -->
<div
class="pointer-events-none absolute inset-0 -z-10 flex items-center justify-center"
aria-hidden="true"
>
<div
class="h-[600px] w-[600px] rounded-full bg-violet-600/10 blur-[120px]"
>
</div>
</div>
<div class="mx-auto max-w-4xl">
<span
class="mb-6 inline-flex items-center gap-2 rounded-full border border-violet-500/30 bg-violet-500/10 px-4 py-1.5 text-sm font-medium text-violet-300"
>
<Icon name="ph:star-duotone" class="h-4 w-4" />
ساخته‌شده توسط Advento
</span>
<h1 class="mb-6 text-5xl leading-tight font-extrabold md:text-7xl">
استارتر کیت
<span
class="bg-gradient-to-l from-violet-400 to-cyan-400 bg-clip-text text-transparent"
>
حرفه‌ای فارسی
</span>
</h1>
<p
class="mx-auto mb-10 max-w-2xl text-xl leading-relaxed text-gray-400"
>
یک پایه آماده و بهینه برای ساخت پروژه‌های وب فارسی با Astro، Tailwind
CSS، Alpine.js و Docker — از صفر تا deploy در چند دقیقه.
</p>
<div class="flex flex-wrap items-center justify-center gap-4">
<a
href="#quickstart"
class="flex items-center gap-2 rounded-xl bg-violet-600 px-7 py-3.5 font-semibold text-white shadow-lg shadow-violet-500/30 transition hover:-translate-y-0.5 hover:bg-violet-500"
>
<Icon name="ph:rocket-launch" class="h-5 w-5" />
شروع کن
</a>
<a
href="http://git.vnto.ir/ooriyap/astro-docker-kit"
target="_blank"
rel="noopener noreferrer"
class="flex items-center gap-2 rounded-xl border border-white/10 bg-white/5 px-7 py-3.5 font-semibold text-white transition hover:-translate-y-0.5 hover:bg-white/10"
>
<Icon name="ph:github-logo" class="h-5 w-5" />
مشاهده در Gitea
</a>
</div>
<!-- Tech badges -->
<div class="mt-14 flex flex-wrap items-center justify-center gap-3">
{
[
{
label: 'Astro 5',
color: 'bg-violet-500/15 text-violet-300 border-violet-500/25',
},
{
label: 'Tailwind v4',
color: 'bg-sky-500/15 text-sky-300 border-sky-500/25',
},
{
label: 'Alpine.js',
color:
'bg-emerald-500/15 text-emerald-300 border-emerald-500/25',
},
{
label: 'Docker',
color: 'bg-blue-500/15 text-blue-300 border-blue-500/25',
},
{
label: 'TypeScript',
color: 'bg-indigo-500/15 text-indigo-300 border-indigo-500/25',
},
{
label: 'RTL/FA',
color: 'bg-rose-500/15 text-rose-300 border-rose-500/25',
},
].map((badge) => (
<span
class={`rounded-full border px-3.5 py-1 text-xs font-medium ${badge.color}`}
>
{badge.label}
</span>
))
}
</div>
</div>
</section>
<!-- Features -->
<section id="features" class="px-6 py-24">
<div class="mx-auto max-w-6xl">
<div class="mb-16 text-center">
<h2 class="mb-4 text-4xl font-bold">همه چیز از قبل آماده است</h2>
<p class="text-lg text-gray-400">
هر ابزاری که برای یک پروژه فارسی حرفه‌ای نیاز دارید، از پیش پیکربندی
شده.
</p>
</div>
<div class="grid gap-5 sm:grid-cols-2 lg:grid-cols-4">
{
features.map((f) => (
<div class="group relative rounded-2xl border border-white/5 bg-white/[0.03] p-6 transition hover:border-white/10 hover:bg-white/[0.06]">
<div
class={`mb-4 inline-flex h-12 w-12 items-center justify-center rounded-xl bg-gradient-to-br ${f.color} shadow-lg`}
>
<Icon name={f.icon} class="h-6 w-6 text-white" />
</div>
<h3 class="mb-2 text-lg font-semibold">{f.title}</h3>
<p class="text-sm leading-relaxed text-gray-400">{f.desc}</p>
</div>
))
}
</div>
</div>
</section>
<!-- Quick Start -->
<section id="quickstart" class="px-6 py-24">
<div class="mx-auto max-w-4xl">
<div class="mb-16 text-center">
<h2 class="mb-4 text-4xl font-bold">شروع سریع</h2>
<p class="text-lg text-gray-400">
در کمتر از ۵ دقیقه پروژه‌ات را راه‌اندازی کن.
</p>
</div>
<div class="relative mx-auto max-w-2xl">
<!-- vertical line -->
<div
class="absolute top-0 right-[1.9rem] h-full w-px bg-gradient-to-b from-violet-500/60 via-violet-500/20 to-transparent"
aria-hidden="true"
>
</div>
{
steps.map((step, i) => (
<div
class={`relative flex items-start gap-6 ${i !== steps.length - 1 ? 'mb-10' : ''}`}
>
<span class="relative z-10 flex h-16 w-16 shrink-0 items-center justify-center rounded-2xl border border-violet-500/30 bg-gray-950 text-xl font-bold text-violet-400 shadow-lg shadow-violet-500/10">
{step.num}
</span>
<div class="flex-1 rounded-2xl border border-white/5 bg-white/[0.03] p-5 transition hover:border-white/10 hover:bg-white/[0.06]">
<p class="mb-3 font-semibold text-white">{step.label}</p>
<code class="block rounded-lg bg-gray-900 px-4 py-3 font-mono text-sm text-emerald-400">
{step.code}
</code>
</div>
</div>
))
}
</div>
</div>
</section>
<!-- Stack / Architecture -->
<section id="stack" class="px-6 py-24">
<div class="mx-auto max-w-6xl">
<div class="mb-16 text-center">
<h2 class="mb-4 text-4xl font-bold">معماری پروژه</h2>
<p class="text-lg text-gray-400">
ساختار تمیز و قابل گسترش برای هر مقیاسی.
</p>
</div>
<div class="grid gap-8 md:grid-cols-2">
<!-- File tree -->
<div class="rounded-2xl border border-white/5 bg-gray-900 p-6">
<div class="mb-4 flex items-center gap-2">
<Icon
name="ph:folder-open-duotone"
class="h-5 w-5 text-amber-400"
/>
<span class="font-semibold text-amber-400">ساختار فایل‌ها</span>
</div>
<pre
class="overflow-x-auto text-right font-mono text-sm leading-7 text-gray-300">
{`/
├── public/
│ └── fonts/iran-yekan-x/
├── src/
│ ├── components/
│ │ ├── blocks/
│ │ ├── core/
│ │ ├── layout/
│ │ └── meta/SEO.astro
│ ├── layouts/
│ │ └── BaseLayout.astro
│ ├── pages/
│ │ └── index.astro
│ └── styles/
│ ├── fonts.css
│ └── global.css
├── Dockerfile
├── docker-compose.yml
└── nginx.conf`}
</pre>
</div>
<!-- Key configs -->
<div class="flex flex-col gap-4">
{
[
{
icon: 'ph:shipping-container-duotone',
color: 'text-blue-400',
title: 'Multi-stage Docker Build',
desc: 'مرحله build با Node.js و مرحله serve با Nginx Alpine برای کوچک‌ترین image ممکن.',
},
{
icon: 'ph:shield-check-duotone',
color: 'text-emerald-400',
title: 'Nginx بهینه‌سازی‌شده',
desc: 'فشرده‌سازی gzip، cache کارآمد و تنظیمات امنیتی پایه آماده.',
},
{
icon: 'ph:text-aa-duotone',
color: 'text-rose-400',
title: 'پشتیبانی کامل RTL',
desc: 'تنظیم dir="rtl" و lang="fa" در layout پایه، همراه فونت Vazir.',
},
{
icon: 'ph:cloud-arrow-up-duotone',
color: 'text-violet-400',
title: 'آماده برای Deploy',
desc: 'یک دستور docker compose up -d برای راه‌اندازی کامل در سرور.',
},
].map((item) => (
<div class="flex items-start gap-4 rounded-2xl border border-white/5 bg-white/[0.03] p-5">
<Icon
name={item.icon}
class={`mt-0.5 h-6 w-6 shrink-0 ${item.color}`}
/>
<div>
<p class="mb-1 font-semibold">{item.title}</p>
<p class="text-sm text-gray-400">{item.desc}</p>
</div>
</div>
))
}
</div>
</div>
</div>
</section>
<!-- Advento CTA -->
<section class="px-6 py-24">
<div class="mx-auto max-w-3xl">
<div
class="relative overflow-hidden rounded-3xl border border-violet-500/20 bg-gradient-to-br from-violet-900/30 via-gray-900 to-gray-950 p-10 text-center shadow-2xl shadow-violet-500/10"
>
<!-- glow -->
<div
class="pointer-events-none absolute -top-20 right-1/2 h-64 w-64 translate-x-1/2 rounded-full bg-violet-600/20 blur-[80px]"
aria-hidden="true"
>
</div>
<div
class="relative mb-6 inline-flex h-16 w-16 items-center justify-center rounded-2xl bg-gradient-to-br from-violet-500 to-purple-700 shadow-xl shadow-violet-500/40"
>
<Icon name="ph:brain-duotone" class="h-8 w-8 text-white" />
</div>
<h2 class="relative mb-4 text-3xl font-bold">
ساخته‌شده توسط
<a
href="https://advento.ir"
target="_blank"
rel="noopener noreferrer"
class="text-violet-400 transition hover:text-violet-300"
>
Advento
</a>
</h2>
<p
class="relative mx-auto mb-8 max-w-xl text-lg leading-relaxed text-gray-400"
>
ما در Advento با بهره‌گیری از آخرین فناوری‌های هوش مصنوعی،
راه‌حل‌های دیجیتال مارکتینگ مقیاس‌پذیر، داده‌محور و نوآورانه ارائه
می‌دهیم.
</p>
</div>
</div>
</section>
</main>
<!-- Footer -->
<footer class="border-t border-white/5 px-6 py-8">
<div
class="mx-auto flex max-w-6xl flex-col items-center justify-between gap-3 text-sm text-gray-500 md:flex-row"
>
<span>۲۰۲۶ © تمامی حقوق برای آژانس ادونتو محفوظ است.</span>
<div>
<span>Crafted with care by</span>
<a
href="https://advento.ir"
target="_blank"
rel="noopener noreferrer"
class="transition hover:text-gray-300"
>
Advento</a
>
</div>
</div>
</footer>
</BaseLayout>

26
src/styles/fonts.css Normal file
View File

@@ -0,0 +1,26 @@
@import '@fontsource/vazirmatn';
/*
@font-face {
font-family: "CustomFont";
src: url("/fonts/custom-font/CustomFont-Light.woff2") format("woff2");
font-weight: 300;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "CustomFont";
src: url("/fonts/custom-font/CustomFont-Regular.woff2") format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "CustomFont";
src: url("/fonts/custom-font/CustomFont-Bold.woff2") format("woff2");
font-weight: bold;
font-style: normal;
font-display: swap;
} */

6
src/styles/global.css Normal file
View File

@@ -0,0 +1,6 @@
@import 'tailwindcss';
@import './fonts.css';
@theme {
--font-sans: 'Vazirmatn', ui-sans-serif, system-ui, sans-serif;
}

View File

@@ -1,5 +1,14 @@
{
"extends": "astro/tsconfigs/strict",
"include": [".astro/types.d.ts", "**/*"],
"exclude": ["dist"]
"exclude": ["dist"],
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/core/*": ["src/components/core/*"],
"@/layout/*": ["src/components/layout/*"],
"@/blocks/*": ["src/components/blocks/*"],
"@/meta/*": ["src/components/meta/*"]
}
}
}