7.4 KiB
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()inmain.js(section 7). - On page load it finds every
form.subscribe-formandform.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).
- forces the input to
- 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():
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
-
The page must load
main.js. Without it the form is dead. Check for:<script src="{{PREFIX}}main.js"></script>just before
</body>. If a page has its own script (e.g.news-filter.js), loadmain.jsas well, before it. (This was the exact bug on the News hub page — it shipped withoutmain.js, so its form — and its header dropdowns — didn't work.) -
Use the exact class names.
subscribe-form-bottom(full band, the default) orsubscribe-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>.
<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:
<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 depth‑3 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 themain.jsscript 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-statuselement 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.