Forms

Summary

Form layout primitives for labels, inputs, validation messages, help text, and grouped controls (radios, checkboxes). Three layout variants (default, top-label, long-label) cover most rendering needs from narrow popovers to wide settings pages. Width modifiers (-short, -medium, -long, -full) constrain inputs to sensible defaults.

When to use

LayoutUse it for
Default
.neura-form
Standard side-by-side label / input layout. Sensible default for most forms.
Top-label
.neura-form-top-label
Stacks labels above inputs. Use in narrow containers (inline dialog, sidebar).
Long-label
.neura-form-long-label
Reserves more horizontal room for the label column. Use when label text wraps awkwardly.

Examples

Live form fields with every input variant, width modifier, and help / error treatment. Inputs aren't wired to a backend: focus a field, try the disabled + file pickers, and toggle the date input to verify the chrome.

Text inputs

Default width input of a required field
Error message here
Long width input
Short width input
Disable field input
Medium width input
We only use your address for account notifications - never marketing.

Dropdowns and multi select

Multi select description

Textarea

Radio buttons

Checkboxes

File upload

HTML

Field group: wraps label, input, optional help text and error.

<form class="neura-form">
  <div class="neura-field-group">
    <label for="email">Email<span class="icon-required"> (required)</span></label>
    <input type="email" id="email" class="neura-field-text">
    <div class="neura-field-description">We never share your email.</div>
  </div>
</form>

Width modifiers go on the input itself:

<input class="neura-field-text neura-field-short">   <!-- 75px  -->
<input class="neura-field-text neura-field-medium">  <!-- 165px -->
<input class="neura-field-text neura-field-long">    <!-- 500px -->
<input class="neura-field-text neura-field-full">    <!-- 100% of group -->

For a validation error, append a .neura-field-error below the input:

<div class="neura-field-group">
  <label for="pw">Password</label>
  <input type="password" id="pw" class="neura-field-password">
  <div class="neura-field-error">Must be at least 8 characters.</div>
</div>

Top-label layout (use in narrow containers):

<form class="neura-form neura-form-top-label">
  <div class="neura-field-group">
    <label for="search">Search</label>
    <input type="search" id="search" class="neura-field-text neura-field-full">
  </div>
</form>

For a radio group, wrap each radio + label in .neura-field-radio:

<div class="neura-field-group">
  <label>Radio buttons</label>
  <div class="neura-field-radio">
    <input type="radio" id="r-blog" name="save-target" value="blog" checked>
    <label for="r-blog">Save as a blog post</label>
  </div>
  <div class="neura-field-radio">
    <input type="radio" id="r-page" name="save-target" value="page">
    <label for="r-page">Save as a page</label>
  </div>
  <div class="neura-field-radio">
    <input type="radio" id="r-draft" name="save-target" value="draft">
    <label for="r-draft">Save to your drafts</label>
  </div>
</div>

Checkbox groups use the same pattern with .neura-field-checkbox:

<div class="neura-field-checkbox">
  <input type="checkbox" id="cb-email">
  <label for="cb-email">Receive email</label>
</div>

For the buttons row, group form submit + cancel in .neura-buttons-container:

<div class="neura-buttons-container">
  <button type="submit" class="neura-button">Save</button>
  <button type="button" class="neura-button neura-button-link">Cancel</button>
</div>

CSS classes

Form layouts

ClassEffect
.neura-formDefault: labels left of inputs.
.neura-form-top-labelStacks labels above inputs.
.neura-form-long-labelWider label column.
.neura-field-groupWraps a single label + input + help + error.
.neura-fieldset-groupOn a <fieldset>: groups radio/checkbox sets with the <legend> floated into the label column (the a11y-correct grouping; screen readers announce the legend with each option). Legends render only inside this construct, matching AUI.
.neura-buttons-containerAligns submit / cancel buttons at the form footer.

Inputs

ClassEffect
.neura-field-textText input baseline.
.neura-field-passwordPassword input baseline (same shape as text).
.neura-field-textareaMulti-line input.
.neura-field-selectSingle-value <select>.
.neura-field-multiselectMulti-value <select multiple>.
.neura-field-fileFile upload: keeps the native widget.
.neura-field-radioWraps a single radio + label pair.
.neura-field-checkboxWraps a single checkbox + label pair.

Width modifiers

ClassWidth
.neura-field-short75px
.neura-field-medium165px
.neura-field-long500px
.neura-field-full100% of the field group

Help text and validation

ClassEffect
.neura-field-descriptionHelp text below the input (gray).
.neura-field-errorValidation error message (red).
.icon-requiredRenders the red asterisk after a required field's label.

JavaScript

Forms are CSS-only: there is no Neura.form(...) API. Wire your own submit handler with addEventListener. For the calendar input, see the dedicated Date picker page.

Form submit pattern:

<form id="signup-form" class="neura-form">
  <!-- ... fields ... -->
  <div class="neura-buttons-container">
    <button type="submit" class="neura-button neura-button-primary">Sign up</button>
    <button type="button" class="neura-button neura-button-link" id="cancel">Cancel</button>
  </div>
</form>

<script>
  document.getElementById('signup-form').addEventListener('submit', (e) => {
    e.preventDefault();
    const data = new FormData(e.currentTarget);
    fetch('/signup', { method: 'POST', body: data });
  });
</script>

Inline help: hide longer guidance behind a help icon next to the input (the Email field above wires exactly this snippet). The icon toggles a hidden description and keeps aria-expanded in sync:

<span class="neura-icon neura-icon-help" role="button" tabindex="0"
      aria-controls="email-help" aria-expanded="false"
      aria-label="Toggle help"></span>
<div class="neura-field-description neura-hidden" id="email-help">
  We only use your address for account notifications - never marketing.
</div>
document.querySelectorAll('.neura-form [role="button"][aria-controls]').forEach((btn) => {
  const help = document.getElementById(btn.getAttribute('aria-controls'));
  const toggle = () => {
    const hidden = help.classList.toggle('neura-hidden');
    btn.setAttribute('aria-expanded', String(!hidden));
  };
  btn.addEventListener('click', toggle);
  btn.addEventListener('keydown', (e) => {
    if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); toggle(); }
  });
});

AUI compatibility

All .aui-* form classes (.aui-form, .aui-field-text, etc.) and form.aui field hooks alias to the Neura rules, including the grouped controls (fieldset.group + legend → .neura-fieldset-group), the .radio/.checkbox row wrappers, the legacy .button (routed onto neura-button), the .cancel link, and the .buttons whitespace handling. Existing Velocity templates render unchanged. AJS.inlineHelp() keeps the legacy .icon-inline-help / .field-help markup toggling; new code should use the inline-help pattern above instead.