Newsletter signup, privacy note, and monthly draft automation - #27
Open
qafears wants to merge 1 commit into
Open
Newsletter signup, privacy note, and monthly draft automation#27qafears wants to merge 1 commit into
qafears wants to merge 1 commit into
Conversation
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
✅ Deploy Preview for qafearless ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.yamland are editable from the/keystaticadmin. No Loops JS embed, so the no-external-scripts rule holds.Placement is a single setting,
footer(a band on every page) orideas(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 incontent/ideas.yaml, then a short "what is new" frominstagram-ledger.jsonand the git history ofcontent/*.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.pynow fails CI if a built form's action drifts fromsettings.yaml, if the honeypot is dropped, or if a page with an email input stops linking the privacy note.tools/test_newsletter_draft.pycovers 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.Still needs you (kept minimal)
LOOPS_API_KEYGitHub 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