Skip to main content Link Menu Expand (external link) Left Arrow Right Arrow Document Search Copy Copied

Accessible Focus Indicators

Accounting for keyboard users is an imperative step towards ensuring that all users can easily navigate your website’s user interface. A keyboard user’s cursor equivalent is called a focus indicator. Unfortunately, not all websites feature accessible focus indicators. By designing and implementing accessible focus indicators, we can make our website more accessible to keyboard users and users of assistive technology.

According to the Web Content Accessibility Guidelines (WCAG), a focus indicator should satisfy the following requirements:

  • The focus indicator should enclose the visual presentation of the user interface component;
  • The focus indicator should have a contrast ratio of at least 3:1 between its pixels in the focused and unfocused states;
  • The focus indicator should have a contrast ratio of at least 3:1 against adjacent colors or should not be thinner than 2px.

By default, MUI uses a pulsing ripple effect when tabbing through UI elements. The pulsing ripple does not meet the accessibility requirements. In this section of the workshop, we will learn how to style the focus indicators of a number of MUI components to ensure that they meet the requirements outlined by the WCAG.

We will start updating the custom buttons we created for the navigation bar to ensure that that they have accessible focus indicators.

Open the MacNavButton.tsx file located under the components/MacComponents directory and the following lines of code under both the MacNavButton and MacIconNavButton declarations:

// adding a solid white border that is 2px thick to the button that is shown when tabbing through it
"&:focus-visible": {
	outline: "2px solid #fff",
},

We defined the focus-visible pseudo-class to be a solid white outline that is 2px thick.

Next, we will need to define the focus-visible pseudo-class for active links in the navigation bar. Open the Navbar.module.css file in the styles directory and add the following lines of code to it:

/* Specific styles when tabbing through an active navbar link (solid yellow border, white text and grey background) */
.active:focus-visible {  
    color: white;  
    background: #707070;  
    outline-color: #fdbf57;  
}

Save the file, stop the container and restart it, and then go back to your browser and reload the page. Try to tab through the navigation bar and notice the updated focus indicator.

Accessible Focus Indictor on MacNavButton Accessible Focus Indicator on MacIconNavButton
focus-nav focus-icon

The focus indicator on active links also adheres to the accessibility standards: focus-nav-active

Buttons

We will now update the custom MacButton component that we created earlier with an accessible focus indicator.

Open the MacButton.tsx file located under the components/MacComponents directory and the following lines of code under MacButton declarations

// setting the focus indicator style  
// in light mode: primary buttons will have a black outline and grey background  
// in light mode: secondary buttons will have a maroon outline and light grey background  
// in dark mode: primary buttons will have a desaturated maroon (pink) outline and grey background  
// in dark mode: secondary buttons will have a vivid maroon outline and light grey background
"&:focus-visible": {
	color: useTheme().palette.mode === 'dark' ? (props.mainColor === 'primary' ? '#FFFFFF' : '') : '',
	backgroundColor:
		props.mainColor === 'secondary' ? '#DBDBDD' : '#5E6A71',
	outline: `2px solid ${useTheme().palette.mode === 'dark' ? (props.mainColor === 'primary' ? '#F4B7C7' : '#dd3765') : (props.mainColor === 'primary' ? 'black' : useTheme().palette.primary.main) }`,
},

We determine the color of the focus indicator depending on the current theme (light/dark) and whether the button is a primary or secondary button. The chosen color of the outline has a contrast ratio that is equal to or greater than 3:1 with neighboring colors.

Focus Indictor on Primary MacButton Focus Indicator on Secondary MacButton
focus-primary-light focus-primary-dark focus-secondary-light focus-secondary-dark

Switches

We will start by adding a regular Switch component to “Page 1” to inspect the default focus indicator.

Open the app/page_1/page.tsx file and add the following import statement:

import {Switch} from "@mui/material";

Next, add the following lines after the </Box> closing tag:

<Box paddingLeft={5}>  
    <Switch {...{ inputProps: { 'aria-label': 'Switch' } }} />  
</Box>

Go back to your browser and try to tab through the switch component and notice how hard it is to see the focus indicator against a white background.

switch-default-focus

We will now create a custom MacSwitch component that has an accessible focus indicator.

Create a new MacSwitch.tsx file under the components/MacComponents directory and add the following lines of code to it:

import styled from "@emotion/styled";
import Switch, {SwitchProps} from "@mui/material/Switch";
import React from "react";

// disabling the ripple effect using disableFocusRipple
export const MacSwitch = styled((props: SwitchProps) => (
    <Switch disableFocusRipple {...props} />  
))(({ }) => ({
    '& .MuiSwitch-switchBase': {
	    // when the switch is disabled and in focus, its background color will be dark grey and the outline will be light pink
        '&.Mui-focusVisible .MuiSwitch-thumb': {
            backgroundColor: '#5E6A71',
            outline: `3px solid #F4B7C7`,
        },
    },
    // when the switch is enabled and in focus, its background color will be vivid maroon and the outline will be heritage gold
    '& .MuiSwitch-switchBase.Mui-checked': {
        '&.Mui-focusVisible .MuiSwitch-thumb': {
            backgroundColor: '#dd3765',
            outline: '3px solid #fdd287',
        },
    }
}));

We disabled the default ripple focus indicator and replaced it with a solid outline that changes color depending on whether the switch is enabled or not. We also change the color of the switch thumb when it is in focus for emphasis.

Go back to the page_1/page.tsx file and replace the Switch import statement with the following import statement:

import {MacSwitch} from "@/components/MacComponents/MacSwitch";

Replace the Switch component with a MacSwitch component:

<Box paddingLeft={5}>  
    <MacSwitch {...{ inputProps: { 'aria-label': 'Switch' } }} />  
</Box>

Go back to your browser and try tab over the new MacSwitch:

Focus Indictor on Enabled MacSwitch Focus Indicator on Disabled MacSwitch
switch-on-focus switch-off-focus

Checkboxes

We will now add a regular CheckBox component to “Page 1” to inspect the default focus indicator.

Open the app/page_1/page.tsx file and add the following import statement:

import {Checkbox} from "@mui/material";

Next, add the following line inside the Box component containing the MacSwitch:

<Box paddingLeft={5}>  
	<MacSwitch {...{ inputProps: { 'aria-label': 'Switch' } }} />
	<Checkbox {...{ inputProps: { 'aria-label': 'Checkbox' } }} />    
</Box>

Go back to your browser and try to tab through the checkbox component and notice how hard it is to see the focus indicator against a white background.

checkbox-default-focus

We will now create a custom MacCheckbox component that has an accessible focus indicator.

Create a new MacCheckbox.tsx file under the components/MacComponents directory and add the following lines of code to it:

import styled from "@emotion/styled";  
import Checkbox, {CheckboxProps} from "@mui/material/Checkbox";  
import React from "react";  
import {useTheme} from "@mui/material/styles";  

// disabling the ripple effect using disableFocusRipple
export const MacCheckbox = styled((props: CheckboxProps) => (  
    <Checkbox disableFocusRipple {...props} />  
))(({ }) => ({  
	// adding a solid outline that is 2px thick and uses the current primary theme color (in light or dark mode)
    '&.Mui-focusVisible': {  
        outline: `2px solid ${useTheme().palette.primary.main}`,  
    },  
}));

We disabled the default ripple focus indicator and replaced it with a solid outline that uses the primary color of our theme.

Go back to the page_1/page.tsx file and replace the Checkbox import statement with the following import statement:

import {MacCheckbox} from "@/components/MacComponents/MacCheckbox";

Replace the Checkbox component with a MacCheckbox component:

<Box paddingLeft={5}>  
    <MacSwitch {...{ inputProps: { 'aria-label': 'Switch' } }} />  
    <MacCheckbox {...{ inputProps: { 'aria-label': 'Checkbox' } }} />  
</Box>

Go back to your browser and try tab over the new MacSwitch:

Focus Indictor on MacCheckbox in Light Mode Focus Indicator on MacCheckbox in Dark Mode
checkbox-focus checkbox-focus-dark

Remaining MUI ButtonBase Components

MUI uses the ButtonBase for many of its clickable components like options in a dropdown menu, action areas, icon buttons, etc.

Adding an accessible focus indicator to the ButtonBase class will ensure that the vast majority of clickable components in our SPA can be easily navigated using the keyboard.

Open the Provider/Provider.tsx file and update the theme constant declaration as shown below:

const theme = React.useMemo(  
    () =>  
        createTheme({  
            ...themeOptions,  
            palette: {  
                mode:  
                    themeMode == null  
                        ? prefersDarkMode  
                            ? 'dark'  
                            : 'light'  
                        : themeMode,  
                primary: {  
                    main: primary_color  
                },  
                secondary: {  
                    main: secondary_color  
                },  
            },  
            components: {  
                ...themeOptions.components,  
                MuiButtonBase: {  
                    styleOverrides: {  
                        root: {
                        // adding a solid outline that is 2px thick and uses the current primary theme color (in light or dark mode)  
                            "&:focus-visible": {  
                                outline: `2px solid ${primary_color}`,  
                            },  
                        },  
                    },  
                }  
            }  
        }),  
    [themeMode, prefersDarkMode]  
)

We added a focus indicator that uses the primary color of our theme to the MuiButtonBase component.

Save the file and then try to tab through the options in the “Download” button dropdown menu on “Page 1”:

focus-dropdown

If your website uses additional MUI components that were not covered, it is important to ensure that all of them have accessible focus indicators that mimic the ones shown in this workshop.