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 33b9ba6..f83a888 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
@@ -267,7 +267,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() {
+  static function get_elements() {
     static $elements = NULL;
     if (!isset($elements)) {
       // @todo Add possible html5 elements.
@@ -546,7 +546,7 @@ public function buildOptionsForm(&$form, &$form_state) {
     );
     $form['element_type'] = array(
       '#title' => t('HTML element'),
-      '#options' => $this->get_elements(),
+      '#options' => static::get_elements(),
       '#type' => 'select',
       '#default_value' => $this->options['element_type'],
       '#description' => t('Choose the HTML element to wrap around this field, e.g. H1, H2, etc.'),
@@ -591,7 +591,7 @@ public function buildOptionsForm(&$form, &$form_state) {
     );
     $form['element_label_type'] = array(
       '#title' => t('Label HTML element'),
-      '#options' => $this->get_elements(FALSE),
+      '#options' => static::get_elements(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.'),
@@ -635,7 +635,7 @@ public function buildOptionsForm(&$form, &$form_state) {
     );
     $form['element_wrapper_type'] = array(
       '#title' => t('Wrapper HTML element'),
-      '#options' => $this->get_elements(FALSE),
+      '#options' => static::get_elements(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 b8b2eaa..cb79797 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;
@@ -217,6 +218,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);
@@ -285,6 +287,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::get_elements(),
+        '#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()) {
@@ -438,6 +454,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.
@@ -458,6 +475,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/views.module b/core/modules/views/views.module
index c3b450a..527c2ca 100644
--- a/core/modules/views/views.module
+++ b/core/modules/views/views.module
@@ -100,7 +100,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(
