Forms on NYC.gov must be both accessible and responsive. Learn about creating accessible forms. Before creating a new form, pull together the content and create the form as follows.
Form Content:
Error Messages for Required Fields
Build, Test, and Publish
NOTE: Captcha will be included on all forms that do not require the ability to upload attachments.
The examples below illustrate how to use the NYC.gov framework to build a form on NYC.gov. HTML 5 standards are used, and we recommend that you adhere as close as possible to the standards when building out individual inputs, fields, selects, radio buttons, and checkboxes. These examples are following the standards set by WCAG 2.1 Level AA.
NYC.gov can be set up to handle different data captures. The first type of form - the "regular form" is used for collecting text data. The second type of form - the "form with upload capability". It is used for collecting data and uploading files. Each form type setup is described below.
<form class="control-form" action="https://apps.nyc.gov/nyc-mailform/validation" name="formName" method="post" id="formId" type="external"> <input type="hidden" value="Subject" name="subject" /> <input type="hidden" value="dropdown, textfield, inline checkbox, inline radio, checkboxes, radios, date, textarea, file" name="required" id="required" /> <input type="hidden" value="REMOTE_HOST,HTTP_ADDR,HTTP_USER_AGENT" name="env_report" /> <input type="hidden" value="3" name="g-recaptcha-version" /> ... </form>
All NYC.gov HTML forms start with <form>
and must have class="control-form"
to inherit the latest styling for the form. The form tags also have several attributes, which are described in the table below.
Attribute | Value | Description |
---|---|---|
action | https://apps.nyc.gov/nyc-mailform/validation | This processes the form data after the user submits the form. |
name | formName | Change this value to reflect the name of what the form is about. |
id | formId | This is the form unique ID. Change as needed to ensure the ID is unique to the page. |
method | post | The method specifies how to send the form data. This is sent to the page specified in the action attribute. The method will always be "POST". |
type | external | This is a TeamSite-related attribute, used only by TeamSite. Not declaring this will cause the form to not work. |
Following the <form>
, there are input fields that contain the hidden attribute. These input fields are mandatory and provides the subject, required fields, and browser information that is collected to process the form routing.
Attribute | Value | Description |
---|---|---|
name | subject | This value does not change and is needed for routing the form data. |
value | Subject of the form | Change the value to reflect the subject of the form that is registered in Siebel. |
Attribute | Value | Description |
---|---|---|
name | required | This value does not change and is need for routing the form data. |
value | The value of the name attribute for each input/fields that is required. | List each value follow by a comma. (Example: dropdown, textfield, inline checkbox). |
id | required | Unique ID for the input. Do not change. |
Attribute | Value | Description |
---|---|---|
name | env_report | This value does not change. |
value | REMOTE_HOST,HTTP_ADDR,HTTP_USER_AGENT | This value does not change as it provides information about browser, page URL address, and the browser. |
Attribute | Value | Description |
---|---|---|
name | g-recaptcha-version | This value does not change. |
value | 3 | This value does not change. It is utlized by reCAPTCHA. |
<form class="control-form" action="https://apps.nyc.gov/nyc-mailform/validation" name="formName" enctype="multipart/form-data" method="post" id="formId" type="external">
<input value="Form Subject" name="subject" type="hidden" />
<input value="Field1,field2,field3" type="hidden" name="required" id="required" />
<input value="REMOTE_HOST,HTTP_ADDR,HTTP_USER_AGENT" name="env_report" type="hidden" />
<input value="#FFFFFF" name="bgcolor" type="hidden" /> <input type="hidden" value="3" name="g-recaptcha-version" />
... </form>
The form setup with upload capability version of the form is used only when files need to be collected with the submissions of the form. The form initial setup is similar to the regular forms, except for a few differences to the input tags and the <form>
tags attributes.
Attribute | Value | Description |
---|---|---|
action | https://apps.nyc.gov/nyc-mailform/validation | This processes the form data after the user submits the form. |
name | formName | Change the value to reflect the name of what the form is about. |
id | formId | This is the form unique ID. Change as needed to ensure the ID is unique to the page. |
method | post | The method specifies how to send the form data. This is sent to the page specified in the action attribute. The method will always be "POST". |
enctype | multipart/form-data | The enctype attribute specifies how the form-data should be encoded when submitting it to the server. This is only used for file upload. |
type | external | This is a TeamSite-related attribute, used only by TeamSite. Not declaring this will cause the form to not work. |
The listed input fields are mandatory and provides the subject, required fields, sort order of the fields, and browser information that is collected to process the form routing.
Attribute | Value | Description |
---|---|---|
name | subject | This value does not change and is needed for routing the form data. |
value | Subject of the form | Change the value to reflect the subject of the form that is registered in Siebel. |
Attribute | Value | Description |
---|---|---|
name | required | This value does not change and is need for routing the form data. |
value | The value of the name attribute for each input/fields that is required. | List each value follow by a comma. (Example: dropdown, textfield, inline checkbox). |
id | required | Unique ID for the input. Do not change. |
Attribute | Value | Description |
---|---|---|
name | env_report | This value does not change. |
value | REMOTE_HOST,HTTP_ADDR,HTTP_USER_AGENT | This value does not change as it provides information about browser, page URL address, and the browser. |
Attribute | Value | Description |
---|---|---|
name | g-recaptcha-version | This value does not change. |
value | 3 | This value does not change. It is utlized by reCAPTCHA. |
Standard form controls like <input>
, <select>
, <textarea>
, inherit the style from the <form>
class .control-form
and is wrapped with control-group. The <input>
, <select>
and <textarea>
is wrapped in the <label> and have a control-label class that includes the styles for general appearance, focus state, and more.
<div class="row">
<div class="container">
<div class="span12">
<div class="control-group">
<label class="control-label">Sample Text field
<input type="text" size="40" name="textfield" placeholder="Textfield"/>
</label>
</div>
</div>
</div>
</div>
<div class="row"> <div class="container"> <div class="span12"> <div class="control-group"> <label class="control-label">Sample drop-down</span> <select name="dropdown" > <option value="" selected disabled hidden>Make a selection</option> <option value="Lorem">Lorem</option> <option value="Ipsum">Ipsum</option> <option value="Dolor">Dolor</option> <option value="Sit">Sit</option> <option value="Amet">Amet</option> </select> </label> </div> </div> </div> </div>
<div class="row">
<div class="container">
<div class="span12">
<div class="control-group">
<label class="control-label">Sample Text-area:
<textarea name="Sample Message"></textarea>
</label>
</div>
</div>
</div>
</div>
All checkboxes and radios button that are within the <form>
with the .control-form
class will inherit all styles for general appearance, focus state, and any additional styles.
<div class="row">
<div class="container">
<div class="span12">
<div class="control-group">
<div class="list_elements">
<label class="checkbox inline">
<input name="The quick brown fox jumps over the lazy dog" value="Yes" type="checkbox">
The quick brown fox jumps over the lazy dog</label>
</div>
</div> </div>
</div>
</div>
<div class="row">
<div class="container"> <div class="span12">
<div class="control-group">
<fieldset>
<legend>The quick brown fox jumps over the lazy dog?</legend>
<div class="list_elements">
<label class="radio inline">
<input name="confirm" value="yes" type="radio">Yes
</label> </div>
<div class="list_elements">
<label class="radio inline"> <input name="confirm" value="no" type="radio">No </label>
</div>
</fieldset> </div> </div>
</div>
</div>
The checkbox example shown above contained one checkbox. However, in most situations you will have several checkboxes. Similar to the radio button example above, the inputs will need to be grouped. This is accomplished using <fieldset>
and with an optional <legend>
.
<div class="row">
<div class="container">
<div class="span12">
<div class="control-group">
<fieldset>
<legend>List of checkboxes</legend>
<div class="list_elements">
<label class="checkbox inline">
<input type="checkbox" value="First checkbox" name="checkboxes" />
First checkbox</label>
</div>
<div class="list_elements">
<label class="checkbox inline">
<input type="checkbox" value="Second checkbox" name="checkboxes" />
Second checkbox</label>
</div>
<div class="list_elements">
<label class="checkbox inline">
<input type="checkbox" value="Third checkbox" name="checkboxes" />
Third checkbox</label>
</div>
<div class="list_elements">
<label class="checkbox inline">
<input type="checkbox" value="Fourth checkbox" name="checkboxes" />
Fourth checkbox</label>
</div>
<div class="list_elements">
<label class="checkbox inline">
<input type="checkbox" value="Fifth checkbox" name="checkboxes" />
Fifth checkbox</label>
</div> <div class="list_elements">
<label class="checkbox inline">
<input type="checkbox" value="Sixth checkbox" name="checkboxes" disabled />
Sixth checkbox</label>
</div>
</fieldset>
</div>
</div>
</div>
</div>
<div class="row">
<div class="container">
<div class="span12">
<fieldset>
<legend>Inline checkboxes</legend>
<div class="inline_elements">
<label class="checkbox inline">
<input type="checkbox" name="inline checkbox" value="First checkbox" />
First checkbox</label>
<label class="checkbox inline">
<input type="checkbox" name="inline checkbox" value="Second checkbox" />
Second checkbox</label>
</div>
</fieldset>
</div>
</div>
</div>
There are two types of predefined submit buttons styles.
<div class="row">
<div class="container">
<button type="submit" class="btn btn-primary btn-form-submit">Save changes</button>
</div>
</div>
<div class="row">
<div class="container">
<div class="span12">
<div class="control-group">
<label for="file-input" class="control-label">File Input</label>
<input type="file" name="file" id="file" aria-describedby="fileHelp" class="inputfile" />
<label class="fileInput-label btn" for="file"><strong>
<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet">
<path d="M0 0h24v24H0z" fill="none"></path>
<path d="M9 16h6v-6h4l-7-7-7 7h4zm-4 2h14v2H5z"></path>
</svg>
Choose a file…</strong><span>no file selected</span> </label>
<small id="fileHelp" class="form-text text-muted">This is some placeholder block-level help text for the above input. It's a bit lighter and easily wraps to a new line.Aria description will be in this help text.</small> </div>
</div>
</div>
</div>
Feedback to users is recommended and must adhere to WCAG 2.1 Level AA when interacting with a form. Using the browser default validation feedback with HTML5 is supported, however it is recommended to use custom validation styles, as native browse defaults are not entirely announced to screen readers. Below are default styles provided to assist in custom validations.
In the following example, the sample drop-down does not utilize the HTML required attribute, instead requires JavaScript to validate the input fields. To make sure assistive technologies know that these fields are required, we added the aria-required
attribute. Please take note of additional aria-
tags that is used for assistive technologies. JavaScript also control the values of the aria tags attributed during the validation process.
<div class="row"> <div class="container"> <div class="span12"> <div class="control-group"> <label class="control-label">Sample drop-down<span class="required">*</span> <div class="validationMessage" aria-live="polite" style="display: none" aria-hidden="true"></div> <select name="dropdown" aria-required="true" > <option value="" selected disabled hidden>Make a selection</option> <option value="Lorem">Lorem</option> <option value="Ipsum">Ipsum</option> <option value="Dolor">Dolor</option> <option value="Sit">Sit</option> <option value="Amet">Amet</option> </select> </label> </div> </div> </div> </div>
<div class="row"> <div class="container"> <div class="span12"> <div class="control-group"> <label class="control-label validationError">Sample drop-down<span class="required">* <div class="validationMessage" aria-live="polite" aria-hidden="false">Validation Error Message <span>The quick brown fox jumps over the lazy dog</span></div> <select name="dropdown" aria-required="true" > <option value="" selected disabled hidden>Make a selection</option> <option value="Lorem">Lorem</option> <option value="Ipsum">Ipsum</option> <option value="Dolor">Dolor</option> <option value="Sit">Sit</option> <option value="Amet">Amet</option> </select> </label> </div> </div> </div> </div>
Any required field will need a <div>
with class="validationMessage"
to display the validation error message. In addition, <label>
will need to have the validationError class for the error states to be applied to the input field.
<div class="row"> <div class="container"> <div class="span12"> <div class="control-group"> <label class="control-label validationError">Sample drop-down<span class="required">*</span> <div class="validationMessage" aria-live="polite" style="display: none" aria-hidden="true"></div> ... </label> </div> </div> </div> </div>
When using either of the type of forms processing methods, a custom JavaScript library was developed to handle basic validation for all fields and handle the fileUpload functionality. The basic validation is limited to checking required fields and validation on email address and phone numbers. Following the HTML pattern laid out here will allow the JavaScript to validate required fields listed in the hidden inputs. It is not encourage to use the HTML 5 required
attribute since the JavaScript is already performing the action for error reporting.
The following JavaScript asset must be included on the page and is recommended to be place after the form, preferable in the footer:
<script type="text/javascript" src="/assets/home/js/forms/forms-v3.js"></script>
To use the validation functionality from the JavaScript, the form id is used as a parameter for the submitForm
function.
<form class="control-form" action="https://apps.nyc.gov/nyc-mailform/validation" name="formName" method="post" id="formId" type="external"> ... <button type="button" onclick="submitForm('formID');" class="btn btn-form-submit">Submit</button>
The JavaScript library file that appears on the form has a default validation error message that will populate the <div>
with the validation message. However, this acts as a failsafe in case the custom error messages were not defined. From an accessibility standpoint, custom error messages are required for required fields on the forms. Custom error messages must include two parts—the first sentence should note the form element/field label that contains the error, and the second sentence should explain how to fix it.
To add your own custom error messges, you will need to add new elements to the existing array. The existing array name is formCustomErrorMsg
which is declaredin the JavaScript library. Start by creating a new JavaScript file to be place directly after the forms-v3.js
<script type="text/javascript" src="/assets/home/js/forms/forms-v3.js"></script>
<script type="text/javascript" src="/assets/agency-name/js/pages/form-id-custom-errors.js"></script>
Next populate the JavaScript file with the data from the error message template. The following syntax is used to add the new elements to the array.
formCustomErrorMsg[formCustomErrorMsg.length] = ['name of input','Your response is required. <span>Please select the type of message. </span>'];
formCustomErrorMsg[formCustomErrorMsg.length]
is the name of the array and the current position in the array. 'name of input'
is the input field name, text or the check box/radio button name'Your response is required. <span>Please select the type of message. </span>'
is the error message that will be displayed.For example, we have two form fields. One is a text field that has a variable name of 'Name' and we also have a text area with a field that is name 'Message'.
<form class="control-form" action="/assets/home/js/forms/forms-v3.js" name="formName" method="post" id="formId" type="external"> <input type="hidden" value="Subject" name="subject" /> <input type="hidden" value="Name, Message, name="required" id="required" /> <input type="hidden" value="REMOTE_HOST,HTTP_ADDR,HTTP_USER_AGENT" name="env_report" /> <input type="text" size="40" name="Name" placeholder="Enter Your Name"/>
<textarea name="Message"></textarea> </form>
Below is the code that will be placed into the custom error JavaScript file as follows:
formCustomErrorMsg[formCustomErrorMsg.length] = ['Name','Name is required. <span>Please enter your name.</span>'];
formCustomErrorMsg[formCustomErrorMsg.length] = ['Message','Message is required. <span>Please type in your message.</span> '];
formCustomErrorMsg[formCustomErrorMsg.length] = ['g-recaptcha-response','Please check the box to indicate that you are not a robot.'];
NOTE: It is important to follow the above pattern. If it is incorrect, the form will fail to submit.
reCAPTCHA is required on all forms and protects the form on the page from spam and abuse.
Insert the following for reCAPTCHA before the submit button to notifiy the user that we utlize reCAPTCHA and is manage by Google.
<div class="row">
<div class="container">
<div class="span12">
<hr />
</div>
</div>
</div>
<div class="row">
<div class="container control-group">
<div class="span12">
<p>
This site is protected by reCAPTCHA and the Google
<a href="https://policies.google.com/privacy" class="exitlink">Privacy Policy</a>
and
<a href="https://policies.google.com/terms" class="exitlink">Terms of Service</a>
apply.
</p>
</div>
</div>
</div>
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.