Developer Documentation

4Naple Documentation

Last updated: September 2026

Everything you need to know about the 4Naple website: how it is built, where to change content, how to open or close a City, how caching works, and how to deploy a change safely.

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.

Quick rule of thumb Content → 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.

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
siteSite name, tagline, email, URL
navMenu items (label, link, icon, group)
carouselAutoplay interval and hover-pause
heroSlidesThe rotating hero slides
citiesThe six city sections and their cards
iconsInline 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.

Placeholder links & city destinations Card links use "#" 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.

  • "1" — the City displays normally with its full links, carousel and "Explore" / "View All" buttons.
  • "0" — the City stays visible on the landing page but is greyed out, its cards and buttons are disabled and labelled "Coming Soon", and nothing can be opened.
  • 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

  • Copy an existing simple page — e.g. faq.html — as your starting point. It already has the correct head, header, nav, footer and theme script.
  • Update the title, meta description, canonical URL and the Open Graph tags in the <head>.
  • Replace the content inside <main> with your own sections. The policy-* styles give you the same panels used on this page.
  • Add the page to the menu on every page (the nav-menu list), to the footer links, and to the homepage js/data.js nav array if you want an icon.
  • Load the same scripts at the bottom of the page (settings, data, carousel, navigation, app, cookie-consent, scroll-top).
  • Remember the cache version — see the Caching section below.
  • 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)

    CityStatusDestination
    LearningCOMPLETEhttps://learn.4naple.com/
    MarketComing Sooncoming-soon.html?city=market
    ProjectComing Sooncoming-soon.html?city=project
    ToolComing Sooncoming-soon.html?city=tool
    GameComing Sooncoming-soon.html?city=game
    ServiceComing Sooncoming-soon.html?city=service

    The one-mainUrl rule

    One City, one URL Each City has ONE 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

    Immediate click feedback When a visitor clicks a navigation element that leads to another page, a lightweight loading indicator provides immediate visual feedback while navigation occurs. It must not introduce an artificial delay.

    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:

  • Edit the CSS/JS file.
  • Bump the version query to the next number — ?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).
  • Upload everything. HTML pages are never cached, so visitors get the new references immediately and download the new files. The old cached files expire harmlessly.
  • Do not reuse a version number Each release must use a new value. Reusing a previous version would serve the old cached copy to returning users.

    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

  • City availability in js/settings.js matches what should be open ("1") vs coming soon ("0").
  • Each open City's mainUrl in js/data.js points to its real destination.
  • All links work — run the site locally and click through every page.
  • CSS/JS versions bumped if you changed those files.
  • The theme toggle works in both light and dark mode.
  • The page is responsive — test on a phone width.
  • New images are optimized and sized (.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.