diff --git a/core/modules/layout/layout.info b/core/modules/layout/layout.info
new file mode 100644
index 00000000000..187bfea6e0b
--- /dev/null
+++ b/core/modules/layout/layout.info
@@ -0,0 +1,5 @@
+name = Layout
+description = Makes it possible to swap different page layouts.
+package = Core
+version = VERSION
+core = 8.x
diff --git a/core/modules/layout/layout.module b/core/modules/layout/layout.module
new file mode 100644
index 00000000000..7d8279826c9
--- /dev/null
+++ b/core/modules/layout/layout.module
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * @file
+ * Manages page layouts for content presentation.
+ */
+
+/**
+ * Get the layout plugin manager instance.
+ *
+ * @return Drupal\layout\Plugin\Type\LayoutManager
+ *   The layout plugin manager instance.
+ */
+function layout_manager() {
+  return drupal_container()->get('plugin.manager.layout');
+}
+
+/**
+ * Implements hook_theme().
+ *
+ * Expose all layouts as theme items, so themes can override layout markup.
+ */
+function layout_theme($existing, $type, $theme, $path) {
+  $items = array();
+  foreach (layout_manager()->getDefinitions() as $name => $layout) {
+    $items[$layout['theme']] = array(
+      'variables' => array('content' => NULL),
+      'path' => $layout['path'],
+      'template' => $layout['template'],
+    );
+  }
+  return $items;
+}
diff --git a/core/modules/layout/lib/Drupal/layout/LayoutBundle.php b/core/modules/layout/lib/Drupal/layout/LayoutBundle.php
new file mode 100644
index 00000000000..59b8513027e
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/LayoutBundle.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\layout\LayoutBundle.
+ */
+
+namespace Drupal\Layout;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\HttpKernel\Bundle\Bundle;
+
+/**
+ * Layout dependency injection container.
+ */
+class LayoutBundle extends Bundle {
+
+  /**
+   * Overrides Symfony\Component\HttpKernel\Bundle\Bundle::build().
+   */
+  public function build(ContainerBuilder $container) {
+    // Register the LayoutManager class with the dependency injection container.
+    $container->register('plugin.manager.layout', 'Drupal\layout\Plugin\Type\LayoutManager');
+  }
+}
diff --git a/core/modules/layout/lib/Drupal/layout/Plugin/Derivative/Layout.php b/core/modules/layout/lib/Drupal/layout/Plugin/Derivative/Layout.php
new file mode 100644
index 00000000000..ce82c655a31
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Plugin/Derivative/Layout.php
@@ -0,0 +1,117 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\layout\Plugin\Derivative\Layout.
+ */
+
+namespace Drupal\layout\Plugin\Derivative;
+
+use DirectoryIterator;
+use Drupal\Component\Plugin\Derivative\DerivativeInterface;
+use Drupal\Core\Config\FileStorage;
+
+/**
+ * Layout plugin derivative definition.
+ */
+class Layout implements DerivativeInterface {
+
+  /**
+   * List of derivatives.
+   *
+   * Associative array keyed by 'provider__layoutname' where provider is the
+   * module or theme name and layoutname is the .yml filename, such as
+   * 'bartik__page' or 'layout__one-col'. The values of the array are
+   * associative arrays themselves with metadata about the layout such as
+   * 'template', 'css', 'admin css' and so on.
+   *
+   * @var array
+   */
+  protected $derivatives = array();
+
+  /**
+   * Layout derivative type.
+   *
+   * Defines the subdirectory under ./layout where layout metadata is loooked
+   * for. Overriding implementations should change this to look for other
+   * types in a different subdirectory.
+   *
+   * @var string
+   */
+  protected $type = 'static';
+
+  /**
+   * Implements DerivativeInterface::getDerivativeDefinition().
+   */
+  public function getDerivativeDefinition($derivative_id, array $base_plugin_definition) {
+    if (!empty($this->derivatives) && !empty($this->derivatives[$derivative_id])) {
+      return $this->derivatives[$derivative_id];
+    }
+    $this->getDerivativeDefinitions($base_plugin_definition);
+    return $this->derivatives[$derivative_id];
+  }
+
+  /**
+   * Implements DerivativeInterface::getDerivativeDefinitions().
+   */
+  public function getDerivativeDefinitions(array $base_plugin_definition) {
+    $available_layout_providers = array();
+
+    // Add all modules as possible layout providers.
+    foreach (module_list() as $module) {
+      $available_layout_providers[$module] = array(
+        'type' => 'module',
+        'provider' => $module,
+        'dir' => drupal_get_path('module', $module),
+      );
+    }
+
+    // Add all themes as possible layout providers.
+    foreach (list_themes() as $theme_id => $theme) {
+      $available_layout_providers[$theme_id] = array(
+        'type' => 'theme',
+        'provider' => $theme->name,
+        'dir' => drupal_get_path('theme', $theme->name),
+      );
+    }
+
+    foreach ($available_layout_providers as $provider) {
+      // Looks for layouts in the 'layout' directory under the module/theme.
+      // There could be subdirectories under there with one layout defined
+      // in each.
+      $dir = $provider['dir'] . DIRECTORY_SEPARATOR . 'layouts' . DIRECTORY_SEPARATOR . $this->type;
+      if (file_exists($dir)) {
+        $this->iterateDirectories($dir, $provider);
+      }
+    }
+    return $this->derivatives;
+  }
+
+  /**
+   * Finds layout definitions by looking for layout metadata.
+   */
+  protected function iterateDirectories($dir, $provider) {
+    $directories = new DirectoryIterator($dir);
+    foreach ($directories as $fileinfo) {
+      if ($fileinfo->isDir() && !$fileinfo->isDot()) {
+        // Keep discovering in subdirectories to arbitrary depth.
+        $this->iterateDirectories($fileinfo->getPathname(), $provider);
+      }
+      elseif ($fileinfo->isFile() && pathinfo($fileinfo->getFilename(), PATHINFO_EXTENSION) == 'yml') {
+        // Declarative layout definitions are defined with a .yml file in a
+        // layout subdirectory. This provides all information about the layout
+        // such as layout markup template and CSS and JavaScript files to use.
+        $directory = new FileStorage($fileinfo->getPath());
+        $key = $provider['provider'] . '__' .  $fileinfo->getBasename('.yml');
+        $this->derivatives[$key] = $directory->read($fileinfo->getBasename('.yml'));
+        $this->derivatives[$key]['theme'] = $key;
+        $this->derivatives[$key]['path'] = $fileinfo->getPath();
+        // If the layout author didn't specify a template name, assume the same
+        // name as the yml file.
+        if (!isset($this->derivatives[$key]['template'])) {
+          $this->derivatives[$key]['template'] = $fileinfo->getBasename('.yml');
+        }
+      }
+    }
+  }
+}
diff --git a/core/modules/layout/lib/Drupal/layout/Plugin/LayoutInterface.php b/core/modules/layout/lib/Drupal/layout/Plugin/LayoutInterface.php
new file mode 100644
index 00000000000..5b874f6f4d4
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Plugin/LayoutInterface.php
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\layout\Plugin\LayoutInterface.
+ */
+
+namespace Drupal\layout\Plugin;
+
+/**
+ * Defines the shared interface for all layout plugins.
+ */
+interface LayoutInterface {
+
+  /**
+   * Returns a list of regions.
+   *
+   * @return array
+   *   An array of region machine names.
+   */
+  public function getRegions();
+
+  /**
+   * Renders layout and returns the rendered markup.
+   *
+   * @return string
+   *   Rendered HTML output from the layout.
+   */
+  public function renderLayout();
+}
diff --git a/core/modules/layout/lib/Drupal/layout/Plugin/Type/LayoutManager.php b/core/modules/layout/lib/Drupal/layout/Plugin/Type/LayoutManager.php
new file mode 100644
index 00000000000..e58ad4ba19e
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Plugin/Type/LayoutManager.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\layout\Plugin\Type\LayoutManager.
+ */
+
+namespace Drupal\layout\Plugin\Type;
+
+use Drupal\Component\Plugin\PluginManagerBase;
+use Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator;
+use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
+use Drupal\Component\Plugin\Factory\ReflectionFactory;
+
+/**
+ * Layout plugin manager.
+ */
+class LayoutManager extends PluginManagerBase {
+
+  protected $defaults = array(
+    'class' => 'Drupal\layout\Plugin\layout\layout\StaticLayout',
+  );
+
+  /**
+   * Overrides Drupal\Component\Plugin\PluginManagerBase::__construct().
+   */
+  public function __construct() {
+    // Create layout plugin derivatives from declaratively defined layouts.
+    $this->discovery = new DerivativeDiscoveryDecorator(new AnnotatedClassDiscovery('layout', 'layout'));
+    $this->factory = new ReflectionFactory($this);
+  }
+}
diff --git a/core/modules/layout/lib/Drupal/layout/Plugin/layout/layout/StaticLayout.php b/core/modules/layout/lib/Drupal/layout/Plugin/layout/layout/StaticLayout.php
new file mode 100644
index 00000000000..4819595a521
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Plugin/layout/layout/StaticLayout.php
@@ -0,0 +1,121 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\layout\Plugin\layout\layout\StaticLayout.
+ */
+
+namespace Drupal\layout\Plugin\layout\layout;
+
+use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
+use Drupal\layout\Plugin\LayoutInterface;
+use Drupal\Component\Plugin\PluginBase;
+use Drupal\Core\Annotation\Plugin;
+
+/**
+ * @Plugin(
+ *  id = "static_layout",
+ *  derivative = "Drupal\layout\Plugin\Derivative\Layout"
+ * )
+ */
+class StaticLayout extends PluginBase implements LayoutInterface {
+
+  /**
+   * Overrides Drupal\Component\Plugin\PluginBase::__construct().
+   */
+  public function __construct(array $configuration, $plugin_id, DiscoveryInterface $discovery) {
+    // Get definition by discovering the declarative information.
+    $definition = $discovery->getDefinition($plugin_id);
+    foreach ($definition['regions'] as $region => $title) {
+      if (!isset($configuration['regions'][$region])) {
+        $configuration['regions'][$region] = array();
+      }
+    }
+    parent::__construct($configuration, $plugin_id, $discovery);
+  }
+
+  /**
+   * Implements Drupal\layout\Plugin\LayoutInterface::getRegions().
+   */
+  public function getRegions() {
+    $definition = $this->getDefinition();
+    return $definition['regions'];
+  }
+
+  /**
+   * Returns the list of CSS files associated with this layout.
+   */
+  public function getStylesheetFiles() {
+    $definition = $this->getDefinition();
+    return isset($definition['stylesheets']) ? $definition['stylesheets'] : array();
+  }
+
+  /**
+   * Returns the list of administrative CSS files associated with this layout.
+   */
+  public function getAdminStylesheetFiles() {
+    $definition = $this->getDefinition();
+    // Fall back on regular CSS for the admin page if admin CSS not provided.
+    return isset($definition['admin stylesheets']) ? $definition['admin stylesheets'] : $this->getStylesheetFiles();
+  }
+
+  /**
+   * Returns the list of JS files associated with this layout.
+   */
+  public function getScriptFiles() {
+    $definition = $this->getDefinition();
+    return isset($definition['scripts']) ? $definition['scripts'] : array();
+  }
+
+  /**
+   * Returns the list of administrative JS files associated with this layout.
+   */
+  public function getAdminScriptFiles() {
+    $definition = $this->getDefinition();
+    return isset($definition['admin scripts']) ? $definition['admin scripts'] : $this->getScriptFiles();
+  }
+
+  /**
+   * Implements Drupal\layout\Plugin\LayoutInterface::renderLayout().
+   */
+  public function renderLayout($admin = FALSE) {
+    $definition = $this->getDefinition();
+
+    // Assemble a render array with the regions and attached CSS/JS.
+    $build = array(
+      '#theme' => $definition['theme'],
+      '#content' => array(),
+    );
+
+    // Render all regions needed for this layout.
+    foreach ($this->getRegions() as $region => $title) {
+      // @todo This is just stub code to fill in regions with stuff for now.
+      // When blocks are related to layouts and not themes, we can make this
+      // really be filled in with blocks.
+      $build['#content'][$region] = '<h3>' . $title . '</h3>';
+    }
+
+    // Fill in attached CSS and JS files based on metadata.
+    if (!$admin) {
+      $build['#attached'] = array(
+        'css' => $this->getStylesheetFiles(),
+        'js' => $this->getScriptFiles(),
+      );
+    }
+    else {
+      $build['#attached'] = array(
+        'css' => $this->getAdminStylesheetFiles(),
+        'js' => $this->getAdminScriptFiles(),
+      );
+    }
+
+    // Include the path of the definition in all CSS and JS files.
+    foreach (array('css', 'js') as $type) {
+      foreach ($build['#attached'][$type] as &$filename) {
+        $filename = $definition['path'] . '/' . $filename;
+      }
+    }
+
+    return drupal_render($build);
+  }
+}
diff --git a/core/modules/layout/lib/Drupal/layout/Tests/LayoutDerivativesTest.php b/core/modules/layout/lib/Drupal/layout/Tests/LayoutDerivativesTest.php
new file mode 100644
index 00000000000..1071713eb5c
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Tests/LayoutDerivativesTest.php
@@ -0,0 +1,91 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\layout\Tests\LayoutDerivativesTest.
+ */
+
+namespace Drupal\layout\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Tests the layout system derivatives.
+ */
+class LayoutDerivativesTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('layout', 'layout_test');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Layout derivatives',
+      'description' => 'Tests layout derivatives discovery.',
+      'group' => 'Layout',
+    );
+  }
+
+  /**
+   * Tests for module/theme layout derivatives.
+   */
+  function testDerivatives() {
+    $manager = drupal_container()->get('plugin.manager.layout');
+
+    $definitions = $manager->getDefinitions();
+    $this->assertTrue(is_array($definitions), 'Definitions found.');
+    $this->assertTrue(count($definitions) == 2, 'Two definitions available.');
+    $this->assertTrue(isset($definitions['static_layout:layout_test__one-col']), 'One column layout found.');
+    $this->assertTrue(isset($definitions['static_layout:layout_test_theme__two-col']), 'Two column layout found.');
+
+    // Get a one column layout instance. This is defined under the layout_test
+    // module.
+    $layout = $manager->createInstance('static_layout:layout_test__one-col', array());
+    // Verify the expected regions are properly available.
+    $regions = $layout->getRegions();
+    $this->assertTrue(is_array($regions), 'Regions array present.');
+    $this->assertTrue(count($regions) == 1, 'One region defined.');
+    $this->assertTrue(isset($regions['middle']), 'Middle region found.');
+
+    // Render the layout and look at whether expected region names and classes
+    // were in the output.
+    $render = $layout->renderLayout();
+    $this->drupalSetContent($render);
+    $this->assertText('Middle column');
+    $this->assertRaw('class="layout-display layout-one-col');
+
+    // Get the two column page layout defined by the layout test theme.
+    $layout = $manager->createInstance('static_layout:layout_test_theme__two-col', array());
+    // Verify the expected regions are properly available.
+    $regions = $layout->getRegions();
+    $this->assertTrue(is_array($regions), 'Regions array present.');
+    $this->assertTrue(count($regions) == 2, 'Two regions defined.');
+    $this->assertTrue(isset($regions['left']), 'Left region found.');
+    $this->assertTrue(isset($regions['right']), 'Right region found.');
+
+    // Render the layout and look at whether expected region names and classes
+    // were in the output.
+    $render = $layout->renderLayout();
+    $this->drupalSetContent($render);
+    $this->assertText('Left side');
+    $this->assertText('Right side');
+    $this->assertRaw('<div class="layout-region layout-col-right">');
+  }
+
+  /**
+   * Test layout functionality as applies to pages.
+   */
+  function testPageLayout() {
+    // The layout-test page uses the layout_test_theme page layout.
+    $this->drupalGet('layout-test');
+    $this->assertText('Left side');
+    $this->assertText('Right side');
+    $this->assertRaw('<div class="layout-region layout-col-right">');
+
+    // Ensure the CSS was added.
+    $this->assertRaw('@import url("' . url('', array('absolute' => TRUE)) . drupal_get_path('theme', 'layout_test_theme') . '/layouts/static/two-col/two-col.css');
+  }
+}
diff --git a/core/modules/layout/tests/layout_test.info b/core/modules/layout/tests/layout_test.info
new file mode 100644
index 00000000000..7b054a90ed5
--- /dev/null
+++ b/core/modules/layout/tests/layout_test.info
@@ -0,0 +1,6 @@
+name = Layout test
+description = Helps with testing layouts.
+package = Testing
+version = VERSION
+core = 8.x
+hidden = TRUE
diff --git a/core/modules/layout/tests/layout_test.module b/core/modules/layout/tests/layout_test.module
new file mode 100644
index 00000000000..36c3915f205
--- /dev/null
+++ b/core/modules/layout/tests/layout_test.module
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * @file
+ * Layout testing module.
+ */
+
+/**
+ * Implementation of hook_menu().
+ */
+function layout_test_menu() {
+  $items['layout-test'] = array(
+    'title' => 'Layout test',
+    'page callback' => 'layout_test_page',
+    'access callback' => TRUE,
+  );
+  return $items;
+}
+
+/**
+ * Page callback for layout testing.
+ */
+function layout_test_page() {
+  // Hack to enable and apply the theme to this page and manually invoke its
+  // layout plugin and render it.
+  global $theme;
+  $theme = 'layout_test_theme';
+  theme_enable(array($theme));
+  $layout = layout_manager()->createInstance('static_layout:layout_test_theme__two-col');
+  return $layout->renderLayout();
+}
+
+/**
+ * Implements hook_system_theme_info().
+ */
+function layout_test_system_theme_info() {
+  $themes['layout_test_theme'] = drupal_get_path('module', 'layout_test') . '/themes/layout_test_theme/layout_test_theme.info';
+  return $themes;
+}
diff --git a/core/modules/layout/tests/layouts/static/one-col/one-col.tpl.php b/core/modules/layout/tests/layouts/static/one-col/one-col.tpl.php
new file mode 100644
index 00000000000..e47f83ec473
--- /dev/null
+++ b/core/modules/layout/tests/layouts/static/one-col/one-col.tpl.php
@@ -0,0 +1,18 @@
+<?php
+/**
+ * @file
+ * Template for a one column layout.
+ *
+ * This template provides a very simple "one column" display layout.
+ *
+ * Variables:
+ * - $content: An array of content, each item in the array is keyed to one
+ *   region of the layout. This layout supports the following sections:
+ *   $content['middle']: The only region in the layout.
+ */
+?>
+<div class="layout-display layout-one-col clearfix">
+  <div class="layout-region layout-col">
+    <div class="inside"><?php print $content['middle']; ?></div>
+  </div>
+</div>
diff --git a/core/modules/layout/tests/layouts/static/one-col/one-col.yml b/core/modules/layout/tests/layouts/static/one-col/one-col.yml
new file mode 100644
index 00000000000..27d7d0601bc
--- /dev/null
+++ b/core/modules/layout/tests/layouts/static/one-col/one-col.yml
@@ -0,0 +1,5 @@
+title: Single column
+category: Columns: 1
+template: one-col
+regions:
+  middle: 'Middle column'
diff --git a/core/modules/layout/tests/themes/layout_test_theme/layout_test_theme.info b/core/modules/layout/tests/themes/layout_test_theme/layout_test_theme.info
new file mode 100644
index 00000000000..84bcff06809
--- /dev/null
+++ b/core/modules/layout/tests/themes/layout_test_theme/layout_test_theme.info
@@ -0,0 +1,4 @@
+name = Layout test theme
+description = Theme for testing the layout system
+core = 8.x
+hidden = TRUE
diff --git a/core/modules/layout/tests/themes/layout_test_theme/layouts/static/two-col/two-col.css b/core/modules/layout/tests/themes/layout_test_theme/layouts/static/two-col/two-col.css
new file mode 100644
index 00000000000..6044e2dce44
--- /dev/null
+++ b/core/modules/layout/tests/themes/layout_test_theme/layouts/static/two-col/two-col.css
@@ -0,0 +1,17 @@
+.layout-two-col .layout-col-left {
+  float: left;
+  width: 50%;
+}
+
+.layout-two-col .layout-col-left .inside {
+  margin-right: .5em;
+}
+
+.layout-two-col .layout-col-right {
+  float: right;
+  width: 50%;
+}
+
+.layout-two-col .layout-col-right .inside {
+  margin-left: .5em;
+}
diff --git a/core/modules/layout/tests/themes/layout_test_theme/layouts/static/two-col/two-col.tpl.php b/core/modules/layout/tests/themes/layout_test_theme/layouts/static/two-col/two-col.tpl.php
new file mode 100644
index 00000000000..1d8dbe46b33
--- /dev/null
+++ b/core/modules/layout/tests/themes/layout_test_theme/layouts/static/two-col/two-col.tpl.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * @file
+ * Template for a 2 column layout.
+ *
+ * This template provides a two column display layout, with each column equal in
+ * width.
+ *
+ * Variables:
+ * - $content: An array of content, each item in the array is keyed to one
+ *   region of the layout. This layout supports the following sections:
+ *   - $content['left']: Content in the left column.
+ *   - $content['right']: Content in the right column.
+ */
+?>
+<div class="layout-display layout-two-col clearfix">
+  <div class="layout-region layout-col-left">
+    <div class="inside"><?php print $content['left']; ?></div>
+  </div>
+
+  <div class="layout-region layout-col-right">
+    <div class="inside"><?php print $content['right']; ?></div>
+  </div>
+</div>
diff --git a/core/modules/layout/tests/themes/layout_test_theme/layouts/static/two-col/two-col.yml b/core/modules/layout/tests/themes/layout_test_theme/layouts/static/two-col/two-col.yml
new file mode 100644
index 00000000000..7ee126f4c30
--- /dev/null
+++ b/core/modules/layout/tests/themes/layout_test_theme/layouts/static/two-col/two-col.yml
@@ -0,0 +1,8 @@
+title: Two column
+category: Columns: 2
+template: two-col
+stylesheets:
+  - two-col.css
+regions:
+  left: 'Left side'
+  right: 'Right side'