diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php
index f72db81..2123a29 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php
@@ -271,7 +271,7 @@ function element_wrapper_type($none_supported = FALSE, $default_empty = FALSE) {
    * elements available, though this seems like it would be an incredibly
    * rare occurence.
    */
-  function get_elements() {
+  public static function getHTMLElementTypes() {
     static $elements = NULL;
     if (!isset($elements)) {
       // @todo Add possible html5 elements.
@@ -549,7 +549,7 @@ public function buildOptionsForm(&$form, &$form_state) {
     );
     $form['element_type'] = array(
       '#title' => t('HTML element'),
-      '#options' => $this->get_elements(),
+      '#options' => static::getHTMLElementTypes(),
       '#type' => 'select',
       '#default_value' => $this->options['element_type'],
       '#description' => t('Choose the HTML element to wrap around this field, e.g. H1, H2, etc.'),
@@ -594,7 +594,7 @@ public function buildOptionsForm(&$form, &$form_state) {
     );
     $form['element_label_type'] = array(
       '#title' => t('Label HTML element'),
-      '#options' => $this->get_elements(FALSE),
+      '#options' => static::getHTMLElementTypes(FALSE),
       '#type' => 'select',
       '#default_value' => $this->options['element_label_type'],
       '#description' => t('Choose the HTML element to wrap around this label, e.g. H1, H2, etc.'),
@@ -638,7 +638,7 @@ public function buildOptionsForm(&$form, &$form_state) {
     );
     $form['element_wrapper_type'] = array(
       '#title' => t('Wrapper HTML element'),
-      '#options' => $this->get_elements(FALSE),
+      '#options' => static::getHTMLElementTypes(FALSE),
       '#type' => 'select',
       '#default_value' => $this->options['element_wrapper_type'],
       '#description' => t('Choose the HTML element to wrap around this field and label, e.g. H1, H2, etc. This may not be used if the field and label are not rendered together, such as with a table.'),
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/style/StylePluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/style/StylePluginBase.php
index 1fd2c22..b36fb4c 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/style/StylePluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/style/StylePluginBase.php
@@ -9,6 +9,7 @@
 
 use Drupal\views\Plugin\views\PluginBase;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
+use Drupal\views\Plugin\views\field\FieldPluginBase;
 use Drupal\views\Plugin\views\wizard\WizardInterface;
 use Drupal\Core\Annotation\Plugin;
 use Drupal\Core\Annotation\Translation;
@@ -227,6 +228,7 @@ function even_empty() {
   protected function defineOptions() {
     $options = parent::defineOptions();
     $options['grouping'] = array('default' => array());
+    $options['title_element_type'] = array('default' => 'h3');
     if ($this->usesRowClass()) {
       $options['row_class'] = array('default' => '');
       $options['default_row_class'] = array('default' => TRUE, 'bool' => TRUE);
@@ -295,6 +297,20 @@ public function buildOptionsForm(&$form, &$form_state) {
           );
         }
       }
+
+      $form['title_element_type'] = array(
+        '#title' => t('Title HTML Element'),
+        '#type' => 'select',
+        '#default_value' => $this->options['title_element_type'],
+        '#options' => FieldPluginBase::getHTMLElementTypes(),
+        '#description' => t('Choose the HTML element to wrap around the grouping title of a view.'),
+        // Hide the element unless at least one grouping field is configured.
+        '#states' => array(
+          'invisible' => array(
+            ':input[name="style_options[grouping][0][field]"]' => array('value' => ''),
+          ),
+        ),
+      );
     }
 
     if ($this->usesRowClass()) {
@@ -448,6 +464,7 @@ function render_grouping_sets($sets, $level = 0) {
           '#grouping_level' => $level,
           '#rows' => $set['rows'],
           '#title' => $set['group'],
+          '#title_element_type' => $this->options['title_element_type'],
         );
       }
       // Render as a record set.
@@ -468,6 +485,7 @@ function render_grouping_sets($sets, $level = 0) {
           '#grouping_level' => $level,
           '#rows' => $set['rows'],
           '#title' => $set['group'],
+          '#title_element_type' => $this->options['title_element_type'],
         );
       }
     }
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/style/Table.php b/core/modules/views/lib/Drupal/views/Plugin/views/style/Table.php
index 5a3e907..9d30dd7 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/style/Table.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/style/Table.php
@@ -71,6 +71,9 @@ protected function defineOptions() {
     $options['summary'] = array('default' => '', 'translatable' => TRUE);
     $options['empty_table'] = array('default' => FALSE, 'bool' => TRUE);
 
+    // Override to set the title to caption by default.
+    $options['title_element_type']['default'] = 'caption';
+
     return $options;
   }
 
diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/FieldWebTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/FieldWebTest.php
index 9d8c66e..0992e7a 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Handler/FieldWebTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Handler/FieldWebTest.php
@@ -397,7 +397,7 @@ public function testFieldClasses() {
     }
 
     // Tests the available html elements.
-    $element_types = $id_field->get_elements();
+    $element_types = $id_field->getHTMLElementTypes();
     $expected_elements = array(
       '',
       0,
diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/StyleTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/StyleTest.php
index cf065be..d0ee0be 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Plugin/StyleTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/StyleTest.php
@@ -335,6 +335,32 @@ function testCustomRowClasses() {
   }
 
   /**
+   * Tests the custom element type for group titles.
+   */
+  public function testCustomElementTitle() {
+    $style_plugins = array('default', 'grid', 'table', 'html_list');
+    $view = views_get_view('test_view');
+
+    foreach ($style_plugins as $plugin) {
+      $view->initDisplay();
+
+      $style = $view->display_handler->getOption('style');
+      $style['type'] = 'table';
+      $style['options']['title_element_type'] = 'h6';
+      $style['options']['grouping'] = 'name';
+      $view->display_handler->overrideOption('style', $style);
+
+      $view->initStyle();
+
+      $this->drupalSetContent($view->preview());
+      $elements = $this->xpath('//h6');
+      $this->assertEqual(count($elements), 5, format_string('Custom element tag found for @plugin plugin', array('@plugin' => $plugin)));
+
+      $view->destroy();
+    }
+  }
+
+  /**
    * Stores a view output in the elements.
    */
   protected function storeViewPreview($output) {
diff --git a/core/modules/views/templates/views-view-grid.tpl.php b/core/modules/views/templates/views-view-grid.tpl.php
index 43ba37b..85ff596 100644
--- a/core/modules/views/templates/views-view-grid.tpl.php
+++ b/core/modules/views/templates/views-view-grid.tpl.php
@@ -11,7 +11,7 @@
  */
 ?>
 <?php if (!empty($title)) : ?>
-  <h3><?php print $title; ?></h3>
+  <<?php print $title_element_type; ?>><?php print $title; ?></<?php print $title_element_type; ?>>
 <?php endif; ?>
 <table <?php print $attributes; ?>>
   <tbody>
diff --git a/core/modules/views/templates/views-view-list.tpl.php b/core/modules/views/templates/views-view-list.tpl.php
index 2c4b9c5..2122de3 100644
--- a/core/modules/views/templates/views-view-list.tpl.php
+++ b/core/modules/views/templates/views-view-list.tpl.php
@@ -11,7 +11,7 @@
 ?>
 <?php print $wrapper_prefix; ?>
   <?php if (!empty($title)) : ?>
-    <h3><?php print $title; ?></h3>
+    <<?php print $title_element_type; ?>><?php print $title; ?><<?php print $title_element_type; ?>/>
   <?php endif; ?>
   <?php print $list_type_prefix; ?>
     <?php foreach ($rows as $id => $row): ?>
diff --git a/core/modules/views/templates/views-view-table.tpl.php b/core/modules/views/templates/views-view-table.tpl.php
index 4d44d42..7d4abfe 100644
--- a/core/modules/views/templates/views-view-table.tpl.php
+++ b/core/modules/views/templates/views-view-table.tpl.php
@@ -17,11 +17,10 @@
  *   field id, then row number. This matches the index in $rows.
  * @ingroup views_templates
  */
-
 ?>
 <table <?php print $attributes; ?>>
   <?php if (!empty($title)) : ?>
-    <caption><?php print $title; ?></caption>
+    <<?php print $title_element_type; ?>><?php print $title; ?></<?php print $title_element_type; ?>>
   <?php endif; ?>
   <?php if (!empty($header)) : ?>
     <thead>
diff --git a/core/modules/views/templates/views-view-unformatted.tpl.php b/core/modules/views/templates/views-view-unformatted.tpl.php
index b7f63b9..2c05b38 100644
--- a/core/modules/views/templates/views-view-unformatted.tpl.php
+++ b/core/modules/views/templates/views-view-unformatted.tpl.php
@@ -8,7 +8,7 @@
  */
 ?>
 <?php if (!empty($title)): ?>
-  <h3><?php print $title; ?></h3>
+  <<?php print $title_element_type; ?>><?php print $title; ?></<?php print $title_element_type; ?>>
 <?php endif; ?>
 <?php foreach ($rows as $id => $row): ?>
   <div <?php print $row_classes[$id]; ?>>
diff --git a/core/modules/views/views.module b/core/modules/views/views.module
index 9de0ff2..f37f2ba 100644
--- a/core/modules/views/views.module
+++ b/core/modules/views/views.module
@@ -95,7 +95,7 @@ function views_theme($existing, $type, $theme, $path) {
     // $view is an object but the core contextual_preprocess() function only
     // attaches contextual links when the primary theme argument is an array.
     'display' => array('view_array' => array(), 'view' => NULL),
-    'style' => array('view' => NULL, 'options' => NULL, 'rows' => NULL, 'title' => NULL),
+    'style' => array('view' => NULL, 'options' => NULL, 'rows' => NULL, 'title' => NULL, 'title_element_type' => NULL),
     'row' => array('view' => NULL, 'options' => NULL, 'row' => NULL, 'field_alias' => NULL),
     'exposed_form' => array('view' => NULL, 'options' => NULL),
     'pager' => array(
