You are here:

NYPL Design Toolkit

Version: 0.1.36

Forms

Accessibility note: When encountering a properly formatted form, screen readers switch over to "form mode." This switch is typically signified by a loud beep. While in form mode, keyboard controls work differently, ex. "h" types a letter "h" rather than prompting the reader to scan headings. Users of screen readers and keyboard nav, generally navigate forms with the ‘Tab’ key.

Forms fields should be wrapped by a fieldset tag. How these wrappings are determined is up to the developer’s good judgment. Recommended microformats for fieldsets are being implemented, such as the Omni Search fieldset.

Omni Search

Omni Search is a special fieldset contained in its own row, in an offsetted column. (See Layout for details.) The fieldset itself contains three child elements: a span tag of class .nypl-omni-fields, a text field, and a submit button. The .nypl-omni-fields element contains a hidden label (for screen readers only) and a select box with the applicable options.

NOTE: The text field is aria-labeledby the button.

Example

.nypl-omnisearch
.nypl-omni-fields

<fieldset class="nypl-omnisearch">
  <span class="nypl-omni-fields">
    <label for="search-by-field">Search in</label>
    <select id="search-by-field">
      <option value="all" selected="selected">All fields</option>
      <option value="title">Title</option>
      <option value="contributor">Author/Contributor</option>
      <option value="subject">Subject</option>
      <option value="series">Series</option>
      <option value="call_number">Call number</option>
    </select>
  </span>
  <input type="text" aria-labelledby="nypl-omni-button" value="" placeholder="Keyword, title, name, or id" />
  <input type="submit" id="nypl-omni-button" value="Search">
</fieldset>

Text Fields

Text fields can include (but not rely on) a placeholder that provides additional information on the type of input it expects.

Example

.nypl-text-field
    

<div class="nypl-text-field">
  <label for="keyword-text">Title or description</label>
  <input id="keyword-text" type="text" placeholder="Enter keywords" />
</div>
    

Required Fields

Required fields must include a span tag of class nypl-required-field in the label that includes the text “Required”. The field itself should include the required and aria-required attributes.

It is recommended that JavaScript-based validation be made in the onchange event (native to select, input[type=checkbox], and input[type=radio], also fired by some frameworks on text blur and submit). Your designer can advise on exceptions to this recommendation if there is a good reason to do otherwise (for example, each time a user types a new letter).

Accessibility note: An optional span element with class nypl-field-status can be included after the input field providing a brief message explaining any additional requirements about the field. If included, the note must include the aria-labelledby="[LABEL_ID] [STATUS_FIELD_ID]", aria-live="assertive", and aria-atomic="true" attributes if the form is being validated via client-side JavaScript. The example below shows a simple validation on every key press using jQuery. See Form Validation for more information.

Example
Username must only contain letters and numbers.

.nypl-required-field
    

<div class="nypl-text-field" id="username1">
  <label for="required-field" id="username1-label">Username
    <span class="nypl-required-field">Required</span>
  </label>
  <input id="required-field" type="text" required="true" aria-labelledby="username1-label username1-status" aria-required="true" />
  <span class="nypl-field-status" id="username1-status" aria-live="assertive" aria-atomic="true">Username must only contain letters and numbers.</span>
</div>
    

$("#username1 #required-field").change(function (e) {
  var self = $(e.target)
  var text = self.val()
  var regex = /\W/g
  if (text.length > 0 && regex.test(text)) {
    $("#username1").addClass("nypl-field-error")
    $("#username1 .nypl-field-status").text("The Username must only contain letters and numbers. Try again.")
  }
})
    

Date Fields

The date field implements a similar structure as the Text Field but uses type="date". This input type has limited browser support so additional server-side and JavaScript validation should be taken into consideration. Server-side validation should still occur since it is more secure.

Note: This only applies for dates that will conform to a year/month/day-style pattern (YYYY/MM/DD, MM/DD/YYYY, etc).

Example

.nypl-date-field
    

<div class="nypl-date-field">
  <label for="date-of-birth">Date of Birth</label>
  <input id="date-of-birth" type="date" class="form-text" />
</div>
    

Year Fields

The year field implements a similar structure as the Text Field but uses type="number" and expects integers (for example: 1997, 2015). It should include min and max attributes with the corresponding values which are up to the developer to decide what they are. A final step="1" attribute should be included unless another step count applies. This input type has limited browser support so additional server-side and JavaScript validation should be taken into consideration. Server-side validation should still occur since it is more secure.

Example

.nypl-year-field
    

<div class="nypl-year-field">
  <label for="date-from">On or After Year</label>
  <input id="date-from" type="number" min="1895" max="9999" step="1">
</div>
    

Name Fields

To be used when requesting the full names of a user in separate first and last name values. Both the first and last name input boxes should be wrapped by a div tag and follow the basic structure of Text Fields.

Example

.nypl-name-field
    

<div class="nypl-name-field">
  <div>
    <label for="first-text">First Name</label>
    <input id="first-text" type="text" />
  </div>
  <div>
    <label for="last-text">Last Name</label>
    <input id="last-text" type="text" />
  </div>
</div>
    

Select Boxes

Example

.nypl-select-field
    

<div class="nypl-select-field">
  <label for="topics">Topics</label>
  <select id="topics" name="topics">
    <option value="1" selected="selected">One</option>
    <option value="2">Two</option>
    <option value="3">Three</option>
    <option value="…">…</option>
  </select>
</div>
    

Alphabetical Filters

Not a form element per se but still used within search forms. This control should contain a fieldset and legend that wrap and label it respectively. This is a list of buttons that apply an alphabetical/number-type filter. Currently applied filter is disabled. The "Any" (or multi-character) selection has a nypl-long class.

Example
Name starts with

.nypl-alphabetical-filter
    

<div class="nypl-alphabetical-filter">
  <fieldset>
    <legend>Name starts with</legend>
    <button name="az-filter" disabled="true" class="nypl-long">Any</button>
    <button name="az-filter" title="Show only resources with name starting with a number">#</button>
    <button name="az-filter" title="Show only resources with name starting with A">A</button>
    <button name="az-filter" title="Show only resources with name starting with B">B</button>
    <button name="az-filter" title="Show only resources with name starting with C">C</button>
    <button name="az-filter" title="Show only resources with name starting with …">…</button>
  </fieldset>
</div>
    

Generic Checkboxes

We override the native styling of checkboxes. We change its state with css only that allows the label and the box to be clicked to toggle it from checked or unchecked.

Example

.nypl-generic-checkbox
        

<div class="nypl-generic-checkbox">
  <input id="book-check-filter" class="switch-input" type="checkbox" name="format" value="Book" />
  <label for="book-check-filter">The Checkbox Label</label>
</div>
        

Terms / Opt-in Checkboxes

For situations where the user is requested to accept a terms of service or to opt-in to communications from the Library. When checked, a class of checked should be added to the main element.

Example

.nypl-terms-checkbox
.checked
    

<div class="nypl-terms-checkbox">
  <label for="checkbox-optin">
    <input id="checkbox-optin" type="checkbox" value="1" />Yes, I'd like to receive e-communications about NYPL's programs, services, and initiatives.
  </label>
</div>
    

$("#checkbox-optin").change(function(e){
  var checked = e.target.checked
  if (checked) {
    $(".nypl-terms-checkbox").addClass("checked")
  } else {
    $(".nypl-terms-checkbox").removeClass("checked")
  }
})
    

Radiobutton/Checkbox Groups

Another control that contains a fieldset and its corresponding legend.

Accessibility note: For proper screen reader behavior, each radiobutton or checkbox must be aria-labelledby both the title legend tag and its corresponding label tag.

Example
Accessible from

.nypl-radiobutton-field
    

NOTE: In this case the input tags are wrapped by the label tag.


<div class="nypl-radiobutton-field">
  <fieldset>
    <legend id="radiobutton-group1">Accessible from</legend>
    <label id="radiobutton-group1_every" for="available-every">
      <input aria-labelledby="radiobutton-group1 radiobutton-group1_every" id="available-every" type="radio" name="available" value="any" />Anywhere
    </label>
    <label id="radiobutton-group1_home" for="available-home">
      <input aria-labelledby="radiobutton-group1 radiobutton-group1_home" id="available-home" type="radio" name="available" value="home" />Home with a library card
    </label>
    <label id="radiobutton-group1_library" for="available-library">
      <input aria-labelledby="radiobutton-group1 radiobutton-group1_library" id="available-library" type="radio" name="available" value="library" />A library branch
    </label>
  </fieldset>
</div>
    

Collapsible Fields

Collapsible fields group multiple form fields into a collapsible box. This allows for better organization and display of multiple visually different form fields.

The general structure is:

  • a nypl-facet-toggle toggle button that controls the open/closed state of the widget with the facet name wrapped in an h3 tag,
  • a div of class nypl-collapsible which contains the fields.

Accessibility note: The toggle button must wrap the facet name with an h3 tag.

Toggling between closed and open states along with the affected aria-expanded values of the widget will be controlled via JavaScript. A collapsed class must be added (or removed) to the nypl-collapsible-field and nypl-collapsible elements. The example below uses jQuery.

NOTE: The widget defaults to the open state but is closed via JavaScript on page load. This is to support situations where JavaScript may be disabled and allow users to see and interact with the facets.

Example

.nypl-collapsible-field
.nypl-collapsible
.collapsed
    

<div class="nypl-collapsible-field" aria-expanded="true">
  <button type="button" id="date" class="nypl-facet-toggle"
    aria-controls="nypl-collapsible-field_date" aria-expanded="true">
    <h3>Date</h3>
    <svg aria-hidden="true" class="nypl-icon" preserveAspectRatio="xMidYMid meet" viewBox="0 0 68 24"><title>wedge down icon</title><polygon points="67.938 0 34 24 0 0 10 0 34.1 16.4 58.144 0 67.938 0" /></svg>
  </button>
  <div class="nypl-collapsible" id="nypl-collapsible-field_date" aria-expanded="true">
    <div class="nypl-year-field">
      <label for="date-from1">On or After Year</label>
      <input id="date-from1" type="number" min="0" max="9999" step="1">
    </div>
    <div class="nypl-year-field">
      <label for="date-to1">On or Before Year</label>
      <input id="date-to1" type="number" min="0" max="9999" step="1">
    </div>
  </div>
</div>
    

// jQuery example
$("button.nypl-facet-toggle").click(function (e) {
  toggleFacet(e)
})

function toggleFacet(e) {
  var button = $(e.target)
  var facet = button.parent()
  var collapsible = facet.find(".nypl-collapsible")
  button.attr("aria-expanded", button.attr("aria-expanded") == "false" ? "true" : "false")
  facet.attr("aria-expanded", facet.attr("aria-expanded") == "false" ? "true" : "false")
  collapsible.attr("aria-expanded", collapsible.attr("aria-expanded") == "false" ? "true" : "false")
  facet.toggleClass("collapsed")
  collapsible.toggleClass("collapsed")
}
    

Searchable Fields

Searchable are a version of the collapsible fieldsets that allow the user to see many non-exclusive options at the same time while also searching within the option list. Use searchable facets when the list of potential selectable items is too large (i.e. more than a dozen or so). The text field should autocomplete (with result count) as the user types in it. By default, show the top 10 items sorted by the amount of results that will be returned.

The general structure is:

  • a nypl-facet-toggle toggle button that controls the open/closed state of the widget with the facet name wrapped in an h3 tag,
  • a div of class nypl-collapsible which contains:
  • a nypl-facet-search that reflects the general structure of a text field element followed by
  • a fieldset of class nypl-facet-list with
  • each of the possible facet values as described below.

Each facet value checkbox has the following structure (note that the result count comes before the facet text):


<label id="my-facet-label-id" for="my-facet-value-id" class="nypl-bar_[1 to 100]">
  <input aria-labelledby="my-facet-label-id my-facet-value-id" id="my-facet-value-id" type="checkbox" name="my-name" value="my-value" />
  <span class="nypl-facet-count">[estimated result count]</span>
  <span>The facet text</span>
</label>
    

The placeholder on the search box should have the text “Search [facetname plural]” to make clear that the user is searching within the possible values to facet on.

The label has an optional class of nypl-bar_[percentnumber] where the [percentnumber] determines the length of the decorative bar behind the value and should reflect the relative size of the selection of that facet. It must be an integer from 1 to 100, from shortest to longest (this is a different number from the estimated result count). A label with class nypl-bar_10 would be 10% long, from the right.

The facet count is optional but must be of class nypl-facet-count.

Accessibility note: Similar to Radiobutton/Checkbox Groups, for proper screen reader behavior, each radiobutton or checkbox must be aria-labelledby both the title group's button tag and its own corresponding label tag.

Accessibility note: The toggle button must wrap the facet name with an h3 tag.

A “Show X more” link button at the end of the list allows for the user to display X more items to select from. Important: When the facet has very few items or when no “Show X more” button is displayed, the class nosearch has to be added to the field. This will hide the search box.

Toggling between closed and open states along with the affected aria-expanded values of the widget will be controlled via JavaScript. A collapsed class must be added (or removed) to the nypl-searchable-field and nypl-collapsible elements. The example below uses jQuery.

NOTE: Place previously checked items first and without the number. The widget defaults to the open state but is closed via JavaScript on page load. This is to support situations where JavaScript may be disabled and allow users to see and interact with the facets.

NOTE: As is the case of the Radio Buttons In this case the input tags are wrapped by the label tag.

Example

.nypl-searchable-field
.nypl-facet-search
.nypl-facet-list
.nypl-facet-count
.nypl-collapsible
.collapsed
    

<div class="nypl-searchable-field" aria-expanded="true">
  <button type="button" id="searchable1" class="nypl-facet-toggle"
    aria-controls="nypl-searchable-field_facet1" aria-expanded="true">
    <h3>Subject</h3>
    <svg aria-hidden="true" class="nypl-icon" preserveAspectRatio="xMidYMid meet" viewBox="0 0 68 24"><title>wedge down icon</title><polygon points="67.938 0 34 24 0 0 10 0 34.1 16.4 58.144 0 67.938 0" /></svg>
  </button>
  <div class="nypl-collapsible" id="nypl-searchable-field_facet1" aria-expanded="true">
    <div class="nypl-facet-search">
      <label for="subject-text">Search within Subjects</label>
      <input id="subject-text" type="text" />
    </div>
    <fieldset class="nypl-facet-list">
      <label id="subject_poems" for="subject-poems">
        <input aria-labelledby="searchable1 subject_poems" id="subject-poems" type="checkbox" name="subject" value="poems" checked="checked" />
        <span>Poems</span>
      </label>
      <label id="subject_ejournals" for="subject-ejournals" class="nypl-bar_43">
        <input aria-labelledby="searchable1 subject_ejournals" id="subject-ejournals" type="checkbox" name="subject" value="ejournals" />
        <span class="nypl-facet-count">1,334,741</span>
        <span>Electronic journals</span>
      </label>
      <label id="subject_home" for="subject-home" class="nypl-bar_21">
        <input aria-labelledby="searchable1 subject_home" id="subject-home" type="checkbox" name="subject" value="home" />
        <span class="nypl-facet-count">236</span>
        <span>Electronic books</span>
      </label>
      <label id="subject_piano" for="subject-piano" class="nypl-bar_18">
        <input aria-labelledby="searchable1 subject_piano" id="subject-piano" type="checkbox" name="subject" value="piano" />
        <span class="nypl-facet-count">198</span>
        <span>Piano music</span>
      </label>
      <label id="subject_opera" for="subject-opera" class="nypl-bar_11">
        <input aria-labelledby="searchable1 subject_opera" id="subject-opera" type="checkbox" name="subject" value="opera" />
        <span class="nypl-facet-count">122</span>
        <span>Operas</span>
      </label>
    </fieldset>
    <button class="nypl-link-button">Show 10 more</button>
  </div>
</div>
    

// jQuery example
$("button.nypl-facet-toggle").click(function (e) {
  toggleFacet(e)
})

function toggleFacet(e) {
  var button = $(e.target)
  var facet = button.parent()
  var collapsible = facet.find(".nypl-collapsible")
  button.attr("aria-expanded", button.attr("aria-expanded") == "false" ? "true" : "false")
  facet.attr("aria-expanded", facet.attr("aria-expanded") == "false" ? "true" : "false")
  collapsible.attr("aria-expanded", collapsible.attr("aria-expanded") == "false" ? "true" : "false")
  facet.toggleClass("collapsed")
  collapsible.toggleClass("collapsed")
}
    

Form Validation

Form validation should be handled such that it works even if JavaScript is disabled by the user. Client-side validation is useful to prevent invalid input beforehand, but can be unreliable. Make sure to do additional server-side validation of any user input.

Form validation includes two aspects: form-level messages and field-level messages.

Error messages for the submitted form

When the form has been submitted, errors or problems with individual fields must be hightlighted and focused at the top of the form and linked internally to the individual fields. The components are: a div element of class nypl-form-error, a heading informing the user that errors have occurred should be used, a child list with the individual field errors. The field errors must link to the field itself to make it easier for users to find it within the form:

Example

Oops! Seems there are some errors in this form!

  • The username already exists. Please choose another username.
  • The email is invalid.

    .nypl-form-error
    

<div class="nypl-form-error">
  <h2>Oops! Seems there are some errors in this form!</h2>
  <ul>
    <li>The <a href="#">username</a> already exists. Please choose another username.</li>
    <li>The <a href="#">email</a> is invalid.</li>
  </ul>
</div>    

Success / Completion messages for the submitted form

In the case were the form has been submitted and the application needs to alert the user that their data or input has been submitted and accepted. Similar to the above error reporting, the components are: a div element of class nypl-form-success, a heading informing the user the their submission has been accepted by the application, either via front-end, server side validation or as the case may be both, and short secondary message with an explantion (optional) wrapped in a p.

Example

Success!

Your form has been accepted.


      .nypl-form-success
      

<div class="nypl-form-success">
  <h2>Success!</h2>
    <p>Your form has been accepted.</p>
</div>

Error state and messages on text fields

Any field where user input could be erroneous should add the class nypl-field-error upon validation. It is up to the product designer to determine what fields should have an error state and provide correct explanatory text using an nypl-field-status as described in Required Fields. It is the responsibility of the developer to implement the necessary validations.

Accessibility note: The correct aria attributes must be added to the input field for proper accessibility coverage. The example below describes both aria-required and aria-invalid. aria-required will be a static state for the form, whereas aria-invalid will initially be set to false, upon a an invalid submission, the attribute should return true as it's value. The field must also make use of the nypl-field-status span element as described in Required Fields above. The example below provides a very simple jQuery-based implementation. See also Basic Form Hints: Required and invalid fields in the Mozilla Developer Network.

Example
Email address must use the correct formatting. Example: prudence@example.org

.nypl-field-error
    

<div class="nypl-text-field nypl-field-error" id="email1">
  <label for="email-field">Email
    <span class="nypl-required-field">Required</span>
  </label>
  <input aria-required="true" aria-invalid="true" id="email-field" type="text" placeholder="" value="sally ride" />
  <span class="nypl-field-status"  aria-live="assertive" aria-atomic="true">Email address must use the correct formatting. Ex: <em>prudence@example.org</em></span>
</div>
    

$("#email1 #email-field").blur(function (e) {
  var self = $(e.target)
  var text = self.val()
  var regex = /^[^@\s]+@[^@\s]+\.[^@\s]+/
  if (!regex.test(text)) {
    $("#email1").addClass("nypl-field-error")
    $("#email1 .nypl-field-status").text("The Email address must use the correct formatting. Example: prudence@example.org. Try again.")
  } else {
    $("#email1").removeClass("nypl-field-error")
    $("#email1 .nypl-field-status").text("")
    self.attr("aria-invalid", "false")
  }
})
    

Fields With Spinners

Use these spinners when the interface being developed updates remotely (via AJAX calls or similar) instead of a regular page refresh to indicate the user that background processing is happening. This prevents user confusion and error.

The main field element (whether a div or fieldset) must include an additional nypl-spinner-field class. Once the user submits or makes changes in the field that produce a remote request, an additional spinning class should be activated so that an animation is displayed overlaying it.

Important: While the overlay prevents some user input, the necessary precautions of disabling and/or blurring elements within the field should also be taken into account.

Accessibility note: This is a visual-only progressive enhancement recommendation and should not be relied on as the only indicator of state.

Example
Name starts with
Accessible from

.nypl-spinner-field
.spinning
  

<fieldset class="nypl-omnisearch nypl-spinner-field">
  <input type="text" aria-labelledby="nypl-omni-button1" value="" />
  <span class="nypl-omni-fields">
    <label for="search-by-field1">Search in</label>
    <select id="search-by-field1"><option value="all">All fields</option><option value="title">Title</option><option value="contributor">Author/Contributor</option><option value="subject">Subject</option><option value="series">Series</option><option value="call_number">Call number</option></select>
  </span>
  <input type="submit" id="nypl-omni-button1" value="Search">
</fieldset>

<div class="nypl-text-field nypl-spinner-field">
  <label for="keyword-text2">Title or description</label>
  <input id="keyword-text2" type="text" placeholder="Enter keywords" />
</div>


<div class="nypl-select-field nypl-spinner-field">
  <label for="topics2">Topics</label>
  <select id="topics2" name="topics">
    <option value="1">One</option>
    <option value="2">Two</option>
    <option value="3">Three</option>
    <option value="…">…</option>
  </select>
</div>

<div class="nypl-alphabetical-filter nypl-spinner-field">
  <fieldset>
    <legend>Name starts with</legend>
    <button name="az-filter" disabled="true" class="nypl-long">Any</button>
    <button name="az-filter" title="Show only resources with name starting with a number">#</button>
    <button name="az-filter" title="Show only resources with name starting with A">A</button>
    <button name="az-filter" title="Show only resources with name starting with B">B</button>
    <button name="az-filter" title="Show only resources with name starting with C">C</button>
    <button name="az-filter" title="Show only resources with name starting with …">…</button>
  </fieldset>
</div>


<div class="nypl-radiobutton-field nypl-spinner-field">
  <fieldset>
    <legend id="radiobutton-group2">Accessible from</legend>
    <label id="radiobutton-group2_every" for="available-every2">
      <input aria-labelledby="radiobutton-group2 radiobutton-group2_every" id="available-every2" type="radio" name="available2" value="any" />
      <span>Anywhere</span>
    </label>
    <label id="radiobutton-group2_home" for="available-home2">
      <input aria-labelledby="radiobutton-group2 radiobutton-group2_home" id="available-home2" type="radio" name="available2" value="home" />
      <span>Home with a library card</span>
    </label>
    <label id="radiobutton-group2_library" for="available-library2">
      <input aria-labelledby="radiobutton-group2 radiobutton-group2_library" id="available-library2" type="radio" name="available2" value="library" />
      <span>A library branch</span>
    </label>
  </fieldset>
</div>


<div class="nypl-searchable-field nypl-spinner-field" aria-expanded="true">
  <button type="button" id="searchable2" class="nypl-facet-toggle"
    aria-controls="nypl-searchable-field_subject" aria-expanded="true">
    Subject
    <svg aria-hidden="true" class="nypl-icon" preserveAspectRatio="xMidYMid meet" viewBox="0 0 68 24">
      <title>wedge down icon</title>
      <polygon points="67.938 0 34 24 0 0 10 0 34.1 16.4 58.144 0 67.938 0" />
    </svg>
  </button>
  <div class="nypl-collapsible" id="nypl-searchable-field_subject" aria-expanded="false">
    <div class="nypl-facet-search">
      <label id="searchable2-label" for="subject-text2">Search within Subjects</label>
      <input id="subject-text2" type="text" />
    </div>
    <fieldset class="nypl-facet-list">
      <label id="subject_poems2" for="subject-poems2">
        <input aria-labelledby="searchable2 subject_poems2" id="subject-poems2" type="checkbox" name="subject2" value="poems" checked="checked" />
        <span>Poems</span>
      </label>
      <label id="subject_ejournals2" for="subject-ejournals2" class="nypl-bar_43">
        <input aria-labelledby="searchable2 subject_ejournals2" id="subject-ejournals2" type="checkbox" name="subject2" value="ejournals" />
        <span class="nypl-facet-count">1,334,741</span>
        <span>Electronic journals</span>
      </label>
      <label id="subject_home2" for="subject-home2" class="nypl-bar_21">
        <input aria-labelledby="searchable2 subject_home2" id="subject-home2" type="checkbox" name="subject2" value="home" />
        <span class="nypl-facet-count">236</span>
        <span>Electronic books</span>
      </label>
      <label id="subject_piano2" for="subject-piano2" class="nypl-bar_18">
        <input aria-labelledby="searchable2 subject_piano2" id="subject-piano2" type="checkbox" name="subject2" value="piano" />
        <span class="nypl-facet-count">198</span>
        <span>Piano music</span>
      </label>
      <label id="subject_opera2" for="subject-opera2" class="nypl-bar_11">
        <input aria-labelledby="searchable2 subject_opera2" id="subject-opera2" type="checkbox" name="subject2" value="opera" />
        <span class="nypl-facet-count">122</span>
        <span>Operas</span>
      </label>
    </fieldset>
    <button class="nypl-link-button">Show 10 more</button>
  </div>
</div>
  

// example jQuery-based activation code
function makeItSpin(who, input) {
  $(who).toggleClass("spinning");
  if (input) $(input).attr("disabled", "true").blur();
  setTimeout(function () {
    $(who).toggleClass("spinning")
    if (input) $(input).removeAttr("disabled");
  }, 2000)
}

$(".nypl-omnisearch.nypl-spinner-field #nypl-omni-button1").click( function(e) {
  var self = $(e.target)
  makeItSpin(self.parent(), self)
})
$(".nypl-omnisearch.nypl-spinner-field input[type=text]").change( function(e) {
  var self = $(e.target)
  makeItSpin(self.parent(), self)
})
$(".nypl-text-field.nypl-spinner-field input").change( function(e) {
  var self = $(e.target)
  makeItSpin(self.parent())
})
$(".nypl-select-field.nypl-spinner-field select").change( function(e) {
  var self = $(e.target)
  makeItSpin(self.parent(), self)
})
$(".nypl-alphabetical-filter.nypl-spinner-field button").click( function(e) {
  var self = $(e.target)
  makeItSpin(self.parent().parent())
})
$(".nypl-radiobutton-field.nypl-spinner-field fieldset").change( function(e) {
  var self = $(e.target)
  makeItSpin(self.parent().parent().parent(), self.parent().parent())
})
$(".nypl-searchable-field.nypl-spinner-field #subject-text2").change( function(e) {
  var self = $(e.target)
  makeItSpin(self.parent().parent())
})
$(".nypl-searchable-field.nypl-spinner-field fieldset").change( function(e) {
  var self = $(e.target)
  makeItSpin(self.parent().parent().parent(), self.parent().parent())
})

  

Basic Text Areas

In the case where an application form needs a mulitline text-area

NOTE: Your application may require this input from the user, in that case, before the closing </label> add the following <span class="nypl-required-field">Required</span>. This adds the existing style for required fields via the .nypl-required-field class.

Example
The application is expecting a certain kind of input. The application developer should describe that criteria in this space.

  .nypl-text-area-with-label
  

    <div class="nypl-text-area-with-label">
      <label for="textarea1">Text Area Label</label>
      <textarea form="the-form-title" id="textarea1" name="application-text-area"></textarea>
      <span class="nypl-field-status" aria-live="assertive" aria-atomic="true">The application is expecting a certain kind of input. The application developer should describe that criteria in this space.</span>
    </div>

Text Area (Accepted)

In cases were the application is performing client side validation onblur the text area should confirm that the user’s input is indeed what the application is expecting.

Example
The input was correct

  .nypl-text-area-with-label
  .nypl-field-success

  <div class="nypl-text-area-with-label nypl-field-success">
    <label for="textarea1">Text Area Label</label>
    <textarea form="the-form-title" id="textarea1" name="application-text-area"></textarea>
    <span class="nypl-field-status" aria-live="assertive" aria-atomic="true">The input was correct</span>
  </div>

Text Area (Error)

In cases were the application is performing client side validation onblur the text area should confirm that the user&rsuqo;s input is incorrect and should be corrected before the application will accept submission. Additional reporting of errors should be presented to the user in the form the Error Message pattern described above.

Example
The input provided has errors and must be corrected before submission can be complete.

.nypl-text-area-with-label
.nypl-field-error

  <div class="nypl-text-area-with-label nypl-field-error">
    <label for="textarea1">Text Area Label </label>
    <textarea form="the-form-title" id="textarea1" name="application-text-area"></textarea>
    <span class="nypl-field-status" aria-live="assertive" aria-atomic="true">The input provided has errors and must be corrected before submission can be complete.</span>
  </div>