I need to show certain content in a modal/fullscreen panel on small devices, triggered by a button. On large devices, this same content, is just always shown and the trigger is then hidden.
How do you approach this for accessibility?
Currently, I have this setup
<button type="button" aria-expanded="false" aria-controls="filter-panel">Filter</button>
<div class="o-panel" id="filter-panel">Form</div>
Initially, the o-panel
is hidden on small devices via CSS (in a media query that targets small devices). I set aria-expanded
to true when the trigger is hit, and add an active
class to the panel itself, which shows the fullscreen o-panel
via CSS. On large devices, I hide the button and always display the content from o-panel
via CSS (in a media query that targets large devices), inline, where it is found in the HTML.
Does this make sense for accessibility? My panel doesn't say role="dialog"
, becuase on large devices it's just content, not a dialog. Is it a problem that my button is hidden on these large devices?
I'm really stuck at what I should do here. If I add role="dialog"
to my o-panel, should I remove this property for large devices, where it is actually not a modal?
Or should I move copy/move the content from my o-panel
in a div with role="dialog"
, in case the trigger is hit? I just don't want two copies of the same content.
On large devices you need to do a couple of things.
First make sure the button is display:none
, not visibility:hidden
or anything else or it will still show up in the accessibility tree.
<main>
) problemA modal should appear outside of your <main>
.
This is so you can add aria-hidden="true"
to the <main>
element when the modal is active, so as to stop people navigating outside of the modal on a screen reader. (Screen reader users use headings, links etc. to navigate a page so you can't just intercept the tab key.)
Now I come from a mobile first philosophy so I would say your markup should be mobile first. That means putting the modal outside of your <main>
as discussed earlier.
This obviously causes a huge problem on a desktop. You now have the content sat somewhere it shouldn't be.
Because of this you only really have two options here.
Use JavaScript to reposition the modal content in a predefined placeholder <div>
.
So you keep your mobile first design, then use JavaScript to find the innerHTML
of your modal and move it into the body within your holder. Then delete the modal itself just to be sure.
While you are at it I would also delete the button, just in case someone resizes the screen to a mobile view, we don't want a button leading nowhere.
Alternatively don't delete the second content then people can resize the browser, just means a few extra DOM nodes (so as long as your modal content isn't over 100 DOM elements I would say do this.)
If you decide to keep the modal make sure that is also display: none
for the same reason as the button, we don't want people accidentally accessing it.
Duplicate content.
I know, I know, duplicate content is just, urgh.
But sometimes you just have to put up with it if it is for the best.
By duplicating the content into a div from the start you do get a few advantages.
Advantages
Disadvantages
This would still be my preference, Keep It Simple! This is far more robust
Client Hints are one way you could solve this, turning responsive design into a hybrid of mobile vs desktop and responsive.
When client hints has enough of a market share you could simply use the header to decide which version of the page to send from the initial request.
You could possibly implement this today if you are willing for 25% of users to see the mobile version of your information on desktop, depends how important the info is.
There are a few other things to consider that you haven't mentioned so I thought I would add for reference.
I already mentioned adding aria-hidden
to all elements outside of the modal when it is active.
To future proof your application use inert
on items outside of the modal. Support isn't great (none existent!), but every little helps and it is quite likely to get implemented!
You can polyfill it if you want but I don't think it has moved outside of the draft spec yet so we just use it as is.
Also add aria-modal="true"
to your modal.
I covered a lot of these points in a bit more detail in this answer if you want a bit more info.