As part of a new UI library, I have created a full-featured, HTML5-enhanced Ajax Form framework in a manner that follows the guidelines found in the Webgrown Solutions UI Manifesto. The Ajax Form supports the following features:
HINT: By entering "" for the username and "demo123" for the password, the JSON data returned will come back with a status of "Valid".
HINT: By entering "William" for the first name and "Familia" for the last name, the JSON data returned will come back with a status of "Duplicate".
The form consists of just basic form fields (<input>, <select>, etc) and corresponding <label>s for the flexible layout. The button decorated with the "ajaxFormPost" class attribute and some custom data (data-*) attributes is all that is needed to enable the Ajax call. The "data-formGroup" custom data attribute is what ties the fields with the button (think ValidationGroup in ASP.NET). No <form> tag is required for the grouping, which works great with ASP.NET Web Form's single server form limitations. The "name" attribute on the form field is what maps each form field to its related service call parameter. The only scripting to be added for each form would be any "success" actions to be performed with the JSON data returned by the service call. That is it! Once the CSS and Script stuff are in place on a website, any page can take advantage of this full-featured Ajax Form by simply setting the button's class attribute and setting the needed button and form field custom data attributes. And don't forget, this gives you HTML5 form feature support ("placeholder", "autofocus", "required", and "pattern") on every browser, regardless as to whether the browser natively supports this new HTML5 feature or not. That means form validation ("required" and "pattern") is built in, with no additional scripting.
That sounds like a DRY solution to me (see UI Manifesto).
<h3>Form Test #1 (Sign In) - Horizontal Layout</h3> <div class="form legendLook horizontalFormLayout"> <div id="hdivFormTest1ConfirmationMessage" class="confirmationMessageWrapper hide"></div> <div id="hdivFormTest1ErrorMessage" class="errorMessageWrapper hide"></div> <label for="txtUsername">Username:</label> <input type="text" id="txtUsername" placeholder="E-mail Address" required autofocus data-formGroup="formAuthenticate" name="username" /> <label for="txtPassword">Password:</label> <input type="password" id="txtPassword" placeholder="e.g. demo123" required data-formGroup="formAuthenticate" name="password" /> <div class="formButtonWrapper clearFix"> <input type="submit" class="ajaxFormPost buttonStrong" value="Sign In" data-formGroup="formAuthenticate" data-serviceRequestUrl="..." data-successFunction="successFormTest1" data-confirmAction="false" data-confirmActionMessage="" data-processingMessage="signing in..." data-overlayWindowWhileProcessing="true" /> </div> <p>HINT: By entering "" for the username and "demo123" for the password, the JSON data returned will come back with a status of "Valid".</p> </div> <h3>Form Test #2 (Add Person) - Vertical Layout</h3> <div class="form legendLook"> <div id="hdivFormTest2ConfirmationMessage" class="confirmationMessageWrapper hide"></div> <div id="hdivFormTest2ErrorMessage" class="errorMessageWrapper hide"></div> <label for="txtPersonFirstName">First Name:</label> <input type="text" id="txtPersonFirstName" required data-formGroup="formAddPerson" name="FirstName" /> <label for="txtPersonMiddleName">Middle Name:</label> <input type="text" id="Text1" data-formGroup="formAddPerson" name="MiddleName" /> <label for="txtPersonLastName">Last Name:</label> <input type="text" id="txtPersonLastName" required data-formGroup="formAddPerson" name="LastName" /> <label for="txtPersonCity">City:</label> <input type="text" id="txtPersonCity" required data-formGroup="formAddPerson" name="City" /> <label for="ddlPersonStateAbbreviated">State:</label> <select id="ddlPersonStateAbbreviated" required data-formGroup="formAddPerson" name="StateAbbreviated"> <option value="">Please select a state...</option> <option value="AL">Alabama</option> <option value="AK">Alaska</option> <option value="AS">America Samoa</option> <option value="AZ">Arizona</option> <option value="AR">Arkansas</option> <option value="AA">Armed Forces - Americas (AA)</option> <option value="AE">Armed Forces - Europe (AE)</option> <option value="AP">Armed Forces - Pacific (AP)</option> <option value="CA">California</option> <option value="CO">Colorado</option> <option value="CT">Connecticut</option> <option value="DE">Delaware</option> <option value="DC">District of Columbia (DC)</option> <option value="FL">Florida</option> <option value="GA">Georgia</option> <option value="HI">Hawaii</option> <option value="ID">Idaho</option> <option value="IL">Illinois</option> <option value="IN">Indiana</option> <option value="IA">Iowa</option> <option value="KS">Kansas</option> <option value="KY">Kentucky</option> <option value="LA">Louisiana</option> <option value="ME">Maine</option> <option value="MD">Maryland</option> <option value="MA">Massachusetts</option> <option value="MI">Michigan</option> <option value="MN">Minnesota</option> <option value="MS">Mississippi</option> <option value="MO">Missouri</option> <option value="MT">Montana</option> <option value="NE">Nebraska</option> <option value="NV">Nevada</option> <option value="NH">New Hampshire</option> <option value="NJ">New Jersey</option> <option value="NM">New Mexico</option> <option value="NY">New York</option> <option value="NC">North Carolina</option> <option value="ND">North Dakota</option> <option value="OH">Ohio</option> <option value="OK">Oklahoma</option> <option value="OR">Oregon</option> <option value="PA">Pennsylvania</option> <option value="PR">Puerto Rico</option> <option value="RI">Rhode Island</option> <option value="SC">South Carolina</option> <option value="SD">South Dakota</option> <option value="TN">Tennessee</option> <option value="TX">Texas</option> <option value="UT">Utah</option> <option value="VT">Vermont</option> <option value="VI">Virgin Islands</option> <option value="VA">Virginia</option> <option value="WA">Washington</option> <option value="WV">West Virginia</option> <option value="WI">Wisconsin</option> <option value="WY">Wyoming</option> </select> <!-- I know having two columns for the full name and abbreviated name for a state is not normalized and a bad idea, but this is just for testing purposes to see how to massage the form as needed before the submit. --> <input id="hfPersonState" type="hidden" data-formGroup="formAddPerson" name="State" /> <label for="txtPersonZipCode">Zip Code:</label> <input type="text" id="txtPersonZipCode" class="postalCodeTextbox" required placeholder="ZIP or ZIP+4" data-allowExtended="true" data-formGroup="formAddPerson" name="ZipCode" /> <div class="formButtonWrapper"> <input type="submit" class="ajaxFormPost buttonStrong" value="Add Person" data-formGroup="formAddPerson" data-serviceRequestUrl="..." data-parameterObjectName="person" data-successFunction="successFormTest2" /> </div> <p>HINT: By entering "William" for the first name and "Familia" for the last name, the JSON data returned will come back with a status of "Duplicate".</p> </div>
function getFormElementValue(formElement) { lastErroredCallTrace += "->getFormElementValue"; var value = ""; if (formElement.attr("type") == "checkbox") { value = formElement.prop("checked"); } else { //Check if the val is just the placeholder text. if (doesFormElementHaveValue(formElement)) { value = jsonValueCleanUp(formElement.val()); } else { //Is just the placeholder text, so pass the value as empty. } } return value; } function enableFeature_AjaxFormPostAndClickFunction(targetElement) { lastErroredCallTrace += "->enableFeature_AjaxFormPostAndClickFunction"; //Check to see if already enabled. if (isAttrDefined(targetElement.attr("disabled"))) { targetElement.addClass("disabled"); } else { targetElement.removeClass("disabled"); } if (!isAttrDefined(targetElement.attr("data-ajaxFormPostEnabled"))) { if (isAttrDefined(targetElement.attr("href"))) { if (targetElement.attr("href").indexOf("#") != 0) { targetElement.removeAttr("href"); //remove the asp.net call (__doPostBack) } } targetElement.click(function () { buttonClick(targetElement); return false; }); //Mark as enabled. targetElement.attr("data-ajaxFormPostEnabled", true); } } function enableFeature_FormGroupFeatures(targetElement, parentElementSelector) { lastErroredCallTrace += "->enableFeature_FormGroupFeatures"; //1. Set Enter Key Event on Form Fields to Simulates Button Click //2. Set Horizontal Form Layout Equal Label Tag Width var activeButtonID = targetElement.attr("id"); var activeButtonFormGroup = targetElement.attr("data-formGroup"); //Loop through associated fields and set keyup event & required field indicator (This is done now on all. Cannot just search for [required], because maybe was required and then marked not to know and need to clear indicator) lastErroredCallTrace += "->simulateButtonClick"; $(parentElementSelector + " [data-formGroup='" + activeButtonFormGroup + "'][name]").each(function () { //SimulateButtonClick //Check to see if already enabled. if (!isAttrDefined($(this).attr("data-enterKeySimulateButtonClickEnabled"))) { //Don't add this to textareas. When hit enter user wants to add new line and not submit. if ($(this).get(0).tagName != "TEXTAREA") { $(this).keyup(function (event) { //13 = Enter if (getKeycode(event) == 13) { simulateButtonClick("#" + activeButtonID); } }); } //Mark as enabled. $(this).attr("data-enterKeySimulateButtonClickEnabled", true); } //Required Field Indicator lastErroredCallTrace += "->requiredIndicator"; var tempLabel = $(parentElementSelector + " label[for='" + $(this).attr("id") + "']"); if ($(this)[0].getAttribute("required") !== null && tempLabel.html() != null) { if (tempLabel.html().indexOf($("#hdivRequiredFieldIndicator").html()) < 0) { //Only add it once. tempLabel.append($("#hdivRequiredFieldIndicator").html()); } } else if (tempLabel.html() != null && tempLabel.html().indexOf($("#hdivRequiredFieldIndicator").html()) > 0) { tempLabel.html(tempLabel.html().replace($("#hdivRequiredFieldIndicator").html(), "")); } }); //Horizontal Form Layout Equal Label Tag Width var widest = 0; //Loop through and determine the widest Label Tag for each form group. lastErroredCallTrace += "->horizontalFormLayout"; $(parentElementSelector + " .horizontalFormLayout [data-formGroup='" + activeButtonFormGroup + "'][name]").each(function () { var tempWidth = $(parentElementSelector + " .horizontalFormLayout label[for='" + $(this).attr("id") + "']").css("width"); $(parentElementSelector + " .horizontalFormLayout label[for='" + $(this).attr("id") + "']").css("width", "auto"); if (widest < $(parentElementSelector + " .horizontalFormLayout label[for='" + $(this).attr("id") + "']").width() && $(parentElementSelector + " .horizontalFormLayout label[for='" + $(this).attr("id") + "']").attr("data-excludeFromLabelWidth") != "true" && !$(parentElementSelector + " .horizontalFormLayout label[for='" + $(this).attr("id") + "']").hasClass("checkbox")) { widest = $(parentElementSelector + " .horizontalFormLayout label[for='" + $(this).attr("id") + "']").width(); } $(parentElementSelector + " .horizontalFormLayout label[for='" + $(this).attr("id") + "']").css("width", tempWidth); }); if (widest > 0) { //Loop through again and set all to the widest for each form group. $(parentElementSelector + " .horizontalFormLayout [data-formGroup='" + activeButtonFormGroup + "'][name]").each(function () { if ($(parentElementSelector + " .horizontalFormLayout label[for='" + $(this).attr("id") + "']").attr("data-excludeFromLabelWidth") != "true" && widest > 0 && !$(parentElementSelector + " .horizontalFormLayout label[for='" + $(this).attr("id") + "']").hasClass("checkbox")) { $(parentElementSelector + " .horizontalFormLayout label[for='" + $(this).attr("id") + "']").width(widest); } }); } } function enableFeature_Placeholder(targetElement) { lastErroredCallTrace += "->enableFeature_Placeholder"; //Check to see if already enabled. if (!isAttrDefined(targetElement.attr("data-placeholderEnabled"))) { if (!Modernizr.input.placeholder) { if (targetElement.get(0).nodeName.toLowerCase() == "input" && targetElement.attr("type").toLowerCase() == "password") { //Create fake input field to provide placeholder for password field. var passwordFieldElementId = targetElement.attr("id"); var passwordPlaceholderElement = document.createElement("input"); passwordPlaceholderElement.id = passwordFieldElementId + "_placeholderFakeInput"; passwordPlaceholderElement.type = "text"; passwordPlaceholderElement.value = targetElement.attr("placeholder"); passwordPlaceholderElement.setAttribute("style", targetElement.attr("style") + " float:left; margin-left:-" + (targetElement.width() + 6) + "px;"); passwordPlaceholderElement.className = targetElement.attr("class") + " placeholder"; targetElement.after(passwordPlaceholderElement); $("#" + passwordPlaceholderElement.id).focus(function () { targetElement.hide(); $("#" + passwordFieldElementId).focus(); }); targetElement.focus(function () { $("#" + passwordPlaceholderElement.id).hide(); }); targetElement.blur(function () { if (!targetElement.val()) { $("#" + passwordPlaceholderElement.id).show(); } }); } else { targetElement.addClass("placeholder"); targetElement.val(targetElement.attr("placeholder")); targetElement.focus(function () { if (targetElement.val() == targetElement.attr("placeholder")) { targetElement.removeClass("placeholder"); targetElement.val(""); targetElement.select(); } }); targetElement.change(function () { if (targetElement.val != targetElement.attr("placeholder")) { targetElement.removeClass("placeholder"); } }); targetElement.blur(function () { if (!targetElement.val()) { targetElement.addClass("placeholder"); targetElement.val(targetElement.attr("placeholder")); } }); } } //Mark as enabled. targetElement.attr("data-placeholderEnabled", true); } } /***** Abstracted Methods *****/ //Abstracted Methods for Ajax Form(s) + HTML5 Feature Support function bindAjaxFormPostEvents(parentElementSelector) { //This would be called on page load, or after client-side DOM modification. lastErroredCallTrace += "->bindAjaxFormPostEvents"; if (typeof parentElementSelector === "undefined") { parentElementSelector = "body" } //Enable AjaxFormPost(s) lastErroredCallTrace += "->ajaxFormPost"; $(parentElementSelector + " .ajaxFormPost, " + parentElementSelector + " .clickFunction").each(function () { var tempThisVar = $(this); self.setTimeout(function () { enableFeature_AjaxFormPostAndClickFunction(tempThisVar); }, 1); }); //Enable autofocus, if not natively supported by the browser. Sets focus on first item found by selector. lastErroredCallTrace += "->autofocus"; if (!Modernizr.input.autofocus) { $(parentElementSelector + " [autofocus]").focus(); } //Enable placeholder(s), if not natively supported by the browser. lastErroredCallTrace += "->placeholder"; $(parentElementSelector + " [placeholder]").each(function () { var tempThisVar = $(this); self.setTimeout(function () { enableFeature_Placeholder(tempThisVar); }, 1); }); lastErroredCallTrace += "->formGroupLoop"; $(parentElementSelector + " .ajaxFormPost[data-formGroup]").each(function () { var tempThisVar = $(this); self.setTimeout(function () { enableFeature_FormGroupFeatures(tempThisVar, parentElementSelector); }, 1); }); } function postAjaxForm(requestingElement, runValidation) { lastErroredCallTrace += "->postAjaxForm"; var isFormValid = true; if (runValidation) { isFormValid = isFormValid(requestingElement); } //If form is valid, build the JSON and make the Ajax POST call. if (isFormValid) { //Find all the corresponding form fields and map them to the service method parameters and add them to JSON string for POST. var isFirstItem = true; var isParameterObject = (isAttrDefined(requestingElement.attr("data-parameterObjectName")) && requestingElement.attr("data-parameterObjectName").length > 0); var jsonToPost = "{"; if (isParameterObject) { jsonToPost += "\"" + requestingElement.attr("data-parameterObjectName") + "\":{"; } $("[data-formGroup='" + requestingElement.attr("data-formGroup") + "']").each(function () { if (isAttrDefined($(this).attr("name"))) { if (isFirstItem) { isFirstItem = false; } else { jsonToPost += "," } jsonToPost += "\"" + $(this).attr("name") + "\":"; jsonToPost += "\"" + getFormElementValue($(this)) + "\""; } }); jsonToPost += "}"; if (isParameterObject) { jsonToPost += "}"; } //Manual JSON Build if (isAttrDefined(requestingElement.attr("data-manualDataBuildFunction"))) { jsonToPost = callFunctionDynamically(requestingElement.attr("data-manualDataBuildFunction"), requestingElement, jsonToPost); } postAjax(requestingElement, jsonToPost, requestingElement.attr("data-serviceRequestUrl"), requestingElement.attr("data-successFunction")); } else { alert(validationErrorMessage); } } function isFormValid(requestingButtonElement) { lastErroredCallTrace += "->isFormValid"; var isFormValid = true; var validationErrorMessage = $("#lblMasterErrorMessageIntroduction").html() + "\n\n"; var isFirstError = true; var firstErrorFormField; //Clear any previous validation clearFormValidation(requestingButtonElement.attr("data-formGroup")); //Validation Check ("required" & "pattern") $("[data-formGroup='" + requestingButtonElement.attr("data-formGroup") + "'][required],[data-formGroup='" + requestingButtonElement.attr("data-formGroup") + "'][pattern]").each(function () { //Required if ($(this)[0].getAttribute("required") !== null) { if (!$(this).val() || $(this).val() == $(this).attr("placeholder") || ($(this).attr("type") == "checkbox" && !$(this).prop("checked"))) { $(this).addClass("errorField"); //Special password field placeholder support. if (!Modernizr.input.placeholder && this.nodeName.toLowerCase() == "input" && $(this).attr("type").toLowerCase() == "password" && isAttrDefined($(this).attr("placeholder"))) { $("#" + $(this).attr("id") + "_placeholderFakeInput").addClass("errorField"); $("#" + $(this).attr("id") + "_placeholderFakeInput").css("marginLeft", "-" + ($(this).width() + 4) + "px"); } var errorMessage = "\"" + $.trim($("label[for='" + $(this).attr("id") + "']").text().replace(":", "").replace($("#hdivRequiredFieldIndicator").html(), "").replace("*", "")) + "\"" + $("#lblMasterErrorMessageIsRequired").html(); $(this).attr("title", errorMessage); validationErrorMessage += " - " + errorMessage + "\n"; if (isFirstError) { isFirstError = false; firstErrorFormField = $(this); } $(this).keyup(function () { if (!$(this).val() || $(this).val() == $(this).attr("placeholder")) { $(this).addClass("errorField"); $(this).attr("title", errorMessage); //Special password field placeholder support. if (!Modernizr.input.placeholder && this.nodeName.toLowerCase() == "input" && $(this).attr("type").toLowerCase() == "password" && isAttrDefined($(this).attr("placeholder"))) { $("#" + $(this).attr("id") + "_placeholderFakeInput").addClass("errorField"); $("#" + $(this).attr("id") + "_placeholderFakeInput").css("marginLeft", "-" + ($(this).width() + 4) + "px"); } } else { $(this).removeClass("errorField"); $(this).attr("title", ""); //Special password field placeholder support. if (!Modernizr.input.placeholder && this.nodeName.toLowerCase() == "input" && $(this).attr("type").toLowerCase() == "password" && isAttrDefined($(this).attr("placeholder"))) { $("#" + $(this).attr("id") + "_placeholderFakeInput").removeClass("errorField"); $("#" + $(this).attr("id") + "_placeholderFakeInput").css("marginLeft", "-" + ($(this).width() + 6) + "px;"); } } }); isFormValid = false; } } //Pattern if (isAttrDefined($(this).attr("pattern"))) { if ($(this).attr("pattern").length > 0 && $(this).val() && $(this).val() != $(this).attr("placeholder")) { try { var regExp = new RegExp($(this).attr("pattern")); if (!regExp.test($(this).val())) { $(this).addClass("errorField"); var errorMessage = "\"" + $.trim($("label[for='" + $(this).attr("id") + "']").text().replace(":", "").replace($("#hdivRequiredFieldIndicator").html(), "").replace("*", "")) + "\"" + $("#lblMasterErrorMessageIsInvalid").html(); $(this).attr("title", errorMessage); validationErrorMessage += " - " + errorMessage + "\n"; if (isFirstError) { isFirstError = false; firstErrorFormField = $(this); } $(this).keyup(function () { if (!regExp.test($(this).val())) { $(this).addClass("errorField"); $(this).attr("title", errorMessage); } else { $(this).removeClass("errorField"); $(this).attr("title", ""); } }); isFormValid = false; } } catch (ex) { //Regular Expression is not well formatted. $(this).addClass("errorField"); var errorMessage = "\"" + $.trim($("label[for='" + $(this).attr("id") + "']").text().replace(":", "").replace($("#hdivRequiredFieldIndicator").html(), "").replace("*", "")) + "\"" + $("#lblMasterErrorMessageIsInvalid").html() + " RexExp failed. Error: " + ex; $(this).attr("title", errorMessage); validationErrorMessage += " - " + errorMessage + "\n"; isFormValid = false; } } } }); //Validation Check (manual) if (isFormValid && isAttrDefined(requestingButtonElement.attr("data-manualValidationFunction"))) { jsonResult = callFunctionDynamically(requestingButtonElement.attr("data-manualValidationFunction"), requestingButtonElement, null); if (!jsonResult.IsFormValid || jsonResult.IsFormValid == "false") { isFormValid = false; validationErrorMessage += jsonResult.AdditionalAlertErrorMessage; firstErrorFormField = $(jsonResult.FirstErrorFormFieldSelector); } } if (!isFormValid) { if (requestingButtonElement.attr("data-showAlertMessageOnError") != "false") { alert(validationErrorMessage); } try { firstErrorFormField.focus(); } catch (ex) { //Do nothing if not work. } } return isFormValid; } function clearFormValidation(formGroup) { lastErroredCallTrace += "->clearFormValidation"; $("[data-formGroup='" + formGroup + "']").removeClass("errorField"); } function clearAjaxForm(requestingElement) { lastErroredCallTrace += "->clearAjaxForm"; $("[data-formGroup='" + requestingElement.attr("data-formGroup") + "']").each(function () { if (isAttrDefined($(this).attr("name"))) { $(this).val(""); } }); }
function successFormTest1() { //Hide Confirmation & Error Messages $("#hdivFormTest1ConfirmationMessage").addClass("hide"); $("#hdivFormTest1ErrorMessage").addClass("hide"); switch (lastJsonResult.Status) { case "Valid": $("#hdivFormTest1ConfirmationMessage").html("<h4>Welcome " + lastJsonResult.Name.First + " " + lastJsonResult.Name.Last + " (" + lastJsonResult.Username + ")!</h4>You are now signed in. Well, not really. This is just a demo ya know?"); $("#hdivFormTest1ConfirmationMessage").removeClass("hide"); break; case "Invalid": $("#hdivFormTest1ErrorMessage").html("<h4>Sign-in failed!</h4>The username \"" + lastJsonResult.Username + "\" does not exist, or your password is wrong."); $("#hdivFormTest1ErrorMessage").removeClass("hide"); break; } clearAjaxForm(lastRequestingElement); } function successFormTest2() { //Hide Confirmation & Error Messages $("#hdivFormTest2ConfirmationMessage").addClass("hide"); $("#hdivFormTest2ErrorMessage").addClass("hide"); switch (lastJsonResult.Status) { case "Added": $("#hdivFormTest2ConfirmationMessage").html("<h4>Person Added!</h4>The following person was added: " + lastJsonResult.Person.FullName + " (" + lastJsonResult.Person.City + ", " + lastJsonResult.Person.StateAbbreviated + " " + lastJsonResult.Person.ZipCode + ")"); $("#hdivFormTest2ConfirmationMessage").removeClass("hide"); break; case "Duplicate": $("#hdivFormTest2ErrorMessage").html("<h4>Person Already Exists!</h4>The following person already exists: " + lastJsonResult.Person.FullName); $("#hdivFormTest2ErrorMessage").removeClass("hide"); break; } clearAjaxForm(lastRequestingElement); } (function ($) { $(document).ready(function () { //Form-specific script to test the ability to massage the data before submitting. $("#ddlPersonStateAbbreviated").bind("change", function() { $("#hfPersonState").val($("#ddlPersonStateAbbreviated option[value='" + $("#ddlPersonStateAbbreviated").val() + "']").text()); }); }); })(this.jQuery);
/* Form Stuff */ .form { margin:0px 0px 12px 12px; } .legendLook { border:1px dashed #B8AFA2; padding:0px 0px 0px 15px; } .form label { display:table; padding:12px 0px 3px 0px; font-weight:bold; white-space:nowrap; } .horizontalFormLayout label { clear:left; float:left; padding-right:6px; } .horizontalFormLayout input { float:left; margin:9px 0px 0px 0px; } .formButtonWrapper { clear:left; margin:12px 0px 12px 0px; } .requiredAsterisk { color:#AB0000; font-weight:bold; padding-left:2px; } .errorField{ border:1px solid #AB0000; } .errorMessage{ color:#AB0000; font-weight:bold; } .errorMessageWrapper { margin:12px 15px 12px 0px; border:1px dashed #AB0000; background-color:#EDF0DA; color:#AB0000; padding:6px; } .confirmationMessageWrapper { margin:12px 15px 12px 0px; border:1px dashed #50692A; background-color:#EDF0DA; color:#50692A; padding:6px; } .errorMessageWrapper h4, .confirmationMessageWrapper h4 { font-size:1em; margin:0 0 0 0; font-style:normal; display:block; } .placeholder { color:#B8AFA2; } input::-webkit-input-placeholder { color:#B8AFA2; } input:-moz-placeholder { color:#B8AFA2; }