|
| 1 | +--- |
| 2 | +alwaysApply: true |
| 3 | +--- |
| 4 | + |
| 5 | +# saacchq.org — Project Rules |
| 6 | + |
| 7 | +## Project Overview |
| 8 | + |
| 9 | +Community website for sa/acc (saacchq.org), inspired by [CursorThailand.com](https://cursorthailand.com). Single-page site with hero, upcoming events, past events, and footer. |
| 10 | + |
| 11 | +## Stack |
| 12 | + |
| 13 | +- **Framework**: Astro 5.x (static site generation) |
| 14 | +- **Styling**: Tailwind CSS v4 via `@tailwindcss/vite` |
| 15 | +- **Language**: TypeScript (strict) |
| 16 | +- **Package manager**: pnpm |
| 17 | +- **Deployment**: GitHub Pages via GitHub Actions |
| 18 | +- **Domain**: cursorsaudi.com (CNAME in `public/`) |
| 19 | + |
| 20 | +## Key Constraints |
| 21 | + |
| 22 | +- **Bilingual**: Arabic (default, RTL) + English (LTR) with client-side toggle stored in `localStorage` |
| 23 | +- **Dark mode only**: No theme toggle. Single dark color scheme. |
| 24 | +- **Minimal scope**: Hero, upcoming events, past events, footer. No blog, search, or RSS. |
| 25 | +- **Content collections**: Events managed as Markdown files in `src/data/events/` with Zod-validated frontmatter. |
| 26 | +- **Community links**: Luma (event registration) + X/Twitter |
| 27 | + |
| 28 | +## Project Structure |
| 29 | + |
| 30 | +``` |
| 31 | +cursorsaudi.com/ |
| 32 | +├── .github/workflows/deploy.yml # GitHub Pages deployment |
| 33 | +├── astro.config.ts # site: "https://cursorsaudi.com", Tailwind v4, sitemap |
| 34 | +├── package.json |
| 35 | +├── tsconfig.json # base: astro/tsconfigs/strict, paths: @/* -> ./src/* |
| 36 | +├── public/ |
| 37 | +│ ├── .nojekyll |
| 38 | +│ ├── CNAME # cursorsaudi.com |
| 39 | +│ ├── favicon.svg |
| 40 | +│ └── toggle-lang.js # Language toggle script (runs before hydration) |
| 41 | +└── src/ |
| 42 | + ├── components/ |
| 43 | + │ ├── Header.astro # Logo + LangToggle |
| 44 | + │ ├── Hero.astro # Tagline, description, CTA |
| 45 | + │ ├── EventCard.astro # Reusable card for upcoming/past events |
| 46 | + │ ├── UpcomingEvents.astro # Future events list |
| 47 | + │ ├── PastEvents.astro # Past events with speakers, slides |
| 48 | + │ ├── Footer.astro # Social links (Luma, X), copyright |
| 49 | + │ └── LangToggle.astro # AR/EN switcher |
| 50 | + ├── config.ts # Site-wide constants (title, description, socials, ambassadors) |
| 51 | + ├── content.config.ts # Event collection schema (Zod) |
| 52 | + ├── data/ |
| 53 | + │ └── events/ # Markdown event files |
| 54 | + ├── i18n/ |
| 55 | + │ ├── ar.json # Arabic UI strings |
| 56 | + │ ├── en.json # English UI strings |
| 57 | + │ └── index.ts # getTranslation(lang, key) utility |
| 58 | + ├── layouts/ |
| 59 | + │ └── Layout.astro # Base HTML: dark mode, RTL/LTR dir, meta, OG |
| 60 | + ├── pages/ |
| 61 | + │ └── index.astro # Homepage assembling all sections |
| 62 | + ├── styles/ |
| 63 | + │ └── global.css # Tailwind imports, dark theme CSS variables |
| 64 | + └── utils/ |
| 65 | + └── events.ts # getSortedEvents, getUpcoming, getPast (computed from date) |
| 66 | +``` |
| 67 | + |
| 68 | +## Event Content Collection Schema |
| 69 | + |
| 70 | +Each Markdown file in `src/data/events/` uses this frontmatter. The `isPast` status is **computed at build time** from `date < today` — not stored in frontmatter. |
| 71 | + |
| 72 | +```yaml |
| 73 | +title: "Kickoff" # Event name (English, required) |
| 74 | +titleAr: "الانطلاقة" # Arabic name (optional, for future) |
| 75 | +date: 2025-05-28 # Event date (required) |
| 76 | +type: "meetup" # "meetup" | "hackathon" | "workshop" (required) |
| 77 | +location: "Riyadh" # City (required) |
| 78 | +locationAr: "الرياض" # Arabic city (optional) |
| 79 | +speakers: # Speaker list (optional) |
| 80 | + - "Mazen Alotaibi" |
| 81 | + - "Dan (Cursor)" |
| 82 | +slides: # Slide URLs (optional) |
| 83 | + - "https://github.com/cursor-sa/events-slides/blob/main/..." |
| 84 | +place: "" # Venue name (optional) |
| 85 | +lumaUrl: "https://lu.ma/..." # Registration link (optional) |
| 86 | +description: "Community kickoff event" # English description (required) |
| 87 | +descriptionAr: "" # Arabic description (optional) |
| 88 | +``` |
| 89 | + |
| 90 | +### Data Source |
| 91 | + |
| 92 | +Event data was imported from a Notion export (`ExportBlock-f616ed77-905b-4068-9702-1263e7bca7eb-Part-1/Cursor Community in 🇸🇦/Events 27d321ff1409804880b6de61fa1c6336.csv`). 17 events total (7 past, 10 upcoming as of Feb 2026). |
| 93 | + |
| 94 | +## i18n Approach |
| 95 | + |
| 96 | +- JSON files (`ar.json`, `en.json`) hold UI string translations (hero text, section headings, button labels, footer text). |
| 97 | +- `toggle-lang.js` in `public/` reads `localStorage` before page render to set `<html dir="rtl" lang="ar">` or `<html dir="ltr" lang="en">`, preventing layout flash. |
| 98 | +- Components use `getTranslation(lang, key)` to resolve strings. |
| 99 | +- Arabic font stack: `"Noto Kufi Arabic", "IBM Plex Arabic", system-ui` |
| 100 | +- English font stack: `Inter, system-ui, sans-serif` |
| 101 | + |
| 102 | +## Visual Design (Dark Mode Only) |
| 103 | + |
| 104 | +- **Background**: `#0d1117` (deep dark) |
| 105 | +- **Surface/Cards**: `#161b22` with `1px solid #30363d` border |
| 106 | +- **Primary text**: `#e6edf3` |
| 107 | +- **Secondary text**: `#8b949e` |
| 108 | +- **Accent**: `#58a6ff` (links, CTAs) |
| 109 | +- **Accent hover**: `#79c0ff` |
| 110 | +- RTL layout: mirrored padding, alignment, and flow direction |
| 111 | + |
| 112 | +## GitHub Actions Deployment |
| 113 | + |
| 114 | +Trigger on push to `main`. Steps: checkout → setup pnpm v9 → setup Node 20 → `pnpm install --frozen-lockfile` → `pnpm run build` → upload pages artifact → deploy pages. |
| 115 | + |
| 116 | +## Site Configuration (`src/config.ts`) |
| 117 | + |
| 118 | +Single source of truth for site metadata, social links, and ambassadors: |
| 119 | + |
| 120 | +```typescript |
| 121 | +export const SITE = { |
| 122 | + title: "Cursor Saudi", |
| 123 | + titleAr: "كيرسر السعودية", |
| 124 | + description: "Cursor Saudi Community", |
| 125 | + descriptionAr: "مجتمع كيرسر السعودية", |
| 126 | + author: "Cursor Saudi", |
| 127 | + social: { |
| 128 | + luma: "https://lu.ma/...", |
| 129 | + twitter: "https://x.com/cursorsaudi", |
| 130 | + }, |
| 131 | + ambassadors: [ |
| 132 | + { name: "Mazen Alotaibi", city: "Riyadh", cityAr: "الرياض", twitter: "https://x.com/ma7dev" }, |
| 133 | + { name: "Abdulhakeem Almidan", city: "Khobar", cityAr: "الخبر", twitter: "https://x.com/abdulhakeem_mdn" }, |
| 134 | + ], |
| 135 | +} as const; |
| 136 | +``` |
| 137 | + |
| 138 | +## Coding Conventions |
| 139 | + |
| 140 | +- All `.ts` files use type hints and JSDoc/docstrings. |
| 141 | +- Prefer stateless, pure functions in `utils/`. |
| 142 | +- Components are black-box: props in, HTML out. No side effects. |
| 143 | +- Content collection access only through utility functions in `src/utils/events.ts`. |
| 144 | +- Keep `config.ts` as the single source of truth for site metadata. |
| 145 | + |
| 146 | +## Reference Projects |
| 147 | + |
| 148 | +- **CursorThailand.com**: Design/UX inspiration (hero, events, community feel) |
| 149 | +- **ma7.dev** (`/Users/ma7dev/Projects/ma7.dev`): Astro + Tailwind v4 + GitHub Pages deployment reference |
0 commit comments