Tips for consistently implementing focusVisible design on a targeted Material-UI Button element:

When using the Material-UI Button component, I want the "focus" styling to match the "focusVisible" styling. This means I want the same ripple effect to be visible whether the button is focused programmatically or with the mouse, as it would be when focused with the tab key.

I discovered a workaround by triggering a

dispatchEvent(new window.Event("keydown"))
on the element before focusing it, which forces the keyboard to be the last input type used. This successfully achieves the desired button appearance until a mouse event like onMouseLeave is fired, causing the visible focus to disappear.

I managed to alter the focus styling of the component using the following code:

import React from "react"
import { withStyles } from "@material-ui/core/styles"
import Button from "@material-ui/core/Button"

const styles = {
  root: {
    '&:focus': {
      border: "3px solid #000000"
    }
  }
}

const CustomButtonRaw = React.forwardRef((props, ref) => {
  const { classes, ...rest } = props
  return <Button classes={{root: classes.root}} {...rest} ref={ref}/>
}

const CustomButton = withStyles(styles, { name: "CustomButton" })(CustomButtonRaw)

export default CustomButton

Although I was able to apply some styles to the button in the "focus" state (such as adding a border), I am struggling to get the styles to actually apply. I attempted adding the className 'Mui-visibleFocus' to the Button, but it appeared to have no effect. Is there a way to access the styles that would be applied if the Button was in the visibleFocus state?

Answer №1

ButtonBase is a crucial component in Material-UI that Button delegates to. It includes an action prop that enables the setting of the button's focus-visible state through the use of the useImperativeHandle hook.

To utilize this functionality, you need to pass a ref into the action prop and later call actionRef.current.focusVisible(). However, this alone is not enough because ButtonBase listens to various mouse and touch events for ripple effects. If you use the disableTouchRipple prop, it stops the ripple animation based on those events.

Unfortunately, disabling the touch ripple effect also removes the click and touch animations on the button. One way to restore these animations is to manually add another TouchRipple element that you have control over. The example provided below demonstrates how to handle onMouseDown and onMouseUp events to achieve this.

For a comprehensive solution, it would be necessary to address all the different events that ButtonBase handles. The working example showcases a custom button component called FocusRippleButton that incorporates these concepts.

Feel free to check out the CodeSandbox example for a live demonstration.

Answer №2

Utilizing the action attribute of the material-ui Button component allows us to create a reference and implement the ripple effect using useLayoutEffect

import React, { createRef, useLayoutEffect } from "react";
import Button from "@material-ui/core/Button";

function RippleButton(props) {
  const { handleClose } = props;

  const actionRef = createRef();

  useLayoutEffect(() => {
    if (actionRef.current) {
      actionRef.current.focusVisible();
    }
  }, []);

  return (
    <Button action={actionRef} onClick={handleClose}>
      Ok
    </Button>
  );
}

The RippleButton above can serve as an alternative to the standard Button, or you can simply utilize a reference to trigger the focusVisible() method

For example:

const buttonRef = createRef();

const handleButton2Click = () => {
    buttonRef.current.focusVisible();
};

.
.
.
.

<Button action={buttonRef} variant="outlined">
        Button 1
</Button>

<Button variant="outlined" color="primary" onClick={handleButton2Click}>
        Button 2
</Button>

Explore the live demo on this link

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Removing a comma from a dynamic loop of Material UI Text fields in React can be achieved by accessing

Have a question for you, Currently, I'm utilizing React's material UI Text Field component, When I iterate through the text field from a dynamic array and update the default value sourced from that same array, the default value ends up includin ...

How to organize initial, exit, and layout animations in Framer Motion (React) tutorial?

Currently, I am utilizing framer-motion library for animating a change in grid columns. This is the objective: Within the grid container (#db-wrapper), there are nine buttons arranged. https://i.stack.imgur.com/61pQqm.png When the user switches to the ...

The options in the drop-down menu appear to be stuck and remain in place

Can someone with expertise lend me a hand? I've been racking my brain over this issue, and while I feel like I'm on the right track, I just can't seem to make my sub-sub menu items drop down to the right on hover. I suspect there might be c ...

Designing table rows to resemble full-width cards

Looking for a way to transform the rows of a table into full-width cards while maintaining the row layout? Here's what I have tried so far: tbody>tr { display: block; padding: 1rem 0.5rem 1rem 0.5rem; margin: 1.5rem; border: 1px solid rgba( ...

Encountering the "node:internal/modules/cjs/loader:1050" error while attempting to execute a folder using the "npm run dev" command

I am encountering an issue while attempting to execute a folder using npm run dev, consistently resulting in the same error message. PS C:\Users\My Name\Desktop\My Folder> npm run dev Debugger attached. > <a href="/cdn-cgi/l/e ...

Using React, the value of the <input> element will remain the same even after editing

I am facing an issue with my edit page where I am unable to update the details of the existing object. Despite having input elements to contain the values, when I try to change them on the view by deleting or typing new values, they do not get updated. C ...

"Developing a stand-alone ASP.NET Core WebAPI with back-end authentication, and creating a React Redux application with front-end authentication that operate autonom

Looking to develop an ASP.NET Core Web API with authentication using ApplicationDbContext as the default database context. I prefer working with Visual Studio for the backend development. 2. Interested in building a React-Redux app with authentication for ...

Is it possible to use both MUI makeStyles and Theming in the _app.js file?

I've been working on a complex navigation component using MUI Persistent Drawers within a React + Next.js setup. To achieve the desired content shrinking effect based on state changes, I ended up placing the entire navigation system inside the _app.js ...

Eliminate file security error in Meteor by utilizing Filestack

I've been working on integrating the react filestack plugin with meteor, and I'm running into issues when enabling the security settings to remove uploaded files. To delete a file, I need to utilize App Secret, policy, signature, apikey. However, ...

Problem with ternary operator when returning in React

I am attempting to incorporate a return statement with a ternary operation, but I encountered an error message stating Unexpected token, expected "," (31:21) at the point where Object.keys is used. I am unsure about what is causing this issue and would a ...

Changing the text alignment to justify in OWA - HTML emails

Having trouble navigating the Outlook Web App (OWA)? It seems like there are countless issues with different Outlook clients, but OWA is definitely the most poorly documented one. The snippet of code below functions flawlessly in all email clients such as ...

Setting a default value for Autocomplete in MaterialUI and React.js

Is there a way to set a default value for an Autocomplete TextField component from Material UI in React.js? I want to load a pre-populated value from the user's profile that can then be changed by selecting another option from a list. Check out my co ...

The element within the iterator is lacking a "key" prop, as indicated by the linter error message in a React component

Encountering an error stating Missing "key" prop for element in iteratoreslintreact/jsx-key {[...Array(10)].map((_) => ( <Skeleton variant="rectangular" sx={{ my: 4, mx: 1 }} /> ))} An attempt to resolve this issue was made ...

React component state change in reverse

As I work on a simple login form component, I encountered an issue where clicking on the form should make it disappear and only display my JSON data. However, due to my limited experience with React state management, I seem to be achieving the opposite eff ...

How can I assign global classes to material-ui components in ReactJs?

I have defined some global classes for fonts and colors, but I am unable to apply these classes within material-ui Typography. I am using the material-ui library available at: https://material-ui.com/ Below is the code snippet: <Typography className={ ...

Removing the values of all keys in Async Storage in React Native when logging out

I have implemented the usage of async storage in React Native to store responses obtained from a server. However, I am facing an issue with removing the value of each key during logout using the removeItem method. Below is the snippet of my code... logout ...

What are the steps to vertically aligning rows in bootstrap?

I've been exploring different solutions to vertically center my rows within the container. Adding d-flex and align-items-center did work for centering them vertically but caused the rows to appear side by side. I'm curious if there's an alte ...

Navigating to the previous page in a Next.js 14 application with the App Router

Recent discussions have mainly focused on the Pages router. I am aware that with react router, I can achieve the following: const history = useHistory(); const { pathname } = useLocation(); history.push('/', { from: pathname }) Subsequently, I ...

Choosing an HTML element within a useEffect Hook

Having an issue with selecting an element inside another element in the DOM that was selected using the useRef hook. const editorRef = useRef(null); Trying to access the child element on the editorRef within a useEffect hook. useEffect(() => { edito ...

Troubles with NextJS and TailwindCSS Styling

I encountered a strange issue when I used the component separately. Here's how the code looked like: <> <Head> <title>Staycation | Home</title> <meta name="viewport" content="initial- ...