Website Overview
4Naple is a static website — there is no build step, no server-side framework and no database. The whole site is plain HTML, CSS and JavaScript that your host (Apache, Netlify, Vercel, GitHub Pages, nginx, any shared host) serves directly. This makes it fast, secure and very easy to maintain.
The homepage is data-driven: most of the content you see is
rendered by JavaScript from a single content file called
js/data.js. Edit that file and the
homepage changes — no HTML editing required for most updates.
Which Cities are open (and which show as "Coming Soon") is controlled
separately from js/settings.js — so content
(data.js), availability (settings.js) and design (css/) each live in
their own place.
Other pages (About, FAQ, Contact, Privacy, Terms, Guide, and this Documentation page) are plain HTML files that reuse the same design system: the same stylesheets, header, footer, theme support and navigation.
js/data.js (homepage) or the HTML page itself (other pages). Design → css/. Behaviour → js/. Caching/deployment → .htaccess.
Project Structure
Here is the complete file layout of the website. Knowing where things live is the key to editing it safely.
4landing/ ├── index.html # Homepage (hero, 6 cities, about, contact) ├── about.html # About page ├── contact.html # Contact page ├── faq.html # FAQ page (accordions) ├── guide.html # Interactive tour page ├── documentation.html # This page ├── privacy.html # Privacy policy ├── terms.html # Terms & conditions ├── 404.html # Custom 404 page ├── coming-soon.html # Coming-soon template │ ├── css/ │ ├── style.css # Design tokens, base styles, components │ ├── themes.css # Light + dark theme variables │ └── responsive.css # Mobile-first responsive rules │ ├── js/ │ ├── settings.js # Site settings + city availability (1 = open, 0 = coming soon) │ ├── data.js # ALL homepage content + city destinations (single source of truth) │ ├── app.js # Renders hero, nav, city cards from data.js │ ├── carousel.js # Hero + card carousel logic │ ├── navigation.js # Menu, scroll-spy, header elevation │ ├── cookie-consent.js # Cookie banner │ ├── scroll-top.js # "Back to top" button (settings.js) │ ├── theme.js # Theme preference helpers │ ├── guide.js # Guide page tour │ └── coming-soon.js # Coming-soon page │ ├── assets/ │ ├── images/ # Photos, illustrations, logo │ ├── icons/ # SVG icons │ └── fonts/ # Self-hosted fonts (inter, sora) │ ├── .htaccess # Pretty URLs, caching, security headers ├── robots.txt # Search-engine rules ├── sitemap.xml # Sitemap for search engines └── README.md # Project readme
The css/ and js/
files are shared by every page. If you change one of them, the change
appears on all pages.
Changing Content
The homepage (most content lives in js/data.js)
Open js/data.js. It defines
window.siteData, a JavaScript object with
one section per area of the homepage:
| Section in data.js | What it controls |
|---|---|
site | Site name, tagline, email, URL |
nav | Menu items (label, link, icon, group) |
carousel | Autoplay interval and hover-pause |
heroSlides | The rotating hero slides |
cities | The six city sections and their cards |
icons | Inline SVG icon paths used by the menu |
Example: add a card to Learning City
In js/data.js, find the
learning city and add one object to its
items array:
{
title: "Robotics",
description: "Build and program simple robots.",
image: "assets/images/cards/robotics.webp",
link: "#"
}
The card renders automatically with the same style as the others. Put
the matching image in assets/images/cards/
(the site uses .webp images).
Example: add a hero slide
{
image: "assets/images/hero/hero-four.webp",
alt: "Description of the image",
kicker: "A Digital Metropolis",
title: "A New Chapter",
text: "Optional supporting sentence.",
cta: { label: "Start Exploring", href: "#learning-city" }
}Editing text on other pages
About, FAQ, Contact, Privacy, Terms, Guide and Documentation are
ordinary HTML files. Open the file and edit the text directly.
The FAQ page uses <details>
elements for its questions — copy an existing
<details class="faq-item"> block to
add a new question.
Changing the menu
Each page has a <nav class="nav-menu">
block in its HTML. Edit the nav-list there.
On the homepage the menu is also listed in
js/data.js under nav
(used for the icons and the "city" divider). Keep the two in sync.
"#" placeholders. app.js automatically routes any placeholder link to the city's mainUrl — so every card in a City leads to that City's one destination, never to an individual category page. See the "City Destination & URL Configuration" section below.
Adding Features & Settings
Feature flags & settings: js/settings.js
Global site settings live in js/settings.js and
load before every other script, so all modules read them from
window.siteSettings.
window.siteSettings = {
/* "Back to top" button */
scrollToTop: true, /* false hides the button */
/* City availability — "1" = open, "0" = coming soon (greyed out) */
cities: {
learning: "1",
market: "0",
tool: "0",
project: "0",
service: "0",
game: "0"
}
};
Add new site-wide flags here so all scripts can read them from
window.siteSettings.
Opening or closing a City
City availability is controlled only from
js/settings.js — no HTML, CSS or other
JavaScript needs editing.
To activate a City later, change its value to "1"
(and set its real mainUrl in
js/data.js — see the City Destination section).
To add a brand-new City later, add one line to this
cities block keyed by the City's
data.js id.
Adding a new page
faq.html — as your starting point. It already has the correct head, header, nav, footer and theme script.<head>.<main> with your own sections. The policy-* styles give you the same panels used on this page.nav-menu list), to the footer links, and to the homepage js/data.js nav array if you want an icon.Adding a new stylesheet or script
Put new CSS in css/style.css (or a new file in
css/ linked from each page) and new behaviour in
a new file under js/. Link new scripts with
defer right before
</body>, after the existing scripts.
Use the design tokens (var(--space-*),
var(--color-*), var(--radius-*))
so new UI matches the site in both themes automatically.
Adding images
Drop new images into assets/images/ using the
same subfolders (hero, cards, cities, logo…). The site uses
.webp for photos and illustrations. Always set
explicit width/height attributes on <img>
tags to prevent layout shift, and add
loading="lazy" decoding="async" for
below-the-fold images (the existing pages show the pattern).
City Destination & URL Configuration
The landing page is a directory / preview page. Each
City has one main destination (mainUrl),
and every clickable element inside that City — category cards, card
images, the "Explore" area, the "More" card, and the main
"View All / Browse / Start a Project / Open the Toolbox / Play Now /
Request a Service" button — inherits the City's
mainUrl. There are no individual
category pages (no programming.html,
templates.html, etc.).
Destinations are configured centrally in js/data.js
(each City's mainUrl + matching
viewAll.href). app.js's
resolveLink() turns placeholder card links
("", "#") into the
City's mainUrl.
Current status (all 6 Cities)
| City | Status | Destination |
|---|---|---|
| Learning | COMPLETE | https://learn.4naple.com/ |
| Market | Coming Soon | coming-soon.html?city=market |
| Project | Coming Soon | coming-soon.html?city=project |
| Tool | Coming Soon | coming-soon.html?city=tool |
| Game | Coming Soon | coming-soon.html?city=game |
| Service | Coming Soon | coming-soon.html?city=service |
The one-mainUrl rule
mainUrl. All category cards and entry points within that City inherit the City's mainUrl. When a City is completed, replace only its mainUrl with the real destination URL. Do not create individual category URLs on the landing page.
Replacing a temporary URL with a real subdomain
When a City is ready, edit only its
mainUrl (and viewAll.href)
in js/data.js, then set its availability to
"1" in js/settings.js.
Every card and entry point in that City updates automatically.
/* BEFORE — temporary destination */
mainUrl: "coming-soon.html?city=market",
viewAll: { label: "Browse the Market", href: "coming-soon.html?city=market" },
/* AFTER — real destination */
mainUrl: "https://real-market-url.example/",
viewAll: { label: "Browse the Market", href: "https://real-market-url.example/" },
Then bump the js/data.js cache version
(?v=) in every HTML page so returning
visitors get the new links immediately.
Navigation loading indicator
The indicator is a small pill (spinner + "Opening…") that appears instantly on any cross-page link click and is cleared when the page is restored (Back/Forward). Same-page anchors and carousel arrow controls never trigger it.
Caching & Releasing Changes
The site uses a cache-busting version query on its CSS and JavaScript files. Every page references them like this:
<link rel="stylesheet" href="css/style.css?v=9"> <script src="js/app.js?v=5" defer></script>
The server sends Cache-Control: public, max-age=31536000, immutable
for CSS/JS/images/fonts (cached for one year) and
no-cache, must-revalidate for HTML pages
(checked every visit). This is configured in
.htaccess.
When you change CSS or JS, bump the version
Because browsers cache CSS/JS for a year, changing a file without bumping its version means returning users keep the old file. To release a change:
?v=2 → ?v=3, or ?v=3 → ?v=4 — in every page that references the changed file (all pages share the same files, so it is a simple find-and-replace).When you change HTML content only
No version bump is needed — HTML is revalidated on every visit. Just upload the changed page.
New images and fonts
Upload them with a new filename (e.g. hero-four.webp)
or a version query so the new asset gets its own URL. If you overwrite an
existing file at the same URL, old caches may keep showing the previous
image for up to a year.
Testing & Deployment
Preview locally
Any static server works. From the project folder:
python3 -m http.server 8080 # or: npx serve .
Then open http://localhost:8080.
Deploy
Upload the entire folder to your host — index.html,
all HTML pages, css/, js/,
assets/, .htaccess,
robots.txt, sitemap.xml.
There is no build step. Works on Apache, Netlify, Vercel, GitHub Pages,
nginx, S3+CloudFront or any shared host.
Pretty URLs (Apache)
.htaccess rewrites /about
to about.html and 301-redirects
/about.html to /about.
A new page at documentation.html is automatically
served at /documentation — no rewrite change needed.
It also forces HTTPS on the canonical www.4naple.com
host, serves the WebP MIME type, enables compression, and sets security headers.
Before you go live, check
js/settings.js matches what should be open ("1") vs coming soon ("0").mainUrl in js/data.js points to its real destination..webp).Common Questions
Why are my CSS/JS changes not showing up for visitors?
You changed the file but did not bump the version query. Update ?v=2 to a new number in every page that references the file, then upload. See the Caching section above.
Why do some card links go to the coming-soon page?
That is by design. Placeholder card links ("#", empty) are automatically resolved to the City's mainUrl by app.js. Unfinished Cities currently point to coming-soon.html?city=<id> as their temporary destination, and their availability is "0" in js/settings.js, so they render greyed out and cannot be opened. When a City is ready, set its availability to "1" and point mainUrl at the real URL.
How do I add a new city section?
Add a new object to the cities array in js/data.js (copy an existing city, change its id/title/color/items and set its mainUrl destination), add a matching <section id="my-city"> block to index.html for the no-JavaScript fallback, add a menu entry in the page navigation and in the nav array, and add its availability ("1" or "0") to the cities block in js/settings.js.
Can I change the accent colors?
Yes. Each city has a color value in js/data.js, and the six brand accents are defined as --accent-* variables in css/style.css. The brand primary red is --color-primary in css/themes.css (different values for light and dark).
Do I need to change anything in .htaccess when adding a page?
No. The rewrite rules serve any .html file at its clean URL automatically. Only touch .htaccess for caching, security headers or redirect changes.
How do I contact support?
The fastest way to reach us is by email. Questions, partnerships and project ideas are always welcome.