Accessible Tabs Using React & Tailwind CSS
We're going for tabs with automatic activation. This means a tab panel will be revealed as soon as it's respective tab receives focus either by click or by using the arrow keys.
We want to re-use the tabs functionality in many places, with many different styles. So the tab component must:
- Be re-usable.
- Be completely unstyled.
- Integrate well with Tailwind CSS.
Requirements for accessibility From W3:
Tab
: When focus moves into the tab list, places focus on the active tab element.- When focus is on a tab element in a horizontal tab list:
Left Arrow
: moves focus to the previous tab. If focus is on the first tab, moves focus to the last tab.Right Arrow
: Moves focus to the next tab. If focus is on the last tab element, moves focus to the first tab.
- When focus is on a tab element in a vertical tab list:
Up Arrow
: moves focus to the previous tab. If focus is on the first tab, moves focus to the last tab.Down Arrow
: Moves focus to the next tab. If focus is on the last tab element, moves focus to the first tab.
- When focus is on a tab in a tablist with either horizontal or vertical orientation:
Space
orEnter
: Activates the tab.Home
: Moves focus to the first tab.End
: Moves focus to the last tab.
Funk
I've created a package called Funk, which contains a set of components to create Tabs.
yarn add @statikly/funk
or
npm install --save @statikly/funk
You can then use it in such fashion:
import React from 'react'
import { TabGroup } from '@statikly/funk'
const Page = () => {
return (
<div className="h-screen w-screen flex flex-col justify-center items-center">
<TabGroup numTabs={3} direction={TabGroup.direction.HORIZONTAL}>
<TabGroup.TabList>
<TabGroup.Tab
index={0}
className="h-12 px-12 transition-colors duration-150"
activeClassName="bg-black text-white"
inactiveClassName="text-black"
>
Tab 1
</TabGroup.Tab>
<TabGroup.Tab
index={1}
className="h-12 px-12 transition-colors duration-150"
activeClassName="bg-black text-white"
inactiveClassName="text-black"
>
Tab with input
</TabGroup.Tab>
<TabGroup.Tab
index={2}
className="h-12 px-12 transition-colors duration-150"
activeClassName="bg-black text-white"
inactiveClassName="text-black"
>
Tab 3
</TabGroup.Tab>
</TabGroup.TabList>
<TabGroup.TabPanel
index={0}
className="p-16 transition-all transform h-64"
activeClassName="opacity-100 duration-500 translate-x-0"
inactiveClassName="absolute opacity-0 -translate-x-2"
>
Content 1
</TabGroup.TabPanel>
<TabGroup.TabPanel
index={1}
className="p-16 transition-all transform h-64 flex flex-col"
activeClassName="opacity-100 duration-500 translate-x-0"
inactiveClassName="absolute opacity-0 -translate-x-2"
>
<label className="font-semibold mb-1" htmlFor="input">
Input
</label>
<input
id="input"
type="text"
className="border border-gray-400 px-8 h-12"
placeholder="Focus me!"
/>
</TabGroup.TabPanel>
<TabGroup.TabPanel
index={2}
className="p-16 transition-all transform h-64"
activeClassName="opacity-100 duration-500 translate-x-0"
inactiveClassName="absolute opacity-0 -translate-x-2"
>
Content 3
</TabGroup.TabPanel>
</TabGroup>
<button className="mt-12 h-12 px-12 bg-indigo-500 text-white">
Outside button
</button>
</div>
)
}
Check out the Codesandbox and give it a whirl!
{% codesandbox funk-example-uw048 %}
Prefer to get the code yourself or take a look under the hood? Check out the repo here!