Private, shareable, trackable single-file pages. No auth. No CMS. No build step.
By Ask the Human LLC -- AI-accelerated engineering for people who ship.
You make a page. You get a URL. You send it to someone. You know when they open it. That's it.
I built this system to solve a specific problem: I need to share polished documents with clients, investors, and collaborators without friction -- no logins, no "request access" buttons, no Google Docs link that expires. Just a URL that works on any device, looks good when printed, and tells me when someone actually reads it.
Over 6 weeks, this turned into 60+ pages across two production domains, covering everything from sales one-pagers to interactive pricing calculators to legal contracts. This repo packages the system so you can stand up your own.
Live examples:
- Each page is one
index.htmlfile with inline CSS/JS. No framework, no build step, no dependencies. - URLs are the access control.
yoursite.com/page-name-a1b2c3d4/-- the hash is unguessable. No auth needed. - View tracking built in. Append
?ref=nameto know who opened it and when. First views fire Slack and/or Teams alerts. - Server-synced checklists. Checkboxes persist across devices via Netlify Blobs or Cloudflare KV.
- Works on Netlify or Cloudflare Pages. Both implementations included. Stand it up in 15 minutes.
- Designed for AI. Drop this repo into your AI coding assistant of choice, tell it where to deploy, and start shipping pages.
STATE.mdandROADMAP.mdgive it full project context across sessions.
- The Decisions (and Why)
- What's In This Repo
- Page Types
- How It Works
- Setup
- Creating Your First Page
- Advanced Patterns
Every page lives at https://yourdomain.com/page-name-a1b2c3d4/. That 8-character hex hash is the access control. You need the URL to find the page. The root of the site says "Nothing here."
Why this works better than passwords or login walls:
- Zero friction for the recipient. You text someone a link. They tap it. They're reading your proposal. No "sign up to view" interstitial, no password to remember, no SSO redirect. This matters because the person you're sending it to is busy. Every click you add between "received link" and "reading your thing" is a chance they'll do it later (meaning never).
- Unguessable by default.
secrets.token_hex(4)gives you 4 billion possible URLs per page name. Nobody is stumbling onto your page. For higher-sensitivity docs (contracts, BAAs), usetoken_hex(16)for 128-bit hashes. - Shareable via any channel. Text, email, Slack, iMessage, WhatsApp -- a URL works everywhere. Try that with a password-protected PDF.
- No user database to maintain. No accounts, no password resets, no GDPR deletion requests. The URL is the credential.
The tradeoff: if someone forwards the URL, the new person can see it too. For my use cases (proposals, meeting prep, pricing tools), that's a feature, not a bug. If your document is so sensitive that forwarding is a threat, you need a different system.
Each page is one index.html file with inline CSS and JS. You can open it from your filesystem and it works.
Why:
- The page IS the deliverable. You can email the HTML file. You can print it. You can host it on any static file server. There's no webpack, no node_modules, no "run
npm installfirst." This makes AI coding assistants dramatically more effective -- there's no framework to learn, no build pipeline to debug. You describe what you want and you get a working page. - No dependency rot. There's no package.json for the page itself. No breaking changes from a React upgrade. A page you build today works exactly the same in 5 years.
- Instant deploys. Deploying is copying files.
netlify deploy --prod --dir=.and you're live. No build step, no CI pipeline, no "deploy failed because Node 18 deprecated something." - Easy to customize per-client. Each page is independent. Change one client's brand colors without touching anyone else's page. Copy a page, modify it, deploy it -- no shared components to worry about.
The tradeoff: you repeat CSS across pages. That's fine. The total site is still tiny (HTML compresses well), and the independence of each page is worth more than DRY CSS.
The system works on either platform. This repo defaults to Netlify because the free tier includes:
- Edge Functions -- run code on every request without adding latency (view tracking)
- Blobs -- serverless key-value storage (checklists, view counts)
- One-command deploy --
netlify deploy --prod
I also run a second instance on Cloudflare Pages using Workers and KV. The cloudflare/ directory has the equivalent infrastructure. The page templates work on either platform without modification -- only the serverless functions differ.
Action items that persist across devices. You check a box on your phone, your colleague sees it on their laptop. Every toggle records who checked it and when.
The problem this solves: I send a meeting debrief to my CEO with 8 action items. He checks 3 of them from his phone on the way to a meeting. I open the same page on my laptop and see which ones he's handled. No "did you see my email" follow-up needed.
How it works:
- Click a checkbox -> POST to serverless function -> stored in Blobs/KV
- Page load -> GET from serverless function -> restore checkbox state
- Offline fallback -> localStorage (syncs on next connection)
- First click prompts for your name (stored in localStorage)
An edge function runs on every page request. It doesn't add latency -- it fires after the response is already sent (waitUntil). It tracks:
- Total views per page
- Per-recipient views via
?ref=nameparameter - First/last view timestamps
On the first view from a named ref, it fires alerts to Slack (Block Kit) and/or Microsoft Teams (Adaptive Cards) -- configure either or both via environment variables:
👀 First view -- dan opened your-proposal Ref:
danWhen: Mar 10, 2026, 2:15 PM ET
Why this matters: You send a proposal to a client. 10 minutes later, your Slack or Teams lights up. They opened it. You now know they're thinking about it. You can follow up at the right time -- not too early (annoying), not too late (they forgot).
The ?ref=name parameter is how you personalize tracking. Send Dan ?ref=dan and send Sarah ?ref=sarah. Same page, different tracking. The share tool at /share generates these links for you.
One-pagers use @page { size: letter; margin: 0.5in }. They're designed to print on a single sheet of paper.
The context: You're in a meeting. Someone says "can you send me that overview?" You want them to be able to print it right there, or save it as a PDF that looks intentional -- not like a screenshot of a website. print-color-adjust: exact forces colors in print. The toolbar with "Print / Save PDF" disappears when printing.
Every page uses the same variable system:
:root {
--accent: #0f766e; /* swap this one value for client brand */
--accent-dark: #134e4a;
--gold: #d97706;
...
}Dark mode is a [data-theme="dark"] attribute that overrides every variable. OS preference is detected on first visit. The toggle persists in localStorage.
The workflow: Copy a template, change --accent to your client's brand color, fill in the content, deploy. The entire page rebrands.
hash-pages/
README.md # You're reading it
SETUP.md # Step-by-step deployment guide
TEMPLATES.md # Detailed catalog of every template type
index.html # Root page ("Nothing here.")
netlify.toml # Edge function config
package.json # @netlify/blobs dependency
netlify/
functions/
checklist.mjs # Server-synced checkbox persistence
views.mjs # View stats API (HTML + JSON)
share.mjs # Tracking link generator tool
edge-functions/
track-views.ts # View tracking + Slack alerts
cloudflare/ # Equivalent infrastructure for Cloudflare Pages
functions/
_middleware.js # View tracking middleware
api/views.js # View stats API
api/share.js # Tracking link generator
workers/checklist/ # Separate worker for checklist sync
wrangler.toml
src/index.js
_templates/
landing-page/ # General purpose page (nav, hero, cards, CTA)
one-pager/ # Print-first, letter-size, single sheet
calculator/ # Interactive tool with sliders and live results
proposal/ # B2B proposal with pricing table
deal-package/ # Multi-page linked documents (proposal + contract + BAA)
meeting-prep/ # Pre-meeting briefing with bios and soundbites
debrief/ # Post-meeting analysis with action items
examples/
acme-calculator-7f3a1b2c/ # Working calculator example
acme-onepager-4d8e9f01/ # Working one-pager example
acme-proposal-b2c4d6e8/ # Working proposal example
When to use: Your client needs to play with numbers. Pricing models, ROI calculators, fee breakdowns, financial projections.
Why it's powerful: Instead of sending a spreadsheet, you send a URL. The client slides the inputs, sees their specific scenario, and shares a link with their exact configuration baked into the URL hash. They can export to CSV. They can compare scenarios. And you see the moment they open it.
Key patterns:
- Range sliders with live-updating value labels
- Toggle buttons for discrete options (monthly/quarterly/annual)
- Results grid that recomputes on every input change
- Scenario comparison cards (conservative / base / aggressive)
- Bar charts built entirely in CSS (no charting library)
- Share via URL hash:
btoa(JSON.stringify(config))encoded in#cfg= - CSV export via
Blob+ dynamic download link update()function bound to all inputs -- single source of truth
When to use: Someone asks "what do you do?" and you need a polished answer that fits on one piece of paper and looks good on a phone.
Why print-first: The recipient might print this for a board meeting, save it as a PDF for their files, or read it on their phone in a taxi. All three need to work. Letter-size layout with forced print colors means the PDF looks identical to the screen. Dense, information-rich -- every pixel earns its space.
Key patterns:
@page { size: letter; margin: 0.5in }for printmax-width: 8.5inscreen wrapper- Fixed toolbar with Print button (hidden
@media print) -webkit-print-color-adjust: exacton colored elements- 9-13px font sizes to fit letter size
- Grid layouts collapse to single column on mobile
When to use: Sending a scoped engagement, contract, or compliance document. Often part of a deal package (see below).
Why HTML over PDF: The recipient can read it on any device without a PDF viewer. It's searchable. It has working links. And you can update it after sending -- the URL stays the same, the content can be refreshed.
Key patterns:
- Problem/solution two-column layouts with color-coded borders
- Pricing tables with story point columns
- CTA box at the bottom
- For legal docs: wider margins (
0.75in), larger font (11pt) in print
When to use: You have 3-4 related documents that form a package -- proposal + contract + BAA, or role description + engineering deep dive + candidate assessment.
Why linked pages instead of one long page: Each document has a different audience and purpose. The proposal goes to the decision-maker. The contract goes to legal. The BAA goes to compliance. They need to be independently printable, independently shareable, but cross-referenced.
Key patterns:
- Toolbar on every page linking to all siblings
- Relative paths (
../sibling-slug/) so the package works locally - Mobile hamburger menu for the toolbar links
- Current page shown as inactive in the nav
- Print hides the toolbar (
no-printclass)
When to use: Before a meeting (prep) and after (debrief). The prep has bios, talking points, and a join link. The debrief has the strategic read, action items, and cleaned transcript.
Key patterns:
- Join bar at top with meeting link and dial-in
- Color-coded callout boxes (green = framing, orange = risks, blue = soundbites)
- Bio cards with photos
- Server-synced checklists for action items
- Prep page links forward to debrief; debrief links back to prep
- Generate a hash:
python -c "import secrets; print(secrets.token_hex(4))" - Create a folder:
my-page-a1b2c3d4/ - Copy a template and customize it
- Deploy:
netlify deploy --prod --dir=. --functions=netlify/functions - Share:
https://yourdomain.com/my-page-a1b2c3d4/
Visitor opens page
|
Edge function intercepts (zero latency -- response sent immediately)
|
Skip if: root page, static asset, bot, non-GET, non-200
|
Extract slug from path: /my-page-a1b2c3d4/ -> my-page-a1b2c3d4
Extract ref from ?ref=dan -> dan (or "_none")
|
Increment view count in Blobs/KV (async, doesn't block)
|
If first view for this ref:
Check dedup sentinel key (_alerted:slug:ref)
If not already alerted -> fire Slack Block Kit message
Set sentinel to prevent duplicate alerts
User clicks checkbox
|
Toggle checked state
POST to /functions/checklist?page=slug
Body: {key: "item-id", checked: true, by: "Dan"}
|
Function reads existing state from Blobs/KV
Upserts the key with {checked, by, at: timestamp}
Returns full updated state
|
UI updates with "Dan, Mar 10 2:15 PM" metadata
You create a page -> deploy
You open /share tool -> paste URL + recipient name
Tool generates: https://yourdomain.com/my-page-a1b2c3d4/?ref=dan
You send the link via text/email/Slack
|
Dan opens the link
Edge function logs: first view, ref=dan
Slack fires: ":eyes: dan opened my-page-a1b2c3d4"
|
You check stats: /views?page=my-page-a1b2c3d4
See: total views, dan's view count + timestamps
See SETUP.md for the full step-by-step guide. The short version:
- Fork this repo (keep it private)
- Create a Netlify site and link it
- Set environment variables (Blobs token, Slack bot token)
netlify deploy --prod --dir=. --functions=netlify/functions- Point your domain at Netlify
# Generate a hash
python -c "import secrets; print(secrets.token_hex(4))"
# Output: 7f3a1b2c
# Copy a template
cp -r _templates/one-pager my-product-7f3a1b2c
# Edit the page
# (open my-product-7f3a1b2c/index.html in your editor or let AI generate it)
# Deploy
netlify deploy --prod --dir=. --functions=netlify/functions
# Share
echo "https://yourdomain.com/my-product-7f3a1b2c/"See TEMPLATES.md for detailed guidance on each template type, including what to customize and what to leave alone.
Encode the current input state in the URL hash so anyone with the link sees the same configuration:
function shareLink() {
const cfg = { /* all slider/toggle values */ };
const encoded = btoa(unescape(encodeURIComponent(JSON.stringify(cfg))));
const url = location.origin + location.pathname + '#cfg=' + encoded;
navigator.clipboard.writeText(url);
}
function loadFromHash() {
if (location.hash.startsWith('#cfg=')) {
const cfg = JSON.parse(decodeURIComponent(escape(atob(location.hash.slice(5)))));
// Apply cfg values to all inputs
}
}When documents belong together, add a cross-linking toolbar to every page:
<div class="toolbar no-print">
<div class="toolbar-links">
<a href="../proposal-a1b2c3d4/">Proposal</a>
<a href="../contract-e5f6g7h8/">Contract</a>
<span class="current">BAA</span> <!-- current page -->
</div>
<button onclick="window.print()">Print / Save PDF</button>
</div>Use ../ relative paths so the package works both deployed and locally.
Every page needs these for link previews in Slack, iMessage, etc.:
<meta name="robots" content="noindex, nofollow">
<meta name="description" content="One sentence.">
<meta property="og:title" content="Page Title">
<meta property="og:description" content="Short description.">
<meta property="og:type" content="article">
<meta property="og:url" content="https://yourdomain.com/slug/">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Page Title">
<meta name="twitter:description" content="Short description.">noindex, nofollow keeps search engines away. The OG/Twitter tags make link previews look professional.
This system was designed to work with AI coding assistants. The prompt pattern:
Build me a [page type] for [subject]. Use the template at
_templates/[type]/index.html. Brand color: #2563eb. Content: [your content].
Because each page is self-contained HTML with no framework dependencies, AI assistants can generate complete, working pages in a single pass. No build errors, no import resolution, no "install these 14 packages first."
This system has shipped 60+ pages across 2 domains in 6 weeks. Page types include:
- 8 interactive calculators and tools
- 12 sales one-pagers and product overviews
- 6 proposals (including multi-page deal packages with contracts and BAAs)
- 10 meeting preps and debriefs
- 8 feature indexes and engineering deep-dives
- 4 resumes and portfolio pages
- Assorted: surveys, release notes, project updates, checklists, thought leadership
Average time to ship a new page from an existing template: 15-30 minutes.
Ask the Human LLC -- Bert Carroll, fractional CTO and AI-accelerated builder. Story-point pricing, outcome-driven delivery.
This repo is a working example of how I build: AI-assisted development with clear architectural decisions, documented reasoning, and fast iteration. If you want pages like these built for your business, get in touch.
MIT. See LICENSE.