Tailwind, Twin-macro, Styled Components className override priority - reactjs

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

Related

MUI v5 : How to pass css classes to components using className prop? Also I want to use theme in those classes?

MUI v5 : How to pass css classes to components using className prop? Also I want to use theme in those classes? I was trying to do it using styled in MUIv5, but if am not wrong we can target certain classes using styled but cannot pass those classes to the component.
You need to first import that css file in that component.Then you can pass it like this
<div className={name of your class that you imported}>
One way for achieving this is to define the css class at a sx property of a parent component, like in the following example:
function Child({ message, className }) {
return <Typography className={className}>{message}</Typography>;
}
function Parent() {
return (
<Box
sx={{
'.childClass': {
color: 'primary.main'
}
}}
>
<Child message="Child with no css class defined" />
<Child className="childClass" message="Child with css class defined" />
</Box>
);
}
In this example, 'primary.main' resolves to theme.pallete.primary.main, as mentioned here.

Apply MUI styles to non-MUI markup elements

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

Can you pass a React useState state object into a Styled Component that's using Tailwind and twin.macro?

The following Vue code works. I'm trying to translate it to React. The key functionality I'm interested in is the ability to toggle tailwind classes with a change in state. In this case, with a boolean that toggles whether a div is shown or hidden by changing Tailwind's property for display from block to hidden:
<template>
<header>
<button #click="isOpen = !isOpen" type="button" class="...">My Button</button> // toggle state
<div :class"isOpen ? 'block' : 'hidden'" class="px-2 pt-2 pb-4"> //show and hide div
</header>
</template>
<script>
export default {
data() {
return {
isOpen: false, // set state
}
},
}
</script>
Here's where I'm at in React using Tailwindcss and twin.macro (a combination of Tailwind and Styled Components) using React's useState Hook:
export default function Header() {
const [isOpen, setIsOpen] = useState(false); // declare state
return (
<Wrapper>
<ButtonWrap type="button" onClick={() => setIsOpen(!isOpen)}> // toggle state
My Button
</ButtonWrap>
<StyledDiv>How do I toggle a style in this?</StyledDiv> // show and hide div
</Wrapper>
)
}
const Wrapper = styled.div`
${tw`container shadow`};
`
const ButtonWrap = styled.button`
${tw`block text-mycolor hover:text-myothercolor focus:text-myothercolor`}
`
const StyledDiv= styled.div<Props>`
display: ${(props) => props.myProp ? 'block' : 'hidden'; // Does NOT work
`
Obviously this does not work as it's using a prop and not state, and with Tailwind as you cannot use display:block/hidden.
Please note, the problem is not hiding the div, that's easy up in the JSX:
{isOpen && (
<StyledDiv> ... </StyledDiv>
)}
I need the solution to change a Tailwind class in the Styled Component itself if possible. I'm not sure if it's possible as the state is not a prop and I understand that Styled Components are stateless. I'm new to SC, so maybe there's an easy solution I'm missing.

emotion use css prop with custom components

In my application I have a component that I want to style with the css prop from outside.
function Component({css}:{css?: React.CSSProperties}) {
// some stuff going on here
return (
<div
css={{
color: blue,
...css
}}
>
// some stuff going on here
</div>
)
}
The background is as follows:
I want to use Component in different scenarios where I have to style the container based on the surrounding layout. E.g. flex, grid or in combination with some components I have to add different margins.
Now instead of introducing many props for all possible scenarios, I want to be able to style the container from outside the component.
E.g. usages of the component could be:
function Layout() {
return (
// some other components
<Component css={{margin: 12}}/>
// some other components
)
}
or
import {css} from "#emotion/react"
const style = css({margin: 12})
function Layout() {
return (
// some other components
<Component css={style}/>
// some other components
)
}
or
import {css} from "#emotion/react"
const style1 = css({margin: 12})
const style2 = css({color: 'red'})
function Layout() {
return (
// some other components
<Component css={[style1, style2]}/>
// some other components
)
}
I have the following problems:
If I use css as the prop name (as in the above example) the style is not applied. If I change the name of the prop to e.g. newCss it works as expected
React.CSSProperties is not the right prop type to handle all the possibilities of emotions css prop.
How can I merge the different css prop possibilities (object, list) with the css prop from Component?
In fact, we don't need to use the extra props. As Ben Laniado mentioned, the official documentation states
Any component or element that accepts a className prop can also use the css prop.
https://emotion.sh/docs/css-prop#use-the-css-prop
So what we need is accepting className and css as props and add className to the component. (We don't need css to the component but need it for types)
type ComponentProps = {
css?: SerializedStyles;
className?: string;
};
const Component: VFC<ComponentProps> = ({ className }) => {
return (
<div css={{ color: blue }} className={className}>
hello world
</div>
);
};
export default function App() {
return (
<div className="App">
<Component css={{ margin: 12 }} />
</div>
);
}
This is the full working example.
https://codesandbox.io/s/react-component-accepting-emotion-css-prop-wskbh?file=/src/App.tsx
The right way of achieving this functionality is modifying the component to accept extra props. This way the css prop passed into the component would be merged with the one within the component.
function Component({prop1, prop2, propN, ...props}) {
// some stuff going on here
return (
<div
css={{
color: blue,
}}
{...props}
>
// some stuff going on here
</div>
)
}
Now you can use additional styles on your component and it will be rendered properly.
function Layout() {
return (
// some other components
<Component css={{marginTop: "1em"}}/>
// some other components
)
}
The side effect of this solution that any additional prop would be passed directly to the HTML element inside the component that takes {...props}.

Locate a component in React project

I wrote a Logout button component in my React app for which I wish to locate at the top right corner of the screen.
render() {
<LogoutButtonComponent height: , backgroudColor: />
}
It wouldn't let me assign any values for height and etc.
This is the Logout component:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
export default class LogOutButton extends Component {
static contextTypes = {
store: PropTypes.object.isRequired,
};
handleClick = () => {
this.props.onLogout();
};
render() {
return <button type="button" onClick={this.handleClick}>Logout</button>;
}
}
Should I locate it by < col /> ?
To add inline styles, you should defined a style object as prop, and pass it values, like doniyor2109 mentioned. There are a few caveats to using this, however.
style={{ height: 100, height: '100px', height: '100%', minHeight: '100px'}}.
Not every value should be passed as integer, some need to be passed as a string
Not every css attribute gets passed as you would expect them to, the css min-height actually gets passed as minHeight, so replace all hyphens with lower camel case style
Inline styles get insanely difficult to manage. I suggest you at the very least create an object outside the component, and pass it in like:
const DivStyle = { minHeight: '100px' }
and then:
<LogoutButtonComponent style={DivStyle} />
You can prefix that DivStyle with an export if you want to import {DivStyle} from './somefile' in other places
I suggest you check out a library like styled-components as it makes styling much easier!
I suggest you check out this article which outlines your options
You don't really add styles to your component like that. It's better to add those styles in the source for the actual component. So how exactly do you want it displayed? I will provide a template kind of and you can change it to what you want.
Go to your source for your Logout Button Component. In the return of your render method try adding a div call it container. Then add styling in a css file to that div or if you are using react-bootstrap or reactstrap or #material/ui/core you can adjust the style according to their documentation.
You can add your css for the className .container to make it appear the way you would like.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
export default class LogOutButton extends Component {
static contextTypes = {
store: PropTypes.object.isRequired,
};
handleClick = () => {
this.props.onLogout();
};
render() {
return (
<div className="container">
{* notice the className here *}
<button type="button" onClick={this.handleClick}>Logout</button>
</div>
)
}
}
Hope this helps.

Resources