Scope Your CSS With Styled-Components

Have you ever accidentally reused the name of a CSS class in another component? Or found that style was applied to an element without knowing where it came from? If so, you know just how frustrating it can be! 😬

The issue of scopingĀ style to the relevant components is a big one!Ā 

What does scope mean?Ā 

Scope refers to your code's parts that have access to an element, such as a variable or CSS class. It can be global (like for the CSS classes just mentioned) or only concern a specific bit of code.

Different solutions are available for scoping style, like CSS architecture methodologies or specific tools such as Sass (which requires a preprocessor). However, in recent years,Ā CSS in JSĀ has emerged as a solution.Ā Ā 

Discover CSS in JS

As its name suggests, CSS in JS is generated with JavaScript. It is inserted into the DOM in a Ā <style>Ā  element.

Is it like the inline style from the chapter "Incorporate Style and Assets in Your Project" in the previous course?

Well, not really. Inline style is inserted into DOM on theĀ  Ā styleĀ  attribute of a specific elementĀ  Ā (<div style={{ color: 'red' }} />Ā ). Moreover, inline style doesn’t allow pseudo-selectors. That’s not the case for CSS in JS, where you can use as many pseudo-selectors as necessary (more about this later).

But with CSS in JS, style is attached to a specific componentĀ directly within the same file. It's much easier when you need to delete or modify an existing style, isn’t it?Ā 

There are lots of solutions for CSS in JS, and they all have their own syntax. We’re going to focus on the styled-components library. šŸ’…

Install the library with Ā yarn add styled-componentsĀ  :

Styled-components installation
Styled-components installation

Now let’s think about the style we want to create. šŸ‘€

Apply the Logic of Styled-Components

Everything is a component with styled-components. So let's create one to see what I mean.

Create a style for theĀ labelĀ in Ā Card/index.jsxĀ  .

Code lines added for import and constant.
Creating our styled component CardLabel.

That gives you:

import styled from 'styled-components'

const CardLabel = styled.span``

Ā And then reuseĀ  Ā CardLabelĀ Ā  directly in the JSX:

<CardLabel>{label}</CardLabel>

What’s up with this weird syntax? 😬

Don’t worry, Ā styled-componentsĀ  uses template literals, which you can read about in Mozilla's documentation. You can write your CSS directly inside, giving you:Ā 

const CardLabel = styled.span`
   color: #5843e4;
   font-size: 22px;
   font-weight: bold;

Ā Let’s take this opportunity to add some style toĀ the image. This time, still in the same Ā Card/index.jsxĀ  file,Ā you get:

const CardImage = styled.img`
   height: 80px;
   width: 80px;
   border-radius: 50%;
`

Then use it in the code:

<CardImage src={picture} alt="freelancer" />

Great work! You’ve generated the Ā spanĀ  andĀ  Ā imgĀ  elements and added style with Ā styled-componentsĀ  ! šŸŽ‰ As you’ve no doubt guessed, it doesn’t end with Ā spanĀ  andĀ  Ā imgĀ  : you can generate all existing DOM elements this way (and more).

What would I do if I wanted to style an element that came from a library?

Styled-components allows for this! If you take the example ofĀ  Ā Header/index.jsxĀ  , you just need to do the following:

import { Link } from 'react-router-dom'
import styled from 'styled-components'

const StyledLink = styled(Link)`
   padding: 15px;
   color: #8186a0;
   text-decoration: none;
   font-size: 18px;
`

function Header() {
   return (
      <nav>
         <StyledLink to="/">Home</StyledLink>
         <StyledLink to="/survey/1">Survey</StyledLink>
         <StyledLink to="/freelancers">Profiles</StyledLink>
      </nav>
   )
}

export default Header

Try this code to see how it rendersĀ  – not bad, is it?

Pass Props Into Your CSS

Very nice – you created styled-components and used them, but what other advantages are there in writing style with JS?

Just that – we’re using JS. šŸ˜Ž You’ll be able to pass props to components directly from the React component.

Let’s see what that looks like in practice with a Ā HeaderĀ  .

<StyledLink to="/survey/1" $isFullLink>
   Run the test
</StyledLink>

Here we’re passing the prop Ā $isFullLinkĀ  . This allows you to use the prop directly in the style:

const StyledLink = styled(Link)`
   padding: 15px;
   color: #8186a0;
   text-decoration: none;
   font-size: 18px;
   ${({props} ) =>
      props.$isFullLink &&
      `color: white; border-radius: 30px; background-color: #5843E4;`}
`

What’s Ā $Ā  ?

This signals to Ā styled-componentsĀ  that you’re using your prop for style, and that it should not be passed in the DOM.

The Ā $Ā  is only required to pass a prop if it's a React component (and not an HTML element), like here for Ā LinkĀ  . If the styled-component was based on a simple Ā aĀ  tag, it would be no issue to use the prop Ā isFullLinkĀ  without the Ā $Ā  .

Learn about using state in props in the screencast below:

Pretty cool! 🤩

Use Variables

As you can see, in the last code snippet just above the screencast, we’ve used the color purple Ā #5843EĀ  again. JS allows you to use variables, so let’s use them toĀ store colors!

It is possible to declare aĀ  Ā colorsĀ  object that takes all of the colors in the app, but it’s better to create a theme managed byĀ Ā styled-componentsĀ  .

Let’s create aĀ Ā  /utilsĀ folder directly inĀ  Ā src/Ā . Then, inside that, put theĀ /styleĀ  folder. And then again in that create theĀ Ā colors.jsĀ  file, which gives you:

ā”œā”€ā”€ assets │
    └── profile.png
ā”œā”€ā”€ components 
│     ā”œā”€ā”€ Card 
│     │ Ā  └── index.jsx
│     ā”œā”€ā”€ Error
│     │ Ā  └── index.jsx
│     └── Header
│        └── index.jsx
ā”œā”€ā”€ index.jsx
ā”œā”€ā”€ pages
│ Ā  ā”œā”€ā”€ Freelances
│ Ā  │ Ā  └── index.jsx
│ Ā  ā”œā”€ā”€ Home
│ Ā  │ Ā  └── index.jsx
│ Ā  ā”œā”€ā”€ Results
│   │ Ā  └── index.jsx
│ Ā  └── Survey
│ Ā  Ā  Ā  └── index.jsx
└── utils
└── style
└── colors.js

Define the colors in Ā colors.jsĀ :

const colors = {
   primary: '#5843E4',
   secondary: '#8186A0',
   backgroundLight: '#F9F9FC',
}
export default colors

To use them, import them in the string template directly:

const StyledLink = styled(Link)`
   padding: 15px;
   color: #8186a0;
   text-decoration: none;
   font-size: 18px;
   ${(props) =>
      props.$isFullLink &&
      `color: white; border-radius: 30px; background-color: ${colors.primary};`}
`

Job done! šŸ’…

How would I create a hover effect for when the mouse goes over my component?

That’s easy because pseudo-selectors work in our styled-components.

To put all of this into practice, let’s go back toĀ CardsĀ  and add a bit of style so the hover effect is easier to see. In Ā pages/freelancers.jsxĀ  , put:

const CardsContainer = styled.div`
   display: grid;
   gap: 24px;
   grid-template-rows: 350px 350px;
   grid-template-columns: repeat(2, 1fr);
`

WhichĀ you can use in the same file:

function Freelancers() {
   return (
      <div>
         <h1>Freelancers šŸ‘©Ā·šŸ’»šŸ‘ØĀ·šŸ’»šŸ‘©Ā·šŸ’»</h1>
         <CardsContainer>
            {freelanceProfiles.map((profile, index) => (
               <Card
                  key={`${profile.name}-${index}`}
                  label={profile.jobTitle}
                  title={profile.name}
               />
            ))}
         </CardsContainer>
      </div>
   )
}

Then inĀ Card/index.jsxĀ  ,Ā you can create a shadow effect when the mouse hovers over the component. To do this, create a Ā CardWrapperĀ  thatĀ will replace the previousĀ  Ā divĀ  :

function Card({ label, title, picture }) {
   return (
      <CardWrapper>
         <CardLabel>{label}</CardLabel>
         <CardImage src={picture} alt="freelancer" />
         <span>{title}</span>
      </CardWrapper>
   )
}

And define Ā CardWrapperĀ  as follows:

const CardWrapper = styled.div`
   display: flex;
   flex-direction: column;
   padding: 15px;
   background-color: ${colors.backgroundLight};
   border-radius: 30px;
   width: 350px;
   transition: 200ms;
   &:hover {
      cursor: pointer;
      box-shadow: 2px 2px 10px #e2e3e9;
   }
`

The syntax   &:hover  allows you to access the pseudo-selector for mouse hover, and now you have the desired effect! 🤩

When hovering with the mouse, the shadow effect is visible
The shadow hover effect

Create a Global Style

We’ve already covered a lot to do with styled-components, but we can’t cover everything. However, let's create a global style that gives the app a basic style, especially font or other CSS properties.

To do this, create a GlobalStyle component in Ā index.jsxĀ  at the project root:

const GlobalStyle = createGlobalStyle`
   div {
      font-family: 'Trebuchet MS', Helvetica, sans-serif;
   }
`

And import it in all of your components:

<Router>
   <GlobalStyle />
   <Header />
      …
</Router>

And there you have it! You just used styled-components to add some style to the application. šŸŽ‰

Give It a Go!

The time has come for you to work independently with theĀ  styled-components library. You’ll find the codebase you need to start the exercise on branch P1C5-begin of the project on GitHub.

This time, use the Figma prototype and create style for:Ā 

  • The header (including the logo, positioning of links).

  • The homepage (including text, illustration, background, etc.).

  • The profiles page (text above the Cards).

  • The error page.

As usual, you’ll find the solution to this exercise on branch P1C5-solution. 🤫

Let’s Recap!

  • Styled-components is a CSS in JS library that can be used to create style by applying component logic.Ā 

  • String templates written between Ā ``Ā  allow you to use variables and pass props.Ā 

  • You can create a global style with Ā createGlobalStyleĀ  .

Great work! Now that you’ve added a bit of style, the app that you’re coding for Shiny Agency is really starting to take shape. ✨

Test your knowledge with the part 1 quiz. Good luck! šŸ€

Ever considered an OpenClassrooms diploma?
  • Up to 100% of your training program funded
  • Flexible start date
  • Career-focused projects
  • Individual mentoring
Find the training program and funding option that suits you best