diff --git a/core/modules/contextual/contextual.base.css b/core/modules/contextual/contextual.base.css
index 5c20c8a..379f334 100644
--- a/core/modules/contextual/contextual.base.css
+++ b/core/modules/contextual/contextual.base.css
@@ -7,14 +7,11 @@
 /**
  * Contextual links behavior.
  */
-.contextual,
-.contextual .contextual-links,
-.contextual .trigger {
+.contextual .contextual-links {
   display: none;
 }
-.contextual-region:hover .contextual,
-.contextual-region:hover .contextual-links-trigger-active,
-.contextual-active .contextual-links {
+.contextual .trigger,
+.contextual-region:hover .contextual {
   display: block;
 }
 
@@ -27,6 +24,7 @@
 .contextual {
   position: absolute;
   z-index: 999;
+  display: block;
 }
 .contextual .trigger {
   overflow: hidden;
diff --git a/core/modules/contextual/contextual.js b/core/modules/contextual/contextual.js
index 3b7aeeb..2f14d1b 100644
--- a/core/modules/contextual/contextual.js
+++ b/core/modules/contextual/contextual.js
@@ -7,48 +7,132 @@
 
 "use strict";
 
-Drupal.contextualLinks = Drupal.contextualLinks || {};
-
 /**
  * Attaches outline behavior for regions associated with contextual links.
  */
 Drupal.behaviors.contextualLinks = {
   attach: function (context) {
-    $(context).find('div.contextual').once('contextual-links', function () {
-      var $wrapper = $(this);
-      var $region = $wrapper.closest('.contextual-region');
-      var $links = $wrapper.find('ul');
-      var $trigger = $('<a class="trigger" href="#" />').text(Drupal.t('Configure')).click(
-        function () {
-          $links.stop(true, true).slideToggle(100);
-          $wrapper.toggleClass('contextual-active');
-          return false;
-        }
-      );
-      // Attach hover behavior to trigger and ul.contextual-links.
-      $trigger.add($links).hover(
-        function () { $region.addClass('contextual-region-active'); },
-        function () { $region.removeClass('contextual-region-active'); }
-      );
-      // Hide the contextual links when user clicks a link or rolls out of the .contextual-region.
-      $region.bind('mouseleave click', Drupal.contextualLinks.mouseleave);
-      $region.hover(
-        function() { $trigger.addClass('contextual-links-trigger-active'); },
-        function() { $trigger.removeClass('contextual-links-trigger-active'); }
-      );
-      // Prepend the trigger.
-      $wrapper.prepend($trigger);
+    $('ul.contextual-links', context).once('contextual-links', function () {
+      var $this = $(this);
+      $this.data('drupal-contextual', new Drupal.contextualLinks(this, $this.closest('.contextual-region')));
     });
   }
 };
 
 /**
- * Disables outline for the region contextual links are associated with.
+ * Contextual links object.
  */
-Drupal.contextualLinks.mouseleave = function () {
-  $(this)
-    .find('.contextual-active').removeClass('contextual-active')
-    .find('.contextual-links').hide();
+Drupal.contextualLinks = function(links, region) {
+  this.links = links;
+  this.region = region;
+
+  this.isHighlighted = false;
+  this.triggerHighlighted = false;
+  this.isOpen = false;
+
+  this.init();
+};
+
+Drupal.contextualLinks.prototype.init = function() {
+  var self = this;
+
+  this.wrapper = $('<div class="contextual" />')
+    .insertBefore(this.links)
+    .append(this.links)
+    // Only store a reference to the DOM element itself to preserve memory.
+    [0];
+
+  this.region.mouseenter(function () {
+      self.highlightTrigger(true);
+    })
+    .mouseleave(function () {
+      self.highlightTrigger(false);
+    });
+
+  this.trigger = $('<a class="trigger" href="#" />')
+    .text(Drupal.t('Configure'))
+    .click(function () {
+      self.showLinks();
+      return false;
+    })
+    .focus(function () {
+      self.highlightTrigger(true);
+    })
+    .focusout(function () {
+      self.highlightTrigger(false);
+    })
+    .prependTo(this.wrapper);
+
+  this.trigger
+    .add(this.links)
+    .mouseenter(function () {
+      self.highlightRegion(true);
+    })
+    .mouseleave(function () {
+      self.highlightRegion(false);
+    });
+
+  // Hide the contextual links when mouse is moved outside of its region.
+  $(this.region).mouseleave(function () {
+    self.showLinks(false);
+  });
+
+  // Hide the contextual links when focus is moved outside of contextual links
+  // wrapper, for example when user uses keyboard (TAB) navigation.
+  $(document).bind('focusin', function (e) {
+    if (self.isOpen && !$(e.target).closest('div.contextual', self.region).length) {
+      self.highlightRegion(false);
+      self.highlightTrigger(false);
+      self.showLinks(false);
+    }
+  });
+};
+
+Drupal.contextualLinks.prototype.highlightTrigger = function(highlight) {
+  if (highlight === undefined) {
+    highlight = !this.isHighlighted;
+  }
+
+  if (this.isHighlighted && !highlight) {
+    $(this.trigger).removeClass('contextual-links-trigger-active');
+    this.isHighlighted = false;
+  }
+  else if (!this.isHighlighted && highlight) {
+    $(this.trigger).addClass('contextual-links-trigger-active');
+    this.isHighlighted = true;
+  }
+};
+
+Drupal.contextualLinks.prototype.highlightRegion = function(highlight) {
+  if (highlight === undefined) {
+    highlight = !this.triggerHighlighted;
+  }
+
+  if (this.triggerHighlighted && !highlight) {
+    $(this.region).removeClass('contextual-region-active');
+    this.triggerHighlighted = false;
+  }
+  else if (!this.triggerHighlighted && highlight) {
+    $(this.region).addClass('contextual-region-active');
+    this.triggerHighlighted = true;
+  }
+};
+
+Drupal.contextualLinks.prototype.showLinks = function(show) {
+  if (show === undefined) {
+    show = !this.isOpen;
+  }
+
+  if (this.isOpen && !show) {
+    $(this.links).stop(true, true).slideUp(100);
+    $(this.wrapper).removeClass('contextual-links-active');
+    this.isOpen = false;
+  }
+  else if (!this.isOpen && show) {
+    $(this.links).stop(true, true).slideDown(100);
+    $(this.wrapper).addClass('contextual-links-active');
+    this.isOpen = true;
+  }
 };
 
 })(jQuery);
diff --git a/core/modules/contextual/contextual.module b/core/modules/contextual/contextual.module
index 844e9ee..4c3daa3 100644
--- a/core/modules/contextual/contextual.module
+++ b/core/modules/contextual/contextual.module
@@ -63,8 +63,6 @@ function contextual_element_info() {
     '#pre_render' => array('contextual_pre_render_links'),
     '#theme' => 'links__contextual',
     '#links' => array(),
-    '#prefix' => '<div class="contextual">',
-    '#suffix' => '</div>',
     '#attributes' => array('class' => array('contextual-links')),
     '#attached' => array(
       'library' => array(
diff --git a/core/modules/contextual/contextual.theme.css b/core/modules/contextual/contextual.theme.css
index 48d8e83..fb9fc2e 100644
--- a/core/modules/contextual/contextual.theme.css
+++ b/core/modules/contextual/contextual.theme.css
@@ -12,8 +12,9 @@
   outline-offset: 1px;
 }
 .contextual {
-  right: 2px; /* LTR */
+  right: 5px; /* LTR */
   top: 2px;
+  font-size: 90%;
 }
 
 /**
@@ -29,18 +30,21 @@
   padding: 0 2px;
   text-indent: 34px;
   width: 28px;
+  position: absolute;
+  right: 0;
+  clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
+  clip: rect(1px, 1px, 1px, 1px);
 }
-.contextual .trigger:hover,
-.contextual-active .trigger {
+.contextual-links-trigger-active {
   background-position: 2px -18px;
+  clip: auto !important;
 }
-.contextual-active .trigger {
-  background-color: #ffffff;
+.contextual-links-active .trigger {
+  background-color: #fff;
   border-bottom: none;
   border-color: #d6d6d6;
   -moz-border-radius: 4px 4px 0 0; /* FF3.6 */
   border-radius: 4px 4px 0 0;
-  position: relative;
   z-index: 1;
 }
 
@@ -68,6 +72,7 @@
   list-style-image: none;
   margin: 0;
   padding: 0;
+  line-height: 100%;
 }
 .contextual-region .contextual .contextual-links a {
   display: block;
@@ -87,4 +92,5 @@
 }
 .contextual-region .contextual .contextual-links li a:hover {
   background-color: #bfdcee;
+  outline: none;
 }
