React Material Card Expands on Hover with CSS

Table of Contents
Introduction
Combining CSS with MUI components, we can animate a card that expands on a mouse enter event. The card uses two different constants that have the CSS. When the card expands it will reveal two additional buttons on the bottom. This example is one of many ways to achieve animation with React. A common alternative for animations is Framer Motion.
Libraries Needed
MUI – Material UI v5.4.3
Material UI library for React
npm install @mui/material @mui/lab @emotion/react @emotion/styled
Source Files
GitHubMUI Card Component
This component renders a MUI card and uses the onMouseEnter()
and onMouseLeave()
events to expand and collapse the card.
The source for ‘MUICard.js’. Below the source is break down of the component.
import React, { useState, useEffect } from 'react'
import Card from '@mui/material/Card';
import CardHeader from '@mui/material/CardHeader';
import CardMedia from '@mui/material/CardMedia';
import CardContent from '@mui/material/CardContent';
import CardActions from '@mui/material/CardActions';
import Button from '@mui/material/Button';
import Avatar from '@mui/material/Avatar';
import Box from '@mui/material/Box';
import Divider from '@mui/material/Divider';
import Stack from '@mui/material/Stack';
import Grid from '@mui/material/Grid';
import Badge from '@mui/material/Badge';
import { red } from '@mui/material/colors';
import GroupIcon from '@mui/icons-material/Group';
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
import PrizeEventsIcon from '@mui/icons-material/EmojiEvents';
import IconButton from '@mui/material/IconButton';
import FavoriteIcon from '@mui/icons-material/Favorite';
export default function MUICard(props) {
const [isLiked, setIsLiked] = useState(false)
const [isEventOpen, setIsEventOpen] = useState(false)
const [isExpandCard, setIsExpandCard] = useState(false)
const expandCard = () => {
setIsExpandCard(true)
}
const collapseCard = () => {
setIsExpandCard(false)
}
const cardRoot = {
minWidth: 320,
maxHeight: 500,
marginLeft: '20px',
transition: 'transform 0.5s'
}
const cardRootExpand = {
position: 'absolute',
minWidth: 340,
maxHeight: 520,
marginLeft: '20px',
transform: 'scale(1.1)',
transition: 'transform 0.5s',
zIndex: 99,
}
return (
<Card style={isExpandCard ? cardRootExpand : cardRoot}
onMouseEnter={() => expandCard()}
onMouseLeave={() => collapseCard()}>
<CardHeader
avatar={
<Avatar sx={{ bgcolor: red[500] }} aria-label="event">
R
</Avatar>
}
title={props.data.title}
subheader={
<b>Sub Header</b>
}
>
</CardHeader>
<CardMedia media={'image'} />
<Box sx={{display: "flex", justifyContent: "center"}}>
<img src={'/contest.svg'}
width="240"
height="240"
layout="intrinsic"/>
</Box>
<CardContent>
<b>{isEventOpen && 'Vote Now'}</b>
</CardContent>
<CardActions disableSpacing>
<Stack spacing={3}>
<Stack spacing={3} direction="row">
<IconButton aria-label="add to favorites">
{isLiked ?
<FavoriteIcon color={'error'}/>
:
<FavoriteBorderIcon color={'primary'}/>
}
</IconButton>
<IconButton aria-label="share">
<Badge badgeContent={4} color="success">
<GroupIcon />
</Badge>
</IconButton>
<Button variant="outlined" size="small" color="primary">Open</Button>
</Stack>
{isExpandCard &&
<>
<Divider/>
<Grid container spacing={1}>
<Grid item xs={6} align="left">
<Button variant='outlined'>Watch Later</Button>
</Grid>
<Grid item xs={6} align="right">
<Button variant='outlined' color="warning">Add to Queue</Button>
</Grid>
</Grid>
</>
}
</Stack>
</CardActions>
</Card>
);
}
Breakdown of the MUICard.js Component
Track the mouse events with a boolean state variable isExpandCard
.
const [isExpandCard, setIsExpandCard] = useState(false)
const expandCard = () => {
setIsExpandCard(true)
}
const collapseCard = () => {
setIsExpandCard(false)
}
CSS and JS to Animate the Card
Define the CSS for initial render and hover.
const cardRoot = {
minWidth: 320,
maxHeight: 500,
marginLeft: '20px',
transition: 'transform 0.5s'
}
const cardRootExpand = {
position: 'absolute',
minWidth: 340,
maxHeight: 520,
marginLeft: '20px',
transform: 'scale(1.1)',
transition: 'transform 0.5s',
zIndex: 99
}
Using the onMouseEnter()
and onMouseLeave()
events to to change the CSS constants above. Using boolean state variable isExpandCard.
<Card style={isExpandCard ? cardRootExpand : cardRoot}
onMouseEnter={() => expandCard()}
onMouseLeave={() => closeCard()}>
App Root Component
Below is the source for app.js
. This component uses the <Grid/> component to render cards from hard coded JSON data constant with 16 records. The cards are optimized for a 16:9 monitor and wrapped with a MUI <Container/>.
import * as React from 'react';
import Box from '@mui/material/Box';
import Grid from '@mui/material/Grid';
import MUICard from './components/MUICard';
import Stack from '@mui/material/Stack';
import Container from '@mui/material/Container';
function App() {
return (
<>
<Box sx={{mt:5}}>
<Container maxWidth="xl" >
<Grid container spacing={2}>
{data.map((elem, index) => (
<Grid item xs={12} sm={12} md={4} lg={4} xl={3}>
<MUICard data={elem} key={index}/>
</Grid>
))}
</Grid>
</Container>
</Box>
</>
);
}
export default App;
const data = [
{id: 1, title: 'Magnificent Visions'},
{id: 2, title: 'The Last Snake'},
{id: 3, title: 'Blade of Year'},
{id: 4, title: 'The Ways Captive'},
{id: 5, title: 'Silk in the Abyss'},
{id: 6, title: 'Growing Star'},
{id: 7, title: 'The Last Snake'},
{id: 8, title: 'The Cracked Willow'},
{id: 9, title: 'Heart of Servant'},
{id: 10, title: 'The Scent of the Door'},
{id: 11, title: 'Flames in the End'},
{id: 12, title: 'Professional Serpents'},
{id: 13, title: 'Hunter of Storm'},
{id: 14, title: 'Storm Illusion'},
{id: 15, title: 'The Abyss'},
{id: 16, title: 'Sucking Name'},
{id: 17, title: 'Moon in the Truth'},
{id: 18, title: 'Silent Rings'},
{id: 19, title: 'Roses of Vision'},
{id: 20, title: 'The Crying Room'},
{id: 21, title: 'Magnificent Visions'},
{id: 22, title: 'The Last Snake'},
{id: 23, title: 'Blade of Year'},
{id: 24, title: 'The Ways Captive'},
{id: 25, title: 'Silk in the Abyss'},
{id: 26, title: 'Growing Star'},
{id: 27, title: 'The Last Snake'},
{id: 28, title: 'The Cracked Willow'},
]
You must be logged in to post a comment.