I've gone through the usual recommendations. Next.js app with postcss, sass, and tailwind.
Trying to load styles from calendar.module.scss.
.react-datepicker {
background: none;
border: none;
color: #969eac;
font-size: 1rem;
font-family: 'Mulish';
}
.react-datepicker .react-datepicker__header {
background-color: #fff !important;
}
Even with !important, the above doesn't work. If I create a class of .calendar and apply that to my component, I can at least get the main div to be styled.
import styles from './calendar.module.scss'
<ReactDatePicker
inline
calendarClassName={styles.calendar}
/>
// ./calendar.module.scss
.calendar {
background: none;
border: none;
color: #969eac;
font-size: 1rem;
font-family: 'Mulish';
}
But trying to add the child classes to this has no effect. I tried adding the .react-datepicker classes to my style.css but that isn't having an effect either.
Curious if someone might have any pointers.
Thanks!
I ran through the exact issue few days ago
I use SCSS with NEXT.js based react app, I will show you some code that will work if you are using material UI calendar
NOTE: this is dark theme customized calendar & pops up onClick with no textfield
import DateFnsUtils from "#date-io/date-fns";
import {DatePicker, MuiPickersUtilsProvider} from "#material-ui/pickers";
import React from "react";
import styles from "./Calendar.module.scss";
import Box from "#material-ui/core/Box";
import {createMuiTheme, MuiThemeProvider} from "#material-ui/core/styles";
import {black, white} from "material-ui/styles/colors";
export default function Calendar(props) {
const materialTheme = createMuiTheme({
palette: {
primary: {
main: '#3E3F42'
}
},
overrides: {
MuiPickersCalendarHeader: {
switchHeader: {
backgroundColor: '#303235',
color: white,
},
iconButton: {
backgroundColor: "transparent",
color: white
},
dayLabel: {
color: white //days in calendar
},
transitionContainer: {
color: white
}
},
MuiPickersBasePicker: {
pickerView: {
backgroundColor: '#3E3F42'
}
},
MuiPickersDay: {
day: {
color: white //days in calendar
},
daySelected: {
backgroundColor: '#FFC561', //calendar circle
color: black
},
current: {
backgroundColor: '#736F69',
color: white
},
},
MuiDialogActions: {
root: {
backgroundColor: '#3E3F42'
}
}
}
});
const isOpened = props.isOpened;
const topPosition = props.topPosition;
const selectedDate = props.selectedDate;
const sleep = (milliseconds) => {
return new Promise(resolve => setTimeout(resolve, milliseconds))
}
async function handleDateChange(date1) {
props.onChange(date1);
await sleep(700);
props.setDDOpen(false);
}
return isOpened ? (
<Box pad={{ vertical: 'small'}} className={styles.smallCard} style={{top: topPosition}}>
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<MuiThemeProvider theme={materialTheme}>
<DatePicker
disableToolbar
variant="static"
value={selectedDate}
onChange={handleDateChange}
/>
</MuiThemeProvider>
</MuiPickersUtilsProvider>
</Box>): null
}
Related
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.
I'm trying to customize a MUI TextField Select component with styled components.
The ideia is styled-compoents provide diferent classes to Select field and Menu, so i can have their styled separated.
const StyledSelect = styled(({ className, ...props }) => {
return (
<TextField {...props}
classes={{ root: className }}
SelectProps={{
MenuProps: {
classes: { paper: className, list: className },
anchorOrigin: {
vertical: "bottom",
horizontal: "left"
},
transformOrigin: {
vertical: "top",
horizontal: "left"
},
getContentAnchorEl: null
},
}}
/>
)
})`
& {
background-color: #454D5D;
border-radius: 10px;
margin-top: 5px;
}
& li {
color: #FFF;
}
&.MuiFormControl-root {
background-color: transparent;
}
& .MuiListItem-root {
font-size: 18px;
}
& .MuiListItem-root.Mui-selected {
background-color: #1A2233;
}
& .MuiFormLabel-root {
font-family: 'Roboto';
font-weight: 300;
}
& .MuiInputLabel-shrink {
color: ${props => props.color};
font-weight: normal;
}
& .MuiInput-underline:after {
border-bottom: 2px solid ${props => props.errors[props.field.name] && props.touched[props.field.name]
? CASABLANCA : props.color};
transition: none;
transform: none;
}
& .MuiInput-underline:before {
border-bottom: 1px solid ${props => props.color}
}
& .MuiSelect-roo {
color: black;
font-family: 'Roboto';
font-weight: 300;
}
& .MuiSelect-select:focus {
background: transparent;
}
`;
I wish my TextField class would be different from MenuProps class
One way to solve this is to have one wrapper component per class name you need to generate. In my example below, StyledTextField takes care of the root class name for TextField (the className property is equivalent to classes.root) and then MenuPaperClass provides an additional class name.
import React from "react";
import ReactDOM from "react-dom";
import TextField from "#material-ui/core/TextField";
import MenuItem from "#material-ui/core/MenuItem";
import styled from "styled-components";
const StyledTextField = styled(TextField)`
/* && to add specificity */
&& {
border: 1px solid green;
}
`;
const MenuPaperClass = styled(({ className, ...props }) => {
return (
<StyledTextField
SelectProps={{ MenuProps: { classes: { paper: className } } }}
value="1"
select
{...props}
>
<MenuItem value="1">One</MenuItem>
<MenuItem value="2">Two</MenuItem>
<MenuItem value="3">Three</MenuItem>
</StyledTextField>
);
})`
&& {
background-color: lightblue;
}
`;
function App() {
return (
<div className="App">
<MenuPaperClass />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
This isn't a particularly elegant solution and gets pretty tedious if you have 3 or more separate classes you want to use, so I may come back to this later to consider alternative approaches/syntax, but this does work.
I am trying to test that ThemeProvider is providing theme to my component. It is properly providing default & custom theme to the base class and passing tests, but when I test for the condition class it does not find any of the styles. This is even while passing through the class directly. This is my first venture into Styled-Components and testing as a whole.
I have tested using the optional { css } import from styled-components, tried passing the class directly, and removing the default class entirely. I also tried setting a default style directly in the styled-component. The toBeTruthy() does pass, so it's at least seeing it with the class I would think?
// This is being called globally
export const mountWithTheme = (Component, customTheme) => {
const theme = customTheme || defaultTheme
return mount(<ThemeProvider theme={theme}>{Component}</ThemeProvider>)
}
import React from 'react';
import { shallow, mount } from 'enzyme';
import { MemoryRouter, Route, Link } from 'react-router-dom';
import { css } from 'styled-components';
import 'jest-styled-components';
import HeaderLinkA from './HeaderLinkA.jsx';
describe('HeaderLinkA', () => {
it('renders color /w prop', () => {
const wrapper = mount(
<MemoryRouter initialEntries={['/about']} >
<Route component={props => <HeaderLinkA {...props} name='about' test='about' theme={{ primarycolor: 'white', secondarycolor: 'black' }} /> } path='/about' />
</MemoryRouter>
)
expect(wrapper.find('Link')).toHaveStyleRule('color', 'white');
expect(wrapper.find('Link')).toHaveStyleRule('color', 'white', {
modifier: `:hover`,
});
});
it('is themed with default styles, when theme is missing', () => {
const wrapper = global.StyledComponents.mountWithTheme(
<MemoryRouter initialEntries={['/about']} >
<React.Fragment>
<HeaderLinkA name='about' testclass='section-link-active' />,
</React.Fragment>
</MemoryRouter>
)
expect(wrapper.find('Link')).toHaveStyleRule('color', '#FFF')
expect(wrapper.find('Link')).toHaveStyleRule('color', '#FFF', {
modifier: `:hover`
});
expect(wrapper.find('Link.section-link-active')).toBeTruthy();
expect(wrapper.find('Link')).toHaveStyleRule('border-bottom', '1px solid #95d5d2');
});
});
import React from 'react';
import { Link } from 'react-router-dom';
import styled from 'styled-components';
const StyledHeaderLink = styled(Link)`
text-decoration: none;
color: ${ props => props.theme.primarycolor };
padding-bottom: 2px;
overflow-x: hidden;
position: relative;
display: inline-flex;
&:active,
&:hover {
color: ${ props => props.theme.primarycolor };
}
&.section-link {
&:after {
content: '';
position: absolute;
bottom: 0;
left: 0;
height: 1px;
background: ${ props => props.theme.secondarycolor };
width: 100%;
transform: translate3d(-110%, 0, 0);
-webkit-transform: translate3d(-110%, 0, 0);
transition: transform .3s ease-in-out;
-webkit-transition: transform .3s ease-in-out;
}
&:hover:after {
transform: translate3d(0%, 0, 0);
-webkit-transform: translate3d(0%, 0, 0);
}
}
&.section-link-active {
border-bottom: 1px solid ${ props => props.theme.secondarycolor || '#95d5d2' };
}
`;
const HeaderLinkA = ( props ) => {
const page = props.name.toLowerCase();
return (
<StyledHeaderLink {...props } to={`/${ page }`}
className={props.testclass || window.location.pathname === `/${ page }` ? // refactor this to be controlled by HeaderO and pass down the prop.
'section-link-active'
: 'section-link'} >
{props.name}
</StyledHeaderLink>
)
}
export default HeaderLinkA;
All of the tests pass up until the final one which I'm stuck on.
"Property 'border-bottom' is not found in style rules"
Expected:
"border-bottom: 1px solid #95d5d2"
Received:
"border-bottom: undefined"
I tried to create a custom styled button with material ui to reuse in other files of my project. Somehow, the button does not adapt the style i defined. Could someone tell me where I made a mistake / forgot something ?
import React from 'react';
import { withStyles } from '#material-ui/core/styles';
import IconButton from '#material-ui/core/IconButton';
const styles = themes => ({
root: {
margin: "50px",
padding: "20px",
display: 'block'
},
button: {
marginTop: '20px',
padding: '100px',
height: "300px+15vh",
width: "300px+15vw",
borderRadius: "20% 20%",
color: '#FFFFFF',
backgroundColor: '#05164D',
'&:hover': {
backgroundColor: "rgba(5, 22, 77, 0.75)",
boxShadow: '0 3px 5px 2px rgba(153, 153, 153, .8)'
},
},
});
const styledButton = props => {
const { classes } = props;
return (
<div>
<IconButton className={classes.button} {...props} aria-label="Gift" disableTouchRipple="true">
{props.children}
</IconButton>
</div>
)
}
export default withStyles(styles)(styledButton);
I have a bunch of content-similar React apps to style, and I'm trying to shape a system, based on Styled-Components/Styled-Theming, to rule and modify their global styles just by changing the <ThemeProvider /> theme prop.
I've been trying different options, and this is what seems to work best :
an external themes.js file with all styles wrapped in a global object
import theme from "styled-theming";
const backG = theme.variants("observatory", "backG", {
positiveDark: {
_OBS1: 'hsl(73, 65%, 50%)',
_OBS2: 'hsl(210, 100%, 60%)',
},
negativeDark: {
_OBS1: 'hsl(8, 70%, 63%)',
_OBS2: 'hsl(0, 100%, 90%)',
},
actionDark: {
_OBS1: 'hsl(53, 82%, 62%)',
_OBS2: 'hsl(48, 100%, 50%)',
},
});
const cursor = theme.variants("observatory", "cursor", {
normal: {
_OBS1: 'initial',
_OBS2: 'initial',
},
pointer: {
_OBS1: 'pointer',
_OBS2: 'pointer',
},
});
const fontS = theme.variants("observatory", "fontS", {
h1: {
_OBS1: "2.4rem",
_OBS2: "2.25rem",
},
h2: {
_OBS1: "2.2rem",
_OBS2: "2rem",
},
h3: {
_OBS1: "2rem",
_OBS2: "1.75rem",
},
});
const flexL = theme.variants("observatory", "flexL", {
layout1: {
_OBS1: "; display: flex; justify-content: center;",
_OBS2: "; display: flex; justify-content: center;",
},
layout2: {
_OBS1: "; display: flex; justify-content: space-around; align-items: flex-start;",
_OBS2: "; display: flex; flex-direction: column; align-items: flex-start;",
},
layout3: {
_OBS1: "; display: flex; flex-direction: column; align-items: flex-start;",
_OBS2: "; display: flex; justify-content: space-around; align-items: flex-start;",
}
});
const themes = {
backG: backG,
cursor: cursor,
flexL: flexL,
fontS: fontS,
}
export {themes};
a CRA representing my app, with
an index.js file with 2 <ThemeProvider />, the first one referencing the external file, the second one dealing with themes inside of that external file
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
// styles
import {themes} from "./themes";
import {ThemeProvider} from "styled-components";
ReactDOM.render(
<ThemeProvider theme={themes}>
<ThemeProvider theme={{ observatory: "_OBS2" }}>
<App />
</ThemeProvider>
</ThemeProvider>,
document.getElementById('root')
);
an App.js file and other children components
import React from 'react';
// styles
import styled, { injectGlobal } from "styled-components";
// components
import ThreeButtons from "./ThreeButtons";
injectGlobal`
#import url('https://fonts.googleapis.com/css?family=Quicksand:400,500');
html {
font: 400 10px Roboto, sans-serif;
box-sizing: border-box;
}
*, *:before, *:after {
box-sizing: inherit;
}
body {
font: 500 1.8rem Quicksand;
}
`
const Wrapper1 = styled.div`
display: ${props => props.theme.flexL};
align-items: ${props => props.theme.flexL};
`
const App = () => (
<Wrapper1 flexL="layout1">
<ThreeButtons alignSelf1="center" flexL="layout2" />
<ThreeButtons alignSelf2="flex-end" flexL="layout3" />
</Wrapper1>
);
export default App;
import React from "react";
// styles
import styled from "styled-components";
// components
import Button from "./Button";
const Wrapper1 = styled.div`
flex: 1;
display: ${props => props.theme.flexL};
flex-direction: ${props => props.theme.flexL};
align-items: ${props => props.theme.flexL};
`
const ThreeButtons = (props) => (
<React.Fragment>
<Wrapper1
flexL={props.flexL}
>
<Button
backG="positiveDark"
cursor="pointer"
fontS="h1"
label="lg-pos-button-pointer"
/>
<Button
alignSelf={props.alignSelf1}
backG="negativeDark"
cursor="pointer"
fontS="h2"
label="md-neg-button-pointer"
/>
<Button
alignSelf={props.alignSelf2}
backG="actionDark"
cursor="normal"
fontS="h3"
label="sm-act-button-nopointer"
/>
</Wrapper1>
</React.Fragment>
)
export default ThreeButtons;
import React from "react";
// styles
import styled from "styled-components";
const StyledButton = styled.button`
align-self: ${props => props.alignSelf};
background: ${props => props.theme.backG};
font-size: ${props => props.theme.fontS};
cursor: ${props => props.theme.cursor};
padding: 1.2rem 1.8rem;
border: none;
border-radius: .3rem;
margin: 1rem;
filter: saturate(50%);
transition: filter ease-in-out .15s;
&:hover {
filter: saturate(100%);
}
`
const Button = (props) => (
<StyledButton
backG={props.backG}
cursor={props.cursor}
alignSelf={props.alignSelf}
fontS={props.fontS}
>
{props.label}
</StyledButton>
)
export default Button;
Like I said before, it looks like it works, but still there're 2 things I'm not really comfortable with because I don't get it :
in the themes.js external file, I've been trying to create some kind of Flexbox mixins : to make it work, I must add an initial semicolon in the string value, or the first property is not considered and its value is not applied. Can someone explain me why ?
in the index.js file, I must nest a <ThemeProvider /> inside another one to make it work correctly : again, can someone explain me how Styled-Components and Styled-Theming are working together ?
generally speaking, have you got any tips or advice to make my solution a better solution or to solve my initial problem, which was to globally style a bunch of content-similar React apps ?
If you need, you can find the entire code on GitHub : https://github.com/paillenapple/styles_sandbox
Thanks for your help.
Silvère