diff --git modules/contextual/contextual.css modules/contextual/contextual.css
index e49eb37..0840cb1 100644
--- modules/contextual/contextual.css
+++ modules/contextual/contextual.css
@@ -14,24 +14,25 @@
  * Contextual links.
  */
 div.contextual-links-wrapper {
-  display: none;
+  display: block;
   font-size: 90%;
   position: absolute;
   right: 5px; /* LTR */
   top: 2px;
   z-index: 999;
 }
-html.js div.contextual-links-wrapper {
-  display: block;
-}
-a.contextual-links-trigger {
+div.contextual-links-wrapper a.contextual-links-trigger {
   background: transparent url(images/gear-select.png) no-repeat 2px 0;
   border: 1px solid transparent;
-  display: none;
+  clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
+  clip: rect(1px, 1px, 1px, 1px);
+  display: block;
   height: 18px;
   margin: 0;
   padding: 0 2px;
+  position: absolute;
   outline: none;
+  right: 0;
   text-indent: 34px; /* LTR */
   width: 28px;
   overflow: hidden;
@@ -40,26 +41,30 @@
   -webkit-border-radius: 4px;
   border-radius: 4px;
 }
-a.contextual-links-trigger:hover,
+div.contextual-links-wrapper a.contextual-links-trigger:hover,
+div.contextual-links-wrapper a.contextual-links-trigger:focus,
+.contextual-links-region:hover a.contextual-links-trigger,
 div.contextual-links-active a.contextual-links-trigger {
   background-position: 2px -18px;
+  clip: auto;
 }
 div.contextual-links-active a.contextual-links-trigger {
-  background-position: 2px -18px;
   background-color: #fff;
   border-color: #ccc;
   border-bottom: none;
-  position: relative;
   z-index: 1;
   -moz-border-radius: 4px 4px 0 0;
   -webkit-border-bottom-left-radius: 0;
   -webkit-border-bottom-right-radius: 0;
   border-radius: 4px 4px 0 0;
+}
+
+ul.contextual-links {
+  display: none;
 }
 div.contextual-links-wrapper ul.contextual-links {
   background-color: #fff;
   border: 1px solid #ccc;
-  display: none;
   margin: 0;
   padding: 0.25em 0;
   position: absolute;
@@ -74,12 +79,10 @@
   -webkit-border-top-left-radius: 4px; /* LTR */
   border-radius: 4px 0 4px 4px; /* LTR */
 }
-.contextual-links-region:hover a.contextual-links-trigger,
-div.contextual-links-active a.contextual-links-trigger,
 div.contextual-links-active ul.contextual-links {
   display: block;
 }
-ul.contextual-links li {
+div.contextual-links-wrapper ul.contextual-links li {
   line-height: 100%;
   list-style: none;
   list-style-image: none;
@@ -89,12 +92,15 @@
 div.contextual-links-wrapper a {
   text-decoration: none;
 }
-ul.contextual-links li a {
+div.contextual-links-wrapper ul.contextual-links li a {
   color: #333 !important;
   display: block;
   margin: 0.25em 0;
   padding: 0.25em 1em 0.25em 0.5em;
 }
-ul.contextual-links li a:hover {
+div.contextual-links-wrapper ul.contextual-links li a:active,
+div.contextual-links-wrapper ul.contextual-links li a:focus,
+div.contextual-links-wrapper ul.contextual-links li a:hover {
+  outline: none;
   background-color: #bfdcee;
 }
diff --git modules/contextual/contextual.js modules/contextual/contextual.js
index ee5b7a0..c19abca 100644
--- modules/contextual/contextual.js
+++ modules/contextual/contextual.js
@@ -1,43 +1,103 @@
 (function ($) {
 
-Drupal.contextualLinks = Drupal.contextualLinks || {};
-
 /**
  * Attach outline behavior for regions associated with contextual links.
  */
 Drupal.behaviors.contextualLinks = {
   attach: function (context) {
-    $('div.contextual-links-wrapper', context).once('contextual-links', function () {
-      var $wrapper = $(this);
-      var $region = $wrapper.closest('.contextual-links-region');
-      var $links = $wrapper.find('ul.contextual-links');
-      var $trigger = $('<a class="contextual-links-trigger" href="#" />').text(Drupal.t('Configure')).click(
-        function () {
-          $links.stop(true, true).slideToggle(100);
-          $wrapper.toggleClass('contextual-links-active');
-          return false;
-        }
-      );
-      // Attach hover behavior to trigger and ul.contextual-links.
-      $trigger.add($links).hover(
-        function () { $region.addClass('contextual-links-region-active'); },
-        function () { $region.removeClass('contextual-links-region-active'); }
-      );
-      // Hide the contextual links when user clicks a link or rolls out of the .contextual-links-region.
-      $region.bind('mouseleave click', Drupal.contextualLinks.mouseleave);
-      // 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-links-region')[0]));
     });
   }
 };
 
 /**
- * Disables outline for the region contextual links are associated with.
+ * Contextual links object.
  */
-Drupal.contextualLinks.mouseleave = function () {
-  $(this)
-    .find('.contextual-links-active').removeClass('contextual-links-active')
-    .find('ul.contextual-links').hide();
+Drupal.contextualLinks = function(links, region) {
+  this.links = links;
+  this.region = region;
+
+  this.isHighlighted = false;
+  this.isOpen = false;
+
+  this.init();
+};
+
+Drupal.contextualLinks.prototype.init = function() {
+  var self = this;
+
+  this.wrapper = $('<div class="contextual-links-wrapper" />')
+    .insertBefore(this.links)
+    .append(this.links)
+    .mouseenter(function () {
+      self.highlightRegion(true);
+    })
+    .mouseleave(function () {
+      self.highlightRegion(false);
+    })
+    // Only store a reference to the DOM element itself to preserve memory.
+    [0];
+
+  this.trigger = $('<a class="contextual-links-trigger" href="#" />')
+    .text(Drupal.t('Configure'))
+    .click(function () {
+      self.showLinks();
+      return false;
+    })
+    .focus(function () {
+      self.highlightRegion(true);
+    })
+    .prependTo(this.wrapper)
+    // Only store a reference to the DOM element itself to preserve memory.
+    [0];
+
+  // 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-links-wrapper', self.region).length) {
+      self.highlightRegion(false);
+      self.showLinks(false);
+    }
+  });
+};
+
+Drupal.contextualLinks.prototype.highlightRegion = function(highlight) {
+  if (highlight === undefined) {
+    highlight = !this.isHighlighted;
+  }
+
+  if (this.isHighlighted && !highlight) {
+    $(this.region).removeClass('contextual-links-region-active');
+    this.isHighlighted = false;
+  }
+  else if (!this.isHighlighted && highlight) {
+    $(this.region).addClass('contextual-links-region-active');
+    this.isHighlighted = 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 modules/contextual/contextual.module modules/contextual/contextual.module
index 0d6b625..093be8f 100644
--- modules/contextual/contextual.module
+++ modules/contextual/contextual.module
@@ -62,8 +62,6 @@
     '#pre_render' => array('contextual_pre_render_links'),
     '#theme' => 'links__contextual',
     '#links' => array(),
-    '#prefix' => '<div class="contextual-links-wrapper">',
-    '#suffix' => '</div>',
     '#attributes' => array(
       'class' => array('contextual-links'),
     ),
