Skip to content

Instantly share code, notes, and snippets.

@SigurdMW
Created April 24, 2018 08:15
Show Gist options
  • Save SigurdMW/fdb74b33b81f741cab12fabedd056669 to your computer and use it in GitHub Desktop.
Save SigurdMW/fdb74b33b81f741cab12fabedd056669 to your computer and use it in GitHub Desktop.
accessible vue tab list
<template>
<div class="tabs">
<ul role="tablist" class="tabs__list">
<li role="presentation" v-for="(tab, key) in children" :key="key" class="tabs__list-item">
<a
:href="'#' + tab.id"
role="tab"
:aria-controls="tab.id"
:id="tab.trigger"
class="tabs__link"
:class="{ 'tabs__link--active': visibleTabIndex === key }"
@click.prevent="showTab(key)"
:aria-selected="visibleTabIndex === key"
:tabindex="visibleTabIndex !== key ? '-1' : '0'"
@keydown.left.right.down.prevent="handleKey"
>
{{ tab.name }}
</a>
</li>
</ul>
<slot></slot>
</div>
</template>
<script>
// Thanks to https://codepen.io/BeyondHyper/pen/xZXXzj
export default {
name: "TabListComponent",
data () {
return {
children: [],
visibleTabIndex: 0
}
},
mounted () {
this.children = this.$children.map(child => ({
id: child.id,
trigger: child.trigger,
name: child.name,
el: child
}));
// shot first tab on load.
this.children[0].el.showMe();
},
methods: {
showTab (key) {
this.children.map(child => child.el.hideMe());
this.visibleTabIndex = key;
this.children[key].el.showMe();
},
updateActiveIndex (newIndex) {
if (newIndex < 0) newIndex = this.children.length - 1;
if (newIndex > this.children.length - 1) newIndex = 0;
this.visibleTabIndex = newIndex;
this.showTab(newIndex);
this.$el.querySelector("#" + this.children[newIndex].trigger).focus();
},
focusTabContent () {
this.children[this.visibleTabIndex].el.$el.focus();
},
handleKey (e) {
if (e.keyCode === 37) { // left
this.updateActiveIndex(this.visibleTabIndex - 1);
} else if (e.keyCode === 39) { // right
this.updateActiveIndex(this.visibleTabIndex + 1)
} else if (e.keyCode === 40) { // down
this.focusTabContent();
}
}
}
}
</script>
<template><!-- new file --></template>
<template>
<section
:id="id"
role="tabpanel"
:aria-labelledby="trigger"
tabindex="-1"
hidden
class="tabs__content"
>
<slot></slot>
</section>
</template>
<script>
export default {
name: "TabComponent",
props: {
name: {
type: String,
required: true
}
},
data () {
return {
id: "tab-component-" + this._uid,
trigger: "tab-trigger-" + this._uid
}
},
methods: {
showMe () {
this.$el.classList.add("tabs__content--open");
this.$el.removeAttribute("hidden");
},
hideMe () {
this.$el.classList.remove("tabs__content--open");
this.$el.setAttribute("hidden", "hidden");
}
}
}
</script>
<template><!-- usage --></template>
<tab-list-component>
<tab-component name="some tab">
<p>some content in tab group 1</p>
</tab-component>
<tab-component name="some other tab">
<p>some content in tab group 2</p>
</tab-component>
</tab-list-component>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment