Add a Skip Link Utility as a Tailwind Plugin
This is part 8 in the Build a Blog with Next, MDX, and Tailwind series
In an effort to start filling in some accessibility gaps, we’re going to add a skip link to our site. This link will be the first element to receive focus and clicking it will jump the user past our header to the content area.
We’ll implement this as a TailwindCSS Plugin that we’ll define right in our tailwind.config.js
file.
Initial Plugin Setup
In order to create this plugin, we’ll start by importing the plugin
function from tailwind into our tailwind config.
tailwind.config.js
+const plugin = require('tailwindcss/plugin')
module.exports = {
content: [
'./pages/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
'./content/**/*.md',
],
theme: {
extend: {
gridTemplateRows: {
3: 'auto 1fr auto',
},
},
},
plugins: [
require('@tailwindcss/typography'),
require('@tailwindcss/line-clamp'),
],
}
Now in the plugins array, we can add our plugin function.
plugins: [
require('@tailwindcss/typography'),
require('@tailwindcss/line-clamp'),
+ plugin(function ({ addComponents }) {
+ addComponents([
+
+ ])
+ }
],
We’re destructuring the argument passed into the plugin callback and grabbing the addComponents
function. Then inside the callback, we’ll call addComponents
with an array. This array defines each component as an object.
Let’s define that object now. The following object will be the only element in that addComponents
array.
{
'.skip-link': {
position: 'absolute',
transform: 'translateY(-100%)',
'&:focus': {
transform: 'translateY(0%)',
},
},
},
Here, we’re defining a CSS class called skip-link
that absolutely positions an element at the top of our page, shifts it up out of the view, and then resets the transform
when the element is focused.
Your Tailwind config should look like this now.
const plugin = require('tailwindcss/plugin')
module.exports = {
content: [
'./pages/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
'./content/**/*.md',
],
theme: {
extend: {
gridTemplateRows: {
3: 'auto 1fr auto',
},
},
},
plugins: [
require('@tailwindcss/typography'),
require('@tailwindcss/line-clamp'),
plugin(function ({ addComponents }) {
addComponents([
{
'.skip-link': {
position: 'absolute',
transform: 'translateY(-100%)',
'&:focus': {
transform: 'translateY(0%)',
},
},
},
])
}),
],
}
We’ll come back and apply a bit more styling to this in a bit, but this is enough for use to add it to our site and try it out.
Adding the Markup
We’re going to go into components/MainLayout.tsx
and at the top of our header
element, we’ll add a link and apply our new skip-link
class to it. We’ll set the href
to #main-content
, and then update the main
element in our layout so that main-content
is the id
attribute for that element.
<header>
+ <a href="#main-content" className="skip-link">
+ Skip to main content
+ </a>
<div className="py-6">
<span className="text-5xl font-extrabold text-sky-800">
My Awesome Blog
</span>
</div>
<nav>
<NavLink href="/">Home</NavLink>
<NavLink href="/blog">Blog</NavLink>
</nav>
</header>
-<main>{children}</main>
+<main id="main-content">{children}</main>
With this in place, you can hit your tab key from the browser’s address bar, and the skip link should be visible and focused. You can hit enter and it should jump to the main content area.
- If you have Chrome’s DevTools open, they will get focus before your page, so you’ll want to close those for this test.
- You might not notice the jump unless you’re on a page with enough content to scroll, so try it out from one of the sample posts with a decent amount of content.
Make it Look Nice
We’ve satisfied our need for a skip link, but there’s no reason not to make it look nice. Let’s see how you can tap into the Tailwind theme and apply some styling in this plugin.
Back in tailwind.config.js
let’s add the theme()
function to our destructured plugin callback arguments.
plugins: [
require('@tailwindcss/typography'),
require('@tailwindcss/line-clamp'),
- plugin(function ({ addComponents }) {
+ plugin(function ({ addComponents, theme }) {
addComponents([
This theme
function allows us to look up values from the currently configured theme. This way, if we override a default value in the theme, it’ll carry through to our plugin.
Let’s throw some additional styling into our plugin and then talk through the values.
plugin(function ({ addComponents, theme }) {
addComponents([
{
'.skip-link': {
position: 'absolute',
transform: 'translateY(-100%)',
+ padding: theme('spacing.1'),
+ borderRadius: theme('borderRadius.md'),
+ color: theme('colors.sky.900'),
+ fontWeight: theme('fontWeight.semibold'),
+ textDecoration: 'underline',
+ backgroundColor: theme('colors.gray.100'),
+ borderWidth: theme('borderWidth.2'),
+ borderColor: theme('colors.sky.900'),
+ transition: 'transform 0.3s',
'&:focus': {
transform: 'translateY(0%)',
},
},
},
])
}),
I’ve thrown some padding, borders, and text styling on here. You’ll notice that several of the values are making use of the theme
function. The theme path of the desired value is passed to theme
as a string. In order to determine what those values are, I referenced the structure of the default tailwind theme here.
Now when we tab from the address bar, we should see a more styled skip link slide in with a subtle animation. You can, of course, style this as much or as little as you want for your own needs.
Wrap Up
By doing this via a tailwind plugin, we’ve created our custom styling without opting out of the theming capabilities offered by using Tailwind. While we defined this one inline, we could easily extract it and publish it via npm if we needed to use this in multiple projects.
We’ve improved the accessibility of our site, and we’ve learned how straightforward it can be to create a tailwind plugin to add custom capabilities.
Check back next week for the next article in the series, Add Page Titles and the lang Attribute in a Next.js App!