Using Material-UI System with Default Components - reactjs

Is it possible to use Material-UI System with the default Material UI components? For example, this currently does not work:
<AppBar bgColor="secondary.main" p={{ xs: 2, sm: 3, md: 4 }}>...</AppBar>
I am wondering, though, is there a way to get this to work (not just for AppBar, but for any Material UI component)? I.e., is there a way to integrate System with the default Material UI components?
If so, how?
Thanks.

The Box component can be used to add the Material-UI System features to other components by using its component prop. One caveat is that if you try to change styling that is explicitly controlled by the component, you may run into specificity issues. For instance the styling of the background color on the Button in my example below doesn't work correctly if you flip the order of the Button and Box imports since this causes the order of their styles in the <head> to also be flipped.
import React from "react";
import styled, { ThemeProvider as SCThemeProvider } from "styled-components";
import { useTheme, StylesProvider } from "#material-ui/core/styles";
import MuiAppBar from "#material-ui/core/AppBar";
import Button from "#material-ui/core/Button";
import Box from "#material-ui/core/Box";
const AppBar = styled(MuiAppBar)`
background-color: red;
${props => props.theme.breakpoints.up("sm")} {
background-color: orange;
}
${props => props.theme.breakpoints.up("md")} {
background-color: yellow;
color: black;
}
${props => props.theme.breakpoints.up("lg")} {
background-color: green;
color: white;
}
`;
export default function App() {
const muiTheme = useTheme();
return (
<StylesProvider injectFirst>
<SCThemeProvider theme={muiTheme}>
<Box component={AppBar} p={{ xs: 2, sm: 3, md: 4, lg: 5 }}>
Sample AppBar 1
</Box>
<div style={{ height: "100px" }} />
<Box component={Button} bgcolor="secondary.main">
Sample Button
</Box>
</SCThemeProvider>
</StylesProvider>
);
}
Related answer: Material-UI Grid does not hide whe use Display

Related

Missing styles when using React with Web Components

I'm creating a widget module that will then be assembled into a bundle and connected to the static sites in a single file. One of the main problems is applying the styles of the parent sites to the widget module. Because of this, an attempt was made to encapsulate the widget using the shadow dom.
import React from 'react';
import ReactDOM from 'react-dom';
import styled from 'styled-components';
const StyledButton = styled.button`...`;
function ModuleComponent() {
return (
<div>
<StyledButton>I does not work</StyledButton>
<button style={{ height: 40, color: "red" }}>It works</button>
</div>
);
}
class IncapsulatedReactWidgetElement extends HTMLElement {
connectedCallback() {
const mountPoint = document.createElement("div");
this.attachShadow({ mode: "open" }).appendChild(mountPoint);
ReactDOM.render(<ModuleComponent />, mountPoint);
}
}
customElements.define(
"incapsulated-react-widget-element",
IncapsulatedReactWidgetElement
);
function Root() {
return <incapsulated-react-widget-element />;
}
const MOUNT_NODE = document.getElementById("root");
ReactDOM.render(<Root />, MOUNT_NODE);
Actually it works as expected except the styles. I'm trying to use styled components but nothing works expect inline styles. As you can see, I do two render actions: first for integration ModuleComponent into custom element and second - for render the app. In which way styles can get lost?
Correct way for applying styles is using Style Hook for React. Make use of makeStyles and useStyles and its an standard way of doing it.
This may help for quick start: https://material-ui.com/styles/basics/#hook-api
The inline styles that you have used are also kinda a subset of it.
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
const useStyles = makeStyles({
root: {
background: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)',
border: 0,
borderRadius: 3,
boxShadow: '0 3px 5px 2px rgba(255, 105, 135, .3)',
color: 'white',
height: 48,
padding: '0 30px',
},
});
export default function Hook() {
const classes = useStyles();
return <Button className={classes.root}>Hook</Button>;
}
if you are going to have larger stylesheets, then we can separate it to different file. For site wide changes, you can use createStyle hook.

React create one styled components for two different icons?

So I am using react icons and I have two different icons that I am styling with styled components. My issue is that they essentially have the exact same styles, but the only difference is which icon I am choosing to style.
I don't know how to combine both icons into one styled component
Here is my code
const backArrow = styled(IoArrowBack)`
width: 50px;
height: 50px;
`;
const forwardArrow = styled(IoArrowForward)`
width: 50px;
height: 50px;
`;
Since I am using styled components, I just pass the icon into the () based on which one I want to style. The issue is I have over 12 lines of the exact same styles for both icons. It doesn't make sense to repeat the exact styles
How would I create one styled component, but display two different icons?
Example concept like this
const arrowIcon = styled(IoArrowBack, IoArrowForward)`
width: 50px;
height: 50px;
`
But then the issue occurs if I were to add them to my JSX
Cause then how would I even add the code?
I can't do
<arrrowIcon>Back arrow</arrowIcon>
<arrrowIcon>Forward arrow</arrowIcon>
So it wouldn't know which icon is which.
Is this even possible with styled components, or would I just have to copy and paste the same styles for each icon?
This piece of code is a bit weird to me, I think this is not a valid code
const arrowIcon = styled(IoArrowBack, IoArrowForward)`
width: 50px;
height: 50px;
`
However you can do a trick to get a shared style
const sharedIconStyle = css`
width: 50px;
height: 50px;
`
And
const styledArowBack= styled(IoArrowBack)`
${sharedIconStyle}
`
const styledArrowForward = styled(IoArrowForward)`
${sharedIconStyle}
`;
Could you just do it with React?
import React from "react";
import "./styles.css";
import { ReactComponent as icon1 } from "./icons/1.svg";
import { ReactComponent as icon2 } from "./icons/2.svg";
export default function App() {
return (
<div className="App">
<SizedIcon Icon={icon1} />
<SizedIcon Icon={icon2} />
</div>
);
}
const SizedIcon = ({ size = 50, Icon }) => {
console.log(typeof Icon);
return (
<div>
<Icon width={size} height={size} />
</div>
);
};
What I did is wrapped the icons in a div and styled the div, for example change the icon colors to red:
const IconStyles = styled.div`
color: red;
`
<IconStyles>
<IoArrowBack />
</IconStyles>
<IconStyles>
<IoArrowForward />
</IconStyles>
If you want to change the size of the icons then add a font-size in the div containing the icons, that is how I personally do it.

Better way to use material-system with styled-components

I am trying to use the stack above to customize Material components with the props provided by the Material system (joining with styled-components).
The component that I'm testing:
import React from 'react'
import Grid from '#material-ui/core/Grid'
import { spacing, sizing, color, flexbox, display } from '#material-ui/system'
import styled from 'styled-components'
const Row = styled(props => <Grid container {...props} />)`
${spacing}
`
export default Row
Everything on screen works very well, but an error on console appear every time that I use a material-system prop. Ex.:
<Row maxWidth='200px'>...</Row>
the following error appear on console:
Warning: React does not recognize the maxWidth prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase maxwidth instead. If you accidentally passed it from a parent component, remove it from the DOM element.
I know that the ...props is passing maxWidth to Grid component (and it's a html element), but I'm confusing about how to use it without these console log's
Let's separate exactly which props are used for styling and which props are sent to the Grid component. Using rest, we can "pull out" certain properties from the props object and send the rest to the Grid component. Then, the properties that we pull out will form the CSS.
import React from 'react'
import Grid from '#material-ui/core/Grid'
import { spacing, sizing, color, flexbox, display } from '#material-ui/system'
import styled from 'styled-components'
const Row = styled(
({ maxWidth, somethingElse, ...rest }) => <Grid container {...rest} />
)`
${spacing}
maxWidth: ${props => props.maxWidth || defaultValue1};
somethingElse: ${props => props.somethingElse || defaultValue2};
`;
export default Row
The Material-UI documentation shows examples of various approaches to styling, including an example showing how to use styled-components with Material-UI.
Here is the example they show:
import React from 'react';
import { styled } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
const MyButton = styled(Button)({
background: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)',
border: 0,
borderRadius: 3,
boxShadow: '0 3px 5px 2px rgba(255, 105, 135, .3)',
color: 'white',
height: 48,
padding: '0 30px',
});
export default function StyledComponents() {
return <MyButton>Styled Components</MyButton>;
}
Another approach is using the Material-UI hook that uses a CSS-in-JS approach and the makeStyles function:
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
const useStyles = makeStyles({
root: {
background: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)',
border: 0,
borderRadius: 3,
boxShadow: '0 3px 5px 2px rgba(255, 105, 135, .3)',
color: 'white',
height: 48,
padding: '0 30px',
},
});
export default function Hook() {
const classes = useStyles();
return <Button className={classes.root}>Hook</Button>;
}
There's also a way to create a custom theme in a separate file and import it where it's needed. Here is an example of the default Theme object and what it contains. It's good to take a look at and get an idea of what can be customized by using createMuiTheme.
Material-UI has multiple approaches to styling and it can be a lot to take in at first. Hope this helps.
tanks a lot about the anwsors. Based in these answers, I develop a helper to separate this kind of props:
import React from 'react'
import styled from "styled-components";
import Button from "#material-ui/core/Button";
import { spacing, sizing } from "#material-ui/system";
const allowedProps = ({props, system}) => {
const notAllowedProps = filterProps({props, system})
const allowedProps = Object.entries(props).reduce((acc, [key, value]) => {
if (!notAllowedProps.includes(key)) {
return {...acc, [key]: value}
}
return acc
}, {})
return allowedProps
}
const filterProps = ({props, system}) => {
const objSystem = system.reduce((acc, element) => ({
...acc,
...element(props)
}), {})
return Object.keys(objSystem)
}
const ButtonBase = (props) => <Button {...allowedProps({props, system: [spacing, sizing]})} />
const MyButton = styled(ButtonBase)...

Media Queries in Material-UI Using Styled-Components

Material UI has a nice set of built-in media queries: https://material-ui.com/customization/breakpoints/#css-media-queries
Material UI also allows us to use Styled-Components with Material UI: https://material-ui.com/guides/interoperability/#styled-components
I want to know how to combine the two together. That is, how can I make media queries using Styled Components and Material-UI's built-in breakpoints?
Thanks.
UPDATE:
Here is an example of what I am trying to do:
import React, { useState } from 'react'
import styled from 'styled-components'
import {
AppBar as MuiAppBar,
Drawer as MuiDrawer,
Toolbar,
} from '#material-ui/core'
const drawerWidth = 240
const AdminLayout = ({ children }) => {
return (
<BaseLayout>
<AppBar position="static">
<Toolbar>
TOOLBAR
</Toolbar>
</AppBar>
<Drawer>
DRAWER
</Drawer>
{children}
</BaseLayout>
)
}
AdminLayout.propTypes = {
children: PropTypes.node.isRequired,
}
export default AdminLayout
// ------- STYLES -------
const AppBar = styled(MuiAppBar)`
/* Implement appBar styles from useStyles */
`
const Drawer = styled(MuiDrawer)`
/* Implement drawer styles from useStyles */
`
// STYLES THAT I WANT TO CONVERT TO STYLED-COMPONENTS
const useStyles = makeStyles(theme => ({
root: {
display: 'flex',
},
drawer: {
[theme.breakpoints.up('sm')]: {
width: drawerWidth,
flexShrink: 0,
},
},
appBar: {
[theme.breakpoints.up('sm')]: {
width: `calc(100% - ${drawerWidth}px)`,
marginLeft: drawerWidth,
},
},
toolbar: theme.mixins.toolbar,
}))
Below is an example showing one way of leveraging the Material-UI theme breakpoints with styled-components. This is passing the Material-UI theme to the styled-components ThemeProvider in order to make it available as a prop within the styles. The example also uses StylesProvider with the injectFirst prop so that the Material-UI styles will occur at the beginning of the <head> rather than the end, so that the styled-components styles occur after the Material-UI styles and therefore win when specificity is otherwise equal.
import React from "react";
import styled, { ThemeProvider as SCThemeProvider } from "styled-components";
import { useTheme, StylesProvider } from "#material-ui/core/styles";
import MuiAppBar from "#material-ui/core/AppBar";
const AppBar = styled(MuiAppBar)`
background-color: red;
${props => props.theme.breakpoints.up("sm")} {
background-color: orange;
}
${props => props.theme.breakpoints.up("md")} {
background-color: yellow;
color: black;
}
${props => props.theme.breakpoints.up("lg")} {
background-color: green;
color: white;
}
`;
export default function App() {
const muiTheme = useTheme();
return (
<StylesProvider injectFirst>
<SCThemeProvider theme={muiTheme}>
<AppBar>Sample AppBar</AppBar>
</SCThemeProvider>
</StylesProvider>
);
}
Related documentation:
styled-components theme usage: https://styled-components.com/docs/advanced#theming
StylesProvider injectFirst: https://material-ui.com/styles/api/#stylesprovider
If you are using the "Style Objects" approach (i.e., "JavaScript") to styled-components, then this is the way to achieve that same outcome. This builds on top of what Ryan Cogswell mentioned earlier.
Some might prefer this if switching over from another CSS-in-JS system (like Material-UI's built-in JSS). Also, the "Style Objects" approach only requires you to bring in props one time as opposed to using the props variable on any line. It's good to have choices. 😇
Style Object
const AppBar = styled(MuiAppBar)((props) => ({
backgroundColor: red;
[props.theme.breakpoints.up("sm")]: {
backgroundColor: orange,
},
[props.theme.breakpoints.up("md")]: {
backgroundColor: yellow,
color: black,
},
[props.theme.breakpoints.up("lg")]: {
backgroundColor: green,
color: white,
},
}));
Style Object, but more concise
Since we only need to access the props one time using the JavaScript approach and we only use theme in this style area, we can destructure theme from the incoming props for a bit less code.
const AppBar = styled(MuiAppBar)(({ theme }) => ({
backgroundColor: red;
[theme.breakpoints.up("sm")]: {
backgroundColor: orange,
},
[theme.breakpoints.up("md")]: {
backgroundColor: yellow,
color: black,
},
[theme.breakpoints.up("lg")]: {
backgroundColor: green,
color: white,
},
}));
Note: If you are using TypeScript and have set up your styled-components theme to match the Material-UI theme, then type safety still works as expected in either the CSS or JavaScript approach.
The breakpoints are provided as part of the default theme.
They are constants and won't change, therefore you can use them across the components or styled themes:
import React from 'react';
import styled from 'styled-components';
import { makeStyles } from '#material-ui/core/styles';
const useStyles = makeStyles(theme => {
console.log('md', theme.breakpoints.up('md'));
return {};
});
const BP = {
MD: `#media (min-width:960px) `,
};
const Container = styled.div`
background-color: green;
${({ bp }) => bp} {
background-color: red;
}
`;
export default function StyledComponentsButton() {
useStyles();
return <Container bp={BP.MD}>Example</Container>;
}
const StyledDrawer = styled(Drawer)(
({ theme }) => `
.MuiDrawer-paper {
${theme.breakpoints.up('sm')} {
width: 370px;
}
${theme.breakpoints.down('sm')} {
width: 100vw;
}
}
`)
The syntax may look weird, but trying this code will explain everything
const StyledDiv = styled.div`
${({theme}) => {
console.log(theme.breakpoints.up('lg'));
return "";
}}
`;
// you will see in your console
// #media (min-width:1280px)
Once you understand that theme.breakpoints.up('lg') is same as #media (min-width:1280px) everything become obvious. everytime you put theme.breakpoints.up(key) it get replaced with #media... string.

Is there a way to override style from Semantic-UI with style from Styled-Components in ReactJS?

I'm building a small project in ReactJS and I would like to use Semantic-UI (as a main theme of my website) and override some parts of it with Styled Components (like if it was some custom CSS).
When I'm trying to import them on the same component, I've noticed that Styled-Components will use some of the style I wrote but not all of it (like text-size or some margin).
EDIT
Here's the way I've tried to implement them in my component:
import "semantic-ui-css/semantic.min.css";
import { Grid, Form, Segment, Button, Header, Message, Icon } from 'semantic-ui-react';
import { StyledRegister } from '../styles/StyledRegister';
class Register extends React.Component {
...
render() {
return (
<StyledRegister>
<Grid textAlign="center" verticalAlign="middle">
<Grid.Column style={{ maxWidth: 450 }}>
// Here's continue the long code of my register page including form ect...
</Grid.Column>
</Grid>
</StyledRegister>
Do any of you have an idea if it's possible, and if so, is there proper way to do it ?
An example with the custom adapted statistics component of semantic ui:
import "semantic-ui-css/semantic.min.css";
import { StyledStatistic } from "./styledStatistic";
function App() {
return (
<div className="App">
<StyledStatistic>
<StyledStatistic.Value>5,550</StyledStatistic.Value>
<StyledStatistic.Label>Downloads</StyledStatistic.Label>
</StyledStatistic>
</div>
);
}
import styled from "styled-components";
import { Statistic } from "semantic-ui-react";
export const StyledStatistic = styled(Statistic)`
padding: 2rem;
display: flex !important;
justify-content: center;
&&& > .value {
color: red;
}
&&& > .label {
color: green;
}
`;
See: https://codesandbox.io/s/goofy-mestorf-go410

Resources