diff --git a/core/modules/views/src/Plugin/views/PluginBase.php b/core/modules/views/src/Plugin/views/PluginBase.php
index e523eba..752f0b8 100644
--- a/core/modules/views/src/Plugin/views/PluginBase.php
+++ b/core/modules/views/src/Plugin/views/PluginBase.php
@@ -192,26 +192,6 @@ public function filterByDefinedOptions(array &$storage) {
   }
 
   /**
-   * Do the work to filter out stored options depending on the defined options.
-   *
-   * @param array $storage
-   *   The stored options.
-   * @param array $options
-   *   The defined options.
-   */
-  protected function doFilterByDefinedOptions(array &$storage, array $options) {
-    foreach ($storage as $key => $sub_storage) {
-      if (!isset($options[$key])) {
-        unset($storage[$key]);
-      }
-
-      if (isset($options[$key]['contains'])) {
-        $this->doFilterByDefinedOptions($storage[$key], $options[$key]['contains']);
-      }
-    }
-  }
-
-  /**
    * {@inheritdoc}
    */
   public function unpackOptions(&$storage, $options, $definition = NULL, $all = TRUE, $check = TRUE) {
@@ -258,6 +238,26 @@ public function destroy() {
   }
 
   /**
+   * Do the work to filter out stored options depending on the defined options.
+   *
+   * @param array $storage
+   *   The stored options.
+   * @param array $options
+   *   The defined options.
+   */
+  protected function doFilterByDefinedOptions(array &$storage, array $options) {
+    foreach ($storage as $key => $sub_storage) {
+      if (!isset($options[$key])) {
+        unset($storage[$key]);
+      }
+
+      if (isset($options[$key]['contains'])) {
+        $this->doFilterByDefinedOptions($storage[$key], $options[$key]['contains']);
+      }
+    }
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function buildOptionsForm(&$form, FormStateInterface $form_state) {
diff --git a/core/modules/views/src/Plugin/views/style/Table.php b/core/modules/views/src/Plugin/views/style/Table.php
index 00e28b3..feece90 100644
--- a/core/modules/views/src/Plugin/views/style/Table.php
+++ b/core/modules/views/src/Plugin/views/style/Table.php
@@ -68,6 +68,7 @@ protected function defineOptions() {
     $options['columns'] = ['default' => []];
     $options['default'] = ['default' => ''];
     $options['info'] = ['default' => []];
+    $options['row_header'] = ['default' => ''];
     $options['override'] = ['default' => TRUE];
     $options['sticky'] = ['default' => FALSE];
     $options['order'] = ['default' => 'asc'];
@@ -273,6 +274,11 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) {
       $default = -1;
     }
 
+    $row_header = -1;
+    if (isset($this->options['row_header']) && isset($columns[$this->options['row_header']])) {
+      $row_header = $this->options['row_header'];
+    }
+
     foreach ($columns as $field => $column) {
       $column_selector = ':input[name="style_options[columns][' . $field . ']"]';
 
@@ -327,6 +333,23 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) {
           ],
         ];
       }
+      $row_header_id = Html::getUniqueId('edit-row-header-' . $field);
+      $form['row_header'][$field] = [
+        '#title' => $this->t('Row header for @field', ['@field' => $field]),
+        '#title_display' => 'invisible',
+        '#type' => 'radio',
+        '#return_value' => $field,
+        '#parents' => ['style_options', 'row_header'],
+        '#id' => $row_header_id,
+        // Because 'radio' doesn't fully support '#id' =(
+        '#attributes' => ['id' => $row_header_id],
+        '#default_value' => $row_header,
+        '#states' => [
+          'visible' => [
+            $column_selector => ['value' => $field],
+          ],
+        ],
+      ];
       $form['info'][$field]['align'] = [
         '#title' => $this->t('Alignment for @field', ['@field' => $field]),
         '#title_display' => 'invisible',
@@ -397,6 +420,17 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) {
       '#default_value' => $default,
     ];
 
+    // Provide a radio for no row header
+    $form['row_header'][-1] = [
+      '#title' => $this->t('No row header'),
+      '#title_display' => 'invisible',
+      '#type' => 'radio',
+      '#return_value' => -1,
+      '#parents' => ['style_options', 'row_header'],
+      '#id' => 'edit-row_header-0',
+      '#default_value' => $row_header,
+    ];
+
     $form['empty_table'] = [
       '#type' => 'checkbox',
       '#title' => $this->t('Show the empty text in the table'),
diff --git a/core/modules/views/templates/views-view-table.html.twig b/core/modules/views/templates/views-view-table.html.twig
index 0c0b445..e2e9e89 100644
--- a/core/modules/views/templates/views-view-table.html.twig
+++ b/core/modules/views/templates/views-view-table.html.twig
@@ -26,6 +26,7 @@
  *     used.
  * - responsive: A flag indicating whether table is responsive.
  * - sticky: A flag indicating whether table header is sticky.
+ * - row_header: The field that should be marked up as a table header on each row.
  *
  * @see template_preprocess_views_view_table()
  *
@@ -71,7 +72,7 @@
               ]
             %}
           {% endif %}
-          <th{{ column.attributes.addClass(column_classes).setAttribute('scope', 'col') }}>
+          <th {{ column.attributes.addClass(column_classes).setAttribute('scope', 'col') }}>
             {%- if column.wrapper_element -%}
               <{{ column.wrapper_element }}>
                 {%- if column.url -%}
@@ -94,7 +95,7 @@
   {% endif %}
   <tbody>
     {% for row in rows %}
-      <tr{{ row.attributes }}>
+      <tr {{ row.attributes }}>
         {% for key, column in row.columns %}
           {% if column.default_classes %}
             {%
@@ -106,7 +107,11 @@
               {% set column_classes = column_classes|merge(['views-field-' ~ field]) %}
             {% endfor %}
           {% endif %}
-          <td{{ column.attributes.addClass(column_classes) }}>
+          {% if (key) == (row_header) %}
+            <th scope="row" {{ column.attributes.addClass(column_classes) }}>
+          {% else %}
+            <td {{ column.attributes.addClass(column_classes) }}>
+          {% endif %}
             {%- if column.wrapper_element -%}
               <{{ column.wrapper_element }}>
               {% for content in column.content %}
@@ -118,7 +123,11 @@
                 {{- content.separator }}{{ content.field_output -}}
               {% endfor %}
             {%- endif %}
-          </td>
+          {% if (key) == (row_header) %}
+            </th>
+          {% else %}
+            </td>
+          {% endif %}
         {% endfor %}
       </tr>
     {% endfor %}
diff --git a/core/modules/views/views.theme.inc b/core/modules/views/views.theme.inc
index 4866d1c..5c6debc 100644
--- a/core/modules/views/views.theme.inc
+++ b/core/modules/views/views.theme.inc
@@ -660,6 +660,8 @@ function template_preprocess_views_view_table(&$variables) {
     // This is needed to target tables constructed by this function.
     $variables['responsive'] = TRUE;
   }
+
+  $variables['row_header'] = $handler->options['row_header'];
 }
 
 /**
diff --git a/core/modules/views_ui/views_ui.theme.inc b/core/modules/views_ui/views_ui.theme.inc
index e6ef875..8c559da 100644
--- a/core/modules/views_ui/views_ui.theme.inc
+++ b/core/modules/views_ui/views_ui.theme.inc
@@ -415,6 +415,10 @@ function template_preprocess_views_ui_style_plugin_table(&$variables) {
       'align' => 'center',
     ],
     [
+      'data' => t('Row Header'),
+      'align' => 'center',
+    ],
+    [
       'data' => t('Hide empty column'),
       'align' => 'center',
     ],
@@ -450,6 +454,7 @@ function template_preprocess_views_ui_style_plugin_table(&$variables) {
       $row[] = '';
       $row[] = '';
     }
+    $row[]['data'] = $form['row_header'][$id];
     $row[] = [
       'data' => $form['info'][$id]['empty_column'],
       'align' => 'center',
@@ -462,13 +467,20 @@ function template_preprocess_views_ui_style_plugin_table(&$variables) {
   }
 
   // Add the special 'None' row.
-  $rows[] = [['data' => t('None'), 'colspan' => 6], ['align' => 'center', 'data' => $form['default'][-1]], ['colspan' => 2]];
+  $rows[] = [
+    array('data' => t('None'), 'colspan' => 2),
+    array('colspan' => 4),
+    array('align' => 'center', 'data' => $form['default'][-1]),
+    array('align' => 'center', 'data' => $form['row_header'][-1]),
+    array('colspan' => 2),
+  ];
 
   // Unset elements from the form array that are used to build the table so that
   // they are not rendered twice.
   unset($form['default']);
   unset($form['info']);
   unset($form['columns']);
+  unset($form['row_header']);
 
   $variables['table'] = [
     '#type' => 'table',
diff --git a/core/themes/classy/templates/views/views-view-table.html.twig b/core/themes/classy/templates/views/views-view-table.html.twig
index 8eccec0..f1fe1d5 100644
--- a/core/themes/classy/templates/views/views-view-table.html.twig
+++ b/core/themes/classy/templates/views/views-view-table.html.twig
@@ -26,6 +26,7 @@
  *     used.
  * - responsive: A flag indicating whether table is responsive.
  * - sticky: A flag indicating whether table header is sticky.
+ * - row_header: The field that should be marked up as a table header on each row.
  *
  * @see template_preprocess_views_view_table()
  */
@@ -106,7 +107,11 @@
               {% set column_classes = column_classes|merge(['views-field-' ~ field]) %}
             {% endfor %}
           {% endif %}
-          <td{{ column.attributes.addClass(column_classes) }}>
+          {% if (key) == (row_header) %}
+            <th scope="row" {{ column.attributes.addClass(column_classes) }}>
+          {% else %}
+            <td {{ column.attributes.addClass(column_classes) }}>
+          {% endif %}
             {%- if column.wrapper_element -%}
               <{{ column.wrapper_element }}>
               {% for content in column.content %}
@@ -118,7 +123,11 @@
                 {{- content.separator }}{{ content.field_output -}}
               {% endfor %}
             {%- endif %}
-          </td>
+          {% if (key) == (row_header) %}
+            </th>
+          {% else %}
+            </td>
+          {% endif %}
         {% endfor %}
       </tr>
     {% endfor %}
