Apply MUI styles to non-MUI markup elements - reactjs

In my Gatsby app I am using MUI v5, and must also output user created markup. I want the user-markup to get the same base-styles as their analogous Typography elements (see below). How can I achieve this?
Note - the user markup already contains <p> <h1> or other tags, which cannot be directly modified by React.
Additionally the app is wrapped with ThemeProvider and I'm using styled-components as the style engine for MUI (if that matters...).
import {Typography} from "#mui/material"
export default function() {
return (
<>
<Typography variant="body1" paragraph>My styled Typography</Typography>
// The next line can't receive any classses or modifiers,
// but must end up styled like the <Typography> element.
<p>my custom user content - how can i style this like above?</p>
</>
)
}

You need to import your theme. From there you can access the body1 default typography style to apply to the p element.
import {Typography} from '#mui/material'
import {useTheme} from //im not exactly sure where this comes from '#mui/material' or '#mui/styles'
export default function() {
const theme = useTheme()
return (
<>
<Typography variant="body1" paragraph>My styled Typography</Typography>
<p style={theme.typography.body1}>my custom user content - how can i style this like above?</p>
</>
)
}

import {useTheme ,Typography} from "#mui/material"
export default function() {
const theme = useTheme()
return (
<>
<Typography variant="body1" paragraph>My styled Typography</Typography>
<p style={theme.typography.body1}>my custom user content - how can i style this like above?</p>
</>
)
}

If you want to add the sx prop in your custom component:
const P = styled("p")({});
const theme = useTheme()
<P sx={{ ...theme.typography.body1 }}>
If you want to use system properties:
import { unstable_extendSxProp as extendSxProp } from "#mui/system";
const Psx = styled("p")();
function P(inProps) {
const { sx, ...other } = extendSxProp(inProps);
return <Psx sx={sx} {...other} />;
}
<P {...theme.typography.body1}>
If you want to use variant prop like in Typography:
const T = styled('p')(({ theme, variant = 'body1' }) => ({
...theme.typography[variant],
}));
<T variant="h3">
Live Demo
Related Answer
Passing props to MUI styles

Related

Tailwind, Twin-macro, Styled Components className override priority

I'm creating a custom Section component using styled-components, twin-macro, tailwind css. The default styling is stored in StyledSection constant.
import tw, { styled } from "twin.macro";
const Section = (props) => {
return (
<StyledSection
className={props.className}
style={{ backgroundImage: `url(${props.backgroundImage})` }}
>
{props.children}
</StyledSection>
);
};
const StyledSection = styled.section(tw`py-20 bg-no-repeat bg-cover bg-center`);
export default Section;
However, I want to override the styling if I pass something on the component, for it to more reusable.
Example of passing override class in the Section component:
<Section className="my-override-class" />
However, the styled-components classes is being priotized. Please refer to the picture below for the render output.
Image reference for the render output

Can't override AppBar background color

could someone please help me with this issue.
this is the code:
import { AppBar } from "#mui/material";
import { makeStyles } from "#mui/styles";
const useStyles = makeStyles({
root: {
backgroundColor: 'red'
}
})
const App = () => {
const classes = useStyles();
return <>
<AppBar className={classes.root}>
test
</AppBar>
<h1 className={classes.root}>test</h1>
</>
}
export default App;
The question is:
why the background-color of the AppBar component is not changing;
No problem with the h1 tag thought;
The other problem I've found is, sometimes it changes, but when I refresh the page, it becomes blue -> color by default;
So, thanks a lot for the answer if there is any :);
I faced a similar issue where styles applied to the AppBar component did not override some of the default styles. The fix was to wrap my component tree with <StyledEngineProvider injectFirst>. The solution looks something like this in the end:
import React from 'react';
import { StyledEngineProvider } from '#mui/material/styles';
export default function GlobalCssPriority() {
return (
<StyledEngineProvider injectFirst>
{/* Your component tree. Now you can override MUI's styles. */}
</StyledEngineProvider>
);
}
Link to source
<AppBar className={classes.root}>
test
</AppBar>
instead wrap your text in a div and use className on that div
<AppBar >
<div className={classes.root}>
test
</div>
</AppBar>

Use style functions for material-ui components besides Box

I'm just starting with Material UI. Thanks for bearing with me.
I know you can use things like <Box mx={2}> out-of-the-box (ha). So if I wanted to put a margin around, say, a TextField, I could wrap it in a box.
Is there a simple way to set up my app's theme so that any component can use those style function props? (m, p, display, etc)
So that I could to <TextField mx={2}/> without having to wrap it in a Box.
The docs imply that you can do this:
(the example uses ThemeProvider from styled-components but I'm assuming that MUI's ThemeProvider works the same way???)
import React from 'react'
import { ThemeProvider } from 'styled-components'
const theme = {
spacing: 4,
palette: {
primary: '#007bff',
},
};
export default function App() {
return (
<ThemeProvider theme={theme}>
{/* children */}
</ThemeProvider>
)
}
I've tried this but it crashes from the TextField's my prop:
import { createMuiTheme, TextField, ThemeProvider } from '#material-ui/core';
// Greatly simplified version of my component
const App = () => <TextField my={2}/>
let theme = createMuiTheme({})
export default () =>
<ThemeProvider theme={ theme }>
<App/>
</ThemeProvider>;
I can do something like this and it works:
function App() {
const Input = styled(TextField)(compose(spacing))
return <Input my={3}/>
}
But then I'd have to compose my components every time I want to do use the style functions.
The docs are showing how the theme can parameterize the Box features (e.g. such that a spacing unit is 4px instead of 8px) -- the theme doesn't do anything to enable those features.
Material-UI is intending to support #material-ui/system features on core components in v5, but that is still months away.
Your main options are doing something like you showed in your example (though you should move const Input = styled(TextField)(compose(spacing)) to the top-level rather than doing this within render of App). You could put this in a separate file and import this component instead of TextField whenever you want to use those features. For instance:
MyTextField.js
import TextField from "#material-ui/core/TextField";
import { styled } from "#material-ui/core/styles";
import { compose, spacing } from "#material-ui/system";
export default styled(TextField)(compose(spacing));
App.js
import React from "react";
import TextField from "./MyTextField";
export default function App() {
return (
<div className="App">
<TextField variant="outlined" label="Material-UI system demo" />
</div>
);
}
Another option is to use Box with the clone prop and wrap the component you want to style. For instance:
import React from "react";
import TextField from "#material-ui/core/TextField";
import Box from "#material-ui/core/Box";
export default function App() {
return (
<div className="App">
<Box my={3} clone>
<TextField variant="outlined" label="Box demo" />
</Box>
</div>
);
}
You can also use the component prop of Box:
import React from "react";
import TextField from "#material-ui/core/TextField";
import Box from "#material-ui/core/Box";
export default function App() {
return (
<div className="App">
<Box my={3} component={TextField} variant="outlined" label="Box demo" />
</div>
);
}
Related answers:
Material-UI Grid does not hide whe use Display
Dynamic icon size in Material-UI
Box vs className vs style for vertical spacing in Material UI
Reusable component using theme-based Box features
Using Material-UI Box Component with the Drawer Compoment
How to use override Button using Box component in Material-UI?

Material UI nested theme providers breaks withStyles HOC

I have a React application created with Create React App and I use the #material-ui/core npm package for theming.
To customize components I use the withStyles higher-order component provided by MaterialUI.
According to documentation it supports nested ThemeProviders https://material-ui.com/customization/theming/#nesting-the-theme.
But inside the child ThemeProvider withStyles won't apply classes.
Here is a basic application demonstrating the issue -> https://codesandbox.io/s/vibrant-tree-eh83d
ExampleComponent.tsx
import React, { FunctionComponent } from "react";
import {
WithStyles,
withStyles,
createStyles,
StepButton,
Step,
Stepper,
Box
} from "#material-ui/core";
const styles = createStyles({
button: {
"& .MuiStepIcon-root.MuiStepIcon-active": {
fill: "red"
}
}
});
interface Props extends WithStyles<typeof styles> {
title: string;
}
const ExampleComponent: FunctionComponent<Props> = ({ title, classes }) => {
console.log(title, classes);
return (
<Box display="flex" alignItems="center">
<span>{title}</span>
<Stepper activeStep={0}>
<Step>
<StepButton className={classes.button}>Test</StepButton>;
</Step>
</Stepper>
</Box>
);
};
export default withStyles(styles)(ExampleComponent);
App.tsx
import * as React from "react";
import { ThemeProvider, createMuiTheme } from "#material-ui/core";
import ExampleComponent from "./ExampleComponent";
const theme = createMuiTheme();
function App() {
return (
<ThemeProvider theme={theme}>
<ExampleComponent title="Root" />
<ThemeProvider theme={theme}>
<ExampleComponent title="Nested" />
</ThemeProvider>
</ThemeProvider>
);
}
export default App;
Inside the ExampleComponent I console.log the generated classes object.
I want to use nested ThemeProviders and override classes inside components regardless of the ThemeProvider.
Am I missing something or is this not possible?
When you are using nested themes, you cannot reliably use Material-UI's global class names (e.g. .MuiStepIcon-root.MuiStepIcon-active). Within a nested theme, the "Mui..." class names have to be different to avoid conflicting with the CSS classes for the top-level theme since the nested theme will cause some of the CSS for the "Mui..." classes to be different.
You can use the following syntax in order to successfully match the suffixed versions of the Mui class names that occur within nested themes:
const styles = createStyles({
button: {
'& [class*="MuiStepIcon-root"][class*="MuiStepIcon-active"]': {
fill: "red"
}
}
});
Related answer:
How reliable are MUI Global Class names in JSS?

How to change button props based on breakpoints in Material UI

I'm trying to change the variant and/or size on a material-ui button for different screen sizes. For example, use no variant or size="small" below the "sm" breakpoint and variant="outlined" and/or size="large" above "sm".
Normally, I'd make use withStyles and create a style with theme.breakpoints to affect changes by applying the style to the element using className, however, variant and size are props.
After reading the api, scouring the web, and fiddling extensively, I can't seem to figure out any straight-forward way to change the props based on viewport width.
I've thought about creating a "width-detector" and then using some JS logic to change the button element's props accordingly, but that seems just a bit far out as a solution.
So I'm asking here to see if there is an easier solution out there. Thanks.
The withWidth HOC is deprecated per Material UI Docs.
This is the approach that works now, with a combination of useTheme and useMediaQuery.
Edit: useTheme is not really required here, since useMediaQuery automatically provides that as an argument.
// import { useTheme } from "#material-ui/core/styles";
import { useMediaQuery } from "#material-ui/core";
...
function ResponsiveButton() {
// const theme = useTheme();
// const isSmallScreen = useMediaQuery(theme.breakpoints.down("xs"));
const isSmallScreen = useMediaQuery(theme => theme.breakpoints.down("xs"));
const buttonProps = {
variant: isSmallScreen ? "text" : "outlined",
size: isSmallScreen ? "small" : "large"
};
return (
<Button {...buttonProps} color="primary">
Responsive Button
</Button>
);
}
export default ResponsiveButton;
Material UI Docs:
Sometimes you might want to change the React rendering tree based on the breakpoint value. We provide a withWidth() higher-order component for this use case.
withWidth injects a width property into your component that gives you access to the current breakpoint value. This allows you to render different props or content based on screen size.
function ResponsiveButton({ width }) {
// This is equivalent to theme.breakpoints.down("sm")
const isSmallScreen = /xs|sm/.test(width);
const buttonProps = {
variant: isSmallScreen ? "text" : "outlined",
size: isSmallScreen ? "small" : "large"
};
return (
<Button {...buttonProps} color="primary">
Responsive Button
</Button>
);
}
export default withWidth()(ResponsiveButton);
This is my implementation
import Button from "#material-ui/core/Button";
import { useTheme } from "#material-ui/core/styles";
import useMediaQuery from "#material-ui/core/useMediaQuery";
import { useEffect } from "react";
const ResponsiveButton = (props) => {
const theme = useTheme();
const desktop = useMediaQuery(theme.breakpoints.up("lg"));
const tablet = useMediaQuery(theme.breakpoints.up("sm"));
const mobile = useMediaQuery(theme.breakpoints.up("xs"));
const sizes = () => {
if (desktop) return "large";
if (tablet) return "medium";
if (mobile) return "small";
};
return <Button {...props} size={sizes()}/>;
};
export default ResponsiveButton;
To all those who are from 'mui' and not 'material-ui' age, you can create two different components and pass display as an in-line style. Only one will be rendered for a specific screen size.
Refer the example below.
<Typography
sx={{ display:{sx:'none', sm:'none', md:'block', lg:'block', xl:'block'} }}
>
This will be rendered only on screens which are medium-sized and above
</Typography>
<Typography
sx={{ display:{sx:'block', sm:'block', md:'none', lg:'none', xl:'none'} }}
>
This will be rendered only on screens which are below medium-sized
</Typography>
Not sure if you must mention all the sizes inside display. Maybe you can skip lg and xl once you set md to 'block'.

Resources