diff --git a/core/includes/form.inc b/core/includes/form.inc
index ce5779c..4d70112 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -939,7 +939,7 @@ function form_get_options($element, $key) {
  *   An associative array containing:
  *   - element: An associative array containing the properties of the element.
  *     Properties used: #attributes, #children, #collapsed, #description, #id,
- *     #title, #value.
+ *     #title, #title_display, #value.
  *
  * @ingroup themeable
  */
@@ -954,27 +954,38 @@ function theme_fieldset($variables) {
   }
 
   $legend_attributes = array();
+  $legend_attributes['class'][] = 'fieldset-legend';
   if (isset($element['#title_display']) && $element['#title_display'] == 'invisible') {
     $legend_attributes['class'][] = 'visually-hidden';
   }
 
-  $output = '<fieldset' . new Attribute($element['#attributes']) . '>';
+  // Hide box for fieldset by default.
+  $element['#attributes']['class'][] = 'fieldset-no-border';
+
+  // Add a class for disabled elements to facilitate cross-browser styling.
+  if (!empty($element['#attributes']['disabled'])) {
+    $element['#attributes']['class'][] = 'form-disabled';
+  }
+  $prefix = isset($element['#field_prefix']) ? '<span class="field-prefix">' . $element['#field_prefix'] . '</span> ' : '';
+  $suffix = isset($element['#field_suffix']) ? ' <span class="field-suffix">' . $element['#field_suffix'] . '</span>' : '';
+
+  $output = '<div class="fieldset-wrapper">';
+  $output .= $prefix . '<fieldset' . new Attribute($element['#attributes']) . '>';
   if (!empty($element['#title'])) {
+    $required = !empty($element['#required']) ? theme('form_required_marker', array('element' => $element)) : '';
+
+    $title = filter_xss_admin($element['#title']);
     // Always wrap fieldset legends in a SPAN for CSS positioning.
-    $output .= '<legend' . new Attribute($legend_attributes) . '><span class="fieldset-legend">' . $element['#title'] . '</span></legend>';
+    $output .= '<legend><span' . new Attribute($legend_attributes) . '>' . $title . '</span>' . $required . '</legend>';
   }
-  $output .= '<div class="fieldset-wrapper">';
   if (!empty($element['#description'])) {
     $attributes = array('class' => 'fieldset-description', 'id' => $description_id);
     $output .= '<div' . new Attribute($attributes) . '>' . $element['#description'] . '</div>';
 
   }
   $output .= $element['#children'];
-  if (isset($element['#value'])) {
-    $output .= $element['#value'];
-  }
+  $output .= "</fieldset>\n" . $suffix;
   $output .= '</div>';
-  $output .= "</fieldset>\n";
   return $output;
 }
 
@@ -1241,11 +1252,15 @@ function theme_checkboxes($variables) {
 }
 
 /**
- * Adds form element theming to an element if its title or description is set.
+ * Adds theming to composite form elements such as checkboxes and radios.
  *
- * This is used as a pre render function for checkboxes and radios.
+ * If either the title or description is set for the composite element as a * whole, then this adds either a 'fieldset' or 'form_element' theme wrapper to
+ * render them. A fieldset is generally preferred to assist screen readers
+ * (http://webaim.org/techniques/forms/screen_reader#group), but is not used
+ * when #title_display is set to an option impossible to render as a fieldset
+ * legend.
  */
-function form_pre_render_conditional_form_element($element) {
+function form_pre_render_composite_form_element($element) {
   // Set the element's title attribute to show #title as a tooltip, if needed.
   if (isset($element['#title']) && $element['#title_display'] == 'attribute') {
     $element['#attributes']['title'] = $element['#title'];
@@ -1256,7 +1271,16 @@ function form_pre_render_conditional_form_element($element) {
   }
 
   if (isset($element['#title']) || isset($element['#description'])) {
-    $element['#theme_wrappers'][] = 'form_element';
+    // To assist screen readers, use a fieldset wrapper when possible, but
+    // fieldset legends are only compatible with the 'before' and 'invisible'
+    // options for #title_display.
+    if (!isset($element['#title_display']) || in_array($element['#title_display'], array('before', 'invisible'))) {
+      $wrapper = 'fieldset';
+    }
+    else {
+      $wrapper = 'form_element';
+    }
+    $element['#theme_wrappers'][] = $wrapper;
   }
   return $element;
 }
@@ -2713,7 +2737,7 @@ function form_process_file($element) {
  *   property hides the label for everyone except screen readers.
  * - attribute: Set the title attribute on the element to create a tooltip
  *   but output no label element. This is supported only for checkboxes
- *   and radios in form_pre_render_conditional_form_element(). It is used
+ *   and radios in form_pre_render_composite_form_element(). It is used
  *   where a visual label is not needed, such as a table of checkboxes where
  *   the row and column provide the context. The tooltip will include the
  *   title and required marker.
diff --git a/core/modules/system/lib/Drupal/system/Tests/Form/ElementsLabelsTest.php b/core/modules/system/lib/Drupal/system/Tests/Form/ElementsLabelsTest.php
index 6c51a04..02bc1d9 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Form/ElementsLabelsTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Form/ElementsLabelsTest.php
@@ -78,10 +78,10 @@ function testFormLabels() {
     $this->assertFalse(isset($elements[0]), 'No label tag when title set not to display.');
 
     // Check #field_prefix and #field_suffix placement.
-    $elements = $this->xpath('//span[@class="field-prefix"]/following-sibling::div[@id="edit-form-radios-test"]');
+    $elements = $this->xpath('//span[@class="field-prefix"]/following-sibling::fieldset[@id="edit-form-radios-test"]');
     $this->assertTrue(isset($elements[0]), 'Properly placed the #field_prefix element after the label and before the field.');
 
-    $elements = $this->xpath('//span[@class="field-suffix"]/preceding-sibling::div[@id="edit-form-radios-test"]');
+    $elements = $this->xpath('//span[@class="field-suffix"]/preceding-sibling::fieldset[@id="edit-form-radios-test"]');
     $this->assertTrue(isset($elements[0]), 'Properly places the #field_suffix element immediately after the form field.');
 
     // Check #prefix and #suffix placement.
diff --git a/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php b/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php
index 5424f66..172b2a1 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php
@@ -94,7 +94,7 @@ function testRequiredFields() {
     $elements['file']['empty_values'] = $empty_strings;
 
     // Regular expression to find the expected marker on required elements.
-    $required_marker_preg = '@<label.*<span class="form-required" aria-hidden="true">\*</span></label>@';
+    $required_marker_preg = '@<(label|legend).*<span class="form-required" aria-hidden="true">\*</span></(label|legend)>@';
 
     // Go through all the elements and all the empty values for them.
     foreach ($elements as $type => $data) {
@@ -520,7 +520,7 @@ function testDisabledElements() {
 
     // All the elements should be marked as disabled, including the ones below
     // the disabled container.
-    $this->assertEqual(count($disabled_elements), 39, 'The correct elements have the disabled property in the HTML code.');
+    $this->assertEqual(count($disabled_elements), 41, 'The correct elements have the disabled property in the HTML code.');
 
     $this->drupalPostForm(NULL, $edit, t('Submit'));
     $returned_values['hijacked'] = drupal_json_decode($this->content);
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 1657f9f..ec91d05 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -449,7 +449,7 @@ function system_element_info() {
     '#input' => TRUE,
     '#process' => array('form_process_radios'),
     '#theme_wrappers' => array('radios'),
-    '#pre_render' => array('form_pre_render_conditional_form_element'),
+    '#pre_render' => array('form_pre_render_composite_form_element'),
   );
   $types['radio'] = array(
     '#input' => TRUE,
@@ -463,7 +463,7 @@ function system_element_info() {
   $types['checkboxes'] = array(
     '#input' => TRUE,
     '#process' => array('form_process_checkboxes'),
-    '#pre_render' => array('form_pre_render_conditional_form_element'),
+    '#pre_render' => array('form_pre_render_composite_form_element'),
     '#theme_wrappers' => array('checkboxes'),
   );
   $types['checkbox'] = array(
