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

lukaszlenart pushed a commit to branch feature/WW-5444-html5
in repository https://gitbox.apache.org/repos/asf/struts.git

commit a5e6374b48a7f813806963dbd1994e1184c7ea50
Author: Lukasz Lenart <[email protected]>
AuthorDate: Sun Nov 23 18:49:47 2025 +0100

    test(html5-theme): add comprehensive unit tests and fix template variables
    
    - Add testGenericHtml5() methods to 13 UI tag test classes
    - Create theme.properties with parent=simple configuration
    - Migrate all 32 HTML5 templates from deprecated 'parameters' to 
'attributes' variable
    - Exclude DoubleSelect and UpDownSelect (not supported in HTML5 theme)
    - All 26 HTML5 theme tests passing (100% success rate)
    
    Components tested: Textfield, Textarea, Password, Checkbox, CheckboxList,
    Radio, Select, File, Hidden, Label, ComboBox, Reset, Submit
    
    🤖 Generated with [Claude Code](https://claude.com/claude-code)
    
    Co-Authored-By: Claude <[email protected]>
---
 core/src/main/resources/template/html5/a-close.ftl |  38 +-
 .../main/resources/template/html5/actionerror.ftl  |  14 +-
 .../resources/template/html5/actionmessage.ftl     |  16 +-
 .../src/main/resources/template/html5/checkbox.ftl |  36 +-
 .../main/resources/template/html5/checkboxlist.ftl |  82 ++--
 .../src/main/resources/template/html5/combobox.ftl |  60 +--
 .../resources/template/html5/common-attributes.ftl |   4 +-
 core/src/main/resources/template/html5/css.ftl     |  22 +-
 core/src/main/resources/template/html5/debug.ftl   |   6 +-
 .../template/html5/dynamic-attributes.ftl          |   6 +-
 .../main/resources/template/html5/fielderror.ftl   |  24 +-
 core/src/main/resources/template/html5/file.ftl    |  36 +-
 .../main/resources/template/html5/form-close.ftl   |   2 +-
 core/src/main/resources/template/html5/form.ftl    |  62 +--
 core/src/main/resources/template/html5/head.ftl    |   2 +-
 core/src/main/resources/template/html5/hidden.ftl  |  26 +-
 core/src/main/resources/template/html5/label.ftl   |  26 +-
 core/src/main/resources/template/html5/link.ftl    |  60 +--
 core/src/main/resources/template/html5/nonce.ftl   |   4 +-
 .../src/main/resources/template/html5/optgroup.ftl |  32 +-
 .../src/main/resources/template/html5/password.ftl |  42 +-
 .../template/html5/prefixed-dynamic-attributes.ftl |   6 +-
 .../src/main/resources/template/html5/radiomap.ftl |  68 +--
 core/src/main/resources/template/html5/reset.ftl   |  78 ++--
 core/src/main/resources/template/html5/script.ftl  |  36 +-
 .../resources/template/html5/scripting-events.ftl  |  56 +--
 core/src/main/resources/template/html5/select.ftl  | 106 ++---
 .../main/resources/template/html5/submit-close.ftl |   8 +-
 core/src/main/resources/template/html5/submit.ftl  |  86 ++--
 core/src/main/resources/template/html5/text.ftl    |  44 +-
 .../src/main/resources/template/html5/textarea.ftl |  54 +--
 .../main/resources/template/html5/theme.properties |   3 +
 core/src/main/resources/template/html5/token.ftl   |   4 +-
 .../struts2/views/jsp/ui/CheckboxListTest.java     |  13 +
 .../apache/struts2/views/jsp/ui/CheckboxTest.java  |  11 +
 .../apache/struts2/views/jsp/ui/ComboBoxTest.java  |  13 +
 .../org/apache/struts2/views/jsp/ui/FileTest.java  |  11 +
 .../apache/struts2/views/jsp/ui/HiddenTest.java    |  11 +
 .../org/apache/struts2/views/jsp/ui/LabelTest.java |  11 +
 .../apache/struts2/views/jsp/ui/PasswordTest.java  |  11 +
 .../org/apache/struts2/views/jsp/ui/RadioTest.java |  13 +
 .../org/apache/struts2/views/jsp/ui/ResetTest.java |  11 +
 .../apache/struts2/views/jsp/ui/SelectTest.java    |  13 +
 .../apache/struts2/views/jsp/ui/SubmitTest.java    |  11 +
 .../apache/struts2/views/jsp/ui/TextareaTest.java  |  11 +
 .../apache/struts2/views/jsp/ui/TextfieldTest.java |  11 +
 .../2025-11-23-WW-5444-html5-theme-testing.md      | 518 +++++++++++++++++++++
 47 files changed, 1245 insertions(+), 573 deletions(-)

diff --git a/core/src/main/resources/template/html5/a-close.ftl 
b/core/src/main/resources/template/html5/a-close.ftl
index 15465dec8..f2d2d9333 100644
--- a/core/src/main/resources/template/html5/a-close.ftl
+++ b/core/src/main/resources/template/html5/a-close.ftl
@@ -18,33 +18,33 @@
  * under the License.
  */
 -->
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" />
 <@compress single_line=true>
 <a
-<#if parameters.id??>
- id="${parameters.id}"
+<#if attributes.id??>
+ id="${attributes.id}"
 </#if>
-<#if parameters.href??>
- href="${parameters.href?no_esc}"
+<#if attributes.href??>
+ href="${attributes.href?no_esc}"
 </#if>
-<#if parameters.disabled!false>
+<#if attributes.disabled!false>
  disabled="disabled"
 </#if>
-<#if parameters.tabindex??>
- tabindex="${parameters.tabindex}"
+<#if attributes.tabindex??>
+ tabindex="${attributes.tabindex}"
 </#if>
-<#if parameters.cssClass??>
- class="${parameters.cssClass}"
+<#if attributes.cssClass??>
+ class="${attributes.cssClass}"
 </#if>
-<#if parameters.cssStyle??>
- style="${parameters.cssStyle}"
+<#if attributes.cssStyle??>
+ style="${attributes.cssStyle}"
 </#if>
-<#if parameters.title??>
- title="${parameters.title}"
+<#if attributes.title??>
+ title="${attributes.title}"
 </#if>
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" />
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" />
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
->${tag.escapeHtmlBody()?then(parameters.body, parameters.body?no_esc)}</a>
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/scripting-events.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" />
+>${tag.escapeHtmlBody()?then(attributes.body, attributes.body?no_esc)}</a>
 </@compress>
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" />
diff --git a/core/src/main/resources/template/html5/actionerror.ftl 
b/core/src/main/resources/template/html5/actionerror.ftl
index e6586adef..0e195e8d5 100644
--- a/core/src/main/resources/template/html5/actionerror.ftl
+++ b/core/src/main/resources/template/html5/actionerror.ftl
@@ -22,21 +22,21 @@
 <#if (actionErrors?? && actionErrors?size > 0)>
     <@compress single_line=true>
         <ul
-                <#if parameters.id??>
-                    id="${parameters.id}"
+                <#if attributes.id??>
+                    id="${attributes.id}"
                 </#if>
-                <#if parameters.cssClass??>
-                    class="${parameters.cssClass}"
+                <#if attributes.cssClass??>
+                    class="${attributes.cssClass}"
                 <#else>
                     class="errorMessage"
                 </#if>
-                <#if parameters.cssStyle??>
-                    style="${parameters.cssStyle}"
+                <#if attributes.cssStyle??>
+                    style="${attributes.cssStyle}"
                 </#if>
         >
             <#list actionErrors as error>
                 <#if error??><#t/>
-                    <li><span><#if 
parameters.escape>${error!}<#else>${error!?no_esc}</#if></span></li>
+                    <li><span><#if 
attributes.escape>${error!}<#else>${error!?no_esc}</#if></span></li>
                 </#if>
             </#list>
         </ul>
diff --git a/core/src/main/resources/template/html5/actionmessage.ftl 
b/core/src/main/resources/template/html5/actionmessage.ftl
index 606c460e3..1e7950be0 100644
--- a/core/src/main/resources/template/html5/actionmessage.ftl
+++ b/core/src/main/resources/template/html5/actionmessage.ftl
@@ -19,24 +19,24 @@
  * under the License.
  */
 -->
-<#if (actionMessages?? && actionMessages?size > 0 && !parameters.isEmptyList)>
+<#if (actionMessages?? && actionMessages?size > 0 && !attributes.isEmptyList)>
     <@compress single_line=true>
         <ul
-                <#if parameters.id??>
-                    id="${parameters.id}"
+                <#if attributes.id??>
+                    id="${attributes.id}"
                 </#if>
-                <#if parameters.cssClass??>
-                    class="${parameters.cssClass}"
+                <#if attributes.cssClass??>
+                    class="${attributes.cssClass}"
                 <#else>
                     class="actionMessage"
                 </#if>
-                <#if parameters.cssStyle??>
-                    style="${parameters.cssStyle}"
+                <#if attributes.cssStyle??>
+                    style="${attributes.cssStyle}"
                 </#if>
         >
             <#list actionMessages as message>
                 <#if message??><#t/>
-                    <li><span><#if 
parameters.escape>${message!}<#else>${message!?no_esc}</#if></span></li>
+                    <li><span><#if 
attributes.escape>${message!}<#else>${message!?no_esc}</#if></span></li>
                 </#if>
             </#list>
         </ul>
diff --git a/core/src/main/resources/template/html5/checkbox.ftl 
b/core/src/main/resources/template/html5/checkbox.ftl
index 134f5d280..10f5300dd 100644
--- a/core/src/main/resources/template/html5/checkbox.ftl
+++ b/core/src/main/resources/template/html5/checkbox.ftl
@@ -18,35 +18,35 @@
  * under the License.
  */
 -->
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" />
 <#compress>
-<input type="checkbox" name="${parameters.name}" 
value="${parameters.fieldValue}"
-<#if parameters.nameValue?? && parameters.nameValue>
+<input type="checkbox" name="${attributes.name}" 
value="${attributes.fieldValue}"
+<#if attributes.nameValue?? && attributes.nameValue>
  checked="checked"
 </#if>
-<#if parameters.disabled!false>
+<#if attributes.disabled!false>
  disabled="disabled"
 </#if>
-<#if parameters.tabindex?has_content>
- tabindex="${parameters.tabindex}"
+<#if attributes.tabindex?has_content>
+ tabindex="${attributes.tabindex}"
 </#if>
-<#if parameters.id?has_content>
- id="${parameters.id}"
+<#if attributes.id?has_content>
+ id="${attributes.id}"
 </#if>
-<#include "/${parameters.templateDir}/${parameters.expandTheme}/css.ftl" />
-<#if parameters.title?has_content>
- title="${parameters.title}"
+<#include "/${attributes.templateDir}/${attributes.expandTheme}/css.ftl" />
+<#if attributes.title?has_content>
+ title="${attributes.title}"
 </#if>
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" />
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" />
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/scripting-events.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" />
 />
-<#if parameters.submitUnchecked!false>
-<input type="hidden" id="__checkbox_${parameters.id}" 
name="__checkbox_${parameters.name}" value="${parameters.fieldValue}"
-<#if parameters.disabled!false>
+<#if attributes.submitUnchecked!false>
+<input type="hidden" id="__checkbox_${attributes.id}" 
name="__checkbox_${attributes.name}" value="${attributes.fieldValue}"
+<#if attributes.disabled!false>
  disabled="disabled"
 </#if>
  />
 </#if>
 </#compress>
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" />
diff --git a/core/src/main/resources/template/html5/checkboxlist.ftl 
b/core/src/main/resources/template/html5/checkboxlist.ftl
index 17745976c..a55732a82 100644
--- a/core/src/main/resources/template/html5/checkboxlist.ftl
+++ b/core/src/main/resources/template/html5/checkboxlist.ftl
@@ -18,103 +18,103 @@
  * under the License.
  */
 -->
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" />
 <#compress>
 <#assign itemCount = 0/>
-<#if parameters.list??>
-<@s.iterator value="parameters.list">
+<#if attributes.list??>
+<@s.iterator value="attributes.list">
     <#assign itemCount = itemCount + 1/>
-    <#if parameters.listKey??>
-        <#assign itemKey = stack.findValue(parameters.listKey)/>
-        <#assign itemKeyStr = stack.findString(parameters.listKey)/>
+    <#if attributes.listKey??>
+        <#assign itemKey = stack.findValue(attributes.listKey)/>
+        <#assign itemKeyStr = stack.findString(attributes.listKey)/>
     <#else>
         <#assign itemKey = stack.findValue('top')/>
         <#assign itemKeyStr = stack.findString('top')>
     </#if>
-    <#if parameters.listLabelKey??>
+    <#if attributes.listLabelKey??>
     <#-- checks the valueStack for the 'valueKey.' The valueKey is then 
looked-up in the locale
        file for it's localized value.  This is then used as a label -->
-        <#assign itemValue = 
struts.getText(stack.findString(parameters.listLabelKey))/>
-    <#elseif parameters.listValue??>
-        <#assign itemValue = stack.findString(parameters.listValue)!""/>
+        <#assign itemValue = 
struts.getText(stack.findString(attributes.listLabelKey))/>
+    <#elseif attributes.listValue??>
+        <#assign itemValue = stack.findString(attributes.listValue)!""/>
     <#else>
          <#assign itemValue = stack.findString('top')/>
     </#if>
-    <#if parameters.listCssClass??>
-        <#if stack.findString(parameters.listCssClass)??>
-          <#assign itemCssClass= stack.findString(parameters.listCssClass)/>
+    <#if attributes.listCssClass??>
+        <#if stack.findString(attributes.listCssClass)??>
+          <#assign itemCssClass= stack.findString(attributes.listCssClass)/>
         <#else>
           <#assign itemCssClass = ''/>
         </#if>
     </#if>
-    <#if parameters.listCssStyle??>
-        <#if stack.findString(parameters.listCssStyle)??>
-          <#assign itemCssStyle= stack.findString(parameters.listCssStyle)/>
+    <#if attributes.listCssStyle??>
+        <#if stack.findString(attributes.listCssStyle)??>
+          <#assign itemCssStyle= stack.findString(attributes.listCssStyle)/>
         <#else>
           <#assign itemCssStyle = ''/>
         </#if>
     </#if>
-    <#if parameters.listTitle??>
-        <#if stack.findString(parameters.listTitle)??>
-          <#assign itemTitle= stack.findString(parameters.listTitle)/>
+    <#if attributes.listTitle??>
+        <#if stack.findString(attributes.listTitle)??>
+          <#assign itemTitle= stack.findString(attributes.listTitle)/>
         <#else>
           <#assign itemTitle = ''/>
         </#if>
     </#if>
-<input type="checkbox" name="${parameters.name}" value="${itemKeyStr}"
-<#if parameters.id?has_content>
- id="${parameters.id}-${itemCount}"
+<input type="checkbox" name="${attributes.name}" value="${itemKeyStr}"
+<#if attributes.id?has_content>
+ id="${attributes.id}-${itemCount}"
 <#else>
- id="${parameters.name}-${itemCount}"
+ id="${attributes.name}-${itemCount}"
 </#if>
-<#if tag.contains(parameters.nameValue, itemKey)>
+<#if tag.contains(attributes.nameValue, itemKey)>
  checked="checked"
 </#if>
-<#if parameters.disabled!false>
+<#if attributes.disabled!false>
  disabled="disabled"
 </#if>
 <#if itemCssClass??>
  class="${itemCssClass}"
 <#else>
-<#if parameters.cssClass?has_content>
- class="${parameters.cssClass}"
+<#if attributes.cssClass?has_content>
+ class="${attributes.cssClass}"
 </#if>
 </#if>
 <#if itemCssStyle??>
  style="${itemCssStyle}"
 <#else>
-<#if parameters.cssStyle?has_content>
- style="${parameters.cssStyle}"
+<#if attributes.cssStyle?has_content>
+ style="${attributes.cssStyle}"
 </#if>
 </#if>
 <#if itemTitle??>
  title="${itemTitle}"
 <#else>
-<#if parameters.title?has_content>
- title="${parameters.title}"
+<#if attributes.title?has_content>
+ title="${attributes.title}"
 </#if>
 </#if>
-<#include "/${parameters.templateDir}/${parameters.expandTheme}/css.ftl" />
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" />
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" />
+<#include "/${attributes.templateDir}/${attributes.expandTheme}/css.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/scripting-events.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" />
 <#global evaluate_dynamic_attributes = true/>
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" />
 
 <label
-<#if parameters.id?has_content>
- for="${parameters.id}-${itemCount}"
+<#if attributes.id?has_content>
+ for="${attributes.id}-${itemCount}"
 <#else>
- for="${parameters.name}-${itemCount}"
+ for="${attributes.name}-${itemCount}"
 </#if>
  class="checkboxLabel">${itemValue}</label>
 </@s.iterator>
 <#else>
 </#if>
-<input type="hidden" id="__multiselect_${parameters.id}" 
name="__multiselect_${parameters.name}"
+<input type="hidden" id="__multiselect_${attributes.id}" 
name="__multiselect_${attributes.name}"
  value=""
-<#if parameters.disabled!false>
+<#if attributes.disabled!false>
  disabled="disabled"
 </#if>
  />
 </#compress>
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" />
diff --git a/core/src/main/resources/template/html5/combobox.ftl 
b/core/src/main/resources/template/html5/combobox.ftl
index 3056e0935..2e950c49c 100644
--- a/core/src/main/resources/template/html5/combobox.ftl
+++ b/core/src/main/resources/template/html5/combobox.ftl
@@ -19,70 +19,70 @@
  */
 -->
 <@s.script>
-    function autoPopulate_${parameters.escapedId}(targetElement) {
-    <#if parameters.headerKey?? && parameters.headerValue??>
-        if (targetElement.options[targetElement.selectedIndex].value == 
'${parameters.headerKey?js_string}') {
+    function autoPopulate_${attributes.escapedId}(targetElement) {
+    <#if attributes.headerKey?? && attributes.headerValue??>
+        if (targetElement.options[targetElement.selectedIndex].value == 
'${attributes.headerKey?js_string}') {
             return;
         }
     </#if>
-    <#if parameters.emptyOption!false>
+    <#if attributes.emptyOption!false>
         if (targetElement.options[targetElement.selectedIndex].value == '') {
             return;
         }
     </#if>
-        
targetElement.form.elements['${parameters.name?js_string}'].value=targetElement.options[targetElement.selectedIndex].value;
+        
targetElement.form.elements['${attributes.name?js_string}'].value=targetElement.options[targetElement.selectedIndex].value;
     }
 </@s.script>
-<#include "/${parameters.templateDir}/html5/text.ftl" />
+<#include "/${attributes.templateDir}/html5/text.ftl" />
 <#compress>
-    <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" />
-    <#if parameters.list??>
-        <select onChange="autoPopulate_${parameters.escapedId}(this);"
-                <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/css.ftl" />
-                <#if parameters.disabled!false>
+    <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" />
+    <#if attributes.list??>
+        <select onChange="autoPopulate_${attributes.escapedId}(this);"
+                <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/css.ftl" />
+                <#if attributes.disabled!false>
                     disabled="disabled"
                 </#if>
         >
-            <#if (parameters.headerKey?? && parameters.headerValue??)>
-                <option 
value="${parameters.headerKey}">${parameters.headerValue}</option>
+            <#if (attributes.headerKey?? && attributes.headerValue??)>
+                <option 
value="${attributes.headerKey}">${attributes.headerValue}</option>
             </#if>
-            <#if parameters.emptyOption!false>
+            <#if attributes.emptyOption!false>
                 <option value=""></option>
             </#if>
-            <@s.iterator value="parameters.list">
-                <#if parameters.listKey??>
-                    <#assign tmpListKey = stack.findString(parameters.listKey) 
/>
+            <@s.iterator value="attributes.list">
+                <#if attributes.listKey??>
+                    <#assign tmpListKey = stack.findString(attributes.listKey) 
/>
                 <#else>
                     <#assign tmpListKey = stack.findString('top') />
                 </#if>
-                <#if parameters.listValue??>
-                    <#assign tmpListValue = 
stack.findString(parameters.listValue) />
+                <#if attributes.listValue??>
+                    <#assign tmpListValue = 
stack.findString(attributes.listValue) />
                 <#else>
                     <#assign tmpListValue = stack.findString('top') />
                 </#if>
-                <#if parameters.listCssClass??>
-                    <#if stack.findString(parameters.listCssClass)??>
-                        <#assign itemCssClass= 
stack.findString(parameters.listCssClass)/>
+                <#if attributes.listCssClass??>
+                    <#if stack.findString(attributes.listCssClass)??>
+                        <#assign itemCssClass= 
stack.findString(attributes.listCssClass)/>
                     <#else>
                         <#assign itemCssClass = ''/>
                     </#if>
                 </#if>
-                <#if parameters.listCssStyle??>
-                    <#if stack.findString(parameters.listCssStyle)??>
-                        <#assign itemCssStyle= 
stack.findString(parameters.listCssStyle)/>
+                <#if attributes.listCssStyle??>
+                    <#if stack.findString(attributes.listCssStyle)??>
+                        <#assign itemCssStyle= 
stack.findString(attributes.listCssStyle)/>
                     <#else>
                         <#assign itemCssStyle = ''/>
                     </#if>
                 </#if>
-                <#if parameters.listTitle??>
-                    <#if stack.findString(parameters.listTitle)??>
-                        <#assign itemTitle= 
stack.findString(parameters.listTitle)/>
+                <#if attributes.listTitle??>
+                    <#if stack.findString(attributes.listTitle)??>
+                        <#assign itemTitle= 
stack.findString(attributes.listTitle)/>
                     <#else>
                         <#assign itemTitle = ''/>
                     </#if>
                 </#if>
             <option value="${tmpListKey}"
-                    <#if (parameters.nameValue == tmpListKey)>
+                    <#if (attributes.nameValue == tmpListKey)>
                         selected="selected"
                     </#if>
                     <#if itemCssClass??>
@@ -99,4 +99,4 @@
         </select>
     </#if>
 </#compress>
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" />
diff --git a/core/src/main/resources/template/html5/common-attributes.ftl 
b/core/src/main/resources/template/html5/common-attributes.ftl
index 0bfe73fc5..bd81fcb15 100644
--- a/core/src/main/resources/template/html5/common-attributes.ftl
+++ b/core/src/main/resources/template/html5/common-attributes.ftl
@@ -19,7 +19,7 @@
  */
 -->
 <#compress>
-<#if parameters.accesskey?has_content>
- accesskey="${parameters.accesskey}"
+<#if attributes.accesskey?has_content>
+ accesskey="${attributes.accesskey}"
 </#if>
 </#compress>
diff --git a/core/src/main/resources/template/html5/css.ftl 
b/core/src/main/resources/template/html5/css.ftl
index 07220ac9f..00ea4b9fc 100644
--- a/core/src/main/resources/template/html5/css.ftl
+++ b/core/src/main/resources/template/html5/css.ftl
@@ -19,17 +19,17 @@
  */
 -->
 <#compress>
-<#assign hasFieldErrors = parameters.name?? && fieldErrors?? && 
fieldErrors.get(parameters.name)??/>
-<#if parameters.cssClass?has_content && !(hasFieldErrors && 
parameters.cssErrorClass??)>
- class="${parameters.cssClass}"
-<#elseif parameters.cssClass?has_content && (hasFieldErrors && 
parameters.cssErrorClass??)>
- class="${parameters.cssClass} ${parameters.cssErrorClass}"
-<#elseif !(parameters.cssClass?has_content) && (hasFieldErrors && 
parameters.cssErrorClass??)>
- class="${parameters.cssErrorClass}"
+<#assign hasFieldErrors = attributes.name?? && fieldErrors?? && 
fieldErrors.get(attributes.name)??/>
+<#if attributes.cssClass?has_content && !(hasFieldErrors && 
attributes.cssErrorClass??)>
+ class="${attributes.cssClass}"
+<#elseif attributes.cssClass?has_content && (hasFieldErrors && 
attributes.cssErrorClass??)>
+ class="${attributes.cssClass} ${attributes.cssErrorClass}"
+<#elseif !(attributes.cssClass?has_content) && (hasFieldErrors && 
attributes.cssErrorClass??)>
+ class="${attributes.cssErrorClass}"
 </#if>
-<#if parameters.cssStyle?has_content && !(hasFieldErrors && 
(parameters.cssErrorStyle?? || parameters.cssErrorClass??))>
- style="${parameters.cssStyle}"
-<#elseif hasFieldErrors && parameters.cssErrorStyle??>
- style="${parameters.cssErrorStyle}"
+<#if attributes.cssStyle?has_content && !(hasFieldErrors && 
(attributes.cssErrorStyle?? || attributes.cssErrorClass??))>
+ style="${attributes.cssStyle}"
+<#elseif hasFieldErrors && attributes.cssErrorStyle??>
+ style="${attributes.cssErrorStyle}"
 </#if>
 </#compress>
diff --git a/core/src/main/resources/template/html5/debug.ftl 
b/core/src/main/resources/template/html5/debug.ftl
index 889702213..fef4f6fa5 100644
--- a/core/src/main/resources/template/html5/debug.ftl
+++ b/core/src/main/resources/template/html5/debug.ftl
@@ -41,7 +41,7 @@
 <br>
 
 <a href="#" id="toggle-button">[Debug]</a>
-<div style="display:none" id="<#if 
parameters.id??>${parameters.id}<#else>debug</#if>">
+<div style="display:none" id="<#if 
attributes.id??>${attributes.id}<#else>debug</#if>">
 <h2>Struts ValueStack Debug</h2>
 <br>
 
@@ -50,7 +50,7 @@
     <tr><th>Object</th><th>Property Name</th><th>Property 
Value</th><th>Property Class</th></tr>
 
     <#assign index=1>
-    <#list parameters.stackValues as stackObject>
+    <#list attributes.stackValues as stackObject>
     <tr>
         <td rowspan="${stackObject.value.size()}">${stackObject.key}</td>
 
@@ -86,7 +86,7 @@
 </div>
 <@s.script>
     document.getElementById('toggle-button').onclick = function() {
-        toggleDebug('<#if parameters.id??>${parameters.id}<#else>debug</#if>');
+        toggleDebug('<#if attributes.id??>${attributes.id}<#else>debug</#if>');
         return false;
     }
 </@s.script>
diff --git a/core/src/main/resources/template/html5/dynamic-attributes.ftl 
b/core/src/main/resources/template/html5/dynamic-attributes.ftl
index 246ec466f..64ea3dfa6 100644
--- a/core/src/main/resources/template/html5/dynamic-attributes.ftl
+++ b/core/src/main/resources/template/html5/dynamic-attributes.ftl
@@ -26,10 +26,10 @@
     <#return true>
   </#if>
 </#function>
-<#if (parameters.dynamicAttributes?? && parameters.dynamicAttributes?size > 0)>
-<#assign aKeys = parameters.dynamicAttributes.keySet()>
+<#if (attributes.dynamicAttributes?? && attributes.dynamicAttributes?size > 0)>
+<#assign aKeys = attributes.dynamicAttributes.keySet()>
 <#list aKeys?filter(acceptKey) as aKey>
-<#assign keyValue = parameters.dynamicAttributes.get(aKey)/>
+<#assign keyValue = attributes.dynamicAttributes.get(aKey)/>
 <#if keyValue?is_string>
   <#if evaluate_dynamic_attributes!false == true>
     <#assign value = struts.translateVariables(keyValue)!keyValue/>
diff --git a/core/src/main/resources/template/html5/fielderror.ftl 
b/core/src/main/resources/template/html5/fielderror.ftl
index 4381fdcd5..ef16199ff 100644
--- a/core/src/main/resources/template/html5/fielderror.ftl
+++ b/core/src/main/resources/template/html5/fielderror.ftl
@@ -33,22 +33,22 @@
                         <#assign eValue = fieldErrors.get(fieldErrorFieldName)>
                         <#if (haveMatchedErrorField && (!doneStartUlTag))>
                             <ul
-                            <#if parameters.id?has_content>
-                                id="${parameters.id}"
+                            <#if attributes.id?has_content>
+                                id="${attributes.id}"
                             </#if>
-                            <#if parameters.cssClass?has_content>
-                                class="${parameters.cssClass}"
+                            <#if attributes.cssClass?has_content>
+                                class="${attributes.cssClass}"
                             <#else>
                                 class="errorMessage"
                             </#if>
-                            <#if parameters.cssStyle?has_content>
-                                style="${parameters.cssStyle}"
+                            <#if attributes.cssStyle?has_content>
+                                style="${attributes.cssStyle}"
                             </#if>
                             >
                             <#assign doneStartUlTag=true>
                         </#if>
                         <#list eValue as eEachValue>
-                            <li><span><#if 
parameters.escape>${eEachValue!}<#else>${eEachValue!?no_esc}</#if></span>
+                            <li><span><#if 
attributes.escape>${eEachValue!}<#else>${eEachValue!?no_esc}</#if></span>
                             </li>
                         </#list>
                     </#if>
@@ -61,19 +61,19 @@
         <#else>
             <#if (eKeysSize > 0)>
                 <ul
-                        <#if parameters.cssClass?has_content>
-                            class="${parameters.cssClass}"
+                        <#if attributes.cssClass?has_content>
+                            class="${attributes.cssClass}"
                         <#else>
                             class="errorMessage"
                         </#if>
-                        <#if parameters.cssStyle?has_content>
-                            style="${parameters.cssStyle}"
+                        <#if attributes.cssStyle?has_content>
+                            style="${attributes.cssStyle}"
                         </#if>
                 >
                     <#list eKeys as eKey>
                         <#assign eValue = fieldErrors.get(eKey)>
                         <#list eValue as eEachValue>
-                            <li><span><#if 
parameters.escape>${eEachValue!}<#else>${eEachValue!?no_esc}</#if></span>
+                            <li><span><#if 
attributes.escape>${eEachValue!}<#else>${eEachValue!?no_esc}</#if></span>
                             </li>
                         </#list>
                     </#list>
diff --git a/core/src/main/resources/template/html5/file.ftl 
b/core/src/main/resources/template/html5/file.ftl
index 60a486f50..386ae0fa2 100644
--- a/core/src/main/resources/template/html5/file.ftl
+++ b/core/src/main/resources/template/html5/file.ftl
@@ -18,32 +18,32 @@
  * under the License.
  */
 -->
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" />
 <#compress>
 <input type="file"
- name="${(parameters.name!"")}"
-<#if parameters.get("size")?has_content>
- size="${parameters.get("size")}"
+ name="${(attributes.name!"")}"
+<#if attributes.get("size")?has_content>
+ size="${attributes.get("size")}"
 </#if>
-<#if parameters.disabled!false>
+<#if attributes.disabled!false>
  disabled="disabled"
 </#if>
-<#if parameters.accept?has_content>
- accept="${parameters.accept}"
+<#if attributes.accept?has_content>
+ accept="${attributes.accept}"
 </#if>
-<#if parameters.tabindex?has_content>
- tabindex="${parameters.tabindex}"
+<#if attributes.tabindex?has_content>
+ tabindex="${attributes.tabindex}"
 </#if>
-<#if parameters.id?has_content>
- id="${parameters.id}"
+<#if attributes.id?has_content>
+ id="${attributes.id}"
 </#if>
-<#include "/${parameters.templateDir}/${parameters.expandTheme}/css.ftl" />
-<#if parameters.title?has_content>
- title="${parameters.title}"
+<#include "/${attributes.templateDir}/${attributes.expandTheme}/css.ftl" />
+<#if attributes.title?has_content>
+ title="${attributes.title}"
 </#if>
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" />
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" />
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/scripting-events.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" />
 />
 </#compress>
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" />
diff --git a/core/src/main/resources/template/html5/form-close.ftl 
b/core/src/main/resources/template/html5/form-close.ftl
index f3aaa7491..b170ba294 100644
--- a/core/src/main/resources/template/html5/form-close.ftl
+++ b/core/src/main/resources/template/html5/form-close.ftl
@@ -19,4 +19,4 @@
  */
 -->
 </form>
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" />
diff --git a/core/src/main/resources/template/html5/form.ftl 
b/core/src/main/resources/template/html5/form.ftl
index f0eaa4342..b7c74c5d3 100644
--- a/core/src/main/resources/template/html5/form.ftl
+++ b/core/src/main/resources/template/html5/form.ftl
@@ -18,55 +18,55 @@
  * under the License.
  */
 -->
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" />
 <#compress>
-<#if (parameters.validate!false == false)>
- <#if parameters.onsubmit?has_content>
-  ${tag.addParameter('onsubmit', "${parameters.onsubmit}") }
+<#if (attributes.validate!false == false)>
+ <#if attributes.onsubmit?has_content>
+  ${tag.addParameter('onsubmit', "${attributes.onsubmit}") }
  </#if>
 </#if>
 <form
-<#if parameters.id?has_content>
- id="${parameters.id}"
+<#if attributes.id?has_content>
+ id="${attributes.id}"
 </#if>
-<#if parameters.name?has_content>
- name="${parameters.name}"
+<#if attributes.name?has_content>
+ name="${attributes.name}"
 </#if>
-<#if parameters.onsubmit?has_content>
- onsubmit="<#outputformat 'JavaScript'>${parameters.onsubmit}</#outputformat>"
+<#if attributes.onsubmit?has_content>
+ onsubmit="<#outputformat 'JavaScript'>${attributes.onsubmit}</#outputformat>"
 </#if>
-<#if parameters.onreset?has_content>
- onreset="<#outputformat 'JavaScript'>${parameters.onreset}</#outputformat>"
+<#if attributes.onreset?has_content>
+ onreset="<#outputformat 'JavaScript'>${attributes.onreset}</#outputformat>"
 </#if>
-<#if parameters.action?has_content>
- action="${parameters.action}"
+<#if attributes.action?has_content>
+ action="${attributes.action}"
 </#if>
-<#if parameters.target?has_content>
- target="${parameters.target}"
+<#if attributes.target?has_content>
+ target="${attributes.target}"
 </#if>
-<#if parameters.method?has_content>
- method="${parameters.method}"
+<#if attributes.method?has_content>
+ method="${attributes.method}"
 <#else>
  method="post"
 </#if>
-<#if parameters.enctype?has_content>
- enctype="${parameters.enctype}"
+<#if attributes.enctype?has_content>
+ enctype="${attributes.enctype}"
 </#if>
-<#if parameters.cssClass?has_content>
- class="${parameters.cssClass}"
+<#if attributes.cssClass?has_content>
+ class="${attributes.cssClass}"
 </#if>
-<#if parameters.cssStyle?has_content>
- style="${parameters.cssStyle}"
+<#if attributes.cssStyle?has_content>
+ style="${attributes.cssStyle}"
 </#if>
-<#if parameters.title?has_content>
- title="${parameters.title}"
+<#if attributes.title?has_content>
+ title="${attributes.title}"
 </#if>
-<#if parameters.acceptcharset?has_content>
- accept-charset="${parameters.acceptcharset}"
+<#if attributes.acceptcharset?has_content>
+ accept-charset="${attributes.acceptcharset}"
 </#if>
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
-<#if parameters.onreset?has_content>
- onreset="${parameters.onreset}"
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" />
+<#if attributes.onreset?has_content>
+ onreset="${attributes.onreset}"
 </#if>
 >
 </#compress>
diff --git a/core/src/main/resources/template/html5/head.ftl 
b/core/src/main/resources/template/html5/head.ftl
index 01c5141a9..502d9a30d 100644
--- a/core/src/main/resources/template/html5/head.ftl
+++ b/core/src/main/resources/template/html5/head.ftl
@@ -19,5 +19,5 @@
  */
 -->
 <@compress single_line=true>
-<@s.script src="${base}${parameters.staticContentPath}/utils.js" />
+<@s.script src="${base}${attributes.staticContentPath}/utils.js" />
 </@compress>
diff --git a/core/src/main/resources/template/html5/hidden.ftl 
b/core/src/main/resources/template/html5/hidden.ftl
index 7e7184953..afcc6b20b 100644
--- a/core/src/main/resources/template/html5/hidden.ftl
+++ b/core/src/main/resources/template/html5/hidden.ftl
@@ -18,26 +18,26 @@
  * under the License.
  */
 -->
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" />
 <#compress>
 <input type="hidden"
- name="${(parameters.name!"")}"
-<#if parameters.nameValue??>
- value="<@s.property value="parameters.nameValue"/>"
+ name="${(attributes.name!"")}"
+<#if attributes.nameValue??>
+ value="<@s.property value="attributes.nameValue"/>"
 </#if>
-<#if parameters.id?has_content>
- id="${parameters.id}"
+<#if attributes.id?has_content>
+ id="${attributes.id}"
 </#if>
-<#if parameters.cssClass?has_content>
- class="${parameters.cssClass}"
+<#if attributes.cssClass?has_content>
+ class="${attributes.cssClass}"
 </#if>
-<#if parameters.cssStyle?has_content>
- style="${parameters.cssStyle}"
+<#if attributes.cssStyle?has_content>
+ style="${attributes.cssStyle}"
 </#if>
-<#if parameters.disabled!false>
+<#if attributes.disabled!false>
  disabled="disabled"
 </#if>
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" />
 />
 </#compress>
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" />
diff --git a/core/src/main/resources/template/html5/label.ftl 
b/core/src/main/resources/template/html5/label.ftl
index 730efb048..7a5641a99 100644
--- a/core/src/main/resources/template/html5/label.ftl
+++ b/core/src/main/resources/template/html5/label.ftl
@@ -18,25 +18,25 @@
  * under the License.
  */
 -->
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" />
 <#compress>
 <label
-<#if parameters.id?has_content>
- id="${parameters.id}"
+<#if attributes.id?has_content>
+ id="${attributes.id}"
 </#if>
-<#include "/${parameters.templateDir}/${parameters.expandTheme}/css.ftl" />
-<#if parameters.title?has_content>
- title="${parameters.title}"
+<#include "/${attributes.templateDir}/${attributes.expandTheme}/css.ftl" />
+<#if attributes.title?has_content>
+ title="${attributes.title}"
 </#if>
-<#if parameters.for?has_content>
- for="${parameters.for}"
+<#if attributes.for?has_content>
+ for="${attributes.for}"
 </#if>
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" />
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" />
 >
-<#if parameters.nameValue??>
-<@s.property value="parameters.nameValue"/>
+<#if attributes.nameValue??>
+<@s.property value="attributes.nameValue"/>
 </#if>
 </label>
 </#compress>
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" />
diff --git a/core/src/main/resources/template/html5/link.ftl 
b/core/src/main/resources/template/html5/link.ftl
index 891d23763..9bd742b55 100644
--- a/core/src/main/resources/template/html5/link.ftl
+++ b/core/src/main/resources/template/html5/link.ftl
@@ -18,57 +18,57 @@
  * under the License.
  */
 -->
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" />
 <@compress single_line=true>
     <link
-            <#if parameters.rel?has_content>
-                rel="${parameters.rel}"
+            <#if attributes.rel?has_content>
+                rel="${attributes.rel}"
             <#else>
                 rel="stylesheet"
             </#if>
-            <#if parameters.type?has_content>
-                type="${parameters.type}"
+            <#if attributes.type?has_content>
+                type="${attributes.type}"
             <#else>
                 type="text/css"
             </#if>
-            <#if parameters.href?has_content>
-                href="${parameters.href}"
+            <#if attributes.href?has_content>
+                href="${attributes.href}"
             </#if>
-            <#if parameters.hreflang?has_content>
-                hreflang="${parameters.hreflang}"
+            <#if attributes.hreflang?has_content>
+                hreflang="${attributes.hreflang}"
             </#if>
-            <#if parameters.disabled?has_content && parameters.disabled == 
"true">
+            <#if attributes.disabled?has_content && attributes.disabled == 
"true">
                 disabled
             </#if>
-            <#if parameters.media?has_content>
-                media="${parameters.media}"
+            <#if attributes.media?has_content>
+                media="${attributes.media}"
             <#else>
                 media="all"
             </#if>
-            <#if parameters.title?has_content>
-                title="${parameters.title}"
+            <#if attributes.title?has_content>
+                title="${attributes.title}"
             </#if>
-            <#if parameters.as?has_content>
-                as="${parameters.as}"
+            <#if attributes.as?has_content>
+                as="${attributes.as}"
             </#if>
-            <#if parameters.referrerpolicy?has_content>
-                referrerpolicy="${parameters.referrerpolicy}"
+            <#if attributes.referrerpolicy?has_content>
+                referrerpolicy="${attributes.referrerpolicy}"
             </#if>
-            <#if parameters.sizes?has_content>
-                sizes="${parameters.sizes}"
+            <#if attributes.sizes?has_content>
+                sizes="${attributes.sizes}"
             </#if>
-            <#if parameters.crossorigin?has_content>
-                crossorigin="${parameters.crossorigin}"
+            <#if attributes.crossorigin?has_content>
+                crossorigin="${attributes.crossorigin}"
             </#if>
-            <#if parameters.integrity?has_content>
-                integrity="${parameters.integrity}"
+            <#if attributes.integrity?has_content>
+                integrity="${attributes.integrity}"
             </#if>
-            <#if parameters.importance?has_content>
-                importance="${parameters.importance}"
+            <#if attributes.importance?has_content>
+                importance="${attributes.importance}"
             </#if>
-            <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" />
-            <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
-            <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/nonce.ftl" />
+            <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" />
+            <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" />
+            <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/nonce.ftl" />
     />
 </@compress>
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" />
diff --git a/core/src/main/resources/template/html5/nonce.ftl 
b/core/src/main/resources/template/html5/nonce.ftl
index 4e1efe179..8570d9786 100644
--- a/core/src/main/resources/template/html5/nonce.ftl
+++ b/core/src/main/resources/template/html5/nonce.ftl
@@ -19,7 +19,7 @@
  */
 -->
 <#compress>
-<#if parameters.nonce?has_content>
- nonce="${parameters.nonce}"
+<#if attributes.nonce?has_content>
+ nonce="${attributes.nonce}"
 </#if>
 </#compress>
diff --git a/core/src/main/resources/template/html5/optgroup.ftl 
b/core/src/main/resources/template/html5/optgroup.ftl
index 8273a5d00..c1d4681c3 100644
--- a/core/src/main/resources/template/html5/optgroup.ftl
+++ b/core/src/main/resources/template/html5/optgroup.ftl
@@ -18,38 +18,38 @@
  * under the License.
  */
 -->
-<#if parameters.optGroupInternalListUiBeanList??>
+<#if attributes.optGroupInternalListUiBeanList??>
     <#compress>
-        <#assign 
optGroupInternalListUiBeans=parameters.optGroupInternalListUiBeanList />
+        <#assign 
optGroupInternalListUiBeans=attributes.optGroupInternalListUiBeanList />
         <#list optGroupInternalListUiBeans as optGroupInternalListUiBean>
             <optgroup<#rt>
-                    <#if 
optGroupInternalListUiBean.parameters.label?has_content>
-                        
label="${optGroupInternalListUiBean.parameters.label}"<#rt>
+                    <#if 
optGroupInternalListUiBean.attributes.label?has_content>
+                        
label="${optGroupInternalListUiBean.attributes.label}"<#rt>
                     </#if>
-                    <#if optGroupInternalListUiBean.parameters.disabled!false>
+                    <#if optGroupInternalListUiBean.attributes.disabled!false>
                         disabled="disabled"<#rt>
                     </#if>
-                    <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
+                    <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" />
             >
-                <#list optGroupInternalListUiBean.parameters.list as 
optGroupBean>
+                <#list optGroupInternalListUiBean.attributes.list as 
optGroupBean>
                     <#assign trash=stack.push(optGroupBean) />
-                    <#assign 
tmpKey=stack.findValue(optGroupInternalListUiBean.parameters.listKey) />
-                    <#assign 
tmpValue=stack.findValue(optGroupInternalListUiBean.parameters.listValue) />
+                    <#assign 
tmpKey=stack.findValue(optGroupInternalListUiBean.attributes.listKey) />
+                    <#assign 
tmpValue=stack.findValue(optGroupInternalListUiBean.attributes.listValue) />
                     <#assign tmpKeyStr = tmpKey.toString() />
                     <#assign optGroupItemCssClass = ''/>
-                    <#if optGroupInternalListUiBean.parameters.listCssClass??>
-                        <#assign optGroupItemCssClass= 
stack.findString(optGroupInternalListUiBean.parameters.listCssClass)!''/>
+                    <#if optGroupInternalListUiBean.attributes.listCssClass??>
+                        <#assign optGroupItemCssClass= 
stack.findString(optGroupInternalListUiBean.attributes.listCssClass)!''/>
                     </#if>
                     <#assign optGroupItemCssStyle = ''/>
-                    <#if optGroupInternalListUiBean.parameters.listCssStyle??>
-                        <#assign optGroupItemCssStyle= 
stack.findString(optGroupInternalListUiBean.parameters.listCssStyle)!''/>
+                    <#if optGroupInternalListUiBean.attributes.listCssStyle??>
+                        <#assign optGroupItemCssStyle= 
stack.findString(optGroupInternalListUiBean.attributes.listCssStyle)!''/>
                     </#if>
                     <#assign optGroupItemTitle = ''/>
-                    <#if optGroupInternalListUiBean.parameters.listTitle??>
-                        <#assign optGroupItemTitle= 
stack.findString(optGroupInternalListUiBean.parameters.listTitle)!''/>
+                    <#if optGroupInternalListUiBean.attributes.listTitle??>
+                        <#assign optGroupItemTitle= 
stack.findString(optGroupInternalListUiBean.attributes.listTitle)!''/>
                     </#if>
                     <option value="${tmpKeyStr}"<#rt>
-                    <#if tag.contains(parameters.nameValue, tmpKey) == true>
+                    <#if tag.contains(attributes.nameValue, tmpKey) == true>
                         selected="selected"<#rt>
                     </#if>
                     <#if optGroupItemCssClass?has_content>
diff --git a/core/src/main/resources/template/html5/password.ftl 
b/core/src/main/resources/template/html5/password.ftl
index 222aa5947..63d807969 100644
--- a/core/src/main/resources/template/html5/password.ftl
+++ b/core/src/main/resources/template/html5/password.ftl
@@ -18,38 +18,38 @@
  * under the License.
  */
 -->
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" />
 <#compress>
 <input type="password"
- name="${(parameters.name!"")}"
-<#if parameters.get("size")?has_content>
- size="${parameters.get("size")}"
+ name="${(attributes.name!"")}"
+<#if attributes.get("size")?has_content>
+ size="${attributes.get("size")}"
 </#if>
-<#if parameters.maxlength?has_content>
- maxlength="${parameters.maxlength}"
+<#if attributes.maxlength?has_content>
+ maxlength="${attributes.maxlength}"
 </#if>
-<#if parameters.nameValue?? && parameters.showPassword!false>
- value="<@s.property value="parameters.nameValue"/>"
+<#if attributes.nameValue?? && attributes.showPassword!false>
+ value="<@s.property value="attributes.nameValue"/>"
 </#if>
-<#if parameters.disabled!false>
+<#if attributes.disabled!false>
  disabled="disabled"
 </#if>
-<#if parameters.readonly!false>
+<#if attributes.readonly!false>
  readonly="readonly"
 </#if>
-<#if parameters.tabindex?has_content>
- tabindex="${parameters.tabindex}"
+<#if attributes.tabindex?has_content>
+ tabindex="${attributes.tabindex}"
 </#if>
-<#if parameters.id?has_content>
- id="${parameters.id}"
+<#if attributes.id?has_content>
+ id="${attributes.id}"
 </#if>
-<#include "/${parameters.templateDir}/${parameters.expandTheme}/css.ftl" />
-<#if parameters.title?has_content>
- title="${parameters.title}"
+<#include "/${attributes.templateDir}/${attributes.expandTheme}/css.ftl" />
+<#if attributes.title?has_content>
+ title="${attributes.title}"
 </#if>
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" />
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" />
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/scripting-events.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" />
 />
 </#compress>
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" />
diff --git 
a/core/src/main/resources/template/html5/prefixed-dynamic-attributes.ftl 
b/core/src/main/resources/template/html5/prefixed-dynamic-attributes.ftl
index ad1dd1cd5..0d94cd74e 100644
--- a/core/src/main/resources/template/html5/prefixed-dynamic-attributes.ftl
+++ b/core/src/main/resources/template/html5/prefixed-dynamic-attributes.ftl
@@ -20,11 +20,11 @@
  */
 -->
 <#macro prefixedDynamicAttributes prefix>
-<#if (parameters.dynamicAttributes?? && parameters.dynamicAttributes?size > 0)>
-<#assign aKeys = parameters.dynamicAttributes.keySet()>
+<#if (attributes.dynamicAttributes?? && attributes.dynamicAttributes?size > 0)>
+<#assign aKeys = attributes.dynamicAttributes.keySet()>
 <#list aKeys as aKey>
 <#if aKey?starts_with(prefix)>
-<#assign keyValue = parameters.dynamicAttributes.get(aKey)/>
+<#assign keyValue = attributes.dynamicAttributes.get(aKey)/>
 <#if keyValue?is_string>
     <#assign value = struts.translateVariables(keyValue)!keyValue/>
 <#else>
diff --git a/core/src/main/resources/template/html5/radiomap.ftl 
b/core/src/main/resources/template/html5/radiomap.ftl
index 208f45f1d..13a597f70 100644
--- a/core/src/main/resources/template/html5/radiomap.ftl
+++ b/core/src/main/resources/template/html5/radiomap.ftl
@@ -18,67 +18,67 @@
  * under the License.
  */
 -->
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" />
 <#compress>
-    <@s.iterator value="parameters.list">
-        <#if parameters.listKey??>
-            <#assign itemKey = stack.findValue(parameters.listKey)/>
-            <#assign itemKeyStr = stack.findString(parameters.listKey)/>
+    <@s.iterator value="attributes.list">
+        <#if attributes.listKey??>
+            <#assign itemKey = stack.findValue(attributes.listKey)/>
+            <#assign itemKeyStr = stack.findString(attributes.listKey)/>
         <#else>
             <#assign itemKey = stack.findValue('top')/>
             <#assign itemKeyStr = stack.findString('top')>
         </#if>
-        <#if parameters.listValueKey??>
+        <#if attributes.listValueKey??>
         <#-- checks the valueStack for the 'valueKey.' The valueKey is then 
looked-up in the locale
              file for it's localized value.  This is then used as a label -->
-            <#assign valueKey = stack.findString(parameters.listValueKey)!''/>
+            <#assign valueKey = stack.findString(attributes.listValueKey)!''/>
             <#if valueKey?has_content>
                 <#assign itemValue = struts.getText(valueKey) />
             <#else>
-                <#assign itemValue = parameters.listValueKey />
+                <#assign itemValue = attributes.listValueKey />
             </#if>
-        <#elseif parameters.listValue??>
-            <#assign itemValue = stack.findString(parameters.listValue)/>
+        <#elseif attributes.listValue??>
+            <#assign itemValue = stack.findString(attributes.listValue)/>
         <#else>
             <#assign itemValue = stack.findString('top')/>
         </#if>
-        <#if parameters.listCssClass??>
-            <#if stack.findString(parameters.listCssClass)??>
-                <#assign itemCssClass= 
stack.findString(parameters.listCssClass)/>
+        <#if attributes.listCssClass??>
+            <#if stack.findString(attributes.listCssClass)??>
+                <#assign itemCssClass= 
stack.findString(attributes.listCssClass)/>
             <#else>
                 <#assign itemCssClass = ''/>
             </#if>
         </#if>
-        <#if parameters.listCssStyle??>
-            <#if stack.findString(parameters.listCssStyle)??>
-                <#assign itemCssStyle= 
stack.findString(parameters.listCssStyle)/>
+        <#if attributes.listCssStyle??>
+            <#if stack.findString(attributes.listCssStyle)??>
+                <#assign itemCssStyle= 
stack.findString(attributes.listCssStyle)/>
             <#else>
                 <#assign itemCssStyle = ''/>
             </#if>
         </#if>
-        <#if parameters.listTitle??>
-            <#if stack.findString(parameters.listTitle)??>
-                <#assign itemTitle= stack.findString(parameters.listTitle)/>
+        <#if attributes.listTitle??>
+            <#if stack.findString(attributes.listTitle)??>
+                <#assign itemTitle= stack.findString(attributes.listTitle)/>
             <#else>
                 <#assign itemTitle = ''/>
             </#if>
         </#if>
         <input type="radio"
-                <#if parameters.name?has_content>
-                    name="${parameters.name?no_esc}"
+                <#if attributes.name?has_content>
+                    name="${attributes.name?no_esc}"
                 </#if>
-               id="${parameters.id}${itemKeyStr?replace(".", "_")}"
-                <#if tag.contains(parameters.nameValue!'', itemKey)>
+               id="${attributes.id}${itemKeyStr?replace(".", "_")}"
+                <#if tag.contains(attributes.nameValue!'', itemKey)>
                     checked="checked"
                 </#if>
                 <#if itemKey??>
                     value="${itemKeyStr}"
                 </#if>
-                <#if parameters.disabled!false>
+                <#if attributes.disabled!false>
                     disabled="disabled"
                 </#if>
-                <#if parameters.tabindex?has_content>
-                    tabindex="${parameters.tabindex}"
+                <#if attributes.tabindex?has_content>
+                    tabindex="${attributes.tabindex}"
                 </#if>
                 <#if itemCssClass?has_content>
                     class="${itemCssClass}"
@@ -89,19 +89,19 @@
                 <#if itemTitle?has_content>
                     title="${itemTitle}"
                 <#else>
-                    <#if parameters.title?has_content>
-                        title="${parameters.title}"
+                    <#if attributes.title?has_content>
+                        title="${attributes.title}"
                     </#if>
                 </#if>
-                <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/css.ftl" />
-                <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" />
-                <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" />
+                <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/css.ftl" />
+                <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/scripting-events.ftl" />
+                <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" />
                 <#global evaluate_dynamic_attributes = true/>
-                <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
+                <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" />
         />
-        <label for="${parameters.id}${itemKeyStr?replace(".", "_")}"<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/css.ftl"/>>
+        <label for="${attributes.id}${itemKeyStr?replace(".", "_")}"<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/css.ftl"/>>
             ${itemValue}
         </label>
     </@s.iterator>
 </#compress>
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" />
diff --git a/core/src/main/resources/template/html5/reset.ftl 
b/core/src/main/resources/template/html5/reset.ftl
index 56dc96aa7..02d36e9e5 100644
--- a/core/src/main/resources/template/html5/reset.ftl
+++ b/core/src/main/resources/template/html5/reset.ftl
@@ -18,67 +18,67 @@
  * under the License.
  */
 -->
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" />
 <#compress>
-    <#if parameters.type?? && parameters.type=="button">
+    <#if attributes.type?? && attributes.type=="button">
         <button type="reset"
-                <#if parameters.name?has_content>
-                    name="${parameters.name}"
+                <#if attributes.name?has_content>
+                    name="${attributes.name}"
                 </#if>
-                <#if parameters.nameValue??>
-                    value="<@s.property value="parameters.nameValue"/>"
+                <#if attributes.nameValue??>
+                    value="<@s.property value="attributes.nameValue"/>"
                 </#if>
-                <#if parameters.cssClass?has_content>
-                    class="${parameters.cssClass}"
+                <#if attributes.cssClass?has_content>
+                    class="${attributes.cssClass}"
                 </#if>
-                <#if parameters.cssStyle?has_content>
-                    style="${parameters.cssStyle}"
+                <#if attributes.cssStyle?has_content>
+                    style="${attributes.cssStyle}"
                 </#if>
-                <#if parameters.disabled!false>
+                <#if attributes.disabled!false>
                     disabled="disabled"
                 </#if>
-                <#if parameters.tabindex?has_content>
-                    tabindex="${parameters.tabindex}"
+                <#if attributes.tabindex?has_content>
+                    tabindex="${attributes.tabindex}"
                 </#if>
-                <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl"/>
-                <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" />
-                <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
-        ><#if parameters.src?has_content>
+                <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/scripting-events.ftl"/>
+                <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" />
+                <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" />
+        ><#if attributes.src?has_content>
                 <img
-                <#if parameters.label?has_content>
-                    alt="${parameters.label}"
+                <#if attributes.label?has_content>
+                    alt="${attributes.label}"
                 </#if>
-                <#if parameters.src?has_content>
-                    src="${parameters.src}"
+                <#if attributes.src?has_content>
+                    src="${attributes.src}"
                 </#if>
-                /><#else><#if parameters.label?has_content><@s.property 
value="parameters.label"/></#if></#if></button>
+                /><#else><#if attributes.label?has_content><@s.property 
value="attributes.label"/></#if></#if></button>
     <#else>
         <input type="reset"
-                <#if parameters.name?has_content>
-                    name="${parameters.name}"
+                <#if attributes.name?has_content>
+                    name="${attributes.name}"
                 </#if>
-                <#if parameters.nameValue??>
-                    value="<@s.property value="parameters.nameValue"/>"
+                <#if attributes.nameValue??>
+                    value="<@s.property value="attributes.nameValue"/>"
                 </#if>
-                <#if parameters.cssClass?has_content>
-                    class="${parameters.cssClass}"
+                <#if attributes.cssClass?has_content>
+                    class="${attributes.cssClass}"
                 </#if>
-                <#if parameters.cssStyle?has_content>
-                    style="${parameters.cssStyle}"
+                <#if attributes.cssStyle?has_content>
+                    style="${attributes.cssStyle}"
                 </#if>
-                <#if parameters.title?has_content>
-                    title="${parameters.title}"
+                <#if attributes.title?has_content>
+                    title="${attributes.title}"
                 </#if>
-                <#if parameters.disabled!false>
+                <#if attributes.disabled!false>
                     disabled="disabled"
                 </#if>
-                <#if parameters.tabindex?has_content>
-                    tabindex="${parameters.tabindex}"
+                <#if attributes.tabindex?has_content>
+                    tabindex="${attributes.tabindex}"
                 </#if>
-                <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" />
-                <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" />
-                <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
+                <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/scripting-events.ftl" />
+                <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" />
+                <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" />
         />
     </#if>
 </#compress>
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" />
diff --git a/core/src/main/resources/template/html5/script.ftl 
b/core/src/main/resources/template/html5/script.ftl
index b37b0b7c2..c5d8b7d35 100644
--- a/core/src/main/resources/template/html5/script.ftl
+++ b/core/src/main/resources/template/html5/script.ftl
@@ -20,34 +20,34 @@
 -->
 <@compress single_line=true>
 <script
-        <#if parameters.async?has_content && parameters.async == "true">
+        <#if attributes.async?has_content && attributes.async == "true">
             async
         </#if>
-        <#if parameters.charset?has_content>
-            charset="${parameters.charset}"
+        <#if attributes.charset?has_content>
+            charset="${attributes.charset}"
         </#if>
-        <#if parameters.defer?has_content && parameters.defer=="true">
+        <#if attributes.defer?has_content && attributes.defer=="true">
             defer
         </#if>
-        <#if parameters.type?has_content>
-            type="${parameters.type}"
+        <#if attributes.type?has_content>
+            type="${attributes.type}"
         </#if>
-        <#if parameters.src?has_content>
-            src="${parameters.src}"
+        <#if attributes.src?has_content>
+            src="${attributes.src}"
         </#if>
-        <#if parameters.referrerpolicy?has_content>
-            referrerpolicy="${parameters.referrerpolicy}"
+        <#if attributes.referrerpolicy?has_content>
+            referrerpolicy="${attributes.referrerpolicy}"
         </#if>
-        <#if parameters.nomodule?has_content && parameters.nomodule=="true">
+        <#if attributes.nomodule?has_content && attributes.nomodule=="true">
             nomodule
         </#if>
-        <#if parameters.integrity?has_content>
-            integrity="${parameters.integrity}"
+        <#if attributes.integrity?has_content>
+            integrity="${attributes.integrity}"
         </#if>
-        <#if parameters.crossorigin?has_content>
-            crossorigin="${parameters.crossorigin}"
+        <#if attributes.crossorigin?has_content>
+            crossorigin="${attributes.crossorigin}"
         </#if>
-        <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" />
-        <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
-        <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/nonce.ftl" />>
+        <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" />
+        <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" />
+        <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/nonce.ftl" />>
 </@compress>
diff --git a/core/src/main/resources/template/html5/scripting-events.ftl 
b/core/src/main/resources/template/html5/scripting-events.ftl
index 714ae9d56..9dc06bc7c 100644
--- a/core/src/main/resources/template/html5/scripting-events.ftl
+++ b/core/src/main/resources/template/html5/scripting-events.ftl
@@ -19,46 +19,46 @@
  */
 -->
 <#compress>
-<#if parameters.onclick??>
- onclick="<#outputformat 'JavaScript'>${parameters.onclick}</#outputformat>"
+<#if attributes.onclick??>
+ onclick="<#outputformat 'JavaScript'>${attributes.onclick}</#outputformat>"
 </#if>
-<#if parameters.ondblclick??>
- ondblclick="<#outputformat 
'JavaScript'>${parameters.ondblclick}</#outputformat>"
+<#if attributes.ondblclick??>
+ ondblclick="<#outputformat 
'JavaScript'>${attributes.ondblclick}</#outputformat>"
 </#if>
-<#if parameters.onmousedown??>
- onmousedown="<#outputformat 
'JavaScript'>${parameters.onmousedown}</#outputformat>"
+<#if attributes.onmousedown??>
+ onmousedown="<#outputformat 
'JavaScript'>${attributes.onmousedown}</#outputformat>"
 </#if>
-<#if parameters.onmouseup??>
- onmouseup="<#outputformat 
'JavaScript'>${parameters.onmouseup}</#outputformat>"
+<#if attributes.onmouseup??>
+ onmouseup="<#outputformat 
'JavaScript'>${attributes.onmouseup}</#outputformat>"
 </#if>
-<#if parameters.onmouseover??>
- onmouseover="<#outputformat 
'JavaScript'>${parameters.onmouseover}</#outputformat>"
+<#if attributes.onmouseover??>
+ onmouseover="<#outputformat 
'JavaScript'>${attributes.onmouseover}</#outputformat>"
 </#if>
-<#if parameters.onmousemove??>
- onmousemove="<#outputformat 
'JavaScript'>${parameters.onmousemove}</#outputformat>"
+<#if attributes.onmousemove??>
+ onmousemove="<#outputformat 
'JavaScript'>${attributes.onmousemove}</#outputformat>"
 </#if>
-<#if parameters.onmouseout??>
- onmouseout="<#outputformat 
'JavaScript'>${parameters.onmouseout}</#outputformat>"
+<#if attributes.onmouseout??>
+ onmouseout="<#outputformat 
'JavaScript'>${attributes.onmouseout}</#outputformat>"
 </#if>
-<#if parameters.onfocus??>
- onfocus="<#outputformat 'JavaScript'>${parameters.onfocus}</#outputformat>"
+<#if attributes.onfocus??>
+ onfocus="<#outputformat 'JavaScript'>${attributes.onfocus}</#outputformat>"
 </#if>
-<#if parameters.onblur??>
- onblur="<#outputformat 'JavaScript'>${parameters.onblur}</#outputformat>"
+<#if attributes.onblur??>
+ onblur="<#outputformat 'JavaScript'>${attributes.onblur}</#outputformat>"
 </#if>
-<#if parameters.onkeypress??>
- onkeypress="<#outputformat 
'JavaScript'>${parameters.onkeypress}</#outputformat>"
+<#if attributes.onkeypress??>
+ onkeypress="<#outputformat 
'JavaScript'>${attributes.onkeypress}</#outputformat>"
 </#if>
-<#if parameters.onkeydown??>
- onkeydown="<#outputformat 
'JavaScript'>${parameters.onkeydown}</#outputformat>"
+<#if attributes.onkeydown??>
+ onkeydown="<#outputformat 
'JavaScript'>${attributes.onkeydown}</#outputformat>"
 </#if>
-<#if parameters.onkeyup??>
- onkeyup="<#outputformat 'JavaScript'>${parameters.onkeyup}</#outputformat>"
+<#if attributes.onkeyup??>
+ onkeyup="<#outputformat 'JavaScript'>${attributes.onkeyup}</#outputformat>"
 </#if>
-<#if parameters.onselect??>
- onselect="<#outputformat 'JavaScript'>${parameters.onselect}</#outputformat>"
+<#if attributes.onselect??>
+ onselect="<#outputformat 'JavaScript'>${attributes.onselect}</#outputformat>"
 </#if>
-<#if parameters.onchange??>
- onchange="<#outputformat 'JavaScript'>${parameters.onchange}</#outputformat>"
+<#if attributes.onchange??>
+ onchange="<#outputformat 'JavaScript'>${attributes.onchange}</#outputformat>"
 </#if>
 </#compress>
diff --git a/core/src/main/resources/template/html5/select.ftl 
b/core/src/main/resources/template/html5/select.ftl
index d30b3e1ae..324ce2abf 100644
--- a/core/src/main/resources/template/html5/select.ftl
+++ b/core/src/main/resources/template/html5/select.ftl
@@ -18,49 +18,49 @@
  * under the License.
  */
 -->
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" />
 <#compress>
     <#setting number_format="#.#####">
     <select
-            name="${(parameters.name!"")}"
-            <#if parameters.get("size")?has_content>
-                size="${parameters.get("size")}"
+            name="${(attributes.name!"")}"
+            <#if attributes.get("size")?has_content>
+                size="${attributes.get("size")}"
             </#if>
-            <#if parameters.disabled!false>
+            <#if attributes.disabled!false>
                 disabled="disabled"
             </#if>
-            <#if parameters.tabindex?has_content>
-                tabindex="${parameters.tabindex}"
+            <#if attributes.tabindex?has_content>
+                tabindex="${attributes.tabindex}"
             </#if>
-            <#if parameters.id?has_content>
-                id="${parameters.id}"
+            <#if attributes.id?has_content>
+                id="${attributes.id}"
             </#if>
-            <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/css.ftl" />
-            <#if parameters.title?has_content>
-                title="${parameters.title}"
+            <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/css.ftl" />
+            <#if attributes.title?has_content>
+                title="${attributes.title}"
             </#if>
-            <#if parameters.multiple!false>
+            <#if attributes.multiple!false>
                 multiple="multiple"
             </#if>
-            <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" />
-            <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" />
-            <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
+            <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/scripting-events.ftl" />
+            <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" />
+            <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" />
     >
-        <#if parameters.headerKey?? && parameters.headerValue??>
-            <option value="${parameters.headerKey}"
-                    <#if tag.contains(parameters.nameValue, 
parameters.headerKey) == true>
+        <#if attributes.headerKey?? && attributes.headerValue??>
+            <option value="${attributes.headerKey}"
+                    <#if tag.contains(attributes.nameValue, 
attributes.headerKey) == true>
                         selected="selected"
                     </#if>
-            >${parameters.headerValue}</option>
+            >${attributes.headerValue}</option>
         </#if>
-        <#if parameters.emptyOption!false>
+        <#if attributes.emptyOption!false>
             <option value=""></option>
         </#if>
-        <@s.iterator value="parameters.list">
-            <#if parameters.listKey??>
-                <#if stack.findValue(parameters.listKey)??>
-                    <#assign itemKey = stack.findValue(parameters.listKey)/>
-                    <#assign itemKeyStr = 
stack.findString(parameters.listKey)/>
+        <@s.iterator value="attributes.list">
+            <#if attributes.listKey??>
+                <#if stack.findValue(attributes.listKey)??>
+                    <#assign itemKey = stack.findValue(attributes.listKey)/>
+                    <#assign itemKeyStr = 
stack.findString(attributes.listKey)/>
                 <#else>
                     <#assign itemKey = ''/>
                     <#assign itemKeyStr = ''/>
@@ -69,47 +69,47 @@
                 <#assign itemKey = stack.findValue('top')/>
                 <#assign itemKeyStr = stack.findString('top')>
             </#if>
-            <#if parameters.listValueKey??>
+            <#if attributes.listValueKey??>
             <#-- checks the valueStack for the 'valueKey.' The valueKey is 
then looked-up in the locale file for it's
                localized value.  This is then used as a label -->
-                <#assign valueKey = 
stack.findString(parameters.listValueKey)!'' />
+                <#assign valueKey = 
stack.findString(attributes.listValueKey)!'' />
                 <#if valueKey?has_content>
                     <#assign itemValue = struts.getText(valueKey) />
                 <#else>
-                    <#assign itemValue = parameters.listValueKey />
+                    <#assign itemValue = attributes.listValueKey />
                 </#if>
-            <#elseif parameters.listValue??>
-                <#if stack.findString(parameters.listValue)??>
-                    <#assign itemValue = 
stack.findString(parameters.listValue)/>
+            <#elseif attributes.listValue??>
+                <#if stack.findString(attributes.listValue)??>
+                    <#assign itemValue = 
stack.findString(attributes.listValue)/>
                 <#else>
                     <#assign itemValue = ''/>
                 </#if>
             <#else>
                 <#assign itemValue = stack.findString('top')/>
             </#if>
-            <#if parameters.listCssClass??>
-                <#if stack.findString(parameters.listCssClass)??>
-                    <#assign itemCssClass= 
stack.findString(parameters.listCssClass)/>
+            <#if attributes.listCssClass??>
+                <#if stack.findString(attributes.listCssClass)??>
+                    <#assign itemCssClass= 
stack.findString(attributes.listCssClass)/>
                 <#else>
                     <#assign itemCssClass = ''/>
                 </#if>
             </#if>
-            <#if parameters.listCssStyle??>
-                <#if stack.findString(parameters.listCssStyle)??>
-                    <#assign itemCssStyle= 
stack.findString(parameters.listCssStyle)/>
+            <#if attributes.listCssStyle??>
+                <#if stack.findString(attributes.listCssStyle)??>
+                    <#assign itemCssStyle= 
stack.findString(attributes.listCssStyle)/>
                 <#else>
                     <#assign itemCssStyle = ''/>
                 </#if>
             </#if>
-            <#if parameters.listTitle??>
-                <#if stack.findString(parameters.listTitle)??>
-                    <#assign itemTitle= 
stack.findString(parameters.listTitle)/>
+            <#if attributes.listTitle??>
+                <#if stack.findString(attributes.listTitle)??>
+                    <#assign itemTitle= 
stack.findString(attributes.listTitle)/>
                 <#else>
                     <#assign itemTitle = ''/>
                 </#if>
             </#if>
             <option value="${itemKeyStr}"
-                    <#if tag.contains(parameters.nameValue, itemKey) == true>
+                    <#if tag.contains(attributes.nameValue, itemKey) == true>
                         selected="selected"
                     </#if>
                     <#if itemCssClass?has_content>
@@ -123,27 +123,27 @@
                     </#if>
             >${itemValue}</option>
         </@s.iterator>
-        <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/optgroup.ftl" />
+        <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/optgroup.ftl" />
     </select>
 
-    <#if parameters.multiple!false>
-        <#if (parameters.id?? && parameters.name??)>
-            <input type="hidden" id="__multiselect_${parameters.id}" 
name="__multiselect_${parameters.name}" value=""
+    <#if attributes.multiple!false>
+        <#if (attributes.id?? && attributes.name??)>
+            <input type="hidden" id="__multiselect_${attributes.id}" 
name="__multiselect_${attributes.name}" value=""
         </#if>
-        <#if (parameters.id?? && !parameters.name??)>
-            <input type="hidden" id="__multiselect_${parameters.id}" 
name="__multiselect_${parameters.id}" value=""
+        <#if (attributes.id?? && !attributes.name??)>
+            <input type="hidden" id="__multiselect_${attributes.id}" 
name="__multiselect_${attributes.id}" value=""
         </#if>
-        <#if ( !parameters.id?? && parameters.name??)>
-            <input type="hidden" id="__multiselect_${parameters.id}" 
name="__multiselect_${parameters.id}" value=""
+        <#if ( !attributes.id?? && attributes.name??)>
+            <input type="hidden" id="__multiselect_${attributes.id}" 
name="__multiselect_${attributes.id}" value=""
         </#if>
-        <#if ( !parameters.id?? && !parameters.name??)>
+        <#if ( !attributes.id?? && !attributes.name??)>
             <input type="hidden" id="" name="" value=""
         </#if>
 
-        <#if parameters.disabled!false>
+        <#if attributes.disabled!false>
             disabled="disabled"
         </#if>
         />
     </#if>
 </#compress>
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" />
diff --git a/core/src/main/resources/template/html5/submit-close.ftl 
b/core/src/main/resources/template/html5/submit-close.ftl
index 1211a060c..3b243d2c9 100644
--- a/core/src/main/resources/template/html5/submit-close.ftl
+++ b/core/src/main/resources/template/html5/submit-close.ftl
@@ -19,11 +19,11 @@
  */
 -->
 <#compress>
-<#if parameters.type?? && parameters.type=="button">
-<#if parameters.body?length gt 0>${tag.escapeHtmlBody()?then(parameters.body, 
parameters.body?no_esc)}<#elseif parameters.label??><@s.property 
value="parameters.label"/></#if>
+<#if attributes.type?? && attributes.type=="button">
+<#if attributes.body?length gt 0>${tag.escapeHtmlBody()?then(attributes.body, 
attributes.body?no_esc)}<#elseif attributes.label??><@s.property 
value="attributes.label"/></#if>
 </button>
 <#else>
-${tag.escapeHtmlBody()?then(parameters.body, parameters.body?no_esc)}
+${tag.escapeHtmlBody()?then(attributes.body, attributes.body?no_esc)}
 </#if>
 </#compress>
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" />
diff --git a/core/src/main/resources/template/html5/submit.ftl 
b/core/src/main/resources/template/html5/submit.ftl
index e3d60452c..d691c090c 100644
--- a/core/src/main/resources/template/html5/submit.ftl
+++ b/core/src/main/resources/template/html5/submit.ftl
@@ -18,77 +18,77 @@
  * under the License.
  */
 -->
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" />
 <#compress>
-<#if parameters.type?? && parameters.type=="button">
+<#if attributes.type?? && attributes.type=="button">
 <button type="submit"
-        <#if parameters.id?has_content>
-            id="${parameters.id}"
+        <#if attributes.id?has_content>
+            id="${attributes.id}"
         </#if>
-        <#if parameters.name?has_content>
-            name="${parameters.name}"
+        <#if attributes.name?has_content>
+            name="${attributes.name}"
         </#if>
-        <#if parameters.nameValue??>
-            value="<@s.property value="parameters.nameValue"/>"
+        <#if attributes.nameValue??>
+            value="<@s.property value="attributes.nameValue"/>"
         </#if>
-        <#if parameters.disabled!false>
+        <#if attributes.disabled!false>
             disabled="disabled"
         </#if>
-        <#if parameters.cssClass?has_content>
-            class="${parameters.cssClass}"
+        <#if attributes.cssClass?has_content>
+            class="${attributes.cssClass}"
         </#if>
-        <#if parameters.cssStyle?has_content>
-            style="${parameters.cssStyle}"
+        <#if attributes.cssStyle?has_content>
+            style="${attributes.cssStyle}"
         </#if>
-        <#if parameters.title?has_content>
-            title="${parameters.title}"
+        <#if attributes.title?has_content>
+            title="${attributes.title}"
         </#if>
-        <#if parameters.tabindex?has_content>
-            tabindex="${parameters.tabindex}"
+        <#if attributes.tabindex?has_content>
+            tabindex="${attributes.tabindex}"
         </#if>
-        <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl"/>
-        <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" />
-        <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
+        <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/scripting-events.ftl"/>
+        <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" />
+        <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" />
 >
 <#else>
-<#if parameters.type?? && parameters.type=="image">
+<#if attributes.type?? && attributes.type=="image">
     <input type="image"
-    <#if parameters.label?has_content>
-     alt="${parameters.label}"
+    <#if attributes.label?has_content>
+     alt="${attributes.label}"
     </#if>
-    <#if parameters.src?has_content>
-     src="${parameters.src}"
+    <#if attributes.src?has_content>
+     src="${attributes.src}"
     </#if>
     <#else>
     <input type="submit"
-    <#if parameters.nameValue?has_content>
-     value="<@s.property value="parameters.nameValue"/>"
+    <#if attributes.nameValue?has_content>
+     value="<@s.property value="attributes.nameValue"/>"
     </#if>
     </#if>
-    <#if parameters.id?has_content>
-     id="${parameters.id}"
+    <#if attributes.id?has_content>
+     id="${attributes.id}"
     </#if>
-    <#if parameters.name?has_content>
-     name="${parameters.name}"
+    <#if attributes.name?has_content>
+     name="${attributes.name}"
     </#if>
-    <#if parameters.disabled!false>
+    <#if attributes.disabled!false>
      disabled="disabled"
     </#if>
-    <#if parameters.cssClass?has_content>
-     class="${parameters.cssClass}"
+    <#if attributes.cssClass?has_content>
+     class="${attributes.cssClass}"
     </#if>
-    <#if parameters.cssStyle?has_content>
-     style="${parameters.cssStyle}"
+    <#if attributes.cssStyle?has_content>
+     style="${attributes.cssStyle}"
     </#if>
-    <#if parameters.title?has_content>
-     title="${parameters.title}"
+    <#if attributes.title?has_content>
+     title="${attributes.title}"
     </#if>
-    <#if parameters.tabindex?has_content>
-     tabindex="${parameters.tabindex}"
+    <#if attributes.tabindex?has_content>
+     tabindex="${attributes.tabindex}"
     </#if>
-    <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" />
-    <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" />
-    <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
+    <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/scripting-events.ftl" />
+    <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" />
+    <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" />
 >
 </#if>
 </#compress>
diff --git a/core/src/main/resources/template/html5/text.ftl 
b/core/src/main/resources/template/html5/text.ftl
index 53b0535bb..7ac956deb 100644
--- a/core/src/main/resources/template/html5/text.ftl
+++ b/core/src/main/resources/template/html5/text.ftl
@@ -18,39 +18,39 @@
  * under the License.
  */
 -->
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" />
 <#compress>
 <input
- type="${(parameters.type!"text")}"
- name="${(parameters.name!"")}"
-<#if parameters.get("size")?has_content>
- size="${parameters.get("size")}"
+ type="${(attributes.type!"text")}"
+ name="${(attributes.name!"")}"
+<#if attributes.get("size")?has_content>
+ size="${attributes.get("size")}"
 </#if>
-<#if parameters.maxlength?has_content>
- maxlength="${parameters.maxlength}"
+<#if attributes.maxlength?has_content>
+ maxlength="${attributes.maxlength}"
 </#if>
-<#if parameters.nameValue??>
- value="${parameters.nameValue}"
+<#if attributes.nameValue??>
+ value="${attributes.nameValue}"
 </#if>
-<#if parameters.disabled!false>
+<#if attributes.disabled!false>
  disabled="disabled"
 </#if>
-<#if parameters.readonly!false>
+<#if attributes.readonly!false>
  readonly="readonly"
 </#if>
-<#if parameters.tabindex?has_content>
- tabindex="${parameters.tabindex}"
+<#if attributes.tabindex?has_content>
+ tabindex="${attributes.tabindex}"
 </#if>
-<#if parameters.id?has_content>
- id="${parameters.id}"
+<#if attributes.id?has_content>
+ id="${attributes.id}"
 </#if>
-<#include "/${parameters.templateDir}/${parameters.expandTheme}/css.ftl" />
-<#if parameters.title?has_content>
- title="${parameters.title}"
+<#include "/${attributes.templateDir}/${attributes.expandTheme}/css.ftl" />
+<#if attributes.title?has_content>
+ title="${attributes.title}"
 </#if>
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" />
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" />
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/scripting-events.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" />
 />
 </#compress>
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" />
diff --git a/core/src/main/resources/template/html5/textarea.ftl 
b/core/src/main/resources/template/html5/textarea.ftl
index 4fc24f322..9866b72c8 100644
--- a/core/src/main/resources/template/html5/textarea.ftl
+++ b/core/src/main/resources/template/html5/textarea.ftl
@@ -18,48 +18,48 @@
  * under the License.
  */
 -->
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" />
 <#compress>
     <textarea
-            name="${(parameters.name!"")}"
-<#if parameters.cols?has_content>
-    cols="${(parameters.cols!"")}"
+            name="${(attributes.name!"")}"
+<#if attributes.cols?has_content>
+    cols="${(attributes.cols!"")}"
 </#if>
-            <#if parameters.rows?has_content>
-                rows="${(parameters.rows!"")}"
+            <#if attributes.rows?has_content>
+                rows="${(attributes.rows!"")}"
             </#if>
-            <#if parameters.wrap?has_content>
-                wrap="${parameters.wrap}"
+            <#if attributes.wrap?has_content>
+                wrap="${attributes.wrap}"
             </#if>
-            <#if parameters.disabled!false>
+            <#if attributes.disabled!false>
                 disabled="disabled"
             </#if>
-            <#if parameters.readonly!false>
+            <#if attributes.readonly!false>
                 readonly="readonly"
             </#if>
-            <#if parameters.tabindex?has_content>
-                tabindex="${parameters.tabindex}"
+            <#if attributes.tabindex?has_content>
+                tabindex="${attributes.tabindex}"
             </#if>
-            <#if parameters.id?has_content>
-                id="${parameters.id}"
+            <#if attributes.id?has_content>
+                id="${attributes.id}"
             </#if>
-            <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/css.ftl" />
-            <#if parameters.title?has_content>
-                title="${parameters.title}"
+            <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/css.ftl" />
+            <#if attributes.title?has_content>
+                title="${attributes.title}"
             </#if>
-            <#if parameters.maxlength?has_content>
-                maxlength="${parameters.maxlength}"
+            <#if attributes.maxlength?has_content>
+                maxlength="${attributes.maxlength}"
             </#if>
-            <#if parameters.minlength?has_content>
-                minlength="${parameters.minlength}"
+            <#if attributes.minlength?has_content>
+                minlength="${attributes.minlength}"
             </#if>
-            <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" />
-            <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" />
-            <#include 
"/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
+            <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/scripting-events.ftl" />
+            <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" />
+            <#include 
"/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" />
 >
-<#if parameters.nameValue??>
-    <@s.property value="parameters.nameValue"/>
+<#if attributes.nameValue??>
+    <@s.property value="attributes.nameValue"/>
 </#if>
 </textarea>
 </#compress>
-<#include 
"/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" />
+<#include 
"/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" />
diff --git a/core/src/main/resources/template/html5/theme.properties 
b/core/src/main/resources/template/html5/theme.properties
new file mode 100644
index 000000000..6625ab56f
--- /dev/null
+++ b/core/src/main/resources/template/html5/theme.properties
@@ -0,0 +1,3 @@
+# HTML5 Theme Configuration
+# Inherits from the simple theme for base functionality
+parent = simple
diff --git a/core/src/main/resources/template/html5/token.ftl 
b/core/src/main/resources/template/html5/token.ftl
index d063bbbb5..1ecc66ec3 100644
--- a/core/src/main/resources/template/html5/token.ftl
+++ b/core/src/main/resources/template/html5/token.ftl
@@ -18,5 +18,5 @@
  * under the License.
  */
 -->
-<input type="hidden" name="${parameters.tokenNameField!""}" 
value="${parameters.name!""}" />
-<input type="hidden" name="${parameters.name!""}" 
value="${parameters.token!""}" />
\ No newline at end of file
+<input type="hidden" name="${attributes.tokenNameField!""}" 
value="${attributes.name!""}" />
+<input type="hidden" name="${attributes.name!""}" 
value="${attributes.token!""}" />
\ No newline at end of file
diff --git 
a/core/src/test/java/org/apache/struts2/views/jsp/ui/CheckboxListTest.java 
b/core/src/test/java/org/apache/struts2/views/jsp/ui/CheckboxListTest.java
index 0ea404aa1..050dbdbb1 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/CheckboxListTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/CheckboxListTest.java
@@ -59,6 +59,19 @@ public class CheckboxListTest extends AbstractUITagTest {
         verifyGenericProperties(tag, "xhtml", new 
String[]{"tabindex","cssClass","cssStyle","id"});
     }
 
+    public void testGenericHtml5() throws Exception {
+        CheckboxListTag tag = new CheckboxListTag();
+        prepareTagGeneric(tag);
+        verifyGenericProperties(tag, "html5", new 
String[]{"tabindex","cssClass","cssStyle","id"});
+    }
+
+    public void testGenericHtml5_clearTagStateSet() throws Exception {
+        CheckboxListTag tag = new CheckboxListTag();
+        tag.setPerformClearTagStateForTagPoolingServers(true);
+        prepareTagGeneric(tag);
+        verifyGenericProperties(tag, "html5", new 
String[]{"tabindex","cssClass","cssStyle","id"});
+    }
+
     private void prepareTagGeneric(CheckboxListTag tag) {
         TestAction testAction = (TestAction) action;
         Collection<String> collection = new ArrayList<String>(2);
diff --git 
a/core/src/test/java/org/apache/struts2/views/jsp/ui/CheckboxTest.java 
b/core/src/test/java/org/apache/struts2/views/jsp/ui/CheckboxTest.java
index e9375ae42..e7da044ca 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/CheckboxTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/CheckboxTest.java
@@ -53,6 +53,17 @@ public class CheckboxTest extends AbstractUITagTest {
         verifyGenericProperties(tag, "xhtml", null);
     }
 
+    public void testGenericHtml5() throws Exception {
+        CheckboxTag tag = new CheckboxTag();
+        verifyGenericProperties(tag, "html5", null);
+    }
+
+    public void testGenericHtml5_clearTagStateSet() throws Exception {
+        CheckboxTag tag = new CheckboxTag();
+        tag.setPerformClearTagStateForTagPoolingServers(true);
+        verifyGenericProperties(tag, "html5", null);
+    }
+
     public void testChecked() throws Exception {
         TestAction testAction = (TestAction) action;
         testAction.setFoo("true");
diff --git 
a/core/src/test/java/org/apache/struts2/views/jsp/ui/ComboBoxTest.java 
b/core/src/test/java/org/apache/struts2/views/jsp/ui/ComboBoxTest.java
index e21ca40bc..03721c9d0 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/ComboBoxTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/ComboBoxTest.java
@@ -44,6 +44,19 @@ public class ComboBoxTest extends AbstractUITagTest {
         verifyGenericProperties(tag, "xhtml", null);
     }
 
+    public void testGenericHtml5() throws Exception {
+        ComboBoxTag tag = new ComboBoxTag();
+        prepareTagGeneric(tag);
+        verifyGenericProperties(tag, "html5", null);
+    }
+
+    public void testGenericHtml5_clearTagStateSet() throws Exception {
+        ComboBoxTag tag = new ComboBoxTag();
+        tag.setPerformClearTagStateForTagPoolingServers(true);
+        prepareTagGeneric(tag);
+        verifyGenericProperties(tag, "html5", null);
+    }
+
     private void prepareTagGeneric(ComboBoxTag tag) {
         TestAction testAction = (TestAction) action;
         ArrayList collection = new ArrayList();
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/FileTest.java 
b/core/src/test/java/org/apache/struts2/views/jsp/ui/FileTest.java
index d5a296c7f..301d6c5c5 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/FileTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/FileTest.java
@@ -113,4 +113,15 @@ public class FileTest extends AbstractUITagTest {
         FileTag tag = new FileTag();
         verifyGenericProperties(tag, "xhtml", null);
     }
+
+    public void testGenericHtml5() throws Exception {
+        FileTag tag = new FileTag();
+        verifyGenericProperties(tag, "html5", null);
+    }
+
+    public void testGenericHtml5_clearTagStateSet() throws Exception {
+        FileTag tag = new FileTag();
+        tag.setPerformClearTagStateForTagPoolingServers(true);
+        verifyGenericProperties(tag, "html5", null);
+    }
 }
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/HiddenTest.java 
b/core/src/test/java/org/apache/struts2/views/jsp/ui/HiddenTest.java
index 7b8044c31..5363ed145 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/HiddenTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/HiddenTest.java
@@ -280,4 +280,15 @@ public class HiddenTest extends AbstractUITagTest {
         HiddenTag tag = new HiddenTag();
         verifyGenericProperties(tag, "xhtml", null);
     }
+
+    public void testGenericHtml5() throws Exception {
+        HiddenTag tag = new HiddenTag();
+        verifyGenericProperties(tag, "html5", null);
+    }
+
+    public void testGenericHtml5_clearTagStateSet() throws Exception {
+        HiddenTag tag = new HiddenTag();
+        tag.setPerformClearTagStateForTagPoolingServers(true);
+        verifyGenericProperties(tag, "html5", null);
+    }
 }
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/LabelTest.java 
b/core/src/test/java/org/apache/struts2/views/jsp/ui/LabelTest.java
index 7bc5a7c54..0409695bc 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/LabelTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/LabelTest.java
@@ -209,6 +209,17 @@ public class LabelTest extends AbstractUITagTest {
         verifyGenericProperties(tag, "xhtml", null);
     }
 
+    public void testGenericHtml5() throws Exception {
+        LabelTag tag = new LabelTag();
+        verifyGenericProperties(tag, "html5", null);
+    }
+
+    public void testGenericHtml5_clearTagStateSet() throws Exception {
+        LabelTag tag = new LabelTag();
+        tag.setPerformClearTagStateForTagPoolingServers(true);
+        verifyGenericProperties(tag, "html5", null);
+    }
+
     public void testWithKeyNoValueFromStack() throws Exception {
         TestAction testAction = (TestAction) action;
         final String key = "labelKey";
diff --git 
a/core/src/test/java/org/apache/struts2/views/jsp/ui/PasswordTest.java 
b/core/src/test/java/org/apache/struts2/views/jsp/ui/PasswordTest.java
index a4d5cf236..098bb8ca8 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/PasswordTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/PasswordTest.java
@@ -84,4 +84,15 @@ public class PasswordTest extends AbstractUITagTest {
         PasswordTag tag = new PasswordTag();
         verifyGenericProperties(tag, "xhtml", new String[]{"value"});
     }
+
+    public void testGenericHtml5() throws Exception {
+        PasswordTag tag = new PasswordTag();
+        verifyGenericProperties(tag, "html5", new String[]{"value"});
+    }
+
+    public void testGenericHtml5_clearTagStateSet() throws Exception {
+        PasswordTag tag = new PasswordTag();
+        tag.setPerformClearTagStateForTagPoolingServers(true);
+        verifyGenericProperties(tag, "html5", new String[]{"value"});
+    }
 }
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/RadioTest.java 
b/core/src/test/java/org/apache/struts2/views/jsp/ui/RadioTest.java
index 47a5caad7..bc86a9909 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/RadioTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/RadioTest.java
@@ -553,6 +553,19 @@ public class RadioTest extends AbstractUITagTest {
         verifyGenericProperties(tag, "xhtml", new String[]{"id", "value"});
     }
 
+    public void testGenericHtml5() throws Exception {
+        RadioTag tag = new RadioTag();
+        prepareTagGeneric(tag);
+        verifyGenericProperties(tag, "html5", new String[]{"id", "value"});
+    }
+
+    public void testGenericHtml5_clearTagStateSet() throws Exception {
+        RadioTag tag = new RadioTag();
+        tag.setPerformClearTagStateForTagPoolingServers(true);
+        prepareTagGeneric(tag);
+        verifyGenericProperties(tag, "html5", new String[]{"id", "value"});
+    }
+
     public void testDynamicAttributes() throws Exception {
         TestAction testAction = (TestAction) action;
         testAction.setFoo("bar");
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/ResetTest.java 
b/core/src/test/java/org/apache/struts2/views/jsp/ui/ResetTest.java
index c297b86cc..c5b2091e7 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/ResetTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/ResetTest.java
@@ -584,4 +584,15 @@ public class ResetTest extends AbstractUITagTest {
         ResetTag tag = new ResetTag();
         verifyGenericProperties(tag, "xhtml", null);
     }
+
+    public void testGenericHtml5() throws Exception {
+        ResetTag tag = new ResetTag();
+        verifyGenericProperties(tag, "html5", null);
+    }
+
+    public void testGenericHtml5_clearTagStateSet() throws Exception {
+        ResetTag tag = new ResetTag();
+        tag.setPerformClearTagStateForTagPoolingServers(true);
+        verifyGenericProperties(tag, "html5", null);
+    }
 }
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/SelectTest.java 
b/core/src/test/java/org/apache/struts2/views/jsp/ui/SelectTest.java
index a087da478..91575a3cf 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/SelectTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/SelectTest.java
@@ -1059,6 +1059,19 @@ public class SelectTest extends AbstractUITagTest {
         verifyGenericProperties(tag, "xhtml", new String[]{"value"});
     }
 
+    public void testGenericHtml5() throws Exception {
+        SelectTag tag = new SelectTag();
+        prepareTagGeneric(tag);
+        verifyGenericProperties(tag, "html5", new String[]{"value"});
+    }
+
+    public void testGenericHtml5_clearTagStateSet() throws Exception {
+        SelectTag tag = new SelectTag();
+        tag.setPerformClearTagStateForTagPoolingServers(true);
+        prepareTagGeneric(tag);
+        verifyGenericProperties(tag, "html5", new String[]{"value"});
+    }
+
     public void testMultipleOn() throws Exception {
         SelectTag tag = new SelectTag();
         tag.setPageContext(pageContext);
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/SubmitTest.java 
b/core/src/test/java/org/apache/struts2/views/jsp/ui/SubmitTest.java
index e5c8299d2..1f44ffc6b 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/SubmitTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/SubmitTest.java
@@ -649,6 +649,17 @@ public class SubmitTest extends AbstractUITagTest {
         verifyGenericProperties(tag, "xhtml", null);
     }
 
+    public void testGenericHtml5() throws Exception {
+        SubmitTag tag = new SubmitTag();
+        verifyGenericProperties(tag, "html5", null);
+    }
+
+    public void testGenericHtml5_clearTagStateSet() throws Exception {
+        SubmitTag tag = new SubmitTag();
+        tag.setPerformClearTagStateForTagPoolingServers(true);
+        verifyGenericProperties(tag, "html5", null);
+    }
+
     /**
      * Test that by default submit tag body is HTML-escaped.
      */
diff --git 
a/core/src/test/java/org/apache/struts2/views/jsp/ui/TextareaTest.java 
b/core/src/test/java/org/apache/struts2/views/jsp/ui/TextareaTest.java
index c83086255..5fa79095f 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/TextareaTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/TextareaTest.java
@@ -152,4 +152,15 @@ public class TextareaTest extends AbstractUITagTest {
         TextareaTag tag = new TextareaTag();
         verifyGenericProperties(tag, "xhtml", new String[] {"value"});
     }
+
+    public void testGenericHtml5() throws Exception {
+        TextareaTag tag = new TextareaTag();
+        verifyGenericProperties(tag, "html5", new String[] {"value"});
+    }
+
+    public void testGenericHtml5_clearTagStateSet() throws Exception {
+        TextareaTag tag = new TextareaTag();
+        tag.setPerformClearTagStateForTagPoolingServers(true);
+        verifyGenericProperties(tag, "html5", new String[] {"value"});
+    }
 }
diff --git 
a/core/src/test/java/org/apache/struts2/views/jsp/ui/TextfieldTest.java 
b/core/src/test/java/org/apache/struts2/views/jsp/ui/TextfieldTest.java
index ad28ee4bd..fc2dfa8a7 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/TextfieldTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/TextfieldTest.java
@@ -59,6 +59,17 @@ public class TextfieldTest extends AbstractUITagTest {
         verifyGenericProperties(tag, "xhtml", null);
     }
 
+    public void testGenericHtml5() throws Exception {
+        TextFieldTag tag = new TextFieldTag();
+        verifyGenericProperties(tag, "html5", null);
+    }
+
+    public void testGenericHtml5_clearTagStateSet() throws Exception {
+        TextFieldTag tag = new TextFieldTag();
+        tag.setPerformClearTagStateForTagPoolingServers(true);
+        verifyGenericProperties(tag, "html5", null);
+    }
+
     public void testErrors() throws Exception {
         TestAction testAction = (TestAction) action;
         testAction.setFoo("bar");
diff --git a/thoughts/shared/research/2025-11-23-WW-5444-html5-theme-testing.md 
b/thoughts/shared/research/2025-11-23-WW-5444-html5-theme-testing.md
new file mode 100644
index 000000000..9c2408d4e
--- /dev/null
+++ b/thoughts/shared/research/2025-11-23-WW-5444-html5-theme-testing.md
@@ -0,0 +1,518 @@
+---
+date: 2025-11-23T17:48:30+01:00
+topic: "HTML5 Theme Implementation and Testing Approach"
+tags: [research, html5-theme, testing, ui-tags, themes, WW-5444]
+status: complete
+git_commit: 461c06d0c5afde260fdb0f32038a32f2af5bb42e
+git_branch: feature/WW-5444-html5
+---
+
+# Research: HTML5 Theme Implementation and Testing Approach
+
+**Date**: 2025-11-23T17:48:30+01:00
+**Branch**: feature/WW-5444-html5
+**Jira**: [WW-5444](https://issues.apache.org/jira/browse/WW-5444)
+
+## Research Question
+
+What testing is needed for the HTML5 theme implementation in Apache Struts, 
and how should it be approached based on existing theme testing patterns?
+
+## Summary
+
+The HTML5 theme has been implemented with 45 FreeMarker templates in the 
showcase application for easier manual testing. Analysis of existing theme 
testing patterns reveals a well-established infrastructure in 
`AbstractUITagTest` that should be replicated for html5 theme validation. The 
theme currently lives in `apps/showcase` but should eventually move to `core/` 
for production distribution.
+
+**Key Findings:**
+- HTML5 theme has 45 templates but NO unit tests yet
+- Existing themes (simple, xhtml, css_xhtml) use `verifyGenericProperties()` 
for testing
+- Test pattern is straightforward: add `testGenericHtml5()` to each UI tag 
test class
+- Production themes belong in `core/src/main/resources/template/`, not 
`apps/showcase`
+- No historical documentation exists in thoughts/ directory about theme testing
+
+## Detailed Findings
+
+### Current HTML5 Theme Implementation
+
+**Location**: `apps/showcase/src/main/resources/template/html5/`
+
+**45 FreeMarker Templates:**
+- Form elements: `form.ftl`, `form-close.ftl`, `text.ftl`, `textarea.ftl`, 
`password.ftl`, `checkbox.ftl`, `checkboxlist.ftl`, `radiomap.ftl`, 
`select.ftl`, `file.ftl`, `hidden.ftl`, `token.ftl`
+- Complex components: `combobox.ftl`, `doubleselect.ftl`, 
`inputtransferselect.ftl`, `optiontransferselect.ftl`, `updownselect.ftl`, 
`optgroup.ftl`
+- Display components: `actionerror.ftl`, `actionmessage.ftl`, 
`fielderror.ftl`, `label.ftl`
+- Navigation: `a.ftl`, `a-close.ftl`, `link.ftl`, `submit.ftl`, 
`submit-close.ftl`, `reset.ftl`
+- Utilities: `head.ftl`, `script.ftl`, `script-close.ftl`, `debug.ftl`, 
`css.ftl`, `nonce.ftl`, `datetextfield.ftl`
+- Structural: `controlheader.ftl`, `controlfooter.ftl`, `empty.ftl`
+- Attribute handling: `common-attributes.ftl`, `dynamic-attributes.ftl`, 
`prefixed-dynamic-attributes.ftl`, `scripting-events.ftl`
+
+**Supporting Files:**
+- Java: 
`apps/showcase/src/main/java/org/apache/struts2/showcase/action/Html5Action.java`
+- JSP: `apps/showcase/src/main/webapp/WEB-INF/html5/index.jsp`
+- Config: `apps/showcase/src/main/resources/struts.xml` (lines 163-167)
+
+**Key Features:**
+- Clean HTML5 markup without table-based layouts
+- Empty control headers/footers (no wrapping divs)
+- Support for HTML5 input types via TextField component
+- Bootstrap CSS integration
+- CSP nonce attribute support
+
+### Theme Testing Infrastructure
+
+**Core Testing Class**: 
`core/src/test/java/org/apache/struts2/views/jsp/AbstractUITagTest.java`
+
+This class provides the foundation for all UI tag testing with two primary 
testing approaches:
+
+#### 1. Generic Property Testing
+
+**Method**: `verifyGenericProperties(AbstractUITag tag, String theme, String[] 
exclude)`
+
+Tests that all standard HTML attributes render correctly for a given theme:
+- Sets 15+ generic properties (id, name, cssClass, cssStyle, title, disabled, 
tabindex, value, onclick, ondblclick, etc.)
+- Adds dynamic attribute test (`data-id="id-random"`)
+- Renders tag with specified theme
+- Verifies each property appears correctly in HTML output
+- Checks for FreeMarker template errors
+
+**Property Test Coverage** (Lines 110-133):
+```java
+protected Map<String, PropertyHolder> initializedGenericTagTestProperties() {
+    Map<String, PropertyHolder> result = new HashMap<>();
+    new PropertyHolder("name", "someName").addToMap(result);
+    new PropertyHolder("id", "someId").addToMap(result);
+    new PropertyHolder("cssClass", "cssClass1", 
"class=\"cssClass1\"").addToMap(result);
+    new PropertyHolder("cssStyle", "cssStyle1", 
"style=\"cssStyle1\"").addToMap(result);
+    new PropertyHolder("title", "someTitle").addToMap(result);
+    new PropertyHolder("disabled", "true", 
"disabled=\"disabled\"").addToMap(result);
+    new PropertyHolder("tabindex", "99").addToMap(result);
+    new PropertyHolder("value", "someValue").addToMap(result);
+    // ... event handlers (onclick, ondblclick, onmousedown, etc.)
+    return result;
+}
+```
+
+#### 2. Specific Output Testing
+
+**Method**: `verify(URL url)`
+
+Compares rendered output against expected `.txt` files:
+- Loads expected HTML from resource file
+- Normalizes both actual and expected output (removes whitespace differences)
+- Performs exact string comparison
+- Provides detailed error messages on mismatch
+
+**Expected File Location**: 
`core/src/test/resources/org/apache/struts2/views/jsp/ui/{TagName}-{N}.txt`
+
+### Existing Theme Test Patterns
+
+**Simple Theme Tests** (from TextfieldTest.java):
+```java
+public void testGenericSimple() throws Exception {
+    TextFieldTag tag = new TextFieldTag();
+    verifyGenericProperties(tag, "simple", null);
+}
+```
+
+**XHTML Theme Tests**:
+```java
+public void testGenericXhtml() throws Exception {
+    TextFieldTag tag = new TextFieldTag();
+    verifyGenericProperties(tag, "xhtml", null);
+}
+```
+
+**With Property Exclusions** (RadioTest.java):
+```java
+public void testGenericSimple() throws Exception {
+    RadioTag tag = new RadioTag();
+    verifyGenericProperties(tag, "simple", new String[]{"id", "value"});
+    // Excludes "id" and "value" as radio buttons handle them differently
+}
+```
+
+### Theme Template Location Patterns
+
+**Production Themes** → `core/src/main/resources/template/`
+- **simple**: 47 templates (base theme, no parent)
+- **xhtml**: 30 templates (parent = simple)
+- **css_xhtml**: 13 templates (parent = xhtml)
+
+**Demo/Showcase Themes** → `apps/showcase/src/main/resources/template/`
+- **html5**: 45 templates (currently here)
+- **ajaxErrorContainers**: 4 templates
+
+**Theme Inheritance Pattern**:
+```
+simple (root)
+  ↓
+xhtml (parent = simple)
+  ↓
+css_xhtml (parent = xhtml)
+```
+
+Defined via `theme.properties` with `parent = <parent-theme>` property.
+
+**Key Pattern Rule**: Production-ready themes MUST be in `core/` to be 
packaged with struts2-core.jar and available to all applications.
+
+### Integration Testing Pattern
+
+**Example from Showcase**: 
`apps/showcase/src/test/java/it/org/apache/struts2/showcase/UITagExampleTest.java`
+
+Uses HtmlUnit to test complete form workflows:
+```java
+@Test
+public void testInputForm() throws Exception {
+    try (final WebClient webClient = new WebClient()) {
+        final HtmlPage page = webClient.getPage(
+            ParameterUtils.getBaseUrl() + "/tags/ui/example!input.action"
+        );
+
+        final HtmlForm form = page.getFormByName("exampleSubmit");
+        final HtmlTextInput textField = form.getInputByName("name");
+        // ... interact with form elements
+
+        final HtmlSubmitInput button = form.getInputByValue("Submit");
+        final HtmlPage page2 = button.click();
+
+        // Verify results
+        Assert.assertEquals("name", 
page2.getElementById("name").asNormalizedText());
+    }
+}
+```
+
+## Code References
+
+### HTML5 Theme Implementation
+- `apps/showcase/src/main/resources/template/html5/*.ftl` - All 45 FreeMarker 
templates
+- 
`apps/showcase/src/main/java/org/apache/struts2/showcase/action/Html5Action.java`
 - Demo action
+- `apps/showcase/src/main/webapp/WEB-INF/html5/index.jsp` - Demo page
+- `apps/showcase/src/main/resources/struts.xml:163-167` - Configuration
+
+### Testing Infrastructure
+- 
`core/src/test/java/org/apache/struts2/views/jsp/AbstractUITagTest.java:155-174`
 - verifyGenericProperties() method
+- 
`core/src/test/java/org/apache/struts2/views/jsp/AbstractUITagTest.java:110-133`
 - Generic property definitions
+- 
`core/src/test/java/org/apache/struts2/views/jsp/AbstractUITagTest.java:201-230`
 - verify() method for output comparison
+- `core/src/test/resources/org/apache/struts2/views/jsp/ui/` - Expected output 
files
+
+### Example Test Classes
+- `core/src/test/java/org/apache/struts2/views/jsp/ui/TextfieldTest.java` - 
Comprehensive test examples
+- `core/src/test/java/org/apache/struts2/views/jsp/ui/PasswordTest.java:78-86` 
- Theme test pattern
+- `core/src/test/java/org/apache/struts2/views/jsp/ui/CheckboxTest.java` - 
Checkbox-specific tests
+- `core/src/test/java/org/apache/struts2/views/jsp/ui/SelectTest.java` - 
Select/dropdown tests
+
+### Integration Tests
+- 
`apps/showcase/src/test/java/it/org/apache/struts2/showcase/UITagExampleTest.java`
 - HtmlUnit integration test example
+
+## Testing Recommendations
+
+### Priority 1: Unit Tests for Generic Properties (High Priority)
+
+Add `testGenericHtml5()` method to each UI tag test class:
+
+**Files to modify** (in `core/src/test/java/org/apache/struts2/views/jsp/ui/`):
+1. `TextfieldTest.java`
+2. `TextareaTest.java`
+3. `PasswordTest.java`
+4. `CheckboxTest.java`
+5. `CheckboxListTest.java`
+6. `RadioTest.java`
+7. `SelectTest.java`
+8. `FileTest.java`
+9. `HiddenTest.java`
+10. `LabelTest.java`
+11. `FormTagTest.java`
+12. `SubmitTest.java`
+13. `ResetTest.java`
+14. `AnchorTagTest.java`
+15. `ActionErrorTagTest.java`
+16. `ActionMessageTagTest.java`
+17. `FieldErrorTagTest.java`
+18. `DoubleSelectTest.java`
+19. `ComboBoxTest.java`
+20. `UpDownSelectTagTest.java`
+21. `InputTransferSelectTagTest.java`
+22. `OptionTransferSelectTagTest.java`
+
+**Pattern to add**:
+```java
+public void testGenericHtml5() throws Exception {
+    <TagName>Tag tag = new <TagName>Tag();
+    verifyGenericProperties(tag, "html5", null);
+}
+
+public void testGenericHtml5_clearTagStateSet() throws Exception {
+    <TagName>Tag tag = new <TagName>Tag();
+    tag.setPerformClearTagStateForTagPoolingServers(true);
+    verifyGenericProperties(tag, "html5", null);
+}
+```
+
+**Exclusions to consider**:
+- Radio/Checkbox: Exclude "id" and "value" (handled differently)
+- Hidden: Exclude properties that don't apply to hidden fields
+- Token: Minimal properties apply
+
+**Effort**: ~2-3 lines per test class × 22 classes = **minimal implementation 
effort**
+
+**Benefit**: Ensures all 45 templates render correctly with standard HTML 
attributes
+
+### Priority 2: Integration Tests in Showcase (Medium Priority)
+
+**Create**: 
`apps/showcase/src/test/java/it/org/apache/struts2/showcase/Html5TagExampleTest.java`
+
+**Test Coverage**:
+1. Basic form rendering with html5 theme
+2. All input types render correctly
+3. Error messages display properly (actionerror, actionmessage, fielderror)
+4. Dynamic attributes work (data-*, aria-*)
+5. Submit workflow completes successfully
+6. No table-based layout in output
+
+**Example structure**:
+```java
+public class Html5TagExampleTest {
+    @Test
+    public void testHtml5FormRendering() throws Exception {
+        try (final WebClient webClient = new WebClient()) {
+            final HtmlPage page = webClient.getPage(
+                ParameterUtils.getBaseUrl() + "/html5/index.action"
+            );
+
+            // Verify clean HTML5 markup
+            assertFalse(page.asXml().contains("<table"));
+
+            // Test error display
+            assertNotNull(page.getElementById("actionErrors"));
+
+            // Verify Bootstrap compatibility
+            assertTrue(page.asXml().contains("class=\""));
+        }
+    }
+}
+```
+
+**Effort**: 1-2 integration test methods
+
+**Benefit**: End-to-end validation that theme works in real application context
+
+### Priority 3: Expand Showcase Demo (Medium Priority)
+
+**Enhance**: `apps/showcase/src/main/webapp/WEB-INF/html5/index.jsp`
+
+**Add demonstrations for**:
+1. All form input types (text, password, checkbox, radio, select, textarea, 
file, hidden)
+2. Complex components (doubleselect, combobox, updownselect, etc.)
+3. Error handling scenarios (fielderror, actionerror, actionmessage)
+4. Dynamic attributes (data-*, aria-*)
+5. HTML5 input types (email, url, number, date, etc.)
+6. Submit/reset buttons
+7. Links and anchors
+8. Label positioning
+
+**Benefit**: Visual validation during development, documentation for users
+
+### Priority 4: Template Relocation Decision (High Priority)
+
+**Question**: Should html5 theme move from `apps/showcase` to `core`?
+
+**Options**:
+
+**Option A: Move to core/** (Recommended for production theme)
+- **Location**: `core/src/main/resources/template/html5/`
+- **Pros**:
+  - Available to all Struts applications
+  - Shipped with struts2-core.jar
+  - Consistent with simple/xhtml/css_xhtml
+- **Cons**:
+  - Commits to maintaining it
+  - Increases core artifact size slightly
+
+**Option B: Keep in showcase** (Recommended for experimental theme)
+- **Location**: `apps/showcase/src/main/resources/template/html5/`
+- **Pros**:
+  - Easy to experiment and iterate
+  - Doesn't affect core framework
+  - Users can copy templates to their apps
+- **Cons**:
+  - Not available in struts2-core.jar
+  - Users must manually add templates
+  - Inconsistent with other themes
+
+**Recommendation**: If html5 theme is meant to be production-ready and 
recommended for new applications, move to `core/`. If it's experimental or an 
example, keep in showcase.
+
+**Implementation**: If moving to core, also move:
+- Templates: `apps/showcase/src/main/resources/template/html5/` → 
`core/src/main/resources/template/html5/`
+- Tests: Create unit tests in 
`core/src/test/java/org/apache/struts2/views/jsp/ui/*Test.java`
+- Update showcase to use core's html5 theme instead of local copy
+
+### Priority 5: HTML5 Validation Testing (Low-Medium Priority)
+
+**Tools to use**:
+- W3C HTML5 Validator (via API or library)
+- HtmlUnit for semantic validation
+- Accessibility checkers (ARIA attributes, semantic tags)
+
+**What to validate**:
+1. Valid HTML5 markup (no deprecated elements)
+2. Proper attribute handling (data-*, aria-*, etc.)
+3. Semantic HTML structure where appropriate
+4. No table-based layouts (unless data tables)
+5. Accessibility compliance (labels, ARIA)
+
+**Example test**:
+```java
+@Test
+public void testHtml5ValidMarkup() throws Exception {
+    // Render form with html5 theme
+    FormTag form = new FormTag();
+    form.setTheme("html5");
+    // ... configure
+    form.doStartTag();
+    form.doEndTag();
+
+    String output = writer.toString();
+
+    // Verify no table layout
+    assertFalse(output.contains("<table"));
+    assertFalse(output.contains("class=\"tdLabel\""));
+
+    // Verify HTML5 elements
+    assertTrue(output.matches(".*<form[^>]*>.*"));
+
+    // No deprecated attributes
+    assertFalse(output.contains("align="));
+    assertFalse(output.contains("border="));
+}
+```
+
+### Priority 6: Documentation (Medium Priority)
+
+**Create**:
+1. **User Guide**: How to use html5 theme in applications
+   - Migration guide from xhtml/simple to html5
+   - Bootstrap CSS integration examples
+   - HTML5 input type usage
+
+2. **Developer Guide**: How html5 theme works
+   - Template structure
+   - Differences from xhtml theme
+   - Customization points
+
+3. **Testing Guide**: How to test themes
+   - Unit test pattern
+   - Integration test approach
+   - Expected output file creation
+
+**Location**: Consider adding to `core/src/site/` for Maven site generation
+
+## Architecture Insights
+
+### Theme Testing Architecture
+
+The Struts theme testing architecture demonstrates several key design patterns:
+
+1. **Template Method Pattern**: `AbstractUITagTest` provides template methods 
like `verifyGenericProperties()` that subclasses call with theme-specific 
parameters
+
+2. **Resource-Based Verification**: Expected output files as resources allow 
platform-independent testing without hardcoded strings in test code
+
+3. **Normalization Strategy**: Whitespace normalization makes tests resilient 
to formatting differences while still catching real HTML changes
+
+4. **Property Holder Pattern**: Encapsulates test data (name, value, 
expectation) for declarative property testing
+
+5. **Theme Inheritance**: FreeMarker template resolution with fallback 
supports theme hierarchies (css_xhtml → xhtml → simple)
+
+### HTML5 Theme Design Decisions
+
+1. **No Control Wrappers**: Empty `controlheader.ftl` and `controlfooter.ftl` 
provide clean HTML without extra divs, unlike xhtml theme's table-based layout
+
+2. **Bootstrap Compatibility**: Clean markup designed to work with Bootstrap 
CSS classes without framework-specific markup
+
+3. **HTML5 Input Types**: TextField component supports HTML5 input types 
(email, url, number, etc.) via type attribute
+
+4. **CSP Support**: Includes `nonce.ftl` for Content Security Policy nonce 
attribute handling
+
+5. **Dynamic Attributes**: Full support for data-* and aria-* attributes via 
`dynamic-attributes.ftl`
+
+### Testing Infrastructure Design
+
+1. **Separation of Concerns**: Generic property tests separate from specific 
behavior tests
+
+2. **Reusable Test Infrastructure**: `AbstractUITagTest` provides shared 
testing utilities for all UI tag tests
+
+3. **Theme Parameterization**: Same test can validate multiple themes by 
changing theme parameter
+
+4. **Exclusion Mechanism**: Allows tags to exclude properties that don't apply 
(e.g., hidden field doesn't render visible properties)
+
+5. **Integration with Maven**: Test resources use classpath resolution for 
portability
+
+## Open Questions
+
+1. **Production Readiness**: Is html5 theme ready for production (core/) or 
still experimental (showcase/)?
+
+2. **Theme Properties**: Should html5 theme have a `theme.properties` file 
defining inheritance (e.g., `parent = simple`)?
+
+3. **Default Theme**: Should html5 become the default theme for new Struts 
applications (replacing xhtml)?
+
+4. **Backward Compatibility**: How to handle applications migrating from xhtml 
to html5 theme?
+
+5. **CSS Framework Integration**: Should html5 theme include optional CSS for 
Bootstrap, Tailwind, or remain unopinionated?
+
+6. **Component Coverage**: Are all 45 templates necessary, or should some 
inherit from simple theme?
+
+7. **Form Layout**: Should html5 theme include layout options (inline, 
horizontal, vertical) like Bootstrap forms?
+
+8. **Error Positioning**: How should field errors be positioned in html5 theme 
compared to xhtml theme's table-based approach?
+
+## Next Steps
+
+### Immediate Actions (Before PR)
+
+1. **Add unit tests**: Implement `testGenericHtml5()` in all UI tag test 
classes
+2. **Run test suite**: Execute `mvn test -DskipAssembly` to verify all tests 
pass
+3. **Decide location**: Determine if html5 theme should move to core/ or stay 
in showcase/
+4. **Create integration test**: Add `Html5TagExampleTest.java` for end-to-end 
validation
+
+### Short-term Actions (Before Release)
+
+1. **Expand showcase demo**: Add comprehensive examples to index.jsp
+2. **HTML5 validation**: Verify all templates produce valid HTML5 markup
+3. **Documentation**: Create user guide for html5 theme usage
+4. **Performance testing**: Compare rendering performance vs xhtml theme
+
+### Long-term Considerations
+
+1. **Default theme discussion**: Consider making html5 the recommended/default 
theme
+2. **Migration tooling**: Create utilities to help migrate from xhtml to html5
+3. **CSS framework integration**: Evaluate official integrations with popular 
CSS frameworks
+4. **Accessibility audit**: Ensure WCAG compliance for all components
+
+## Testing Effort Estimation
+
+**Unit Tests**:
+- 22 test classes × 2 methods each = 44 test methods
+- ~2-3 lines per method
+- **Effort**: 2-3 hours
+
+**Integration Tests**:
+- 1 test class with 3-5 test methods
+- **Effort**: 2-4 hours
+
+**Expected Output Files** (if needed for specific tests):
+- 0 files needed for generic property tests
+- Optional for specific behavior tests
+- **Effort**: Variable, only if specific behavior needs validation
+
+**Total Estimated Effort**: **4-7 hours** for comprehensive test coverage
+
+## References
+
+### Jira Issues
+- [WW-5444](https://issues.apache.org/jira/browse/WW-5444) - Implement a new 
html5 theme
+
+### Related Code
+- Theme rendering: 
`core/src/main/java/org/apache/struts2/components/UIBean.java`
+- Template engine: `core/src/main/java/org/apache/struts2/components/template/`
+- FreeMarker templates: `core/src/main/resources/template/`
+
+### Maven Commands
+- Run tests: `mvn test -DskipAssembly`
+- Build showcase: `mvn clean install` (from apps/showcase/)
+- Full build: `mvn clean install` (from project root)

Reply via email to