Make WordPress Core

Changeset 38880


Ignore:
Timestamp:
10/23/2016 06:15:07 PM (8 years ago)
Author:
afercia
Message:

Accessibility: Improve the Tags meta box accessibility.

  • changes the "X" links in buttons, improves their color contrast ratio and focus style
  • adds screen reader text "Remove item: + tagname"
  • uses wp.a11y.speak() to give screen reader users feedback when adding/removing tags
  • makes the tagcloud-link toggle a button, with an aria-expanded attribute to indicate the tag cloud collapsed/expanded state
  • changes colors for the autocomplete highlighted option in order to have a better color contrast ratio
  • reduces the font size for the autocomplete on Press This
  • removes CSS related to the old suggest.js from Press This

Props joedolson, cgrymala, azaozz, afercia.
Fixes #27555.

Location:
trunk/src
Files:
11 edited

Legend:

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

    r38648 r38880  
    760760/* @todo can we combine these into a class or use an existing dashicon one? */
    761761.welcome-panel .welcome-panel-close:before,
    762 .tagchecklist span a:before,
     762.tagchecklist .ntdelbutton .remove-tag-icon:before,
    763763#bulk-titles div a:before,
    764764.notice-dismiss:before {
     
    780780}
    781781
    782 .tagchecklist span a:before,
    783782#bulk-titles div a:before {
    784783    margin: 1px 0;
    785784}
    786785
     786.tagchecklist .ntdelbutton .remove-tag-icon:before {
     787    margin-left: 2px;
     788    -webkit-border-radius: 50%;
     789    border-radius: 50%;
     790    color: #0073aa;
     791    /* vertically center the icon cross browsers */
     792    line-height: 1.28;
     793}
     794
     795.tagchecklist .ntdelbutton:focus {
     796    outline: 0;
     797}
     798
    787799.welcome-panel .welcome-panel-close:hover:before,
    788800.welcome-panel .welcome-panel-close:focus:before,
    789 .tagchecklist span a:hover:before,
    790 #bulk-titles div a:hover:before {
     801.tagchecklist .ntdelbutton:hover .remove-tag-icon:before,
     802.tagchecklist .ntdelbutton:focus .remove-tag-icon:before,
     803#bulk-titles div a:hover:before,
     804#bulk-titles div a:focus:before {
    791805    color: #c00;
     806}
     807
     808.tagchecklist .ntdelbutton:focus .remove-tag-icon:before {
     809    -webkit-box-shadow:
     810        0 0 0 1px #5b9dd9,
     811        0 0 2px 1px rgba(30, 140, 190, .8);
     812    box-shadow:
     813        0 0 0 1px #5b9dd9,
     814        0 0 2px 1px rgba(30, 140, 190, .8);
    792815}
    793816
     
    35723595    .sidebar-name:hover .sidebar-name-arrow,
    35733596    .meta-box-sortables .postbox:hover .handlediv,
    3574     .tagchecklist span a,
    35753597    #bulk-titles div a,
    3576     .tagchecklist span a:hover,
    35773598    #bulk-titles div a:hover {
    35783599        background: none !important;
  • trunk/src/wp-admin/css/customize-controls.css

    r38853 r38880  
    277277    -webkit-transition: 0.18s -webkit-transform cubic-bezier(0.645, 0.045, 0.355, 1);
    278278    transition: 0.18s -webkit-transform cubic-bezier(0.645, 0.045, 0.355, 1);
     279    -webkit-transition: 0.18s transform cubic-bezier(0.645, 0.045, 0.355, 1);
    279280    transition: 0.18s transform cubic-bezier(0.645, 0.045, 0.355, 1);
     281    -webkit-transition: 0.18s transform cubic-bezier(0.645, 0.045, 0.355, 1), 0.18s -webkit-transform cubic-bezier(0.645, 0.045, 0.355, 1);
    280282    transition: 0.18s transform cubic-bezier(0.645, 0.045, 0.355, 1), 0.18s -webkit-transform cubic-bezier(0.645, 0.045, 0.355, 1); /* easeInOutCubic */
    281283}
  • trunk/src/wp-admin/css/edit.css

    r38700 r38880  
    583583}
    584584
    585 .tagchecklist span {
     585.tagchecklist > span {
     586    float: left;
    586587    margin-right: 25px;
    587     display: block;
    588     float: left;
    589588    font-size: 13px;
    590589    line-height: 1.8em;
     
    595594}
    596595
    597 .tagchecklist span a {
    598     margin: 1px 0 0 -17px;
     596.tagchecklist .ntdelbutton {
     597    position: absolute;
     598    width: 24px;
     599    height: 24px;
     600    border: none;
     601    margin: 0 0 0 -19px;
     602    padding: 0;
     603    background: none;
    599604    cursor: pointer;
    600     width: 20px;
    601     height: 20px;
    602     display: block;
    603     float: left;
    604605    text-indent: 0;
    605     overflow: hidden;
    606     position: absolute;
    607606}
    608607
     
    10281027}
    10291028
     1029.tagcloud-link.button-link {
     1030    color: #0073aa;
     1031    text-decoration: underline;
     1032}
     1033
     1034.tagcloud-link.button-link:hover {
     1035    color: #00a0d2;
     1036}
     1037
     1038.tagcloud-link.button-link:focus {
     1039    color: #124964;
     1040    -webkit-box-shadow:
     1041        0 0 0 1px #5b9dd9,
     1042        0 0 2px 1px rgba(30, 140, 190, .8);
     1043    box-shadow:
     1044        0 0 0 1px #5b9dd9,
     1045        0 0 2px 1px rgba(30, 140, 190, .8);
     1046}
     1047
    10301048#post-body-content .tagsdiv .the-tags {
    10311049    margin: 0 5px;
     
    10531071}
    10541072
     1073/* Suggest.js autocomplete, no more used by core. */
    10551074.ac_results {
    10561075    display: none;
     
    14391458    }
    14401459
    1441     .tagchecklist span {
     1460    .tagchecklist > span {
    14421461        font-size: 16px;
    14431462        line-height: 1.4;
  • trunk/src/wp-admin/css/forms.css

    r38700 r38880  
    595595    white-space: nowrap;
    596596    text-align: left;
    597 }
    598 
    599 .ui-autocomplete li.ui-state-focus {
     597    cursor: pointer;
     598}
     599
     600/* Colors for the wplink toolbar autocomplete. */
     601.ui-autocomplete .ui-state-focus {
    600602    background-color: #ddd;
    601     cursor: pointer;
     603}
     604
     605/* Colors for the tags autocomplete. */
     606.wp-tags-autocomplete .ui-state-focus {
     607    background-color: #0073aa;
     608    color: #fff;
    602609}
    603610
  • trunk/src/wp-admin/css/ie.css

    r38672 r38880  
    442442}
    443443
    444 .tagchecklist span, .tagchecklist span a {
     444.tagchecklist > span, .tagchecklist .ntdelbutton {
    445445    display: inline-block;
    446446    display: block;
     447}
     448
     449.tagchecklist .ntdelbutton:focus .remove-tag-icon:before {
     450    outline: 1px solid #5b9dd9;
    447451}
    448452
  • trunk/src/wp-admin/css/press-this.css

    r38152 r38880  
    771771}
    772772
    773 
    774 /* Tag hint TODO needed? */
    775 /* Tag suggestions */
    776 .ac_results {
    777     padding: 0;
    778     margin: -1px 0 0 -1px;
    779     list-style: none;
    780     position: absolute;
    781     z-index: 10000;
    782     display: none;
    783     border: 1px solid #d8d8d8;
    784     background-color: #fff;
    785     font-size: 14px;
    786 }
    787 
    788 .ac_results li {
    789     padding: 6px 16px;
    790     white-space: nowrap;
    791     text-align: left;
    792 }
    793 
    794 .ac_results .ac_over {
    795     background-color: #e5e5e5;
    796     background-color: #00a0d2;
    797     color: #fff;
    798     cursor: pointer;
    799 }
    800 
    801 .ac_match {
    802     text-decoration: underline;
    803 }
    804 
    805773/* Tags */
    806774.tagchecklist {
     
    818786}
    819787
    820 .tagchecklist span {
    821     display: block;
     788.tagchecklist > span {
     789    float: left;
    822790    margin-right: 25px;
    823     float: left;
    824791    font-size: 13px;
    825792    line-height: 1.8;
     
    829796
    830797@media (max-width: 600px) {
    831     .tagchecklist span {
     798    .tagchecklist > span {
    832799        margin-bottom: 15px;
    833800        font-size: 16px;
     
    837804
    838805.tagchecklist .ntdelbutton {
    839     margin: 1px 0 0 -17px;
     806    position: absolute;
     807    width: 24px;
     808    height: 24px;
     809    border: none;
     810    margin: 0 0 0 -19px;
     811    padding: 0;
     812    background: none;
    840813    cursor: pointer;
    841     width: 20px;
    842     height: 20px;
    843     display: block;
    844     float: left;
    845     text-indent: 0;
    846     overflow: hidden;
     814    text-indent: 0;;
    847815    position: absolute;
    848     outline: 0;
    849 }
    850 
    851 .tagchecklist .ntdelbutton:before {
     816}
     817
     818.tagchecklist .ntdelbutton .remove-tag-icon:before {
    852819    content: "\f153";
    853820    display: block;
    854     margin: 2px 0;
     821    margin-left: 2px;
    855822    height: 20px;
    856823    width: 20px;
    857     background: 0 0;
    858     color: #9ea7af;
    859     font: 400 16px/1 dashicons;
     824    -webkit-border-radius: 50%;
     825    border-radius: 50%;
     826    background: transparent;
     827    color: #0073aa;
     828    /* line-height tweak to vertically center the icon cross browsers */
     829    font: 400 16px/1.28 dashicons;
    860830    text-align: center;
    861     speak: none;
    862831    -webkit-font-smoothing: antialiased;
    863832}
    864833
    865 .tagchecklist .ntdelbutton:focus:before {
    866     color: #00a0d2;
    867 }
    868 
     834.tagchecklist .ntdelbutton:focus {
     835    outline: 0;
     836}
     837
     838.tagchecklist .ntdelbutton:hover .remove-tag-icon:before,
     839.tagchecklist .ntdelbutton:focus .remove-tag-icon:before {
     840    color: #c00;
     841}
     842
     843.tagchecklist .ntdelbutton:focus .remove-tag-icon:before {
     844    -webkit-box-shadow:
     845        0 0 0 1px #5b9dd9,
     846        0 0 2px 1px rgba(30, 140, 190, .8);
     847    box-shadow:
     848        0 0 0 1px #5b9dd9,
     849        0 0 2px 1px rgba(30, 140, 190, .8);
     850}
    869851
    870852/* THE TAG CLOUD. */
     
    21952177    box-shadow: 0 1px 2px rgba( 30, 140, 190, 0.8 );
    21962178    background-color: #fff;
     2179    font-size: 14px;
    21972180}
    21982181
     
    22022185    white-space: nowrap;
    22032186    text-align: left;
    2204 }
    2205 
    2206 .ui-autocomplete li.ui-state-focus {
     2187    cursor: pointer;
     2188}
     2189
     2190/* Colors for the wplink toolbar autocomplete. */
     2191.ui-autocomplete .ui-state-focus {
    22072192    background-color: #ddd;
    2208     cursor: pointer;
    2209 }
     2193}
     2194
     2195/* Colors for the tags autocomplete. */
     2196.wp-tags-autocomplete .ui-state-focus {
     2197    background-color: #0073aa;
     2198    color: #fff;
     2199}
  • trunk/src/wp-admin/includes/class-wp-press-this.php

    r38705 r38880  
    941941        if ( $user_can_assign_terms ) {
    942942            ?>
    943             <button type="button" class="button-link tagcloud-link" id="link-post_tag"><?php echo $taxonomy->labels->choose_from_most_used; ?></button>
     943            <button type="button" class="button-link tagcloud-link" id="link-post_tag" aria-expanded="false"><?php echo $taxonomy->labels->choose_from_most_used; ?></button>
    944944            <?php
    945945        }
  • trunk/src/wp-admin/includes/meta-boxes.php

    r38797 r38880  
    443443</div>
    444444<?php if ( $user_can_assign_terms ) : ?>
    445 <p class="hide-if-no-js"><a href="#titlediv" class="tagcloud-link" id="link-<?php echo $tax_name; ?>"><?php echo $taxonomy->labels->choose_from_most_used; ?></a></p>
     445<p class="hide-if-no-js"><button type="button" class="button-link tagcloud-link" id="link-<?php echo $tax_name; ?>" aria-expanded="false"><?php echo $taxonomy->labels->choose_from_most_used; ?></button></p>
    446446<?php endif; ?>
    447447<?php
  • trunk/src/wp-admin/js/tags-box.js

    r38797 r38880  
    8787                // If tags editing isn't disabled, create the X button.
    8888                if ( ! disabled ) {
    89                     xbutton = $( '<a id="' + id + '-check-num-' + key + '" class="ntdelbutton" tabindex="0">X</a>' );
     89                    /*
     90                     * Build the X buttons, hide the X icon with aria-hidden and
     91                     * use visually hidden text for screen readers.
     92                     */
     93                    xbutton = $( '<button type="button" id="' + id + '-check-num-' + key + '" class="ntdelbutton">' +
     94                        '<span class="remove-tag-icon" aria-hidden="true"></span>' +
     95                        '<span class="screen-reader-text">' + window.tagsSuggestL10n.removeTerm + ' ' + val + '</span>' +
     96                        '</button>' );
    9097
    9198                    xbutton.on( 'click keypress', function( e ) {
    92                         // Trigger function if pressed Enter - keyboard navigation
    93                         if ( e.type === 'click' || e.keyCode === 13 ) {
    94                             // When using keyboard, move focus back to the new tag field.
    95                             if ( e.keyCode === 13 ) {
    96                                 $( this ).closest( '.tagsdiv' ).find( 'input.newtag' ).focus();
    97                             }
    98 
     99                        // On click or when using the Enter/Spacebar keys.
     100                        if ( 'click' === e.type || 13 === e.keyCode || 32 === e.keyCode ) {
     101                            /*
     102                             * When using the keyboard, move focus back to the
     103                             * add new tag field. Note: when releasing the pressed
     104                             * key this will fire the `keyup` event on the input.
     105                             */
     106                            if ( 13 === e.keyCode || 32 === e.keyCode ) {
     107                                $( this ).closest( '.tagsdiv' ).find( 'input.newtag' ).focus();
     108                            }
     109
     110                            tagBox.userAction = 'remove';
    99111                            tagBox.parseTags( this );
    100112                        }
     
    107119                tagchecklist.append( span );
    108120            });
     121            // The buttons list is built now, give feedback to screen reader users.
     122            tagBox.screenReadersMessage();
    109123        },
    110124
     
    118132            text = a ? $(a).text() : newtag.val();
    119133
    120             if ( 'undefined' == typeof( text ) ) {
     134            /*
     135             * Return if there's no new tag or if the input field is empty.
     136             * Note: when using the keyboard to add tags, focus is moved back to
     137             * the input field and the `keyup` event attached on this field will
     138             * fire when releasing the pressed key. Checking also for the field
     139             * emptiness avoids to set the tags and call quickClicks() again.
     140             */
     141            if ( 'undefined' == typeof( text ) || '' === text ) {
    121142                return false;
    122143            }
     
    149170
    150171                $( 'a', r ).click( function() {
     172                    tagBox.userAction = 'add';
    151173                    tagBox.flushTags( $( '#' + tax ), this );
    152174                    return false;
     
    157179        },
    158180
     181        /**
     182         * Track the user's last action.
     183         *
     184         * @since 4.7.0
     185         */
     186        userAction: '',
     187
     188        /**
     189         * Dispatch an audible message to screen readers.
     190         *
     191         * @since 4.7.0
     192         */
     193        screenReadersMessage: function() {
     194            var message;
     195
     196            switch ( this.userAction ) {
     197                case 'remove':
     198                    message = window.tagsSuggestL10n.termRemoved;
     199                    break;
     200
     201                case 'add':
     202                    message = window.tagsSuggestL10n.termAdded;
     203                    break;
     204
     205                default:
     206                    return;
     207            }
     208
     209            window.wp.a11y.speak( message, 'assertive' );
     210        },
     211
    159212        init : function() {
    160213            var ajaxtag = $('div.ajaxtag');
     
    165218
    166219            $( '.tagadd', ajaxtag ).click( function() {
     220                tagBox.userAction = 'add';
    167221                tagBox.flushTags( $( this ).closest( '.tagsdiv' ) );
    168222            });
     
    170224            $( 'input.newtag', ajaxtag ).keyup( function( event ) {
    171225                if ( 13 == event.which ) {
     226                    tagBox.userAction = 'add';
    172227                    tagBox.flushTags( $( this ).closest( '.tagsdiv' ) );
    173228                    event.preventDefault();
     
    190245            });
    191246
    192             // tag cloud
     247            // Fetch and toggle the Tag cloud.
    193248            $('.tagcloud-link').click(function(){
    194                 tagBox.get( $(this).attr('id') );
    195                 $(this).unbind().click(function(){
    196                     $(this).siblings('.the-tagcloud').toggle();
    197                     return false;
    198                 });
    199                 return false;
     249                // On the first click, fetch the tag cloud and insert it in the DOM.
     250                tagBox.get( $( this ).attr( 'id' ) );
     251                // Update button state, remove previous click event and attach a new one to toggle the cloud.
     252                $( this )
     253                    .attr( 'aria-expanded', 'true' )
     254                    .unbind()
     255                    .click( function() {
     256                        $( this )
     257                            .attr( 'aria-expanded', 'false' === $( this ).attr( 'aria-expanded' ) ? 'true' : 'false' )
     258                            .siblings( '.the-tagcloud' ).toggle();
     259                    });
    200260            });
    201261        }
  • trunk/src/wp-admin/js/tags-suggest.js

    r38797 r38880  
     1/**
     2 * Default settings for jQuery UI Autocomplete for use with non-hierarchical taxonomies.
     3 */
    14( function( $ ) {
     5    if ( typeof window.tagsSuggestL10n === 'undefined' || typeof window.uiAutocompleteL10n === 'undefined' ) {
     6        return;
     7    }
     8
    29    var tempID = 0;
    3     var separator = ( window.tagsSuggestL10n && window.tagsSuggestL10n.tagDelimiter ) || ',';
     10    var separator = window.tagsSuggestL10n.tagDelimiter || ',';
    411
    512    function split( val ) {
     
    1118    }
    1219
     20    /**
     21     * Add UI Autocomplete to an input or textarea element with presets for use
     22     * with non-hierarchical taxonomies.
     23     *
     24     * Example: `$( element ).wpTagsSuggest( options )`.
     25     *
     26     * The taxonomy can be passed in a `data-wp-taxonomy` attribute on the element or
     27     * can be in `options.taxonomy`.
     28     *
     29     * @since 4.7
     30     *
     31     * @param {object} options Options that are passed to UI Autocomplete. Can be used to override the default settings.
     32     * @returns {object} jQuery instance.
     33     */
    1334    $.fn.wpTagsSuggest = function( options ) {
    1435        var cache;
     
    1738
    1839        options = options || {};
    19        
     40
    2041        var taxonomy = options.taxonomy || $element.attr( 'data-wp-taxonomy' ) || 'post_tag';
    21        
     42
    2243        delete( options.taxonomy );
    2344
    2445        options = $.extend( {
    25             source: function( request, response ) {             
     46            source: function( request, response ) {
    2647                var term;
    2748
     
    4061                    $element.removeClass( 'ui-autocomplete-loading' ); // UI fails to remove this sometimes?
    4162                } ).done( function( data ) {
    42                     var value;
    43                     var terms = [];
     63                    var tagName;
     64                    var tags = [];
    4465
    4566                    if ( data ) {
    4667                        data = data.split( '\n' );
    4768
    48                         for ( value in data ) {
     69                        for ( tagName in data ) {
    4970                            var id = ++tempID;
    5071
    51                             terms.push({
     72                            tags.push({
    5273                                id: id,
    53                                 name: data[value]
     74                                name: data[tagName]
    5475                            });
    5576                        }
    5677
    57                         cache = terms;
    58                         response( terms );
     78                        cache = tags;
     79                        response( tags );
    5980                    } else {
    60                         response( terms );
     81                        response( tags );
    6182                    }
    6283                } );
     
    81102
    82103                if ( $.ui.keyCode.TAB === event.keyCode ) {
    83                     if ( typeof window.uiAutocompleteL10n !== 'undefined' ) {
    84                         // Audible confirmation message when a tag has been selected.
    85                         window.wp.a11y.speak( window.uiAutocompleteL10n.itemSelected );
    86                     }
    87 
     104                    // Audible confirmation message when a tag has been selected.
     105                    window.wp.a11y.speak( window.tagsSuggestL10n.termSelected, 'assertive' );
    88106                    event.preventDefault();
    89107                } else if ( $.ui.keyCode.ENTER === event.keyCode ) {
     
    106124            },
    107125            messages: {
    108                 noResults: ( typeof window.uiAutocompleteL10n !== 'undefined' ) ? window.uiAutocompleteL10n.noResults : '',
     126                noResults: window.uiAutocompleteL10n.noResults,
    109127                results: function( number ) {
    110                     if ( typeof window.uiAutocompleteL10n !== 'undefined' ) {
    111                         if ( number > 1 ) {
    112                             return window.uiAutocompleteL10n.manyResults.replace( '%d', number );
    113                         }
     128                    if ( number > 1 ) {
     129                        return window.uiAutocompleteL10n.manyResults.replace( '%d', number );
     130                    }
    114131
    115                         return window.uiAutocompleteL10n.oneResult;
    116                     }
     132                    return window.uiAutocompleteL10n.oneResult;
    117133                }
    118134            }
     
    161177                $( this ).find( '[aria-selected="true"]' ).removeAttr( 'aria-selected' );
    162178            });
    163            
     179
    164180        return this;
    165181    };
  • trunk/src/wp-includes/script-loader.php

    r38832 r38880  
    539539        did_action( 'init' ) && $scripts->localize( 'tags-suggest', 'tagsSuggestL10n', array(
    540540            'tagDelimiter' => _x( ',', 'tag delimiter' ),
     541            'removeTerm'   => __( 'Remove term:' ),
     542            'termSelected' => __( 'Term selected.' ),
     543            'termAdded'    => __( 'Term added.' ),
     544            'termRemoved'  => __( 'Term removed.' ),
    541545        ) );
    542546
Note: See TracChangeset for help on using the changeset viewer.