forms component

Drop-in semantic component. See examples, sizes, variants, and source.

These are end-to-end form recipes — login, signup, contact, settings, search, newsletter — built from the canonical input components. They show how the pieces compose and how to wire submission + validation through the bluestack framework. For per-input options (sizes, types, color intents, states), open the dedicated component pages in the sidebar.

Login Form

Email + password with auto-mounted password toggle (added by text-input.js when an input has class password), inline email validation on blur, and a remember-me checkbox.

Login

Sign in

Please enter a valid email address
At least 8 characters
Forgot password?

Signup Form with Inline Validation

Required fields receive a red * automatically (added by text-input.js). The email/url types are validated by text-input.js on blur and again by form_handler on submit. Errors surface via bluestack.say().

Create account

Create your account

Please enter a valid email address
https://
Optional — we'll grab your favicon for your profile.
Min 8 characters, mix of letters and numbers.
Plan

Contact Form

A native <select> wrapped in .select-wrapper, a textarea inside .textarea-field with a character hint, and a "send me a copy" checkbox.

Contact us

Get in touch

20–1000 characters.

Settings Form

Real-world settings panel: a profile section with text inputs, a notifications section using switch for each preference, and a danger-zone radio group for the visibility setting. Each <fieldset> groups a logical block.

Account settings

Settings

Profile
Auto-formatted as you type.
Notifications
Email digest
A weekly summary of activity.
Push notifications
Browser alerts for mentions and replies.
Marketing emails
Product news and announcements.
Profile visibility

Search / Filter Bar

Inline horizontal form: a search input with a leading magnifier icon (class="input search"), a category select, and a submit button. Use method="GET" so filters live in the URL.

Search bar

Inline Newsletter Signup

The compact .input-group pattern: input flush against a submit button, useful for newsletter signups, invite-by-email rows, and anything single-field.

Newsletter

One email a month. Unsubscribe anytime.

Payment Form

Demonstrates the credit-card and amount input types from text-input.js: card number is auto-grouped and the brand icon appears as you type; the amount field formats to two decimals on blur. The expiry uses the date mask.

Card payment

Payment details

$

Class API Reference

Type Class Usage Notes
Field shell field Container around label + control + hint/error Add the error modifier to colour everything inside
label Label text above the input Wrap a required child for the asterisk
required The red * marker Inside label
Input core input Required base class for any text input See the Text Input page for full options
input-wrapper Hosts an input plus leading/trailing icons Use with has-left-icon / has-right-icon
input-group Glues an input flush against a button Newsletter signups, search bars
Addons input-addon Container for input + prefix/suffix add-ons Holds addon left / addon right children
addon A prefix/suffix piece (e.g. $, https://) Combine with left or right
Messaging hint Helper text below the input Inside field
error-message Inline error message — auto-revealed when field.error Inside field
Form-level data-submit="ajax" Opt the <form> into the bluestack AJAX submit strategy Without it, the form submits natively. See the wiring notes below.

How validation & submission are wired

Validation and submission are two independent layers. The CSS is framework-agnostic — nothing in field / input / error-message / hint requires bluestack JS to look right. You can use these classes with native HTML5 forms, htmx, Alpine, React, or anything else.

Layer 1 — Validator (always on)

app/bluestack.core/form.js is a pure observer. It listens for submit on every form and runs the standard HTML5 attribute checks: required, pattern, minlength, maxlength, min, max, type=email|url|number. It does not mutate the form, its inputs, or its action.

  • Invalid → e.preventDefault() + e.stopImmediatePropagation(), then dispatches bluestack:form:invalid (cancelable) on the form with detail.messages and detail.fields. If no listener prevents the event, falls back to bluestack.say(messages, $form) for the bundled error toast. stopImmediatePropagation ensures no submission strategy fires for an invalid form.
  • Valid → dispatches bluestack:form:valid (cancelable). Listeners can call preventDefault() to abort. Otherwise control falls through to whatever submit handlers are bound after the validator (see Layer 2) and then to native submit.
  • Field labels for error messages resolve in this order: data-message="…" → wrapping <label> text → label[for=…] text → name attribute.

Layer 2 — Submission (opt in)

app/bluestack.core/form_submit.js is the bluestack AJAX strategy. It activates only on forms tagged data-submit="ajax". For matching forms it resolves the action, builds a FormData, copies form attributes onto the AJAX settings (so target, custom data-*, etc. flow through), disables the submit button + adds loading, and calls bluestack.ajax(). Forms without data-submit="ajax" are submitted natively (or by whatever framework you've wired up).

Use any other framework

Skip data-submit="ajax" and listen for bluestack:form:valid from your own code, htmx, Alpine, etc.:

document.querySelector('#my-form').addEventListener('bluestack:form:valid', (e) => {
    // Validation already passed. Take it from here.
    fetch('/api/save', { method: 'POST', body: new FormData(e.target) });
    e.preventDefault(); // Stop the native submit.
});

Or override the default error display by listening for bluestack:form:invalid:

document.querySelector('#my-form').addEventListener('bluestack:form:invalid', (e) => {
    e.preventDefault(); // Skip bluestack.say
    renderMyOwnInlineErrors(e.detail.fields);
});