الموقع → dartajweed.com
دليلٌ مجّانيٌّ مفتوح المصدر لتعلُّم أحكام تجويد القرآن الكريم، بالعربيّة الفصحى، على رواية حفص عن عاصم من طريق الشاطبيّة.
مكتوبٌ لمن يقرأ العربيّة ويعرف التشكيل لكنّه لا يعرف أسماء الأحكام ولا متى تُطبَّق: أربعةٌ وثلاثون درسًا في إحدى عشرة وحدة، من الأسهل إلى الأصعب، مع أمثلةٍ مسموعةٍ من القرآن الكريم وتمارينَ بعد كلّ درس.
A free, open-source Arabic guide to Qur'anic tajweed. Thirty-four lessons in eleven units, ordered from easiest to hardest, with audio examples and quizzes.
Nobody types a Qur'anic verse into this repository. Lessons reference verses by number
(ref: 2:19) and the build fills in the text from a checked-in Uthmani corpus that is
pinned by SHA-256.
npm run build refuses to produce a site if any of these fail:
- The corpus in
data/quran-uthmani.txtno longer matches its pinned checksum. - A lesson references a verse that does not exist.
- A phrase a lesson wants to highlight is not in that verse, or occurs more than once.
- Any file contains Uthmani orthography that does not match the mushaf character for character.
- Any lesson has broken frontmatter, a duplicate order, or an unknown unit.
Full details: docs/quran-pipeline.md.
- React 19 + TypeScript (strict) on Vite
- Tailwind CSS v4, CSS-first theming, light and dark
- React Router for pages
- Lessons are plain Markdown rendered with react-markdown, plus a handful of custom blocks
- Self-hosted fonts: Cairo for the interface, Amiri Quran for Qur'anic text
- No backend, no accounts, no tracking. Progress lives in
localStorage.
npm install
npm run devOther scripts:
npm run build # verify the text, type-check, build, and pre-render routes
npm run preview # serve the production build
npm run lint # oxlint
npm run check # verify + lint + type-check, without building
npm run content:build # regenerate the verse data and the lesson index (runs automatically)
npm run quran:fetch # re-download the corpus from Tanzil and verify its checksum
npm run quran:find -- "من الصواعق" # find which verse contains a phrase
npm run og # regenerate the share card and PNG icons (needs Chrome)- Create a file in
src/content/lessons/, for examplemadd-badal.md. - Copy the frontmatter from a neighbouring lesson and fill it in.
- Write the body using the blocks documented in docs/writing-lessons.md.
- Run
npm run check. It will tell you, in Arabic, exactly what is wrong.
No code change is needed. The file name becomes the URL
(madd-badal.md → /lessons/madd-badal), and the site picks it up automatically.
data/ the Uthmani mushaf text + surah names (checked in, pinned)
scripts/
fetch-quran.mjs downloads the corpus and verifies its checksum
build-quran.mjs verifies every reference and generates the runtime data
find-ayah.mjs search helper for lesson authors
src/
content/lessons/*.md THE CONTENT. One file = one lesson.
content/pages/*.md the summary sheet and the about page
lib/units.ts the eleven units, in curriculum order
lib/rules.ts every tajweed rule: name, colour, definition, lesson
lib/glossary.ts terms that are not rule names
components/content/ the custom Markdown blocks
components/layout/ header, footer, theme toggle, back-to-top
components/CompletionCard.tsx what the reader sees on finishing the guide
components/ProgressBar.tsx how far through the curriculum they are
pages/ Home, LessonPage, Practice, Cheatsheet, Glossary, About
docs/ how the pipeline works, how to write a lesson
- Qur'anic text: Tanzil Project, Uthmani script, CC BY 3.0.
- Surah names: quran.com API.
- Recitation audio: everyayah.com, Shaykh Mahmoud Khalil al-Husary, murattal.
- Content: built on the standard references of the field, cited lesson by lesson. The main ones are قواعد التجويد على رواية حفص by عبد العزيز بن عبد الفتاح القارئ, المقدّمة الجزريّة, متن تحفة الأطفال, and هداية القاري by المرصفيّ.
Found a mistake? It is a mistake about the Book of Allah, so please report it:
- Open an issue on this repository, or
- Send a pull request.
If the correction is a point of tajweed, cite a source from the recognised books of the field so it can be checked.
To get it deployed: nothing. Fork it, turn on GitHub Pages (Settings → Pages →
Source: GitHub Actions), and push. The workflow works out your address from your
repository name, so your copy builds and deploys to
https://<your-username>.github.io/<your-repo>/ without you editing a single file.
site.config.mjs needs no changes, and no URL in the build points at the original site.
Three things still carry our details, and they are not automatic:
| What | Where | Why it matters |
|---|---|---|
| The share card image | public/og.png |
The address is printed into the picture. Until you regenerate it, every WhatsApp or Telegram preview of your site shows ours. |
| The "found a mistake?" link | src/content/pages/about.md |
Sends your readers' bug reports to our issue tracker. |
| The copyright line | LICENSE |
0BSD lets you keep it, but you probably want your own name. |
Regenerating the card needs Chrome and your own address:
SITE_URL=https://your-site.example/ npm run og
# not on macOS? point CHROME_PATH at your browser first:
CHROME_PATH=/usr/bin/google-chrome SITE_URL=https://your-site.example/ npm run ogThe footer's GitHub link needs no attention — it is derived from your repository at build time, so it already points at your fork.
You may also want to change name in package.json, and the localStorage keys in
src/hooks/ if you will host more than one copy under the same github.io account,
since browser storage is shared per origin rather than per path.
Every push to main runs .github/workflows/deploy.yml. It verifies the Qur'anic text,
builds the site, and publishes it to GitHub Pages. Two environment variables control the
addresses, and both have sensible defaults:
| Variable | What it means | Default |
|---|---|---|
SITE_URL |
Where this build is served from. Assets, links and fonts resolve against it. | https://<owner>.github.io/<repo>/ |
SITE_CANONICAL |
The address this build says it is, in canonical tags and the sitemap. | the same as SITE_URL |
They are only different when one site is published at two addresses. To build a copy locally for a specific address:
SITE_URL=https://example.com/ npm run buildThis repository publishes twice, at dartajweed.com and at edriso.github.io/learn-tajweed. The reason is worth knowing before you copy it: GitHub Pages redirects a repository's own Pages URL to its custom domain, and there is no setting to stop it. So attaching a domain does not leave the github.io address as a spare — it turns it into a sign pointing at the domain, and if the domain ever expires both addresses break at once. Publishing the same files to a second repository is the only way to keep an address that does not depend on the domain being paid for.
To set that up, add these in Settings → Secrets and variables → Actions:
| Name | Kind | Example |
|---|---|---|
MIRROR_REPO |
Variable | edriso/dartajweed |
SITE_URL |
Variable | https://dartajweed.com/ |
MIRROR_DEPLOY_KEY |
Secret | private half of an SSH deploy key with write access to MIRROR_REPO |
Leave MIRROR_REPO unset and the whole second half is skipped — the mirror job
never runs, so a fork never sees a failed deploy. The mirror repository holds no source,
only the built site, replaced on every deploy. Its build writes a CNAME file, because
branch-based Pages reads that file where actions/deploy-pages ignores it, and every
mirror push replaces everything.
Both copies claim the same canonical address, so two sites with identical text never
compete in search, and each serves its own og:image so link previews keep working even
if the other address is down.
| What | Licence |
|---|---|
| Everything written for this repository — the app, the build scripts, the styling, and the lessons | 0BSD |
The Qur'anic text in data/ |
© Tanzil Project, CC BY 3.0 — see NOTICE |
| Surah names, recitation audio, fonts | third-party — see NOTICE |
0BSD is a public-domain-equivalent licence: fork the site, print the lessons for a halaqah, translate them, put them in a book, re-host them, sell them. You owe no credit and no notice. It is sadaqah, and being asked for a footer would defeat the point.
What you cannot relicense is the material this repository only redistributes. The Tanzil terms on the Qur'anic text require Tanzil to be named and forbid modifying the text; they travel with it into your fork, so keep the attribution that is already on the «عن هذا الدليل» page. NOTICE lists all of it.
One request, and it is a request rather than a condition of the licence: if you republish the lessons, keep the places where the text names a scholarly disagreement or the riwayah a rule belongs to. Flattening those is how a guide to reciting the Qur'an starts doing harm.