diff --git a/core/lib/Drupal/Core/Ajax/AnnounceCommand.php b/core/lib/Drupal/Core/Ajax/AnnounceCommand.php
new file mode 100644
index 0000000000000000000000000000000000000000..aa58bdbfee03172809fbd0fff0bcdbaf298f5005
--- /dev/null
+++ b/core/lib/Drupal/Core/Ajax/AnnounceCommand.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace Drupal\Core\Ajax;
+
+use Drupal\Core\Asset\AttachedAssets;
+
+/**
+ * AJAX command for a JavaScript Drupal.announce() call.
+ *
+ * @ingroup ajax
+ */
+class AnnounceCommand implements CommandInterface, CommandWithAttachedAssetsInterface {
+
+  /**
+   * The assertive priority attribute value.
+   *
+   * @var string
+   */
+  const PRIORITY_ASSERTIVE = 'assertive';
+
+  /**
+   * The polite priority attribute value.
+   *
+   * @var string
+   */
+  const PRIORITY_POLITE = 'polite';
+
+  /**
+   * The text to be announced.
+   *
+   * @var string
+   */
+  protected $text;
+
+  /**
+   * The priority that will be used for the announcement.
+   *
+   * @var string
+   */
+  protected $priority;
+
+  /**
+   * Constructs an AnnounceCommand object.
+   *
+   * @param string $text
+   *   The text to be announced.
+   * @param string|null $priority
+   *   (optional) The priority that will be used for the announcement. Defaults
+   *   to NULL which will not set a 'priority' in the response sent to the
+   *   client and therefore the JavaScript Drupal.announce() default of 'polite'
+   *   will be used for the message.
+   */
+  public function __construct($text, $priority = NULL) {
+    $this->text = $text;
+    $this->priority = $priority;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function render() {
+    $render = [
+      'command' => 'announce',
+      'text' => $this->text,
+    ];
+    if ($this->priority !== NULL) {
+      $render['priority'] = $this->priority;
+    }
+    return $render;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAttachedAssets() {
+    $assets = new AttachedAssets();
+    $assets->setLibraries(['core/drupal.announce']);
+    return $assets;
+  }
+
+}
diff --git a/core/misc/ajax.es6.js b/core/misc/ajax.es6.js
index 39784c403c2a9b2d362e9efb93e67851b4901d60..ca3c0a5e5db9db645fc5526ae1df7926848573a1 100644
--- a/core/misc/ajax.es6.js
+++ b/core/misc/ajax.es6.js
@@ -1336,6 +1336,26 @@
       window.alert(response.text, response.title);
     },
 
+    /**
+     * Command to provide triggers audio UAs to read the supplied text.
+     *
+     * @param {Drupal.Ajax} [ajax]
+     *   {@link Drupal.Ajax} object created by {@link Drupal.ajax}.
+     * @param {object} response
+     *   The JSON response from the Ajax request.
+     * @param {string} [response.text]
+     *   The text that will be read.
+     * @param {string} [response.priority]
+     *   An optional priority that will be used for the announcement.
+     */
+    announce(ajax, response) {
+      if (response.priority) {
+        Drupal.announce(response.text, response.priority);
+      } else {
+        Drupal.announce(response.text);
+      }
+    },
+
     /**
      * Command to set the window.location, redirecting the browser.
      *
diff --git a/core/misc/ajax.js b/core/misc/ajax.js
index 79aac05e8520f2e672dd2953cf8aba9a594cccec..cceffe4762463ba6768e9c301138df62a43a378b 100644
--- a/core/misc/ajax.js
+++ b/core/misc/ajax.js
@@ -576,6 +576,13 @@ function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr
     alert: function alert(ajax, response, status) {
       window.alert(response.text, response.title);
     },
+    announce: function announce(ajax, response) {
+      if (response.priority) {
+        Drupal.announce(response.text, response.priority);
+      } else {
+        Drupal.announce(response.text);
+      }
+    },
     redirect: function redirect(ajax, response, status) {
       window.location = response.url;
     },
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 a93372d06d10585b07b6f2c68bac72375579504c..7adeffef9cec7c01b5ed7cc29712e0017d7a55f6 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
@@ -9,6 +9,7 @@
 use Drupal\Core\Ajax\AfterCommand;
 use Drupal\Core\Ajax\AjaxResponse;
 use Drupal\Core\Ajax\AlertCommand;
+use Drupal\Core\Ajax\AnnounceCommand;
 use Drupal\Core\Ajax\AppendCommand;
 use Drupal\Core\Ajax\BeforeCommand;
 use Drupal\Core\Ajax\ChangedCommand;
@@ -43,6 +44,35 @@ function ajax_forms_test_advanced_commands_alert_callback($form, FormStateInterf
   return $response;
 }
 
+/**
+ * Ajax form callback: Selects 'announce' with no priority specified.
+ */
+function ajax_forms_test_advanced_commands_announce_callback($form, FormStateInterface $form_state) {
+  return (new AjaxResponse())->addCommand(new AnnounceCommand('Default announcement.'));
+}
+
+/**
+ * Ajax form callback: Selects 'announce' with 'polite' priority.
+ */
+function ajax_forms_test_advanced_commands_announce_polite_callback($form, FormStateInterface $form_state) {
+  return (new AjaxResponse())->addCommand(new AnnounceCommand('Polite announcement.', 'polite'));
+}
+
+/**
+ * Ajax form callback: Selects 'announce' with 'assertive' priority.
+ */
+function ajax_forms_test_advanced_commands_announce_assertive_callback($form, FormStateInterface $form_state) {
+  return (new AjaxResponse())->addCommand(new AnnounceCommand('Assertive announcement.', 'assertive'));
+}
+
+/**
+ * Ajax form callback: Selects 'announce' with two announce commands returned.
+ */
+function ajax_forms_test_advanced_commands_double_announce_callback($form, FormStateInterface $form_state) {
+  return (new AjaxResponse())->addCommand(new AnnounceCommand('Assertive announcement.', 'assertive'))
+    ->addCommand(new AnnounceCommand('Another announcement.'));
+}
+
 /**
  * Ajax form callback: Selects 'append'.
  */
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 924a9fda61ceaf652944000423cd3c22e15d2e4e..8804b1bd3788e2540c8b07391cdf04d92968a675 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
@@ -44,6 +44,42 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       ],
     ];
 
+    // Shows the 'announce' command with default priority.
+    $form['announce_command_example'] = [
+      '#value' => $this->t("AJAX 'Announce': Click to announce"),
+      '#type' => 'submit',
+      '#ajax' => [
+        'callback' => 'ajax_forms_test_advanced_commands_announce_callback',
+      ],
+    ];
+
+    // Shows the 'announce' command with 'polite' priority.
+    $form['announce_command_polite_example'] = [
+      '#value' => $this->t("AJAX 'Announce': Click to announce with 'polite' priority"),
+      '#type' => 'submit',
+      '#ajax' => [
+        'callback' => 'ajax_forms_test_advanced_commands_announce_polite_callback',
+      ],
+    ];
+
+    // Shows the 'announce' command with 'assertive' priority.
+    $form['announce_command_assertive_example'] = [
+      '#value' => $this->t("AJAX 'Announce': Click to announce with 'assertive' priority"),
+      '#type' => 'submit',
+      '#ajax' => [
+        'callback' => 'ajax_forms_test_advanced_commands_announce_assertive_callback',
+      ],
+    ];
+
+    // Shows the 'announce' command used twice in one AjaxResponse.
+    $form['announce_command_double_example'] = [
+      '#value' => $this->t("AJAX 'Announce': Click to announce twice"),
+      '#type' => 'submit',
+      '#ajax' => [
+        'callback' => 'ajax_forms_test_advanced_commands_double_announce_callback',
+      ],
+    ];
+
     // Shows the 'append' command.
     $form['append_command_example'] = [
       '#value' => $this->t("AJAX 'Append': Click to append something"),
diff --git a/core/tests/Drupal/FunctionalJavascriptTests/Ajax/CommandsTest.php b/core/tests/Drupal/FunctionalJavascriptTests/Ajax/CommandsTest.php
index f2e95770482380e242cee605b05a08aabea022c6..eca511d82239b4b63127f7bfe483b84b59341ade 100644
--- a/core/tests/Drupal/FunctionalJavascriptTests/Ajax/CommandsTest.php
+++ b/core/tests/Drupal/FunctionalJavascriptTests/Ajax/CommandsTest.php
@@ -52,6 +52,22 @@ public function testAjaxCommands() {
     $this->assertEquals('Alert', $alert_text);
     $this->getSession()->getDriver()->getWebDriverSession()->accept_alert();
 
+    $this->drupalGet($form_path);
+    $page->pressButton("AJAX 'Announce': Click to announce");
+    $this->assertWaitPageContains('<div id="drupal-live-announce" class="visually-hidden" aria-live="polite" aria-busy="false">Default announcement.</div>');
+
+    $this->drupalGet($form_path);
+    $page->pressButton("AJAX 'Announce': Click to announce with 'polite' priority");
+    $this->assertWaitPageContains('<div id="drupal-live-announce" class="visually-hidden" aria-live="polite" aria-busy="false">Polite announcement.</div>');
+
+    $this->drupalGet($form_path);
+    $page->pressButton("AJAX 'Announce': Click to announce with 'assertive' priority");
+    $this->assertWaitPageContains('<div id="drupal-live-announce" class="visually-hidden" aria-live="assertive" aria-busy="false">Assertive announcement.</div>');
+
+    $this->drupalGet($form_path);
+    $page->pressButton("AJAX 'Announce': Click to announce twice");
+    $this->assertWaitPageContains('<div id="drupal-live-announce" class="visually-hidden" aria-live="assertive" aria-busy="false">Assertive announcement.' . "\nAnother announcement.</div>");
+
     // Tests the 'append' command.
     $page->pressButton("AJAX 'Append': Click to append something");
     $this->assertWaitPageContains('<div id="append_div">Append inside this divAppended text</div>');
diff --git a/core/tests/Drupal/Tests/Core/Ajax/AjaxCommandsTest.php b/core/tests/Drupal/Tests/Core/Ajax/AjaxCommandsTest.php
index 1dd66b83283793753d56e1c28107a17aeb5413ad..2a5cc4faf3877ead91177c7ba916cf916cb5aa7f 100644
--- a/core/tests/Drupal/Tests/Core/Ajax/AjaxCommandsTest.php
+++ b/core/tests/Drupal/Tests/Core/Ajax/AjaxCommandsTest.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Tests\Core\Ajax;
 
+use Drupal\Core\Ajax\AnnounceCommand;
+use Drupal\Core\Asset\AttachedAssets;
 use Drupal\Tests\UnitTestCase;
 use Drupal\Core\Ajax\AddCssCommand;
 use Drupal\Core\Ajax\AfterCommand;
@@ -78,6 +80,61 @@ public function testAlertCommand() {
     $this->assertEquals($expected, $command->render());
   }
 
+  /**
+   * @covers \Drupal\Core\Ajax\AnnounceCommand
+   *
+   * @dataProvider announceCommandProvider
+   */
+  public function testAnnounceCommand($message, $priority, array $expected) {
+    if ($priority === NULL) {
+      $command = new AnnounceCommand($message);
+    }
+    else {
+      $command = new AnnounceCommand($message, $priority);
+    }
+
+    $expected_assets = new AttachedAssets();
+    $expected_assets->setLibraries(['core/drupal.announce']);
+
+    $this->assertEquals($expected_assets, $command->getAttachedAssets());
+    $this->assertSame($expected, $command->render());
+  }
+
+  /**
+   * Data provider for testAnnounceCommand().
+   */
+  public function announceCommandProvider() {
+    return [
+      'no priority' => [
+        'Things are going to change!',
+        NULL,
+        [
+          'command' => 'announce',
+          'text' => 'Things are going to change!',
+        ],
+      ],
+      'polite priority' => [
+        'Things are going to change!',
+        'polite',
+        [
+          'command' => 'announce',
+          'text' => 'Things are going to change!',
+          'priority' => AnnounceCommand::PRIORITY_POLITE,
+        ],
+
+      ],
+      'assertive priority' => [
+        'Important!',
+        'assertive',
+        [
+          'command' => 'announce',
+          'text' => 'Important!',
+          'priority' => AnnounceCommand::PRIORITY_ASSERTIVE,
+        ],
+      ],
+    ];
+  }
+
   /**
    * @covers \Drupal\Core\Ajax\AppendCommand
    */