Search code examples
htmlpopupaccessibility

making a searchform accesible for screenreaders


I am using checkbox to show and hide a searchform. When the label is clicked then the checkbox is checked. So I thought I had sorted it all out, some fancy role and aria atributes here and there make it play nice with accessibility.

CSS is doing the magic:

#cb-search-form:checked + .page-search {
    display: block;
}

This is the html:

<div class="header-icons">
    <label for="cb-search-form" class="btn js-has-popup" id="searchFormLbl" role="button" tabindex="0" aria-haspopup="true" aria-expanded="false">
        <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16" class="bi bi-search" width="1em" height="1em"><path d="M11.742 10.344a6.5 6.5 0 10-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 001.415-1.414l-3.85-3.85a1.007 1.007 0 00-.115-.1zM12 6.5a5.5 5.5 0 11-11 0 5.5 5.5 0 0111 0z"></path></svg>
        <span class="sr-only">open search form</span>
    </label>
</div>

<input type="checkbox" id="cb-search-form">

<div class="page-search js-focus" aria-labelledby="searchFormLbl" aria-hidden="true">
    <label for="cb-search-form" class="btn close js-has-popup-closer" aria-label="Close">
        <span aria-hidden="true">&times;</span>
    </label>

    <form action="searchpage" class="form js-page-search" id="form-search" method="POST" novalidate itemprop="potentialAction" itemscope itemtype="http://schema.org/SearchAction">
        <meta itemprop="target" content="searchpage?q={q}">
        <!-- the inputs fields and stuff here -->
    </form>
</div>

The label seems no valid interaction thingy. The role as button is not realy valid I guess. The tabindex on it is not cooperating at all when I press Enter while focused. The checkbox is not getting checked. I think this will never get "fixed": https://bugs.chromium.org/p/chromium/issues/detail?id=122652

Is this screenreader friendly, I am not even sure if I should "fix" this?

I thought this was nice way to do it like this without javascript.


Solution

  • For clarity, I assume this is meant to be a fall-back for when JS fails / for edge cases where it is switched off and not a JS free solution.

    That distinction is important as if this is meant to be a "no JS" solution all of your role information needs removing.

    Best practices with checkboxes as no JS fall-back

    What you should do is start with a checkbox and CSS solution (that works with no JS), then through progressive enhancement, add the role="button" etc. with JavaScript rather than having it there in the first place.

    You then just treat it as a <button> element from that point onwards.

    However, the role needs moving to the <input type="checkbox" not the <label>, along with all the relevant WAI-ARIA! (see "Focus the checkbox not the label" h2 below)

    Also the reason "Enter" does not work is because adding a role only changes the semantic meaning, it does not add functionality. Checkboxes only work with Space.

    So you would need to add this functionality via JavaScript with element.addEventListener('keydown'... and listening for the "Enter" key (key 13) to change the checked state so that it now behaves like a button.

    Focus the checkbox not the label

    Finally, when using any checkbox - label "hacks" like this the item that should receive focus should be the checkbox itself not the label.

    Some screen reader and browser combinations will not announce the checked state for example if you focus the <label>.

    The other thing to consider is voice navigation, I do not know how something like "dragon naturally speaking" would behave with this. But as I do not know I cannot comment in any meaningful way other than an educated guess that this would not be very robust due to experience with Dragon not behaving well with workarounds.

    I mean, Dragon even has problems with implicit labels so it shows how careful you sometimes have to be due to the scattered support across assistive tech.

    Changing your CSS / HTML

    So you hide the checkbox using a visually-hidden class, but position it in such a way that when it receives focus you can add focus indicators to the label, using something like input[type="checkbox"]:focus ~ label{/*your focus indicators*/}. You need to move the checkbox and the .page-search within the <div class="header-icons" to use the sibling selector.

    I would also encourage you to use the visually-hidden class I linked as the bootstrap sr-only class has a few flaws.

    Hopefully that makes sense, any questions just ask.

    Quick Demo

    I haven't tidied up any of your role information etc. in the following demo, all I have done is remove the tabindex="0" on the label.

    The demo is purely to show how you can focus the checkbox and still put the focus indicator on the label.

    .visually-hidden { 
        border: 0;
        padding: 0;
        margin: 0;
        position: absolute !important;
        height: 1px; 
        width: 1px;
        overflow: hidden;
        clip: rect(1px 1px 1px 1px); /* IE6, IE7 - a 0 height clip, off to the bottom right of the visible 1px box */
        clip: rect(1px, 1px, 1px, 1px); /*maybe deprecated but we need to support legacy browsers */
        clip-path: inset(50%); /*modern browsers, clip-path works inwards from each corner*/
        white-space: nowrap; /* added line to stop words getting smushed together (as they go onto seperate lines and some screen readers do not understand line feeds as a space */
    }
    
    #searchFormLbl{
       display: inline-block;
       padding: 5px;
    }
    
    input[type="checkbox"]:focus ~ label{
    /*your focus indicators*/
    outline: 0;
    border: 0;
    outline: 4px solid #333;
    outline-offset: 4px;
    }
    
    
    .page-search{
        margin-top: 20px;
        display: none;
    }
    
    #cb-search-form:checked ~ .page-search {
        display: block!important;
    }
    <div class="header-icons">
        <input type="checkbox" id="cb-search-form" class="visually-hidden">
        <label for="cb-search-form" class="btn js-has-popup" id="searchFormLbl" role="button" aria-haspopup="true" aria-expanded="false">
            <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16" class="bi bi-search" width="1em" height="1em"><path d="M11.742 10.344a6.5 6.5 0 10-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 001.415-1.414l-3.85-3.85a1.007 1.007 0 00-.115-.1zM12 6.5a5.5 5.5 0 11-11 0 5.5 5.5 0 0111 0z"></path></svg>
            <span class="sr-only">open search form</span>
        </label>
    <div class="page-search js-focus" aria-labelledby="searchFormLbl">
        I am open
    </div>
    </div>