diff --git a/lib/Drupal/views/Plugin/views/style/Mapping.php b/lib/Drupal/views/Plugin/views/style/Mapping.php
new file mode 100644
index 0000000..00a5d8a
--- /dev/null
+++ b/lib/Drupal/views/Plugin/views/style/Mapping.php
@@ -0,0 +1,145 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\style\Mapping.
+ */
+
+namespace Drupal\views\Plugin\views\style;
+
+/**
+ * Allows fields to be mapped to specific use cases.
+ *
+ * @ingroup views_style_plugins
+ */
+abstract class Mapping extends StylePluginBase {
+
+  /**
+   * Do not use grouping.
+   *
+   * @var bool
+   */
+  protected $usesGrouping = FALSE;
+
+  /**
+   * Use fields without a row plugin.
+   *
+   * @var bool
+   */
+  protected $usesFields = TRUE;
+
+  /**
+   * Builds the list of field mappings.
+   *
+   * @return array
+   *   An associative array, keyed by the field name, containing the following
+   *   key-value pairs:
+   *   - #title: The human-readable label for this field.
+   *   - #default_value: The default value for this field. If not provided, an
+   *     empty string will be used.
+   *   - #description: A description of this field.
+   *   - #required: Whether this field is required.
+   *   - #filter: (optional) A method on the plugin to filter field options.
+   *   - #toggle: (optional) If this select should be toggled by a checkbox.
+   */
+  protected function defineMapping() {
+    return array();
+  }
+
+  /**
+   * Overrides Drupal\views\Plugin\views\style\StylePluginBase::defineOptions().
+   */
+  protected function defineOptions() {
+    $options = parent::defineOptions();
+
+    // Parse the mapping and add a default for each.
+    foreach ($this->defineMapping() as $key => $value) {
+      $default = !empty($value['#multiple']) ? array() : '';
+      $options['mapping']['contains'][$key] = array(
+        'default' => isset($value['#default_value']) ? $value['#default_value'] : $default,
+      );
+      if (!empty($value['#toggle'])) {
+        $options['mapping']['contains']["toggle_$key"] = array(
+          'default' => FALSE,
+          'bool' => TRUE,
+        );
+      }
+    }
+
+    return $options;
+  }
+
+  /**
+   * Overrides Drupal\views\Plugin\views\style\StylePluginBase::buildOptionsForm().
+   */
+  public function buildOptionsForm(&$form, &$form_state) {
+    parent::buildOptionsForm($form, $form_state);
+
+    // Get the mapping.
+    $mapping = $this->defineMapping();
+
+    // Restrict the list of defaults to the mapping, in case they have changed.
+    $options = array_intersect_key($this->options['mapping'], $mapping);
+
+    // Get the labels of the fields added to this display.
+    $field_labels = $this->display->handler->getFieldLabels();
+
+    // Provide some default values.
+    $defaults = array(
+      '#type' => 'select',
+      '#required' => FALSE,
+      '#multiple' => FALSE,
+    );
+
+    // For each mapping, add a select element to the form.
+    foreach ($options as $key => $value) {
+      // If the field is optional, add a 'None' value to the top of the options.
+      $field_options = array();
+      $required = !empty($mapping[$key]['#required']);
+      if (!$required && empty($mapping[$key]['#multiple'])) {
+        $field_options = array('' => t('- None -'));
+      }
+      $field_options += $field_labels;
+
+      // Optionally filter the available fields.
+      if (isset($mapping[$key]['#filter'])) {
+        $this->view->initHandlers();
+        $this::$mapping[$key]['#filter']($field_options);
+        unset($mapping[$key]['#filter']);
+      }
+
+      // These values must always be set.
+      $overrides = array(
+        '#options' => $field_options,
+        '#default_value' => $options[$key],
+      );
+
+      // Optionally allow the select to be toggleable.
+      if (!empty($mapping[$key]['#toggle'])) {
+        $form['mapping']["toggle_$key"] = array(
+          '#type' => 'checkbox',
+          '#title' => t('Use a custom %field_name', array('%field_name' => strtolower($mapping[$key]['#title']))),
+          '#default_value' => $this->options['mapping']["toggle_$key"],
+        );
+        $overrides['#states']['visible'][':input[name="style_options[mapping][' . "toggle_$key" . ']"]'] = array('checked' => TRUE);
+      }
+
+      $form['mapping'][$key] = $overrides + $mapping[$key] + $defaults;
+    }
+  }
+
+  /**
+   * Overrides Drupal\views\Plugin\views\style\StylePluginBase::render().
+   *
+   * Provides the mapping definition as an available variable.
+   */
+  function render() {
+    return theme($this->themeFunctions(), array(
+      'view' => $this->view,
+      'options' => $this->options,
+      'rows' => $this->view->result,
+      'mapping' => $this->defineMapping(),
+    ));
+  }
+
+}
diff --git a/lib/Drupal/views/Tests/Plugin/StyleMappingTest.php b/lib/Drupal/views/Tests/Plugin/StyleMappingTest.php
new file mode 100644
index 0000000..4de3ae5
--- /dev/null
+++ b/lib/Drupal/views/Tests/Plugin/StyleMappingTest.php
@@ -0,0 +1,86 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Tests\Plugin\StyleMappingTest.
+ */
+
+namespace Drupal\views\Tests\Plugin;
+
+/**
+ * Tests the default/mapping row style.
+ */
+class StyleMappingTest extends StyleTestBase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Style: Mapping',
+      'description' => 'Test mapping style functionality.',
+      'group' => 'Views Plugins',
+    );
+  }
+
+  /**
+   * Overrides Drupal\views\Tests\ViewTestBase::getBasicView().
+   */
+  protected function getBasicView() {
+    return $this->createViewFromConfig('test_style_mapping');
+  }
+
+  /**
+   * Verifies that the fields were mapped correctly.
+   */
+  public function testMappedOutput() {
+    $view = $this->getView();
+    $this->mappedOutputHelper($view);
+    $output = $this->mappedOutputHelper($view);
+    $this->assertTrue(strpos($output, 'job') === FALSE, 'The job field is added to the view but not in the mapping.');
+
+    $view = $this->getView();
+    $view->display['default']->handler->options['style_options']['mapping']['name_field'] = 'job';
+    $output = $this->mappedOutputHelper($view);
+    $this->assertTrue(strpos($output, 'job') !== FALSE, 'The job field is added to the view and is in the mapping.');
+  }
+
+  /**
+   * Tests the mapping of fields.
+   *
+   * @param Drupal\views\View $view
+   *   The view to test.
+   *
+   * @return string
+   *   The view rendered as HTML.
+   */
+  protected function mappedOutputHelper($view) {
+    $rendered_output = $view->preview();
+    $this->storeViewPreview($rendered_output);
+    $rows = $this->elements->body->div->div->div;
+    $data_set = $this->dataSet();
+
+    $count = 0;
+    foreach ($rows as $row) {
+      $attributes = $row->attributes();
+      $class = (string) $attributes['class'][0];
+      $this->assertTrue(strpos($class, 'views-row-mapping-test') !== FALSE, 'Make sure that each row has the correct CSS class.');
+
+      foreach ($row->div as $field) {
+        // Split up the field-level class, the first part is the mapping name
+        // and the second is the field ID.
+        $field_attributes = $field->attributes();
+        $name = strtok((string) $field_attributes['class'][0], '-');
+        $field_id = strtok('-');
+
+        // The expected result is the mapping name and the field value,
+        // separated by ':'.
+        $expected_result = $name . ':' . $data_set[$count][$field_id];
+        $actual_result = (string) $field;
+        $this->assertIdentical($expected_result, $actual_result, format_string('The fields were mapped successfully: %name => %field_id', array('%name' => $name, '%field_id' => $field_id)));
+      }
+
+      $count++;
+    }
+
+    return $rendered_output;
+  }
+
+}
diff --git a/lib/Drupal/views/Tests/Plugin/StyleTest.php b/lib/Drupal/views/Tests/Plugin/StyleTest.php
index 6e16e94..8e13650 100644
--- a/lib/Drupal/views/Tests/Plugin/StyleTest.php
+++ b/lib/Drupal/views/Tests/Plugin/StyleTest.php
@@ -8,12 +8,11 @@
 namespace Drupal\views\Tests\Plugin;
 
 use stdClass;
-use DOMDocument;
 
 /**
  * Tests some general style plugin related functionality.
  */
-class StyleTest extends PluginTestBase {
+class StyleTest extends StyleTestBase {
 
   public static function getInfo() {
     return array(
@@ -23,12 +22,6 @@ public static function getInfo() {
     );
   }
 
-  protected function setUp() {
-    parent::setUp();
-
-    $this->enableViewsTestModule();
-  }
-
   /**
    * Tests the grouping legacy features of styles.
    */
@@ -244,20 +237,6 @@ function _testGrouping($stripped = FALSE) {
     }
   }
 
-
-  /**
-   * Stores a view output in the elements.
-   */
-  function storeViewPreview($output) {
-    $htmlDom = new DOMDocument();
-    @$htmlDom->loadHTML($output);
-    if ($htmlDom) {
-      // It's much easier to work with simplexml than DOM, luckily enough
-      // we can just simply import our DOM tree.
-      $this->elements = simplexml_import_dom($htmlDom);
-    }
-  }
-
   /**
    * Tests custom css classes.
    */
diff --git a/lib/Drupal/views/Tests/Plugin/StyleTestBase.php b/lib/Drupal/views/Tests/Plugin/StyleTestBase.php
new file mode 100644
index 0000000..20fb2d6
--- /dev/null
+++ b/lib/Drupal/views/Tests/Plugin/StyleTestBase.php
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Tests\Plugin\StyleTestBase.
+ */
+
+namespace Drupal\views\Tests\Plugin;
+
+use DOMDocument;
+
+/**
+ * Tests some general style plugin related functionality.
+ */
+abstract class StyleTestBase extends PluginTestBase {
+
+  /**
+   * Stores the SimpleXML representation of the output.
+   *
+   * @var SimpleXMLElement
+   */
+  protected $elements;
+
+  protected function setUp() {
+    parent::setUp();
+
+    $this->enableViewsTestModule();
+  }
+
+  /**
+   * Stores a view output in the elements.
+   */
+  function storeViewPreview($output) {
+    $htmlDom = new DOMDocument();
+    @$htmlDom->loadHTML($output);
+    if ($htmlDom) {
+      // It's much easier to work with simplexml than DOM, luckily enough
+      // we can just simply import our DOM tree.
+      $this->elements = simplexml_import_dom($htmlDom);
+    }
+  }
+
+}
diff --git a/lib/Drupal/views/Tests/Plugin/StyleUnformattedTest.php b/lib/Drupal/views/Tests/Plugin/StyleUnformattedTest.php
index b937825..bcc2e8c 100644
--- a/lib/Drupal/views/Tests/Plugin/StyleUnformattedTest.php
+++ b/lib/Drupal/views/Tests/Plugin/StyleUnformattedTest.php
@@ -7,19 +7,10 @@
 
 namespace Drupal\views\Tests\Plugin;
 
-use DOMDocument;
-
 /**
  * Tests the default/unformatted row style.
  */
-class StyleUnformattedTest extends PluginTestBase {
-
-  /**
-   * Stores all created nodes.
-   *
-   * @var array
-   */
-  protected $nodes;
+class StyleUnformattedTest extends StyleTestBase {
 
   public static function getInfo() {
     return array(
@@ -29,25 +20,6 @@ public static function getInfo() {
     );
   }
 
-  protected function setUp() {
-    parent::setUp();
-
-    $this->enableViewsTestModule();
-  }
-
-  /**
-   * Stores a view output in the elements.
-   */
-  function storeViewPreview($output) {
-    $htmlDom = new DOMDocument();
-    @$htmlDom->loadHTML($output);
-    if ($htmlDom) {
-      // It's much easier to work with simplexml than DOM, luckily enough
-      // we can just simply import our DOM tree.
-      $this->elements = simplexml_import_dom($htmlDom);
-    }
-  }
-
   /**
    * Take sure that the default css classes works as expected.
    */
diff --git a/tests/views_test_config/config/views.view.test_style_mapping.yml b/tests/views_test_config/config/views.view.test_style_mapping.yml
new file mode 100644
index 0000000..47223ac
--- /dev/null
+++ b/tests/views_test_config/config/views.view.test_style_mapping.yml
@@ -0,0 +1,57 @@
+api_version: '3.0'
+base_table: views_test
+core: '8'
+description: ''
+disabled: '0'
+display:
+    default:
+        display_options:
+            defaults:
+                fields: '0'
+                pager: '0'
+                pager_options: '0'
+                sorts: '0'
+            fields:
+                age:
+                    field: age
+                    id: age
+                    relationship: none
+                    table: views_test
+                job:
+                    field: job
+                    id: job
+                    relationship: none
+                    table: views_test
+                name:
+                    field: name
+                    id: name
+                    relationship: none
+                    table: views_test
+            pager:
+                options:
+                    offset: '0'
+                type: none
+            pager_options: {  }
+            sorts:
+                id:
+                    field: id
+                    id: id
+                    order: ASC
+                    relationship: none
+                    table: views_test
+            style_options:
+                mapping:
+                    name_field: name
+                    numeric_field:
+                        age: age
+                    title_field: name
+                    toggle_numeric_field: '1'
+                    toggle_title_field: '1'
+            style_plugin: mapping_test
+        display_plugin: default
+        display_title: Master
+        id: default
+        position: '0'
+human_name: ''
+name: test_style_mapping
+tag: ''
diff --git a/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/style/MappingTest.php b/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/style/MappingTest.php
new file mode 100644
index 0000000..1cb18c7
--- /dev/null
+++ b/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/style/MappingTest.php
@@ -0,0 +1,70 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views_test_data\Plugin\views\style\MappingTest;
+ */
+
+namespace Drupal\views_test_data\Plugin\views\style;
+
+use Drupal\views\Plugin\views\style\Mapping;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Drupal\views\Plugin\views\field\Numeric;
+
+/**
+ * @todo.
+ *
+ * @ingroup views_style_plugins
+ *
+ * @Plugin(
+ *   id = "mapping_test",
+ *   title = @Translation("Field mapping"),
+ *   help = @Translation("Maps specific fields to specific purposes."),
+ *   theme = "views_view_mapping_test",
+ *   type = "normal"
+ * )
+ */
+class MappingTest extends Mapping {
+
+  /**
+   * Overrides Drupal\views\Plugin\views\style\Mapping::defineMapping().
+   */
+  protected function defineMapping() {
+    return array(
+      'title_field' => array(
+        '#title' => t('Title field'),
+        '#description' => t('Choose the field with the custom title.'),
+        '#toggle' => TRUE,
+        '#required' => TRUE,
+      ),
+      'name_field' => array(
+        '#title' => t('Name field'),
+        '#description' => t('Choose the field with the custom name.'),
+      ),
+      'numeric_field' => array(
+        '#title' => t('Numeric field'),
+        '#description' => t('Select one or more numeric fields.'),
+        '#multiple' => TRUE,
+        '#toggle' => TRUE,
+        '#filter' => 'allowNumericFields',
+        '#required' => TRUE,
+      ),
+    );
+  }
+
+  /**
+   * Restricts the allowed fields to only numeric fields.
+   *
+   * @param array $fields
+   *   An array of field labels, keyed by the field ID.
+   */
+  protected function allowNumericFields(&$fields) {
+    foreach ($this->view->field as $id => $field) {
+      if (!($field instanceof Numeric)) {
+        unset($fields[$id]);
+      }
+    }
+  }
+
+}
diff --git a/tests/views_test_data/views_test_data.module b/tests/views_test_data/views_test_data.module
index 3583229..5e7b4f5 100644
--- a/tests/views_test_data/views_test_data.module
+++ b/tests/views_test_data/views_test_data.module
@@ -68,3 +68,59 @@ function views_test_data_views_post_build(View &$view) {
     }
   }
 }
+
+/**
+ * Implements hook_preprocess_HOOK() for theme_views_view_mapping_test().
+ */
+function template_preprocess_views_view_mapping_test(&$variables) {
+  $variables['element'] = array();
+
+  foreach ($variables['rows'] as $delta => $row) {
+    $fields = array();
+    foreach ($variables['options']['mapping'] as $type => $field_names) {
+      if (!is_array($field_names)) {
+        $field_names = array($field_names);
+      }
+      foreach ($field_names as $field_name) {
+        if ($value = $variables['view']->style_plugin->get_field($delta, $field_name)) {
+          $fields[$type . '-' . $field_name] = $type . ':' . $value;
+        }
+      }
+    }
+
+    // If there are no fields in this row, skip to the next one.
+    if (empty($fields)) {
+      continue;
+    }
+
+    // Build a container for the row.
+    $variables['element'][$delta] = array(
+      '#type' => 'container',
+      '#attributes' => array(
+        'class' => array(
+          'views-row-mapping-test',
+        ),
+      ),
+    );
+
+    // Add each field to the row.
+    foreach ($fields as $key => $render) {
+      $variables['element'][$delta][$key] = array(
+        '#children' => $render,
+        '#type' => 'container',
+        '#attributes' => array(
+          'class' => array(
+            $key,
+          ),
+        ),
+      );
+    }
+  }
+}
+
+/**
+ * Returns HTML for the Mapping Test style.
+ */
+function theme_views_view_mapping_test($variables) {
+  return drupal_render($variables['element']);
+}
