A modern, responsive, bilingual (FA/EN) web app to explore Iran’s provinces with a clickable map, search, animated dark mode, and rich province pages (intro, counties, cities, attractions). Built with vanilla HTML/CSS/JS for zero-dependency hosting—perfect for GitHub Pages.
- 🗺️ Interactive map with province deep links
- 🔍 Keyboard-first search (
/to focus,Escto clear) - 🌓 Animated dark mode with system preference + persistence
- 🌐 Full i18n: Persian ⇄ English toggle, auto RTL/LTR +
dir - 📚 Data-driven: provinces from JSON (
data/provinces/index.json) - 📱 Responsive UI (sticky header, mobile drawer)
- ♿ A11y: focus rings, ARIA labels, high-contrast friendly
- ⚡ Fast: static assets only, clean CSS transitions
- Live Demo
- Screenshots
- Project Structure
- How It Works
- Quick Start
- i18n (FA/EN)
- Dark Mode
- Data Format
- Add or Edit a Province
- Keyboard Shortcuts
- Contributing
- Roadmap
- CI / Pages
- License
- Credits
👉 https://power0matin.github.io/iran-provinces/
Designed for static hosting (GitHub Pages or any CDN).
iran-provinces/
├─ index.html # Home (map + province list)
├─ province.html # Province detail (intro, counties, cities, attractions)
├─ about.html
├─ contact.html
├─ css/
│ ├─ style.css # Theme, layout, components
│ ├─ components.css # Reusable UI (tabs, chips, cards, etc.)
│ └─ responsive.css # Breakpoints
├─ js/
│ ├─ app.js # Home page (map/list/search)
│ ├─ province.js # Province page loader/renderer
│ ├─ darkMode.js # Theme switch (animated, persisted)
│ └─ i18n.js # Live translations + lang/dir sync
├─ data/
│ └─ provinces/
│ ├─ index.json # All provinces metadata + content (SSOT)
│ └─ ... # (optional per-province JSONs if split later)
├─ images/
├─ README.md
├─ README_FA.md
└─ LICENSE
-
Home fetches
data/provinces/index.json, renders the 31 provinces list, and binds map areas toprovince.html?id=<slug>. -
Province detail reads
idfrom the query string, finds the object inindex.json, then fills:- Chips: capital, population, area
- Intro & hero image
- Counties accordion (each with cities)
- Cities & Attractions tiles
-
i18n: updates
[data-i18n]nodes and toggles<html lang>anddir(rtl/ltr). -
Dark mode: respects
prefers-color-schemeand persists vialocalStorage.
# 1) Clone
git clone https://github.com/power0matin/iran-provinces.git
cd iran-provinces
# 2) Serve locally (choose one)
npx serve .
npx http-server .
python3 -m http.server
# 3) Open in browser
# http://localhost:5000 (or whatever your tool prints)Due to
fetch()security, openingindex.htmlvia file:// may block JSON loading—use a tiny local server.
-
Use the Language toggle in the header to switch Persian ⇄ English.
-
i18n.js:- Updates all
[data-i18n]texts on the fly - Sets
html[lang="fa"|"en"]anddir="rtl"|"ltr" - Persists choice in
localStorage
- Updates all
Extend translations
Add keys to your i18n resources (inside i18n.js). Example keys:
nav.home,nav.about,nav.contacthome.provinces,home.hintprovince.intro,province.counties,province.cities,province.attractionsprovince.aboutTitle,footer.rights
- Toggle in header (animated knob + glow)
- Honors
prefers-color-scheme - Persists in
localStorage - Kept fully in sync with the UI
All content comes from data/provinces/index.json:
{
"provinces": [
{
"id": "alborz",
"nameFa": "البرز",
"nameEn": "Alborz",
"capital": "کرج",
"population": 2712400,
"areaKm2": 5122,
"intro": "Intro text...",
"hero": "images/alborz_1.jpg",
"counties": [{ "name": "کرج", "cities": ["کرج", "ماهدشت"] }],
"cities": ["کرج", "هشتگرد", "نظرآباد", "فردیس"],
"attractions": ["جاده چالوس", "پیست دیزین"]
}
]
}Required: id, nameFa, nameEn
Recommended: intro, hero, counties, cities, attractions, population, areaKm2, capital
- Edit
data/provinces/index.json. - Add/modify a province object (see schema).
- Place images in
images/and reference viahero. - Commit & push—Pages auto-publishes.
Tip: Keep id lowercase and URL-safe (- instead of spaces).
/→ focus searchEsc→ clear search
We welcome contributions!
Conventional Commits (recommended):
feat: add province Golestan data
fix(i18n): sync header label keys
chore(css): tidy responsive utilities
PR Checklist
- JSON validates & lints
- i18n keys updated for FA/EN
- Tested locally (home + province deep link)
- Screens OK in light & dark themes
Consider opening an issue first for larger features.
- Province photo galleries
- Offline cache (Service Worker)
- Charts (e.g., population over time)
- Unit tests for loaders/renderers
- CSV/JSON export
Deployed with GitHub Pages (Settings → Pages → Deploy from main, /).
Optional CI for JSON validation and link checks:
# .github/workflows/ci.yml
name: CI
on:
push: { branches: [main] }
pull_request: { branches: [main] }
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Validate JSON
run: |
sudo apt-get update && sudo apt-get install -y jq
jq . data/provinces/index.json > /dev/null
- name: Link Check (optional)
uses: lycheeverse/lychee-action@v1
with:
args: --no-progress --accept 200,429 .Released under the MIT License. See LICENSE for details.
Matin Shahabadi (متین شاهآبادی / متین شاه آبادی)
- Website: matinshahabadi.ir
- Email: me@matinshahabadi.ir
- GitHub: power0matin
- LinkedIn: matin-shahabadi
Designed & developed by @power0matin. Map image & province boundaries: public educational resources—open an issue if attribution needs adjustment.

