generated from ooriyap/astro-docker-kit
Initial commit
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
|
||||
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# build output
|
||||
dist/
|
||||
# generated types
|
||||
.astro/
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
|
||||
# environment variables
|
||||
.env
|
||||
.env.production
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
|
||||
# jetbrains setting folder
|
||||
.idea/
|
||||
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',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
4
.vscode/extensions.json
vendored
Normal file
4
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"recommendations": ["astro-build.astro-vscode"],
|
||||
"unwantedRecommendations": []
|
||||
}
|
||||
11
.vscode/launch.json
vendored
Normal file
11
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"command": "./node_modules/.bin/astro dev",
|
||||
"name": "Development server",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
}
|
||||
]
|
||||
}
|
||||
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
|
||||
273
README.md
Normal file
273
README.md
Normal file
@@ -0,0 +1,273 @@
|
||||
<div align="center">
|
||||
|
||||
# 🚀 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
|
||||
```
|
||||
|
||||
Visit `http://localhost:4321`.
|
||||
|
||||
---
|
||||
|
||||
## 🐳 Deploy with Docker
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
docker network create private-net # required once
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📁 Project Structure
|
||||
|
||||
```text
|
||||
/
|
||||
├── public/fonts/iran-yekan-x/ # IRANYekanX woff2 files
|
||||
├── src/
|
||||
│ ├── 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
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 SEO Component
|
||||
|
||||
`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
|
||||
|
||||
| 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 |
|
||||
|
||||
---
|
||||
|
||||
## 🤝 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>
|
||||
14
astro.config.mjs
Normal file
14
astro.config.mjs
Normal file
@@ -0,0 +1,14 @@
|
||||
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({
|
||||
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
33
package.json
Normal file
33
package.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "astro-docker-kit",
|
||||
"type": "module",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro",
|
||||
"format": "prettier --write .",
|
||||
"lint": "eslint ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@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"
|
||||
}
|
||||
}
|
||||
9
public/favicon.svg
Normal file
9
public/favicon.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
|
||||
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
|
||||
<style>
|
||||
path { fill: #000; }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
path { fill: #FFF; }
|
||||
}
|
||||
</style>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 749 B |
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>
|
||||
430
src/pages/index.astro
Normal file
430
src/pages/index.astro
Normal file
@@ -0,0 +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>
|
||||
|
||||
<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
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;
|
||||
}
|
||||
14
tsconfig.json
Normal file
14
tsconfig.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
"include": [".astro/types.d.ts", "**/*"],
|
||||
"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