diff --git a/includes/webform.theme.inc b/includes/webform.theme.inc
index 6db50c67..ceedee98 100644
--- a/includes/webform.theme.inc
+++ b/includes/webform.theme.inc
@@ -7,7 +7,10 @@
 
 use Drupal\file\Entity\File;
 use Drupal\Component\Utility\Html;
+use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Template\Attribute;
+use Drupal\webform\Utility\WebformAccessibilityHelper;
+use Drupal\webform\Utility\WebformElementHelper;
 
 /******************************************************************************/
 // Theme hooks.
@@ -93,7 +96,7 @@ function webform_theme() {
     ],
 
     'webform_element_help' => [
-      'variables' => ['help' => NULL],
+      'variables' => ['help' => NULL, 'attributes' => []],
     ],
 
     'webform_element_more' => [
@@ -248,6 +251,10 @@ function webform_preprocess_menu_local_action(&$variables) {
  * @see \Drupal\webform\Plugin\WebformElement\OptionsBase
  */
 function webform_preprocess_checkboxes(&$variables) {
+  if (!_webform_preprocess_is_webform_element($variables)) {
+    return;
+  }
+
   _webform_preprocess_options($variables);
 }
 
@@ -257,41 +264,33 @@ function webform_preprocess_checkboxes(&$variables) {
  * @see \Drupal\webform\Plugin\WebformElement\OptionsBase
  */
 function webform_preprocess_radios(&$variables) {
+  if (!_webform_preprocess_is_webform_element($variables)) {
+    return;
+  }
+
   _webform_preprocess_options($variables);
 }
 
-/**
- * Prepares variables for checkboxes and radios options templates.
- *
- * Below code must be called by template_preprocess_(radios|checkboxes) which
- * reset the element's 'attributes';
- */
-function _webform_preprocess_options(&$variables) {
-  $element = $variables['element'];
-
-  $variables['attributes']['class'][] = Html::getClass('js-webform-' . $element['#type']);
-
-  if (!empty($element['#options_display'])) {
-    $variables['attributes']['class'][] = Html::getClass('webform-options-display-' . $element['#options_display']);
-    $variables['#attached']['library'][] = 'webform/webform.element.options';
-  }
-}
+/******************************************************************************/
+// Preprocess containers.
+/******************************************************************************/
 
 /**
  * Prepares variables for details element templates.
  */
 function webform_preprocess_details(&$variables) {
-  // Move #description to #help for webform admin routes.
-  _webform_preprocess_description_help($variables);
+  if (!_webform_preprocess_is_webform_element($variables)) {
+    return;
+  }
 
-  // Add (read) more to #description.
-  _webform_preprocess_details_description_more($variables);
+  // Setup description, help, and more.
+  _webform_preprocess_element($variables);
 
   $element = &$variables['element'];
 
-  // Restructure the details's title to include #help.
-  if (isset($variables['title'])) {
-    _webform_preprocess_help_title($variables['title'], $element);
+  // Hide details title.
+  if (isset($element['#title_display']) && $element['#title_display'] === 'invisible') {
+    $variables['title'] = WebformAccessibilityHelper::buildVisuallyHidden($variables['title']);
   }
 
   // Remove invalid 'required' and 'aria-required' attributes from details.
@@ -307,11 +306,12 @@ function webform_preprocess_details(&$variables) {
  * Prepares variables for fieldset element templates.
  */
 function webform_preprocess_fieldset(&$variables) {
-  // Move #description to #help for webform admin routes.
-  _webform_preprocess_description_help($variables);
+  if (!_webform_preprocess_is_webform_element($variables)) {
+    return;
+  }
 
-  // Add (read) more to #description.
-  _webform_preprocess_form_element_description_more($variables);
+  // Setup description, help, and more.
+  _webform_preprocess_element($variables, ['legend', 'title']);
 
   $element = &$variables['element'];
 
@@ -321,11 +321,6 @@ function webform_preprocess_fieldset(&$variables) {
     $variables['attributes']['class'][] = 'webform-fieldset--title-inline';
   }
 
-  // Restructure the fieldset's legend title to include #help.
-  if (isset($variables['legend']['title'])) {
-    _webform_preprocess_help_title($variables['legend']['title'], $element);
-  }
-
   // Add .js-webform-form-composite class to be used #states API.
   // @see js/webform.states.js
   if (isset($element['#type']) && in_array($element['#type'], ['checkboxes', 'radios'])) {
@@ -342,62 +337,240 @@ function webform_preprocess_fieldset(&$variables) {
   }
 }
 
+/******************************************************************************/
+// Preprocess form element.
+/******************************************************************************/
+
 /**
- * Prepares variables for webform section element templates.
+ * Prepares variables for form element templates.
  */
-function webform_preprocess_webform_section(&$variables) {
-  // Move #description to #help for webform admin routes.
-  _webform_preprocess_description_help($variables);
+function webform_preprocess_form_element(&$variables) {
+  if (!_webform_preprocess_is_webform_element($variables)) {
+    return;
+  }
+
+  // Setup description, help, and more.
+  _webform_preprocess_element($variables);
 
   $element = &$variables['element'];
 
-  // Restructure the section's title to include #help.
-  if (isset($variables['title'])) {
-    _webform_preprocess_help_title($variables['title'], $element);
-  }
+  // Add #help and #_title_display to label.
+  // Note: #_title_display is used to track inline titles.
+  // @see \Drupal\webform\Plugin\WebformElementBase::prepare
+  $variables['label'] += array_intersect_key($element, array_flip(['#help', '#_title_display']));
 }
 
 /**
- * Prepares variables for datetime form wrapper templates.
+ * Prepares variables for form label templates.
  */
-function webform_preprocess_datetime_wrapper(&$variables) {
-  // Move #description to #help for webform admin routes.
-  _webform_preprocess_description_help($variables);
+function webform_preprocess_form_element_label(&$variables) {
+  $element = &$variables['element'];
 
-  // Add (read) more to #description.
-  _webform_preprocess_form_element_description_more($variables);
+  // Restructure the label's title to include #help.
+  _webform_preprocess_help_title($variables['title'], $element);
+}
+
+/******************************************************************************/
+// Preprocess file/image elements.
+/******************************************************************************/
+
+/**
+ * Prepares variables for file managed file templates.
+ *
+ * @see https://stackoverflow.com/questions/21842274/cross-browser-custom-styling-for-file-upload-button
+ * @see template_preprocess_file_managed_file()
+ */
+function webform_preprocess_file_managed_file(&$variables) {
+  if (!_webform_preprocess_is_webform_element($variables)) {
+    return;
+  }
 
   $element = &$variables['element'];
+  if (empty($element['#button'])) {
+    return;
+  }
+
+  // Don't alter hidden file upload input.
+  if (isset($element['upload']['#access']) && $element['upload']['#access'] === FALSE) {
+    return;
+  }
+
+  // Create an unique id for the file upload input and label.
+  $button_id = Html::getUniqueId($variables['element']['upload']['#id'] . '-button');
+
+  // Create a label that is styled like an action button.
+  $label = [
+    '#type' => 'html_tag',
+    '#tag' => 'label',
+    '#value' => (isset($element['#button__title'])) ? $element['#button__title'] : (empty($element['#multiple']) ? t('Choose file') : t('Choose files')),
+    '#attributes' => (isset($element['#button__attributes'])) ? $element['#button__attributes'] : [],
+  ];
+
+  // Add 'for' attribute.
+  $label['#attributes']['for'] = $button_id;
 
-  // Restructure the title to include #help.
-  if (isset($variables['title'])) {
-    _webform_preprocess_help_title($variables['title'], $element);
+  // Add default button classes.
+  if (empty($label['#attributes']['class'])) {
+    $label['#attributes']['class'][] = 'button';
+    $label['#attributes']['class'][] = 'button-action';
   }
+
+  // Add .webform-file-button.
+  $label['#attributes']['class'][] = 'webform-file-button';
+
+  // Make sure the label is first.
+  $element = ['label' => $label] + $element;
+
+  // Set the custom button ID for file upload input.
+  $element['upload']['#attributes']['id'] = $button_id;
+
+  // Hide the file upload.
+  $element['upload']['#attributes']['class'][] = 'webform-file-button-input';
+
+  // Attach library.
+  $element['#attached']['library'][] = 'webform/webform.element.file.button';
 }
 
 /**
- * Prepares variables for form label templates.
+ * Prepares variables for file link templates.
+ *
+ * @see webform_file_access
  */
-function webform_preprocess_form_element_label(&$variables) {
-  $element = &$variables['element'];
+function webform_preprocess_file_link(&$variables) {
+  /** @var \Drupal\file\FileInterface $file */
+  $file = $variables['file'];
+  $file = ($file instanceof File) ? $file : File::load($file->fid);
 
-  // Unset 'for' attribute.
-  // @see \Drupal\webform\Element\WebformOtherBase::processWebformOther
-  if (isset($element['#attributes']['for']) && $element['#attributes']['for'] === FALSE) {
-    unset($variables['attributes']['for']);
+  // Remove link to temporary anonymous private file uploads.
+  if ($file->isTemporary() && $file->getOwner()->isAnonymous() && strpos($file->getFileUri(), 'private://webform/') === 0) {
+    $variables['link'] = $file->getFilename();
   }
+}
 
-  // Restructure the label's title to include #help.
-  if (!empty($element['#help'])) {
-    _webform_preprocess_help_title($variables['title'], $element);
+/**
+ * Prepares variables for image.
+ *
+ * Make sure the image src for the 'webform_image_file' src is an absolute URL.
+ */
+function webform_preprocess_image(&$variables) {
+  global $base_url;
+  if (isset($variables['attributes']['class']) && in_array('webform-image-file', (array) $variables['attributes']['class'])) {
+    $variables['attributes']['src'] = $base_url . preg_replace('/^' . preg_quote(base_path(), '/') . '/', '/', $variables['attributes']['src']);
   }
 }
 
+/******************************************************************************/
+// Preprocess webform specific elements.
+/******************************************************************************/
+
 /**
- * Prepares variables for form element templates.
+ * Prepares variables for webform section element templates.
  */
-function webform_preprocess_form_element(&$variables) {
+function webform_preprocess_webform_section(&$variables) {
+  // Setup description, help, and more.
+  _webform_preprocess_element($variables);
+}
+
+/******************************************************************************/
+// Preprocess helpers.
+/******************************************************************************/
+
+/**
+ * Determine if a preprocessor should be executed for a webform element.
+ *
+ * @param array $variables
+ *   An array of variables.
+ *
+ * @return bool
+ *   TRUE if a preprocessor should be executed for a webform element.
+ */
+function _webform_preprocess_is_webform_element(array &$variables) {
   $element = &$variables['element'];
+  if (WebformElementHelper::isWebformElement($element)) {
+    return TRUE;
+  }
+  elseif (\Drupal::service('webform.request')->isWebformAdminRoute()) {
+    return TRUE;
+  }
+  else {
+    return FALSE;
+  }
+}
+
+/**
+ * Prepares variables for checkboxes and radios options templates.
+ *
+ * Below code must be called by template_preprocess_(radios|checkboxes) which
+ * reset the element's 'attributes';
+ */
+function _webform_preprocess_options(array &$variables) {
+  $element =& $variables['element'];
+
+  $variables['attributes']['class'][] = Html::getClass('js-webform-' . $element['#type']);
+
+  if (!empty($element['#options_display'])) {
+    $variables['attributes']['class'][] = Html::getClass('webform-options-display-' . $element['#options_display']);
+    $variables['#attached']['library'][] = 'webform/webform.element.options';
+  }
+}
+
+/**
+ * Prepares webform element description, help, and more templates.
+ *
+ * @see template_preprocess_form_element()
+ * @see core/modules/system/templates/form-element.html.twig
+ * @see template_preprocess_details()
+ * @see /core/modules/system/templates/details.html.twig
+ * @see template_preprocess_fieldset()
+ * @see /core/modules/system/templates/fieldset.html.twig
+ * @see template_preprocess_webform_section()
+ * @see /webform/templates/webform-section.html.twig
+ */
+function _webform_preprocess_element(array &$variables, $title_parents = ['title']) {
+  $element =& $variables['element'];
+  $type = $element['#type'];
+
+  // Fix details 'description' property which does not have description.content.
+  // @see template_preprocess_details
+  // @see Issue #2896169: Details elements have incorrect aria-describedby attributes
+  if (!empty($element['#description'])) {
+    $description = (is_array($element['#description'])) ? $element['#description'] : ['#markup' => $element['#description']];
+    if ($type === 'details') {
+      $description_attributes = [];
+      if (!empty($element['#id'])) {
+        $description_attributes['id'] = $element['#id'] . '--description';
+      }
+      $variables['description'] = [];
+      $variables['description']['content'] = [
+        '#type' => 'container',
+        '#attributes' => new Attribute($description_attributes),
+      ] + $description;
+    }
+    else {
+      // Wrap description in a container.
+      $variables['description']['content'] = [
+        '#type' => 'container',
+        '#attributes' => $variables['description']['attributes'],
+      ] + $description;
+      $variables['description']['attributes'] = new Attribute();
+    }
+
+    $variables['description']['content']['#attributes']->addClass('webform-element-description');
+
+    // Handle invisible descriptions.
+    if (isset($element['#description_display']) && $element['#description_display'] === 'invisible') {
+      $variables['description']['content']['#attributes']->addClass('visually-hidden');
+      $variables['description_display'] = 'after';
+    }
+
+    // Nest description content so that we can a more link
+    // below the description.
+    $variables['description']['content'] = [
+      'description' => $variables['description']['content'],
+    ];
+  }
+  
+  $title =& NestedArray::getValue($variables, $title_parents);
 
   // Move #description to #help for webform admin routes.
   _webform_preprocess_description_help($variables);
@@ -405,31 +578,29 @@ function webform_preprocess_form_element(&$variables) {
   // Add (read) more to #description.
   _webform_preprocess_form_element_description_more($variables);
 
-  // Add #help and #_title_display to label.
-  // Note: #_title_display is used to track inline titles.
-  // @see \Drupal\webform\Plugin\WebformElementBase::prepare
-  $variables['label'] += array_intersect_key($element, array_flip(['#help', '#_title_display']));
+  // Add help to title (aka label).
+  _webform_preprocess_help_title($title,$element);
 }
 
 /**
  * Prepares #description and #help properties for form element templates.
  */
-function _webform_preprocess_description_help(&$variables) {
+function _webform_preprocess_description_help(array &$variables) {
   $element = &$variables['element'];
   // Move #description to #help for webform admin routes.
   if (\Drupal::config('webform.settings')->get('ui.description_help')
     && \Drupal::service('webform.request')->isWebformAdminRoute()
     && \Drupal::routeMatch()->getRouteName() != 'webform.contribute.settings'
-    && isset($element['#description'])
+    && !empty($element['#description'])
     && !isset($element['#help'])
     && !empty($element['#title'])
     && !(isset($element['#title_display']) && in_array($element['#title_display'], ['attribute', 'invisible']))
   ) {
-    $element['#help'] = $element['#description'];
-    unset($variables['description']);
-    if (is_array($element['#help'])) {
-      $element['#help'] = \Drupal::service('renderer')->render($element['#help']);
-    }
+    $element['#help'] = (is_array($element['#description'])) ? \Drupal::service('renderer')->render($element['#description']) : $element['#description'];
+    $element['#help_attributes']['aria-hidden'] = 'true';
+
+    $variables['description_display'] = 'after';
+    $variables['description']['content']['description']['#attributes']->addClass('visually-hidden');
   }
 }
 
@@ -437,7 +608,7 @@ function _webform_preprocess_description_help(&$variables) {
  * Append #help to title variable.
  */
 function _webform_preprocess_help_title(&$variables, array &$element) {
-  if (empty($element['#help'])) {
+  if (empty($variables) || empty($element['#help'])) {
     return;
   }
 
@@ -448,10 +619,15 @@ function _webform_preprocess_help_title(&$variables, array &$element) {
     ] + array_intersect_key($element, array_flip(['#help'])),
   ];
 
+  // Add help attributes.
+  if (isset($element['#help_attributes'])) {
+    $variables['help']['#attributes'] = $element['#help_attributes'];
+  }
+
   // Get #title_display and move help before title for 'inline' titles.
   if (isset($element['#_title_display'])) {
-    // #_title_display is set
-    // via \Drupal\webform\Plugin\WebformElementBase::prepare.
+    // #_title_display is set via WebformElementBase::prepare.
+    // @see \Drupal\webform\Plugin\WebformElementBase::prepare.
     $title_display = $element['#_title_display'];
   }
   elseif (isset($element['#title_display'])) {
@@ -461,7 +637,7 @@ function _webform_preprocess_help_title(&$variables, array &$element) {
     $title_display = NULL;
   }
 
-  if ($title_display == 'inline') {
+  if ($title_display === 'inline') {
     $variables['title']['#weight'] = 0;
     $variables['help']['#weight'] = -1;
   }
@@ -475,33 +651,14 @@ function _webform_preprocess_help_title(&$variables, array &$element) {
  * @see template_preprocess_datetime_wrapper()
  * @see datetime-wrapper.html.twig
  */
-function _webform_preprocess_form_element_description_more(&$variables) {
+function _webform_preprocess_form_element_description_more(array &$variables) {
   $element = &$variables['element'];
   if (empty($element['#more'])) {
     return;
   }
 
-  // Make sure $variables['description']['content'] is a render array.
-  if (!isset($variables['description'])) {
-    $variables['description_display'] = $element['#description_display'];
-    $description_attributes = [];
-    if (!empty($element['#id'])) {
-      $description_attributes['id'] = $element['#id'] . '--description';
-    }
-    $variables['description']['attributes'] = new Attribute($description_attributes);
-    $variables['description']['content'] = [];
-  }
-
-  // Move content to content.description and convert description to
-  // render array.
-  $variables['description']['content'] = [
-    'description' => is_array($variables['description']['content']) ? $variables['description']['content'] : ['#markup' => $variables['description']['content']],
-  ];
-
-  // If description display is invisible hie the description.
-  if (isset($variables['description_display']) && $variables['description_display'] == 'invisible') {
-    $variables['description']['content']['description']['#prefix'] = '<div class="visually-hidden">';
-    $variables['description']['content']['description']['#suffix'] = '</div>';
+  // Make sure description is displayed.
+  if (!isset($variables['description_display'])) {
     $variables['description_display'] = 'after';
   }
 
@@ -512,112 +669,6 @@ function _webform_preprocess_form_element_description_more(&$variables) {
   ] + array_intersect_key($element, array_flip(['#more', '#more_title']));
 }
 
-/**
- * Prepares #more property for details template.
- *
- * @see template_preprocess_details()
- * @see details.html.twig
- */
-function _webform_preprocess_details_description_more(&$variables) {
-  $element = &$variables['element'];
-  if (empty($element['#more'])) {
-    return;
-  }
-
-  // Convert description to render array.
-  $variables['description'] = [
-    'description' => is_array($variables['description']) ? $variables['description'] : ['#markup' => $variables['description']],
-  ];
-
-  // Add more element.
-  $variables['description']['more'] = [
-    '#type' => 'webform_more',
-    '#attributes' => (!empty($element['#id'])) ? ['id' => $element['#id'] . '--more'] : [],
-  ] + array_intersect_key($element, array_flip(['#more', '#more_title']));
-
-}
-
-/**
- * Prepares variables for file managed file templates.
- *
- * @see https://stackoverflow.com/questions/21842274/cross-browser-custom-styling-for-file-upload-button
- * @see template_preprocess_file_managed_file()
- */
-function webform_preprocess_file_managed_file(&$variables) {
-  $element = &$variables['element'];
-  if (empty($element['#button'])) {
-    return;
-  }
-
-  // Don't alter hidden file upload input.
-  if (isset($element['upload']['#access']) && $element['upload']['#access'] === FALSE) {
-    return;
-  }
-
-  // Create an unique id for the file upload input and label.
-  $button_id = Html::getUniqueId($variables['element']['upload']['#id'] . '-button');
-
-  // Create a label that is styled like an action button.
-  $label = [
-    '#type' => 'html_tag',
-    '#tag' => 'label',
-    '#value' => (isset($element['#button__title'])) ? $element['#button__title'] : (empty($element['#multiple']) ? t('Choose file') : t('Choose files')),
-    '#attributes' => (isset($element['#button__attributes'])) ? $element['#button__attributes'] : [],
-  ];
-
-  // Add 'for' attribute.
-  $label['#attributes']['for'] = $button_id;
-
-  // Add default button classes.
-  if (empty($label['#attributes']['class'])) {
-    $label['#attributes']['class'][] = 'button';
-    $label['#attributes']['class'][] = 'button-action';
-  }
-
-  // Add .webform-file-button.
-  $label['#attributes']['class'][] = 'webform-file-button';
-
-  // Make sure the label is first.
-  $element = ['label' => $label] + $element;
-
-  // Set the custom button ID for file upload input.
-  $element['upload']['#attributes']['id'] = $button_id;
-
-  // Hide the file upload.
-  $element['upload']['#attributes']['class'][] = 'webform-file-button-input';
-
-  // Attach library.
-  $element['#attached']['library'][] = 'webform/webform.element.file.button';
-}
-
-/**
- * Prepares variables for file link templates.
- *
- * @see webform_file_access
- */
-function webform_preprocess_file_link(&$variables) {
-  /** @var \Drupal\file\FileInterface $file */
-  $file = $variables['file'];
-  $file = ($file instanceof File) ? $file : File::load($file->fid);
-
-  // Remove link to temporary anonymous private file uploads.
-  if ($file->isTemporary() && $file->getOwner()->isAnonymous() && strpos($file->getFileUri(), 'private://webform/') === 0) {
-    $variables['link'] = $file->getFilename();
-  }
-}
-
-/**
- * Prepares variables for image.
- *
- * Make sure the image src for the 'webform_image_file' src is an absolute URL.
- */
-function webform_preprocess_image(&$variables) {
-  global $base_url;
-  if (isset($variables['attributes']['class']) && in_array('webform-image-file', (array) $variables['attributes']['class'])) {
-    $variables['attributes']['src'] = $base_url . preg_replace('/^' . preg_quote(base_path(), '/') . '/', '/', $variables['attributes']['src']);
-  }
-}
-
 /******************************************************************************/
 // Theme suggestions.
 /******************************************************************************/
diff --git a/includes/webform.theme.template.inc b/includes/webform.theme.template.inc
index 83334b24..9fa6c2d3 100644
--- a/includes/webform.theme.template.inc
+++ b/includes/webform.theme.template.inc
@@ -797,18 +797,19 @@ function template_preprocess_webform_composite_telephone(array &$variables) {
  *   An associative array containing the following key:
  *   - element: The webform element.
  *   - help: The help content.
+ *   - attributes: The help attributes.
  */
 function template_preprocess_webform_element_help(array &$variables) {
+  $attributes = (isset($variables['attributes'])) ? $variables['attributes'] : [];
+  $attributes['title'] = $variables['help'];
+  $attributes['class'][] = 'webform-element-help';
+  $attributes['data-webform-help'] = WebformHtmlEditor::stripTags($variables['help']);
+
   $variables['help_icon'] = [
     '#type' => 'link',
     '#title' => '?',
     '#url' => Url::fromRoute('<none>', [], ['fragment' => 'help']),
-    '#attributes' => [
-      'title' => $variables['help'],
-      // Add 'data-webform-help' attribute which support HTML markup.
-      'data-webform-help' => WebformHtmlEditor::stripTags($variables['help']),
-      'class' => ['webform-element-help'],
-    ],
+    '#attributes' => $attributes,
   ];
 }
 
diff --git a/js/webform.tooltip.js b/js/webform.tooltip.js
index bcb5761b..69bdbff1 100644
--- a/js/webform.tooltip.js
+++ b/js/webform.tooltip.js
@@ -51,10 +51,10 @@
         // @see \Drupal\webform\Plugin\WebformElement\OptionsBase::prepare
         var $description;
         if ($element.is('fieldset')) {
-          $description = $element.find('> .fieldset-wrapper > .field-suffix .description.visually-hidden');
+          $description = $element.find('> .fieldset-wrapper > .description > .webform-element-description.visually-hidden');
         }
         else {
-          $description = $element.children('.description.visually-hidden');
+          $description = $element.find('> .description > .webform-element-description.visually-hidden');
         }
 
         var has_visible_input = $element.find(':input:not([type=hidden])').length;
diff --git a/modules/webform_examples_accessibility/config/install/webform.webform.example_accessibility_basic.yml b/modules/webform_examples_accessibility/config/install/webform.webform.example_accessibility_basic.yml
index 2203a704..9df4393d 100644
--- a/modules/webform_examples_accessibility/config/install/webform.webform.example_accessibility_basic.yml
+++ b/modules/webform_examples_accessibility/config/install/webform.webform.example_accessibility_basic.yml
@@ -14,24 +14,6 @@ title: 'Example: Accessibility Basic'
 description: 'Basic webform accessibility example.'
 category: 'Example: Accessibility'
 elements: |
-  general_elements:
-    '#type': details
-    '#title': 'General element'
-    '#open': true
-    form_element:
-      '#type': textfield
-      '#title': 'Form element'
-      '#placeholder': 'This is a placeholder'
-      '#description': 'This is a description.'
-      '#help': 'This is help text.'
-      '#more': 'This is more text'
-      '#field_prefix': 'prefix'
-      '#field_suffix': 'suffix'
-    form_element_tooltip:
-      '#type': textfield
-      '#title': 'Form element tooltip'
-      '#description': 'This is a description.'
-      '#description_display': tooltip
   text_elements:
     '#type': details
     '#title': 'Text elements'
diff --git a/modules/webform_examples_accessibility/config/install/webform.webform.example_accessibility_containers.yml b/modules/webform_examples_accessibility/config/install/webform.webform.example_accessibility_containers.yml
index 0b31a412..e92b4f01 100644
--- a/modules/webform_examples_accessibility/config/install/webform.webform.example_accessibility_containers.yml
+++ b/modules/webform_examples_accessibility/config/install/webform.webform.example_accessibility_containers.yml
@@ -14,28 +14,41 @@ title: 'Example: Accessibility Containers'
 description: 'Container webform accessibility example.'
 category: 'Example: Accessibility'
 elements: |
+  form_element:
+    '#type': textfield
+    '#title': 'Form element'
+    '#placeholder': 'This is a placeholder'
+    '#description': 'This is a description.'
+    '#help': 'This is help text.'
+    '#more': 'This is more text'
+    '#required': true
+    '#field_prefix': 'prefix'
+    '#field_suffix': 'suffix'
   fieldset:
     '#type': fieldset
     '#title': 'Fieldset'
-    fieldset_textfield:
-      '#title': 'Text field'
-      '#type': textfield
+    '#placeholder': 'This is a placeholder'
+    '#description': 'This is a description.'
+    '#help': 'This is help text.'
+    '#more': 'This is more text'
+    '#required': true
+    '#field_prefix': 'prefix'
+    '#field_suffix': 'suffix'
   details:
     '#type': details
-    '#title': 'Details'
-    details_textfield:
-      '#title': 'Text field'
-      '#type': textfield
-  webform_section:
+    '#title': 'details'
+    '#placeholder': 'This is a placeholder'
+    '#description': 'This is a description.'
+    '#help': 'This is help text.'
+    '#more': 'This is more text'
+    '#required': true
+    '#open': true
+  section:
     '#type': webform_section
-    '#title': 'Section'
-    details_textfield:
-      '#title': 'Text field'
-      '#type': textfield
-  webform_message:
-    '#type': webform_message
-    '#message_message': 'This is an <strong>info</strong> message.'
-    '#message_close': true
+    '#title': Section
+    '#help': 'This is help text'
+    '#required': true
+    '#description': 'This is a description'
 css: ''
 javascript: ''
 settings:
diff --git a/modules/webform_examples_accessibility/config/install/webform.webform.example_accessibility_labels.yml b/modules/webform_examples_accessibility/config/install/webform.webform.example_accessibility_labels.yml
new file mode 100644
index 00000000..73c1bfb3
--- /dev/null
+++ b/modules/webform_examples_accessibility/config/install/webform.webform.example_accessibility_labels.yml
@@ -0,0 +1,315 @@
+langcode: en
+status: open
+dependencies:
+  enforced:
+    module:
+      - webform_examples_accessibility
+open: null
+close: null
+uid: null
+template: false
+archive: false
+id: example_accessibility_labels
+title: 'Example: Accessibility Labels & Descriptions'
+description: 'Example of webform label and description accessibility.'
+category: 'Example: Accessibility'
+elements: |
+  form_elements:
+    '#type': details
+    '#title': 'Form elements'
+    '#open': true
+    form_element:
+      '#type': textfield
+      '#title': 'Form element'
+      '#placeholder': 'This is a placeholder'
+      '#description': 'This is a description.'
+      '#help': 'This is help text.'
+      '#more': 'This is more text'
+      '#required': true
+      '#field_prefix': 'prefix'
+      '#field_suffix': 'suffix'
+    datelist_element:
+      '#type': datelist
+      '#title': 'Datelist'
+      '#placeholder': 'This is a placeholder'
+      '#description': 'This is a description.'
+      '#help': 'This is help text.'
+      '#more': 'This is more text'
+      '#field_prefix': 'prefix'
+      '#field_suffix': 'suffix'
+  element_title:
+    '#type': details
+    '#title': 'Element title'
+    '#open': true
+    title_display_before:
+      '#type': textfield
+      '#title': 'Title displayed before'
+      '#title_display': before
+    title_display_after:
+      '#type': textfield
+      '#title': 'Title displayed after'
+      '#title_display': after
+    title_display_inline:
+      '#type': textfield
+      '#title': 'Title displayed inline'
+      '#title_display': inline
+      '#description': 'This is a description.'
+    title_display_inline_composite:
+      '#type': radios
+      '#title': 'Title displayed inline composite'
+      '#title_display': inline
+      '#description': 'This is a description.'
+      '#title_display': inline
+      '#options':
+        one: One
+        two: Two
+        three: Three
+  element_description:
+    '#type': details
+    '#title': 'Element description'
+    '#open': true
+    description_display_after:
+      '#type': textfield
+      '#title': 'Description displayed after'
+      '#description': 'This is a description.'
+    description_display_before:
+      '#type': textfield
+      '#title': 'Description displayed before'
+      '#description': 'This is a description.'
+      '#description_display': before
+    description_display_tooltip:
+      '#type': textfield
+      '#title': 'Description displayed in tooltip'
+      '#description': 'This is a description.'
+      '#description_display': tooltip
+  element_help:
+    '#type': details
+    '#title': 'Element help'
+    '#open': true
+    help:
+      '#type': textfield
+      '#title': 'Help'
+      '#help': 'This is a help.'
+  element_more:
+    '#type': details
+    '#title': 'Element more'
+    '#open': true
+    more:
+      '#type': textfield
+      '#title': 'More'
+      '#more': 'This is more text.'
+  fieldset_elements:
+    '#type': details
+    '#title': 'Fieldset elements'
+    '#open': true
+    fieldset:
+      '#type': fieldset
+      '#title': 'Fieldset'
+      '#description': 'This is a description.'
+      '#help': 'This is help text.'
+      '#more': 'This is more text'
+      '#required': true
+      '#field_prefix': 'prefix'
+      '#field_suffix': 'suffix'
+    fieldset_description:
+      '#type': fieldset
+      '#title': 'Fieldset description'
+      '#description': 'This is a description.'
+    fieldset_title_invisible:
+      '#type': fieldset
+      '#title': 'Fieldset description invisible'
+      '#description': 'This is a description.'
+      '#title_display': invisible
+    fieldset_help:
+      '#type': fieldset
+      '#title': 'Fieldset help'
+      '#help': 'This is help text.'
+    fieldset_more:
+      '#type': fieldset
+      '#title': 'Fieldset more'
+      '#more': 'This is more text'
+  details_elements:
+    '#type': details
+    '#title': 'Details elements'
+    '#open': true
+    details:
+      '#type': details
+      '#title': 'details'
+      '#description': 'This is a description.'
+      '#help': 'This is help text.'
+      '#more': 'This is more text'
+      '#required': true
+      '#open': true
+    details_description:
+      '#type': details
+      '#title': 'details description'
+      '#description': 'This is a description.'
+      '#open': true
+    details_description_before:
+      '#type': details
+      '#title': 'details description before'
+      '#description': 'This is a description.'
+      '#description_display': before
+      '#open': true
+      details_description_before_textfield:
+        '#type': textfield
+        '#title': 'Details description before textfield'
+    details_description_invisible:
+      '#type': details
+      '#title': 'details description invisible'
+      '#description': 'This is a description.'
+      '#description_display': invisible
+      '#open': true
+    details_help:
+      '#type': details
+      '#title': 'details help'
+      '#help': 'This is help text.'
+      '#open': true
+    details_more:
+      '#type': details
+      '#title': 'details more'
+      '#more': 'This is more text'
+      '#open': true
+  section_elements:
+    '#type': details
+    '#title': 'Section elements'
+    '#open': true
+    section:
+      '#type': webform_section
+      '#title': Section
+      '#help': 'This is help text'
+      '#required': true
+      '#description': 'This is a description'
+css: ''
+javascript: ''
+settings:
+  ajax: false
+  ajax_scroll_top: form
+  page: true
+  page_submit_path: ''
+  page_confirm_path: ''
+  form_submit_once: false
+  form_exception_message: ''
+  form_open_message: ''
+  form_close_message: ''
+  form_previous_submissions: true
+  form_confidential: false
+  form_confidential_message: ''
+  form_convert_anonymous: false
+  form_prepopulate: false
+  form_prepopulate_source_entity: false
+  form_prepopulate_source_entity_required: false
+  form_prepopulate_source_entity_type: ''
+  form_reset: false
+  form_disable_autocomplete: false
+  form_novalidate: false
+  form_disable_inline_errors: false
+  form_required: false
+  form_unsaved: false
+  form_disable_back: false
+  form_submit_back: false
+  form_autofocus: false
+  form_details_toggle: false
+  form_login: false
+  form_login_message: ''
+  submission_label: ''
+  submission_log: false
+  submission_user_columns: {  }
+  submission_login: false
+  submission_login_message: ''
+  submission_exception_message: ''
+  submission_locked_message: ''
+  previous_submission_message: ''
+  previous_submissions_message: ''
+  autofill: false
+  autofill_message: ''
+  autofill_excluded_elements: {  }
+  wizard_progress_bar: true
+  wizard_progress_pages: false
+  wizard_progress_percentage: false
+  wizard_start_label: ''
+  wizard_confirmation: true
+  wizard_confirmation_label: ''
+  wizard_track: ''
+  preview: 0
+  preview_label: ''
+  preview_title: ''
+  preview_message: ''
+  preview_attributes: {  }
+  preview_excluded_elements: {  }
+  preview_exclude_empty: true
+  preview_exclude_empty_checkbox: false
+  draft: none
+  draft_multiple: false
+  draft_auto_save: false
+  draft_saved_message: ''
+  draft_loaded_message: ''
+  confirmation_type: page
+  confirmation_title: ''
+  confirmation_message: ''
+  confirmation_url: ''
+  confirmation_attributes: {  }
+  confirmation_back: true
+  confirmation_back_label: ''
+  confirmation_back_attributes: {  }
+  confirmation_exclude_query: false
+  confirmation_exclude_token: false
+  limit_total: null
+  limit_total_interval: null
+  limit_total_message: ''
+  limit_user: null
+  limit_user_interval: null
+  limit_user_message: ''
+  entity_limit_total: null
+  entity_limit_total_interval: null
+  entity_limit_user: null
+  entity_limit_user_interval: null
+  purge: none
+  purge_days: null
+  results_disabled: false
+  results_disabled_ignore: false
+  token_update: false
+access:
+  create:
+    roles:
+      - anonymous
+      - authenticated
+    users: {  }
+    permissions: {  }
+  view_any:
+    roles: {  }
+    users: {  }
+    permissions: {  }
+  update_any:
+    roles: {  }
+    users: {  }
+    permissions: {  }
+  delete_any:
+    roles: {  }
+    users: {  }
+    permissions: {  }
+  purge_any:
+    roles: {  }
+    users: {  }
+    permissions: {  }
+  view_own:
+    roles: {  }
+    users: {  }
+    permissions: {  }
+  update_own:
+    roles: {  }
+    users: {  }
+    permissions: {  }
+  delete_own:
+    roles: {  }
+    users: {  }
+    permissions: {  }
+  administer:
+    roles: {  }
+    users: {  }
+    permissions: {  }
+  test:
+    roles: {  }
+    users: {  }
+    permissions: {  }
+handlers: {  }
diff --git a/src/Element/WebformHelp.php b/src/Element/WebformHelp.php
index 927ecd4a..856dc59a 100644
--- a/src/Element/WebformHelp.php
+++ b/src/Element/WebformHelp.php
@@ -18,6 +18,7 @@ class WebformHelp extends RenderElement {
     return [
       '#help' => '',
       '#theme' => 'webform_element_help',
+      '#attributes' => [],
     ];
   }
 
diff --git a/src/Element/WebformLikert.php b/src/Element/WebformLikert.php
index 1587f9e2..5c872cc6 100644
--- a/src/Element/WebformLikert.php
+++ b/src/Element/WebformLikert.php
@@ -129,6 +129,7 @@ class WebformLikert extends FormElement {
         // Must include an empty <span> so that the item's value is
         // not required.
         '#value' => '<span></span>',
+        '#webform_element' => TRUE,
         '#required' => $element['#required'],
       ];
       if ($question_description_property_name) {
diff --git a/src/Element/WebformOtherBase.php b/src/Element/WebformOtherBase.php
index c3811dc9..a1856d41 100644
--- a/src/Element/WebformOtherBase.php
+++ b/src/Element/WebformOtherBase.php
@@ -101,6 +101,7 @@ abstract class WebformOtherBase extends FormElement {
     $element['#tree'] = TRUE;
 
     $element[$type]['#type'] = static::$type;
+    $element[$type]['#webform_element'] = TRUE;
     $element[$type] += array_intersect_key($element, array_combine($properties, $properties));
     $element[$type]['#title_display'] = 'invisible';
     if (!isset($element[$type]['#options'][static::OTHER_OPTION])) {
@@ -113,12 +114,6 @@ abstract class WebformOtherBase extends FormElement {
     // @see \Drupal\Core\Render\Element\CompositeFormElementTrait
     $element[$type]['#pre_render'] = [];
 
-    // Disable label[for] which does not point to any specific element.
-    // @see webform_preprocess_form_element_label()
-    if (in_array($type, ['radios', 'checkboxes', 'buttons'])) {
-      $element['#label_attributes']['for'] = FALSE;
-    }
-
     // Build other textfield.
     $element['other']['#error_no_message'] = TRUE;
     foreach ($element as $key => $value) {
@@ -131,6 +126,7 @@ abstract class WebformOtherBase extends FormElement {
     }
     $element['other'] += [
       '#type' => 'textfield',
+      '#webform_element' => TRUE,
       '#placeholder' => t('Enter other...'),
     ];
     if (!isset($element['other']['#title'])) {
diff --git a/src/Plugin/WebformElement/ContainerBase.php b/src/Plugin/WebformElement/ContainerBase.php
index c9e7bf44..337a3b5d 100644
--- a/src/Plugin/WebformElement/ContainerBase.php
+++ b/src/Plugin/WebformElement/ContainerBase.php
@@ -19,8 +19,6 @@ abstract class ContainerBase extends WebformElementBase {
   public function getDefaultProperties() {
     return [
       'title' => '',
-      // General settings.
-      'description' => '',
       // Form validation.
       'required' => FALSE,
       // Attributes.
@@ -67,8 +65,8 @@ abstract class ContainerBase extends WebformElementBase {
     // @see core/modules/system/templates/details.html.twig
     if (isset($element['#title_display'])
       && $element['#title_display'] === 'invisible'
-      && ($this instanceof Fieldset || $this instanceof Details)) {
-      unset($element['#title']);
+      && $this instanceof Details) {
+      // unset($element['#title']);
     }
   }
 
diff --git a/src/Plugin/WebformElement/Details.php b/src/Plugin/WebformElement/Details.php
index c2c392ff..bf21eb39 100644
--- a/src/Plugin/WebformElement/Details.php
+++ b/src/Plugin/WebformElement/Details.php
@@ -22,7 +22,14 @@ class Details extends ContainerBase {
    */
   public function getDefaultProperties() {
     $properties = [
+      // Description/Help.
       'help' => '',
+      'description' => '',
+      'more' => '',
+      'more_title' => '',
+      // Title.
+      'title_display' => '',
+      // Details.
       'open' => FALSE,
     ] + parent::getDefaultProperties();
 
diff --git a/src/Plugin/WebformElement/Fieldset.php b/src/Plugin/WebformElement/Fieldset.php
index ef09df5e..4f7944cf 100644
--- a/src/Plugin/WebformElement/Fieldset.php
+++ b/src/Plugin/WebformElement/Fieldset.php
@@ -20,7 +20,12 @@ class Fieldset extends ContainerBase {
    */
   public function getDefaultProperties() {
     return [
+      // Description/Help.
       'help' => '',
+      'description' => '',
+      'more' => '',
+      'more_title' => '',
+      // Title.
       'title_display' => '',
     ] + parent::getDefaultProperties();
   }
diff --git a/src/Plugin/WebformElement/WebformSection.php b/src/Plugin/WebformElement/WebformSection.php
index 061a55de..c11c07d6 100644
--- a/src/Plugin/WebformElement/WebformSection.php
+++ b/src/Plugin/WebformElement/WebformSection.php
@@ -22,7 +22,12 @@ class WebformSection extends ContainerBase {
    */
   public function getDefaultProperties() {
     return [
+      // Description/Help.
       'help' => '',
+      'description' => '',
+      'more' => '',
+      'more_title' => '',
+      // Title.
       'title_tag' => \Drupal::config('webform.settings')->get('element.default_section_title_tag'),
       'title_display' => '',
     ] + parent::getDefaultProperties();
diff --git a/src/Plugin/WebformElementBase.php b/src/Plugin/WebformElementBase.php
index 12428d3b..1c5a6db2 100644
--- a/src/Plugin/WebformElementBase.php
+++ b/src/Plugin/WebformElementBase.php
@@ -711,8 +711,6 @@ class WebformElementBase extends PluginBase implements WebformElementInterface {
       $element[$attributes_property]['class'][] = 'js-webform-tooltip-element';
       $element[$attributes_property]['class'][] = 'webform-tooltip-element';
       $element['#attached']['library'][] = 'webform/webform.tooltip';
-      // More is not supported with tooltip.
-      unset($element['#more']);
     }
 
     // Add iCheck support.
@@ -2174,11 +2172,6 @@ class WebformElementBase extends PluginBase implements WebformElementInterface {
       '#type' => 'webform_html_editor',
       '#title' => $this->t('More text'),
       '#description' => $this->t('A long description of the element that provides form additional information which can opened and closed.'),
-      '#states' => [
-        'invisible' => [
-          ':input[name="properties[description_display]"]' => ['value' => 'tooltip'],
-        ],
-      ],
     ];
 
     /* Form display */
diff --git a/src/Tests/Composite/WebformCompositeTest.php b/src/Tests/Composite/WebformCompositeTest.php
index c082f7e7..853f3d42 100644
--- a/src/Tests/Composite/WebformCompositeTest.php
+++ b/src/Tests/Composite/WebformCompositeTest.php
@@ -47,8 +47,8 @@ class WebformCompositeTest extends WebformTestBase {
 
     // Check link multiple in table.
     $this->assertRaw('<label for="edit-link-multiple">Link multiple</label>');
-    $this->assertRaw('<th class="link_multiple-table--title webform-multiple-table--title">Link Title<a href="#help" title="This is link title help" data-webform-help="This is link title help" class="webform-element-help">?</a>');
-    $this->assertRaw('<th class="link_multiple-table--url webform-multiple-table--url">Link URL<a href="#help" title="This is link url help" data-webform-help="This is link url help" class="webform-element-help">?</a>');
+    $this->assertRaw('<th class="link_multiple-table--title webform-multiple-table--title">Link Title<a href="#help" title="This is link title help" class="webform-element-help" data-webform-help="This is link title help">?</a>');
+    $this->assertRaw('<th class="link_multiple-table--url webform-multiple-table--url">Link URL<a href="#help" title="This is link url help" class="webform-element-help" data-webform-help="This is link url help">?</a>');
 
     /* Processing */
 
diff --git a/src/Tests/Element/WebformElementAddressTest.php b/src/Tests/Element/WebformElementAddressTest.php
index 3a67c6a2..58ff7861 100644
--- a/src/Tests/Element/WebformElementAddressTest.php
+++ b/src/Tests/Element/WebformElementAddressTest.php
@@ -46,8 +46,8 @@ class WebformElementAddressTest extends WebformElementTestBase {
 
     // Check advanced fieldset, legend, help, and description.
     $this->assertRaw('<fieldset data-drupal-selector="edit-address-advanced" aria-describedby="edit-address-advanced--wrapper--description" id="edit-address-advanced--wrapper" class="address--wrapper fieldgroup form-composite webform-composite-visible-title js-form-item form-item js-form-wrapper form-wrapper">');
-    $this->assertRaw('<span class="fieldset-legend">address_advanced<a href="#help" title="This is help text" data-webform-help="This is help text" class="webform-element-help">?</a>');
-    $this->assertRaw('<div id="edit-address-advanced--wrapper--description" class="description">');
+    $this->assertRaw('<span class="fieldset-legend">address_advanced<a href="#help" title="This is help text" class="webform-element-help" data-webform-help="This is help text">?</a>');
+    $this->assertRaw('<div class="description"><div id="edit-address-advanced--wrapper--description" class="webform-element-description">This is a description</div>');
 
     /**************************************************************************/
     // Processing.
diff --git a/src/Tests/Element/WebformElementDetailsTest.php b/src/Tests/Element/WebformElementDetailsTest.php
index 2b4d4905..e6b42516 100644
--- a/src/Tests/Element/WebformElementDetailsTest.php
+++ b/src/Tests/Element/WebformElementDetailsTest.php
@@ -20,10 +20,17 @@ class WebformElementDetailsTest extends WebformElementTestBase {
    * Test details element.
    */
   public function testDetails() {
-    // Check that invalid 'required' and 'aria-required' attributes are removed
-    // from details.
     $this->drupalGet('webform/test_element_details');
-    $this->assertRaw('<details data-webform-key="details_required" data-drupal-selector="edit-details-required" id="edit-details-required" class="js-form-wrapper form-wrapper required">');
+
+    // Check details with help, field prefix, field suffix, description,
+    // and more. Also, check that invalid 'required' and 'aria-required'
+    // attributes are removed.
+    $this->assertRaw('<details data-webform-key="details" data-drupal-selector="edit-details" aria-describedby="edit-details--description" id="edit-details" class="js-form-wrapper form-wrapper required" open="open">    <summary role="button" aria-controls="edit-details" aria-expanded="true" aria-pressed="true" class="js-form-required form-required">details<a href="#help" title="This is help text." class="webform-element-help" data-webform-help="This is help text.">?</a>');
+    $this->assertRaw('<div class="details-description"><div id="edit-details--description" class="webform-element-description">This is a description.</div>');
+    $this->assertRaw('<div id="edit-details--more" class="js-webform-element-more webform-element-more">');
+
+    // Check details title_display: invisible.
+    $this->assertRaw('<summary role="button" aria-controls="edit-details-title-invisible" aria-expanded="false" aria-pressed="false"><span class="visually-hidden">Details title invisible</span></summary>');
   }
 
 }
diff --git a/src/Tests/Element/WebformElementFieldsetTest.php b/src/Tests/Element/WebformElementFieldsetTest.php
index c64d0a25..f3b79e77 100644
--- a/src/Tests/Element/WebformElementFieldsetTest.php
+++ b/src/Tests/Element/WebformElementFieldsetTest.php
@@ -20,10 +20,21 @@ class WebformElementFieldsetTest extends WebformElementTestBase {
    * Test fieldset element.
    */
   public function testFieldset() {
-    // Check that invalid 'required' and 'aria-required' attributes are removed
-    // from fieldset.
     $this->drupalGet('webform/test_element_fieldset');
-    $this->assertRaw('<fieldset data-drupal-selector="edit-fieldset-required" id="edit-fieldset-required" class="required js-form-item form-item js-form-wrapper form-wrapper">');
+
+    // Check fieldset with help, field prefix, field suffix, description,
+    // and more. Also, check that invalid 'required' and 'aria-required'
+    // attributes are removed.
+    $this->assertRaw('<fieldset class="webform-has-field-prefix webform-has-field-suffix required js-form-item form-item js-form-wrapper form-wrapper" data-drupal-selector="edit-fieldset" aria-describedby="edit-fieldset--description" id="edit-fieldset">');
+    $this->assertRaw('<span class="fieldset-legend js-form-required form-required">Fieldset<a href="#help" title="This is help text." class="webform-element-help" data-webform-help="This is help text.">?</a>');
+    $this->assertRaw('<span class="field-prefix">prefix</span>');
+    $this->assertRaw('<span class="field-suffix">suffix</span>');
+    $this->assertRaw('<div class="description">');
+    $this->assertRaw('<div id="edit-fieldset--description" class="webform-element-description">This is a description.</div>');
+    $this->assertRaw('<div id="edit-fieldset--more" class="js-webform-element-more webform-element-more">');
+
+    // Check fieldset title_display: invisible.
+    $this->assertRaw('<span class="visually-hidden fieldset-legend">Fieldset title invisible</span>');
   }
 
 }
diff --git a/src/Tests/Element/WebformElementHelpTest.php b/src/Tests/Element/WebformElementHelpTest.php
index c1353278..8179a0bd 100644
--- a/src/Tests/Element/WebformElementHelpTest.php
+++ b/src/Tests/Element/WebformElementHelpTest.php
@@ -23,29 +23,29 @@ class WebformElementHelpTest extends WebformElementTestBase {
     $this->drupalGet('webform/test_element_help');
 
     // Check basic help.
-    $this->assertRaw('<a href="#help" title="{This is an example of help}" data-webform-help="{This is an example of help}" class="webform-element-help">?</a>');
+    $this->assertRaw('<a href="#help" title="{This is an example of help}" class="webform-element-help" data-webform-help="{This is an example of help}">?</a>');
 
     // Check help with HTML markup.
-    $this->assertRaw('<a href="#help" title="{This is an example of help with HTML markup}" data-webform-help="{This is an example of help with &lt;b&gt;HTML markup&lt;/b&gt;}" class="webform-element-help">?</a>');
+    $this->assertRaw('<a href="#help" title="{This is an example of help with HTML markup}" class="webform-element-help" data-webform-help="{This is an example of help with &lt;b&gt;HTML markup&lt;/b&gt;}">?</a>');
 
     // Check help with XSS.
-    $this->assertRaw('<a href="#help" title="{This is an example of help with XSS alert(&quot;XSS&quot;)}" data-webform-help="{This is an example of help with &lt;b&gt;XSS alert(&quot;XSS&quot;)&lt;/b&gt;}" class="webform-element-help">?</a>');
+    $this->assertRaw('<a href="#help" title="{This is an example of help with XSS alert(&quot;XSS&quot;)}" class="webform-element-help" data-webform-help="{This is an example of help with &lt;b&gt;XSS alert(&quot;XSS&quot;)&lt;/b&gt;}">?</a>');
 
     // Check help with inline title.
-    $this->assertRaw('<a href="#help" title="{This is an example of help with an inline title}" data-webform-help="{This is an example of help with an inline title}" class="webform-element-help">?</a>
+    $this->assertRaw('<a href="#help" title="{This is an example of help with an inline title}" class="webform-element-help" data-webform-help="{This is an example of help with an inline title}">?</a>
 help_inline</label>');
 
     // Check radios (fieldset).
-    $this->assertRaw('<a href="#help" title="{This is an example of help for radio buttons}" data-webform-help="{This is an example of help for radio buttons}" class="webform-element-help">?</a>');
+    $this->assertRaw('<a href="#help" title="{This is an example of help for radio buttons}" class="webform-element-help" data-webform-help="{This is an example of help for radio buttons}">?</a>');
 
     // Check fieldset.
-    $this->assertRaw('<a href="#help" title="{This is an example of help for a fieldset}" data-webform-help="{This is an example of help for a fieldset}" class="webform-element-help">?</a>');
+    $this->assertRaw('<a href="#help" title="{This is an example of help for a fieldset}" class="webform-element-help" data-webform-help="{This is an example of help for a fieldset}">?</a>');
 
     // Check details.
-    $this->assertRaw('<a href="#help" title="{This is an example of help for a details element}" data-webform-help="{This is an example of help for a details element}" class="webform-element-help">?</a>');
+    $this->assertRaw('<a href="#help" title="{This is an example of help for a details element}" class="webform-element-help" data-webform-help="{This is an example of help for a details element}">?</a>');
 
     // Check section.
-    $this->assertRaw('<a href="#help" title="{This is an example of help for a section element}" data-webform-help="{This is an example of help for a section element}" class="webform-element-help">?</a>');
+    $this->assertRaw('<a href="#help" title="{This is an example of help for a section element}" class="webform-element-help" data-webform-help="{This is an example of help for a section element}">?</a>');
   }
 
 }
diff --git a/src/Tests/Element/WebformElementLikertTest.php b/src/Tests/Element/WebformElementLikertTest.php
index 1939b253..2bffeccb 100644
--- a/src/Tests/Element/WebformElementLikertTest.php
+++ b/src/Tests/Element/WebformElementLikertTest.php
@@ -40,14 +40,14 @@ class WebformElementLikertTest extends WebformElementTestBase {
     // Check likert with description.
     $this->assertRaw('<th>Option 1<div class="description">This is a description</div>');
     $this->assertRaw('<label for="edit-likert-description-table-q1-likert-question">Question 1</label>');
-    $this->assertRaw('<div id="edit-likert-description-table-q1-likert-question--description" class="description">');
+    $this->assertRaw('<div id="edit-likert-description-table-q1-likert-question--description" class="webform-element-description">');
     $this->assertRaw('<label for="edit-likert-description-q1" class="option"><span class="webform-likert-label visually-hidden">Option 1</span></label>');
     $this->assertRaw('<span class="webform-likert-description hidden">This is a description</span>');
 
     // Check likert with help.
-    $this->assertRaw('<th>Option 1<a href="#help" title="This is a help text" data-webform-help="This is a help text" class="webform-element-help">?</a>');
-    $this->assertRaw('<label for="edit-likert-help-table-q1-likert-question">Question 1<a href="#help" title="This is a help text" data-webform-help="This is a help text" class="webform-element-help">?</a>');
-    $this->assertRaw('<label for="edit-likert-help-q1--2" class="option"><span class="webform-likert-label visually-hidden">Option 2<span class="webform-likert-help hidden"><a href="#help" title="This is a help text" data-webform-help="This is a help text" class="webform-element-help">?</a>
+    $this->assertRaw('<th>Option 1<a href="#help" title="This is a help text" class="webform-element-help" data-webform-help="This is a help text">?</a>');
+    $this->assertRaw('<label for="edit-likert-help-table-q1-likert-question">Question 1<a href="#help" title="This is a help text" class="webform-element-help" data-webform-help="This is a help text">?</a>');
+    $this->assertRaw('<label for="edit-likert-help-q1--2" class="option"><span class="webform-likert-label visually-hidden">Option 2<span class="webform-likert-help hidden"><a href="#help" title="This is a help text" class="webform-element-help" data-webform-help="This is a help text">?</a>
 </span></span></label>');
 
     // Check likert required.
diff --git a/src/Tests/Element/WebformElementMoreTest.php b/src/Tests/Element/WebformElementMoreTest.php
index a8eeda69..d435880d 100644
--- a/src/Tests/Element/WebformElementMoreTest.php
+++ b/src/Tests/Element/WebformElementMoreTest.php
@@ -23,12 +23,10 @@ class WebformElementMoreTest extends WebformElementTestBase {
     $this->drupalGet('webform/test_element_more');
 
     // Check default more.
-    $this->assertRaw('<div id="edit-more--description" class="description">');
     $this->assertRaw('<div id="edit-more--more" class="js-webform-element-more webform-element-more">');
     $this->assertRaw('<div class="webform-element-more--link"><a role="button" aria-expanded="false" aria-controls="edit-more--more--content" href="#more">More</a></div>');
 
     // Check more with custom title.
-    $this->assertRaw('<div id="edit-more-title--description" class="description">');
     $this->assertRaw('<div id="edit-more-title--more" class="js-webform-element-more webform-element-more">');
     $this->assertRaw('<div class="webform-element-more--link"><a role="button" aria-expanded="false" aria-controls="edit-more-title--more--content" href="#more">{Custom more title}</a></div>');
 
@@ -37,20 +35,18 @@ class WebformElementMoreTest extends WebformElementTestBase {
     $this->assertRaw('<div id="edit-more-html--more--content" class="webform-element-more--content">{This is an example of more with <b>HTML markup</b>}</div>');
 
     // Check more with description.
-    $this->assertRaw('<div id="edit-more-title-description--description" class="description">');
-    $this->assertRaw('{This is an example of a description}');
+    $this->assertRaw('<div id="edit-more-title-description--description" class="webform-element-description">{This is an example of a description}</div>');
     $this->assertRaw('<div id="edit-more-title-description--more" class="js-webform-element-more webform-element-more">');
 
     // Check more with hidden description.
-    $this->assertRaw('<div id="edit-more-title-description-hidden--description" class="description">');
-    $this->assertRaw('<div class="visually-hidden">{This is an example of a hidden description}</div>');
+    $this->assertRaw('<div id="edit-more-title-description-hidden--description" class="webform-element-description visually-hidden">{This is an example of a hidden description}</div>');
     $this->assertRaw('<div id="edit-more-title-description-hidden--more" class="js-webform-element-more webform-element-more">');
 
     // Check datetime more.
     $this->assertRaw('<div id="edit-more-datetime--more" class="js-webform-element-more webform-element-more">');
 
     // Check fieldset more.
-    $this->assertRaw('<div id="edit-more-fieldset--description" class="description">{This is a description}');
+    $this->assertRaw('<div id="edit-more-fieldset--description" class="webform-element-description">{This is a description}</div>');
     $this->assertRaw('<div id="edit-more-fieldset--more" class="js-webform-element-more webform-element-more">');
 
     // Check details more.
@@ -58,8 +54,8 @@ class WebformElementMoreTest extends WebformElementTestBase {
     $this->assertRaw('<div class="webform-element-more--link"><a role="button" aria-expanded="false" aria-controls="edit-more-details--more--content" href="#more">More</a></div>');
 
     // Check tooltip ignored more.
-    $this->assertRaw('<div id="edit-more-tooltip--description" class="description visually-hidden">');
-    $this->assertNoRaw('<div id="edit-more-tooltip--more" class="js-webform-element-more webform-element-more">');
+    $this->assertRaw('<div id="edit-more-tooltip--description" class="webform-element-description visually-hidden">{This is a description}</div>');
+    $this->assertRaw('<div id="edit-more-tooltip--more" class="js-webform-element-more webform-element-more">');
   }
 
 }
diff --git a/src/Tests/Element/WebformElementOtherTest.php b/src/Tests/Element/WebformElementOtherTest.php
index d535cf6d..e21ec952 100644
--- a/src/Tests/Element/WebformElementOtherTest.php
+++ b/src/Tests/Element/WebformElementOtherTest.php
@@ -41,8 +41,7 @@ class WebformElementOtherTest extends WebformElementTestBase {
     $this->assertRaw('<option value="_other_" selected="selected">Is there another option you wish to enter?</option>');
     $this->assertRaw('<label for="edit-select-other-advanced-other">Other</label>');
     $this->assertRaw('<input data-drupal-selector="edit-select-other-advanced-other" aria-describedby="edit-select-other-advanced-other--description" type="text" id="edit-select-other-advanced-other" name="select_other_advanced[other]" value="Four" size="20" maxlength="20" placeholder="What is this other option" class="form-text" />');
-    $this->assertRaw('<div id="edit-select-other-advanced-other--description" class="description">');
-    $this->assertRaw('Other select description');
+    $this->assertRaw('<div id="edit-select-other-advanced-other--description" class="webform-element-description">Other select description</div>');
 
     // Check multiple select_other.
     $this->assertRaw('<span class="fieldset-legend">Select other multiple</span>');
@@ -63,8 +62,7 @@ class WebformElementOtherTest extends WebformElementTestBase {
     $this->assertRaw('<div id="edit-checkboxes-other-advanced-checkboxes" class="js-webform-checkboxes webform-options-display-two-columns form-checkboxes">');
     $this->assertRaw('<span class="fieldset-legend js-form-required form-required">Checkboxes other advanced</span>');
     $this->assertRaw('<input data-drupal-selector="edit-checkboxes-other-advanced-other" aria-describedby="edit-checkboxes-other-advanced-other--description" type="text" id="edit-checkboxes-other-advanced-other" name="checkboxes_other_advanced[other]" value="Four" size="60" maxlength="128" placeholder="What is this other option" class="form-text" />');
-    $this->assertRaw('<div id="edit-checkboxes-other-advanced-other--description" class="description">');
-    $this->assertRaw('Other checkbox description');
+    $this->assertRaw('<div id="edit-checkboxes-other-advanced-other--description" class="webform-element-description">Other checkbox description</div>');
 
     /**************************************************************************/
     // radios_other
@@ -80,8 +78,7 @@ class WebformElementOtherTest extends WebformElementTestBase {
     $this->assertRaw('<span class="fieldset-legend js-form-required form-required">Radios other advanced</span>');
     $this->assertRaw('<input data-drupal-selector="edit-radios-other-advanced-radios-other-" type="radio" id="edit-radios-other-advanced-radios-other-" name="radios_other_advanced[radios]" value="_other_" checked="checked" class="form-radio" />');
     $this->assertRaw('<input data-drupal-selector="edit-radios-other-advanced-other" aria-describedby="edit-radios-other-advanced-other--description" type="text" id="edit-radios-other-advanced-other" name="radios_other_advanced[other]" value="Four" size="60" maxlength="128" placeholder="What is this other option" class="form-text" />');
-    $this->assertRaw('<div id="edit-radios-other-advanced-other--description" class="description">');
-    $this->assertRaw('Other radio description');
+    $this->assertRaw('<div id="edit-radios-other-advanced-other--description" class="webform-element-description">Other radio description</div>');
 
     /**************************************************************************/
     // buttons_other
@@ -97,8 +94,7 @@ class WebformElementOtherTest extends WebformElementTestBase {
     $this->assertRaw('<span class="fieldset-legend js-form-required form-required">Buttons other advanced</span>');
     $this->assertRaw('<input data-drupal-selector="edit-buttons-other-advanced-buttons-one" type="radio" id="edit-buttons-other-advanced-buttons-one" name="buttons_other_advanced[buttons]" value="One" class="form-radio" />');
     $this->assertRaw('<input data-drupal-selector="edit-buttons-other-advanced-other" aria-describedby="edit-buttons-other-advanced-other--description" type="text" id="edit-buttons-other-advanced-other" name="buttons_other_advanced[other]" value="Four" size="60" maxlength="128" placeholder="What is this other option" class="form-text" />');
-    $this->assertRaw('<div id="edit-buttons-other-advanced-other--description" class="description">');
-    $this->assertRaw('Other button description');
+    $this->assertRaw('<div id="edit-buttons-other-advanced-other--description" class="webform-element-description">Other button description</div>');
   }
 
   /**
diff --git a/src/Tests/Element/WebformElementRadiosTest.php b/src/Tests/Element/WebformElementRadiosTest.php
index 3c66982c..f0234c9b 100644
--- a/src/Tests/Element/WebformElementRadiosTest.php
+++ b/src/Tests/Element/WebformElementRadiosTest.php
@@ -27,11 +27,11 @@ class WebformElementRadiosTest extends WebformElementTestBase {
     // Check radios with description display.
     $this->assertRaw('<input data-drupal-selector="edit-radios-description-one" aria-describedby="edit-radios-description-one--description" type="radio" id="edit-radios-description-one" name="radios_description" value="one" class="form-radio" />');
     $this->assertRaw('<label for="edit-radios-description-one" class="option">One</label>');
-    $this->assertRaw('<div id="edit-radios-description-one--description" class="description">');
+    $this->assertRaw('<div id="edit-radios-description-one--description" class="webform-element-description">This is a description</div>');
 
     // Check radios with help text display.
     $this->assertRaw('<input data-drupal-selector="edit-radios-help-one" type="radio" id="edit-radios-help-one" name="radios_help" value="one" class="form-radio" />');
-    $this->assertRaw('<label for="edit-radios-help-one" class="option">One<a href="#help" title="This is a description" data-webform-help="This is a description" class="webform-element-help">?</a>');
+    $this->assertRaw('<label for="edit-radios-help-one" class="option">One<a href="#help" title="This is a description" class="webform-element-help" data-webform-help="This is a description">?</a>');
 
     // Check radios results does not include description.
     $edit = [
diff --git a/src/Tests/Element/WebformElementSectionTest.php b/src/Tests/Element/WebformElementSectionTest.php
index 332c776a..75b37d73 100644
--- a/src/Tests/Element/WebformElementSectionTest.php
+++ b/src/Tests/Element/WebformElementSectionTest.php
@@ -23,30 +23,26 @@ class WebformElementSectionTest extends WebformElementTestBase {
     $this->drupalGet('webform/test_element_section');
 
     // Check section element.
-    $this->assertRaw('<section data-drupal-selector="edit-webform-section" aria-describedby="edit-webform-section--description" id="edit-webform-section" class="js-form-item form-item js-form-wrapper form-wrapper webform-section">');
-
-    // Check section help.
-    $this->assertRaw('<h2 class="webform-section-title">webform_section<a href="#help" title="{This is help text}" data-webform-help="{This is help text}" class="webform-element-help">?</a>');
-
-    // Check section description.
-    $this->assertRaw('<div id="edit-webform-section--description" class="description">{This is a description}</div>');
-
-    // Check section required.
-    $this->assertRaw('<section data-drupal-selector="edit-webform-section-required" id="edit-webform-section-required" class="required js-form-item form-item js-form-wrapper form-wrapper webform-section" required="required" aria-required="true">');
-    $this->assertRaw('<h2 class="webform-section-title js-form-required form-required">webform_section_required</h2>');
+    $this->assertRaw('<section data-drupal-selector="edit-webform-section" aria-describedby="edit-webform-section--description" id="edit-webform-section" class="required js-form-item form-item js-form-wrapper form-wrapper webform-section" required="required" aria-required="true">');
+    $this->assertRaw('<h2 class="webform-section-title js-form-required form-required">webform_section<a href="#help" title="This is help text." class="webform-element-help" data-webform-help="This is help text.">?</a>');
+    $this->assertRaw('<div class="description"><div id="edit-webform-section--description" class="webform-element-description">This is a description.</div>');
+    $this->assertRaw('<div id="edit-webform-section--more" class="js-webform-element-more webform-element-more">');
 
     // Check custom h5 title tag.
     $this->assertRaw('<section data-drupal-selector="edit-webform-section-title-custom" id="edit-webform-section-title-custom" class="js-form-item form-item js-form-wrapper form-wrapper webform-section">');
     $this->assertRaw('<h5 style="color: red" class="webform-section-title">webform_section_title_custom</h5>');
 
+    // Check section title_display: invisible.
+    $this->assertRaw('<h2 class="visually-hidden webform-section-title">webform_section_title_invisible</h2>');
+
     // Check change default title tag.
     \Drupal::configFactory()->getEditable('webform.settings')
       ->set('element.default_section_title_tag', 'address')
       ->save();
 
     $this->drupalGet('webform/test_element_section');
-    $this->assertNoRaw('<h2 class="webform-section-title">');
-    $this->assertRaw('<address class="webform-section-title">');
+    $this->assertNoRaw('<h2 class="webform-section-title js-form-required form-required">');
+    $this->assertRaw('<address class="webform-section-title js-form-required form-required">');
   }
 
 }
diff --git a/src/Tests/Element/WebformElementSignatureTest.php b/src/Tests/Element/WebformElementSignatureTest.php
index 44228be7..f6216402 100644
--- a/src/Tests/Element/WebformElementSignatureTest.php
+++ b/src/Tests/Element/WebformElementSignatureTest.php
@@ -36,8 +36,7 @@ class WebformElementSignatureTest extends WebformElementTestBase {
     $this->assertRaw('<input type="submit" name="op" value="Reset" class="button js-form-submit form-submit" />');
     $this->assertRaw('<canvas></canvas>');
     $this->assertRaw('</div>');
-    $this->assertRaw('<div id="edit-signature--description" class="description">');
-    $this->assertRaw('Sign above');
+    $this->assertRaw('<div id="edit-signature--description" class="webform-element-description">Sign above</div>');
 
     // Check signature preview image.
     $this->postSubmissionTest($webform, [], t('Preview'));
diff --git a/src/Tests/Element/WebformElementTermsOfServiceTest.php b/src/Tests/Element/WebformElementTermsOfServiceTest.php
index 42a96b67..76562bca 100644
--- a/src/Tests/Element/WebformElementTermsOfServiceTest.php
+++ b/src/Tests/Element/WebformElementTermsOfServiceTest.php
@@ -34,7 +34,7 @@ class WebformElementTermsOfServiceTest extends WebformElementTestBase {
     $this->assertRaw('<div data-webform-terms-of-service-type="modal" class="form-type-webform-terms-of-service js-form-type-webform-terms-of-service js-form-item form-item js-form-type-checkbox form-type-checkbox js-form-item-terms-of-service-default form-item-terms-of-service-default">');
     $this->assertRaw('<input data-drupal-selector="edit-terms-of-service-default" type="checkbox" id="edit-terms-of-service-default" name="terms_of_service_default" value class="form-checkbox required" required="required" aria-required="true" />');
     $this->assertRaw('<label for="edit-terms-of-service-default" class="option js-form-required form-required">I agree to the <a role="button" href="#terms">terms of service</a>. (default)</label>');
-    $this->assertRaw('<div id="edit-terms-of-service-default--description" class="description">');
+    $this->assertRaw('<div id="edit-terms-of-service-default--description" class="webform-element-description">');
     $this->assertRaw('<div id="webform-terms-of-service-terms_of_service_default--description" class="webform-terms-of-service-details js-hide">');
     $this->assertRaw('<div class="webform-terms-of-service-details--title">terms_of_service_default</div>');
     $this->assertRaw('<div class="webform-terms-of-service-details--content">These are the terms of service.</div>');
diff --git a/src/Tests/Settings/WebformSettingsAdminTest.php b/src/Tests/Settings/WebformSettingsAdminTest.php
index 2751c675..fa110c48 100644
--- a/src/Tests/Settings/WebformSettingsAdminTest.php
+++ b/src/Tests/Settings/WebformSettingsAdminTest.php
@@ -107,11 +107,11 @@ class WebformSettingsAdminTest extends WebformTestBase {
 
     // Check moving #description to #help for webform admin routes.
     $this->drupalPostForm('admin/structure/webform/config/advanced', ['ui[description_help]' => TRUE], t('Save configuration'));
-    $this->assertRaw('<a href="#help" title="If checked, all element descriptions will be moved to help text (tooltip)." data-webform-help="If checked, all element descriptions will be moved to help text (tooltip)." class="webform-element-help">?</a>');
+    $this->assertRaw('<a href="#help" title="If checked, all element descriptions will be moved to help text (tooltip)." class="webform-element-help" data-webform-help="If checked, all element descriptions will be moved to help text (tooltip).">?</a>');
 
     // Check moving #description to #help for webform admin routes.
     $this->drupalPostForm('admin/structure/webform/config/advanced', ['ui[description_help]' => FALSE], t('Save configuration'));
-    $this->assertNoRaw('<a href="#help" title="If checked, all element descriptions will be moved to help text (tooltip)." data-webform-help="If checked, all element descriptions will be moved to help text (tooltip)." class="webform-element-help">?</a>');
+    $this->assertNoRaw('<a href="#help" title="If checked, all element descriptions will be moved to help text (tooltip)." class="webform-element-help" data-webform-help="If checked, all element descriptions will be moved to help text (tooltip).">?</a>');
   }
 
   /**
diff --git a/src/Utility/WebformElementHelper.php b/src/Utility/WebformElementHelper.php
index 450d0216..3af76def 100644
--- a/src/Utility/WebformElementHelper.php
+++ b/src/Utility/WebformElementHelper.php
@@ -71,6 +71,19 @@ class WebformElementHelper {
    */
   protected static $ignoredSubPropertiesRegExp;
 
+  /**
+   * Determine if an element is a webform element and should be enhanced.
+   *
+   * @param array $element
+   *   An element.
+   *
+   * @return bool
+   *   TRUE if an element is a webform element.
+   */
+  public static function isWebformElement(array $element) {
+    return (isset($element['#webform_key']) || isset($element['#webform_element'])) ? TRUE : FALSE;
+  }
+
   /**
    * Determine if a webform element's title is displayed.
    *
diff --git a/tests/modules/webform_test/config/install/webform.webform.test_element_details.yml b/tests/modules/webform_test/config/install/webform.webform.test_element_details.yml
index 1ffb244e..b6a38030 100644
--- a/tests/modules/webform_test/config/install/webform.webform.test_element_details.yml
+++ b/tests/modules/webform_test/config/install/webform.webform.test_element_details.yml
@@ -16,15 +16,19 @@ category: 'Test: Element'
 elements: |
   details:
     '#type': details
-    '#title': details
+    '#title': 'details'
+    '#description': 'This is a description.'
+    '#help': 'This is help text.'
+    '#more': 'This is more text'
+    '#required': true
+    '#open': true
     details_markup:
       '#markup': '<p>This is some markup</p>'
-  details_required:
+  details_title_invisible:
     '#type': details
-    '#title': details_required
-    '#required': true
-    details_required_markup:
-      '#markup': '<p>This is some markup</p>'
+    '#title': 'Details title invisible'
+    '#description': 'This is a description.'
+    '#title_display': invisible
 css: ''
 javascript: ''
 settings:
diff --git a/tests/modules/webform_test/config/install/webform.webform.test_element_fieldset.yml b/tests/modules/webform_test/config/install/webform.webform.test_element_fieldset.yml
index 092f8eaf..b7c87f65 100644
--- a/tests/modules/webform_test/config/install/webform.webform.test_element_fieldset.yml
+++ b/tests/modules/webform_test/config/install/webform.webform.test_element_fieldset.yml
@@ -16,15 +16,20 @@ category: 'Test: Element'
 elements: |
   fieldset:
     '#type': fieldset
-    '#title': fieldset
+    '#title': 'Fieldset'
+    '#description': 'This is a description.'
+    '#help': 'This is help text.'
+    '#more': 'This is more text'
+    '#required': true
+    '#field_prefix': 'prefix'
+    '#field_suffix': 'suffix'
     fieldset_markup:
       '#markup': '<p>This is some markup</p>'
-  fieldset_required:
+  fieldset_title_invisible:
     '#type': fieldset
-    '#title': fieldset_required
-    '#required': true
-    fieldset_required_markup:
-      '#markup': '<p>This is some markup</p>'
+    '#title': 'Fieldset title invisible'
+    '#description': 'This is a description.'
+    '#title_display': invisible
 css: ''
 javascript: ''
 settings:
diff --git a/tests/modules/webform_test/config/install/webform.webform.test_element_section.yml b/tests/modules/webform_test/config/install/webform.webform.test_element_section.yml
index 0e0e4260..4032a7b3 100644
--- a/tests/modules/webform_test/config/install/webform.webform.test_element_section.yml
+++ b/tests/modules/webform_test/config/install/webform.webform.test_element_section.yml
@@ -17,27 +17,22 @@ elements: |
   webform_section:
     '#type': webform_section
     '#title': webform_section
-    '#help': '{This is help text}'
-    '#description': '{This is a description}'
-    webform_section_textfield:
-      '#type': textfield
-      '#title': webform_section_textfield
-  webform_section_required:
-    '#type': webform_section
-    '#title': webform_section_required
+    '#description': 'This is a description.'
+    '#help': 'This is help text.'
+    '#more': 'This is more text'
     '#required': true
-    webform_section_required_textfield:
-      '#type': textfield
-      '#title': webform_section_required_textfield
+    webform_section_markup:
+      '#markup': '<p>This is some markup</p>'
+  webform_section_title_invisible:
+    '#type': webform_section
+    '#title': webform_section_title_invisible
+    '#title_display': invisible
   webform_section_title_custom:
     '#type': webform_section
     '#title': webform_section_title_custom
     '#title_tag': h5
     '#title_attributes':
       'style': 'color: red'
-    webform_section_h5_textfield:
-      '#type': textfield
-      '#title': webform_section_h5_textfield
 css: ''
 javascript: ''
 settings:
diff --git a/webform.module b/webform.module
index 67dea160..a44e2b89 100644
--- a/webform.module
+++ b/webform.module
@@ -23,6 +23,7 @@ use Drupal\webform\Entity\WebformSubmission;
 use Drupal\webform\Element\WebformMessage;
 use Drupal\webform\Plugin\WebformElement\ManagedFile;
 use Drupal\webform\Utility\WebformArrayHelper;
+use Drupal\webform\Utility\WebformElementHelper;
 use Drupal\webform\Utility\WebformDialogHelper;
 use Drupal\webform\Utility\WebformOptionsHelper;
 use Drupal\webform\WebformInterface;
@@ -735,6 +736,10 @@ function webform_element_info_alter(array &$info) {
  *   The processed element.
  */
 function webform_process_options(&$element, FormStateInterface $form_state, &$complete_form) {
+  if (!WebformElementHelper::isWebformElement($element)) {
+    return $element;
+  }
+
   if (!empty($element['#options_description_display'])) {
     $description_property_name = ($element['#options_description_display'] == 'help') ? '#help' : '#description';
     foreach (Element::children($element) as $key) {
@@ -746,6 +751,7 @@ function webform_process_options(&$element, FormStateInterface $form_state, &$co
 
       list($title, $description) = explode(WebformOptionsHelper::DESCRIPTION_DELIMITER, $title);
       $element[$key]['#title'] = $title;
+      $element[$key]['#webform_element'] = TRUE;
       $element[$key][$description_property_name] = $description;
     }
   }
