This commit is contained in:
42
.env.example
Normal file
42
.env.example
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# Astro + Tina CMS Environment Variables
|
||||||
|
|
||||||
|
# Site URL
|
||||||
|
PUBLIC_SITE_URL=https://your-domain.com
|
||||||
|
|
||||||
|
# Tina CMS (optional - for production Tina admin)
|
||||||
|
TINA_TOKEN=your-tina-token-from-tina-cloud
|
||||||
|
TINA_CLIENT_ID=your-client-id
|
||||||
|
|
||||||
|
# ConsentOS - Consent Management (moreminimore.com)
|
||||||
|
PUBLIC_CONSENT_SITE_ID=your-consent-site-id
|
||||||
|
PUBLIC_CONSENT_API_BASE=https://consent.moreminimore.com
|
||||||
|
|
||||||
|
# ===== TRACKING SCRIPTS =====
|
||||||
|
# All tracking scripts are blocked by ConsentOS until user gives consent
|
||||||
|
|
||||||
|
# --- Analytics ---
|
||||||
|
# Google Analytics 4
|
||||||
|
PUBLIC_GA4_ID=G-XXXXXXXXXX
|
||||||
|
|
||||||
|
# Google Tag Manager
|
||||||
|
PUBLIC_GTM_ID=GTM-XXXXXXX
|
||||||
|
|
||||||
|
# Umami (self-hosted analytics)
|
||||||
|
PUBLIC_UMAMI_URL=https://umami.example.com
|
||||||
|
PUBLIC_UMAMI_WEBSITE_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||||
|
|
||||||
|
# Microsoft Clarity (heatmaps)
|
||||||
|
PUBLIC_CLARITY_ID=xxxxxxxxxx
|
||||||
|
|
||||||
|
# --- Marketing / Advertising ---
|
||||||
|
# Facebook Pixel
|
||||||
|
PUBLIC_FB_PIXEL_ID=123456789
|
||||||
|
|
||||||
|
# Google Ads Conversion
|
||||||
|
PUBLIC_GOOGLE_ADS_ID=AW-123456789
|
||||||
|
|
||||||
|
# TikTok Pixel
|
||||||
|
PUBLIC_TIKTOK_PIXEL_ID=XXXXXXXX
|
||||||
|
|
||||||
|
# LINE Channel Tag (Thailand)
|
||||||
|
PUBLIC_LINE_CHANNEL_ID=1234567890
|
||||||
41
.gitea/workflows/deploy.yml
Normal file
41
.gitea/workflows/deploy.yml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
name: Deploy to Easypanel
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: npm run build
|
||||||
|
|
||||||
|
- name: Push to Easypanel
|
||||||
|
env:
|
||||||
|
EASYPANEL_HOST: ${{ secrets.EASYPANEL_HOST }}
|
||||||
|
EASYPANEL_TOKEN: ${{ secrets.EASYPANEL_TOKEN }}
|
||||||
|
EASYPANEL_PROJECT: ${{ secrets.EASYPANEL_PROJECT }}
|
||||||
|
run: |
|
||||||
|
# Install Easypanel CLI
|
||||||
|
curl -sSL https://get.easypanel.io | sh
|
||||||
|
|
||||||
|
# Deploy
|
||||||
|
easypanel deploy $EASYPANEL_PROJECT \
|
||||||
|
--host $EASYPANEL_HOST \
|
||||||
|
--token $EASYPANEL_TOKEN \
|
||||||
|
--dir ./dist
|
||||||
28
.gitignore
vendored
Normal file
28
.gitignore
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# Dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Build output
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.production
|
||||||
|
|
||||||
|
# Tina CMS
|
||||||
|
.tina/dump/**
|
||||||
|
.tina/node_modules/**
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
18
.tina/config.ts
Normal file
18
.tina/config.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { defineConfig } from 'tinacms';
|
||||||
|
import { schema } from './schema';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
schema,
|
||||||
|
ui: {
|
||||||
|
navigation: {
|
||||||
|
'content/posts': { label: 'Posts' },
|
||||||
|
'content/pages': { label: 'Pages' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
media: {
|
||||||
|
tina: {
|
||||||
|
publicFolder: 'public',
|
||||||
|
mediaRoot: 'uploads',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
91
.tina/schema.ts
Normal file
91
.tina/schema.ts
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import { defineSchema } from 'tinacms'
|
||||||
|
|
||||||
|
export const schema = defineSchema({
|
||||||
|
collections: [
|
||||||
|
{
|
||||||
|
name: 'post',
|
||||||
|
label: 'Posts',
|
||||||
|
path: 'src/content/posts',
|
||||||
|
format: 'mdx',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'title',
|
||||||
|
label: 'Title',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'description',
|
||||||
|
label: 'Description',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'datetime',
|
||||||
|
name: 'publishedAt',
|
||||||
|
label: 'Published At',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'category',
|
||||||
|
label: 'Category',
|
||||||
|
options: ['news', 'blog', 'tutorial'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'rich-text',
|
||||||
|
name: 'body',
|
||||||
|
label: 'Body',
|
||||||
|
isBody: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'page',
|
||||||
|
label: 'Pages',
|
||||||
|
path: 'src/content/pages',
|
||||||
|
format: 'mdx',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'title',
|
||||||
|
label: 'Title',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'description',
|
||||||
|
label: 'Description',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'rich-text',
|
||||||
|
name: 'body',
|
||||||
|
label: 'Body',
|
||||||
|
isBody: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'settings',
|
||||||
|
label: 'Settings',
|
||||||
|
path: 'src/content/settings',
|
||||||
|
format: 'json',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'siteName',
|
||||||
|
label: 'Site Name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'siteDescription',
|
||||||
|
label: 'Site Description',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'language',
|
||||||
|
label: 'Language',
|
||||||
|
options: ['th', 'en', 'th-en'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
207
AGENTS.md
Normal file
207
AGENTS.md
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
# Astro Tina Starter - Agent Knowledge Base
|
||||||
|
|
||||||
|
**Generated:** 2026-04-17
|
||||||
|
**Version:** 2.0.0
|
||||||
|
**Type:** Astro 6 + Tina CMS Starter Template
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## OVERVIEW
|
||||||
|
|
||||||
|
Starter template for building websites with Astro 6, Tina CMS, and Tailwind CSS 4.x.
|
||||||
|
|
||||||
|
### Tech Stack
|
||||||
|
|
||||||
|
| Component | Technology | Version |
|
||||||
|
|-----------|------------|---------|
|
||||||
|
| Framework | Astro | 6.1.7 |
|
||||||
|
| CMS | Tina CMS | 2.x |
|
||||||
|
| Styling | Tailwind CSS | 4.x |
|
||||||
|
|
||||||
|
### Key Features
|
||||||
|
|
||||||
|
- Self-hosted Tina CMS with schema-based content
|
||||||
|
- Tailwind CSS 4.x using `@tailwindcss/vite` plugin
|
||||||
|
- External consent system integration
|
||||||
|
- Thai language support with Noto Sans Thai
|
||||||
|
- Docker-ready with nginx
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PROJECT STRUCTURE
|
||||||
|
|
||||||
|
```
|
||||||
|
astro-tina-starter/
|
||||||
|
├── .tina/
|
||||||
|
│ ├── config.ts # Tina CMS configuration
|
||||||
|
│ └── schema.ts # Content schema definitions
|
||||||
|
├── src/
|
||||||
|
│ ├── styles/
|
||||||
|
│ │ └── global.css # Tailwind v4 styles + @theme
|
||||||
|
│ ├── layouts/
|
||||||
|
│ │ └── Layout.astro
|
||||||
|
│ ├── pages/
|
||||||
|
│ │ └── index.astro
|
||||||
|
│ ├── components/
|
||||||
|
│ │ ├── Header.astro
|
||||||
|
│ │ └── TrackingScripts.astro # Tracking scripts (GA4, FB Pixel, etc.)
|
||||||
|
│ └── content/
|
||||||
|
│ ├── config.ts # Astro content collections
|
||||||
|
│ ├── posts/ # Blog posts (MDX)
|
||||||
|
│ ├── pages/ # Static pages (MDX)
|
||||||
|
│ └── settings/ # Site settings (JSON)
|
||||||
|
├── public/
|
||||||
|
│ └── favicon.svg
|
||||||
|
├── Dockerfile
|
||||||
|
├── nginx.conf
|
||||||
|
├── astro.config.mjs
|
||||||
|
├── tsconfig.json
|
||||||
|
└── package.json
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## IMPORTANT CONVENTIONS
|
||||||
|
|
||||||
|
### Tailwind CSS 4.x Setup
|
||||||
|
|
||||||
|
**CRITICAL:** This template uses `@tailwindcss/vite` plugin, NOT `@astrojs/tailwind`.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// astro.config.mjs
|
||||||
|
import tailwindcss from '@tailwindcss/vite'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
vite: {
|
||||||
|
plugins: [tailwindcss()],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* src/styles/global.css */
|
||||||
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
@theme {
|
||||||
|
--color-primary: #1a1a1a;
|
||||||
|
--color-accent: #3b82f6;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tina CMS Content
|
||||||
|
|
||||||
|
Tina CMS manages content in `src/content/`:
|
||||||
|
- `posts/` - Blog posts (MDX format)
|
||||||
|
- `pages/` - Static pages (MDX format)
|
||||||
|
- `settings/` - Site settings (JSON format)
|
||||||
|
|
||||||
|
Schema defined in `.tina/schema.ts`.
|
||||||
|
|
||||||
|
### ConsentOS + Tracking System
|
||||||
|
|
||||||
|
ConsentOS (`consent.moreminimore.com`) manages consent and blocking:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# ConsentOS
|
||||||
|
PUBLIC_CONSENT_SITE_ID=your-site-id
|
||||||
|
PUBLIC_CONSENT_API_BASE=https://consent.moreminimore.com
|
||||||
|
|
||||||
|
# Analytics
|
||||||
|
PUBLIC_GA4_ID=G-XXXXXXXXXX
|
||||||
|
PUBLIC_GTM_ID=GTM-XXXXXXX
|
||||||
|
PUBLIC_UMAMI_URL=https://umami.example.com
|
||||||
|
PUBLIC_UMAMI_WEBSITE_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||||
|
PUBLIC_CLARITY_ID=xxxxxxxxxx
|
||||||
|
|
||||||
|
# Marketing
|
||||||
|
PUBLIC_FB_PIXEL_ID=123456789
|
||||||
|
PUBLIC_GOOGLE_ADS_ID=AW-123456789
|
||||||
|
PUBLIC_TIKTOK_PIXEL_ID=XXXXXXXX
|
||||||
|
PUBLIC_LINE_CHANNEL_ID=1234567890
|
||||||
|
```
|
||||||
|
|
||||||
|
**Tracking Scripts:**
|
||||||
|
- `TrackingScripts.astro` - contains all tracking scripts
|
||||||
|
- Scripts are auto-blocked by ConsentOS until user consent
|
||||||
|
- Categories: `analytics`, `marketing`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## CREDENTIALS
|
||||||
|
|
||||||
|
No external API credentials required for this template.
|
||||||
|
|
||||||
|
### Optional Environment Variables
|
||||||
|
|
||||||
|
| Variable | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `TINA_TOKEN` | Tina CMS production authentication |
|
||||||
|
| `TINA_CLIENT_ID` | Tina CMS client ID |
|
||||||
|
| `PUBLIC_CONSENT_SITE_ID` | ConsentOS site ID |
|
||||||
|
| `PUBLIC_CONSENT_API_BASE` | ConsentOS API base URL |
|
||||||
|
| `PUBLIC_GA4_ID` | Google Analytics 4 |
|
||||||
|
| `PUBLIC_GTM_ID` | Google Tag Manager |
|
||||||
|
| `PUBLIC_UMAMI_URL` | Umami analytics URL |
|
||||||
|
| `PUBLIC_UMAMI_WEBSITE_ID` | Umami website ID |
|
||||||
|
| `PUBLIC_CLARITY_ID` | Microsoft Clarity |
|
||||||
|
| `PUBLIC_FB_PIXEL_ID` | Facebook Pixel |
|
||||||
|
| `PUBLIC_GOOGLE_ADS_ID` | Google Ads conversion ID |
|
||||||
|
| `PUBLIC_TIKTOK_PIXEL_ID` | TikTok Pixel |
|
||||||
|
| `PUBLIC_LINE_CHANNEL_ID` | LINE Channel Tag |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## COMMANDS
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install dependencies
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# Development
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# Build
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# Preview
|
||||||
|
npm run preview
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ANTI-PATTERNS
|
||||||
|
|
||||||
|
- **NEVER** use `@astrojs/tailwind` (deprecated)
|
||||||
|
- **ALWAYS** use `@tailwindcss/vite` for Tailwind v4
|
||||||
|
- **NEVER** commit environment files (.env)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DEPLOYMENT
|
||||||
|
|
||||||
|
### Docker
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -t astro-tina-starter .
|
||||||
|
docker run -p 8080:80 astro-tina-starter
|
||||||
|
```
|
||||||
|
|
||||||
|
### Easypanel
|
||||||
|
|
||||||
|
Static hosting - no persistent volume needed.
|
||||||
|
|
||||||
|
### Manual
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
# Serve dist/ folder with any static server
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## NOTES
|
||||||
|
|
||||||
|
- Tina CMS admin: http://localhost:4321/admin
|
||||||
|
- Astro default port: 4321
|
||||||
|
- Tina dev server: 3001
|
||||||
24
Dockerfile
Normal file
24
Dockerfile
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Build stage
|
||||||
|
FROM node:20-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm install
|
||||||
|
COPY . .
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Static files stage
|
||||||
|
FROM nginx:alpine AS runner
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy static files from builder
|
||||||
|
COPY --from=builder /app/dist ./dist
|
||||||
|
|
||||||
|
# Copy nginx config
|
||||||
|
COPY nginx.conf /etc/nginx/http.d/default.conf
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
121
README.md
Normal file
121
README.md
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
# Astro Tina Starter
|
||||||
|
|
||||||
|
Astro 6.1.7 + Tina CMS starter template with Tailwind CSS 4.x
|
||||||
|
|
||||||
|
## Tech Stack
|
||||||
|
|
||||||
|
- **Framework:** Astro 6.1.7 (static mode)
|
||||||
|
- **CMS:** Tina CMS (self-hosted)
|
||||||
|
- **Styling:** Tailwind CSS 4.x with `@tailwindcss/vite`
|
||||||
|
- **Language:** TypeScript
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Self-hosted Tina CMS with schema-based content
|
||||||
|
- Tailwind CSS 4.x using `@tailwindcss/vite` plugin
|
||||||
|
- ConsentOS + Tracking Scripts (GA4, Facebook Pixel, etc.)
|
||||||
|
- Docker-ready with nginx
|
||||||
|
- Thai language support foundation
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install dependencies
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# Start development (accessible at http://0.0.0.0:4321)
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# Build for production
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tina CMS Access
|
||||||
|
|
||||||
|
During development, access Tina CMS at:
|
||||||
|
- http://localhost:4321/admin
|
||||||
|
|
||||||
|
For production, you'll need a TINA_TOKEN environment variable.
|
||||||
|
|
||||||
|
## ConsentOS + Tracking
|
||||||
|
|
||||||
|
This template includes ConsentOS (`consent.moreminimore.com`) for PDPA-compliant consent management and auto-blocking of tracking scripts.
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# ConsentOS
|
||||||
|
PUBLIC_CONSENT_SITE_ID=your-site-id
|
||||||
|
PUBLIC_CONSENT_API_BASE=https://consent.moreminimore.com
|
||||||
|
|
||||||
|
# Analytics
|
||||||
|
PUBLIC_GA4_ID=G-XXXXXXXXXX
|
||||||
|
PUBLIC_GTM_ID=GTM-XXXXXXX
|
||||||
|
PUBLIC_UMAMI_URL=https://umami.example.com
|
||||||
|
PUBLIC_UMAMI_WEBSITE_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||||
|
PUBLIC_CLARITY_ID=xxxxxxxxxx
|
||||||
|
|
||||||
|
# Marketing
|
||||||
|
PUBLIC_FB_PIXEL_ID=123456789
|
||||||
|
PUBLIC_GOOGLE_ADS_ID=AW-123456789
|
||||||
|
PUBLIC_TIKTOK_PIXEL_ID=XXXXXXXX
|
||||||
|
PUBLIC_LINE_CHANNEL_ID=1234567890
|
||||||
|
```
|
||||||
|
|
||||||
|
### How It Works
|
||||||
|
|
||||||
|
1. `TrackingScripts.astro` contains all tracking codes with `data-consent-category` attributes
|
||||||
|
2. ConsentOS `consent-loader.js` scans and auto-blocks scripts until user consent
|
||||||
|
3. Categories: `analytics`, `marketing`
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
astro-tina-starter/
|
||||||
|
├── .tina/
|
||||||
|
│ ├── config.ts # Tina CMS configuration
|
||||||
|
│ └── schema.ts # Content schema definitions
|
||||||
|
├── src/
|
||||||
|
│ ├── styles/
|
||||||
|
│ │ └── global.css # Tailwind v4 styles
|
||||||
|
│ ├── layouts/
|
||||||
|
│ │ └── Layout.astro
|
||||||
|
│ ├── pages/
|
||||||
|
│ │ └── index.astro
|
||||||
|
│ ├── components/
|
||||||
|
│ │ ├── Header.astro
|
||||||
|
│ │ └── TrackingScripts.astro # Tracking scripts
|
||||||
|
│ └── content/
|
||||||
|
│ └── config.ts # Tina content collections
|
||||||
|
├── Dockerfile
|
||||||
|
├── nginx.conf
|
||||||
|
└── package.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tailwind CSS 4.x
|
||||||
|
|
||||||
|
This template uses Tailwind CSS 4.x with the `@tailwindcss/vite` plugin.
|
||||||
|
The configuration is done via CSS `@theme` block in `src/styles/global.css`.
|
||||||
|
|
||||||
|
```css
|
||||||
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
@theme {
|
||||||
|
--color-primary: #1a1a1a;
|
||||||
|
--color-accent: #3b82f6;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Docker
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build
|
||||||
|
docker build -t astro-tina .
|
||||||
|
|
||||||
|
# Run
|
||||||
|
docker run -p 8080:80 astro-tina
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
39
astro.config.mjs
Normal file
39
astro.config.mjs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { defineConfig } from 'astro/config'
|
||||||
|
import tailwindcss from '@tailwindcss/vite'
|
||||||
|
import tina from 'tinacms'
|
||||||
|
import { fileURLToPath } from 'url'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
site: 'https://example.com',
|
||||||
|
integrations: [
|
||||||
|
tina({
|
||||||
|
enabled: !!process.env.TINA_TOKEN,
|
||||||
|
sidebar: {
|
||||||
|
partials: [],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
vite: {
|
||||||
|
plugins: [tailwindcss()],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': path.resolve(__dirname, './src'),
|
||||||
|
'@components': path.resolve(__dirname, './src/components'),
|
||||||
|
'@layouts': path.resolve(__dirname, './src/layouts'),
|
||||||
|
'@styles': path.resolve(__dirname, './src/styles'),
|
||||||
|
'@content': path.resolve(__dirname, './src/content'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
output: 'static',
|
||||||
|
build: {
|
||||||
|
assets: '_assets',
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
host: '0.0.0.0',
|
||||||
|
port: 4321,
|
||||||
|
},
|
||||||
|
})
|
||||||
14
nginx.conf
Normal file
14
nginx.conf
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
root /app/dist;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ $uri.html =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
}
|
||||||
|
}
|
||||||
34
package.json
Normal file
34
package.json
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"name": "astro-tina-starter",
|
||||||
|
"type": "module",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"description": "Astro 6 + Tina CMS starter template with Tailwind CSS 4.x",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "tinacms dev --port 3001 & astro dev",
|
||||||
|
"dev:astro": "astro dev",
|
||||||
|
"dev:tina": "tinacms dev --port 3001",
|
||||||
|
"build": "tinacms build && astro build",
|
||||||
|
"preview": "astro preview",
|
||||||
|
"astro": "astro"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@astrojs/check": "^0.9.4",
|
||||||
|
"@astrojs/mdx": "^4.0.0",
|
||||||
|
"@tailwindcss/typography": "^0.5.15",
|
||||||
|
"@tailwindcss/vite": "^4.0.0",
|
||||||
|
"astro": "^6.1.7",
|
||||||
|
"react": "^18.3.1",
|
||||||
|
"react-dom": "^18.3.1",
|
||||||
|
"tailwindcss": "^4.0.0",
|
||||||
|
"tina": "^2.1.4",
|
||||||
|
"tinacms": "^2.2.4",
|
||||||
|
"typescript": "^5.6.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.3.12",
|
||||||
|
"@types/react-dom": "^18.3.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
10
public/favicon.svg
Normal file
10
public/favicon.svg
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||||
|
<stop offset="0%" style="stop-color:#3b82f6"/>
|
||||||
|
<stop offset="100%" style="stop-color:#1d4ed8"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<rect width="32" height="32" rx="6" fill="url(#grad)"/>
|
||||||
|
<text x="16" y="22" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="white" text-anchor="middle">A</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 473 B |
27
src/components/Header.astro
Normal file
27
src/components/Header.astro
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
interface Props {
|
||||||
|
siteName?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const { siteName = "Astro Tina Starter" } = Astro.props
|
||||||
|
---
|
||||||
|
|
||||||
|
<header class="sticky top-0 z-50 bg-white/80 backdrop-blur-md border-b border-primary-200">
|
||||||
|
<nav class="max-w-6xl mx-auto px-6 h-16 flex items-center justify-between">
|
||||||
|
<a href="/" class="font-bold text-xl text-primary-900 hover:text-accent-600 transition-colors">
|
||||||
|
{siteName}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="flex items-center gap-6">
|
||||||
|
<a href="/" class="text-primary-600 hover:text-primary-900 transition-colors">
|
||||||
|
Home
|
||||||
|
</a>
|
||||||
|
<a href="/blog" class="text-primary-600 hover:text-primary-900 transition-colors">
|
||||||
|
Blog
|
||||||
|
</a>
|
||||||
|
<a href="/about" class="text-primary-600 hover:text-primary-900 transition-colors">
|
||||||
|
About
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
167
src/components/TrackingScripts.astro
Normal file
167
src/components/TrackingScripts.astro
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
---
|
||||||
|
const ga4Id = import.meta.env.PUBLIC_GA4_ID
|
||||||
|
const gtmId = import.meta.env.PUBLIC_GTM_ID
|
||||||
|
const umamiUrl = import.meta.env.PUBLIC_UMAMI_URL
|
||||||
|
const umamiWebsiteId = import.meta.env.PUBLIC_UMAMI_WEBSITE_ID
|
||||||
|
const clarityId = import.meta.env.PUBLIC_CLARITY_ID
|
||||||
|
const fbPixelId = import.meta.env.PUBLIC_FB_PIXEL_ID
|
||||||
|
const googleAdsId = import.meta.env.PUBLIC_GOOGLE_ADS_ID
|
||||||
|
const tiktokPixelId = import.meta.env.PUBLIC_TIKTOK_PIXEL_ID
|
||||||
|
const lineChannelId = import.meta.env.PUBLIC_LINE_CHANNEL_ID
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- Google Analytics 4 -->
|
||||||
|
{ga4Id && (
|
||||||
|
<script
|
||||||
|
data-consent-category="analytics"
|
||||||
|
async
|
||||||
|
src={`https://www.googletagmanager.com/gtag/js?id=${ga4Id}`}
|
||||||
|
></script>
|
||||||
|
)}
|
||||||
|
{ga4Id && (
|
||||||
|
<script
|
||||||
|
data-consent-category="analytics"
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag(){dataLayer.push(arguments);}
|
||||||
|
gtag('js', new Date());
|
||||||
|
gtag('config', '${ga4Id}');
|
||||||
|
`
|
||||||
|
}}
|
||||||
|
></script>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<!-- Google Tag Manager -->
|
||||||
|
{gtmId && (
|
||||||
|
<script
|
||||||
|
data-consent-category="analytics"
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
||||||
|
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
||||||
|
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
||||||
|
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
||||||
|
})(window,document,'script','dataLayer','${gtmId}');
|
||||||
|
`
|
||||||
|
}}
|
||||||
|
></script>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<!-- Umami Analytics -->
|
||||||
|
{umamiUrl && umamiWebsiteId && (
|
||||||
|
<script
|
||||||
|
data-consent-category="analytics"
|
||||||
|
async
|
||||||
|
src={`${umamiUrl}/script.js`}
|
||||||
|
data-website-id={umamiWebsiteId}
|
||||||
|
></script>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<!-- Microsoft Clarity -->
|
||||||
|
{clarityId && (
|
||||||
|
<script
|
||||||
|
data-consent-category="analytics"
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
(function(c,l,a,r,i,t,y){
|
||||||
|
a[q]=a[q]||function(){(a[q].q=a[q].q||[]).push(arguments)};
|
||||||
|
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
|
||||||
|
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
|
||||||
|
})(window, document, "clarity", "script", "${clarityId}");
|
||||||
|
`
|
||||||
|
}}
|
||||||
|
></script>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<!-- Facebook Pixel -->
|
||||||
|
{fbPixelId && (
|
||||||
|
<script
|
||||||
|
data-consent-category="marketing"
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
!function(f,b,e,v,n,t,s)
|
||||||
|
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
|
||||||
|
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
|
||||||
|
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
|
||||||
|
n.queue=[];t=b.createElement(e);t.async=!0;
|
||||||
|
t.src=v;s=b.getElementsByTagName(e)[0];
|
||||||
|
s.parentNode.insertBefore(t,s)}(window, document,'script',
|
||||||
|
'https://connect.facebook.net/en_US/fbevents.js');
|
||||||
|
fbq('init', '${fbPixelId}');
|
||||||
|
fbq('track', 'PageView');
|
||||||
|
`
|
||||||
|
}}
|
||||||
|
></script>
|
||||||
|
)}
|
||||||
|
{fbPixelId && (
|
||||||
|
<noscript data-consent-category="marketing">
|
||||||
|
<img
|
||||||
|
height="1"
|
||||||
|
width="1"
|
||||||
|
style="display:none"
|
||||||
|
src={`https://www.facebook.com/tr?id=${fbPixelId}&ev=PageView&noscript=1`}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
</noscript>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<!-- Google Ads Conversion -->
|
||||||
|
{googleAdsId && (
|
||||||
|
<script
|
||||||
|
data-consent-category="marketing"
|
||||||
|
async
|
||||||
|
src={`https://www.googletagmanager.com/gtag/js?id=${googleAdsId}`}
|
||||||
|
></script>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<!-- TikTok Pixel -->
|
||||||
|
{tiktokPixelId && (
|
||||||
|
<script
|
||||||
|
data-consent-category="marketing"
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
!function (w, d, t) {
|
||||||
|
w.TiktokAnalyticsObject = t;
|
||||||
|
var ttq = w[t] = w[t] || [];
|
||||||
|
ttq.methods = ["page", "track", "identify", "instances", "debug", "on", "off", "once", "ready", "alias", "group", "enableCookie", "disableCookie"];
|
||||||
|
ttq.setAndDefer = function (t, e) { t[e] = function () { t.push([e].concat(Array.prototype.slice.call(arguments, 0))) } };
|
||||||
|
for (var i = 0; i < ttq.methods.length; i++) ttq.setAndDefer(ttq, ttq.methods[i]);
|
||||||
|
ttq.instance = function (t) {
|
||||||
|
var e = t.slice(0);
|
||||||
|
return ttq.push([e]), ttq
|
||||||
|
};
|
||||||
|
for (var i = 0; i < ttq.methods.length; i++) {
|
||||||
|
var e = ttq.methods[i];
|
||||||
|
ttq[e] = ttq.instance.bind(ttq, e)
|
||||||
|
}
|
||||||
|
ttq.load = function (t, e) {
|
||||||
|
var n = "https://analytics.tiktok.com/i18n/pixel/events.js";
|
||||||
|
n = n + "?sdkid=" + t + "&lib=" + e;
|
||||||
|
var i = d.createElement("script");
|
||||||
|
i.type = "text/javascript";
|
||||||
|
i.src = n;
|
||||||
|
d.getElementsByTagName("head")[0].appendChild(i)
|
||||||
|
};
|
||||||
|
ttq.load("${tiktokPixelId}", "exc");
|
||||||
|
ttq.page()
|
||||||
|
}(window, document, 'ttq');
|
||||||
|
`
|
||||||
|
}}
|
||||||
|
></script>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<!-- LINE Channel Tag -->
|
||||||
|
{lineChannelId && (
|
||||||
|
<script
|
||||||
|
data-consent-category="marketing"
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag(){dataLayer.push(arguments);}
|
||||||
|
gtag('js', new Date());
|
||||||
|
gtag('config', '${lineChannelId}');
|
||||||
|
`
|
||||||
|
}}
|
||||||
|
></script>
|
||||||
|
)}
|
||||||
34
src/content/config.ts
Normal file
34
src/content/config.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { defineCollection, z } from "astro:content"
|
||||||
|
|
||||||
|
const postCollection = defineCollection({
|
||||||
|
type: "content",
|
||||||
|
schema: z.object({
|
||||||
|
title: z.string(),
|
||||||
|
description: z.string().optional(),
|
||||||
|
publishedAt: z.date().optional(),
|
||||||
|
category: z.enum(["news", "blog", "tutorial"]).optional(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
const pageCollection = defineCollection({
|
||||||
|
type: "content",
|
||||||
|
schema: z.object({
|
||||||
|
title: z.string(),
|
||||||
|
description: z.string().optional(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
const settingsCollection = defineCollection({
|
||||||
|
type: "data",
|
||||||
|
schema: z.object({
|
||||||
|
siteName: z.string(),
|
||||||
|
siteDescription: z.string(),
|
||||||
|
language: z.enum(["th", "en", "th-en"]).default("th"),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
export const collections = {
|
||||||
|
posts: postCollection,
|
||||||
|
pages: pageCollection,
|
||||||
|
settings: settingsCollection,
|
||||||
|
}
|
||||||
17
src/content/posts/welcome.mdx
Normal file
17
src/content/posts/welcome.mdx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
title: Welcome to Astro Tina Starter
|
||||||
|
description: A modern starter template with Astro 6, Tina CMS, and Thai language support.
|
||||||
|
publishedAt: 2026-04-17
|
||||||
|
category: blog
|
||||||
|
---
|
||||||
|
|
||||||
|
Welcome to our new blog built with Astro and Tina CMS!
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Tina CMS** - Self-hosted content management
|
||||||
|
- **Tailwind CSS v4** - Latest styling with @tailwindcss/vite
|
||||||
|
- **ConsentOS** - PDPA-compliant consent management
|
||||||
|
- **Thai Support** - Ready for Thai language content
|
||||||
|
|
||||||
|
Stay tuned for more updates!
|
||||||
5
src/content/settings/site.json
Normal file
5
src/content/settings/site.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"siteName": "Astro Tina Starter",
|
||||||
|
"siteDescription": "Astro 6 + Tina CMS starter template with Thai language support",
|
||||||
|
"language": "th"
|
||||||
|
}
|
||||||
41
src/layouts/Layout.astro
Normal file
41
src/layouts/Layout.astro
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
import "@/styles/global.css"
|
||||||
|
import TrackingScripts from "@/components/TrackingScripts.astro"
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
title?: string
|
||||||
|
description?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
title = "Astro Tina Starter",
|
||||||
|
description = "Astro 6 + Tina CMS starter template",
|
||||||
|
} = Astro.props
|
||||||
|
|
||||||
|
const consentSiteId = import.meta.env.PUBLIC_CONSENT_SITE_ID || 'demo'
|
||||||
|
const consentApiBase = import.meta.env.PUBLIC_CONSENT_API_BASE || 'https://consent.moreminimore.com'
|
||||||
|
---
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="th">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta name="description" content={description} />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
|
<title>{title}</title>
|
||||||
|
</head>
|
||||||
|
<body class="bg-primary-50 text-primary-900 min-h-screen">
|
||||||
|
<slot />
|
||||||
|
|
||||||
|
<!-- Tracking Scripts (ConsentOS will auto-block until consent) -->
|
||||||
|
<TrackingScripts />
|
||||||
|
|
||||||
|
<!-- ConsentOS - Consent Management -->
|
||||||
|
<script
|
||||||
|
src={`${consentApiBase}/consent-loader.js`}
|
||||||
|
data-site-id={consentSiteId}
|
||||||
|
data-api-base={consentApiBase}
|
||||||
|
></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
47
src/pages/index.astro
Normal file
47
src/pages/index.astro
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
---
|
||||||
|
import Layout from "@/layouts/Layout.astro"
|
||||||
|
---
|
||||||
|
|
||||||
|
<Layout>
|
||||||
|
<main>
|
||||||
|
<section class="px-6 py-24 max-w-4xl mx-auto">
|
||||||
|
<h1 class="text-4xl md:text-5xl font-bold tracking-tight mb-6">
|
||||||
|
Welcome to Astro Tina Starter
|
||||||
|
</h1>
|
||||||
|
<p class="text-lg text-primary-600 mb-8 max-w-2xl">
|
||||||
|
A modern starter template with Astro 6, Tina CMS, Tailwind CSS 4.x,
|
||||||
|
and Thai language support.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="grid gap-6 md:grid-cols-2">
|
||||||
|
<div class="p-6 bg-white rounded-xl border border-primary-200">
|
||||||
|
<h2 class="text-xl font-semibold mb-3">Tina CMS</h2>
|
||||||
|
<p class="text-primary-600">
|
||||||
|
Self-hosted content management with schema-based editing.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="p-6 bg-white rounded-xl border border-primary-200">
|
||||||
|
<h2 class="text-xl font-semibold mb-3">Tailwind v4</h2>
|
||||||
|
<p class="text-primary-600">
|
||||||
|
Latest Tailwind CSS with @tailwindcss/vite plugin.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="p-6 bg-white rounded-xl border border-primary-200">
|
||||||
|
<h2 class="text-xl font-semibold mb-3">ConsentOS</h2>
|
||||||
|
<p class="text-primary-600">
|
||||||
|
PDPA-compliant consent management with auto-blocking tracking.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="p-6 bg-white rounded-xl border border-primary-200">
|
||||||
|
<h2 class="text-xl font-semibold mb-3">Thai Support</h2>
|
||||||
|
<p class="text-primary-600">
|
||||||
|
Ready for Thai language content with Noto Sans Thai.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</Layout>
|
||||||
57
src/styles/global.css
Normal file
57
src/styles/global.css
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
@import "tailwindcss";
|
||||||
|
@plugin "@tailwindcss/typography";
|
||||||
|
|
||||||
|
@theme {
|
||||||
|
--font-sans: "Inter", "Noto Sans Thai", system-ui, sans-serif;
|
||||||
|
--font-serif: "Merriweather", Georgia, serif;
|
||||||
|
|
||||||
|
--color-primary-50: #f8fafc;
|
||||||
|
--color-primary-100: #f1f5f9;
|
||||||
|
--color-primary-200: #e2e8f0;
|
||||||
|
--color-primary-300: #cbd5e1;
|
||||||
|
--color-primary-400: #94a3b8;
|
||||||
|
--color-primary-500: #64748b;
|
||||||
|
--color-primary-600: #475569;
|
||||||
|
--color-primary-700: #334155;
|
||||||
|
--color-primary-800: #1e293b;
|
||||||
|
--color-primary-900: #0f172a;
|
||||||
|
--color-primary-950: #020617;
|
||||||
|
|
||||||
|
--color-accent-50: #eff6ff;
|
||||||
|
--color-accent-100: #dbeafe;
|
||||||
|
--color-accent-200: #bfdbfe;
|
||||||
|
--color-accent-300: #93c5fd;
|
||||||
|
--color-accent-400: #60a5fa;
|
||||||
|
--color-accent-500: #3b82f6;
|
||||||
|
--color-accent-600: #2563eb;
|
||||||
|
--color-accent-700: #1d4ed8;
|
||||||
|
--color-accent-800: #1e40af;
|
||||||
|
--color-accent-900: #1e3a8a;
|
||||||
|
|
||||||
|
--color-success-500: #22c55e;
|
||||||
|
--color-warning-500: #f59e0b;
|
||||||
|
--color-error-500: #ef4444;
|
||||||
|
|
||||||
|
--radius-sm: 0.25rem;
|
||||||
|
--radius-md: 0.5rem;
|
||||||
|
--radius-lg: 0.75rem;
|
||||||
|
--radius-xl: 1rem;
|
||||||
|
--radius-2xl: 1.5rem;
|
||||||
|
--radius-full: 9999px;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
line-height: 1.6;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
background-color: var(--color-accent-200);
|
||||||
|
color: var(--color-primary-900);
|
||||||
|
}
|
||||||
17
tsconfig.json
Normal file
17
tsconfig.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"extends": "astro/tsconfigs/strict",
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"],
|
||||||
|
"@components/*": ["./src/components/*"],
|
||||||
|
"@layouts/*": ["./src/layouts/*"],
|
||||||
|
"@styles/*": ["./src/styles/*"],
|
||||||
|
"@content/*": ["./src/content/*"]
|
||||||
|
},
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"jsxImportSource": "react"
|
||||||
|
},
|
||||||
|
"include": ["src/**/*", ".tina/**/*", "db/**/*"],
|
||||||
|
"exclude": ["node_modules", "dist", ".astro"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user