Building TNT Adventures
Session 1: Foundations & File Naming
How the Adventures section was placed in TNT, named, and wired into the ecosystem
This is the running development diary for TNT Adventures — the section of TechNoviceTools dedicated to structured learning missions: Scavenger Hunts, Fix the Bug challenges, and DevTools Challenges.
The log follows the S.P.A.R.K. method: Set goal → Prompt thoughtfully → Analyze the response → Refine → Know what you built. Each development session adds its goals, the conversation that shaped the decisions, and the lessons that came out of it.
Session 1 (2026-08-02) covers the rationale for the Adventures section and the file-naming decisions that established adventureHome.html and sHuntHome.html as canonical entry points.
Session 2 (2026-08-02) documents the stoogeAdventures.html refactor (adopting the swingAlphabetSPARK.html page format) and the addition of an intro blurb and patron saints link to the adventureHome.html hero.
Session 3 (2026-08-07) adds a localStorage-backed student identity modal to adventureHome.html — a welcome form that collects first name, last name, and subject/period on first visit, skips on return visits, and exposes the profile under the key tntAdventureProfile for use by all hunt pages.
Session 4 (2026-08-07) fixes two initialization bugs: the profile modal was not appearing on first load (Bootstrap CSS not yet applied when the script ran), and navigating away then back via the browser’s Back button bypassed re-initialization entirely due to the Back-Forward Cache.
Session 5 (2026-08-07) standardizes the footer across all five Adventures pages, applying CRAP’s Repetition principle to establish one consistent footer template for the section.
TNT Adventures: the design rationale. Most of the TNT site is organized by technology — JS Apps, Processing, PHP, Styl’n. Adventures is organized by learning activity. It answers the question: what can a student do with the existing TNT pages beyond reading them?
Three mission types are planned:
- Scavenger Hunts — navigate real TNT pages, read source code, use DevTools, answer questions that prove genuine understanding. Hunt #1 launched 2026-08-01.
- Fix the Bug — a deliberately broken TNT page, with an answer key that teaches rather than just corrects. (Coming soon.)
- DevTools Challenges — live pages, professional browser tools, no file downloads. (Coming soon.)
Every hunt uses a three-tier model: 🥉 Bronze (read the page) · 🥈 Silver (inspect the code) · 🥇 Gold (understand the why). A student can stop at Bronze and still learn something; a stronger student pushes to Gold.
Why it matters: Adventures turns TNT’s existing content into an active learning environment without requiring new apps. The pages already built are the curriculum. Hunt #1 covers navigation, source reading, hyperlink inspection, the XRAY framework, and first use of DevTools — entirely through nine guided stops on existing live pages.
I am working on some ‘adventures’ improvements. We link to it from the TNT index only to find it’s index and then an index for Scavenger Hunts. The word ‘index’ in so many connected settings is head-spinning. Let’s rename the adventure one: adventureIndex or adventureHome and the scavenger hunt one sHuntIndex or sHuntHome. Also, since we will be doing some ‘hefty’ work together on this, let’s create a adventuresChatlog.html to chronicle our work, in the same way we’ve done other work and laid out our SPARK method analysis. Tell me your thoughts on the index renames for clarity.
The phrase “head-spinning” is precise. The problem is not that index.html files are wrong — they function correctly. The problem is cognitive overhead for the developer. Three browser tabs labelled “index.html,” VS Code showing adventures/index.html and adventures/scavenger-hunts/index.html side by side — they look identical. The names do not reflect the files’ roles.
Offering two naming options (adventureIndex or adventureHome) and asking for a recommendation is the correct approach — it establishes intent without locking in a choice before hearing the reasoning. Good prompting leaves room for the agent to inform the decision.
Requesting the chatlog in the same breath as the rename is good planning. It means Session 1 of the log documents its own origin — the log begins with its own story.
Naming recommendation:
adventureHome.html over adventureIndex. A landing page that orients visitors and sends them to three activity types is performing the role of a home page, not a sorted list. “Index” implies catalogued content; “Home” implies orientation and departure. The file’s job is to tell you where you are and where you can go.
sHuntHome.html over sHuntIndex for the same reason, and for an additional one: sHunt already exists in the project’s naming convention. Individual hunt files are named sh-xray-2026-08-01.html — the sh- prefix is established. sHuntHome (camelCase) extends that prefix to the catalog level, distinguishing the “all hunts” page from an individual hunt by name pattern alone.
One production consideration: index.html is what Apache serves automatically for a directory URL (e.g. technovicetools.com/adventures/). The fix — keeping the old index.html as a thin redirect shim — costs nothing and preserves that directory URL for existing bookmarks or external links.
“Home” describes what the file does; “Index” describes how it was historically generated. In the early web, index.html was auto-generated by Apache directory listings — a convention that survived long after most sites stopped using directory listings. In a deliberately designed site, every file should have a name that communicates its role to the developer who opens the project in an editor six months from now.
Rule: If you can read the filename and immediately know what the page does without opening it, the name is correct. adventureHome.html passes that test. index.html does not.
The naming hierarchy for the Adventures section now has three distinct patterns, each self-describing:
adventureHome.html— the section landing pagesHuntHome.html— the scavenger hunt catalog (camelCase: activity type + role)sh-xray-2026-08-01.html— individual hunts (kebab-case: type prefix + topic + date)
Link audit results: Seven files contain links that needed updating — ten link instances total.
| File | Old link | New link |
|---|---|---|
index.html (TNT root) | adventures/index.html | adventures/adventureHome.html |
news.html | adventures/index.html | adventures/adventureHome.html |
movie_clips.html | adventures/index.html | adventures/adventureHome.html |
adventureHome.html (self nav + card) | index.html / scavenger-hunts/index.html | adventureHome.html / scavenger-hunts/sHuntHome.html |
sHuntHome.html (self ×3) | ../index.html (nav, hero, button) | ../adventureHome.html |
sh-xray-2026-08-01.html | ../../scavenger-hunts/index.html & ../../index.html | ../../scavenger-hunts/sHuntHome.html & ../../adventureHome.html |
sh-xray-2026-08-01-chatlog.html | ../../index.html | ../../adventureHome.html |
When index.html is removed from a directory, any browser that navigates to the bare directory URL (/adventures/) gets a 404. Keeping a redirect shim in place — two-line HTML with <meta http-equiv="refresh"> plus a JS fallback — costs nothing and eliminates all compatibility risk.
The canonical name is the new one (adventureHome.html). The old file exists only to serve any traffic that arrives via the directory URL or an outdated bookmark. It is not linked from anywhere in the project going forward.
| File | Action | What Changed |
|---|---|---|
adventures/adventureHome.html | Created | Renamed from index.html; self-nav and scavenger-hunts card link updated |
adventures/scavenger-hunts/sHuntHome.html | Created | Renamed from index.html; all three ../index.html back-links updated to ../adventureHome.html |
adventures/adventuresChatlog.html | Created | This page — Adventures development diary, Session 1 |
adventures/index.html | Redirect shim | Converted to meta-refresh redirect → adventureHome.html |
adventures/scavenger-hunts/index.html | Redirect shim | Converted to meta-refresh redirect → sHuntHome.html |
index.html (TNT root) | Updated | Feature card link updated |
news.html | Updated | Entry #045 text link updated |
movie_clips.html | Updated | Stooges Adventures card link updated |
sh-xray-2026-08-01.html | Updated | Hero back-link and two footer buttons updated |
sh-xray-2026-08-01-chatlog.html | Updated | Adventures Home button updated |
- Name files after their function, not their origin.
index.htmlis a web-server convention inherited from directory listings, not a design decision. A landing page that orients visitors should be calledadventureHome.html. A catalog that lists hunts should be calledsHuntHome.html. When you open the project in an editor six months from now, the filename should tell you immediately what the file does. - Audit all inbound links before renaming anything. This rename touched 10 link instances across 5 external files. Without a prior audit, at least one link would have produced a silent 404. An audit is not overhead — it is the work. Building a complete list of inbound links before touching a filename is always faster than debugging broken navigation afterward.
- Keep redirect shims when renaming files on a live server. A renamed file on a live server becomes a 404 for any user with a bookmark, any external site that linked to it, and any browser with a cached path. A two-line meta-refresh shim in the old location eliminates all of those problems at zero cost. The canonical name is the new one; the old file exists only to serve stragglers.
With Session 1 file-naming complete, two content improvements followed: (1) refactoring stoogeAdventures.html to match the swingAlphabetSPARK.html format, and (2) adding an intro blurb and patron saints link to the adventureHome.html hero section.
If you look at the swingAlphabet movie clip, you added a bit of history about the episode. I liked the page’s format. Can you refactor the stoogeAdventures page to do likewise? It is the motivating page for our ‘Adventures’ area and I’d like to create a short intro blurb on that page (similar to what you wrote on the movie short) to set a fun, thematic tone for the adventures area.
The three-section <details> format from swingAlphabetSPARK.html maps directly onto the Stooges page:
- Episode overview (open) — We Want Our Mummy (1939), a
.mission-quotedialogue block, and the persistence theme - The Adventures connection — the Curly–student parallel, with Hunt #1 stops 1 and 9 as concrete evidence
- Fun Facts — Stooges adventure-genre history with amber border (matching swingAlphabet)
The .content-card was updated to #111827 and the clip frame border changed from green to amber — consistent with swingAlphabet and other clip pages. Adventures green stays in the hero gradient; the shared dark card palette is now uniform across the clip collection.
On the adventuresHome, let’s write a short intro blurb about the purpose of the adventures and a mention of our ‘patron saints’ in the hero image: the 3 stooges and perhaps a link to the stoogeAdventures movie clip. Let’s also update our adventuresChatlog with the progress we are making.
Two placements were considered: in the content area (between hero and activity cards) or inside the hero itself. The hero wins: the blurb is visible on load alongside the title, setting tone before anything else is read.
The blurb renders at 88% size and 62% opacity — subordinate to the title and subtitle, but immediately readable. The “Meet them →” link uses amber so it reads as the one actionable element in the hero without competing with the main headings.
“Meet them” implies the Stooges are characters you can go and encounter — not a footnote about the background image. The arrow makes it a direction, not a label.
| File | What Changed |
|---|---|
movie_clips/stoogeAdventures.html | Three <details> sections; .mission-quote block; .clip-label header; card + frame CSS aligned to swingAlphabet pattern; Adventures nav link corrected |
adventures/adventureHome.html | Intro blurb added to hero: purpose statement + patron saints + link to stoogeAdventures.html |
adventures/adventuresChatlog.html | Session 2 added (this entry); about-box updated; nav updated with Patron Saints button |
With the Adventures structure established, the focus shifted to student identification. A welcome modal now collects first name, last name, and subject/period on first visit, storing the profile under the key tntAdventureProfile in localStorage so that hunt pages can access it without asking again.
When a novice visits our ‘adventureHome’ it’s likely they will be interacting with some form of challenge, eventually. It would be nice to secure information from them that can be used to populate identity and class/period information from them. Why not have a modal welcome to secure their: first, last name; their subject/period. (The date can be secured for each activity at the point of submission). We could store this info in local storage and once it’s there, the modal would not need launching. In case someone wants to edit their info, we could have a subtle link at the bottom of the page to edit their info or to ‘reset’ the app. I think this will be a useful feature and we can use it for our Scavenger Hunt scenarios to start with.
localStorage as the Identity LayerThe profile is stored under tntAdventureProfile as a JSON object: { firstName, lastName, period }. Date is deliberately excluded — it belongs to individual activity submissions, not the identity record. When a hunt page needs to record a response, it reads the profile from localStorage and appends the submission date at that moment.
Three behaviors driven by the presence or absence of the stored key:
No profile → modal opens immediately, non-dismissable (static backdrop, no Escape, no close button) until form is completed.
Profile exists → modal skipped; student bar renders at page bottom showing name, period, and Edit / Reset links.
Edit clicked → modal opens in edit mode with pre-filled values and a visible close button (fully dismissable).
The same <div class="modal"> serves onboarding and editing, but with different Bootstrap options. On first visit: backdrop: 'static', keyboard: false. On edit: backdrop: true, keyboard: true with the close button made visible.
Options are passed programmatically as the second argument to new bootstrap.Modal(el, options) rather than set as data-bs-* HTML attributes, because the behavior must differ between the two modes. One element, two instantiation contexts, correct behavior in both cases.
The student bar sits at the bottom of <main>, between the content section and the footer — visible to a student looking for it, unobtrusive for one who is not. It is a status indicator, not a call to action.
The Reset link invokes confirm() before clearing the profile. A student who clicks Reset accidentally should not lose their saved data without a second deliberate action.
| File | What Changed |
|---|---|
adventures/adventureHome.html | Student identity modal added; student bar added before </main>; profile management IIFE added after Bootstrap bundle; lastUpdate updated to 08/07/2026 |
adventures/adventuresChatlog.html | Session 3 documented; about-box updated; lastUpdate updated to 08/07/2026 |
localStorageis the correct identity layer for a classroom tool without a back-end. Server-side accounts are disproportionate when the teacher already knows the students.localStorageis per-browser, per-device, and requires no server round-trip. The profile persists until the student explicitly resets it or clears browser storage — simple, auditable, and sufficient for submitting named responses from a known-student population.- Separate onboarding behavior from edit behavior, even when they share a UI element. First-time collection requires non-dismissable interaction — the profile is a prerequisite for submission tracking. Editing is always optional — the profile already exists. Conflating the two behaviors produces the wrong experience for one of the two cases. Programmatic options per mode, one element, correct behavior in both contexts.
I notice when I reset my personal information, an ugly popup happens (it should be ‘pretty’) and then, once cleared, the modal pops back up immediately, almost like an infinite loop. We need to document this ‘hiccup’ in the chatlog as well as a fleshed out solution to it.
Bug 1 — The ugly popup: The reset confirmation used window.confirm() — the browser’s native blocking dialog. It cannot be styled; its appearance is controlled entirely by the OS and browser. On different platforms it looks different, and on none of them does it match the page. It also blocks the entire browser thread while waiting for an answer.
Bug 2 — The apparent infinite loop: When openModal(false) was called after clearing localStorage, it ran new bootstrap.Modal(el, options) on the same DOM element that already held a Bootstrap Modal instance from the original page-load call. Bootstrap 5 does not dispose a stale instance when a second one is created on the same element — both instances coexist on the element, sharing its internal event handlers. The result: the first instance’s hidden.bs.modal event fires when the second instance shows, which can immediately trigger the modal to reappear.
window.confirm()The student bar now has two layers: the normal status row (id="studentBarMain") and a hidden confirmation row (id="resetConfirm"). When Reset is clicked, the confirmation row slides into view below the status line with two styled buttons: Yes, clear it (red) and Cancel (grey). Cancel hides the confirmation row. Yes, clear it performs the actual reset.
No overlay, no browser dependency, no thread blocking — and the visual language (red for destructive, grey for safe) communicates the intent without any words beyond the label.
openModal() now calls bootstrap.Modal.getInstance(el) before creating a new instance. If a stale instance exists, prev.dispose() is called first. Only then is new bootstrap.Modal(el, options) called, with a clean element.
Rule: Always call getInstance() before new Modal() on a shared DOM element. Stale instances are the most common source of unexpected Bootstrap modal behavior, and they are invisible — no console error, just wrong behavior.
When openModal(false) is called (first-visit or post-reset mode), the three form fields are now explicitly cleared and any is-invalid classes from a previous attempt are removed. Without this, old values and red validation highlights from a prior session persist in the form after a reset — which is confusing precisely because the user just cleared their profile and expects a blank slate.
| File | What Changed |
|---|---|
adventures/adventureHome.html | Student bar: studentBarMain wrapper + hidden resetConfirm row added; confirm() removed; inline button handlers added; openModal() updated with dispose() guard and form-clear on non-edit mode |
adventures/adventuresChatlog.html | Session 3 addendum documented |
The student identity modal was not appearing on first load, and navigating away then back produced a console error about the Back-Forward Cache with no modal or status bar on the restored page. Both symptoms share a root cause: profile initialization ran too early and did not survive cache restoration.
I just opened the adventuresHome ‘from scratch’ and it did not show a ‘login’ status for me, nor a modal for input of data. When I went to a scavenger hunt and back again, the console reported: sHuntHome.html:1 WebSocket connection to ‘ws://127.0.0.1:5500/…’ failed: Page entered Back-Forward Cache. Let’s get this corrected so we can incorporate the data use in scavenger hunts.
Problem 1 — Script runs before Bootstrap CSS is applied: The profile IIFE’s final line called initProfile() synchronously as soon as the <script> tag was parsed. Bootstrap JS is loaded synchronously and is available at that moment — but Bootstrap CSS is fetched asynchronously from a CDN <link> tag in <head>. On any non-trivial connection, that stylesheet may still be downloading when the script runs. bootstrap.Modal.show() executes before Bootstrap’s CSS transitions are active, so the modal is technically “shown” but the backdrop and positioning render incorrectly or not at all.
Problem 2 — Back-Forward Cache (BFCache) bypasses re-initialization: Modern browsers cache full page states — HTML, JS heap, scroll position — so back/forward navigation is instantaneous. When a page is restored from BFCache, neither DOMContentLoaded nor window.load fires. The IIFE ran once on the original load; it never runs again on a cache restoration. The result: navigating from adventureHome.html to any hunt page and pressing Back produced a frozen page state with neither modal nor status bar. The WebSocket error in the console is the VS Code Live Server losing its hot-reload connection — it is not a JavaScript error and does not affect the deployed site.
window.load with readyState GuardThe immediate initProfile() call is replaced with a two-branch check. If document.readyState === 'complete', all resources are already loaded and initProfile() runs immediately. Otherwise, it is registered as a window.load listener, which fires only after every stylesheet, script, and image is fully applied.
The readyState guard is necessary because the script sits at the bottom of <body>: by the time the browser parses it, load may have already fired. Registering a listener for an event that has already fired does nothing — the guard prevents that silent failure.
pageshow Event Handles BFCache RestorationThe pageshow event fires for both normal loads and BFCache restorations. When event.persisted === true, the page was served from cache. A listener for this event calls initProfile() again, correctly rendering either the student status bar (if a profile is saved) or the welcome modal (if not).
pageshow is the only event that fires in both load and cache-restore contexts. DOMContentLoaded and window.load do not fire on BFCache restoration — this is by design, for performance. Any page that initializes UI from stored state must listen for pageshow to behave correctly after back/forward navigation.
| File | What Changed |
|---|---|
adventures/adventureHome.html | Immediate initProfile() call replaced with readyState guard + window.load listener; pageshow listener added for BFCache restoration |
adventures/adventuresChatlog.html | Session 4 documented; about-box updated |
- A script at the bottom of
<body>runs before CDN-hosted stylesheets are guaranteed to be applied. Scripts load in document order, but external CSS fetches are asynchronous. If your script calls code that depends on CSS being active (like Bootstrap’s Modal), defer towindow.loador checkdocument.readyState === 'complete'first. “Script at bottom of body” is not a substitute for “all resources loaded.” - The Back-Forward Cache is invisible to
loadandDOMContentLoaded— usepageshow. BFCache is a major performance feature in all modern browsers. Pages served from it look identical to freshly-loaded pages, but no initialization code re-runs. Any UI state driven by JavaScript — modals, status bars, dynamic content — will be stale on a cached restoration. Listen forpageshowwithevent.persisted === trueto re-initialize that state every time the user arrives on the page, regardless of how they got there.
A design audit of the Adventures section revealed inconsistent footers across five pages — three distinct footer patterns where CRAP’s Repetition principle demands one. Standardization corrects this without loss of information or function.
I’m looking at the footers of adventure-related pages as compared to footers of more ‘main’ pages like explore. Since the genre is like a sister site, I can see the logic of having a lower-footprint footer, like on the sHuntHome page. However, those pages have inconsistent footers: Using our CRAP design principles, I think they should all be the same, albeit simplified. Your view? Minimally, they should be consistent while all still validating.
The CRAP Repetition principle is exactly right here. Five pages in the Adventures section, three footer patterns — that’s a design inconsistency that erodes the “sister site” identity. The footer is the one element that appears on every page in the section; it should be the one element that is absolutely uniform.
Two of the five pages (adventureHome.html and sHuntHome.html) already share the correct footer structure — TNT home icon, Validate icon, one copyright/date line. That pattern is the right target: simple, functional, and already validated. The other three pages deviate in three different ways, none of which add value worth the inconsistency cost.
Pages already matching the standard: adventureHome.html, sHuntHome.html — no changes needed.
| Page | Deviation | Why it matters |
|---|---|---|
sh-xray-2026-08-01.html | Bootstrap icon; footer text prefixed with “TNT Adventures — Hunt #1: X-Ray Vision” | Bootstrap credit does not belong in the Adventures sister-section footer. The hunt title is already in the page title, hero, and print header. |
sh-xray-2026-08-01-chatlog.html | Bootstrap icon | Inconsistent with section standard; adds dependency credit no other Adventures page includes. |
adventuresChatlog.html | Duplicate validator icon; mission-statement paragraph | Two links to the same validator domain add no information. The mission statement belongs on the main site footer, not the Adventures sub-section footer. |
One template for all five Adventures pages. Path values vary by directory depth; the structure is identical:
- Icon 1: TNT dynamite icon → site home (
index.html) - Icon 2: Validate icon → W3C validator (dynamic URL via
validateLinkJS pattern) - Text:
All rights reserved. • © 2015–[year] TechNoviceTools (TNT) • Last updated: [date]
Why no Bootstrap icon? Bootstrap is a framework dependency, not a content partner. Dependency credit belongs in page comments or chatlog tech notes, not in student-facing footer chrome.
| File | What Changed |
|---|---|
sh-xray-2026-08-01.html | Bootstrap icon removed from footer; footer text changed from hunt-title prefix to standard copyright/date line |
sh-xray-2026-08-01-chatlog.html | Bootstrap icon removed from footer; lastUpdate updated to 08/07/2026 |
adventuresChatlog.html | Duplicate validator icon removed from footer; mission-statement paragraph removed; Session 5 documented; about-box updated |
Repetition is not decoration — it is trust. When every page in a section shares the same footer, the footer disappears from conscious attention. The student never has to ask “wait, is this still the same site?” because every visual cue confirms it. Inconsistency forces that question — and cognitive load spent on navigation chrome is cognitive load stolen from the content. A sister-section footer standard exists not to look tidy but to let the student ignore it.