I'm trying to display a grid of Avatar images. While in a transition state I would like for a skeleton representation of the Image to appear. For that I'm using @material-ui/lab/Skeleton
.
The problem I'm having is that, because my images are set to autoscale within a grid using height: auto, width: 100%
, and the height/length of the content displayed under the image varies, there is no set height val that I can pass over to the skeleton ponent.
You can see the problem this causes if you scale down the width of the sandbox screen. The grid item's height increases causing the circle skeleton to begin morphing into an oval.
Is there a solution here that might give me behavior similar to the image's height: auto, width: 100%
?
The full code of what I have so far is below and in the sandbox here: .
import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import Avatar from "@material-ui/core/Avatar";
import Skeleton from "@material-ui/lab/Skeleton";
import Typography from "@material-ui/core/Typography";
import clsx from "clsx";
import Grid from "@material-ui/core/Grid";
const useStyles = makeStyles(theme => ({
root: {
textAlign: "center",
height: "100%",
width: "100%"
},
title: {
marginTop: theme.spacing(1)
},
avatarRoot: {
// width: '100%',
// height: 'auto',
// minHeight: '273px',
},
withTitle: {
margin: "auto"
},
img: {},
content: {
lineHeight: "1.4em"
},
link: {
color: "inherit",
textDecoration: "none"
},
icon: {},
fillContainer: {
height: `auto`,
width: `100%`,
fontSize: "4em"
},
container: {
marginTop: "250px"
},
fallback: {
height: "75%",
width: "auto"
},
loader: {},
avatarLoader: {
height: "75%",
width: "100%"
},
titleLoader: {
width: "60%",
margin: "auto",
marginTop: "8px",
height: "5%"
},
contentLoader: {
width: "40%",
margin: "auto",
marginTop: "8px",
height: "5%"
},
testImg: {
borderRadius: "100%",
height: "auto",
width: "100%"
},
isLoading: {
display: "none"
},
imgContainer: {
paddingTop: "100%",
borderRadius: "100%",
backgroundPosition: "center",
backgroundSize: "contain",
backgroundImage: "url()"
}
}));
export default function ImageAvatars() {
const classes = useStyles();
return (
<div className={classes.root}>
<Grid container spacing={1} className={classes.container}>
<Grid container item xs={3} spacing={0} direction="column">
<Avatar
src={""}
variant="circle"
className={clsx(classes.avatarRoot, {
[classes.isLoading]: false,
[classes.withTitle]: true,
[classes.fallback]: false,
[classes.fillContainer]: true
})}
/>
<Typography className={classes.title}>MUI Avatar</Typography>
<Typography className={classes.content}>test test test</Typography>
</Grid>
<Grid container item xs={3} spacing={0} direction="column">
<div className={classes.imgContainer} />
<div>
<Typography className={classes.title}>background image</Typography>
<Typography className={classes.content}>test test test</Typography>
</div>
</Grid>
<Grid container item xs={3} spacing={0} direction="column">
<img
className={classes.testImg}
src={""}
alt={"test"}
/>
<div>
<Typography className={classes.title}>image el</Typography>
<Typography className={classes.content}>test test test</Typography>
</div>
</Grid>
<Grid container item xs={3} spacing={0} direction="column">
<Skeleton
variant="circle"
className={clsx(classes.avatarLoader, classes.avatarRoot)}
/>
<Skeleton className={clsx(classes.titleLoader, classes.title)} />
<Skeleton className={clsx(classes.contentLoader, classes.content)} />
</Grid>
</Grid>
</div>
);
}
I'm trying to display a grid of Avatar images. While in a transition state I would like for a skeleton representation of the Image to appear. For that I'm using @material-ui/lab/Skeleton
.
The problem I'm having is that, because my images are set to autoscale within a grid using height: auto, width: 100%
, and the height/length of the content displayed under the image varies, there is no set height val that I can pass over to the skeleton ponent.
You can see the problem this causes if you scale down the width of the sandbox screen. The grid item's height increases causing the circle skeleton to begin morphing into an oval.
Is there a solution here that might give me behavior similar to the image's height: auto, width: 100%
?
The full code of what I have so far is below and in the sandbox here: https://codesandbox.io/s/skeleton-scaling-y00cd.
import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import Avatar from "@material-ui/core/Avatar";
import Skeleton from "@material-ui/lab/Skeleton";
import Typography from "@material-ui/core/Typography";
import clsx from "clsx";
import Grid from "@material-ui/core/Grid";
const useStyles = makeStyles(theme => ({
root: {
textAlign: "center",
height: "100%",
width: "100%"
},
title: {
marginTop: theme.spacing(1)
},
avatarRoot: {
// width: '100%',
// height: 'auto',
// minHeight: '273px',
},
withTitle: {
margin: "auto"
},
img: {},
content: {
lineHeight: "1.4em"
},
link: {
color: "inherit",
textDecoration: "none"
},
icon: {},
fillContainer: {
height: `auto`,
width: `100%`,
fontSize: "4em"
},
container: {
marginTop: "250px"
},
fallback: {
height: "75%",
width: "auto"
},
loader: {},
avatarLoader: {
height: "75%",
width: "100%"
},
titleLoader: {
width: "60%",
margin: "auto",
marginTop: "8px",
height: "5%"
},
contentLoader: {
width: "40%",
margin: "auto",
marginTop: "8px",
height: "5%"
},
testImg: {
borderRadius: "100%",
height: "auto",
width: "100%"
},
isLoading: {
display: "none"
},
imgContainer: {
paddingTop: "100%",
borderRadius: "100%",
backgroundPosition: "center",
backgroundSize: "contain",
backgroundImage: "url(https://via.placeholder./500)"
}
}));
export default function ImageAvatars() {
const classes = useStyles();
return (
<div className={classes.root}>
<Grid container spacing={1} className={classes.container}>
<Grid container item xs={3} spacing={0} direction="column">
<Avatar
src={"https://via.placeholder./500"}
variant="circle"
className={clsx(classes.avatarRoot, {
[classes.isLoading]: false,
[classes.withTitle]: true,
[classes.fallback]: false,
[classes.fillContainer]: true
})}
/>
<Typography className={classes.title}>MUI Avatar</Typography>
<Typography className={classes.content}>test test test</Typography>
</Grid>
<Grid container item xs={3} spacing={0} direction="column">
<div className={classes.imgContainer} />
<div>
<Typography className={classes.title}>background image</Typography>
<Typography className={classes.content}>test test test</Typography>
</div>
</Grid>
<Grid container item xs={3} spacing={0} direction="column">
<img
className={classes.testImg}
src={"https://via.placeholder./500"}
alt={"test"}
/>
<div>
<Typography className={classes.title}>image el</Typography>
<Typography className={classes.content}>test test test</Typography>
</div>
</Grid>
<Grid container item xs={3} spacing={0} direction="column">
<Skeleton
variant="circle"
className={clsx(classes.avatarLoader, classes.avatarRoot)}
/>
<Skeleton className={clsx(classes.titleLoader, classes.title)} />
<Skeleton className={clsx(classes.contentLoader, classes.content)} />
</Grid>
</Grid>
</div>
);
}
Share
Improve this question
edited Dec 23, 2019 at 22:52
Ryan Cogswell
81.1k9 gold badges241 silver badges212 bronze badges
asked Dec 23, 2019 at 21:45
colemarscolemars
1,0983 gold badges14 silver badges29 bronze badges
2 Answers
Reset to default 6The solution below is based on the article here: https://css-tricks./aspect-ratio-boxes/#article-header-id-3
The gist of the solution is to use padding-top expressed as a percentage to create a box with a specific aspect ratio (square in this case). Padding in percentages is based on width even when specifying padding-top or padding-bottom, so a padding-top of 100% produces a padding height equal to the width.
The relevant CSS/JSS is:
avatarSkeletonContainer: {
height: 0,
overflow: "hidden",
paddingTop: "100%",
position: "relative"
},
avatarLoader: {
position: "absolute",
top: 0,
left: 0,
width: "100%",
height: "100%"
},
This is then used as follows:
<Grid container item xs={3} spacing={0} direction="column">
<div className={classes.avatarSkeletonContainer}>
<Skeleton variant="circle" className={classes.avatarLoader} />
</div>
<Skeleton className={clsx(classes.titleLoader, classes.title)} />
<Skeleton className={clsx(classes.contentLoader, classes.content)} />
</Grid>
Here's the full code of my modification of your sandbox:
import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import Avatar from "@material-ui/core/Avatar";
import Skeleton from "@material-ui/lab/Skeleton";
import Typography from "@material-ui/core/Typography";
import clsx from "clsx";
import Grid from "@material-ui/core/Grid";
const useStyles = makeStyles(theme => ({
root: {
textAlign: "center",
height: "100%",
width: "100%"
},
title: {
marginTop: theme.spacing(1)
},
withTitle: {
margin: "auto"
},
content: {
lineHeight: "1.4em"
},
link: {
color: "inherit",
textDecoration: "none"
},
fillContainer: {
height: `auto`,
width: `100%`,
fontSize: "4em"
},
container: {
marginTop: "250px"
},
fallback: {
height: "75%",
width: "auto"
},
avatarSkeletonContainer: {
height: 0,
overflow: "hidden",
paddingTop: "100%",
position: "relative"
},
avatarLoader: {
position: "absolute",
top: 0,
left: 0,
width: "100%",
height: "100%"
},
titleLoader: {
width: "60%",
margin: "auto",
marginTop: "8px",
height: "5%"
},
contentLoader: {
width: "40%",
margin: "auto",
marginTop: "8px",
height: "5%"
},
testImg: {
borderRadius: "100%",
height: "auto",
width: "100%"
},
isLoading: {
display: "none"
},
imgContainer: {
paddingTop: "100%",
borderRadius: "100%",
backgroundPosition: "center",
backgroundSize: "contain",
backgroundImage: "url(https://via.placeholder./500)"
}
}));
export default function ImageAvatars() {
const classes = useStyles();
return (
<div className={classes.root}>
<Grid container spacing={1} className={classes.container}>
<Grid container item xs={3} spacing={0} direction="column">
<Avatar
src={"https://via.placeholder./500"}
variant="circle"
className={clsx(classes.avatarRoot, {
[classes.isLoading]: false,
[classes.withTitle]: true,
[classes.fallback]: false,
[classes.fillContainer]: true
})}
/>
<Typography className={classes.title}>MUI Avatar</Typography>
<Typography className={classes.content}>test test test</Typography>
</Grid>
<Grid container item xs={3} spacing={0} direction="column">
<div className={classes.imgContainer} />
<div>
<Typography className={classes.title}>background image</Typography>
<Typography className={classes.content}>test test test</Typography>
</div>
</Grid>
<Grid container item xs={3} spacing={0} direction="column">
<img
className={classes.testImg}
src={"https://via.placeholder./500"}
alt={"test"}
/>
<div>
<Typography className={classes.title}>image el</Typography>
<Typography className={classes.content}>test test test</Typography>
</div>
</Grid>
<Grid container item xs={3} spacing={0} direction="column">
<div className={classes.avatarSkeletonContainer}>
<Skeleton variant="circle" className={classes.avatarLoader} />
</div>
<Skeleton className={clsx(classes.titleLoader, classes.title)} />
<Skeleton className={clsx(classes.contentLoader, classes.content)} />
</Grid>
</Grid>
</div>
);
}
Hello in my case I was able to fix it by adding sx={{ transform: "unset"}}, just removing the transformation will fix it.
<Skeleton width={width} height={height} sx={{ transform: "unset" }} />