msos/docs/newsletter-signup.md

137 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Standard: Newsletter signup (MailerLite)
**Status:** established convention — follow it on every page that should collect email subscribers.
**Audience:** anyone (human or AI agent) adding a newsletter signup to this site.
This documents the one and only way we add a "subscribe to our newsletter" form. It is already
wired to MailerLite through `main.js`; you do **not** write any JavaScript. You only paste HTML.
---
## 1. How it works (read this once)
- All subscribe forms are handled centrally by `setupNewsletter()` in **`main.js`** (section 7).
- On page load it finds every `form.subscribe-form` and `form.subscribe-form-bottom`, and:
- forces the input to `type="email"`, `name="fields[email]"`, `required`,
- inserts a live status message element after the form,
- on submit: validates the email, then POSTs to MailerLite's JSONP endpoint,
- shows a localized success / error / "sending" message (EN / SI / MK).
- MailerLite uses **double opt-in**: the subscriber gets a confirmation email. The success
message intentionally says "check your inbox to confirm".
**MailerLite config** lives in one place — `main.js`, `setupNewsletter()`:
```js
const ML_ACCOUNT = '1253161';
const ML_FORM = '194374752800868123';
```
To point the whole site at a different MailerLite audience/form, change those two values only.
(They come from MailerLite → Forms → Embedded forms → your form → the subscribe URL
`https://assets.mailerlite.com/jsonp/<ACCOUNT>/forms/<FORM>/subscribe`.)
---
## 2. Two non-negotiable requirements
1. **The page must load `main.js`.** Without it the form is dead. Check for:
```html
<script src="{{PREFIX}}main.js"></script>
```
just before `</body>`. If a page has its own script (e.g. `news-filter.js`), load `main.js`
**as well**, before it. (This was the exact bug on the News hub page — it shipped without
`main.js`, so its form — and its header dropdowns — didn't work.)
2. **Use the exact class names.** `subscribe-form-bottom` (full band, the default) or
`subscribe-form` (compact hero). No other class is auto-wired.
---
## 3. The snippet (default — full "band" CTA)
This is the standard block shown above the footer on content pages (News, articles, projects,
get-to-know…). Paste it just before `</main>`.
```html
<section class="newsletter-cta">
<div class="container">
<div class="cta-content">
<h2>{{HEADING}}</h2>
<p>{{SUBTITLE}}</p>
<form class="subscribe-form-bottom">
<div class="input-wrapper">
<input type="email" placeholder="{{PLACEHOLDER}}">
</div>
<button type="submit" class="btn btn-primary-orange">{{BUTTON}}</button>
</form>
<p class="privacy-note">{{PRIVACY_TEXT}} <a href="{{PREFIX}}{{LANG}}/privacy-policy/">{{PRIVACY_LINK}}</a>{{PRIVACY_SUFFIX}}</p>
</div>
</div>
</section>
```
Styling is already defined in `css/pages/_article.css` (`.newsletter-cta`, `.subscribe-form-bottom`,
`.privacy-note`) and is globally available via `main.css`. Nothing to add.
### Compact variant (hero)
When you want it inline under a page hero (as on the get-to-know pages), use the smaller form —
same wiring, no band wrapper:
```html
<form class="subscribe-form">
<input type="email" placeholder="{{PLACEHOLDER}}">
<button type="submit" class="btn btn-primary-orange">{{BUTTON}}</button>
</form>
<p class="privacy-note">{{PRIVACY_TEXT}} <a href="{{PREFIX}}{{LANG}}/privacy-policy/">{{PRIVACY_LINK}}</a>{{PRIVACY_SUFFIX}}</p>
```
---
## 4. `{{PREFIX}}` — the relative path to the site root
Count the folders between the page and the repo root; that's how many `../` you need.
| Page location (example) | `{{PREFIX}}` |
| -------------------------------------------------------- | ------------ |
| `en/index.html` | `../` |
| `en/news/index.html` | `../../` |
| `en/news/proof-of-means-updated-2026/index.html` | `../../../` |
So the privacy link on a depth3 article is `../../../en/privacy-policy/`, and the script tag is
`../../../main.js`. Get this wrong and the link/JS 404s.
---
## 5. Copy — fill the placeholders per language
Always ship all three languages (`en/`, `si/`, `mk/`) — the site is fully mirrored.
**Honest copy only.** Do **not** reuse the legacy `"Join 2,000+ subscribers"` heading that still
appears on some older pages — we don't have that many subscribers. Tailor the heading to the page.
The values below are the current News-section standard:
| Field | EN | MK | SI |
| ------------------ | --------------------------------------------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------ |
| `{{HEADING}}` | Never miss an MSOS update | Не пропуштајте ниту една новост од МСОС | Ne zamudite nobene novice MSOS |
| `{{SUBTITLE}}` | News, announcements and open calls — straight to your inbox. | Вести, соопштенија и отворени повици — директно во вашето сандаче. | Novice, obvestila in razpisi — naravnost v vaš e-poštni predal. |
| `{{PLACEHOLDER}}` | Enter your email | Внесете ја вашата е-пошта | Vnesite svoj e-mail |
| `{{BUTTON}}` | Subscribe | Претплати се | Naroči se |
| `{{PRIVACY_TEXT}}` | We care about your data in our | Ние се грижиме за вашите податоци во нашата | Skrbimo za vaše podatke v naši |
| `{{PRIVACY_LINK}}` | privacy policy | политика за приватност | politiki zasebnosti |
| `{{PRIVACY_SUFFIX}}` | *(empty)* | *(empty)* | `.` |
Change `{{HEADING}}` / `{{SUBTITLE}}` to fit the page's purpose; keep the rest as-is for consistency.
---
## 6. Checklist before you call it done
- [ ] Block pasted before `</main>` (band) or under the hero (compact).
- [ ] `{{PREFIX}}` correct for the page depth (privacy link **and** the `main.js` script tag).
- [ ] Page loads `main.js` (added *before* any page-specific script).
- [ ] All three languages updated (`en/`, `si/`, `mk/`).
- [ ] Copy is honest (no fake subscriber counts).
- [ ] Tested locally: on load the input gets `name="fields[email]"` and a `.subscribe-status`
element appears after the form; an invalid email shows the "valid email" error.
**Do not** submit a real address while testing — it lands in the live MailerLite audience.