# Building for htmlhost.co

This project is deployed to [htmlhost.co](https://htmlhost.co), a static host with built-in form handling. Follow these rules when you build or edit it.

## Hosting model

- **Static files only.** HTML, CSS, JS, images, fonts and media are served as-is. No server code runs: there are no API routes, no server-side rendering and no environment secrets. Everything you ship is public, so never put API keys or secrets in client code.
- **No build step runs on the server.** If the project uses a bundler or framework, build it locally and deploy the output folder (for example `htmlhost deploy dist`).
- **Pages map to clean URLs.** `index.html` → `/`, `about.html` → `/about`, `blog/index.html` → `/blog`. Links to `about.html` or `/about/index.html` redirect to the clean URL, so either style works. Relative links to CSS, JS and images work as normal.
- **Reserved paths.** Everything under `/__hh/` belongs to the platform. Don't create files or links there, except posting forms to `/__hh/form`.
- **Not deployed:** `node_modules`, `.git`, `.env*`, lock files, editor folders, and agent files such as `AGENTS.md`, `CLAUDE.md` and `.claude/`. Keep secrets out of any other file, because everything else in the folder is published.

## CLI

- `htmlhost deploy [dir|file]` publishes a folder as a multi-page site, or a single file. The folder is linked to its site through a `.htmlhost` file, so later deploys update the same site. Add `--json` for machine-readable output.
- `htmlhost status` shows what the next deploy would change (new, modified and removed files) and whether the live site changed since the last sync. It writes nothing. Run it before deploying; add `--json` for machine-readable output.
- `htmlhost pull` fetches changes made in the web editor. Run it before editing a linked project so you don't overwrite someone else's changes (deploy also checks for this).
- `htmlhost clone <slug>` downloads an existing site into a new folder.
- `htmlhost upload <file>` uploads media and prints a hosted URL.
- `htmlhost recipients [slug]` lists, adds, or removes form-notification recipients for a site. Subcommands: `add <email>`, `remove <email|id>`, `resend <email|id>`.

## Forms

Any form can deliver submissions to the site owner, with no backend and no third-party service. Messages appear in the owner's dashboard and are emailed to them.

### Making a form deliver

1. Set `action="/__hh/form"` and `method="post"`. Use exactly this relative path, never an absolute URL.
2. Give every field a descriptive `name`. Fields whose names start with `_` are treated as controls and not stored.
3. Add `<input type="hidden" name="_form" value="Contact">` to name the form. One site can run several forms, and the owner sees messages grouped by this name.
4. Add the honeypot. People never see it, but bots fill it in and are discarded:

```html
<div aria-hidden="true" style="position:absolute;left:-10000px;width:1px;height:1px;overflow:hidden"><label>Leave this field empty <input type="text" name="_hh_hp" tabindex="-1" autocomplete="off"></label></div>
```

A complete contact form:

```html
<form action="/__hh/form" method="post">
  <input type="hidden" name="_form" value="Contact">
  <label>Name <input name="name" required></label>
  <label>Email <input name="email" type="email" required></label>
  <label>Message <textarea name="message" required></textarea></label>
  <div aria-hidden="true" style="position:absolute;left:-10000px;width:1px;height:1px;overflow:hidden"><label>Leave this field empty <input type="text" name="_hh_hp" tabindex="-1" autocomplete="off"></label></div>
  <button type="submit">Send</button>
</form>
```

The published site injects a small script that sends the form in the background and shows the result under the form. Don't add your own submit handler for these forms. If you do call `preventDefault()` on the submit event, the platform script steps aside and nothing is sent. Without JavaScript the form still works as a normal POST.

### What visitors see after sending

By default the site owner's settings apply (a confirmation message, or a redirect to one of their pages). Only when the user asks, override this for one form with hidden inputs:

| Input | Effect |
|---|---|
| `<input type="hidden" name="_success" value="…">` | Confirmation message shown under the form (up to 500 characters). The default is "Thanks! Your message has been sent." |
| `<input type="hidden" name="_redirect" value="/thanks">` | Send the visitor to a page **on this site** after a successful send. It must start with `/`. External URLs are ignored |
| `<input type="hidden" name="_error" value="…">` | Message shown instead of the specific reason when sending fails |

If a form sets `_success` or `_redirect`, that form's choice replaces the site default for both. If you add `_redirect`, make sure the target page exists in the project.

### Styling the result

The message appears in a `<p data-hh-form-msg role="status">` appended to the form, with `data-state="success"` or `data-state="error"`. It inherits the page's font and colour. Style it with CSS:

```css
[data-hh-form-msg] { margin-top: 12px; }
[data-hh-form-msg][data-state="error"] { color: #b42318; }
```

### Rules and limits

- **Never collect credentials or payment details.** Fields named like passwords, PINs, card numbers, CVV, SSN, IBAN, seed phrases or one-time codes are rejected, and so are values that look like card numbers. The whole submission fails.
- **File uploads:** add `enctype="multipart/form-data"` to the form and `accept=".jpg,.jpeg,.png,.gif,.webp,.heic,.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt,.csv"` to the file input. Attachments are delivered only on Pro accounts: up to 3 files, 10 MB each and 25 MB in total. Only add a file input if the user asks for one.
- **Volume:** 50 messages a month on Free and 5,000 on Pro, per account. Each visitor can send 5 messages per site every 10 minutes.
- **Only same-site forms send.** Forms on other websites can't post to this site, and on password-protected sites the visitor must unlock the site first.
- **Search forms aren't delivered forms.** Leave `role="search"` and other GET forms alone.
- **Forms don't send from local previews** or from the htmlhost editor preview, only from the published site.

## Keeping these instructions current

These instructions are maintained at https://htmlhost.co/agents.md. Run `htmlhost agents` to refresh them in this project.
