diff --git a/core/misc/dialog/dialog.ajax.es6.js b/core/misc/dialog/dialog.ajax.es6.js
index a6f2e71ce7..7d3452fd7a 100644
--- a/core/misc/dialog/dialog.ajax.es6.js
+++ b/core/misc/dialog/dialog.ajax.es6.js
@@ -132,6 +132,10 @@
       response.dialogOptions.buttons = Drupal.behaviors.dialog.prepareDialogButtons($dialog);
     }
 
+    if (ajax.hasOwnProperty('element') && ajax.element instanceof Element) {
+      response.dialogOptions.drupalTriggerElement = ajax.element;
+    }
+
     // Bind dialogButtonsChange.
     $dialog.on('dialogButtonsChange', () => {
       const buttons = Drupal.behaviors.dialog.prepareDialogButtons($dialog);
diff --git a/core/misc/dialog/dialog.ajax.js b/core/misc/dialog/dialog.ajax.js
index de1d3c9219..53130377a7 100644
--- a/core/misc/dialog/dialog.ajax.js
+++ b/core/misc/dialog/dialog.ajax.js
@@ -85,6 +85,10 @@
       response.dialogOptions.buttons = Drupal.behaviors.dialog.prepareDialogButtons($dialog);
     }
 
+    if (ajax.hasOwnProperty('element') && ajax.element instanceof Element) {
+      response.dialogOptions.drupalTriggerElement = ajax.element;
+    }
+
     $dialog.on('dialogButtonsChange', function () {
       var buttons = Drupal.behaviors.dialog.prepareDialogButtons($dialog);
       $dialog.dialog('option', 'buttons', buttons);
diff --git a/core/modules/contextual/js/contextual.es6.js b/core/modules/contextual/js/contextual.es6.js
index 701ed126cb..705411ef2b 100644
--- a/core/modules/contextual/js/contextual.es6.js
+++ b/core/modules/contextual/js/contextual.es6.js
@@ -263,4 +263,30 @@
   $(document).on('drupalContextualLinkAdded', (event, data) => {
     Drupal.ajax.bindAjaxLinks(data.$el[0]);
   });
+
+  // The contextual button for the link that opened the dialog.
+  let dialogLinkContextualButton;
+  $(window).on({
+    'dialog:beforecreate': (event, dialog, $element, settings) => {
+      if (settings.hasOwnProperty('drupalTriggerElement')) {
+        // Determine if the trigger element is a contextual link.
+        if ($(settings.drupalTriggerElement).closest('[data-contextual-id]').find('button').length === 1) {
+          /**
+           * Save the contextual link button so it will be available for
+           * 'dialog:afterclose'.
+           */
+          dialogLinkContextualButton = $(settings.drupalTriggerElement)
+            .closest('[data-contextual-id]')
+            .find('button')
+            .get(0);
+        }
+      }
+    },
+    'dialog:afterclose': (event, dialog, $element) => {
+      if (dialogLinkContextualButton) {
+        // Set focus to the contextual trigger button.
+        $(dialogLinkContextualButton).focus();
+      }
+    },
+  });
 }(jQuery, Drupal, drupalSettings, _, Backbone, window.JSON, window.sessionStorage));
diff --git a/core/modules/contextual/js/contextual.js b/core/modules/contextual/js/contextual.js
index ed210d070d..79e505e4c5 100644
--- a/core/modules/contextual/js/contextual.js
+++ b/core/modules/contextual/js/contextual.js
@@ -148,4 +148,20 @@
   $(document).on('drupalContextualLinkAdded', function (event, data) {
     Drupal.ajax.bindAjaxLinks(data.$el[0]);
   });
-})(jQuery, Drupal, drupalSettings, _, Backbone, window.JSON, window.sessionStorage);
\ No newline at end of file
+
+  var dialogLinkContextualButton = void 0;
+  $(window).on({
+    'dialog:beforecreate': function dialogBeforecreate(event, dialog, $element, settings) {
+      if (settings.hasOwnProperty('drupalTriggerElement')) {
+        if ($(settings.drupalTriggerElement).closest('[data-contextual-id]').find('button').length === 1) {
+          dialogLinkContextualButton = $(settings.drupalTriggerElement).closest('[data-contextual-id]').find('button').get(0);
+        }
+      }
+    },
+    'dialog:afterclose': function dialogAfterclose(event, dialog, $element) {
+      if (dialogLinkContextualButton) {
+        $(dialogLinkContextualButton).focus();
+      }
+    }
+  });
+})(jQuery, Drupal, drupalSettings, _, Backbone, window.JSON, window.sessionStorage);
diff --git a/core/modules/contextual/tests/src/FunctionalJavascript/ContextualLinksTest.php b/core/modules/contextual/tests/src/FunctionalJavascript/ContextualLinksTest.php
index de83692411..f8e414dbb0 100644
--- a/core/modules/contextual/tests/src/FunctionalJavascript/ContextualLinksTest.php
+++ b/core/modules/contextual/tests/src/FunctionalJavascript/ContextualLinksTest.php
@@ -80,6 +80,11 @@ public function testContextualLinksClick() {
     // Check to make sure that page was not reloaded.
     $this->assertSession()->pageTextContains($current_page_string);
 
+    // Close the dialog.
+    $this->getSession()->getPage()->find('css', '.ui-dialog .ui-dialog-titlebar-close')->click();
+    // Confirm the contextual button has become the active element.
+    $this->assertJsCondition('document.activeElement === document.querySelector("#block-branding [data-contextual-id] button")');
+
     // Test clicking contextual link with toolbar.
     $this->container->get('module_installer')->install(['toolbar']);
     $this->grantPermissions(Role::load(Role::AUTHENTICATED_ID), ['access toolbar']);
