diff --git a/core/modules/layout/layout.info b/core/modules/layout/layout.info
new file mode 100644
index 0000000..187bfea
--- /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 0000000..7b0ab2c
--- /dev/null
+++ b/core/modules/layout/layout.module
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * @file
+ * Manages page layouts for content presentation.
+ */
+
+/**
+ * Implements hook_page_build().
+ *
+ * @todo
+ *   Eventually, Drupal routing will be improved so as to replace
+ *   drupal_render_page() (which builds blocks from bottom-up, even if the
+ *   layout instance is not configured to render them) with a top-down
+ *   subrequest workflow. For now, however, we integrate with the
+ *   drupal_render_page() flow, but replace theme('page') with
+ *   $layout->renderLayout().
+ */
+function layout_page_build(&$page) {
+  $request = drupal_container()->get('request');
+  $options = array(
+    'request' => $request,
+  );
+  $layout = layout_manager()->getInstance($options);
+  if ($layout) {
+    $page['#children'] = $layout->renderLayout();
+    $page['#theme'] = NULL;
+  }
+}
+
+/**
+ * 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');
+}
diff --git a/core/modules/layout/layout/onecol/layout--onecol.tpl.php b/core/modules/layout/layout/onecol/layout--onecol.tpl.php
new file mode 100644
index 0000000..4a9fa6d
--- /dev/null
+++ b/core/modules/layout/layout/onecol/layout--onecol.tpl.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ * @file
+ * Template for a one column layout.
+ *
+ * This template provides a very simple "one column" display layout.
+ *
+ * Variables:
+ * - $id: An optional CSS id to use for the layout.
+ * - $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-1col clearfix" <?php if (!empty($css_id)) { print "id=\"$css_id\""; } ?>>
+  <div class="layout-region layout-col">
+    <div><?php print $content['middle']; ?></div>
+  </div>
+</div>
diff --git a/core/modules/layout/layout/onecol/onecol.css b/core/modules/layout/layout/onecol/onecol.css
new file mode 100644
index 0000000..9515b54
--- /dev/null
+++ b/core/modules/layout/layout/onecol/onecol.css
@@ -0,0 +1,22 @@
+
+.layout-1col {
+/*  overflow: hidden;  */
+}
+
+.layout-2col .layout-col-first .inside {
+  margin: 0;
+}
+
+
+.layout-1col .layout-col {
+  width: 100%;
+}
+
+#layout-edit-display .layout-region,
+#layout-edit-display .helperclass {
+  margin: .5em;
+}
+
+.layout-2col .layout-separator {
+  margin: 0 0 1em 0;
+}
diff --git a/core/modules/layout/layout/onecol/onecol.yml b/core/modules/layout/layout/onecol/onecol.yml
new file mode 100644
index 0000000..233f3cd
--- /dev/null
+++ b/core/modules/layout/layout/onecol/onecol.yml
@@ -0,0 +1,7 @@
+title: Single column
+category: Columns: 1
+template: layout--onecol.tpl.php
+icon: onecol.png
+css: onecol.css
+regions:
+  middle: 'Middle column'
diff --git a/core/modules/layout/layout/twocol/layout--twocol.tpl.php b/core/modules/layout/layout/twocol/layout--twocol.tpl.php
new file mode 100644
index 0000000..90a69af
--- /dev/null
+++ b/core/modules/layout/layout/twocol/layout--twocol.tpl.php
@@ -0,0 +1,25 @@
+<?php
+/**
+ * @file
+ * Template for a 2 column layout.
+ *
+ * This template provides a two column display layout, with each column
+ * roughly equal in width.
+ *
+ * Variables:
+ * - $id: An optional CSS id to use for the layout.
+ * - $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-2col clearfix" <?php if (!empty($css_id)) { print "id=\"$css_id\""; } ?>>
+  <div class="layout-region layout-col-first">
+    <div class="inside"><?php print $content['left']; ?></div>
+  </div>
+
+  <div class="layout-region layout-col-last">
+    <div class="inside"><?php print $content['right']; ?></div>
+  </div>
+</div>
diff --git a/core/modules/layout/layout/twocol/twocol.css b/core/modules/layout/layout/twocol/twocol.css
new file mode 100644
index 0000000..ecbd867
--- /dev/null
+++ b/core/modules/layout/layout/twocol/twocol.css
@@ -0,0 +1,37 @@
+
+.layout-2col {
+/*  overflow: hidden;  */
+}
+
+.layout-2col .layout-col-first {
+  float: left;
+  width: 50%;
+}
+* html .layout-2col .layout-col-first {
+  width: 49.9%;
+}
+
+.layout-2col .layout-col-first .inside {
+  margin: 0 .5em 1em 0;
+}
+
+.layout-2col .layout-col-last {
+  float: left;
+  width: 50%;
+}
+* html .layout-2col .layout-col-last {
+  width: 49.9%;
+}
+
+.layout-2col .layout-col-last .inside {
+  margin: 0 0 1em .5em;
+}
+
+#layout-edit-display .layout-region,
+#layout-edit-display .helperclass {
+  margin: .5em;
+}
+
+.layout-2col .layout-separator {
+  margin: 0 0 1em 0;
+}
diff --git a/core/modules/layout/layout/twocol/twocol.yml b/core/modules/layout/layout/twocol/twocol.yml
new file mode 100644
index 0000000..40b1cba
--- /dev/null
+++ b/core/modules/layout/layout/twocol/twocol.yml
@@ -0,0 +1,8 @@
+title: Two column
+category: Columns: 2
+template: layout--twocol.tpl.php
+icon: twocol.png
+css: twocol.css
+regions:
+  left: 'Left side'
+  right: 'Right side'
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 0000000..59b8513
--- /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 0000000..0f7485a
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Plugin/Derivative/Layout.php
@@ -0,0 +1,98 @@
+<?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.
+   *
+   * @type array
+   *   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__onecol'. The values of the array are associative
+   *   arrays themselves with metadata about the layout such as 'template', 'css',
+   *   'admin css' and so on.
+   */
+  protected $derivatives = array();
+
+  /**
+   * 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 . 'layout';
+      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() && $fileinfo->getExtension() == '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());
+        $this->derivatives[$provider['provider'] . '__' .  $fileinfo->getBasename('.yml')] = $directory->read($fileinfo->getBasename('.yml'));
+        $this->derivatives[$provider['provider'] . '__' .  $fileinfo->getBasename('.yml')]['path'] = $fileinfo->getPath();
+      }
+    }
+  }
+}
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 0000000..0863a67
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Plugin/LayoutInterface.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\layout\Plugin\LayoutInterface.
+ */
+
+namespace Drupal\layout\Plugin;
+
+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 0000000..5bd6429
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Plugin/Type/LayoutManager.php
@@ -0,0 +1,31 @@
+<?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\DefaultLayout'
+  );
+
+  /**
+   * 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/DefaultLayout.php b/core/modules/layout/lib/Drupal/layout/Plugin/layout/layout/DefaultLayout.php
new file mode 100644
index 0000000..6d7ebf0
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Plugin/layout/layout/DefaultLayout.php
@@ -0,0 +1,140 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\layout\Plugin\layout\layout\DefaultLayout.
+ */
+
+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 = "default_layout",
+ *  derivative = "Drupal\layout\Plugin\Derivative\Layout"
+ * )
+ */
+class DefaultLayout 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'];
+  }
+
+  /**
+   * Add the CSS files associated with this layout to the page.
+   */
+  public function addCss() {
+    $definition = $this->getDefinition();
+    $files = $definition['css'];
+    if (is_string($files)) {
+      $files = array($files);
+    }
+    foreach ($files as $filename) {
+      drupal_add_css($definition['path'] . '/' . $filename);
+    }
+  }
+
+  /**
+   * Add the administrative CSS files associated with this layout to the page.
+   */
+  public function addAdminCss() {
+    $definition = $this->getDefinition();
+    // Fall back on regular CSS for the admin page if admin CSS not provided.
+    $files = isset($definition['admin css']) ? $definition['admin css'] : $definition['css'];
+    if (is_string($files)) {
+      $files = array($files);
+    }
+    foreach ($files as $filename) {
+      drupal_add_css($definition['path'] . '/' . $filename);
+    }
+  }
+
+  /**
+   * Add the JS files associated with this layout to the page.
+   */
+  public function addJs() {
+    $definition = $this->getDefinition();
+    if (isset($definition['js']))  {
+      $files = $definition['js'];
+      if (is_string($files)) {
+        $files = array($files);
+      }
+      foreach ($files as $filename) {
+        drupal_add_js($definition['path'] . '/' . $filename);
+      }
+    }
+  }
+
+  /**
+   * Add the admin JS files associated with this layout to the page.
+   */
+  public function addAdminJs() {
+    $definition = $this->getDefinition();
+    $files = array();
+    if (isset($definition['admin js'])) {
+      $files = $definition['admin js'];
+    }
+    elseif (isset($definition['js'])) {
+      // Fall back on regular JS for the admin page if admin JS not provided.
+      $files = $definition['js'];
+    }
+    if (is_string($files)) {
+      $files = array($files);
+    }
+    foreach ($files as $filename) {
+      drupal_add_js($definition['path'] . '/' . $filename);
+    }
+  }
+
+  /**
+   * Implements Drupal\layout\Plugin\LayoutInterface::renderLayout().
+   */
+  public function renderLayout($admin = FALSE) {
+    $definition = $this->getDefinition();
+
+    // Render all regions needed for this layout.
+    $regions = array();
+    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.
+      $regions[$region] = '<h3>' . $title . '</h3>';
+    }
+
+    if (!$admin) {
+      $this->addCss();
+      $this->addJs();
+    }
+    else {
+      $this->addAdminCss();
+      $this->addAdminJs();
+    }
+
+    $template = $definition['path'] . '/' . $definition['template'] . '.tpl.php';
+    $output = theme_render_template($template, array('content' => $regions));
+
+    return $output;
+  }
+}
diff --git a/core/modules/layout/tests/layout_test.info b/core/modules/layout/tests/layout_test.info
new file mode 100644
index 0000000..7b054a9
--- /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 0000000..e96bf71
--- /dev/null
+++ b/core/modules/layout/tests/layout_test.module
@@ -0,0 +1,15 @@
+<?php
+
+/**
+ * @file
+ *   Layout testing module.
+ */
+
+/**
+ * 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/themes/layout_test_theme/layout/page/layout_test_theme--page.tpl.php b/core/modules/layout/tests/themes/layout_test_theme/layout/page/layout_test_theme--page.tpl.php
new file mode 100644
index 0000000..3a5db91
--- /dev/null
+++ b/core/modules/layout/tests/themes/layout_test_theme/layout/page/layout_test_theme--page.tpl.php
@@ -0,0 +1,62 @@
+<div id="page-wrapper"><div id="page">
+
+  <div id="header"><div class="section clearfix">
+    <?php print $content['header']; ?>
+  </div></div> <!-- /.section, /#header -->
+
+  <?php if ($content['featured']): ?>
+    <div id="featured"><div class="section clearfix">
+      <?php print $content['featured']; ?>
+    </div></div> <!-- /.section, /#featured -->
+  <?php endif; ?>
+
+  <div id="main-wrapper" class="clearfix"><div id="main" class="clearfix">
+
+    <div id="content" class="column"><div class="section">
+      <?php if ($content['highlighted']): ?><div id="highlighted"><?php print $content['highlighted']; ?></div><?php endif; ?>
+      <?php print $content['help']; ?>
+      <?php print $content['content']; ?>
+    </div></div> <!-- /.section, /#content -->
+
+    <?php if ($content['sidebar_first']): ?>
+      <div id="sidebar-first" class="column sidebar"><div class="section">
+        <?php print $content['sidebar_first']; ?>
+      </div></div> <!-- /.section, /#sidebar-first -->
+    <?php endif; ?>
+
+    <?php if ($content['sidebar_second']): ?>
+      <div id="sidebar-second" class="column sidebar"><div class="section">
+        <?php print $content['sidebar_second']; ?>
+      </div></div> <!-- /.section, /#sidebar-second -->
+    <?php endif; ?>
+
+  </div></div> <!-- /#main, /#main-wrapper -->
+
+  <?php if ($content['triptych_first'] || $content['triptych_middle'] || $content['triptych_last']): ?>
+    <div id="triptych-wrapper"><div id="triptych" class="clearfix">
+      <?php print $content['triptych_first']; ?>
+      <?php print $content['triptych_middle']; ?>
+      <?php print $content['triptych_last']; ?>
+    </div></div> <!-- /#triptych, /#triptych-wrapper -->
+  <?php endif; ?>
+
+  <div id="footer-wrapper"><div class="section">
+
+    <?php if ($content['footer_firstcolumn'] || $content['footer_secondcolumn'] || $content['footer_thirdcolumn'] || $content['footer_fourthcolumn']): ?>
+      <div id="footer-columns" class="clearfix">
+        <?php print $content['footer_firstcolumn']; ?>
+        <?php print $content['footer_secondcolumn']; ?>
+        <?php print $content['footer_thirdcolumn']; ?>
+        <?php print $content['footer_fourthcolumn']; ?>
+      </div> <!-- /#footer-columns -->
+    <?php endif; ?>
+
+    <?php if ($content['footer']): ?>
+      <div id="footer" class="clearfix">
+        <?php print $content['footer']; ?>
+      </div> <!-- /#footer -->
+    <?php endif; ?>
+
+  </div></div> <!-- /.section, /#footer-wrapper -->
+
+</div></div> <!-- /#page, /#page-wrapper -->
diff --git a/core/modules/layout/tests/themes/layout_test_theme/layout/page/page.yml b/core/modules/layout/tests/themes/layout_test_theme/layout/page/page.yml
new file mode 100644
index 0000000..9a86456
--- /dev/null
+++ b/core/modules/layout/tests/themes/layout_test_theme/layout/page/page.yml
@@ -0,0 +1,21 @@
+title: Layout test page
+category: Other
+template: layout_test_theme--page.tpl.php
+icon: page.png
+css: page.css
+regions:
+  header: 'Header'
+  help: 'Help'
+  highlighted: 'Highlighted'
+  featured: 'Featured'
+  content: 'Content'
+  sidebar_first: 'Sidebar first'
+  sidebar_second: 'Sidebar second'
+  triptych_first: 'Triptych first'
+  triptych_middle: 'Triptych middle'
+  triptych_last: 'Triptych last'
+  footer_firstcolumn: 'Footer first column'
+  footer_secondcolumn: 'Footer second column'
+  footer_thirdcolumn: 'Footer third column'
+  footer_fourthcolumn: 'Footer fourth column'
+  footer: 'Footer'
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 0000000..84bcff0
--- /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
