diff --git a/core/includes/common.inc b/core/includes/common.inc
index b13c3bc3f8ecad7dae1ff421ff47a21c07f42ec8..bb14e95fb34b90973651ccb85d1c48acaf3f0683 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -2564,9 +2564,20 @@ function drupal_add_css($data = NULL, $options = NULL) {
         // key as $data could be a very long string of CSS.
         $css[] = $options;
         break;
+
+      case 'file':
+        // Local CSS files are keyed by basename; if a file with the same
+        // basename is added more than once, it gets overridden.
+        // By default, take over the filename as basename.
+        if (!isset($options['basename'])) {
+          $options['basename'] = drupal_basename($data);
+        }
+        $css[$options['basename']] = $options;
+        break;
+
       default:
-        // Local and external files must keep their name as the associative key
-        // so the same CSS file is not be added twice.
+        // External files are keyed by their full URI, so the same CSS file is
+        // not added twice.
         $css[$data] = $options;
     }
   }
@@ -2604,6 +2615,8 @@ function drupal_add_css($data = NULL, $options = NULL) {
  * @see drupal_add_css()
  */
 function drupal_get_css($css = NULL, $skip_alter = FALSE) {
+  global $theme_info;
+
   if (!isset($css)) {
     $css = drupal_add_css();
   }
@@ -2616,17 +2629,20 @@ function drupal_get_css($css = NULL, $skip_alter = FALSE) {
   // Sort CSS items, so that they appear in the correct order.
   uasort($css, 'drupal_sort_css_js');
 
-  // Remove the overridden CSS files. Later CSS files override former ones.
-  $previous_item = array();
-  foreach ($css as $key => $item) {
-    if ($item['type'] == 'file') {
-      // If defined, force a unique basename for this file.
-      $basename = isset($item['basename']) ? $item['basename'] : drupal_basename($item['data']);
-      if (isset($previous_item[$basename])) {
-        // Remove the previous item that shared the same base name.
-        unset($css[$previous_item[$basename]]);
+  // Allow themes to remove CSS files by basename.
+  if (!empty($theme_info->stylesheets_remove)) {
+    foreach ($css as $key => $options) {
+      if (isset($options['basename']) && isset($theme_info->stylesheets_remove[$options['basename']])) {
+        unset($css[$key]);
+      }
+    }
+  }
+  // Allow themes to conditionally override CSS files by basename.
+  if (!empty($theme_info->stylesheets_override)) {
+    foreach ($css as $key => $options) {
+      if (isset($options['basename']) && isset($theme_info->stylesheets_override[$options['basename']])) {
+        $css[$key]['data'] = $theme_info->stylesheets_override[$options['basename']];
       }
-      $previous_item[$basename] = $key;
     }
   }
 
@@ -2967,24 +2983,11 @@ function drupal_pre_render_styles($elements) {
         elseif ($group['preprocess']) {
           $import = array();
           foreach ($group['items'] as $item) {
-            // A theme's .info file may have an entry for a file that doesn't
-            // exist as a way of overriding a module or base theme CSS file from
-            // being added to the page. Normally, file_exists() calls that need
-            // to run for every page request should be minimized, but this one
-            // is okay, because it only runs when CSS aggregation is disabled.
-            // On a server under heavy enough load that file_exists() calls need
-            // to be minimized, CSS aggregation should be enabled, in which case
-            // this code is not run. When aggregation is enabled,
-            // drupal_load_stylesheet() checks file_exists(), but only when
-            // building the aggregate file, which is then reused for many page
-            // requests.
-            if (file_exists($item['data'])) {
-              // The dummy query string needs to be added to the URL to control
-              // browser-caching. IE7 does not support a media type on the
-              // @import statement, so we instead specify the media for the
-              // group on the STYLE tag.
-              $import[] = '@import url("' . check_plain(file_create_url($item['data']) . '?' . $query_string) . '");';
-            }
+            // The dummy query string needs to be added to the URL to control
+            // browser-caching. IE7 does not support a media type on the
+            // @import statement, so we instead specify the media for the
+            // group on the STYLE tag.
+            $import[] = '@import url("' . check_plain(file_create_url($item['data']) . '?' . $query_string) . '");';
           }
           // In addition to IE's limit of 31 total CSS inclusion tags, it also
           // has a limit of 31 @import statements per STYLE tag.
@@ -3007,14 +3010,6 @@ function drupal_pre_render_styles($elements) {
         else {
           foreach ($group['items'] as $item) {
             $element = $link_element_defaults;
-            // We do not check file_exists() here, because this code runs for
-            // files whose 'preprocess' is set to FALSE, and therefore, even
-            // when aggregation is enabled, and we want to avoid needlessly
-            // taxing a server that may be under heavy load. The file_exists()
-            // performed above for files whose 'preprocess' is TRUE is done for
-            // the benefit of theme .info files, but code that deals with files
-            // whose 'preprocess' is FALSE is responsible for ensuring the file
-            // exists.
             // The dummy query string needs to be added to the URL to control
             // browser-caching.
             $query_string_separator = (strpos($item['data'], '?') !== FALSE) ? '&' : '?';
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index da8b32807c649ca495b4a555e35ad8ba202af84e..35b9cd3ccd663301684af3900d12a6dc414a3507 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -149,6 +149,10 @@ function _drupal_theme_initialize($theme, $base_theme = array(), $registry_callb
   // We work it this way so that we can have child themes override parent
   // theme stylesheets easily.
   $final_stylesheets = array();
+  // CSS file basenames to override, pointing to the final, overridden filepath.
+  $theme->stylesheets_override = array();
+  // CSS file basenames to remove.
+  $theme->stylesheets_remove = array();
 
   // Grab stylesheets from base theme
   foreach ($base_theme as $base) {
@@ -159,6 +163,18 @@ function _drupal_theme_initialize($theme, $base_theme = array(), $registry_callb
         }
       }
     }
+    $base_theme_path = dirname($base->filename);
+    if (!empty($base->info['stylesheets-remove'])) {
+      foreach ($base->info['stylesheets-remove'] as $basename) {
+        $theme->stylesheets_remove[$basename] = $base_theme_path . '/' . $basename;
+      }
+    }
+    if (!empty($base->info['stylesheets-override'])) {
+      foreach ($base->info['stylesheets-override'] as $name) {
+        $basename = drupal_basename($name);
+        $theme->stylesheets_override[$basename] = $base_theme_path . '/' . $name;
+      }
+    }
   }
 
   // Add stylesheets used by this theme.
@@ -169,6 +185,25 @@ function _drupal_theme_initialize($theme, $base_theme = array(), $registry_callb
       }
     }
   }
+  if (!empty($theme->info['stylesheets-remove'])) {
+    foreach ($theme->info['stylesheets-remove'] as $basename) {
+      $theme->stylesheets_remove[$basename] = $theme_path . '/' . $basename;
+
+      if (isset($theme->stylesheets_override[$basename])) {
+        unset($theme->stylesheets_override[$basename]);
+      }
+    }
+  }
+  if (!empty($theme->info['stylesheets-override'])) {
+    foreach ($theme->info['stylesheets-override'] as $name) {
+      $basename = drupal_basename($name);
+      $theme->stylesheets_override[$basename] = $theme_path . '/' . $name;
+
+      if (isset($theme->stylesheets_remove[$basename])) {
+        unset($theme->stylesheets_remove[$basename]);
+      }
+    }
+  }
 
   // And now add the stylesheets properly
   foreach ($final_stylesheets as $media => $stylesheets) {
diff --git a/core/modules/color/color.module b/core/modules/color/color.module
index 53a7f39858ccf32af33a1c144161862cfcac4d51..87d0a3725d490cf6c35a9b92b325993881bb1078 100644
--- a/core/modules/color/color.module
+++ b/core/modules/color/color.module
@@ -77,7 +77,7 @@ function _color_html_alter(&$vars) {
         if (drupal_basename($old_path) == drupal_basename($color_path)) {
           // Replace the path to the new css file.
           // This keeps the order of the stylesheets intact.
-          $vars['css'][$old_path]['data'] = $color_path;
+          $vars['css'][drupal_basename($old_path)]['data'] = $color_path;
         }
       }
     }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Ajax/FrameworkTest.php b/core/modules/system/lib/Drupal/system/Tests/Ajax/FrameworkTest.php
index 00e7f14f89df2564ea3d90a2015fa35fb6cedd1e..e7aa0e04a6c170d5855ae99af4cb4f968aea5261 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Ajax/FrameworkTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Ajax/FrameworkTest.php
@@ -78,8 +78,11 @@ function testLazyLoad() {
       'css' => drupal_get_path('module', 'system') . '/system.admin.css',
       'js' => drupal_get_path('module', 'system') . '/system.js',
     );
+    // CSS files are stored by basename, see drupal_add_css().
+    $expected_css_basename = drupal_basename($expected['css']);
+
     // @todo D8: Add a drupal_css_defaults() helper function.
-    $expected_css_html = drupal_get_css(array($expected['css'] => array(
+    $expected_css_html = drupal_get_css(array($expected_css_basename => array(
       'type' => 'file',
       'group' => CSS_DEFAULT,
       'weight' => 0,
@@ -135,7 +138,7 @@ function testLazyLoad() {
 
     // Verify the expected CSS file was added, both to Drupal.settings, and as
     // an Ajax command for inclusion into the HTML.
-    $this->assertEqual($new_css, $original_css + array($expected['css'] => 1), format_string('Page state now has the %css file.', array('%css' => $expected['css'])));
+    $this->assertEqual($new_css, $original_css + array($expected_css_basename => 1), format_string('Page state now has the %css file.', array('%css' => $expected['css'])));
     $this->assertCommand($commands, array('data' => $expected_css_html), format_string('Page now has the %css file.', array('%css' => $expected['css'])));
 
     // Verify the expected JS file was added, both to Drupal.settings, and as
diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/CascadingStylesheetsTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/CascadingStylesheetsTest.php
index c40fbe8abdeaf90fe24d0983141da4b0186aaf79..30cf24e21bb142b7f974844948c14679b559628c 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Common/CascadingStylesheetsTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Common/CascadingStylesheetsTest.php
@@ -48,7 +48,7 @@ function testDefault() {
   function testAddFile() {
     $path = drupal_get_path('module', 'simpletest') . '/simpletest.css';
     $css = drupal_add_css($path);
-    $this->assertEqual($css[$path]['data'], $path, 'Adding a CSS file caches it properly.');
+    $this->assertEqual($css['simpletest.css']['data'], $path);
   }
 
   /**
diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeInfoStylesTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeInfoStylesTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..c2b6c43eb31a37e1265ccfe62bd8b9a7cae9d43c
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeInfoStylesTest.php
@@ -0,0 +1,62 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\system\Tests\Theme\ThemeInfoStylesTest.
+ */
+
+namespace Drupal\system\Tests\Theme;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Tests processing of theme .info stylesheets.
+ */
+class ThemeInfoStylesTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('theme_test');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Theme .info styles',
+      'description' => 'Tests processing of theme .info stylesheets.',
+      'group' => 'Theme',
+    );
+  }
+
+  /**
+   * Tests stylesheets-override and stylesheets-remove.
+   */
+  function testStylesheets() {
+    theme_enable(array('test_basetheme', 'test_subtheme'));
+    variable_set('theme_default', 'test_subtheme');
+
+    $base = drupal_get_path('theme', 'test_basetheme');
+    // Unlike test_basetheme (and the original module CSS), the subtheme decides
+    // to put all of its CSS into a ./css subdirectory. All overrides and
+    // removals are expected to be based on a file's basename and should work
+    // nevertheless.
+    $sub = drupal_get_path('theme', 'test_subtheme') . '/css';
+
+    $this->drupalGet('theme-test/info/stylesheets');
+
+    $this->assertRaw("$base/base-add.css");
+    $this->assertRaw("$base/base-override.css");
+    $this->assertNoRaw("base-remove.css");
+
+    $this->assertRaw("$sub/sub-add.css");
+
+    $this->assertRaw("$sub/sub-override.css");
+    $this->assertRaw("$sub/base-add.sub-override.css");
+    $this->assertRaw("$sub/base-remove.sub-override.css");
+
+    $this->assertNoRaw("sub-remove.css");
+    $this->assertNoRaw("base-add.sub-remove.css");
+    $this->assertNoRaw("base-override.sub-remove.css");
+  }
+}
diff --git a/core/modules/system/tests/modules/theme_test/theme_test.module b/core/modules/system/tests/modules/theme_test/theme_test.module
index 40196d760db2c4ca1316c795e4dad50442ef618d..73ff44b5430609efe4c16bd41368af32e337e261 100644
--- a/core/modules/system/tests/modules/theme_test/theme_test.module
+++ b/core/modules/system/tests/modules/theme_test/theme_test.module
@@ -59,6 +59,11 @@ function theme_test_menu() {
     'access callback' => TRUE,
     'type' => MENU_CALLBACK,
   );
+  $items['theme-test/info/stylesheets'] = array(
+    'page callback' => 'theme_test_info_stylesheets',
+    'access callback' => TRUE,
+    'type' => MENU_CALLBACK,
+  );
   return $items;
 }
 
@@ -106,6 +111,25 @@ function theme_test_template_test_page_callback() {
   return theme('theme_test_template_test');
 }
 
+/**
+ * Page callback; Adds stylesheets to test theme .info property processing.
+ *
+ * @see test_basetheme.info
+ * @see test_subtheme.info
+ * @see \Drupal\system\Tests\Theme\ThemeInfoStylesTest
+ * @see http://drupal.org/node/967266#comment-3689670
+ */
+function theme_test_info_stylesheets() {
+  $path = drupal_get_path('module', 'theme_test');
+  drupal_add_css("$path/css/base-override.css");
+  drupal_add_css("$path/css/base-override.sub-remove.css");
+  drupal_add_css("$path/css/base-remove.css");
+  drupal_add_css("$path/css/base-remove.sub-override.css");
+  drupal_add_css("$path/css/sub-override.css");
+  drupal_add_css("$path/css/sub-remove.css");
+  return '';
+}
+
 /**
  * Custom theme callback.
  */
diff --git a/core/modules/system/tests/themes/test_basetheme/test_basetheme.info b/core/modules/system/tests/themes/test_basetheme/test_basetheme.info
index 2b5f66e521af3088936bf0968bb67a4876c67455..24b1db6b3ceb1f02a432b8c3fac68c07832c792c 100644
--- a/core/modules/system/tests/themes/test_basetheme/test_basetheme.info
+++ b/core/modules/system/tests/themes/test_basetheme/test_basetheme.info
@@ -3,5 +3,16 @@ description = Test theme which acts as a base theme for other test subthemes.
 core = 8.x
 hidden = TRUE
 
+; Unlike test_subtheme, the base theme does not use a ./css subdirectory.
+stylesheets[all][] = base-add.css
+stylesheets[all][] = base-add.sub-override.css
+stylesheets[all][] = base-add.sub-remove.css
+
+stylesheets-override[] = base-override.css
+stylesheets-override[] = base-override.sub-remove.css
+
+stylesheets-remove[] = base-remove.css
+stylesheets-remove[] = base-remove.sub-override.css
+
 settings[basetheme_only] = base theme value
 settings[subtheme_override] = base theme value
diff --git a/core/modules/system/tests/themes/test_subtheme/test_subtheme.info b/core/modules/system/tests/themes/test_subtheme/test_subtheme.info
index 974e00fb7bd958900e5a522d49968b6734dee232..a4590ce2ac24e91908791184d9390961a4a270dd 100644
--- a/core/modules/system/tests/themes/test_subtheme/test_subtheme.info
+++ b/core/modules/system/tests/themes/test_subtheme/test_subtheme.info
@@ -4,4 +4,17 @@ core = 8.x
 base theme = test_basetheme
 hidden = TRUE
 
+; Unlike test_basetheme (and the original module CSS), this subtheme decides to
+; put all of its CSS into a ./css subdirectory. All overrides and removals are
+; expected to be based on a file's basename and should work nevertheless.
+stylesheets[all][] = css/sub-add.css
+
+stylesheets-override[] = css/sub-override.css
+stylesheets-override[] = css/base-add.sub-override.css
+stylesheets-override[] = css/base-remove.sub-override.css
+
+stylesheets-remove[] = sub-remove.css
+stylesheets-remove[] = base-add.sub-remove.css
+stylesheets-remove[] = base-override.sub-remove.css
+
 settings[subtheme_override] = subtheme value
diff --git a/core/modules/system/tests/themes/test_theme/test_theme.info b/core/modules/system/tests/themes/test_theme/test_theme.info
index b5d1bfc9f3da0654fc1aa1a77f94544f25793d14..aa2842b92262919a5a862f6b5b7cb5b258e10b6c 100644
--- a/core/modules/system/tests/themes/test_theme/test_theme.info
+++ b/core/modules/system/tests/themes/test_theme/test_theme.info
@@ -3,16 +3,6 @@ description = Theme for testing the theme system
 core = 8.x
 hidden = TRUE
 
-; Normally, themes may list CSS files like this, and if they exist in the theme
-; folder, then they get added to the page. If they have the same file name as a
-; module CSS file, then the theme's version overrides the module's version, so
-; that the module's version is not added to the page. Additionally, a theme may
-; have an entry like this one, without having the corresponding CSS file in the
-; theme's folder, and in this case, it just stops the module's version from
-; being loaded, and does not replace it with an alternate version. We have this
-; here in order for a test to ensure that this correctly prevents the module
-; version from being loaded, and that errors aren't caused by the lack of this
-; file within the theme folder.
-stylesheets[all][] = system.base.css
+stylesheets-remove[] = system.base.css
 
 settings[theme_test_setting] = default value
diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/CacheTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/CacheTest.php
index 204dfbb2895fb5814953d29a0b6c83dba97a4241..0f32a60ffe928907dc1b6b50cfede1f1e0e838c9 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Plugin/CacheTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/CacheTest.php
@@ -152,7 +152,7 @@ function testHeaderStorage() {
     $js_path = drupal_get_path('module', 'views_test_data') . '/views_cache.test.js';
     $js = drupal_add_js();
 
-    $this->assertTrue(isset($css[$css_path]), 'Make sure the css is added for cached views.');
+    $this->assertTrue(isset($css[basename($css_path)]), 'Make sure the css is added for cached views.');
     $this->assertTrue(isset($js[$js_path]), 'Make sure the js is added for cached views.');
     $this->assertFalse(!empty($view->build_info['pre_render_called']), 'Make sure hook_views_pre_render is not called for the cached view.');
 
@@ -178,7 +178,7 @@ function testHeaderStorage() {
     $css = drupal_add_css();
     $js = drupal_add_js();
 
-    $this->assertFalse(isset($css[$system_css_path]), 'Make sure that unrelated css is not added.');
+    $this->assertFalse(isset($css[basename($system_css_path)]), 'Make sure that unrelated css is not added.');
     $this->assertFalse(isset($js[$system_js_path]), 'Make sure that unrelated js is not added.');
   }
 
diff --git a/core/themes/seven/seven.info b/core/themes/seven/seven.info
index 87edba394c1a8f6f5c469b1826a0793d8d426a89..b737eafd10e64409f19353033a9ada0f03a067b7 100644
--- a/core/themes/seven/seven.info
+++ b/core/themes/seven/seven.info
@@ -3,8 +3,14 @@ description = A simple one-column, tableless, fluid width administration theme.
 package = Core
 version = VERSION
 core = 8.x
+
 stylesheets[screen][] = style.css
+stylesheets-override[] = vertical-tabs.css
+stylesheets-override[] = vertical-tabs-rtl.css
+stylesheets-override[] = jquery.ui.theme.css
+
 settings[shortcut_module_link] = 1
+
 regions[content] = Content
 regions[help] = Help
 regions[page_top] = Page top
diff --git a/core/themes/seven/template.php b/core/themes/seven/template.php
index ef9d50e517860acfbc56181035baa56333bde0e7..d6793697c63b52d4ae6bbc3ed5e38fde6545fb98 100644
--- a/core/themes/seven/template.php
+++ b/core/themes/seven/template.php
@@ -105,23 +105,6 @@ function seven_tablesort_indicator($variables) {
   }
 }
 
-/**
- * Implements hook_css_alter().
- */
-function seven_css_alter(&$css) {
-  // Use Seven's vertical tabs style instead of the default one.
-  if (isset($css['core/misc/vertical-tabs.css'])) {
-    $css['core/misc/vertical-tabs.css']['data'] = drupal_get_path('theme', 'seven') . '/vertical-tabs.css';
-  }
-  if (isset($css['core/misc/vertical-tabs-rtl.css'])) {
-    $css['core/misc/vertical-tabs-rtl.css']['data'] = drupal_get_path('theme', 'seven') . '/vertical-tabs-rtl.css';
-  }
-  // Use Seven's jQuery UI theme style instead of the default one.
-  if (isset($css['core/misc/ui/themes/base/jquery.ui.theme.css'])) {
-    $css['core/misc/ui/themes/base/jquery.ui.theme.css']['data'] = drupal_get_path('theme', 'seven') . '/jquery.ui.theme.css';
-  }
-}
-
 /**
  * Implements hook_preprocess_install_page().
  */