A split newsletter signup block built with Tailwind CSS: a desk photo with a frosted "24,000+ readers" badge fills one half, and the other half holds the pitch, topic chips, an email field and a subscribe button. Vanilla JavaScript validates the form, shows a loading state and swaps in a confirmation panel. It fits blog footers, landing pages and editorial sites.
How it works
The root is section.nsi-newsletter. Inside, a rounded card becomes a two-column grid at the md breakpoint; on phones the photo sits on top with min-h-64. The img is absolutely positioned with object-cover so it always fills its column, and a gradient overlay keeps the white social-proof badge readable.
The form (data-nsi-form) has novalidate, so the script controls messages. Topic chips are visually hidden checkboxes; Tailwind's peer-checked: variant turns the following span into a filled pill. On submit the script prevents the default action and checks three things: the email is not empty, it matches a simple email pattern, and at least one topic is ticked. Errors go into data-nsi-error, the email input gets aria-invalid="true" (styled red through the aria-invalid: variant) and focus moves to the field that needs attention.
A valid submission adds nsi-loading to the form, which reveals the spinning .nsi-spinner, disables .nsi-submit and changes the label to "Subscribing...". After a simulated 900 ms request the form gets hidden, the data-nsi-success panel appears with the address filled into data-nsi-email-out, and focus moves to its heading.
Customization
- Replace the
setTimeoutin thesubmithandler ofscript.jswith afetch()call to your newsletter provider, and show the success panel when it resolves. EMAIL_PATTERNat the top ofscript.js: tighten or loosen the email check.- Add or remove topic chips by copying a
labelwith aninput name="topics"; the "Pick at least one topic" rule applies to all of them. bg-emerald-950on the section andbg-[#f4efe4]on the card: change the outer and form-side background colors.bg-amber-400on.nsi-submit: recolor the call-to-action button.md:grid-cols-2on the card: switch tomd:grid-cols-[2fr_3fr]to give the form more room, or move the imagedivafter the form to place the photo on the right.900inscript.js: the simulated delay in milliseconds.
Accessibility
The email field has a real label, autocomplete="email" and aria-describedby pointing to the error paragraph, which is an aria-live="polite" region so messages are announced. Topic chips are native checkboxes inside a fieldset with a legend, and peer-focus-visible: rings show keyboard focus. The success heading has tabindex="-1" and receives focus. Decorative avatars, icons and the overlay are aria-hidden, the photo has a descriptive alt, and the spinner and success animations stop under prefers-reduced-motion.
Usage notes
The photo comes from Unsplash; replace it with your own image. The preview never sends data. Several signup blocks can share a page because each .nsi-newsletter is wired up separately; give each copy a unique nsi-email id.