Newsletter Signup
A newsletter signup is a small <form> composition that collects an email address for subscription. It supports four explicit states — idle, submitting, success, and error — surfaced as data-state and reflected in the input/button disabled state and live-region messages.
Use it in marketing footers, blog sidebars, splash pages, and dedicated subscription landing pages.
Example
Show demo markup
<div class="newsletter-signup" aria-label="an email subscription form composition with idle, submitting, success, and error states">NewsletterSignup</div>
Usage example
<script lang="ts">
import NewsletterSignup from './NewsletterSignup.svelte';
let state = $state<'idle' | 'submitting' | 'success' | 'error'>('idle');
</script>
<NewsletterSignup
label="Subscribe to newsletter"
heading="Stay informed"
description="Monthly product updates."
emailLabel="Email address"
emailPlaceholder="you@example.com"
submitLabel="Subscribe"
successMessage="Thanks for subscribing!"
errorMessage="Something went wrong. Please try again."
{state}
onsubmit={(e) => { e.preventDefault(); state = 'submitting'; /* … */ }}
/>
Show Svelte source
// In your Svelte component:
import NewsletterSignup from "lily-design-system-svelte-headless/components/NewsletterSignup/NewsletterSignup.svelte";
<NewsletterSignup>
<!-- content -->
</NewsletterSignup>
Usage
Idle state with heading and description:
<NewsletterSignup
label="Subscribe to the newsletter"
heading="Stay in the loop"
description="One email per week, no spam."
emailLabel="Email address"
submitLabel="Subscribe"
onsubmit={handleSubmit}
/>
Success state:
<NewsletterSignup
label="Subscribe to the newsletter"
emailLabel="Email address"
submitLabel="Subscribe"
state="success"
successMessage="Thanks! Please check your inbox to confirm."
/>
Props
label: string (required) -- aria-label for the form
heading: string (optional) -- visible heading
description: string (optional) -- body text below the heading
emailLabel: string (required) -- visible label for the email input
emailPlaceholder: string (optional)
submitLabel: string (required) -- submit button text
state: "idle" | "submitting" | "success" | "error" (default: "idle")
successMessage: string (optional) -- confirmation message when state is "success"
errorMessage: string (optional) -- error message when state is "error"
onSubmit / onsubmit: callback (optional) -- submit handler
...restProps: any additional HTML attributes
Keyboard Interactions
- Tab: Move focus through the email input and submit button
- Enter inside the email input: Submit the form
- (All handled natively by
<form>, <input>, and <button>)
ARIA
aria-label on the <form> provides the form's accessible name
data-state reflects the current state ("idle" | "submitting" | "success" | "error")
- Email input and submit button are
disabled when state is "submitting"
- Success message:
role="status" + aria-live="polite" (non-disruptive)
- Error message:
role="alert" (immediately announced)
- Both messages use
hidden when not in their corresponding state
When to Use
- Use to collect newsletter subscriptions in marketing surfaces.
- Use whenever you need an explicit submit/loading/success/error progression for a single email field.
When Not to Use
- Do not use for multi-field signup or registration flows — use Form with multiple Field components.
- Do not use for in-app notification preferences — use a settings UI.
Headless
Renders a <form> with <label> + <input type="email"> + <button type="submit">, plus conditional success/error message paragraphs. Visual styling, layout direction (row/column), inline placement of messages, and submit logic are the consumer's responsibility.
Styles
Consumer CSS targets the newsletter-signup class on the form. Use the data-state attribute selector to style the form per state. Style the inner newsletter-signup-success and newsletter-signup-error classes for status messaging.
Testing
- Verify the component renders a
<form> element with class newsletter-signup
- Verify
aria-label is present and equals the label prop
- Verify
data-state reflects the state prop
- Verify the email input and submit button are
disabled when state is "submitting"
- Verify the success message renders with
role="status" + aria-live="polite" and is hidden unless state is "success"
- Verify the error message renders with
role="alert" and is hidden unless state is "error"
- Verify the submit handler fires on form submission
Advice
- Designers: Use a clear, single-line layout where possible. Reserve vertical space for the success and error messages so the layout does not jump.
- Developers: Always supply
label, emailLabel, and submitLabel (required). Do not announce success with role="alert" — that is reserved for errors. Disable the submit while submitting to avoid duplicate calls.
Implementation Notes
- Uses a native
<form> element with aria-label describing the form's purpose
state is exposed as data-state and gates disabled on the email input and submit button (when "submitting")
- Success message uses
role="status" + aria-live="polite" so screen readers announce non-disruptive confirmation
- Error message uses
role="alert" so screen readers announce errors immediately
- Both messages are conditionally
hidden based on state
- All visible strings (heading, description, email label, submit label, success/error) are provided as props for i18n
- Submit handling is delegated to the consumer via
onSubmit / onsubmit
Related components
form — a form element for collecting and submitting user data
email-input — an input for entering an email address <input type="email">
call-to-action — a prominent prompt encouraging user action
References
- WAI-ARIA
role="status": https://www.w3.org/TR/wai-aria-1.2/#status
- WAI-ARIA
role="alert": https://www.w3.org/TR/wai-aria-1.2/#alert
- MDN
aria-live: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-live
- Mozilla Protocol Design System: https://protocol.mozilla.org/
Canonical contract
Generated from this component's canonical metadata — the machine-checked source the implementations are held to.
Metadata
- Component: newsletter-signup
- PascalCase: NewsletterSignup
- Description: an email subscription form composition with idle, submitting, success, and error states
- Status: beta — implemented and unit-tested in all seven frameworks with an axe-clean demo page; not yet exercised in composed flows
- HTML tag: <form>
- CSS class: .newsletter-signup
- Interactive: yes
ARIA
- aria-label on the <form> provides the form's accessible name (required)
- data-state reflects current state
- Success: role="status" + aria-live="polite"
- Error: role="alert"
- Hidden attribute toggles message visibility per state
Keyboard
- Tab: Move focus through the email input and submit button
- Enter inside the email input: Submit the form
- (All handled natively by <form>, <input>, and <button>)
Props
label: string (REQUIRED) -- aria-label for the form heading: string (optional) -- visible heading description: string (optional) -- body text emailLabel: string (REQUIRED) -- visible label for email input emailPlaceholder: string (optional) submitLabel: string (REQUIRED) -- submit button text state: "idle" | "submitting" | "success" | "error" (default "idle") successMessage: string (optional) errorMessage: string (optional) onSubmit / onsubmit: callback (optional) ...restProps: any additional HTML attributes