WordPress.org

Make WordPress Core

Changeset 39141


Ignore:
Timestamp:
11/04/2016 04:01:24 PM (3 years ago)
Author:
jorbin
Message:

Administration: Ensure collapse menu is usable with a keyboard

Currently, the "Collapse menu" item is not focusable and keyboard users can't collapse/expand the admin menu. This aims to fix it so that screen readers no longer announce it as a clickable but it remains unfocusable and thus unusable. So it's now a button.

Quoting joedolson at WordCamp Chicago 2014:
"If it's supposed to act like a button, it should be a button."

Also includes a grunt:precommit run that picked up some postcss changes to src/wp-includes/css/customize-preview.css

Fixes #29958.
Props ajercia, ipm-frommen for an iterative patch, valendesigns for an iterative patch, GaryJ for feedback, joedolson for feedback, helen for feedback

Location:
trunk/src
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/css/admin-menu.css

    r37365 r39141  
    265265.folded #adminmenu a.menu-top:focus + .wp-submenu,
    266266.folded #adminmenu .wp-has-current-submenu a.menu-top:focus + .wp-submenu,
    267 .no-js.folded #adminmenu .wp-has-submenu:hover .wp-submenu  {
     267.no-js.folded #adminmenu .wp-has-submenu:hover .wp-submenu {
    268268    top: 0;
    269269    left: 36px;
     
    548548}
    549549
    550 #collapse-menu {
    551     font-size: 13px;
     550#collapse-button {
     551    display: block;
     552    width: 100%;
     553    height: 34px;
     554    margin: 0;
     555    border: none;
     556    padding: 0;
     557    position: relative;
     558    overflow: visible;
    552559    line-height: 34px;
    553     margin-top: 10px;
    554     color: #a0a5aa;
    555     color: rgba(240,245,250,0.6);
    556     -webkit-transition: all .1s ease-in-out;
    557     transition: all .1s ease-in-out;
    558 }
    559 
    560 #collapse-menu:hover,
    561 #collapse-menu:hover #collapse-button div:after {
     560    background: none;
     561    color: #aaa;
     562    cursor: pointer;
     563    outline: 0;
     564}
     565
     566#collapse-button:hover,
     567#collapse-button:focus {
    562568    color: #00b9eb;
    563569}
    564570
    565 .folded #collapse-menu span {
     571#collapse-button .collapse-button-icon,
     572#collapse-button .collapse-button-label {
     573    /* absolutely positioned to avoid 1px shift in IE when button is pressed */
     574    display: block;
     575    position: absolute;
     576    top: 0;
     577    left: 0;
     578    line-height: 34px;
     579}
     580
     581#collapse-button .collapse-button-icon {
     582    width: 36px;
     583    height: 34px;
     584}
     585
     586#collapse-button .collapse-button-label {
     587    padding: 0 0 0 36px;
     588}
     589
     590.folded #collapse-button .collapse-button-label {
    566591    display: none;
    567592}
    568593
    569 #collapse-button,
    570 #collapse-button div {
    571     width: 15px;
    572     height: 15px;
    573 }
    574 
    575 #collapse-button {
    576     float: left;
    577     height: 15px;
    578     margin: 10px 8px 10px 11px;
    579     width: 15px;
    580     -webkit-border-radius: 10px;
    581     border-radius: 10px;
    582 }
    583 
    584 #wpwrap #collapse-button div {
    585     padding: 0;
    586 }
    587 
    588 #collapse-button div:after {
     594#collapse-button .collapse-button-icon:after {
    589595    content: "\f148";
    590596    display: block;
    591     line-height: 15px;
    592     left: -3px;
    593     top: -3px;
    594     color: #a0a5aa;
    595     color: rgba(240,245,250,0.6);
     597    position: relative;
     598    top: 7px;
     599    text-align: center;
    596600    font: normal 20px/1 dashicons !important;
    597601    speak: none;
    598     margin: 0 auto;
    599     padding: 0 !important;
    600     position: relative;
    601     text-align: center;
    602     width: 20px;
    603     -webkit-transition: all .1s ease-in-out;
    604     transition: all .1s ease-in-out;
    605602    -webkit-font-smoothing: antialiased;
    606603    -moz-osx-font-smoothing: grayscale;
     
    608605
    609606/* rtl:ignore */
    610 .folded #collapse-button div:after,
    611 .rtl #collapse-button div:after {
     607.folded #collapse-button .collapse-button-icon:after,
     608.rtl #collapse-button .collapse-button-icon:after {
    612609    -webkit-transform: rotate(180deg);
    613610    -ms-transform: rotate(180deg);
     
    615612}
    616613
    617 .rtl.folded #collapse-button div:after {
     614.rtl.folded #collapse-button .collapse-button-icon:after {
    618615    -webkit-transform: none;
    619616    -ms-transform: none;
    620617    transform: none;
     618}
     619
     620#collapse-button .collapse-button-icon:after,
     621#collapse-button .collapse-button-label {
     622    -webkit-transition: all .1s ease-in-out;
     623    transition: all .1s ease-in-out;
    621624}
    622625
     
    653656    .auto-fold #adminmenu .wp-has-current-submenu.opensub .wp-submenu,
    654657    .auto-fold #adminmenu a.menu-top:focus + .wp-submenu,
    655     .auto-fold #adminmenu .wp-has-current-submenu a.menu-top:focus + .wp-submenu  {
     658    .auto-fold #adminmenu .wp-has-current-submenu a.menu-top:focus + .wp-submenu {
    656659        top: 0px;
    657660        left: 36px;
     
    732735    }
    733736
    734     .auto-fold #collapse-menu span {
     737    .auto-fold #collapse-menu .collapse-button-label {
    735738        display: none;
    736739    }
    737740
    738     .auto-fold #collapse-button div {
    739         background: none;
    740     }
    741 
    742741    /* rtl:ignore */
    743     .auto-fold #collapse-button div:after {
     742    .auto-fold #collapse-button .collapse-button-icon:after {
    744743        -webkit-transform: rotate(180deg);
    745744        -ms-transform: rotate(180deg);
     
    747746    }
    748747
    749     .rtl.auto-fold #collapse-button div:after {
     748    .rtl.auto-fold #collapse-button .collapse-button-icon:after {
    750749        -webkit-transform: none;
    751750        -ms-transform: none;
  • trunk/src/wp-admin/css/colors/_admin.scss

    r38660 r39141  
    228228/* Admin Menu: collapse button */
    229229
    230 #collapse-menu {
     230#collapse-button {
    231231    color: $menu-collapse-text;
    232232}
    233233
    234 #collapse-menu:hover {
    235     color: $menu-collapse-focus-text;
    236 }
    237 
    238 #collapse-button div:after {
    239     color: $menu-collapse-icon;
    240 }
    241 
    242 #collapse-menu:hover #collapse-button div:after {
    243     color: $menu-collapse-focus-icon;
    244 }
    245 
     234#collapse-button:hover,
     235#collapse-button:focus {
     236    color: $menu-submenu-focus-text;
     237}
    246238
    247239/* Admin Bar */
  • trunk/src/wp-admin/js/common.js

    r38983 r39141  
    197197        pinnedMenuBottom = false,
    198198        menuTop = 0,
     199        menuState,
    199200        menuIsPinned = false,
    200201        height = {
     
    212213    });
    213214
    214     $('#collapse-menu').on('click.collapse-menu', function() {
    215         var respWidth, state;
     215    $( '#collapse-button' ).on( 'click.collapse-menu', function() {
     216        var viewportWidth = getViewportWidth() || 961;
    216217
    217218        // reset any compensation for submenus near the bottom of the screen
    218219        $('#adminmenu div.wp-submenu').css('margin-top', '');
    219220
    220         if ( window.innerWidth ) {
    221             // window.innerWidth is affected by zooming on phones
    222             respWidth = Math.max( window.innerWidth, document.documentElement.clientWidth );
    223         } else {
    224             // IE < 9 doesn't support @media CSS rules
    225             respWidth = 961;
    226         }
    227 
    228         if ( respWidth && respWidth < 960 ) {
     221        if ( viewportWidth < 960 ) {
    229222            if ( $body.hasClass('auto-fold') ) {
    230223                $body.removeClass('auto-fold').removeClass('folded');
    231224                setUserSetting('unfold', 1);
    232225                setUserSetting('mfold', 'o');
    233                 state = 'open';
     226                menuState = 'open';
    234227            } else {
    235228                $body.addClass('auto-fold');
    236229                setUserSetting('unfold', 0);
    237                 state = 'folded';
     230                menuState = 'folded';
    238231            }
    239232        } else {
     
    241234                $body.removeClass('folded');
    242235                setUserSetting('mfold', 'o');
    243                 state = 'open';
     236                menuState = 'open';
    244237            } else {
    245238                $body.addClass('folded');
    246239                setUserSetting('mfold', 'f');
    247                 state = 'folded';
    248             }
    249         }
    250 
    251         currentMenuItemHasPopup();
    252         $document.trigger( 'wp-collapse-menu', { state: state } );
     240                menuState = 'folded';
     241            }
     242        }
     243
     244        $document.trigger( 'wp-collapse-menu', { state: menuState } );
    253245    });
    254246
    255247    // Handle the `aria-haspopup` attribute on the current menu item when it has a sub-menu.
    256248    function currentMenuItemHasPopup() {
    257         var respWidth,
    258             $current = $( 'a.wp-has-current-submenu' );
    259 
    260         if ( window.innerWidth ) {
    261             respWidth = Math.max( window.innerWidth, document.documentElement.clientWidth );
    262         } else {
    263             respWidth = 961;
    264         }
    265 
    266         if ( $body.hasClass( 'folded' ) || ( $body.hasClass( 'auto-fold' ) && respWidth && respWidth <= 960 && respWidth > 782 ) ) {
     249        var $current = $( 'a.wp-has-current-submenu' );
     250
     251        if ( 'folded' === menuState ) {
    267252            // When folded or auto-folded and not responsive view, the current menu item does have a fly-out sub-menu.
    268253            $current.attr( 'aria-haspopup', 'true' );
     
    273258    }
    274259
    275     $document.on( 'wp-window-resized wp-responsive-activate wp-responsive-deactivate', currentMenuItemHasPopup );
     260    $document.on( 'wp-menu-state-set wp-collapse-menu wp-responsive-activate wp-responsive-deactivate', currentMenuItemHasPopup );
    276261
    277262    /**
     
    837822
    838823        trigger: function() {
    839             var width;
    840 
    841             if ( window.innerWidth ) {
    842                 // window.innerWidth is affected by zooming on phones
    843                 width = Math.max( window.innerWidth, document.documentElement.clientWidth );
    844             } else {
    845                 // Exclude IE < 9, it doesn't support @media CSS rules
     824            var viewportWidth = getViewportWidth();
     825
     826            // Exclude IE < 9, it doesn't support @media CSS rules.
     827            if ( ! viewportWidth ) {
    846828                return;
    847829            }
    848830
    849             if ( width <= 782 ) {
     831            if ( viewportWidth <= 782 ) {
    850832                if ( ! wpResponsiveActive ) {
    851833                    $document.trigger( 'wp-responsive-activate' );
     
    859841            }
    860842
    861             if ( width <= 480 ) {
     843            if ( viewportWidth <= 480 ) {
    862844                this.enableOverlay();
    863845            } else {
     
    913895    });
    914896
     897    /**
     898     * @summary Get the viewport width.
     899     *
     900     * @since 4.7
     901     *
     902     * @returns {number|boolean} The current viewport width or false if the
     903     *                           browser doesn't support innerWidth (IE < 9).
     904     */
     905    function getViewportWidth() {
     906        var viewportWidth = false;
     907
     908        if ( window.innerWidth ) {
     909            // On phones, window.innerWidth is affected by zooming.
     910            viewportWidth = Math.max( window.innerWidth, document.documentElement.clientWidth );
     911        }
     912
     913        return viewportWidth;
     914    }
     915
     916    /**
     917     * @summary Set the admin menu collapsed/expanded state.
     918     *
     919     * Sets the global variable `menuState` and triggers a custom event passing
     920     * the current menu state.
     921     *
     922     * @since 4.7
     923     *
     924     * @returns {void}
     925     */
     926    function setMenuState() {
     927        var viewportWidth = getViewportWidth() || 961;
     928
     929        if ( viewportWidth <= 782  ) {
     930            menuState = 'responsive';
     931        } else if ( $body.hasClass( 'folded' ) || ( $body.hasClass( 'auto-fold' ) && viewportWidth <= 960 && viewportWidth > 782 ) ) {
     932            menuState = 'folded';
     933        } else {
     934            menuState = 'open';
     935        }
     936
     937        $document.trigger( 'wp-menu-state-set', { state: menuState } );
     938    }
     939
     940    // Set the menu state when the window gets resized.
     941    $document.on( 'wp-window-resized.set-menu-state', setMenuState );
     942
     943    /**
     944     * @summary Set ARIA attributes on the collapse/expand menu button.
     945     *
     946     * When the admin menu is open or folded, updates the `aria-expanded` and
     947     * `aria-label` attributes of the button to give feedback to assistive
     948     * technologies. In the responsive view, the button is always hidden.
     949     *
     950     * @since 4.7
     951     *
     952     * @returns {void}
     953     */
     954    $document.on( 'wp-menu-state-set wp-collapse-menu', function( event, eventData ) {
     955        var $collapseButton = $( '#collapse-button' ),
     956            ariaExpanded = 'true',
     957            ariaLabelText = commonL10n.collapseMenu;
     958
     959        if ( 'folded' === eventData.state ) {
     960            ariaExpanded = 'false';
     961            ariaLabelText = commonL10n.expandMenu;
     962        }
     963
     964        $collapseButton.attr({
     965            'aria-expanded': ariaExpanded,
     966            'aria-label': ariaLabelText
     967        });
     968    });
     969
    915970    window.wpResponsive.init();
    916971    setPinMenu();
     972    setMenuState();
    917973    currentMenuItemHasPopup();
    918974    makeNoticesDismissible();
  • trunk/src/wp-admin/menu-header.php

    r37488 r39141  
    235235    }
    236236
    237     echo '<li id="collapse-menu" class="hide-if-no-js"><div id="collapse-button"><div></div></div>';
    238     echo '<span>' . esc_html__( 'Collapse menu' ) . '</span>';
    239     echo '</li>';
     237    echo '<li id="collapse-menu" class="hide-if-no-js">' .
     238        '<button type="button" id="collapse-button" aria-label="' . esc_attr__( 'Collapse Main menu' ) . '" aria-expanded="true">' .
     239        '<span class="collapse-button-icon" aria-hidden="true"></span>' .
     240        '<span class="collapse-button-label">' . __( 'Collapse menu' ) . '</span>' .
     241        '</button></li>';
    240242}
    241243
  • trunk/src/wp-includes/css/customize-preview.css

    r39136 r39141  
    2424    background: transparent;
    2525    color: transparent;
     26    -webkit-box-shadow: none;
    2627    box-shadow: none;
    2728    outline: none;
     
    4950    background-color: #0085ba;
    5051    background: #0085ba;
     52    -webkit-border-radius: 50%;
    5153    border-radius: 50%;
    5254    border: 2px solid #fff;
     55    -webkit-box-shadow: 0 2px 1px rgba(46,68,83,0.15);
    5356    box-shadow: 0 2px 1px rgba(46,68,83,0.15);
    5457    text-align: center;
     58    display: -webkit-box;
     59    display: -webkit-flex;
     60    display: -moz-box;
     61    display: -ms-flexbox;
    5562    display: flex;
     63    -webkit-box-orient: horizontal;
     64    -webkit-box-direction: normal;
     65    -webkit-flex-direction: row;
     66    -moz-box-orient: horizontal;
     67    -moz-box-direction: normal;
     68    -ms-flex-direction: row;
    5669    flex-direction: row;
     70    -webkit-box-pack: center;
     71    -webkit-justify-content: center;
     72    -moz-box-pack: center;
     73    -ms-flex-pack: center;
    5774    justify-content: center;
     75    -webkit-box-align: center;
     76    -webkit-align-items: center;
     77    -moz-box-align: center;
     78    -ms-flex-align: center;
    5879    align-items: center;
    5980    cursor: pointer;
    6081    padding: 0;
     82    -webkit-animation-fill-mode: both;
    6183    animation-fill-mode: both;
     84    -webkit-animation-duration: .4s;
    6285    animation-duration: .4s;
    6386    opacity: 0;
     
    85108
    86109body.customize-partial-edit-shortcuts-shown .customize-partial-edit-shortcut button {
     110    -webkit-animation-name: customize-partial-edit-shortcut-bounce-appear;
    87111    animation-name: customize-partial-edit-shortcut-bounce-appear;
    88112    pointer-events: auto;
    89113}
    90114body.customize-partial-edit-shortcuts-hidden .customize-partial-edit-shortcut button {
     115    -webkit-animation-name: customize-partial-edit-shortcut-bounce-disappear;
    91116    animation-name: customize-partial-edit-shortcut-bounce-disappear;
    92117    pointer-events: none;
     
    102127}
    103128
     129@-webkit-keyframes customize-partial-edit-shortcut-bounce-appear {
     130    from, 20%, 40%, 60%, 80%, to {
     131        -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
     132        animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
     133    }
     134    0% {
     135        opacity: 0;
     136        -webkit-transform: scale3d(.3, .3, .3);
     137        transform: scale3d(.3, .3, .3);
     138    }
     139    20% {
     140        -webkit-transform: scale3d(1.1, 1.1, 1.1);
     141        transform: scale3d(1.1, 1.1, 1.1);
     142    }
     143    40% {
     144        -webkit-transform: scale3d(.9, .9, .9);
     145        transform: scale3d(.9, .9, .9);
     146    }
     147    60% {
     148        opacity: 1;
     149        -webkit-transform: scale3d(1.03, 1.03, 1.03);
     150        transform: scale3d(1.03, 1.03, 1.03);
     151    }
     152    80% {
     153        -webkit-transform: scale3d(.97, .97, .97);
     154        transform: scale3d(.97, .97, .97);
     155    }
     156    to {
     157        opacity: 1;
     158        -webkit-transform: scale3d(1, 1, 1);
     159        transform: scale3d(1, 1, 1);
     160    }
     161}
     162
    104163@keyframes customize-partial-edit-shortcut-bounce-appear {
    105164    from, 20%, 40%, 60%, 80%, to {
    106         animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
    107     }
    108     0% {
    109         opacity: 0;
    110         transform: scale3d(.3, .3, .3);
    111     }
    112     20% {
    113         transform: scale3d(1.1, 1.1, 1.1);
    114     }
    115     40% {
    116         transform: scale3d(.9, .9, .9);
    117     }
    118     60% {
    119         opacity: 1;
    120         transform: scale3d(1.03, 1.03, 1.03);
    121     }
    122     80% {
    123         transform: scale3d(.97, .97, .97);
    124     }
    125     to {
    126         opacity: 1;
    127         transform: scale3d(1, 1, 1);
     165        -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
     166        animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
     167    }
     168    0% {
     169        opacity: 0;
     170        -webkit-transform: scale3d(.3, .3, .3);
     171        transform: scale3d(.3, .3, .3);
     172    }
     173    20% {
     174        -webkit-transform: scale3d(1.1, 1.1, 1.1);
     175        transform: scale3d(1.1, 1.1, 1.1);
     176    }
     177    40% {
     178        -webkit-transform: scale3d(.9, .9, .9);
     179        transform: scale3d(.9, .9, .9);
     180    }
     181    60% {
     182        opacity: 1;
     183        -webkit-transform: scale3d(1.03, 1.03, 1.03);
     184        transform: scale3d(1.03, 1.03, 1.03);
     185    }
     186    80% {
     187        -webkit-transform: scale3d(.97, .97, .97);
     188        transform: scale3d(.97, .97, .97);
     189    }
     190    to {
     191        opacity: 1;
     192        -webkit-transform: scale3d(1, 1, 1);
     193        transform: scale3d(1, 1, 1);
     194    }
     195}
     196
     197@-webkit-keyframes customize-partial-edit-shortcut-bounce-disappear {
     198    from, 20%, 40%, 60%, 80%, to {
     199        -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
     200        animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
     201    }
     202    0% {
     203        opacity: 1;
     204        -webkit-transform: scale3d(1, 1, 1);
     205        transform: scale3d(1, 1, 1);
     206    }
     207    20% {
     208        -webkit-transform: scale3d(.97, .97, .97);
     209        transform: scale3d(.97, .97, .97);
     210    }
     211    40% {
     212        opacity: 1;
     213        -webkit-transform: scale3d(1.03, 1.03, 1.03);
     214        transform: scale3d(1.03, 1.03, 1.03);
     215    }
     216    60% {
     217        -webkit-transform: scale3d(.9, .9, .9);
     218        transform: scale3d(.9, .9, .9);
     219    }
     220    80% {
     221        -webkit-transform: scale3d(1.1, 1.1, 1.1);
     222        transform: scale3d(1.1, 1.1, 1.1);
     223    }
     224    to {
     225        opacity: 0;
     226        -webkit-transform: scale3d(.3, .3, .3);
     227        transform: scale3d(.3, .3, .3);
    128228    }
    129229}
     
    131231@keyframes customize-partial-edit-shortcut-bounce-disappear {
    132232    from, 20%, 40%, 60%, 80%, to {
    133         animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
    134     }
    135     0% {
    136         opacity: 1;
    137         transform: scale3d(1, 1, 1);
    138     }
    139     20% {
    140         transform: scale3d(.97, .97, .97);
    141     }
    142     40% {
    143         opacity: 1;
    144         transform: scale3d(1.03, 1.03, 1.03);
    145     }
    146     60% {
    147         transform: scale3d(.9, .9, .9);
    148     }
    149     80% {
    150         transform: scale3d(1.1, 1.1, 1.1);
    151     }
    152     to {
    153         opacity: 0;
     233        -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
     234        animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
     235    }
     236    0% {
     237        opacity: 1;
     238        -webkit-transform: scale3d(1, 1, 1);
     239        transform: scale3d(1, 1, 1);
     240    }
     241    20% {
     242        -webkit-transform: scale3d(.97, .97, .97);
     243        transform: scale3d(.97, .97, .97);
     244    }
     245    40% {
     246        opacity: 1;
     247        -webkit-transform: scale3d(1.03, 1.03, 1.03);
     248        transform: scale3d(1.03, 1.03, 1.03);
     249    }
     250    60% {
     251        -webkit-transform: scale3d(.9, .9, .9);
     252        transform: scale3d(.9, .9, .9);
     253    }
     254    80% {
     255        -webkit-transform: scale3d(1.1, 1.1, 1.1);
     256        transform: scale3d(1.1, 1.1, 1.1);
     257    }
     258    to {
     259        opacity: 0;
     260        -webkit-transform: scale3d(.3, .3, .3);
    154261        transform: scale3d(.3, .3, .3);
    155262    }
  • trunk/src/wp-includes/script-loader.php

    r39132 r39141  
    7878    $scripts->add( 'common', "/wp-admin/js/common$suffix.js", array('jquery', 'hoverIntent', 'utils'), false, 1 );
    7979    did_action( 'init' ) && $scripts->localize( 'common', 'commonL10n', array(
    80         'warnDelete' => __( "You are about to permanently delete these items.\n  'Cancel' to stop, 'OK' to delete." ),
    81         'dismiss'    => __( 'Dismiss this notice.' ),
     80        'warnDelete'   => __( "You are about to permanently delete these items.\n  'Cancel' to stop, 'OK' to delete." ),
     81        'dismiss'      => __( 'Dismiss this notice.' ),
     82        'collapseMenu' => __( 'Collapse Main menu' ),
     83        'expandMenu'   => __( 'Expand Main menu' ),
    8284    ) );
    8385
Note: See TracChangeset for help on using the changeset viewer.