Context
I am building a website with a marquee, <ul role='marquee'>...</ul>
with content that visually changes every few seconds. The marquee sits at the top of the page above a form. The marquee displays helpful hints and promotional messages as the user is filling out the form.
The content displayed in the marquee does not change according to any user interaction, rather the marquee cycles through a static list of li
tags.
Although the marquee may contain important information about the form, that information will be conveyed in appropriate places throughout the form.
Lastly, the marquee will never contain a link or other interactive element.
Task
In order to comply with WCAG, I am looking for the best practice as far as using ARIA attributes on the marquee.
Labeling
My initial thought is to add aria-label
to help screen reader users know what the ul
is all about. I have only seen this applied to button
and a
tags. Will it work the same on a ul
tag? Or should I create a visually hidden h_
tag above and use aria-labelledby
, like in this answer.
Next, I am unsure about what label to give. I came up with 'hints'
, 'tips'
, and 'advice'
.
Then I thought about making those sound friendlier, for instance 'helpful hints'
, 'tips and tricks'
, and 'words of advice'
. Is this overly casual?
My concern with all of these labels is that they may be too vague and non-descriptive.
What are your thoughts?
Hiding
Do you think I should simply apply aria-hidden
to make screen readers ignore the marquee altogether?
My thinking behind this is that the information displayed in the marquee is disorganized (many messages without a consistent theme), and the messages that are important will be repeated elsewhere. I wonder if the marquee would only get in the way.
Other approaches
Please let me know if you have other approaches that maybe I have not thought of.
Minimal working example
const list = document.querySelector('ul');
const listItems = document.querySelectorAll('li');
let position = 0;
let animationID = null;
const startAnimation = () => {
animationID = window.setInterval(() => {
position = (position + 1) % listItems.length;
listItems.forEach((item, index) => {
if (position === index) {
item.classList.remove('invisible');
item.classList.add('visible');
} else {
item.classList.remove('visible');
item.classList.add('invisible');
}
});
}, 4000);
};
const stopAnimation = () => {
window.clearInterval(animationID);
animationID = null;
};
const initialize = () => {
listItems.forEach((item, index) => {
if (position === index) {
item.classList.remove('invisible');
item.classList.add('visible');
} else {
item.classList.remove('visible');
item.classList.add('invisible');
}
});
startAnimation();
};
window.onload = initialize;
list.addEventListener('mouseenter', stopAnimation);
list.addEventListener('mouseleave', startAnimation);
ul {
font-family: monospace;
list-style: none;
margin: 0 auto;
padding: 0;
width: 100%;
max-width: 360px;
height: 36px;
border: 1px solid black;
border-radius: 8px;
overflow: hidden;
position: relative;
}
@media (max-width: 400px) {
ul {
height: 56px;
}
}
li {
position: absolute;
width: 100%;
height: 100%;
transition: opacity 500ms;
opacity: 0;
transform-origin: 50% 50% -30px;
display: inline-flex;
justify-content: center;
align-items: center;
}
li.visible {
animation: swoop-in 500ms linear forwards;
opacity: 1;
}
li.invisible {
animation: swoop-out 500ms linear forwards;
opacity: 0;
}
@keyframes swoop-in {
0% {
transform: rotateX(-0.25turn);
}
100% {
transform: none;
}
}
@keyframes swoop-out {
0% {
transform: none;
}
100% {
transform: rotateX(0.25turn);
}
}
<ul role='marquee'>
<li>All websites should be accessible</li>
<li>Save money by signing up today</li>
<li>This form will only take a few minutes</li>
<li>Ask us about our discounts</li>
<li>We will never share your information</li>
</ul>