diff --git a/core/lib/Drupal/Core/Ajax/AddCssCommand.php b/core/lib/Drupal/Core/Ajax/AddCssCommand.php
index 80ee17aa0334ed7820c937906f65d01ecdb09d6c..84c5810138227c30e77fc53b0827b80209e722e3 100644
--- a/core/lib/Drupal/Core/Ajax/AddCssCommand.php
+++ b/core/lib/Drupal/Core/Ajax/AddCssCommand.php
@@ -15,30 +15,30 @@
 class AddCssCommand implements CommandInterface {
 
   /**
-   * A string that contains the styles to be added to the page.
+   * Arrays containing attributes of the stylesheets to be added to the page.
    *
-   * It should include the wrapping style tag.
-   *
-   * @var string
+   * @var string[][]|string
    */
   protected $styles;
 
   /**
    * Constructs an AddCssCommand.
    *
-   * @param string $styles
-   *   A string that contains the styles to be added to the page, including the
-   *   wrapping <style> tag.
+   * @param string[][]|string $styles
+   *   Arrays containing attributes of the stylesheets to be added to the page.
+   *   i.e. `['href' => 'someURL']` becomes `<link href="someURL">`.
    */
   public function __construct($styles) {
+    if (is_string($styles)) {
+      @trigger_error('The ' . __NAMESPACE__ . '\AddCssCommand with a string argument is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. See http://www.drupal.org/node/3154948', E_USER_DEPRECATED);
+    }
     $this->styles = $styles;
   }
 
   /**
-   * Implements Drupal\Core\Ajax\CommandInterface:render().
+   * {@inheritdoc}
    */
   public function render() {
-
     return [
       'command' => 'add_css',
       'data' => $this->styles,
diff --git a/core/lib/Drupal/Core/Ajax/AjaxResponseAttachmentsProcessor.php b/core/lib/Drupal/Core/Ajax/AjaxResponseAttachmentsProcessor.php
index 3936ce8fe0db7f55ec5ccf61b0386bd3ee4312fe..a96e0e14dcffd27a95deca1d510b83f8350aef19 100644
--- a/core/lib/Drupal/Core/Ajax/AjaxResponseAttachmentsProcessor.php
+++ b/core/lib/Drupal/Core/Ajax/AjaxResponseAttachmentsProcessor.php
@@ -174,7 +174,7 @@ protected function buildAttachmentsCommands(AjaxResponse $response, Request $req
     $resource_commands = [];
     if ($css_assets) {
       $css_render_array = $this->cssCollectionRenderer->render($css_assets);
-      $resource_commands[] = new AddCssCommand($this->renderer->renderPlain($css_render_array));
+      $resource_commands[] = new AddCssCommand(array_column($css_render_array, '#attributes'));
     }
     if ($js_assets_header) {
       $js_header_render_array = $this->jsCollectionRenderer->render($js_assets_header);
diff --git a/core/misc/ajax.js b/core/misc/ajax.js
index cb416174f720aad0fd4760dbd043d87e8c626b6c..ec18625bf656d9358218d585f1a04e3e2714fbf3 100644
--- a/core/misc/ajax.js
+++ b/core/misc/ajax.js
@@ -1659,13 +1659,52 @@
      *   {@link Drupal.Ajax} object created by {@link Drupal.ajax}.
      * @param {object} response
      *   The response from the Ajax request.
-     * @param {string} response.data
-     *   A string that contains the styles to be added.
+     * @param {object[]|string} response.data
+     *   An array of styles to be added.
      * @param {number} [status]
      *   The XMLHttpRequest status.
      */
     add_css(ajax, response, status) {
-      $('head').prepend(response.data);
+      if (typeof response.data === 'string') {
+        Drupal.deprecationError({
+          message:
+            'Passing a string to the Drupal.ajax.add_css() method is deprecated in 10.1.0 and is removed from drupal:11.0.0. See https://www.drupal.org/node/3154948.',
+        });
+        $('head').prepend(response.data);
+        return;
+      }
+
+      const allUniqueBundleIds = response.data.map(function (style) {
+        const uniqueBundleId = style.href + ajax.instanceIndex;
+        loadjs(style.href, uniqueBundleId, {
+          before(path, styleEl) {
+            // This allows all attributes to be added, like media.
+            Object.keys(style).forEach((attributeKey) => {
+              styleEl.setAttribute(attributeKey, style[attributeKey]);
+            });
+          },
+        });
+        return uniqueBundleId;
+      });
+      // Returns the promise so that the next AJAX command waits on the
+      // completion of this one to execute, ensuring the CSS is loaded before
+      // executing.
+      return new Promise((resolve, reject) => {
+        loadjs.ready(allUniqueBundleIds, {
+          success() {
+            // All CSS files were loaded. Resolve the promise and let the
+            // remaining commands execute.
+            resolve();
+          },
+          error(depsNotFound) {
+            const message = Drupal.t(
+              `The following files could not be loaded: @dependencies`,
+              { '@dependencies': depsNotFound.join(', ') },
+            );
+            reject(message);
+          },
+        });
+      });
     },
 
     /**
diff --git a/core/modules/system/tests/modules/ajax_forms_test/ajax_forms_test.module b/core/modules/system/tests/modules/ajax_forms_test/ajax_forms_test.module
index 7896856a4b0d7ce29bdea676e69e66ec0f2efe3c..30ca6bb834c052d40d6cc63d5aae024f6fed3940 100644
--- a/core/modules/system/tests/modules/ajax_forms_test/ajax_forms_test.module
+++ b/core/modules/system/tests/modules/ajax_forms_test/ajax_forms_test.module
@@ -202,7 +202,23 @@ function ajax_forms_test_advanced_commands_settings_callback($form, FormStateInt
  */
 function ajax_forms_test_advanced_commands_add_css_callback($form, FormStateInterface $form_state) {
   $response = new AjaxResponse();
-  $response->addCommand(new AddCssCommand('my/file.css'));
+  $response->addCommand(new AddCssCommand([
+    [
+      'href' => 'my/file.css',
+      'media' => 'all',
+    ],
+  ]));
+  return $response;
+}
+
+/**
+ * Ajax callback for 'add_css' that uses legacy string parameter.
+ *
+ * @todo Remove in Drupal 11.0.0 https://www.drupal.org/i/3339374
+ */
+function ajax_forms_test_advanced_commands_add_css_legacy_callback($form, FormStateInterface $form_state) {
+  $response = new AjaxResponse();
+  $response->addCommand(new AddCssCommand("<link rel='stylesheet' href='my/file.css' media='all' />"));
   return $response;
 }
 
diff --git a/core/modules/system/tests/modules/ajax_forms_test/src/Form/AjaxFormsTestCommandsForm.php b/core/modules/system/tests/modules/ajax_forms_test/src/Form/AjaxFormsTestCommandsForm.php
index 5532b3d06d68c525e1ce03c287a03f091c63cf8f..8c1e205afbb5d68b10252d6c6a0e9742fc27c7da 100644
--- a/core/modules/system/tests/modules/ajax_forms_test/src/Form/AjaxFormsTestCommandsForm.php
+++ b/core/modules/system/tests/modules/ajax_forms_test/src/Form/AjaxFormsTestCommandsForm.php
@@ -224,6 +224,16 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       ],
     ];
 
+    // Shows the Ajax 'add_css' command with legacy string parameter.
+    // @todo Remove in Drupal 11.0.0 https://www.drupal.org/i/3339374
+    $form['add_css_legacy_command_example'] = [
+      '#type' => 'submit',
+      '#value' => $this->t("AJAX 'add_css' legacy command"),
+      '#ajax' => [
+        'callback' => 'ajax_forms_test_advanced_commands_add_css_legacy_callback',
+      ],
+    ];
+
     $form['submit'] = [
       '#type' => 'submit',
       '#value' => $this->t('Submit'),
diff --git a/core/modules/system/tests/src/Functional/Ajax/FrameworkTest.php b/core/modules/system/tests/src/Functional/Ajax/FrameworkTest.php
index ccd21ca523c7e834fd550425266113b2b4f70c01..afa6f8df54d0fda862d6a45cc57e6c241274b1f8 100644
--- a/core/modules/system/tests/src/Functional/Ajax/FrameworkTest.php
+++ b/core/modules/system/tests/src/Functional/Ajax/FrameworkTest.php
@@ -53,7 +53,7 @@ public function testOrder() {
     $build['#attached']['library'][] = 'ajax_test/order-css-command';
     $assets = AttachedAssets::createFromRenderArray($build);
     $css_render_array = $css_collection_renderer->render($asset_resolver->getCssAssets($assets, FALSE, \Drupal::languageManager()->getCurrentLanguage()));
-    $expected_commands[1] = new AddCssCommand($renderer->renderRoot($css_render_array));
+    $expected_commands[1] = new AddCssCommand(array_column($css_render_array, '#attributes'));
     $build['#attached']['library'][] = 'ajax_test/order-header-js-command';
     $build['#attached']['library'][] = 'ajax_test/order-footer-js-command';
     $assets = AttachedAssets::createFromRenderArray($build);
diff --git a/core/tests/Drupal/FunctionalJavascriptTests/Ajax/CommandsTest.php b/core/tests/Drupal/FunctionalJavascriptTests/Ajax/CommandsTest.php
index 5f4b48bf8bd731ab9fd77b43cd841f82d5fa5666..6c4621431d0071b89c7c04e71b3e6e8db533bbc6 100644
--- a/core/tests/Drupal/FunctionalJavascriptTests/Ajax/CommandsTest.php
+++ b/core/tests/Drupal/FunctionalJavascriptTests/Ajax/CommandsTest.php
@@ -140,6 +140,25 @@ public function testAjaxCommands() {
     $this->assertWaitPageContains('<div class="test-settings-command">42</div>');
   }
 
+  /**
+   * Tests the various Ajax Commands with legacy parameters.
+   * @group legacy
+   */
+  public function testLegacyAjaxCommands() {
+    $session = $this->getSession();
+    $page = $this->getSession()->getPage();
+
+    $form_path = 'ajax_forms_test_ajax_commands_form';
+    $web_user = $this->drupalCreateUser(['access content']);
+    $this->drupalLogin($web_user);
+    $this->drupalGet($form_path);
+
+    // Tests the 'add_css' command with legacy string value.
+    $this->expectDeprecation('Javascript Deprecation: Passing a string to the Drupal.ajax.add_css() method is deprecated in 10.1.0 and is removed from drupal:11.0.0. See https://www.drupal.org/node/3154948.');
+    $page->pressButton("AJAX 'add_css' legacy command");
+    $this->assertWaitPageContains('my/file.css');
+  }
+
   /**
    * Asserts that page contains a text after waiting.
    *
diff --git a/core/tests/Drupal/Tests/Core/Ajax/AjaxCommandsTest.php b/core/tests/Drupal/Tests/Core/Ajax/AjaxCommandsTest.php
index c482a02119765f08dd6cb59bee01940855b9ec3f..90169e978e4aded0fdb8ea600709124360126589 100644
--- a/core/tests/Drupal/Tests/Core/Ajax/AjaxCommandsTest.php
+++ b/core/tests/Drupal/Tests/Core/Ajax/AjaxCommandsTest.php
@@ -36,15 +36,106 @@
  */
 class AjaxCommandsTest extends UnitTestCase {
 
+  /**
+   * @return array
+   *   - Array of css elements
+   *   - Expected value
+   */
+  public function providerCss() {
+    return [
+      'empty' => [
+        [],
+        [
+          'command' => 'add_css',
+          'data' => [],
+        ],
+      ],
+      'single' => [
+        [
+          [
+            'href' => 'core/misc/example.css',
+            'media' => 'all',
+          ],
+        ],
+        [
+          'command' => 'add_css',
+          'data' => [
+            [
+              'href' => 'core/misc/example.css',
+              'media' => 'all',
+            ],
+          ],
+        ],
+      ],
+      'single-data-property' => [
+        [
+          [
+            'href' => 'core/misc/example.css',
+            'media' => 'all',
+            'data-test' => 'test',
+          ],
+        ],
+        [
+          'command' => 'add_css',
+          'data' => [
+            [
+              'href' => 'core/misc/example.css',
+              'media' => 'all',
+              'data-test' => 'test',
+            ],
+          ],
+        ],
+      ],
+      'multiple' => [
+        [
+          [
+            'href' => 'core/misc/example1.css',
+            'media' => 'all',
+          ],
+          [
+            'href' => 'core/misc/example2.css',
+            'media' => 'all',
+          ],
+        ],
+        [
+          'command' => 'add_css',
+          'data' => [
+            [
+              'href' => 'core/misc/example1.css',
+              'media' => 'all',
+            ],
+            [
+              'href' => 'core/misc/example2.css',
+              'media' => 'all',
+            ],
+          ],
+        ],
+      ],
+    ];
+  }
+
   /**
    * @covers \Drupal\Core\Ajax\AddCssCommand
+   * @dataProvider providerCss
    */
-  public function testAddCssCommand() {
-    $command = new AddCssCommand('p{ text-decoration:blink; }');
+  public function testAddCssCommand($css, $expected) {
+    $command = new AddCssCommand($css);
+
+    $this->assertEquals($expected, $command->render());
+  }
+
+  /**
+   * @covers \Drupal\Core\Ajax\AddCssCommand
+   * @group legacy
+   */
+  public function testStringAddCssCommand() {
+    $this->expectDeprecation("The Drupal\Core\Ajax\AddCssCommand with a string argument is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. See http://www.drupal.org/node/3154948");
+
+    $command = new AddCssCommand('<style>p{ text-decoration:blink; }</style>');
 
     $expected = [
       'command' => 'add_css',
-      'data' => 'p{ text-decoration:blink; }',
+      'data' => '<style>p{ text-decoration:blink; }</style>',
     ];
 
     $this->assertEquals($expected, $command->render());