⬡ Learn Xahau
A free, open-source basic course on Xahau, in eight languages — from your first wallet to your first Hook.
https://learn.xahau.network/xahau-course.
Features
- 📖 Theory — Formatted content with markdown support
- 💻 Code Blocks — Copyable code examples with syntax highlighting (C, JavaScript, Bash)
- 📊 Presentation Mode — Fullscreen slides with keyboard navigation
- 🌐 Multilingual — English, Spanish, French, Portuguese, Japanese, Korean, Simplified Chinese and Arabic (RTL)
- 🔎 Search — Full-text across every lesson title and body (⌘K / Ctrl-K)
- ✅ Module checks — Self-assessment at the end of every module, in all eight languages
- 📈 Progress Tracking — Marked lessons persist in the browser
- 🔌 Modular — Easy to add new modules and lessons
Design
The interface follows the xahau.network design system, so the course reads as part of the Xahau family rather than a separate product:
- Canvas
#f7f7f7, white editorial cards at24pxradius with a soft lifted shadow - Type Onest for everything,
ui-monospacefor micro-labels, chips and counters - Accent Xahau green (
#007a28on light,#5de48con dark) with a teal secondary - Modules cycle through four brand tints — green, teal, sand, sage
- Buttons
6pxradius, never pills
Shared primitives (.x-card, .x-btn, .x-chip, .x-act-label) and the token set live in
src/styles/index.css; the wordmark lockup, icon set and module themes live in
src/components/Brand.jsx. Light is the default; a dark counterpart built on the brand's
#0f2328 ink is available from the header toggle.
Deployment
site.config.js holds SITE_URL, by default
https://learn.xahau.network/xahau-course. Everything that depends on the
host is derived from it at build time, and nothing else hardcodes it:
| Derived | Where |
|---|---|
| Canonical link, Open Graph and Twitter image URLs | index.html, via vite.config.js |
Vite's base (the sub-path assets are served from) |
vite.config.js |
sitemap.xml and robots.txt |
scripts/build-course-data.mjs |
public/CNAME |
scripts/build-course-data.mjs |
Moving the site
- For one build, set the variable:
SITE_URL=https://example.org npm run build. - For every GitHub Pages deploy, set a repository variable
SITE_URL(Settings → Secrets and variables → Actions → Variables). The deploy workflow passes it to the build; left unset, the default applies. - For good, change the default in
site.config.js.
public/CNAME is generated, never committed. It is written only when the
site sits at the root of a domain of its own (for example
https://learnxahau.inftf.org), and removed otherwise: on a sub-path the
domain belongs to the Pages site that serves its root, and a *.github.io URL
needs none. On a sub-path, robots.txt is only honoured if the host's root
serves it, so list this course's sitemap.xml there.
.github/workflows/deploy.yml is manual-trigger only until someone confirms
GitHub Pages is the right target.
Tech Stack
- React 18 + Vite
- Tailwind CSS
- No backend required — all content is static JS modules
How the course data loads
The twelve modules total roughly 3 MB. Importing them all up front meant every visitor downloaded the entire curriculum before the index page painted, so the data is split in three:
| Artefact | Generated | Loaded |
|---|---|---|
src/data/generated/manifest.js |
at build | bundled — module and lesson titles, so the overview renders instantly |
src/data/modules/*.js |
hand-written | one dynamic import() per module, on open (next module prefetched) |
public/search/<lang>.json |
at build | fetched the first time search opens |
npm run prebuild (and predev) runs scripts/build-course-data.mjs, which
writes the manifest, the per-language search indexes and public/sitemap.xml.
Never edit src/data/generated/ by hand.
Result: the index page loads one ~100 KB script instead of 3.1 MB.
Adding a module
- Create
src/data/modules/mXX-your-slug.js(copy_template.js) - Register the filename in
src/data/module-list.js - Add a line icon for its
idtoICON_PATHSinsrc/components/Brand.jsx - Optionally add
src/data/quizzes/<module id>.jsfor an end-of-module check. Questions must be answerable from that module's own lesson text — the point is to check comprehension, not to introduce new material.
npm run dev regenerates everything derived.
Project Structure
src/
├── components/ # React UI components
│ └── Brand.jsx # Wordmark, icon set, module colour themes
├── data/
│ ├── i18n.js # UI translations
│ ├── locales.js # Language registry (internal code -> BCP-47 tag)
│ ├── courses.js # Manifest + lazy module loaders
│ ├── module-list.js # Course running order
│ ├── modules/ # Individual course modules
│ ├── quizzes/ # Optional end-of-module checks
│ └── generated/ # Build output — do not edit
└── styles/ # Global CSS
scripts/
├── build-course-data.mjs # manifest, search indexes, sitemap, robots.txt
└── make-og-image.py # social card
tests/
└── course-data.test.js # smoke tests for the data pipeline
Scripts
npm run dev |
regenerates course data, then serves |
npm run build |
regenerates course data, then builds |
npm run lint |
ESLint |
npm test |
regenerates course data, then runs the smoke tests |
npm run check-links |
checks every external link in the lessons, quizzes and README |
Contributing
Contributions welcome! Whether it's new modules, translations, or UI improvements.
- Fork the repo
- Create a branch (
git checkout -b feature/new-module) - Commit your changes
- Push and open a PR
License
MIT — Use freely for education and community building.
Credits
Built by INFTF. Learn more about Xahau at xahau.network.