Form overview

Overview and usage guidelines for creating a wide variety of forms.

Form controls

Style textual inputs and textareas with support for multiple states.


Improve browser default select elements with a custom initial appearance.

Checkbox and radio

Use our custom radio buttons and checkboxes in forms for selecting input options.


Replace browser default range inputs with our custom version.

Input group

Attach labels and buttons to your inputs for increased semantic value.

Floating labels

Create beautifully simple form labels that float over your input fields.


Create inline, horizontal, or complex grid-based layouts with your forms.


Validate your forms with custom or native validation behaviors and styles.

Overview #

Every group of form fields should reside in a <form> element. The <form> element has no styling, but there are some powerful browser features that are provided by default. You can read more about this element on MDN: <form> (opens in new tab). A few more things worth noting:

  • Form controls require classes to be styled properly. You can find examples for each type in the different sections linked above.
  • <button> elements within a <form> default to type="submit", so it's a good idea to be specific and always include a type.
  • Be sure to use an appropriate type attribute on all inputs (e.g., email for email address or number for numerical information) to take advantage of newer input controls like email verification, number selection, and more.

Here's an example of a simple sign in form that can be commonly found everywhere on the web.

<!-- Sign in form -->
  <div class="mb-3">
    <label class="form-label" for="email">Email address</label>
    <input type="email" class="form-control" id="email" required placeholder="Email address">
  <div class="mb-3">
    <label class="form-label" for="password">Password</label>
    <input type="password" class="form-control" id="password" required placeholder="Password">
  <div class="d-flex align-items-center">
    <div class="form-check mb-0">
      <input type="checkbox" class="form-check-input" id="remember" value="">
      <label class="form-check-label" for="remember">Remember me</label>
    <button type="submit" class="btn btn-primary ms-auto">Sign in</button>

Disabled forms #

Add the disabled attribute on an input to prevent user interactions. You can also add the disabled attribute to a <fieldset> to disable all the controls within. Browsers treat all native form controls (<input>, <select>, <button>, etc.) inside a <fieldset disabled> as disabled, preventing both keyboard and mouse interactions on them.

However, if your form also includes custom button-like elements such as <a class="btn btn-*">...</a>, these will only be given a style of pointer-events: none, meaning they are still focusable and operable using the keyboard. In this case, you must manually modify these controls by adding tabindex="-1" to prevent them from receiving focus and aria-disabled="disabled" to signal their state to assistive technologies.

<!-- Disabled sign in form -->
  <fieldset disabled>
    <div class="mb-3">
      <label class="form-label" for="email-disabled">Email address</label>
      <input type="email" class="form-control" id="email-disabled" required placeholder="Email address">
    <div class="mb-3">
      <label class="form-label" for="password-disabled">Password</label>
      <input type="password" class="form-control" id="password-disabled" required placeholder="Password">
    <div class="d-flex align-items-center">
      <div class="form-check">
        <input class="form-check-input" type="checkbox" id="remember-disabled" value="">
        <label class="form-check-label" for="remember-disabled">Remember me</label>
      <button type="submit" class="btn btn-primary ms-auto">Sign in</button>

Accessibility #

Ensure that all form controls have an appropriate accessible name so that their purpose can be conveyed to users of assistive technologies. The simplest way to achieve this is to use a <label> element, or—in the case of buttons—to include sufficiently descriptive text as part of the <button>...</button> content.

For situations where it's not possible to include a visible <label> or appropriate text content, there are alternative ways of still providing an accessible name, such as:

  • <label> elements hidden using the .visually-hidden class
  • Pointing to an existing element that can act as a label using aria-labelledby
  • Providing a title attribute
  • Explicitly setting the accessible name on an element using aria-label

If none of these are present, assistive technologies may resort to using the placeholder attribute as a fallback for the accessible name on <input> and <textarea> elements. The examples in this section provide a few suggested, case-specific approaches.

While using visually hidden content (.visually-hidden, aria-label, and even placeholder content, which disappears once a form field has content) will benefit assistive technology users, a lack of visible label text may still be problematic for certain users. Some form of visible label is generally the best approach, both for accessibility and usability.

Up next
Form controls

Help us grow

Our main goal is to make Halfmoon the go-to framework for building websites, dashboards and tools. If you believe in our mission, consider becoming a sponsor and help us grow.

You can email us directly if you have any queries. We are always happy to answer.

Subscribe for updates

We will notify you when the framework gets a substantial update. No spam ever.

Follow us on Twitter so that you can stay updated that way.