Purpose of flexGrow in the parent div of a Material UI grid? - css

I'm trying to understand this code example, https://codesandbox.io/s/9rvlm, from the examples in the Material UI docs (https://material-ui.com/components/grid/):
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import Paper from '#material-ui/core/Paper';
import Grid from '#material-ui/core/Grid';
const styles = theme => ({
root: {
flexGrow: 1,
},
paper: {
padding: theme.spacing(2),
textAlign: 'center',
color: theme.palette.text.secondary,
},
});
function CenteredGrid(props) {
const { classes } = props;
return (
<div className={classes.root}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Paper className={classes.paper}>xs=12</Paper>
</Grid>
<Grid item xs={6}>
<Paper className={classes.paper}>xs=6</Paper>
</Grid>
<Grid item xs={6}>
<Paper className={classes.paper}>xs=6</Paper>
</Grid>
<Grid item xs={3}>
<Paper className={classes.paper}>xs=3</Paper>
</Grid>
<Grid item xs={3}>
<Paper className={classes.paper}>xs=3</Paper>
</Grid>
<Grid item xs={3}>
<Paper className={classes.paper}>xs=3</Paper>
</Grid>
<Grid item xs={3}>
<Paper className={classes.paper}>xs=3</Paper>
</Grid>
</Grid>
</div>
);
}
CenteredGrid.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(CenteredGrid);
My question is: what is the purpose of assigning flexGrow: 1 to the parent div element?
As I understand from https://material-ui.com/system/flexbox/#flex-grow and https://developer.mozilla.org/en-US/docs/Web/CSS/flex-grow, flex-grow is a CSS property of items of flex containers. In this example though, I don't see there being a flex container element containing this component; the CenteredGrid is displayed (as <Demo/>) directly in the root div.
Are the styles.root applied to the parent div 'just in case' the component is rendered in a flex container? I'd appreciate some clarification.

The comment by Anthony mentioned above seems the right answer to me. If the parent is set to display:flex and child component being loaded does not have flex:1 or flex-grow:1 then it will not take up 100% width of the parent.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<div style="border:1px solid black; padding:2px; ">
<div style="border:1px solid red; ">parent is not flex container - child div takes 100% width</div>
</div>
<br/>
<div style="border:1px solid black; padding:2px; ">
<div style="border:1px solid red; flex-grow:1; ">parent is not flex container - child div takes 100% width</div>
</div>
<br/>
<div style="border:1px solid black; padding:2px; display:flex; ">
<div style="border:1px solid red; ">parent is flex container - sorry</div>
</div>
<br/>
<div style="border:1px solid black; padding:2px; display:flex; ">
<div style="border:1px solid red; display:flex; flex-grow:1;">parent is flex container - child div takes 100%</div>
</div>
</body>
</html>

The comment of AnthonyZ said:
The only time it would make a difference is when there is a flex
parent.
But in a React App the outer Div in the Body is not the outer-most parent.
In HTML/CSS that is the HTML and the BODY.
In a create-react-app they get the default "display:block" in either index.css or base stylesheet (of the browser).
I solved it by doing the following:
Move index.css to public
Seriously, why is index.css in /src and index.html in /public ??
Modify index.css
Specify "display:flex" for both HTML and BODY in index.css
html {
display: flex;
min-height: 100%;
}
body {
margin: 0;
min-height: 100%;
display:flex;
flex-grow: 1;
}
Modify App.js
In App.js create an outer div
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
},
}));
// ....
function App() {
const classes = useStyles();
return (
<div className={classes.root}>
<Provider store={store}>
<BrowserRouter>
<TopMenu/>
<AllRoutes/>
</BrowserRouter>
</Provider>
</div>
);
}
TopMenu.js
const useStyles = makeStyles((theme) => ({
offset: theme.mixins.toolbar,
title: {
flexGrow: 1,
},
}));
export default function TopMenu() {
const classes = useStyles();
return (
<React.Fragment>
<AppBar position="fixed">
<Toolbar variant='dense'>
<Typography variant="h6" className={classes.title}>
Application Title
</Typography>
<MainMenu/>
</Toolbar>
</AppBar>
<div className={classes.offset}/>
</React.Fragment>
);
}
If you inspect the elements, you'll see that the whole height is taken started from the HTML-tag.
Problem with this solution: That annoying scrollbar which takes lets you scroll the height of the AppBar.
If somebody has a solution for that, I'll edit my answer (or delete the answer when there's a far better solution :-) )

Related

Material UI Grid Items are overlapping

I am using Material UI for styling my react components but if I put some other elements on grid columns then they start overlapped with each other.
import { Grid } from "#material-ui/core";
import ReactPlayer from "react-player/file";
const MyReactlayer = () => {
return (
<div>
<h2>"test"</h2>
<Grid container wrap={"nowrap"}>
<Grid item md={9}>
<Grid container wrap={"nowrap"} direction={"column"}>
<Grid item md={12}>
<div className="VPlayer">
<ReactPlayer
height={"100%"}
width={"100%"}
url={"../../video/sample.mp4"}
muted={true}
playing={true}
/>
</div>
</Grid>
<Grid item md={12}>
<div className="BControls">
</div>
</Grid>
</Grid>
</Grid>
<Grid item md={3} className="RControls">
</Grid>
</Grid>
</div>
);
};
export default MyReactlayer;
My css file contain style only for two elements:
.VPlayer{
height: 100%;
width: 100%;
}
.BControls{
height: 50px;
background-color: #3fb8f7;
width: 100%;
}
I attached the output screenshot also

Material UI and React &hover selector not working

I have this code in which the hover selector doesn't work at all. Everything else when it comes to style works perfectly but the hover doesn't do anything visible.
import React from "react";
import Paper from '#material-ui/core/Paper';
import Grid from '#material-ui/core/Grid';
const styles = {
divStyle: {
width: "300px",
height: "200px",
backgroundColor: "red",
margin: "30px",
'&:hover': {
border: '5px solid #000000',
bordeBottomColor: 'transparent',
borderRightColor: 'transparent'
}
}
};
const StartPage = ()=> {
return(
<React.Fragment>
<Paper>
<div style={styles.firstContainer}>
</div>
<div style={styles.secondContainer}>
<Grid container >
<Grid style={styles.Grid} item>
<div style={styles.gridDivStyle}>
<div style={styles.divStyle}></div>
<div style={styles.divStyle}></div>
</div>
<div style={styles.gridDivStyle}>
<div style={styles.divStyle}></div>
<div style={styles.divStyle}></div>
</div>
</Grid>
</Grid>
</div>
<div style={styles.lastContainer}>
</div>
</Paper>
</React.Fragment>
);
}
export default StartPage;
How can I make the hover selector work. Do I need to use the state from React in order to make the change?
if you want to use hover style , you can use the package
import { withStyles } from 'material-ui/styles';
Here is the code:
import React from "react";
import Paper from "#material-ui/core/Paper";
import Grid from "#material-ui/core/Grid";
import { withStyles } from "#material-ui/styles";
const styles = {
divStyle: {
width: "300px",
height: "200px",
backgroundColor: "red",
margin: "30px",
"&:hover": {
border: "5px solid #000000",
bordeBottomColor: "transparent",
borderRightColor: "transparent"
}
}
};
const StartPage = props => {
return (
<React.Fragment>
<Paper>
<div style={styles.firstContainer} />
<div style={styles.secondContainer}>
<Grid container>
<Grid style={styles.Grid} item>
<div style={styles.gridDivStyle}>
<div className={props.classes.divStyle} /> // use the styles through className
<div className={props.classes.divStyle} />
</div>
<div style={styles.gridDivStyle}>
<div className={props.classes.divStyle} />
<div className={props.classes.divStyle} />
</div>
</Grid>
</Grid>
</div>
<div style={styles.lastContainer} />
</Paper>
</React.Fragment>
);
};
export default withStyles(styles)(StartPage);
Working Demo

MaterialUI Box takes over background image

I have a react component that contains a forms and it is formatted as follows:
<Box
display="flex"
justifyContent="center"
alignItems="center"
style={{ minHeight: "100vh", backgroundColor: "gray", opacity: "0.8" }}
> ...
</Box>
This componenent, called Form is then passed in App.js as follows:
import React from "react";
import Form from "./components/Form";
const sectionStyle = {
height: "100vh",
backgroundImage:
"url('www.blablabla.com/img.jpg') ",
backgroundRepeat: "no-repeat",
backgroundSize: "cover"
};
const App = () => {
return (
<div style={sectionStyle}>
<Form />
</div>
);
};
export default App;
However, the results I get is this one:
I added the opacity to better show that my Box component is 'stretched' all over the window, while I would like it to just wrap its content, so that if some opacity is applied, it will only appear I side the Box, and the background image will be normal.
How can I achieve this?
material-ui Box's default component is a div. see material-ui Box
A div's default behavior is to stretch horizontally across the available space. That is why your Box is stretching horizontally. Where is the default size of a div element defined or calculated?
The minHeight: "100vh" style you are applying to the Box is telling it to stretch across the available vertical space. That is why your Box is stretching vertically.
see Fun with Viewport Units
Perhaps using the Grid component as a wrapper will give you what you are looking for
<Grid container className={props.classes.sectionStyle}
direction="column"
justify="space-evenly"
alignItems="center"
>
<Grid item>
<Form />
</Grid>
</Grid>
This would change your code to be:
import React from "react";
import {Grid} from '#material-ui/core';
import Form from "./components/Form";
const sectionStyle = {
height: "100vh",
backgroundImage:
"url('www.blablabla.com/img.jpg') ",
backgroundRepeat: "no-repeat",
backgroundSize: "cover"
};
const App = () => {
return (
<Grid style={sectionStyle}
container
direction="column"
justify="space-evenly"
alignItems="center"
>
<Grid item>
<Form />
</Grid>
</Grid>
);
};
export default App;
I would suggest you to use pseudo elem like:after and :before
Here is an example.
.background-filter::after {
content: "";
display: block;
position: absolute;
width: 100%;
height: 100%;
opacity: .8;
background: red;
}
.background-filter {
position: relative;
}
.background {
background-image: url('https://upload.wikimedia.org/wikipedia/en/6/62/Kermit_the_Frog.jpg');
width: 200px;
height: 200px;
}
.background span{
position: absolute;
z-index: 1;
}
<div class="background background-filter"><span>Hello World I am text with background blur</span></div>
Do what ever you want to apply to ::after wont effect the form above

Aligning logo to center with Material-UI

Trying to center a logo in my log in form with Material-UI, everything else appears to be centered exception of the logo which is hugging the left side of the card.
I've tried adding align="center" and justify="center" under the img tag but it still is not moving the image in the center of the card. I've also tried adding a "gridItem" alignItems:'center' under my const styles
const styles = theme => ({
logo: {
padding: "1rem",
width: 300,
[theme.breakpoints.down("sm")]: {
width: 200,
}
},
appName: {
color: '#2979ff',
[theme.breakpoints.down("sm")]: {
fontSize: theme.typography.h5.fontSize
}
},
});
<Grid container justify="center" alignItems="center">
<Grid item>
<Card>
<Grid item>
<img className={classes.logo} alt="Portal Logo" src={Logo} align="center" />
</Grid>
I am expecting the logo to be centered in my my "login" form that I've created.
You are using flexbox so better to use justifyContent: center; on parent element like .
<Grid container justify="center" alignItems="center">
<Grid item>
<Card>
<Grid item justifyContent="center">
<img className={classes.logo} alt="Portal Logo" src={Logo} align="center" />
</Grid>
Try text-align:center for the parent element of logo
HTML
<div class="logo">
<img src="logo.png">
</div>
CSS
.logo{
text-align:center;
}

AppBar overlaps with other elements

I am starting to use React/Material-UI, and also new to CSS etc...
I have a simple page layout with an APPBar. Unfortunately this AppBar overlaps the elements which are meant to go below it.
I have found this answer:
AppBar Material UI questions
But this feels completely wrong. What if my AppBar has a variable height, depending on the icons, display modes etc...?
I have tried to create a vertical grid, to wrap the elements in different items, made the top container a flex one and play with flex settings, nothing seems to work, the app bar always sits on top of the text.
The code is very simple:
import React from 'react';
import { AppBar, Typography, Box } from '#material-ui/core';
function App() {
return (
<div>
<AppBar>
<Typography variant='h3'>
AppBar
</Typography>
</AppBar>
<Box>
<Typography variant='h1' style={{ border: '1px solid black' }}>
Hello
</Typography>
</Box>
</div>
)
}
export default App;
The "Hello" text chunk is only half visible:
This is happening because the MaterialUI App Bar defaults to position="fixed". This separates it from the standard DOM's layout to allow content to scroll beneath it, but as a result no space is made for it on the page.
You can get around this by wrapping all content below it in a div and specifying enough margin, or by changing the position property of <AppBar> so it's no longer "fixed". In your example, you could also just apply the styles to <Box> if that's the only content below the <AppBar>.
e.g.
import React from 'react';
import { AppBar, Typography, Box } from '#material-ui/core';
function App() {
return (
<div>
<AppBar>
<Typography variant='h3'>
AppBar
</Typography>
</AppBar>
<div style={{marginTop: 80}}>
<Box>
<Typography variant='h1' style={{ border: '1px solid black' }}>
Hello
</Typography>
</Box>
</div>
</div>
)
}
export default App;
MaterialUI provides a theme mixin for the AppBar that can help. Not sure if you're using the recomended JSS setup, but you can do something like this:
import withStyles from '#material-ui/core/styles/withStyles';
const styles = theme => ({
appBarSpacer: theme.mixins.toolbar
});
const style = withStyles(styles)
function MyScreen ({ classes }) {
<AppBar></AppBar>
<div className={classes.appBarSpacer}></div>
<Box></Box>
}
export default style(MyScreen)
The mixin will give that div the same height as your AppBar, pushing down the other content.
According to Material-ui, there are 3 solutions to this problem.
https://material-ui.com/components/app-bar/#fixed-placement
You can use position="sticky" instead of fixed. ⚠️ sticky is not supported by IE 11.
You can render a second component
You can use theme.mixins.toolbar CSS
I personally enjoy using the 2nd solution like this.
return (
<>
<AppBar position="fixed">
<Toolbar>{/* content */}</Toolbar>
</AppBar>
<Toolbar />
</>
);
<AppBar position='static'>
use this it will do it and content won't hide under Appear
I think having a good app setup is opinianted, but I would recommend the following
import React from "react";
import ReactDOM from "react-dom";
import {
AppBar,
Typography,
Box,
CssBaseline,
makeStyles,
Container,
Grid,
Toolbar
} from "#material-ui/core";
const useStyles = makeStyles(theme => ({
content: {
flexGrow: 1,
height: "100vh",
overflow: "auto"
},
appBarSpacer: theme.mixins.toolbar,
title: {
flexGrow: 1
},
container: {
paddingTop: theme.spacing(4),
paddingBottom: theme.spacing(4)
}
}));
function App() {
const classes = useStyles();
return (
<div className={classes.root}>
<CssBaseline />
<AppBar position="absolute">
<Toolbar className={classes.toolbar}>
<Typography
component="h1"
variant="h6"
color="inherit"
noWrap
className={classes.title}
>
AppBar
</Typography>
</Toolbar>
</AppBar>
<main className={classes.content}>
<div className={classes.appBarSpacer} />
<Container maxWidth="lg" className={classes.container}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Box>
<Typography variant="h1" style={{ border: "1px solid black" }}>
Hello
</Typography>
</Box>
</Grid>
</Grid>
</Container>
</main>
</div>
);
}
try this!
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
[theme.breakpoints.down('sm')]: {
marginBottom: 56,
},
[theme.breakpoints.up('sm')]: {
marginBottom: 64,
},
},
menuButton: {
marginRight: theme.spacing(1),
},
title: {
flexGrow: 1,
}, }))
You can add the above to your code like this
const Navbar = () => {
const classes = useStyles()
return (
<div className={classes.root}>
<AppBar position='fixed' color='primary'>
<Toolbar>
<IconButton
edge='start'
className={classes.menuButton}
color='inherit'
aria-label='menu'>
<MenuIcon />
</IconButton>
<Typography variant='h6' className={classes.title}>
News
</Typography>
<Button color='inherit'>Login</Button>
</Toolbar>
</AppBar>
</div>
)}
For more documentation visit material-ui breakpoint customization

Resources