Production site + a small, data-driven stamps catalog (vanilla JS). This repo is intentionally zero-build: no bundler, no framework, and no compile step—just PHP/HTML/CSS/JS that’s easy to host and easy to update.
-
Serve the site over HTTP (don’t use
file://). -
Open it in a browser:
- VS Code task: Open in Browser (prints
http://127.0.0.1:8080/and attempts to open it) - Or any local server:
http://127.0.0.1:8080/(see “Running locally”)
- Production: https://jamisonstamps.com/
- Local:
http://127.0.0.1:8080/
Legacy URLs:
- The old top-level
.html/.htmURLs are intended to permanently redirect to their.phpequivalents (see.htaccess).
Note: in some remote/headless Linux environments the task may not be able to launch a GUI browser; it will still print the URL.
- PHP: simple
.phppages (mostly static HTML), semantic landmarks - CSS: global theme + responsive breakpoints
- JavaScript:
- Site-wide behaviors (mobile menu + scroll-to-top):
js/javascripts.js - Stamps catalog “mini-app” (render/filter/sort/paging + PayPal add-to-cart):
js/stamps.js
- Site-wide behaviors (mobile menu + scroll-to-top):
- PayPal Cart (NCP):
https://www.paypalobjects.com/ncp/cart/cart.js(used only on the stamps catalog) - Formspree: contact form submissions (
contact.php)
Top-level pages:
/
index.php
about.php
contact.php
resources.php
site_map.php
thanks.php
thanks-payment.php
robots.txt
sitemap.xml
Shared assets:
css/
style.css
media-queries.css
js/
javascripts.js
picts/
(site images)
stamps/
(stamp photos)
tools/
optimize-images.cmd
Stamps catalog (separate “mini-app” area):
stamps.php
css/stamps.css
js/stamps.js
api/
stamps.php
filters.php
Why the stamps page is separate: it renders a large inventory list from a JS dataset, supports filter/sort/paging, and integrates commerce widgets. Keeping it isolated avoids complicating the brochure-style pages.
Because the site uses external scripts (PayPal) and browsers restrict some behavior on file:// URLs, run it through an HTTP server.
Run the Open in Browser task (prints http://127.0.0.1:8080/).
From repo root:
php -S 127.0.0.1:8080 -t .Then open http://127.0.0.1:8080/.
Any server that can run PHP works (IIS, Apache, Nginx, etc.).
Most pages follow the same pattern:
- Landmarks:
<header>,<nav>,<main>,<aside>,<footer> - Skip link to
#maincontent - Top “image bar” per page
- Left content + right sidebar layout (sidebar stacks below on smaller screens)
- Theme tokens are CSS variables in
css/style.css(dark “desk/paper” surfaces + postal accent colors) - Responsive rules live in
css/media-queries.css - Mobile menu breakpoint is ~740px (see
js/javascripts.js+ menu CSS)
Progressive enhancements kept intentionally small:
- Mobile menu open/close +
aria-expandedupdates - Click-away and Escape-to-close behavior (mobile only)
- Scroll-to-top affordance
- Page:
stamps.php - Styles:
css/stamps.css - Frontend logic:
js/stamps.js
The stamps page fetches inventory data from same-origin endpoints:
/api/stamps.php(paged results; 25 items per page)/api/filters.php(distinct values for dropdowns)
These endpoints query MariaDB/MySQL (table name is configurable via STAMPS_DB_TABLE) and return JSON to the browser.
Configure connection via environment variables:
STAMPS_DB_HOST(e.g.localhost)STAMPS_DB_PORT(optional; default3306)STAMPS_DB_NAMESTAMPS_DB_USERSTAMPS_DB_PASS
Or provide a full DSN:
STAMPS_DB_DSN(e.g.mysql:host=HOST;port=3306;dbname=DBNAME;charset=utf8mb4)
Alternatively (common on shared hosting), use a server-side config file:
- Production server: copy api/db.config.php.example to
api/db.config.php - Local dev with
php -S: copy api/db.local.php.example toapi/db.local.php
Both files are ignored by git.
SQLite can be used as an editing/source-of-truth format for bulk updates and exports.
- Import JS dataset → SQLite:
- Export SQLite → JSON for the browser:
Example (from repo root):
python3 tools/import_usa_js_to_sqlite.py --input /path/to/USA.js --db data/stamps.sqlite3 --table stamps --country "United States" --replace
python3 tools/export_stamps_json.py --db data/stamps.sqlite3 --table stamps --out stamps/data/stamps.jsonNote: stamps/data/stamps.json is an optional export artifact and is not required for the live site. The live site uses the /api/*.php endpoints at runtime.
This repo’s live /api/*.php endpoints are designed to run on the same server as the website and query a MariaDB/MySQL table (default: stamps, configurable via STAMPS_DB_TABLE).
Important: the browser cannot connect directly to MariaDB. The browser only calls the /api/*.php endpoints.
Run this SQL in phpMyAdmin (or any MySQL client):
Note: the MariaDB price column is stored as a dollar amount (e.g. 0.47).
From repo root:
python3 tools/export_stamps_mariadb_csv.py --db data/stamps.sqlite3 --table stamps --out data/stamps_mariadb.csv- phpMyAdmin → select your database
- Run the “Create the table” SQL above
- Click the
stampstable → Import tab - Choose
data/stamps_mariadb.csv - Format: CSV
- Enable “The first line of the file contains the table column names”
- Run the import
Each stamp inventory row is an object like:
{
country: "United States",
scott: "147",
condition: "Used",
hinged: "Never Hinged",
gum: "No Gum",
grade: "Good/Very Good",
price: 0.47,
location: "NA01-0023-04-01",
paypalId: "VLXK5MGURSGMW"
}Notes:
scottis treated as a collector-facing identifier and supports mixed formats (letters + numbers + suffixes).paypalIdmust match the PayPal configuration; it should be unique per item.
- Text search (Scott #)
- Dropdown filters (condition / hinging / gum / grade)
- Sorting by Scott # or price
- Pagination renders both a top and bottom pager
The page loads PayPal’s cart script and renders <paypal-add-to-cart-button> elements. Initialization is done lazily (via IntersectionObserver, with fallback) to keep large lists responsive.
contact.php posts to Formspree and redirects to thanks.php.
Implementation notes:
- Uses HTML5 input types (
email,tel) + autocomplete hints - Uses required fields + a confirm-email field
- Uses
<fieldset><legend>for the “Contact regarding” radio group
Core pages include:
meta name="description"- canonical URLs (
<link rel="canonical" ...>) - Open Graph + Twitter card metadata
robots.txtblocks non-content utility pages (thank-you pages, etc.)sitemap.xmlis the machine-readable sitemapsite_map.phpis the human-readable site map
When you add/remove a public page, update both site_map.php and sitemap.xml (and consider updating robots.txt if it’s a utility/thank-you page).
Implemented directly in HTML/CSS:
- Skip link (
.skip-link) to#maincontent - Screen-reader-only utility (
.sr-only) for control labels <nav aria-label="Primary navigation">semantics- Mobile menu updates
aria-expanded - Forms use explicit
<label for=...>and appropriate grouping
- Update
<title>andmeta name="description"if the page topic changed - Ensure a single clear
<h1>per page - Confirm nav links work and
aria-current="page"is correct - Check images: decorative should use
alt=""; informative should have meaningfulalt - Verify mobile layout and menu toggle at narrow widths
- Copy the structure from an existing top-level page
- Include
css/style.css,css/media-queries.css, andjs/javascripts.js - Ensure the skip link targets
<main id="maincontent"> - Update navigation across pages (this repo uses copied markup, not templating)
- Add to
site_map.phpandsitemap.xmlif it’s public
- Use the admin entry page to add/update/delete stamps, or edit the backing database table directly.
- The stamps UI loads inventory via the
/api/*.phpendpoints at runtime.
Script: tools/optimize-images.cmd
Requirements:
- ImageMagick installed and available as
magickon PATH
Dry run (writes *.opt.* files next to originals):
tools\optimize-images.cmdOptimize in place (optionally with backups):
tools\optimize-images.cmd /inplace /backupThis is a static site. Deployment is typically “copy files to the web root”:
- Upload HTML/CSS/JS/assets as-is
- Keep paths intact (relative links assume this structure)
- Re-check canonical URLs and
sitemap.xmlafter environment changes
Some markup originated from a legacy template and may contain vendor notices. Those notices are kept intact; changes are layered carefully (theme variables, accessibility improvements, SEO metadata, and the stamps renderer) to avoid regressions.