This is an automated email from the ASF dual-hosted git repository.

jleroux pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 79e55dd6e1 Improved: Use browser validation for input element, support 
types (#862)
79e55dd6e1 is described below

commit 79e55dd6e1790211f3a14710caa2947adb1ce5d3
Author: Florian Motteau <florian.mott...@nereide.fr>
AuthorDate: Mon Dec 16 17:29:19 2024 +0100

    Improved: Use browser validation for input element, support types (#862)
    
    * Improved: Use browser validation for input element, support types
    
    Rely on browser native validation for inputs, using the "required"
    attribute, instead of jQuery Validation plugin.
    Support "number", "email", "url" and "tel" input types.
    Support "pattern" attribute for input types that supports it.
    Fix boolean attributes uses in FTL macros ("readonly", "required").
    Fix required behavior when "required-field-style" is not empty.
    Add "required" attribute on password fields.
    Add "required" attribute to inputs in login forms
    
    OFBIZ-13183
    
    * Improved: Use input type=number for numeric fields
    
    OFBIZ-13183
    
    * Adjust new input types appearance
    
    OFBIZ-13183
    
    * Use input type "email" instead of "text" for email fields
    
    OFBIZ-13183
    
    * Fix checkstyle issues
    
    OFBIZ-13183
    
    * Fix XSD issue
    
    You can't have both type="xs:string" and a nested complexType for the same 
element.
    
    OFBIZ-13183
    
    * Always add an asterisk on mandatory fields
    
    Even if we defined a "required-field-style" attribute
    
    OFBIZ-13183
    
    * Fix test on required-field-style attribute
    
    OFBIZ-13183
    
    * Update renderAsterisks macro declarations
    
    OFBIZ-13183
---
 applications/accounting/widget/InvoiceForms.xml    |  10 +-
 .../humanres/widget/forms/EmployeeForms.xml        |   2 +-
 .../marketing/widget/sfa/forms/AccountForms.xml    |   2 +-
 .../marketing/widget/sfa/forms/ContactForms.xml    |   2 +-
 .../marketing/widget/sfa/forms/LeadForms.xml       |   4 +-
 applications/order/template/entry/CustSettings.ftl |   2 +-
 .../party/template/party/EditContactMech.ftl       |   2 +-
 .../widget/partymgr/CommunicationEventForms.xml    |   2 +-
 .../product/template/facility/EditContactMech.ftl  |   2 +-
 framework/widget/dtd/widget-form.xsd               |  19 +++
 .../apache/ofbiz/widget/model/ModelFormField.java  |  32 ++++-
 .../ofbiz/widget/model/ModelFormFieldBuilder.java  |   7 +-
 .../macro/RenderableFtlFormElementsBuilder.java    |  33 +++--
 .../RenderableFtlFormElementsBuilderTest.java      | 148 +++++++++++++++++++++
 themes/bluelight/webapp/bluelight/style.css        |   3 +-
 themes/common-theme/template/Login.ftl             |   4 +-
 .../template/macro/CsvFormMacroLibrary.ftl         |   2 +-
 .../template/macro/FoFormMacroLibrary.ftl          |   2 +-
 .../template/macro/HtmlFormMacroLibrary.ftl        |  38 ++++--
 .../template/macro/TextFormMacroLibrary.ftl        |   2 +-
 .../template/macro/XlsFormMacroLibrary.ftl         |   2 +-
 .../template/macro/XmlFormMacroLibrary.ftl         |   2 +-
 .../webapp/common-theme/js/util/OfbizUtil.js       |   4 -
 themes/helveticus/template/Login.ftl               |   4 +-
 .../webapp/helveticus/helveticus-main-theme.less   |   6 +-
 themes/rainbowstone/template/Login.ftl             |   4 +-
 themes/tomahawk/webapp/tomahawk/css/style.css      |   3 +-
 27 files changed, 281 insertions(+), 62 deletions(-)

diff --git a/applications/accounting/widget/InvoiceForms.xml 
b/applications/accounting/widget/InvoiceForms.xml
index c14a7390ed..6035393580 100644
--- a/applications/accounting/widget/InvoiceForms.xml
+++ b/applications/accounting/widget/InvoiceForms.xml
@@ -636,11 +636,11 @@ under the License.
             </service>
         </actions>
         <field name="invoiceId"><hidden/></field>
-        <field name="emailAddressFrom" entry-name="mapFrom.emailAddress" 
parameter-name="sendFrom" 
use-when="&quot;${invoice.invoiceTypeId}&quot;.equals(&quot;SALES_INVOICE&quot;)"><text/></field>
-        <field name="emailAddressFrom" entry-name="mapTo.emailAddress" 
parameter-name="sendFrom" 
use-when="&quot;${invoice.invoiceTypeId}&quot;.equals(&quot;PURCHASE_INVOICE&quot;)"><text/></field>
-        <field name="emailAddressTo" entry-name="mapTo.emailAddress" 
parameter-name="sendTo" 
use-when="&quot;${invoice.invoiceTypeId}&quot;.equals(&quot;SALES_INVOICE&quot;)"><text/></field>
-        <field name="emailAddressTo" entry-name="mapFrom.emailAddress" 
parameter-name="sendTo" 
use-when="&quot;${invoice.invoiceTypeId}&quot;.equals(&quot;PURCHASE_INVOICE&quot;)"><text/></field>
-        <field name="emailAddressCc" entry-name="ccEmailAddress" 
parameter-name="sendCc"><text/></field>
+        <field name="emailAddressFrom" entry-name="mapFrom.emailAddress" 
parameter-name="sendFrom" 
use-when="&quot;${invoice.invoiceTypeId}&quot;.equals(&quot;SALES_INVOICE&quot;)"><text
 type="email"/></field>
+        <field name="emailAddressFrom" entry-name="mapTo.emailAddress" 
parameter-name="sendFrom" 
use-when="&quot;${invoice.invoiceTypeId}&quot;.equals(&quot;PURCHASE_INVOICE&quot;)"><text
 type="email"/></field>
+        <field name="emailAddressTo" entry-name="mapTo.emailAddress" 
parameter-name="sendTo" 
use-when="&quot;${invoice.invoiceTypeId}&quot;.equals(&quot;SALES_INVOICE&quot;)"><text
 type="email"/></field>
+        <field name="emailAddressTo" entry-name="mapFrom.emailAddress" 
parameter-name="sendTo" 
use-when="&quot;${invoice.invoiceTypeId}&quot;.equals(&quot;PURCHASE_INVOICE&quot;)"><text
 type="email"/></field>
+        <field name="emailAddressCc" entry-name="ccEmailAddress" 
parameter-name="sendCc"><text type="email"/></field>
         <field name="subject"><text default-value="Please find attached 
invoice."/></field>
         <field name="otherCurrency" entry-name="parameters.other" 
parameter-name="other"><check/></field>
         <field name="bodyText"><textarea/></field>
diff --git a/applications/humanres/widget/forms/EmployeeForms.xml 
b/applications/humanres/widget/forms/EmployeeForms.xml
index 8fd76ffc47..93724e031d 100644
--- a/applications/humanres/widget/forms/EmployeeForms.xml
+++ b/applications/humanres/widget/forms/EmployeeForms.xml
@@ -63,7 +63,7 @@
         <field name="contactNumber" title="${uiLabelMap.PartyPhoneNumber}" 
required-field="true"><text size="15" maxlength="15"/></field>
         <field name="extension" title="${uiLabelMap.PartyContactExt}"><text 
size="6" maxlength="10"/></field>
         <field name="emailAddressTitle" 
title="${uiLabelMap.PartyEmailAddress}" 
title-area-style="group-label"><display/></field>
-        <field name="emailAddress" title="${uiLabelMap.CommonEmail}"><text 
size="50" maxlength="60"/></field>
+        <field name="emailAddress" title="${uiLabelMap.CommonEmail}"><text 
size="50" maxlength="60" type="email"/></field>
         <field name="submitButton" title="${uiLabelMap.CommonSave}" 
widget-style="smallSubmit"><submit button-type="button"/></field>
     </form>
     <form name="AddEmployeeSkills" type="single" target="createEmployeeSkill" 
default-map-name="partySkill">
diff --git a/applications/marketing/widget/sfa/forms/AccountForms.xml 
b/applications/marketing/widget/sfa/forms/AccountForms.xml
index c1540527b0..b6f81d80d9 100644
--- a/applications/marketing/widget/sfa/forms/AccountForms.xml
+++ b/applications/marketing/widget/sfa/forms/AccountForms.xml
@@ -64,7 +64,7 @@ under the License.
         <field name="contactNumber" 
title="${uiLabelMap.PartyPhoneNumber}"><text size="15" maxlength="15"/></field>
         <field name="extension" title="${uiLabelMap.PartyContactExt}"><text 
size="6" maxlength="10"/></field>
         <field name="emailAddressTitle" 
title="${uiLabelMap.PartyEmailAddress}" 
title-area-style="group-label"><display/></field>
-        <field name="emailAddress" title="${uiLabelMap.CommonEmail}"><text 
size="50" maxlength="60"/></field>
+        <field name="emailAddress" title="${uiLabelMap.CommonEmail}"><text 
size="50" maxlength="60" type="email"/></field>
         <field name="submitButton" title="${uiLabelMap.CommonSave}" 
widget-style="smallSubmit"><submit button-type="button"/></field>
     </form>
 
diff --git a/applications/marketing/widget/sfa/forms/ContactForms.xml 
b/applications/marketing/widget/sfa/forms/ContactForms.xml
index 1ff65396a6..83f71885a5 100644
--- a/applications/marketing/widget/sfa/forms/ContactForms.xml
+++ b/applications/marketing/widget/sfa/forms/ContactForms.xml
@@ -141,7 +141,7 @@ under the License.
         <field name="contactNumber" 
title="${uiLabelMap.PartyPhoneNumber}"><text size="15" maxlength="15"/></field>
         <field name="extension" title="${uiLabelMap.PartyContactExt}"><text 
size="6" maxlength="10"/></field>
         <field name="emailAddressTitle" 
title="${uiLabelMap.PartyEmailAddress}" 
title-area-style="group-label"><display/></field>
-        <field name="emailAddress" title="${uiLabelMap.CommonEmail}"><text 
size="50" maxlength="60"/></field>
+        <field name="emailAddress" title="${uiLabelMap.CommonEmail}"><text 
size="50" maxlength="60" type="email"/></field>
         <field name="contactListTitle" 
title="${uiLabelMap.MarketingContactList}" 
title-area-style="group-label"><display/></field>
         <field name="contactListId" title="${uiLabelMap.MarketingContactList}">
             <drop-down allow-empty="true">
diff --git a/applications/marketing/widget/sfa/forms/LeadForms.xml 
b/applications/marketing/widget/sfa/forms/LeadForms.xml
index 59070b6a30..2db0fc9ec6 100644
--- a/applications/marketing/widget/sfa/forms/LeadForms.xml
+++ b/applications/marketing/widget/sfa/forms/LeadForms.xml
@@ -56,7 +56,7 @@ under the License.
         <field name="contactNumber" 
title="${uiLabelMap.PartyPhoneNumber}"><text size="15" maxlength="15"/></field>
         <field name="extension" title="${uiLabelMap.PartyContactExt}"><text 
size="6" maxlength="10"/></field>
         <field name="emailAddressTitle" 
title="${uiLabelMap.PartyEmailAddress}" 
title-area-style="group-label"><display/></field>
-        <field name="emailAddress" title="${uiLabelMap.CommonEmail}"><text 
size="50" maxlength="60"/></field>
+        <field name="emailAddress" title="${uiLabelMap.CommonEmail}"><text 
size="50" maxlength="60" type="email"/></field>
         <field name="leadSourceTitle" title="${uiLabelMap.SfaLeadSource}" 
title-area-style="group-label"><display/></field>
         <field name="dataSourceId" title="${uiLabelMap.SfaLeadSource}">
             <drop-down allow-empty="true">
@@ -123,7 +123,7 @@ under the License.
         <field name="firstName" title="${uiLabelMap.PartyFirstName}" 
required-field="true"><text size="15"/></field>
         <field name="lastName" title="${uiLabelMap.PartyLastName}" 
required-field="true"><text size="15"/></field>
         <field name="groupName" title="${uiLabelMap.CommonGroup}"><text 
size="15"/></field>
-        <field name="emailAddress" title="${uiLabelMap.CommonEmail}"><text 
size="15"/></field>
+        <field name="emailAddress" title="${uiLabelMap.CommonEmail}"><text 
size="15" type="email"/></field>
         <field name="contactListId" title="${uiLabelMap.MarketingContactList}">
             <drop-down allow-empty="true">
                 <entity-options entity-name="ContactList" 
key-field-name="contactListId" 
description="${groovy:contactListName.substring(0,Math.min(contactListName.length(),
 12))}..."/>
diff --git a/applications/order/template/entry/CustSettings.ftl 
b/applications/order/template/entry/CustSettings.ftl
index 791a97ebb4..82542c3caa 100644
--- a/applications/order/template/entry/CustSettings.ftl
+++ b/applications/order/template/entry/CustSettings.ftl
@@ -107,7 +107,7 @@ under the License.
                 <td width="26%" 
align="right"><div>${uiLabelMap.PartyEmailAddress}<br/>${uiLabelMap.OrderAllowSolicitation}</div></td>
                 <td width="5">&nbsp;</td>
                 <td width="74%">
-                  <input type="text" name="emailAddress" value="" size="60" 
maxlength="255" />
+                  <input type="text" name="emailAddress" value="" size="60" 
maxlength="255" type="email"/>
                   <br/>
                   <select name="emailSol">
                     <#if ("Y" == ((requestParameters.emailSol)!""))><option 
value="Y">${uiLabelMap.CommonY}</option></#if>
diff --git a/applications/party/template/party/EditContactMech.ftl 
b/applications/party/template/party/EditContactMech.ftl
index f418ab3053..cfb9ebaded 100644
--- a/applications/party/template/party/EditContactMech.ftl
+++ b/applications/party/template/party/EditContactMech.ftl
@@ -203,7 +203,7 @@ under the License.
       <tr>
           <td 
class="label">${mechMap.contactMechType.get("description",locale)}</td>
           <td>
-              <input type="text" size="60" maxlength="255" name="emailAddress" 
value="${(mechMap.contactMech.infoString)?default(request.getParameter('emailAddress')!)}"
 />
+              <input type="email" size="60" maxlength="255" 
name="emailAddress" 
value="${(mechMap.contactMech.infoString)?default(request.getParameter('emailAddress')!)}"
 />
           </td>
       </tr>
   <#elseif "FTP_ADDRESS" = mechMap.contactMechTypeId!>
diff --git a/applications/party/widget/partymgr/CommunicationEventForms.xml 
b/applications/party/widget/partymgr/CommunicationEventForms.xml
index b526dda26b..f4d5a9effa 100644
--- a/applications/party/widget/partymgr/CommunicationEventForms.xml
+++ b/applications/party/widget/partymgr/CommunicationEventForms.xml
@@ -928,7 +928,7 @@ under the License.
         <field name="donePage"><hidden value="${donePage}"/></field>
         <field name="communicationEventId"><hidden 
value="${parameters.communicationEventId}"/></field>
         <field name="partyId" entry-name="dummy" 
tooltip="${uiLabelMap.PartyLeaveEmpty}"><lookup 
target-form-name="LookupPartyName"/></field>
-        <field name="emailAddress"><text/></field>
+        <field name="emailAddress"><text type="email"/></field>
         <field name="firstName" position="1"><text/></field>
         <field name="middleName" position="2"><text/></field>
         <field name="lastName"><text/></field>
diff --git a/applications/product/template/facility/EditContactMech.ftl 
b/applications/product/template/facility/EditContactMech.ftl
index 1ec648119e..356d133e1c 100644
--- a/applications/product/template/facility/EditContactMech.ftl
+++ b/applications/product/template/facility/EditContactMech.ftl
@@ -221,7 +221,7 @@ under the License.
     <tr>
       <td class="label">${uiLabelMap.PartyEmailAddress}</td>
       <td>
-          <input type="text" class="required" size="60" maxlength="255" 
name="emailAddress" 
value="${(mechMap.contactMech.infoString)?default(request.getParameter('emailAddress')!)}"
 />
+          <input type="email" class="required" size="60" maxlength="255" 
name="emailAddress" 
value="${(mechMap.contactMech.infoString)?default(request.getParameter('emailAddress')!)}"
 />
       *</td>
     </tr>
   <#else>
diff --git a/framework/widget/dtd/widget-form.xsd 
b/framework/widget/dtd/widget-form.xsd
index 76e9cda4f0..25993c66d7 100644
--- a/framework/widget/dtd/widget-form.xsd
+++ b/framework/widget/dtd/widget-form.xsd
@@ -1497,6 +1497,25 @@ under the License.
                     <xs:documentation>Specifies a short hint that describes 
the expected value of an input field.</xs:documentation>
                 </xs:annotation>
             </xs:attribute>
+            <xs:attribute name="type" default="text">
+                <xs:annotation>
+                    <xs:documentation>Controls the type attribute on the input 
element. Limited to a few types. See 
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types</xs:documentation>
+                </xs:annotation>
+                <xs:simpleType>
+                    <xs:restriction base="xs:token">
+                        <xs:enumeration value="text" />
+                        <xs:enumeration value="number" />
+                        <xs:enumeration value="email" />
+                        <xs:enumeration value="url" />
+                        <xs:enumeration value="tel" />
+                    </xs:restriction>
+                </xs:simpleType>
+            </xs:attribute>
+            <xs:attribute name="pattern" type="xs:string">
+                <xs:annotation>
+                    <xs:documentation>Controls the pattern attribute of the 
input element. See 
https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/pattern</xs:documentation>
+                </xs:annotation>
+            </xs:attribute>
         </xs:complexType>
     </xs:element>
     <xs:element name="textarea" substitutionGroup="AllFields">
diff --git 
a/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelFormField.java
 
b/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelFormField.java
index fdb653f9f8..02b6b1de08 100644
--- 
a/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelFormField.java
+++ 
b/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelFormField.java
@@ -5441,12 +5441,16 @@ public final class ModelFormField {
         private final boolean readonly;
         private final int size;
         private final SubHyperlink subHyperlink;
+        private final String type;
+        private final String pattern;
 
         public TextField(Element element, ModelFormField modelFormField) {
             super(element, modelFormField);
             this.clientAutocompleteField = 
!"false".equals(element.getAttribute("client-autocomplete-field"));
             this.defaultValue = 
FlexibleStringExpander.getInstance(element.getAttribute("default-value"));
             this.mask = element.getAttribute("mask");
+            this.type = element.getAttribute("type");
+            this.pattern = element.getAttribute("pattern");
             Integer maxlength = null;
             String maxlengthStr = element.getAttribute("maxlength");
             if (!maxlengthStr.isEmpty()) {
@@ -5484,6 +5488,8 @@ public final class ModelFormField {
             this.clientAutocompleteField = true;
             this.defaultValue = FlexibleStringExpander.getInstance("");
             this.mask = "";
+            this.type = "";
+            this.pattern = "";
             this.maxlength = maxlength;
             this.placeholder = FlexibleStringExpander.getInstance("");
             this.readonly = false;
@@ -5491,11 +5497,13 @@ public final class ModelFormField {
             this.subHyperlink = null;
         }
 
-        protected TextField(int fieldSource, int size, Integer maxlength, 
ModelFormField modelFormField) {
+        protected TextField(int fieldSource, int size, Integer maxlength, 
String type, ModelFormField modelFormField) {
             super(fieldSource, FieldInfo.TEXT, modelFormField);
             this.clientAutocompleteField = true;
             this.defaultValue = FlexibleStringExpander.getInstance("");
             this.mask = "";
+            this.type = type;
+            this.pattern = "";
             this.maxlength = maxlength;
             this.placeholder = FlexibleStringExpander.getInstance("");
             this.readonly = false;
@@ -5508,6 +5516,8 @@ public final class ModelFormField {
             this.clientAutocompleteField = true;
             this.defaultValue = FlexibleStringExpander.getInstance("");
             this.mask = "";
+            this.type = "";
+            this.pattern = "";
             this.maxlength = null;
             this.placeholder = FlexibleStringExpander.getInstance("");
             this.readonly = false;
@@ -5528,6 +5538,8 @@ public final class ModelFormField {
             this.clientAutocompleteField = original.clientAutocompleteField;
             this.defaultValue = original.defaultValue;
             this.mask = original.mask;
+            this.type = original.type;
+            this.pattern = original.pattern;
             this.placeholder = original.placeholder;
             this.size = original.size;
             this.maxlength = original.maxlength;
@@ -5639,6 +5651,22 @@ public final class ModelFormField {
                 throws IOException {
             formStringRenderer.renderTextField(writer, context, this);
         }
+
+        /**
+         * Gets type.
+         * @return the type
+         */
+        public String getType() {
+            return this.type;
+        }
+
+        /**
+         * Gets pattern.
+         * @return the pattern
+         */
+        public String getPattern() {
+            return this.pattern;
+        }
     }
 
     /**
@@ -5666,7 +5694,7 @@ public final class ModelFormField {
         }
 
         public TextFindField(int fieldSource, int size, Integer maxlength, 
ModelFormField modelFormField) {
-            super(fieldSource, size, maxlength, modelFormField);
+            super(fieldSource, size, maxlength, "", modelFormField);
             this.defaultOption = UtilProperties.getPropertyValue("widget", 
"widget.form.defaultTextFindOption", "contains");
             this.hideIgnoreCase = false;
             this.hideOptions = false;
diff --git 
a/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelFormFieldBuilder.java
 
b/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelFormFieldBuilder.java
index 9647fa9641..d3429d8691 100644
--- 
a/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelFormFieldBuilder.java
+++ 
b/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelFormFieldBuilder.java
@@ -803,10 +803,10 @@ public class ModelFormFieldBuilder {
                 this.setFieldInfo(textareaField);
             } else if (TEXT_FIELD_TYPES.contains(fieldType)) {
                 ModelFormField.TextField textField = new 
ModelFormField.TextField(FieldInfo.SOURCE_AUTO_ENTITY,
-                        TEXT_SIZE_BY_FIELD_TYPES.get(fieldType), 
TEXT_MAX_SIZE_BY_FIELD_TYPES.get(fieldType), null);
+                        TEXT_SIZE_BY_FIELD_TYPES.get(fieldType), 
TEXT_MAX_SIZE_BY_FIELD_TYPES.get(fieldType), "text", null);
                 this.setFieldInfo(textField);
             } else if (NUMERIC_FIELD_TYPES.contains(fieldType)) {
-                ModelFormField.TextField textField = new 
ModelFormField.TextField(FieldInfo.SOURCE_AUTO_ENTITY, 6, null, null);
+                ModelFormField.TextField textField = new 
ModelFormField.TextField(FieldInfo.SOURCE_AUTO_ENTITY, 6, null, "number", null);
                 this.setFieldInfo(textField);
             } else if (DATA_FIELD_TYPES.contains(fieldType)) {
                 String type = fieldType;
@@ -880,7 +880,7 @@ public class ModelFormFieldBuilder {
             if ("text".equals(modelParamFieldType)) {
                 fieldInfo = new 
ModelFormField.TextField(FieldInfo.SOURCE_AUTO_SERVICE, null);
             } else if ("numeric".equals(modelParamFieldType)) {
-                fieldInfo = new 
ModelFormField.TextField(FieldInfo.SOURCE_AUTO_SERVICE, 6, null, null);
+                fieldInfo = new 
ModelFormField.TextField(FieldInfo.SOURCE_AUTO_SERVICE, 6, null, "number", 
null);
             } else if ("timestamp".equals(modelParamFieldType)) {
                 fieldInfo = new 
ModelFormField.DateTimeField(FieldInfo.SOURCE_AUTO_SERVICE, "timestamp");
             } else if ("date".equals(modelParamFieldType)) {
@@ -1047,6 +1047,7 @@ public class ModelFormFieldBuilder {
         this.encodeOutput = builder.getEncodeOutput();
         this.position = builder.getPosition();
         this.requiredField = builder.getRequiredField();
+        this.requiredFieldStyle = builder.getRequiredFieldStyle();
         this.separateColumn = builder.getSeparateColumn();
         this.disabled = builder.getDisabledSpec();
     }
diff --git 
a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/RenderableFtlFormElementsBuilder.java
 
b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/RenderableFtlFormElementsBuilder.java
index 92e336131d..413132c09f 100644
--- 
a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/RenderableFtlFormElementsBuilder.java
+++ 
b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/RenderableFtlFormElementsBuilder.java
@@ -108,16 +108,13 @@ public final class RenderableFtlFormElementsBuilder {
 
     public RenderableFtl asterisks(final Map<String, Object> context, final 
ModelFormField modelFormField) {
         String requiredField = "false";
-        String requiredStyle = "";
         if (modelFormField.getRequiredField()) {
             requiredField = "true";
-            requiredStyle = modelFormField.getRequiredFieldStyle();
         }
 
         return RenderableFtlMacroCall.builder()
                 .name("renderAsterisks")
                 .stringParameter("requiredField", requiredField)
-                .stringParameter("requiredStyle", requiredStyle)
                 .build();
     }
 
@@ -253,12 +250,20 @@ public final class RenderableFtlFormElementsBuilder {
                                    final boolean javaScriptEnabled) {
         ModelFormField modelFormField = textField.getModelFormField();
         String name = modelFormField.getParameterName(context);
-        String className = "";
+        String type = textField.getType();
+        if (UtilValidate.isEmpty(type)) {
+            type = "text";
+        }
+        String pattern = "";
+        if (List.of("text", "email", "url", "tel").contains(type)) {
+            pattern = textField.getPattern();
+        }
+        List<String> classes = new ArrayList<>();
         String alert = "false";
         String mask = "";
         String placeholder = textField.getPlaceholder(context);
         if (UtilValidate.isNotEmpty(modelFormField.getWidgetStyle())) {
-            className = modelFormField.getWidgetStyle();
+            classes.add(modelFormField.getWidgetStyle());
             if (modelFormField.shouldBeRed(context)) {
                 alert = "true";
             }
@@ -275,14 +280,13 @@ public final class RenderableFtlFormElementsBuilder {
         String clientAutocomplete = "false";
         //check for required field style on single forms
         if ("single".equals(modelFormField.getModelForm().getType()) && 
modelFormField.getRequiredField()) {
+            // kept for backward compatibility with existing CSS/JS
+            // maybe unused if jQuery Validation is no longer used
+            // for styling we should rely on "required" attribute
+            classes.add("required");
             String requiredStyle = modelFormField.getRequiredFieldStyle();
-            if (UtilValidate.isEmpty(requiredStyle)) {
-                requiredStyle = "required";
-            }
-            if (UtilValidate.isEmpty(className)) {
-                className = requiredStyle;
-            } else {
-                className = requiredStyle + " " + className;
+            if (UtilValidate.isNotEmpty(requiredStyle)) {
+                classes.add(requiredStyle);
             }
         }
         List<ModelForm.UpdateArea> updateAreas = 
modelFormField.getOnChangeUpdateAreas();
@@ -301,7 +305,9 @@ public final class RenderableFtlFormElementsBuilder {
         return RenderableFtlMacroCall.builder()
                 .name("renderTextField")
                 .stringParameter("name", name)
-                .stringParameter("className", className)
+                .stringParameter("className", String.join(" ", classes))
+                .stringParameter("type", type)
+                .stringParameter("pattern", pattern)
                 .stringParameter("alert", alert)
                 .stringParameter("value", value)
                 .stringParameter("textSize", textSize)
@@ -311,6 +317,7 @@ public final class RenderableFtlFormElementsBuilder {
                 .stringParameter("action", action != null ? action : "")
                 .booleanParameter("disabled", disabled)
                 .booleanParameter("readonly", readonly)
+                .booleanParameter("required", 
modelFormField.getRequiredField())
                 .stringParameter("clientAutocomplete", clientAutocomplete)
                 .stringParameter("ajaxUrl", ajaxUrl)
                 .booleanParameter("ajaxEnabled", ajaxEnabled)
diff --git 
a/framework/widget/src/test/java/org/apache/ofbiz/widget/renderer/macro/RenderableFtlFormElementsBuilderTest.java
 
b/framework/widget/src/test/java/org/apache/ofbiz/widget/renderer/macro/RenderableFtlFormElementsBuilderTest.java
index 80debaed87..c31b0a966d 100644
--- 
a/framework/widget/src/test/java/org/apache/ofbiz/widget/renderer/macro/RenderableFtlFormElementsBuilderTest.java
+++ 
b/framework/widget/src/test/java/org/apache/ofbiz/widget/renderer/macro/RenderableFtlFormElementsBuilderTest.java
@@ -240,6 +240,154 @@ public class RenderableFtlFormElementsBuilderTest {
                                 + "areaId2,http://host.domain/target2,";)));
     }
 
+    @Test
+    public void textFieldDefaultTypeIsText(@Mocked final 
ModelFormField.TextField textField) {
+        new Expectations() {
+            {
+                httpSession.getAttribute("delegatorName"); result = 
"DelegatorName";
+            }
+        };
+
+        final RenderableFtl renderableFtl = 
renderableFtlFormElementsBuilder.textField(Map.of("session", httpSession), 
textField, true);
+        assertThat(renderableFtl, MacroCallMatcher.hasName("renderTextField"));
+        assertThat(renderableFtl, 
MacroCallMatcher.hasParameters(MacroCallParameterMatcher.hasNameAndStringValue("type",
 "text")));
+    }
+
+    @Test
+    public void textFieldTypeNumber(@Mocked final ModelFormField.TextField 
textField) {
+        new Expectations() {
+            {
+                textField.getType(); result = "number";
+                httpSession.getAttribute("delegatorName"); result = 
"DelegatorName";
+            }
+        };
+
+        final RenderableFtl renderableFtl = 
renderableFtlFormElementsBuilder.textField(Map.of("session", httpSession), 
textField, true);
+        assertThat(renderableFtl, MacroCallMatcher.hasName("renderTextField"));
+        assertThat(renderableFtl, 
MacroCallMatcher.hasParameters(MacroCallParameterMatcher.hasNameAndStringValue("type",
 "number")));
+    }
+
+    @Test
+    public void textFieldTypeEmail(@Mocked final ModelFormField.TextField 
textField) {
+        new Expectations() {
+            {
+                textField.getType(); result = "email";
+                httpSession.getAttribute("delegatorName"); result = 
"DelegatorName";
+            }
+        };
+
+        final RenderableFtl renderableFtl = 
renderableFtlFormElementsBuilder.textField(Map.of("session", httpSession), 
textField, true);
+        assertThat(renderableFtl, MacroCallMatcher.hasName("renderTextField"));
+        assertThat(renderableFtl, 
MacroCallMatcher.hasParameters(MacroCallParameterMatcher.hasNameAndStringValue("type",
 "email")));
+    }
+
+    @Test
+    public void textFieldTypeUrl(@Mocked final ModelFormField.TextField 
textField) {
+        new Expectations() {
+            {
+                textField.getType(); result = "url";
+                httpSession.getAttribute("delegatorName"); result = 
"DelegatorName";
+            }
+        };
+
+        final RenderableFtl renderableFtl = 
renderableFtlFormElementsBuilder.textField(Map.of("session", httpSession), 
textField, true);
+        assertThat(renderableFtl, MacroCallMatcher.hasName("renderTextField"));
+        assertThat(renderableFtl, 
MacroCallMatcher.hasParameters(MacroCallParameterMatcher.hasNameAndStringValue("type",
 "url")));
+    }
+
+    @Test
+    public void textFieldTypeTel(@Mocked final ModelFormField.TextField 
textField) {
+        new Expectations() {
+            {
+                textField.getType(); result = "tel";
+                httpSession.getAttribute("delegatorName"); result = 
"DelegatorName";
+            }
+        };
+
+        final RenderableFtl renderableFtl = 
renderableFtlFormElementsBuilder.textField(Map.of("session", httpSession), 
textField, true);
+        assertThat(renderableFtl, MacroCallMatcher.hasName("renderTextField"));
+        assertThat(renderableFtl, 
MacroCallMatcher.hasParameters(MacroCallParameterMatcher.hasNameAndStringValue("type",
 "tel")));
+    }
+
+    @Test
+    public void textFieldNotRequired(@Mocked final ModelFormField.TextField 
textField) {
+        new Expectations() {
+            {
+                modelFormField.getRequiredField(); result = false;
+                httpSession.getAttribute("delegatorName"); result = 
"DelegatorName";
+            }
+        };
+
+        final RenderableFtl renderableFtl = 
renderableFtlFormElementsBuilder.textField(Map.of("session", httpSession), 
textField, true);
+        assertThat(renderableFtl, MacroCallMatcher.hasName("renderTextField"));
+        assertThat(renderableFtl, 
MacroCallMatcher.hasParameters(MacroCallParameterMatcher.hasNameAndBooleanValue("required",
 false)));
+    }
+
+    @Test
+    public void textFieldRequiredWithoutRequiredStyle(@Mocked final 
ModelFormField.TextField textField) {
+        new Expectations() {
+            {
+                modelFormField.getModelForm().getType(); result = "single";
+                modelFormField.getRequiredField(); result = true;
+                httpSession.getAttribute("delegatorName"); result = 
"DelegatorName";
+            }
+        };
+
+        final RenderableFtl renderableFtl = 
renderableFtlFormElementsBuilder.textField(Map.of("session", httpSession), 
textField, true);
+        assertThat(renderableFtl, MacroCallMatcher.hasName("renderTextField"));
+        assertThat(renderableFtl, 
MacroCallMatcher.hasParameters(MacroCallParameterMatcher.hasNameAndBooleanValue("required",
 true)));
+        assertThat(renderableFtl, 
MacroCallMatcher.hasParameters(MacroCallParameterMatcher.hasNameAndStringValue("className",
 "required")));
+    }
+
+    @Test
+    public void textFieldRequiredWithRequiredStyle(@Mocked final 
ModelFormField.TextField textField) {
+        new Expectations() {
+            {
+                modelFormField.getModelForm().getType(); result = "single";
+                modelFormField.getRequiredField(); result = true;
+                modelFormField.getRequiredFieldStyle(); result = 
"someCssClass";
+                httpSession.getAttribute("delegatorName"); result = 
"DelegatorName";
+            }
+        };
+
+        final RenderableFtl renderableFtl = 
renderableFtlFormElementsBuilder.textField(Map.of("session", httpSession), 
textField, true);
+        assertThat(renderableFtl, MacroCallMatcher.hasName("renderTextField"));
+        assertThat(renderableFtl, 
MacroCallMatcher.hasParameters(MacroCallParameterMatcher.hasNameAndBooleanValue("required",
 true)));
+        assertThat(renderableFtl, MacroCallMatcher.hasParameters(
+                MacroCallParameterMatcher.hasNameAndStringValue("className", 
"required someCssClass")));
+    }
+
+    @Test
+    public void textFieldPattern(@Mocked final ModelFormField.TextField 
textField) {
+        new Expectations() {
+            {
+                textField.getPattern(); result = "\\d{4,4}";
+                httpSession.getAttribute("delegatorName"); result = 
"DelegatorName";
+            }
+        };
+
+        final RenderableFtl renderableFtl = 
renderableFtlFormElementsBuilder.textField(Map.of("session", httpSession), 
textField, true);
+        assertThat(renderableFtl, MacroCallMatcher.hasName("renderTextField"));
+        assertThat(renderableFtl, 
MacroCallMatcher.hasParameters(MacroCallParameterMatcher.hasNameAndStringValue("pattern",
 "\\d{4,4}")));
+    }
+
+    @Test
+    public void textFieldPatternOnInvalidType(@Mocked final 
ModelFormField.TextField textField) {
+        new Expectations() {
+            {
+                textField.getPattern(); result = "\\d{4,4}";
+                textField.getType(); result = "number";
+                httpSession.getAttribute("delegatorName"); result = 
"DelegatorName";
+            }
+        };
+
+        textField.getPattern();
+        final RenderableFtl renderableFtl = 
renderableFtlFormElementsBuilder.textField(Map.of("session", httpSession), 
textField, true);
+
+        assertThat(renderableFtl, MacroCallMatcher.hasName("renderTextField"));
+        assertThat(renderableFtl, 
MacroCallMatcher.hasParameters(MacroCallParameterMatcher.hasNameAndStringValue("pattern",
 "")));
+    }
+
     @Test
     public void textareaFieldSetsIdValueLengthAndSize(@Mocked final 
ModelFormField.TextareaField textareaField) {
         final int maxLength = 142;
diff --git a/themes/bluelight/webapp/bluelight/style.css 
b/themes/bluelight/webapp/bluelight/style.css
index ec7858f198..e64d8e6fea 100644
--- a/themes/bluelight/webapp/bluelight/style.css
+++ b/themes/bluelight/webapp/bluelight/style.css
@@ -106,7 +106,8 @@ input[type="radio"], input[type="checkbox"] {
     margin: 0.2em;
 }
 
-input[type="text"], input[type="password"] {
+input[type="text"], input[type="password"], input[type="number"],
+input[type="email"], input[type="url"], input[type="tel"] {
     background-color: #ffffff;
     border: #999999 solid 0.1em;
     font-size: 1.1em;
diff --git a/themes/common-theme/template/Login.ftl 
b/themes/common-theme/template/Login.ftl
index 72ec964d06..98fe5cdd0f 100644
--- a/themes/common-theme/template/Login.ftl
+++ b/themes/common-theme/template/Login.ftl
@@ -36,11 +36,11 @@ under the License.
         <table class="basic-table" cellspacing="0">
           <tr>
             <td class="label">${uiLabelMap.CommonUsername}</td>
-            <td><input type="text" name="USERNAME" value="${username}" 
size="20"/></td>
+            <td><input type="text" name="USERNAME" value="${username}" 
size="20" required/></td>
           </tr>
           <tr>
             <td class="label">${uiLabelMap.CommonPassword}</td>
-            <td><input type="password" name="PASSWORD" autocomplete="off" 
value="" size="20"/></td>
+            <td><input type="password" name="PASSWORD" autocomplete="off" 
value="" size="20" required/></td>
           </tr>
           <#if ("Y" == useMultitenant) >
             <#if !requestAttributes.userTenantId??>
diff --git a/themes/common-theme/template/macro/CsvFormMacroLibrary.ftl 
b/themes/common-theme/template/macro/CsvFormMacroLibrary.ftl
index 8b95d8d86e..379b8ae111 100644
--- a/themes/common-theme/template/macro/CsvFormMacroLibrary.ftl
+++ b/themes/common-theme/template/macro/CsvFormMacroLibrary.ftl
@@ -117,7 +117,7 @@ under the License.
 <#macro renderHyperlinkTitle name title showSelectAll="N"></#macro>
 <#macro renderSortField style title linkUrl ajaxEnabled 
tooltip=""><@renderFieldTitle style title /></#macro>
 <#macro formatBoundaryComment boundaryType widgetType widgetName></#macro>
-<#macro renderAsterisks requiredField requiredStyle></#macro>
+<#macro renderAsterisks requiredField></#macro>
 <#macro makeHiddenFormLinkForm actionUrl name parameters targetWindow></#macro>
 <#macro makeHiddenFormLinkAnchor linkStyle hiddenFormName event action imgSrc 
description confirmation><@renderField description /></#macro>
 <#macro makeHyperlinkString hiddenFormName imgSrc imgTitle title alternate 
linkUrl description linkStyle="" event="" action="" targetParameters="" 
targetWindow="" confirmation="" uniqueItemName="" height="" width="" 
id=""><@renderField description />,<#rt/></#macro>
diff --git a/themes/common-theme/template/macro/FoFormMacroLibrary.ftl 
b/themes/common-theme/template/macro/FoFormMacroLibrary.ftl
index 15cc3c43f2..a48b69925b 100644
--- a/themes/common-theme/template/macro/FoFormMacroLibrary.ftl
+++ b/themes/common-theme/template/macro/FoFormMacroLibrary.ftl
@@ -145,5 +145,5 @@ under the License.
 <#macro makeHiddenFormLinkAnchor linkStyle hiddenFormName event action imgSrc 
description><@renderField description /></#macro>
 <#macro makeHyperlinkString hiddenFormName imgSrc imgTitle title alternate 
linkUrl description linkStyle="" event="" action="" targetParameters="" 
targetWindow="" confirmation="" uniqueItemName="" height="" width="" 
id=""><@makeBlock linkStyle description /></#macro>
 <#macro renderTooltip tooltip tooltipStyle></#macro>
-<#macro renderAsterisks requiredField requiredStyle></#macro>
+<#macro renderAsterisks requiredField></#macro>
 </#escape>
diff --git a/themes/common-theme/template/macro/HtmlFormMacroLibrary.ftl 
b/themes/common-theme/template/macro/HtmlFormMacroLibrary.ftl
index bccbb7eaea..0199d51da1 100644
--- a/themes/common-theme/template/macro/HtmlFormMacroLibrary.ftl
+++ b/themes/common-theme/template/macro/HtmlFormMacroLibrary.ftl
@@ -43,8 +43,10 @@ under the License.
 </#macro>
 <#macro renderHyperlinkField></#macro>
 
-<#macro renderTextField name className alert value="" textSize="" maxlength="" 
id="" event="" action="" disabled=false clientAutocomplete="" ajaxUrl="" 
ajaxEnabled="" mask="" tabindex="" readonly="" placeholder="" 
delegatorName="default">
-  <input type="text" name="${name?default("")?html}"<#t/>
+<#macro renderTextField type pattern name className alert value="" textSize="" 
maxlength="" id="" event="" action=""
+        disabled=false clientAutocomplete="" ajaxUrl="" ajaxEnabled="" mask="" 
tabindex="" readonly="" required=false
+        placeholder="" delegatorName="default">
+  <input type="${type}" name="${name?default("")?html}"<#t/>
   <#if ajaxEnabled?has_content && ajaxEnabled && ajaxUrl?has_content>
     <#local defaultMinLength = modelTheme.getAutocompleterDefaultMinLength()>
     <#local defaultDelay = modelTheme.getAutocompleterDefaultDelay()>
@@ -58,14 +60,15 @@ under the License.
     <#if value?has_content> value="${value}"</#if><#rt/>
     <#if textSize?has_content> size="${textSize}"</#if><#rt/>
     <#if maxlength?has_content> maxlength="${maxlength}"</#if><#rt/>
-    <#if readonly?has_content && readonly> readonly="readonly"</#if><#rt/>
+    <#if readonly?has_content && readonly> readonly</#if><#rt/>
     <#if mask?has_content> data-mask="${mask}"</#if><#rt/>
     <#if id?has_content> id="${id}"</#if><#rt/>
     <#if event?has_content && action?has_content> 
${event}="${action}"</#if><#rt/>
     <#if clientAutocomplete?has_content && clientAutocomplete=="false"> 
autocomplete="off"</#if><#rt/>
     <#if placeholder?has_content> placeholder="${placeholder}"</#if><#rt/>
     <#if tabindex?has_content> tabindex="${tabindex}"</#if><#rt/>
-    require
+    <#if required?has_content && required> required</#if>
+    <#if pattern?has_content> pattern="${pattern}"</#if>
   /><#t/>
 </#macro>
 
@@ -78,7 +81,7 @@ under the License.
     <#if cols?has_content> cols="${cols}"</#if><#rt/>
     <#if rows?has_content> rows="${rows}"</#if><#rt/>
     <#if id?has_content> id="${id}"</#if><#rt/>
-    <#if readonly?has_content && readonly=='readonly'> 
readonly="readonly"</#if><#rt/>
+    <#if readonly?has_content && readonly=='readonly'> readonly</#if><#rt/>
     <#if maxlength?has_content> maxlength="${maxlength}"</#if><#rt/>
     <#if tabindex?has_content> tabindex="${tabindex}"</#if><#rt/>
     <#if visualEditorEnable?has_content> 
data-toolbar="${buttons?default("maxi")}"</#if><#rt/>
@@ -321,7 +324,15 @@ under the License.
 <#macro renderSingleFormFieldTitle></#macro>
 
 <#macro renderFormOpen linkUrl formType name viewIndexField viewSizeField 
viewIndex viewSize targetWindow="" containerId="" containerStyle="" 
autocomplete="" useRowSubmit="" focusFieldName="" hasRequiredField="" 
csrfNameValue="">
-  <form method="post" action="${linkUrl}"<#if formType=="upload"> 
enctype="multipart/form-data"</#if><#if targetWindow?has_content> 
target="${targetWindow}"</#if><#if containerId?has_content> 
id="${containerId}"</#if> <#if focusFieldName?has_content> 
data-focus-field="${focusFieldName}"</#if> class="<#if 
containerStyle?has_content>${containerStyle}<#else>basic-form</#if><#if 
hasRequiredField?has_content> requireValidation</#if>" 
onsubmit="javascript:submitFormDisableSubmits(this)"<#if au [...]
+  <form method="post" action="${linkUrl}"
+        <#if formType=="upload"> enctype="multipart/form-data"</#if>
+        <#if targetWindow?has_content> target="${targetWindow}"</#if>
+        <#if containerId?has_content> id="${containerId}"</#if>
+        <#if focusFieldName?has_content> 
data-focus-field="${focusFieldName}"</#if>
+        class="<#if 
containerStyle?has_content>${containerStyle}<#else>basic-form</#if>"
+        onsubmit="javascript:submitFormDisableSubmits(this)"
+        <#if autocomplete?has_content> autocomplete="${autocomplete}"</#if>
+        name="${name}"><#lt/>
     <#if csrfNameValue?has_content>
       <#assign result = csrfNameValue?matches(r"(\w+) (\w+)")>
       <#if result>
@@ -640,7 +651,11 @@ Parameter: lastViewName, String, optional - If the 
ajaxEnabled parameter is true
 Parameter: tabindex, String, optional - HTML tabindex number.
 Parameter: delegatorName, String, optional - name of the delegator in context.
 -->
-<#macro renderLookupField name formName fieldFormName conditionGroup="" 
className="" alert="false" value="" size="" maxlength="" id="" event="" 
action="" readonly=false autocomplete="" descriptionFieldName="" 
targetParameterIter="" imgSrc="" ajaxUrl="" ajaxEnabled=javaScriptEnabled 
presentation="layer" width=modelTheme.getLookupWidth() 
height=modelTheme.getLookupHeight() position=modelTheme.getLookupPosition() 
fadeBackground="true" clearText="" showDescription="" initiallyCollapsed="" la 
[...]
+<#macro renderLookupField name formName fieldFormName conditionGroup="" 
className="" alert="false" value="" size=""
+        maxlength="" id="" event="" action="" readonly=false autocomplete="" 
descriptionFieldName="" targetParameterIter=""
+        imgSrc="" ajaxUrl="" ajaxEnabled=javaScriptEnabled 
presentation="layer" width=modelTheme.getLookupWidth()
+        height=modelTheme.getLookupHeight() 
position=modelTheme.getLookupPosition() fadeBackground="true" clearText=""
+        showDescription="" initiallyCollapsed="" lastViewName="main" 
tabindex="" delegatorName="default" disabled=false>
   <#if 
Static["org.apache.ofbiz.widget.model.ModelWidget"].widgetBoundaryCommentsEnabled(context)><#--
 context is always null here, but this is handled in 
widgetBoundaryCommentsEnabled -->
   <!-- @renderLookupField -->
   </#if>
@@ -669,7 +684,7 @@ Parameter: delegatorName, String, optional - name of the 
delegator in context.
       <input type="text" <@renderClass className alert /> <@renderDisabled 
disabled />
         <#if name?has_content> name="${name}"</#if><#if value?has_content> 
value="${value}"</#if><#if tabindex?has_content> 
tabindex="${tabindex}"</#if><#rt/>
         <#if size?has_content> size="${size}"</#if><#if maxlength?has_content> 
maxlength="${maxlength}"</#if><#if id?has_content> id="${id}"</#if><#rt/>
-        <#if readonly?has_content && readonly> 
readonly="readonly"</#if><#rt/><#if event?has_content && action?has_content> 
${event}="${action}"</#if><#rt/>
+        <#if readonly?has_content && readonly> readonly</#if><#rt/><#if 
event?has_content && action?has_content> ${event}="${action}"</#if><#rt/>
         <#if autocomplete?has_content> autocomplete="off"</#if><#rt/>
     </#if>
       data-lookup-ajax-enabled="<#if 
ajaxEnabled?has_content>${ajaxEnabled?string}<#else>false</#if>" <#rt/>
@@ -771,7 +786,8 @@ Parameter: delegatorName, String, optional - name of the 
delegator in context.
     <#if maxlength?has_content> maxlength="${maxlength}"</#if>
     <#if id?has_content> id="${id}"</#if>
     <#if autocomplete?has_content> autocomplete="off"</#if>
-    <#if tabindex?has_content> tabindex="${tabindex}"</#if>/><#rt/>
+    <#if tabindex?has_content> tabindex="${tabindex}"</#if>
+    required/><#rt/>
 </#macro>
 
 <#macro renderImageField action value="" description="" alternate="" style="" 
event=""><img<#if value?has_content> src="${value}"</#if><#if 
description?has_content> title="${description}"</#if> alt="<#if 
alternate?has_content>${alternate}"</#if><#if style?has_content> 
class="${style}"</#if><#if event?has_content> ${event?html}="${action}" 
</#if>/></#macro>
@@ -827,8 +843,8 @@ Parameter: delegatorName, String, optional - name of the 
delegator in context.
   <#if className?has_content || (alert?has_content && alert=="true")> 
class="${className}<#if alert?has_content && alert=="true"> alert</#if>" </#if>
 </#macro>
 
-<#macro renderAsterisks requiredField requiredStyle>
-  <#if requiredField=="true"><#if !requiredStyle?has_content>*</#if></#if>
+<#macro renderAsterisks requiredField>
+  <#if requiredField=="true">*</#if>
 </#macro>
 
 <#macro renderDisabled disabled>
diff --git a/themes/common-theme/template/macro/TextFormMacroLibrary.ftl 
b/themes/common-theme/template/macro/TextFormMacroLibrary.ftl
index ad10a54c05..42cc065153 100644
--- a/themes/common-theme/template/macro/TextFormMacroLibrary.ftl
+++ b/themes/common-theme/template/macro/TextFormMacroLibrary.ftl
@@ -116,7 +116,7 @@ under the License.
 <#macro renderHyperlinkTitle name title showSelectAll="N"></#macro>
 <#macro renderSortField style title linkUrl ajaxEnabled 
tooltip=""><@renderFieldTitle style title /></#macro>
 <#macro formatBoundaryComment boundaryType widgetType widgetName></#macro>
-<#macro renderAsterisks requiredField requiredStyle>*</#macro>
+<#macro renderAsterisks requiredField>*</#macro>
 <#macro makeHiddenFormLinkForm actionUrl name parameters targetWindow></#macro>
 <#macro makeHiddenFormLinkAnchor linkStyle hiddenFormName event action imgSrc 
description confirmation><@renderField description /></#macro>
 <#macro makeHyperlinkString linkStyle hiddenFormName event action imgSrc title 
targetParameters alternate linkUrl targetWindow description confirmation 
uniqueItemName="" height="" width="" id=""><@renderField description /></#macro>
diff --git a/themes/common-theme/template/macro/XlsFormMacroLibrary.ftl 
b/themes/common-theme/template/macro/XlsFormMacroLibrary.ftl
index c8872e2f67..c5dd16e416 100644
--- a/themes/common-theme/template/macro/XlsFormMacroLibrary.ftl
+++ b/themes/common-theme/template/macro/XlsFormMacroLibrary.ftl
@@ -150,7 +150,7 @@ under the License.
 
 <#macro renderClass className="" alert=""></#macro>
 
-<#macro renderAsterisks requiredField requiredStyle></#macro>
+<#macro renderAsterisks requiredField></#macro>
 
 <#macro makeHiddenFormLinkForm actionUrl name parameters targetWindow></#macro>
 <#macro makeHiddenFormLinkAnchor linkStyle hiddenFormName event action imgSrc 
description confirmation><td>${description!}</td></#macro>
diff --git a/themes/common-theme/template/macro/XmlFormMacroLibrary.ftl 
b/themes/common-theme/template/macro/XmlFormMacroLibrary.ftl
index 5678840d5b..3845880b1d 100644
--- a/themes/common-theme/template/macro/XmlFormMacroLibrary.ftl
+++ b/themes/common-theme/template/macro/XmlFormMacroLibrary.ftl
@@ -118,4 +118,4 @@ under the License.
 <#macro makeHiddenFormLinkAnchor linkStyle hiddenFormName event action imgSrc 
description confirmation><@renderField description /></#macro>
 <#macro makeHyperlinkString linkStyle hiddenFormName event action imgSrc title 
targetParameters alternate linkUrl targetWindow description confirmation 
uniqueItemName="" height="" width="" id=""><@renderField description /></#macro>
 <#macro renderTooltip tooltip tooltipStyle></#macro>
-<#macro renderAsterisks requiredField requiredStyle></#macro>
+<#macro renderAsterisks requiredField></#macro>
diff --git a/themes/common-theme/webapp/common-theme/js/util/OfbizUtil.js 
b/themes/common-theme/webapp/common-theme/js/util/OfbizUtil.js
index 5f1942614c..fb1cb53f6c 100644
--- a/themes/common-theme/webapp/common-theme/js/util/OfbizUtil.js
+++ b/themes/common-theme/webapp/common-theme/js/util/OfbizUtil.js
@@ -345,10 +345,6 @@ function bindObservers(bind_element) {
         var focusField = element.data("focus-field");
         element.find("[name=" + focusField + "]").focus();
     });
-    jQuery(bind_element).find(".requireValidation").each(function () {
-        var element = jQuery(this);
-        element.validate();
-    });
     jQuery(bind_element).find(".date-time-picker").each(function () {
         initDateTimePicker(this);
     });
diff --git a/themes/helveticus/template/Login.ftl 
b/themes/helveticus/template/Login.ftl
index 25434bf9dc..273d1ad7f0 100644
--- a/themes/helveticus/template/Login.ftl
+++ b/themes/helveticus/template/Login.ftl
@@ -36,14 +36,14 @@ under the License.
       <form method="post" action="<@ofbizUrl>login</@ofbizUrl>" 
name="loginform">
         <label>
           ${uiLabelMap.CommonUsername}
-          <input type="text" name="USERNAME" value="${username}" tabindex="0"/>
+          <input type="text" name="USERNAME" value="${username}" tabindex="0" 
required/>
         </label>
         
         <label>
           <span>
             ${uiLabelMap.CommonPassword}
           </span>
-          <input type="password" name="PASSWORD" autocomplete="off" value="" 
tabindex="0"/>
+          <input type="password" name="PASSWORD" autocomplete="off" value="" 
tabindex="0" required/>
             <a 
href="<@ofbizUrl>forgotPassword</@ofbizUrl>">${uiLabelMap.CommonForgotYourPassword}</a>
         </label>
 
diff --git a/themes/helveticus/webapp/helveticus/helveticus-main-theme.less 
b/themes/helveticus/webapp/helveticus/helveticus-main-theme.less
index f9e7c7401f..c9d309ce35 100644
--- a/themes/helveticus/webapp/helveticus/helveticus-main-theme.less
+++ b/themes/helveticus/webapp/helveticus/helveticus-main-theme.less
@@ -1479,7 +1479,8 @@ i.hoverTooltip {
             }
         }
 
-        input[type="text"], input[type="password"] {
+        input[type="text"], input[type="password"], input[type="number"],
+        input[type="email"], input[type="url"], input[type="tel"] {
             border: none;
             background-color: @grey-light;
             padding: 1rem;
@@ -1606,7 +1607,8 @@ i.hoverTooltip {
             }
         }
 
-        input[type="text"], input[type="password"] {
+        input[type="text"], input[type="password"], input[type="number"],
+        input[type="email"], input[type="url"], input[type="tel"] {
             border: none;
             background-color: @grey-light;
             padding: 1rem;
diff --git a/themes/rainbowstone/template/Login.ftl 
b/themes/rainbowstone/template/Login.ftl
index 44cee9552c..557b87fbd8 100644
--- a/themes/rainbowstone/template/Login.ftl
+++ b/themes/rainbowstone/template/Login.ftl
@@ -37,11 +37,11 @@ under the License.
         <table class="basic-table" cellspacing="0">
           <tr>
             <td class="label">${uiLabelMap.CommonUsername}</td>
-            <td><input type="text" name="USERNAME" value="${username}" 
size="20"/></td>
+            <td><input type="text" name="USERNAME" value="${username}" 
size="20" required/></td>
           </tr>
           <tr>
             <td class="label">${uiLabelMap.CommonPassword}</td>
-            <td><input type="password" name="PASSWORD" autocomplete="off" 
value="" size="20"/></td>
+            <td><input type="password" name="PASSWORD" autocomplete="off" 
value="" size="20" required/></td>
           </tr>
           <#if ("Y" == useMultitenant) >
               <#if !requestAttributes.userTenantId??>
diff --git a/themes/tomahawk/webapp/tomahawk/css/style.css 
b/themes/tomahawk/webapp/tomahawk/css/style.css
index aecc9fbe06..0590fa66b7 100644
--- a/themes/tomahawk/webapp/tomahawk/css/style.css
+++ b/themes/tomahawk/webapp/tomahawk/css/style.css
@@ -102,7 +102,8 @@ input[type="radio"], input[type="checkbox"] {
     vertical-align: middle;
 }
 
-input[type="text"], input[type="password"] {
+input[type="text"], input[type="password"], input[type="number"],
+input[type="email"], input[type="url"], input[type="tel"] {
     background-color: #fffcea;
     border: 0.1em solid #999999;
     font-size: 1.1em;

Reply via email to