Material UI responsive based on element size - reactjs

I'm using React and MaterialUI to build a system that will display widgets inside another (not React-based) web site. I'd like to make the widgets responsive, but they need to respond to their own container width rather than the window width, as I won't know how much of the page the widget will take up.
Options I've considered:
Polling the container size on an interval basis
Polling the container size on window resize events
Setting up the theme breakpoints based on the container and window sizes at startup
These all seem rather ugly solutions to me. Is there an elegant way to do what I want?

Instead of breakpoints you can listen to changes to the component size. You can use react-use hook useMeasure to achieve that (it relies on ResizeObserver, which is supported by all major browsers), like in the following example:
/** #jsx jsx */
import { css, jsx } from '#emotion/core';
import { faAddressBook, faCoffee } from '#fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { useMeasure } from 'react-use';
const useMyComponentStyle = (params) => {
const { color } = params;
const [ref, { width }] = useMeasure();
const borderColor = width < 150 ? 'red' : width < 400 ? 'yellow' : 'green';
const icon = width < 250 ? faCoffee : faAddressBook;
const style = css`
color: ${color};
padding: 10px;
border: 1px solid ${borderColor};
`;
return {
ref,
style,
icon,
width,
};
};
export const MyComponent = (props) => {
const { ref, style, icon, width } = useMyComponentStyle(props);
return (
<div ref={ref} css={style}>
<FontAwesomeIcon icon={icon} />
{props.children} [[{parseInt('' + width)}px]]
</div>
);
};
const containerStyle = css`
padding: 100px 200px;
border: 1px solid blue;
`;
export const MyContainer = () => (
<div css={containerStyle}>
<MyComponent color='blue'></MyComponent>
</div>
);
ReactDOM.render(<MyContainer />, document.getElementById('root'));
The example above uses emotion for the css styles, but the style could be defined using another library, like jss or styled components, or even plain react inline style.
The component MyComponent is included inside the container component MyContainer that has left and right padding with value 200px, and you can see as you resize your browser view that the border color of MyComponent is based on the size of the component itself (red if the width of the component is less than 150px, yellow if it's less than 400px, otherwise it's green), not based on the size of the window.

To make various #material-ui/core elements take up only as much space as the container you place them in, I'd use the Grid component.
I've used the following:
<Grid container spacing={24}>
<Grid item>
Your content here
</Grid>
</Grid>
If you further want your grid item to be responsive to things like screen size, you may do:
const grid = {
xs: 24,
sm: 24,
md: 24,
lg: 12,
xl: 12,
};
and
<Grid item {...grid}>
Documentation: https://material-ui.com/layout/grid/

Related

How to style Input in material ui

I am new to material ui.
I use fifth version.
<InputBase
ref={params.InputProps.ref}
inputProps={params.inputProps}
autoFocus
sx={{
...InputCSS,
}}
/>
const InputCSS = {
width: '100%',
textIndent: '17px',
py: '12px',
fontSize: '20px',
borderRadius: '3px',
border: '1.5px solid rgba(0, 0, 0, 0.4)',
'MuiInputBase-root': { // does not work
borderColor: 'red !important',
color: 'red !important',
},
'&:focus' : { // does not work
...
}
}
I could have used styled('input') and it works, I can set &:focus and it works but I can't type anything in the input.
I want to get rid of the initial border and set focus property.
How can I change the border for this class?
I know that in v5 we can style our components using variants or sx or styled.
What is the best advice for styling mui components? Because almost all the info in the internet uses outdated useStyle or makeStyles which doesn't work with react 18v.
sometimes I just struggle with components styling in mui
You have several ways to customize a Mui component, but my three favorite approaches are:
Styled utility
Sx prop
Custom global theme
So when should I use each of these approaches?
Styled utility
If you want to make a component reusable across your app, go with styled, if you had ever used styled components then styled will be very familiar to you, here is an example of how to use it:
import * as React from 'react';
import Slider, { SliderProps } from '#mui/material/Slider';
import { alpha, styled } from '#mui/material/styles';
// if you are using typescript, don't forget to pass the component props on styled
const SuccessSlider = styled(Slider)<SliderProps>(({ theme }) => ({
width: 300,
color: theme.palette.success.main,
// to override the styles of inner elements,
// you have to use the & selector followed by the class name.
'&.MuiSlider-thumb': {
'&:hover, &.Mui-focusVisible': {
boxShadow: `0px 0px 0px 8px ${alpha(theme.palette.success.main, 0.16)}`,
},
'&.Mui-active': {
boxShadow: `0px 0px 0px 14px ${alpha(theme.palette.success.main, 0.16)}`,
},
},
}));
export default function StyledCustomization() {
return <SuccessSlider defaultValue={30} />;
}
To make the component even more dynamically, you can define custom props as well, like width, color, border and so on, read the styled docs to know more about it.
Sx prop
If you want to make an one time off style in a single component, the easiest way is to use the sx prop, but be careful with this approach because it can lead to a lot of repetition in your code. Following the code you gave:
<InputBase
ref={params.InputProps.ref}
inputProps={params.inputProps}
autoFocus
sx={{
...InputCSS,
}}
/>
const InputCSS = {
width: '100%',
textIndent: '17px',
py: '12px',
fontSize: '20px',
borderRadius: '3px',
border: '1.5px solid rgba(0, 0, 0, 0.4)',
// you should pass the & selector followed by the class that you want to override
'&.MuiInputBase-root': {
// avoid the use of !important
borderColor: 'red',
color: 'red',
},
'&.MuiInputBase-root:focus': {
...
}
}
check it out on codeSandbox. Just a suggestion, depending on your case the component TextField could fit better as it comes with some good styles and you don't need to style it from scratch.
Side note:
Every mui component has an api doc with a css section where you can find all available classes to override, for instance, see the InputBase api docs, also read the docs about sx prop.
Custom theme
At last but not least, if you want to customize your whole app with custom palette, components, typography, breakpoints and so on, no doubt you should use a custom theme, as mui provides a powerful tool called createTheme to do so, what I like the most in custom theme is the possibility to make custom variants of the component, like so:
import * as React from "react";
import { createTheme, ThemeProvider } from "#mui/material/styles";
import Button from "#mui/material/Button";
import { red } from "#mui/material/colors";
// if you are using typescript, you must declare the module with the
// custom properties in order to get access of this property when using the component
declare module "#mui/material/Button" {
// define custom variants
interface ButtonPropsVariantOverrides {
dashed: true;
redVariant: true;
}
}
const defaultTheme = createTheme();
const theme = createTheme({
components: {
MuiButton: {
variants: [
{
// use the variant name defined earlier
props: { variant: "dashed" },
// set the styles for this variant
style: {
textTransform: "none",
border: `2px dashed ${defaultTheme.palette.primary.main}`,
color: defaultTheme.palette.primary.main
}
},
{
props: { variant: "redVariant" },
style: {
border: `2px solid ${red[300]}`,
color: red[600]
}
}
]
}
}
});
export default function GlobalThemeVariants() {
return (
// use the theme provider to get access of custom theme
// and variants within any child component
<ThemeProvider theme={theme}>
<Button variant="dashed" sx={{ m: 1 }}>
Dashed
</Button>
<Button variant="redVariant" color="secondary">
custom red variant
</Button>
</ThemeProvider>
);
}
See the example, also take a look at the custom theme docs. Hope I clarified the things a bit!

MUI: cannot apply background color to Paper component

I'm trying to style the MUI <Paper> component, however setting the background color isn't working. Following the online tutorials I'm applying the background-color CSS property to the root element of the Paper but the color remains white while other CSS properties - in this case padding and text-align work. Can someone please tell me what I am missing?
import React from 'react';
import { Paper, Typography } from '#mui/material';
import { makeStyles } from '#mui/styles';
import AccessAlarmIcon from '#mui/icons-material/AccessAlarm';
const useStyles = makeStyles({
root: {
textAlign: 'center',
padding: 15,
backgroundColor: '#37474f',
}
});
export default function Header() {
const classes = useStyles();
return(
<Paper outlined square className={classes.root}
>
<Typography variant="h2" component="h1">
Pomodoro Cl
<AccessAlarmIcon sx={{ fontSize: 45, color: 'red' }} />
ck
</Typography>
</Paper>
)
}
First of all your attribute outlined is incorrect, it should bevariant="outlined" as per the documentation.
For the background-color not working, you will need to add !important to the property so it takes precedence over mui's default styling.
Here's a working codesandbox for you: https://codesandbox.io/s/strange-smoke-y6yhv?file=/src/App.tsx

jss-rtl is not flipping styles, i.e. not flipping rule on the x-axis

I have a test application which is using Material UI.
I have RTL enabled on it
I have a margin left for my button on the application and when I use jss-rtl, I expect the margin to become margin-right which is not happening. It still remains as margin-left
The direction is passed to theme.js
const customizeTheme = (direction = "ltr") => {
const theme = createMuiTheme({
...defaultThemeOptions,
direction
});
theme.js also has styles for button that should have flipped when direction is RTL
contained: {
color: "red",
marginLeft: theme.spacing(4)
}
But it does not.
The code is here
https://codesandbox.io/s/eloquent-night-5uo4c?file=/src/index.js
I know its bit late to answer this. But posting it, for those who lands here for answer
import { Theme } from '#material-ui/core';
import { createStyles, makeStyles } from '#material-ui/styles';
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
margin: theme.spacing(4),
border: "1px solid red"
}
})
);
const classes = useStyles();
.
.
.
<div className={classes.root}>
{/* Content */}
...
{/* End content */}
</div>

Responsive Typography in MUI?

Designs commonly have smaller headline fonts for mobile designs.
Does MUI have a mechanism for making the typography responsive?
I see that the default theme has the font sizes defined in rems - does that mean it's a matter of just reducing the base font-size? (That doesn't seem to work, what if you want to reduce the headline fonts at different rates).
Update
MUI v4 has responsive typography built in! Check here for details.
Old response
#Luke's answer is great. I wanted to add some detail to make this work in practice, because both breakpoints and pxToRem are accessible on the theme object... making this becomes a chicken and egg problem! My approach:
import { createMuiTheme } from "#material-ui/core"
const defaultTheme = createMuiTheme({ ... customisations that don’t rely on theme properties... })
const { breakpoints, typography: { pxToRem } } = defaultTheme
const theme = {
...defaultTheme,
overrides: {
MuiTypography: {
h1: {
fontSize: "5rem",
[breakpoints.down("xs")]: {
fontSize: "3rem"
}
}
}
}
}
export default theme
None of the other answers uses the variant preset.
The best way to MUI v5, and use variant:
<Typography sx={{ typography: { sm: 'body1', xs: 'body2' } }} >
Hello world!
</Typography>
Update
The latest version of Material UI (v4) fully supports response typography. See the official documentation for details.
Original Answer
As of version 1.x, Material UI does not have a specific mechanism for handling responsive typography.
You can scale the size of all MUI Typography by changing the font-size of the <html> element, as you mentioned. (docs)
const styles = theme => ({
"#global": {
html: {
[theme.breakpoints.up("sm")]: {
fontSize: 18
}
}
}
}
Theme overrides
As far as i know, the only other option is to use theme overrides to define custom styles for each of the Typography variants.
This requires replicating some of the logic in createTypography.js (ie setting line heights to maintain vertical rhythm)
const theme = createMuiTheme({
overrides: {
MuiTypography: {
headline: {
fontSize: pxToRem(24),
[breakpoints.up("md")]: {
fontSize: pxToRem(32)
}
}
}
}
As described in the docs you can use the responsiveFontSizes() helper to make Typography font sizes in the theme responsive.
import { createMuiTheme, responsiveFontSizes } from '#material-ui/core/styles';
let theme = createMuiTheme();
theme = responsiveFontSizes(theme);
MUI v5 adds sx prop to all MUI components which supports responsive values where you can pass an object to define the values at different breakpoints:
<Typography
sx={{
fontSize: {
lg: 30,
md: 20,
sm: 15,
xs: 10
}
}}
>
This text is resized on different screen breakpoints
</Typography>
Similar to Box, Typography also supports system properties so you can skip the sx prop and pass the fontSize property directly. The code below is the same as above, just a bit shorter to write:
<Typography
fontSize={{
lg: 30,
md: 20,
sm: 15,
xs: 10
}}
>
This text is resized on different screen breakpoints
</Typography>
My approach with v5
import { createTheme } from "#mui/material";
let theme = createTheme({
// ...
});
theme = createTheme(theme, {
typography: {
h1: {
fontSize: 53,
[theme.breakpoints.only("sm")]: {
fontSize: 71,
},
},
},
});
export default theme;
This seem to be working for me v5
in App.js
...
import {
createTheme,
responsiveFontSizes,
ThemeProvider,
} from '#mui/material/styles';
let theme = createTheme();
theme = responsiveFontSizes(theme);
...
function App() {
return (
<ThemeProvider theme={theme}>
<Router>
.....
</Router>
</ThemeProvider>
);
}
"To automate this setup, you can use the responsiveFontSizes() helper to make Typography font sizes in the theme responsive."
https://mui.com/customization/typography/#responsive-font-sizes

Material UI - font size in button label

I'm trying to figure out how to increase the font size of a label in my Material UI button in react.
I have a button setup:
import React from 'react';
import MyButton from '../materialui/Button.js';
const style = {
background: '#FF5349',
color: 'white',
padding: '0 30px',
textTransform: "uppercase",
};
const Start = () => (
<span>
<MyButton style={style} size="large">GET STARTED</MyButton>
</span>
);
export default Start;
I can't find a way to add font-size to the styles property.
Other stack overflow posts suggest doing it as an inline style using the style property, but that overrides my const definition.
If you don't want to add fontSize: '42px' to your styles object (probably because you want to reuse this somewhere else?) you can just build a new one with the style added for your button:
const Start = () => (
<span>
<MyButton style={{...style, fontSize: '42px'}} size="large">GET STARTED</MyButton>
</span>
);
I don't know if myButton in Material is the same of RaisedButton Anyway,
Button in Material-ui it's coming like Div > Button > Div > Div > span
So if you want to styling the button first of all you need to create a CSS class like
in your css file
This for styling button
.StylingButtonExample button{
background-color: red;
}
This for styling the text inside the button
.StylingButtonExample button div div span{
color: blue;
}
in react file
import RaisedButton from 'material-ui/RaisedButton';
<RaisedButton label="Default" className='StylingButtonExample' />
Hope this will help you and the others
Assuming the <MyButton /> Component is, in fact, the <RaisedButton /> Component that material-ui offers, you can simply apply your label styling to the labelStyle property.
Therefore, if you wanted to change the font-size to 42px, you could simply write it as follows:
<MyButton labelStyle={{ fontSize: '42px' }}>GET STARTED</MyButton>

Resources