Newsletter
Newsletter subscriptions work with or without an account: anonymous visitors subscribe with just an email (double opt-in), while identified users manage subscriptions and preferences in place. All state handling — has the visitor entered an email, are they subscribed, is the subscription confirmed — is exposed through <u-conditional-render>.
View this guide as MarkdownSubscribe form
An anonymous subscribe widget: email field, one checkbox per newsletter with nested topic preferences, an explicit consent checkbox, and a submit button. After subscribing, the double-opt-in email is sent automatically. The 'already subscribed' link uses for="login" to switch into management mode instead.
- Works logged-out — email is enough; no account required
- Newsletter + preference checkboxes — per-newsletter topics via preference-identifier
- Consent checkbox — u-newsletter-consent-checkbox stores explicit consent
- State-driven UI — u-conditional-render on newsletter.loggedIn / hasEmail
<u-newsletter-root>
<!-- Shown until the visitor identifies themselves (subscribes or logs in) -->
<u-conditional-render when="newsletter.loggedIn" not>
<u-email-field class-name="px-4 py-2 border border-border rounded-lg w-full"></u-email-field>
<u-error-message for="email" class-name="text-danger text-sm"></u-error-message>
<!-- Offered once an email is typed: jump to subscription management instead -->
<u-conditional-render when="newsletter.hasEmail">
<u-submit-button
for="login"
class-name="mt-2 text-sm font-medium text-primary hover:underline cursor-pointer">
Already subscribed? Manage your subscriptions
</u-submit-button>
</u-conditional-render>
<div class="mt-4 flex flex-col gap-3">
<p class="text-sm font-medium text-text-light">Select newsletters</p>
<div class="rounded-xl border border-border p-3">
<label class="flex cursor-pointer items-center justify-between p-2">
<span class="font-semibold">Hammarby Newsletter</span>
<u-newsletter-checkbox
internal-name="ns_with_prefs"
checked="true"
class-name="appearance-none relative w-11 h-6 bg-border rounded-full cursor-pointer transition-colors checked:bg-primary after:content-[\'\'] after:absolute after:top-0.5 after:left-0.5 after:w-5 after:h-5 after:bg-white after:rounded-full after:shadow after:transition-transform checked:after:translate-x-5"
></u-newsletter-checkbox>
</label>
<div class="ml-2 mt-1 flex flex-col gap-2">
<label class="flex cursor-pointer items-center gap-3 text-sm">
<u-newsletter-preference-checkbox
internal-name="ns_with_prefs"
preference-identifier="club_news_pref"
checked="true"></u-newsletter-preference-checkbox>
Club News
</label>
<label class="flex cursor-pointer items-center gap-3 text-sm">
<u-newsletter-preference-checkbox
internal-name="ns_with_prefs"
preference-identifier="player_news_pref"
checked="true"></u-newsletter-preference-checkbox>
Player News
</label>
<label class="flex cursor-pointer items-center gap-3 text-sm">
<u-newsletter-preference-checkbox
internal-name="ns_with_prefs"
preference-identifier="random_news_pref"></u-newsletter-preference-checkbox>
Other News
</label>
</div>
<u-error-message for="ns_with_prefs" class-name="text-danger text-sm"></u-error-message>
</div>
<div class="rounded-xl border border-border p-3">
<label class="flex cursor-pointer items-center justify-between p-2">
<span class="font-semibold">General Updates</span>
<u-newsletter-checkbox
internal-name="ns_without_prefs"
class-name="appearance-none relative w-11 h-6 bg-border rounded-full cursor-pointer transition-colors checked:bg-primary after:content-[\'\'] after:absolute after:top-0.5 after:left-0.5 after:w-5 after:h-5 after:bg-white after:rounded-full after:shadow after:transition-transform checked:after:translate-x-5"
></u-newsletter-checkbox>
</label>
<u-error-message for="ns_without_prefs" class-name="text-danger text-sm"></u-error-message>
</div>
<!-- Explicit consent, stored with the subscription -->
<label class="flex cursor-pointer items-start gap-3 text-sm text-text-light">
<u-newsletter-consent-checkbox class-name="mt-0.5"></u-newsletter-consent-checkbox>
I agree to receive the selected newsletters and accept the privacy policy.
</label>
<u-submit-button
class-name="w-full bg-accent hover:bg-accent-hover text-white font-semibold py-2.5 rounded-lg transition cursor-pointer mt-2">
Subscribe
</u-submit-button>
<u-error-message for="general" class-name="text-danger text-sm"></u-error-message>
</div>
</u-conditional-render>
<u-conditional-render when="newsletter.loggedIn">
<p class="text-success-text">
You are identified for newsletter management — see the preference center below.
</p>
</u-conditional-render>
</u-newsletter-root> Select newsletters
You are identified for newsletter management — see the preference center below.
Preference center
Subscription management for an identified visitor: toggle subscriptions, resend the double-opt-in email while unconfirmed, and edit topic preferences that save on change. Visitors are identified by account sign-in, by subscribing, or by the preference token in newsletter email links — u-newsletter-logout-button switches to a different email.
- Toggle subscriptions — u-newsletter-toggle-subscription-button with per-state styling
- Double opt-in handling — newsletter.confirmed state + resend-DOI button
- Preference token flow — email links identify the visitor without a password
- Switch email — u-newsletter-logout-button clears the newsletter session
<u-newsletter-root>
<u-conditional-render when="newsletter.loggedIn">
<!-- The identified email, with a "switch email" logout button -->
<div class="relative mb-6">
<u-email-field
class-name="px-4 py-2 pr-10 border border-border rounded-lg w-full bg-background-light"
disabled>
<u-newsletter-logout-button
class="absolute right-3 top-1/2 -translate-y-1/2 z-10"
class-name="flex items-center justify-center w-6 h-6 rounded-full cursor-pointer text-text-muted hover:bg-background-muted hover:text-text"
title="Use a different email">
✕
</u-newsletter-logout-button>
</u-email-field>
</div>
<div class="flex flex-col gap-4">
<div class="rounded-xl border border-border p-4">
<div class="flex items-center justify-between">
<span class="font-semibold">Hammarby Newsletter</span>
<u-newsletter-toggle-subscription-button
internal-name="ns_with_prefs"
class-name="text-sm font-medium rounded-lg px-3 py-1 cursor-pointer"
subscribe-class-name="text-white bg-accent hover:bg-accent-hover"
unsubscribe-class-name="text-danger border border-error-border hover:bg-error-bg"
></u-newsletter-toggle-subscription-button>
</div>
<!-- Double opt-in: subscribed but not yet confirmed -->
<u-conditional-render when="newsletter.subscribed" is="ns_with_prefs">
<u-conditional-render when="newsletter.confirmed" is="ns_with_prefs" not>
<div
class="mt-3 flex items-center justify-between gap-3 rounded-lg bg-warning-bg px-4 py-3 text-sm text-warning-text">
<span>Please confirm your subscription — we sent you an email.</span>
<u-newsletter-resend-doi-button
internal-name="ns_with_prefs"
class-name="whitespace-nowrap rounded-lg border border-warning-border px-3 py-1 text-sm font-medium cursor-pointer hover:bg-warning-border/40"
></u-newsletter-resend-doi-button>
</div>
</u-conditional-render>
</u-conditional-render>
<div class="mt-4 border-t border-border pt-4">
<p class="mb-3 text-sm font-medium text-text-light">Preferences</p>
<div class="flex flex-col gap-2">
<label class="flex cursor-pointer items-center gap-3 text-sm">
<u-newsletter-preference-checkbox
internal-name="ns_with_prefs"
preference-identifier="club_news_pref"></u-newsletter-preference-checkbox>
Club News
</label>
<label class="flex cursor-pointer items-center gap-3 text-sm">
<u-newsletter-preference-checkbox
internal-name="ns_with_prefs"
preference-identifier="player_news_pref"></u-newsletter-preference-checkbox>
Player News
</label>
<label class="flex cursor-pointer items-center gap-3 text-sm">
<u-newsletter-preference-checkbox
internal-name="ns_with_prefs"
preference-identifier="random_news_pref"></u-newsletter-preference-checkbox>
Other News
</label>
</div>
</div>
</div>
<div class="rounded-xl border border-border p-4">
<div class="flex items-center justify-between">
<span class="font-semibold">General Updates</span>
<u-newsletter-toggle-subscription-button
internal-name="ns_without_prefs"
class-name="text-sm font-medium rounded-lg px-3 py-1 cursor-pointer"
subscribe-class-name="text-white bg-accent hover:bg-accent-hover"
unsubscribe-class-name="text-danger border border-error-border hover:bg-error-bg"
></u-newsletter-toggle-subscription-button>
</div>
<u-conditional-render when="newsletter.subscribed" is="ns_without_prefs">
<u-conditional-render when="newsletter.confirmed" is="ns_without_prefs" not>
<div
class="mt-3 flex items-center justify-between gap-3 rounded-lg bg-warning-bg px-4 py-3 text-sm text-warning-text">
<span>Please confirm your subscription — we sent you an email.</span>
<u-newsletter-resend-doi-button
internal-name="ns_without_prefs"
class-name="whitespace-nowrap rounded-lg border border-warning-border px-3 py-1 text-sm font-medium cursor-pointer hover:bg-warning-border/40"
></u-newsletter-resend-doi-button>
</div>
</u-conditional-render>
</u-conditional-render>
</div>
</div>
</u-conditional-render>
<u-conditional-render when="newsletter.loggedIn" not>
<p class="text-text-light">
Not identified yet — subscribe above, sign in, or open the link from a newsletter email (the
preference token in the URL identifies you without a full account login).
</p>
</u-conditional-render>
</u-newsletter-root> Preferences
Not identified yet — subscribe above, sign in, or open the link from a newsletter email (the preference token in the URL identifies you without a full account login).