Airgead uses a deliberately simple architecture — no framework, no bundler, and no npm dependencies. Every interactive behaviour is written in plain JavaScript organised into ES modules that are loaded natively by the browser viaDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/apursley2012/airgead-investment-calculator/llms.txt
Use this file to discover all available pages before exploring further.
<script type="module"> tags. This keeps the project transparent, easy to read, and straightforward to extend without a build pipeline standing in the way.
Module Layers
The codebase is divided into four distinct layers, each with a clearly scoped responsibility:Shell Component
js/components/shell.jsInjects the full page chrome — header, desktop nav, mobile nav with hamburger toggle, footer, and the toast notification container — into every page. Also exports the toast() helper used by page controllers to display feedback messages.Calculation Library
js/lib/calculator.jsContains all compound interest logic, the money() currency formatter, localStorage read/write helpers (saveCurrentCalculation, getCurrentCalculation, getSavedPlans, setSavedPlans), and the downloadCsv() export utility.Chart Library
js/lib/charts.jsHandles all Canvas API drawing. Exports drawGrowthChart() for the single-scenario area/line chart on the Results page, and drawComparisonChart() for the stacked bar chart on the Compare page. Both functions are responsive and re-draw on window resize.Page Controllers
js/pages/Thin scripts, one per HTML page, that wire together the DOM, the calculation library, and the chart library. They contain no business logic of their own — their job is to read inputs, call library functions, and update the UI with the returned values.Page Controller Responsibilities
Each script injs/pages/ corresponds to one HTML page and owns a narrow slice of the user interface:
| Script | Page | Responsibility |
|---|---|---|
calculator.js | calculator.html | Reads form inputs on every input event, calls calculateCompoundInterest(), animates the live balance display, updates quick-stat tiles, and calls saveCurrentCalculation() + redirects to results.html on form submit |
results.js | results.html | Calls getCurrentCalculation() on load, populates the summary stats and year-by-year table, draws the growth chart via drawGrowthChart(), handles the Save Plan button (getSavedPlans / setSavedPlans), and triggers CSV export via downloadCsv() |
compare.js | compare.html | Reads both scenario forms on every input event, calls calculateCompoundInterest() for each, updates the final-balance tiles, and redraws the comparison chart via drawComparisonChart() |
login.js | login.html | Handles the mock sign-in form submission and stores the entered email under the airgead_user localStorage key for demonstration purposes |
noop.js | Static pages | Imported by home.js for pages that only need the shell rendered and have no additional interactive behaviour of their own |
Shell Injection
js/components/shell.js runs immediately on import — the final line of the file calls renderShell() at module evaluation time, so no explicit invocation is needed in page scripts.
When renderShell() executes, it:
- Reads
location.pathnameto determine whether the page is inside the/pages/subdirectory and setsrootto'../'if so, or''if not — ensuring all asset and navigation paths resolve correctly regardless of nesting depth - Iterates the hardcoded
linksarray to build<a>elements, comparing eachhrefagainst the current filename (location.pathname.split('/').pop()) to apply an.activeCSS class to the matching link - Calls
shell.insertAdjacentHTML('afterbegin', ...)to inject the<header>and mobile panel before the[data-shell]wrapper’s existing content - Calls
shell.insertAdjacentHTML('beforeend', ...)to inject the<footer>and toast container after the wrapper’s content
toast(message) export sets the text content of the .toast element, adds a show class, and removes it after 2,400 ms.
Data Flow
The following steps trace the full path from user input on the Calculator page through to a saved plan:- User fills the Calculator form → on every
inputevent,calculator.jscallscalculateCompoundInterest(inputs)from the library and updates the animated live display and quick-stat tiles in real time - User submits the form →
calculator.jscallssaveCurrentCalculation(inputs, data), which serialises inputs, the full results array, and a timestamp intolocalStorageunderairgead_current_calc, then redirects the browser toresults.html - Results page loads →
results.jsimmediately callsgetCurrentCalculation()to read back the stored object; if the key is absent or unparseable, the user is redirected back tocalculator.html - Results page renders →
results.jspopulates the summary stat cards and the year-by-year<table>, then callsdrawGrowthChart(canvas, results)to render the area chart via the Canvas API - User clicks Save Plan →
results.jscallsgetSavedPlans()to load the existing array, pushes a new plan object (containingid,date,name, and the fulldataobject), and callssetSavedPlans(plans)to write the updated array back tolocalStorageunderairgead_saved_plans
Because Airgead uses native ES modules (
<script type="module">), the project must be served over HTTP or HTTPS — it cannot be opened directly from the filesystem via a file:// URL. Modern browsers block cross-origin module imports when using the file:// protocol. Running a local development server (such as VS Code’s Live Server extension, Python’s http.server, or any static file server) is all that is needed.