Added initial Astro starter kit with font setup, linting/formatting,SEO/Sitemap TailwindCSS, Alpine.js, and Dockerize
This commit is contained in:
2
.env.example
Normal file
2
.env.example
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
CONTAINER_NAME=astro-docker-kit
|
||||||
|
SITE_URL=https://example.com
|
||||||
3
.prettierignore
Normal file
3
.prettierignore
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
.astro
|
||||||
21
.prettierrc.mjs
Normal file
21
.prettierrc.mjs
Normal 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
17
Dockerfile
Normal 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
278
README.md
@@ -1,43 +1,273 @@
|
|||||||
# Astro Starter Kit: Minimal
|
<div align="center">
|
||||||
|
|
||||||
```sh
|
# 🚀 Astro Docker Kit
|
||||||
npm create astro@latest -- --template minimal
|
|
||||||
|
</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
|
```text
|
||||||
/
|
/
|
||||||
├── public/
|
├── public/fonts/iran-yekan-x/ # IRANYekanX woff2 files
|
||||||
├── src/
|
├── src/
|
||||||
│ └── pages/
|
│ ├── components/
|
||||||
│ └── index.astro
|
│ │ ├── blocks/ # Content block components
|
||||||
└── package.json
|
│ │ ├── 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
|
## 🧞 Commands
|
||||||
|
|
||||||
All commands are run from the root of the project, from a terminal:
|
|
||||||
|
|
||||||
| Command | Action |
|
| Command | Action |
|
||||||
| :------------------------ | :----------------------------------------------- |
|
| ---------------------- | ------------------------------------ |
|
||||||
| `npm install` | Installs dependencies |
|
| `npm run dev` | Start dev server at `localhost:4321` |
|
||||||
| `npm run dev` | Starts local dev server at `localhost:4321` |
|
| `npm run build` | Build production site to `./dist/` |
|
||||||
| `npm run build` | Build your production site to `./dist/` |
|
| `npm run preview` | Preview the production build locally |
|
||||||
| `npm run preview` | Preview your build locally, before deploying |
|
| `npm run format` | Format code with Prettier |
|
||||||
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
|
| `npm run lint` | Lint with ESLint |
|
||||||
| `npm run astro -- --help` | Get help using the Astro CLI |
|
| `docker compose up -d` | Run container in background |
|
||||||
|
|
||||||
## 👀 Want to learn more?
|
---
|
||||||
|
|
||||||
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).
|
## 🤝 Contributing
|
||||||
|
|
||||||
|
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>
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
// @ts-check
|
|
||||||
import { defineConfig } from 'astro/config';
|
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
|
// 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
15
docker-compose.yml
Normal 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
16
eslint.config.mjs
Normal 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
12
nginx.conf
Normal 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
10170
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
23
package.json
23
package.json
@@ -6,9 +6,28 @@
|
|||||||
"dev": "astro dev",
|
"dev": "astro dev",
|
||||||
"build": "astro build",
|
"build": "astro build",
|
||||||
"preview": "astro preview",
|
"preview": "astro preview",
|
||||||
"astro": "astro"
|
"astro": "astro",
|
||||||
|
"format": "prettier --write .",
|
||||||
|
"lint": "eslint ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
307
src/components/meta/SEO.astro
Normal file
307
src/components/meta/SEO.astro
Normal 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)} />
|
||||||
|
))}
|
||||||
19
src/layouts/BaseLayout.astro
Normal file
19
src/layouts/BaseLayout.astro
Normal 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>
|
||||||
@@ -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">
|
<main>
|
||||||
<head>
|
<!-- Hero -->
|
||||||
<meta charset="utf-8" />
|
<section class="relative overflow-hidden px-6 py-28 text-center">
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<!-- Background glow -->
|
||||||
<meta name="viewport" content="width=device-width" />
|
<div
|
||||||
<meta name="generator" content={Astro.generator} />
|
class="pointer-events-none absolute inset-0 -z-10 flex items-center justify-center"
|
||||||
<title>Astro</title>
|
aria-hidden="true"
|
||||||
</head>
|
>
|
||||||
<body>
|
<div
|
||||||
<h1>Astro</h1>
|
class="h-[600px] w-[600px] rounded-full bg-violet-600/10 blur-[120px]"
|
||||||
</body>
|
>
|
||||||
</html>
|
</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
26
src/styles/fonts.css
Normal 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
6
src/styles/global.css
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
@import 'tailwindcss';
|
||||||
|
@import './fonts.css';
|
||||||
|
|
||||||
|
@theme {
|
||||||
|
--font-sans: 'Vazirmatn', ui-sans-serif, system-ui, sans-serif;
|
||||||
|
}
|
||||||
@@ -1,5 +1,14 @@
|
|||||||
{
|
{
|
||||||
"extends": "astro/tsconfigs/strict",
|
"extends": "astro/tsconfigs/strict",
|
||||||
"include": [".astro/types.d.ts", "**/*"],
|
"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/*"]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user