Skip to content

Newsletter signup, privacy note, and monthly draft automation - #27

Open
qafears wants to merge 1 commit into
mainfrom
claude/newsletter-signup-drafts-a19ro9
Open

Newsletter signup, privacy note, and monthly draft automation#27
qafears wants to merge 1 commit into
mainfrom
claude/newsletter-signup-drafts-a19ro9

Conversation

@qafears

@qafears qafears commented Jul 22, 2026

Copy link
Copy Markdown
Owner

Adds email capture and a monthly newsletter draft, on Loops (free plan), plus the privacy and consent layer that has to ship with it. This is a structural change, so it is here for your approval on a rendered preview before it merges.

What is in it

1. Signup box (no third-party JavaScript)
A plain HTML form that posts to a Loops "custom form" endpoint. It works with JavaScript disabled as a normal page submit, and enhances to an inline message when JS is on (same pattern and honeypot as the contact form). Every word, and the endpoint URL, live in content/settings.yaml and are editable from the /keystatic admin. No Loops JS embed, so the no-external-scripts rule holds.

Placement is a single setting, footer (a band on every page) or ideas (one block on the Ideas page). Both are built; screenshots are in the chat for you to pick.

2. Privacy note and consent, shipped together
A short, plain-English privacy page (/privacy) covering what is collected, why, that Loops processes it, how to unsubscribe, and how to ask for deletion. A consent line sits next to every email field and links to it, including on the existing contact form.

3. Monthly draft that never sends
tools/newsletter_draft.py (run by .github/workflows/newsletter.yml, monthly) assembles a lead styling tip from your own notes in content/ideas.yaml, then a short "what is new" from instagram-ledger.json and the git history of content/*.yaml. It creates a draft in Loops and opens a GitHub issue to tell you it is ready. It never sends, never schedules. If a month has nothing to report, it writes nothing.

Checks in code, not eyeballed

  • tools/validate_site.py now fails CI if a built form's action drifts from settings.yaml, if the honeypot is dropped, or if a page with an email input stops linking the privacy note.
  • tools/test_newsletter_draft.py covers a normal month, a zero-change month (asserts no draft), and a trims-and-replacements month, and asserts the body has no em dash and no unfilled placeholder. Wired into CI.
  • The new privacy page is added to the Lighthouse shard, so its SEO / accessibility / CLS / TBT gates run too.

Still needs you (kept minimal)

  1. Create the free Loops account and a form, and send me the form endpoint URL (I ship a placeholder until then).
  2. Create a Loops API key so I can store it as the LOOPS_API_KEY GitHub secret.

Click-by-click for both is in the chat. I will also test the Campaign API with a real call as soon as I have the key: no Loops page confirms it is on by default for a new free team, so that call is the test.

A decision for you

CAN-SPAM expects a physical mailing address in the email footer. That belongs in Loops account settings, never in this repo, and a PO box is the usual answer rather than a home address. Flagging so you can decide.

This is good practice, not legal advice.

🤖 Generated with Claude Code

https://claude.ai/code/session_013vhwEDHVM3nfhVpAP9q5Mw


Generated by Claude Code

Email capture on Loops (free plan) with a plain HTML form (no third-party JS),
a plain-English privacy note shipped alongside consent lines, and a monthly
GitHub Actions job that assembles a Loops draft and never sends.

Signup box
- Newsletter.astro posts to settings.newsletter.formEndpoint (Loops custom
  form). Works with JS off as a full-page POST; main.js enhances it to an
  inline AJAX submit with the same _honey honeypot as the contact form.
- All copy and the endpoint live in the newsletter object of settings.yaml
  (schema in keystatic.config.ts), editable from /keystatic.
- Placement is one setting: footer (every page) or ideas (Ideas-page block).
- Real label, aria-live status, visible focus-visible outline.

Privacy and consent
- New privacy page (privacy.astro + content/privacy.yaml), in the sitemap,
  nav, and Lighthouse shard. Consent line beside every email field links to it,
  including the contact form.

Monthly draft (never sends)
- tools/newsletter_draft.py assembles a lead styling tip (from ideas.yaml notes,
  rotated by month) plus a short "what's new" from instagram-ledger.json and the
  git history of content/*.yaml. No news means no draft. Creates a Loops draft
  via POST /v1/campaigns using the LOOPS_API_KEY secret; --dry-run renders
  locally. .github/workflows/newsletter.yml runs it monthly and opens an issue.

Checks in code
- validate_site.py now fails CI if a built form's action drifts from
  settings.yaml, if the honeypot is dropped, or if an email-input page stops
  linking the privacy note.
- tools/test_newsletter_draft.py covers a normal month, a zero-change month
  (no draft), and a trims-and-replacements month; asserts no em dash and no
  placeholder in the body. Wired into CI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013vhwEDHVM3nfhVpAP9q5Mw
@netlify

netlify Bot commented Jul 22, 2026

Copy link
Copy Markdown

Deploy Preview for qafearless ready!

Name Link
🔨 Latest commit 8edd7a7
🔍 Latest deploy log https://app.netlify.com/projects/qafearless/deploys/6a603476017dda000835d36b
😎 Deploy Preview https://deploy-preview-27--qafearless.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants