CSS-in-JS is a powerful construct for styling with React based applications. In some small cases it can be unnecessary, however as applications grow CSS can become difficult to maintain. Even more so within applications that do theming and or need run-time evaluation CSS, normal CSS can be difficult to use for these cases.
There are a lot of rules around CSS when it comes to specificity, and even the order at which a .css
file was included on the page to determine whether or not a style is applied/overridden. These are generally things you don't really want to have to worry about when building out a large dynamic application. This is where CSS-in-JS libraries like Emotion come into play.
To get going with emotion in a React application we need to install @emotion/styled
.
yarn add @emotion/styled
To use styled we import it into our React component.
import styled from "@emotion/styled/macro";
We use the macro
version which will allow create-react-app
to use the babel-macros babel plugin provided by emotion to run on this file. This gives additional power to emotion when it comes to compiling your app, you can read more about what it allows for you to do here babel-plugin-emotion.
Get the background here and toss it in the public
directory.
This is an arbitrary background that I pulled from Unsplash which is an amazing site for free photos from the community!
const Background = styled.div({ backgroundSize: "cover", backgroundRepeat: "no-repeat", color: "#FFF", position: "relative", width: "500px", height: "350px", cursor: "pointer", backgroundImage: "url(/bg.jpg)", });
We use the styled.div
call which is provided from Emotion that will actually create a React component that will be a <div>
, create a css class, and apply that className
to the div with those styles.
If you were to re-use this Background
the css class won't be re-created and will be reused. So Emotion is smart about creating classes.
Now that we've created it we can just use it like a normal React component.
class App extends Component { render() { return ( <div className="App"> <Background /> </div> ); } }
Now that we have a background created we can create the content that will be displayed when we aren't hovering. We setup our Background
above as a position: 'relative'
so that the rest of our content that is contained in the DisplayOver
will just cover the entire card.
I won't go into any depth of explanation with this css but the key piece to point out is the backgroundColor
is transparent, and we've create a transition
for our background color to transition in the event it changes.
const DisplayOver = styled.div({ height: "100%", left: "0", position: "absolute", top: "0", width: "100%", zIndex: 2, transition: "background-color 350ms ease", backgroundColor: "transparent", padding: "20px 20px 0 20px", boxSizing: "border-box", }); const BigTitle = styled.h2({ textTransform: "uppercase", fontFamily: "Helvetica", });
We also setup our BigTitle
which is just an h2
and apply some css styling. Then once again we use the styled components as normal React components.
<Background> <DisplayOver> <BigTitle>Really Cool Title!</BigTitle> </DisplayOver> </Background>
Now onto the section to appear when we hover. We create a wrapper called Hover
with an opacity of 0
but with a transition so when we hover all the children content will fade in.
const Hover = styled.div({ opacity: 0, transition: "opacity 350ms ease", });
Here is the heart of the animated effects that we see. We position the components by default translated away. This is a translate3d
but only animating the Y
portion of the translate.
With our opacity
on Hover
set to 0
we won't see these so we just move them to the position where they should start. Then later when we hover we can animate them to 0
. So they fade in and slide in from the bottom.
const SubTitle = styled.h4({ fontFamily: "Helvetica", transform: "translate3d(0,50px,0)", transition: "transform 350ms ease", }); const Paragraph = styled.p({ transform: "translate3d(0,50px,0)", transition: "transform 350ms ease", }); const CTA = styled.a({ position: "absolute", bottom: "20px", left: "20px", });
Here have our final DOM structure with our styled components. As you can see there is not a single className
. Just descriptive components. These are components that could have come from anywhere. By that I mean you could have a library of these components for all your text/title/paragraph styling needs.
<Background> <DisplayOver> <BigTitle>Really Cool Title!</BigTitle> <Hover> <SubTitle>You could vacation here!</SubTitle> <Paragraph> More description about this really cool random desert photo from unsplash! </Paragraph> <CTA>View More +</CTA> </Hover> </DisplayOver> </Background>
One cool point I want to make is knowing exactly what styling is being used. Generally with .css
files you're referencing random global names from a file that may come from anywhere.
If you go to the .css
file you may or may not know what or if any of those styles are still being used. This can cause you to hesitate when it comes to refactoring your CSS.
Emotion and other CSS-in-JS libraries utilize JS, and thus the JS import system. So if a specific component isn't being used it becomes very easy to determine that. Not only that with JS bundling systems it wouldn't even include that component in your JS bundle automatically.
:hover
Because we're using the babel macros version of emotion this allows us to reference React components as normal classes. This means :hover
will work as expected. So you wouldn't need to apply any sort of mouse over/mouse out code in React to create this effect.
So because DisplayOver
, SubTitle
, and other components are children of Background
we can configure our style changes when we :hover
over the Background
.
const Background = styled.div({ // Other background code [`:hover ${DisplayOver}`]: { backgroundColor: "rgba(0,0,0,.5)", }, [`:hover ${SubTitle}, :hover ${Paragraph}`]: { transform: "translate3d(0,0,0)", }, [`:hover ${Hover}`]: { opacity: 1, }, });
On our previous components we had applied transitions
for these specific styles we are now changing. So on hover, these styles will change and animate accordingly.
This only scratches the basic surface of Emotion. Emotion can additionally use props passed in to control dynamic styling. You can create the ultimate flexible styling using React components.