idiomatic way to share styles in styled-components? - javascript

trying to port some code from jss to styled-components,
jss code looks something like:
//...
const styles = {
myStyles: {
color: 'green'
}
}
const {classes} = jss.createStyleSheet(styles).attach()
export default function(props) {
return (
<div>
<Widget1 className={classes.myStyles}/>
<Widget2 className={classes.myStyles}/>
</div>
)
}
my question is what would be the idiomatic way to accomplish this sharing of the same styles across multiple components?

You can either share actual CSS strings or share styled-components components:
Share CSS strings:
import {css} from 'styled-components'
const sharedStyle = css`
color: green
`
// then later
const ComponentOne = styled.div`
${sharedStyle}
/* some non-shared styles */
`
const ComponentTwo = styled.div`
${sharedStyle}
/* some non-shared styles */
`
Share actual styled-components:
const Shared = styled.div`
color: green;
`
// ... then later
const ComponentOne = styled(Shared)`
/* some non-shared styles */
`
const ComponentTwo = styled(Shared)`
/* some non-shared styles */
`
Update: Based on questions in the comments, I created an example to show that passing props to styled-components's css function works the same way as passing props to the components themselves: https://codesandbox.io/s/2488xq91qj?fontsize=14. The official recommendation from styled-components is to always wrap strings you will pass to styled-components in the css tag function. In this example, the Test component receives it background and foreground colors through passed-in props embedded in the cssString variable created by invoking the css function.

In addition to the posted answer, you can also create a function that accepts props / theme and returns the css``.
styled-components will check the type of the value provided eg: ${shared} and if its a function it will invoke it with the relevant props / theme.
import styled, {css} from 'styled-components';
const shared = ({theme, myProp}) => css`
color: ${theme.color};
`
/* ------------ */
const Component1 = styled.div`
${shared};
/* more styles ... */
`
const Component2 = styled.div`
${shared};
/* more styles ... */
`

In addition to the 2 answers above, you can also share style between tags as such:
const MyText = styled.div`
color: orange;
`
const MyLink = MyText.withComponent("a")

Related

Pass Styled component as className to another component

I am using react-paginate library that accepts class names in props to style internal components:
<ReactPaginate
...
breakClassName={'break-class-name'}
containerClassName={'pagination-class-name'}
activeClassName={'active-class-name'} />
I am also using styled-components, so I would like to avoid style react components using plain CSS or createGlobalStyle. Is there any way to pass styles from styled-components to breakClassName and containerClassName props of ReactPaginate?
I tried this, but it does not work, because Button.toString() returns class name without styles:
const Button = Styled.button`
color: green;
`
export default () => (
<ReactPaginate
...
breakClassName={Button.toString()} />
)
Code bellow also does not work, because Button has class name my-class-name, but this class name is without styles:
const Button = Styled.button.attrs({ className: 'my-class-name' })`
color: green;
`
export default () => (
<ReactPaginate
...
breakClassName='my-class-name' />
)
Is there any way to do that?
have you tried to make <Button as={Component} />?
UPDATE:
You can use wrapper with classes
const ReactPaginateStyled = styled(ReactPaginate)`
&.break-class-name {
//your styles
}
&.pagination-class-name {
//your styles
}
&.active-class-name {
//your styles
}
`;
I don't think there's a simple way to pass a className down like that. But if you're trying to style a component from a library, they should allow for this pattern to work:
const Button = styled(SomeLibraryComponent)`
color: green;
`;
Styled components will "wrap" around the base component and try to pass styles to it. Most library components should work with this but I can't speak for all of them.

Using a styled component's rules within `createGlobalStyle`

New to styled components and am wondering if someone might have some advice on how to use a styled component's rules within a call to createGlobalStyle?
The below example is working, but I feel that it is not a great solution as componentStyle.rules is not in the official api docs.
// A styled component
import Modal from '../Modal'
import styled, { createGlobalStyle } from 'styled-components'
const StyledComponent = styled(Modal)`
background-color: pink;
`
createGlobalStyle`
// this div is mounted outside of the React root
.modal-from-external-library {
${StyledComponent.componentStyle.rules}
}
`
Not sure if what I was trying to do was possible, but I ended up solving the problem by exporting the css from the Modal using the css function of styled components.
// Modal.js
const styles = css`
// styles here
`
export default styled.div`
${styles}
`
// ... later
const GlobalStyles = createGlobalStyle`${styles}`
render() { return (<GlobalStyles {...props} />) }

Is there a way to theme with emotion without "styled"

With emotion (https://github.com/emotion-js/emotion) I know I can theme with css and styled together with something like:
const carouselCss = ({ theme }) => css`
.. css properties etc
`;
const CarouselDiv = styled('div')`
${carouselCss};
`;
Then in JSX:
<ThemeProvider theme={{ stuff: 'blah' }}>
<CarouselDiv>
..
</CarouselDiv>
</ThemeProvider>
But is there any elegant way to theme with only css - prefer not to use componentized styles because I want to keep to semantic html elements in JSX (also have a massive code base and its easier to migrate from sass to emotion without having to use styled)
I know I can do something like:
const carouselCss = (theme) => css`
.. css properties etc
`;
then jsx:
<div className={carouselCss(this.props.theme)}>
..
</div>
But it means passing a theme prop all the time from the component - which is a little cumbersome
Is there a better way to do this ? Somehow wrap css with something so it has theme vars injected ?
ThemeProvider will get you that. Here is an example with both styled and css options shown :
/** #jsx jsx */
import { jsx } from '#emotion/core'
import styled from '#emotion/styled'
import { ThemeProvider } from 'emotion-theming'
const theme = {
colors: {
primary: 'hotpink'
}
}
const SomeText = styled.div`
color: ${props => props.theme.colors.primary};
`
render(
<ThemeProvider theme={theme}>
<SomeText>some text</SomeText>
<div css={theme => ({ color: theme.colors.primary })}>
some other text
</div>
</ThemeProvider>
)
You could use theme HoC. But if your concern is prop passing to slider, this HoC is basically doing the same, i.e. injects theme into this.props.theme.
Personally, i would use ThemeProvider API if possible, maybe with function-style theme, as pointed out in the docs:
// function-style theme; note that if multiple <ThemeProvider> are used,
// the parent theme will be passed as a function argument
const adjustedTheme = ancestorTheme => ({ ...ancestorTheme, color: 'blue' });

Passing props from react-cosmos to styled-components

I have the following component where I have applied the css using styled-components:
import styled from 'styled-components'
// square-block css can be re-written as a styled-component(???)
const SquareBlock = styled.div`
width: 100%;
height: 100%;
background-color: ${props => props.color};
`
export default SquareBlock
I would like to use the following fixture with react-cosmos to adapt the background-color of the component based on the props:
import { COLORS } from '../../../constants/tetronimo'
export default {
color: COLORS.I
}
In the React developer tools I noticed that the component PropsProxy had a fixture prop which has the color property:
JSON.stringify($r.props.fixture, null, 2)
{
"color": "#3cc7d6"
}
How can I ensure that props are passed correctly to react-cosmos?
Props need to be placed under fixture.props in the latest version of React Cosmos, but you seem to have already figured this out. Does this solve your problem?

How to work with styled components in my react app?

I had trouble naming this question and it seems quite broad, so, forgive me oh moderators. I'm trying out styled components for the first time and trying to integrate it into my react app. I have the following so far:
import React from 'react';
import styled from 'styled-components';
const Heading = styled.h1`
background: red;
`;
class Heading extends React.Component {
render () {
return (
<Heading>
{this.props.title}
</Heading>
);
}
}
export default Heading;
So, just a normal class, but then I import styled components up top, define the const Heading, where I specify that a Heading really is just a styled h1. But I get an error stating that Heading is a duplicate declaration since I also say class Heading....
I'm obviously completely missing something here. All the examples online doesn't actually show how you also use this with React. I.e. where do I define my class, my constructor, set my state, etc.
Do I have to move the styled component into it's own file, i.e.:
import styled from 'styled-components';
const Heading = styled.h1`
background: red;
`;
export default Heading;
Then create a React component that will serve as a wrapper of sorts, e.g. 'HeadingWrapper':
import React from 'react';
import Heading from './Heading';
class HeadingWrapper extends React.Component {
render () {
return (
<Heading>
{this.props.title}
</Heading>
);
}
}
export default HeadingWrapper;
A bit of clarity on this would greatly be appreciated! Thanks :)
styled.h1`...` (for example) returns a React component that works just like <h1>. In other words, you use <h1> like this:
<h1>h1's children</h1>
...so when you do const Heading = styled.h1`...`;, you'll use <Heading> the same way:
<Heading>Heading's children</Heading>
If you want a component that behaves differently, e.g. one that uses the title prop instead of children, you'll need to define such a component, and it will need to have a different name than the Heading component you already defined.
For example:
const styled = window.styled.default;
const Heading = styled.h1`
background: red;
`;
const TitleHeading = ({title}) => <Heading>{title}</Heading>;
// ...or...
class StatefulTitleHeading extends React.Component {
render() {
return <Heading>{this.props.title}</Heading>;
}
}
ReactDOM.render(
<div>
<Heading>I'm Heading</Heading>
<TitleHeading title="I'm TitleHeading"/>
<StatefulTitleHeading title="I'm StatefulTitleHeading"/>
</div>,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://unpkg.com/styled-components#1.4.3/dist/styled-components.js"></script>
<div id="container"></div>
Frankly, though, it makes more sense to just use the component returend by styled.h1 directly:
const Heading = styled.h1`...`;
export default Heading;
// ...then...
<Heading>Children go here</Heading>
The semantics of children are already clear, and using <Heading title="Children go here"/> instead detracts significantly from that.
Let me make this really simple for you.
Let's create one styled component for heading named 'Heading'
const Heading = styled.h1`
color: 'black';
font-size: 2rem;
`
and now you can use it like following.
<Heading>{this.props.title}</Heading>
How you manage to get the title prop as it's child is no concern of style component's. It only manages how that title looks. Styled component is not a container that maintains your app/business logic.
Now let's look at an example in it's entirety.
import styled from 'styled-components'
// Heading.js (Your styled component)
const Heading = styled.h1`
color: 'black';
font-size: 2rem;
`
export default Heading
and now your container that will use your styled component
// Header.jsx (Your container)
class Header extends Component {
componentWillReceiveProps(nextProps) {
// This your title that you receive as props
console.log(nextProps.title)
}
render() {
const { title } = this.props
return (
<div id="wrapper">
<Heading>{title}</Heading>
</div>
)
}
}
I hope that helps. Let me know if you need further clarification.

Categories

Resources