diff --git a/src/wp-admin/css/wp-admin.css b/src/wp-admin/css/wp-admin.css index 950f36b5b8..556ff70f3b 100644 --- a/src/wp-admin/css/wp-admin.css +++ b/src/wp-admin/css/wp-admin.css @@ -6465,6 +6465,24 @@ span.imgedit-scale-warn { -webkit-transition: opacity 0.1s ease-in-out; transition: opacity 0.1s ease-in-out; } +.theme-browser .theme:focus { + outline: 1px dotted #222; +} +/* Hide shortcut actions and hover feedback when using tab navigation */ +.theme-browser .theme:focus .theme-actions { + display: none; +} +/* Restore display of theme controls if you hover a focused theme */ +.theme-browser .theme:focus:hover .theme-actions { + display: block; +} +.theme-browser .theme:focus .more-details { + opacity: 1; +} +/* Current theme needs to have its action always on view */ +.theme-browser .theme.active:focus .theme-actions { + display: block; +} .theme-browser.rendered .theme:hover .more-details { opacity: 1; @@ -6690,10 +6708,13 @@ body.theme-overlay-open { width: 50px; text-align: center; float: right; + border: 0; border-left: 1px solid #ddd; + background-color: transparent; } -.theme-overlay .theme-header .close:hover:before { +.theme-overlay .theme-header .close:hover:before, +.theme-overlay .theme-header .close:focus:before { color: #fff; } @@ -6710,20 +6731,21 @@ body.theme-overlay-open { .theme-overlay .theme-header .left { cursor: pointer; color: #777; + background-color: transparent; height: 48px; width: 54px; float: left; text-align: center; + border: 0; border-right: 1px solid #ddd; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; } .theme-overlay .theme-header .close:hover, .theme-overlay .theme-header .right:hover, -.theme-overlay .theme-header .left:hover { +.theme-overlay .theme-header .left:hover, +.theme-overlay .theme-header .close:focus, +.theme-overlay .theme-header .right:focus, +.theme-overlay .theme-header .left:focus { background: #0074a2; color: #fff; } @@ -6832,7 +6854,8 @@ body.folded .theme-overlay .theme-wrap { background: transparent; } -.theme-overlay .theme-actions .delete-theme:hover { +.theme-overlay .theme-actions .delete-theme:hover, +.theme-overlay .theme-actions .delete-theme:focus { background: #d54e21; color: #fff; border-color: #d54e21; diff --git a/src/wp-admin/js/theme.js b/src/wp-admin/js/theme.js index d576b24482..c9e2681220 100644 --- a/src/wp-admin/js/theme.js +++ b/src/wp-admin/js/theme.js @@ -188,6 +188,7 @@ themes.view.Theme = wp.Backbone.View.extend({ events: { 'click': 'expand', + 'keydown': 'expand', 'touchend': 'expand', 'touchmove': 'preventExpand' }, @@ -197,7 +198,8 @@ themes.view.Theme = wp.Backbone.View.extend({ render: function() { var data = this.model.toJSON(); // Render themes using the html template - this.$el.html( this.html( data ) ); + this.$el.html( this.html( data ) ).attr( 'tabindex', 0 ); + // Renders active theme styles this.activeTheme(); @@ -219,19 +221,27 @@ themes.view.Theme = wp.Backbone.View.extend({ expand: function( event ) { var self = this; + event = event || window.event; + + // 'enter' and 'space' keys expand the details view when a theme is :focused + if ( event.type === 'keydown' && ( event.which !== 13 && event.which !== 32 ) ) { + return; + } + // Bail if the user scrolled on a touch device if ( this.touchDrag === true ) { return this.touchDrag = false; } - event = event || window.event; - // Prevent the modal from showing when the user clicks // one of the direct action buttons if ( $( event.target ).is( '.theme-actions a' ) ) { return; } + // Set focused theme to current element + themes.focusedTheme = this.$el; + this.trigger( 'theme:expand', self.model.cid ); }, @@ -266,6 +276,8 @@ themes.view.Details = wp.Backbone.View.extend({ this.navigation(); // Checks screenshot size this.screenshotCheck( this.$el ); + // Contain "tabbing" inside the overlay + this.containFocus( this.$el ); }, // Adds a class to the currently active theme @@ -275,6 +287,34 @@ themes.view.Details = wp.Backbone.View.extend({ this.$el.toggleClass( 'active', this.model.get( 'active' ) ); }, + // Keeps :focus within the theme details elements + containFocus: function( $el ) { + var $target; + + // Move focus to the primary action + _.delay( function() { + $( '.theme-wrap a.button-primary:visible' ).focus(); + }, 500 ); + + $el.on( 'keydown.wp-themes', function( event ) { + + // Tab key + if ( event.which === 9 ) { + $target = $( event.target ); + + // Keep focus within the overlay by making the last link on theme actions + // switch focus to button.left on tabbing and vice versa + if ( $target.is( 'button.left' ) && event.shiftKey ) { + $el.find( '.theme-actions a:last-child' ).focus(); + event.preventDefault(); + } else if ( $target.is( '.theme-actions a:last-child' ) ) { + $el.find( 'button.left' ).focus(); + event.preventDefault(); + } + } + }); + }, + // Single theme overlay screen // It's shown when clicking a theme collapse: function( event ) { @@ -291,7 +331,7 @@ themes.view.Details = wp.Backbone.View.extend({ // Detect if the click is inside the overlay // and don't close it unless the target was // the div.back button - if ( $( event.target ).is( '.theme-backdrop' ) || $( event.target ).is( 'div.close' ) || event.keyCode === 27 ) { + if ( $( event.target ).is( '.theme-backdrop' ) || $( event.target ).is( '.close' ) || event.keyCode === 27 ) { // Add a temporary closing class while overlay fades out $( 'body' ).addClass( 'closing-overlay' ); @@ -311,6 +351,11 @@ themes.view.Details = wp.Backbone.View.extend({ // Restore scroll position document.body.scrollTop = scroll; + + // Return focus to the theme div + if ( themes.focusedTheme ) { + themes.focusedTheme.focus(); + } }); } }, diff --git a/src/wp-admin/themes.php b/src/wp-admin/themes.php index 435abfe415..3493261041 100644 --- a/src/wp-admin/themes.php +++ b/src/wp-admin/themes.php @@ -192,7 +192,7 @@ if ( ! $ct->errors() || ( 1 == count( $ct->errors()->get_error_codes() ) */ foreach ( $themes as $theme ) : ?> -
+
@@ -309,9 +309,9 @@ if ( ! is_multisite() && current_user_can('edit_themes') && $broken_themes = wp_
-
-
-
+ + +