Not connecting parts of a UI that relate to one another is a leading cause of stress accessibility issues
In this case, there's an implicit relation between the tab list and its associated tabs
<tab selected>Tab 1</tab>
Tab 2
Behind the scenes, we may want to automatically set the aria-activedescendant
attribute
It could look like the following in Vue.js using some utility mixins
const tabs = relation({ explicit: false })
const TabList = {
mixins: [hasMany(tabs, "tabs")],
computed: {
activeDescendant() {
return this.tabs.find(tab => tab.selected)
}
}
}
const Tab = {
mixins: [memberOf(tabs)]
}
In this case, there's an explicit relation between the button and the content it collapses
...
Behind the scenes, we may want to automatically set the aria-controls
attribute
<div id="collapsible" class="collapse-content" hidden>...</div>
Again, it could look like the following in Vue.js using some utility mixins
const collapse = relation({ explicit: true })
const Collapse = {
mixins: [hasOne(collapse)],
}
const Button = {
mixins: [memberOf(collapse, "collapse")],
computed: {
controls() {
return this.collapse.id
}
}
}
Another common pitfall: When to use <a>
and when to use <button>
?
Find the "right" question to ask!
What kind of interaction do you want?
If you don't want interaction, this...
...
If you want interaction and have a link, this...
...
If you want interaction and have a click handler, this...
...
It could look like the following in Vue.js as a component template