Pakistan's definitive heritage travel guide. Explore 5,000 years of history across five regions β Punjab, Sindh, KPK, Balochistan, and Gilgit-Baltistan.
Live demo:Deployment
Uncharted Travels is a static multi-page website built with plain HTML, CSS, and vanilla JavaScript β no framework, no build step, no npm install. It's a heritage travel guide and booking portal for curated cultural and historical tours across Pakistan, covering UNESCO World Heritage Sites, ancient civilisations, Mughal architecture, Buddhist monasteries, Sufi shrines, and curated land routes.
8 pages, 9 stylesheets, one shared JS file, zero dependencies beyond two CDN links (Google Fonts, Font Awesome).
Uncharted-Travels-main/
β
βββ index.html # Home β hero, regions, featured sites, timeline, testimonials
βββ about.html # Company story, team, milestones, mission
βββ destinations.html # Full destination listing by province (30+ locations)
βββ historical-sites.html # Deep-dive into major archaeological sites + UNESCO table
βββ cultural-insights.html # Culture, festivals, Gandhara art, Sufism, food, language
βββ travel-guide.html # Visas, safety, transport, budget, packing, itineraries
βββ contact.html # Multi-step booking form, FAQ, emergency contact
βββ 404.html # Custom not-found page, matches site design system
β
βββ css/
β βββ shared.css # Design tokens, reset, nav, footer, utilities, a11y
β βββ index.css about.css contact.css
β βββ destinations.css historical-sites.css cultural-insights.css
β βββ travel-guide.css # ...one file per page for its unique sections
β βββ 404.css # 404 page layout
β
βββ js/
β βββ main.js # Nav, hamburger, scroll reveal, forms, FAQ, sliders
β
βββ htmlpictures/ # ~75 image assets (photos, icons, OG previews)
β
βββ manifest.json # PWA manifest β name, icons, theme colour
βββ robots.txt # Crawler rules + sitemap pointer
βββ sitemap.xml # Search-engine sitemap (needs your real domain β see below)
βββ .hintrc # webhint/Edge Tools linter config
βββ LICENSE # MIT
βββ README.md
No build tools, no frameworks, no npm. Drop the folder on any static host and it works exactly as-is.
| Token | Hex | Usage |
|---|---|---|
--void |
#07070D |
Page background |
--stone |
#12121A |
Card backgrounds |
--deep |
#1C1C28 |
Section backgrounds |
--slate |
#252535 |
Elevated surfaces |
--copper |
#BF7A35 |
Primary accent, CTAs |
--gold |
#D4A843 |
Highlight, numbers, active states |
--rust |
#A03820 |
Emergency/alert sections |
--parchment |
#EBD9B8 |
Body text |
--cream |
#F7F0E0 |
Headings |
--sand |
#C8B08A |
Secondary text |
--mist |
#9696AF |
Subdued text (WCAG AA compliant) |
--white |
#FAFAF5 |
High-contrast text |
--nav-h |
68px |
Fixed nav height β used in calc() throughout for hero padding, scroll offsets, mobile menu positioning |
- Cormorant Garamond β all headings, serif display type
- Outfit β body text, UI labels, nav
- Google Fonts β Cormorant Garamond, Outfit
- Font Awesome 6.7.2 β icons, loaded with an SRI integrity hash
That's the entire dependency list. No build tools, no frameworks, no npm.
Built to meet WCAG 2.1 Level AA across all pages.
| Element | Background | Ratio | Status |
|---|---|---|---|
--mist text |
--void |
6.96:1 | β AA |
--mist text |
--stone |
6.46:1 | β AA |
--mist text |
--deep |
5.84:1 | β AA |
--sand text |
--void |
9.60:1 | β AA |
--copper |
--void |
5.79:1 | β AA |
--cream headings |
--void |
17.69:1 | β AA |
| Newsletter placeholder | Input background | 7.19:1 | β AA |
| Emergency subtext | Dark red bg | 8.50:1 | β AA |
- Skip-to-content link on every page, visible on focus
- Single
<h1>per page, no skipped heading levels <nav>,<main>,<footer>landmarks throughout β every page's<main>correctly wraps its full content, including the hero- Hamburger menu: full
aria-expanded/aria-controls/aria-labelstate management, toggled in JS - All decorative icons
aria-hidden="true"; all functional icons have accessible labels - Every
#anchortarget (region tabs, deep-dive sections, table-of-contents links) hasscroll-margin-topset, so the fixed nav never covers content you jump to @media (prefers-reduced-motion: reduce)disables scroll-reveal animation, the homepage ticker, and other transitions for users who've asked their OS for reduced motion
| Feature | Description |
|---|---|
| Hamburger menu | Toggles .open on .nav-links, swaps bars β times icon, closes on outside click. Switches on below 1100px (not 768px) β the full desktop nav genuinely needs that much width for the logo, 7 links, and CTA button to fit without overlapping |
| Nav scroll darkening | On the homepage only, nav starts transparent over the hero and darkens after 60px of scroll |
| Scroll reveal | IntersectionObserver adds .visible to .reveal elements as they enter the viewport, staggered slightly per element |
| Scroll-to-top button | Appears after 400px of scroll, smooth-scrolls to top on click |
| FAQ accordion | Single-open accordion on the Contact page |
| Tour type selector | Click-to-select cards on the Contact page's tour picker |
| Booking form | Live progress indicator as fields fill in; validates with reportValidity() before showing success (so an empty form can't submit); attempts a real fetch() POST to its configured endpoint, falling back to a demo success state if that endpoint isn't set up yet β see Making the Forms Real |
| Newsletter form | Same validate-then-submit-with-fallback pattern as the booking form |
| Region tabs / filter buttons | Simple active-state toggles on Destinations and Historical Sites |
| Group size slider | Live numeric display synced to the range input on the booking form |
Note on "active" nav state: which link shows as current-page is set directly in each page's HTML (class="active" on the matching <a>), not detected automatically via window.location.pathname. If you add more pages, remember to set this by hand.
Every page has:
- Unique
<title>and meta description (within recommended length ranges) <link rel="canonical"><meta name="robots" content="index, follow">(404.html correctly usesnoindex, followinstead)- Full Open Graph + Twitter Card tags, each pointing at a real, page-specific preview image already in
htmlpictures/ manifest.jsonlinked, with a full icon set (16px, 32px, 180px apple-touch, 192px, 512px) generated from the actual logo β not stretched up from the small favicontheme-colorset to the site's copper accent, so mobile browser chrome matches the brand
Before this does anything for real search engines:
- Open
sitemap.xmland replace everyhttps://REPLACE-WITH-YOUR-DOMAIN.comwith your real deployed domain β the sitemap spec requires absolute URLs, so it's inert until you do this robots.txtalready points atsitemap.xmlcorrectly and needs no changes
- Every image below the fold uses
loading="lazy" decoding="async"(13 images across the site); the logo, which is above the fold on every page, correctly stays eager htmlpictures/was audited and re-compressed this pass: 30MB β 12MB. The worst single offender was a 9.4MB, 5248Γ2944px DSLR photo used as a small ~400px card background β resized and re-encoded down to 178KB with no visible quality loss. Several page-preview screenshots were converted from lossless PNG to high-quality JPEG for the same reason (OG images get recompressed by social platforms anyway, so there's no benefit to serving them lossless)will-change: transformon the homepage's continuously-animating ticker, so it runs on the compositor thread instead of repainting every frame- Two Google Fonts
preconnecthints (fonts.googleapis.comandfonts.gstatic.com, the second withcrossorigin) β the font files themselves are served from the second domain, and skipping that preconnect is a common miss that costs a full extra DNS+TLS round trip - Font Awesome is loaded with an SRI
integrityhash andcrossorigin="anonymous"
Still open, if you want to push further: the three remaining .webp images over 500KB (Altit-Fort, Carved-stupas, Swat-Valley) are already in an efficient format at reasonable dimensions for their use as hero/card backgrounds β further gains there would be marginal. htmlpictures/homepage-desktop.png (1.3MB) isn't referenced anywhere in the codebase; it's safe to delete if you don't need it for something outside the site itself.
Both the booking form (contact.html) and the newsletter form (index.html) are fully wired to submit β they're just pointed at a placeholder endpoint right now:
<form id="bookingForm" action="https://formspree.io/f/YOUR_FORM_ID" method="POST" novalidate>The JS in main.js already does a real fetch() POST to whatever's in that action attribute, and only falls back to the demo success message if that request fails β which right now, it always will, since YOUR_FORM_ID isn't real.
To make it actually send emails, no other code changes needed:
- Create a free account at formspree.io
- Create a new form, copy the endpoint it gives you (
https://formspree.io/f/xxxxxxxx) - Replace
https://formspree.io/f/YOUR_FORM_IDin bothcontact.htmlandindex.htmlwith that real URL - Done β submissions will land in your Formspree inbox (and forward to your email) immediately
Any other form backend that accepts a standard multipart/form-data POST and returns a 2xx status works exactly the same way β Netlify Forms, Getform, EmailJS with a small adapter, or your own serverless function.
Tested against current Chrome, Firefox, Safari, and Edge. Uses:
- CSS custom properties,
clamp(), CSS Grid,backdrop-filter(with-webkit-prefix for Safari) IntersectionObserverfor scroll revealfetch()for form submission
No transpilation or polyfills β all of the above have been baseline browser features since 2020 or earlier.
- Zero inline
style=attributes anywhere in the project. The one place that used to need them β six safety-rating bars on the Travel Guide page, each with a different fill percentage β now use plain utility classes (.pct-55through.pct-88) defined intravel-guide.cssinstead. .hintrcconfigures webhint (the linter behind the "Microsoft Edge Tools" VS Code extension) with a real target browser list and two intentional exceptions:theme-colorandmin-height: autoare both progressive enhancements that fail gracefully in browsers that don't support them, so they're excluded rather than removed. Everything else webhint flags is worth a look if you see it.
- Click the green Code button on GitHub β Download ZIP
- Extract it
- Double-click
index.htmlto open it in your browser - Navigate using the top nav bar β every page works from
file://with no server needed, since all asset paths are relative
git clone https://github.com/ahmedabbas52233-a11y/Uncharted-Travels.git
cd Uncharted-TravelsThen open index.html directly, or serve it locally:
# Python (built in on most systems)
python3 -m http.server 8000
# Node
npx serve .
# VS Code β install the "Live Server" extension,
# right-click index.html β "Open with Live Server"This is a static site β any static host works with zero configuration.
Netlify (fastest):
- Go to app.netlify.com/drop
- Drag the whole project folder onto the page
- Live in about 30 seconds, with a real URL
GitHub Pages:
- Repo Settings β Pages β Source:
mainbranch,/ (root) - Save β live at
https://ahmedabbas52233-a11y.github.io/Uncharted-Travels/a minute or two later
Vercel: npx vercel from the project folder, follow the prompts.
After deploying anywhere, come back and:
- Update the
og:urland<link rel="canonical">values in all 8 HTML files to your real domain - Replace the placeholder domain in
sitemap.xml - Swap in a real Formspree endpoint (see above) if you want the forms to actually work
| Page | Purpose |
|---|---|
index.html |
Landing page β hero, five-region overview, featured sites, historical timeline, testimonials, newsletter signup |
about.html |
Company story, team (with real photos), milestones, values, awards |
destinations.html |
Full destination grid sorted by province β 30+ locations across 5 regions |
historical-sites.html |
In-depth coverage of Taxila, Lahore Fort, Takht-i-Bahi, Mohenjo-daro, plus a full UNESCO sites reference table |
cultural-insights.html |
Gandhara art, Mughal architecture, Sufism, Kalash culture, regional festivals, cuisine, languages |
travel-guide.html |
Visa info, safety by region, transport options, budget tiers, packing lists, sample itineraries |
contact.html |
Multi-step booking inquiry form, FAQ accordion, emergency contact, office hours |
404.html |
Custom not-found page matching the site's design system |
MIT β see LICENSE.