allan00958
allan00958

Reputation: 173

React Styled component: how to add css styles based on props?

I've been trying to find the solution to my problem. I have several Heading Tags (H1, H2 etc) each in their own file. I would like to add some css when calling them based on a prop. Some headings have a small border-bottom and some don't. So, in order to refractor my code, I want to add some css based on a prop. I can't seem to find a way.

Here's an example of Heading H2:

import styled from 'styled-components';
import colors from '../../../../colors'; 
import fonts from '../../../../fonts';
import fontWeights from '../../../../fontWeights';

const HeadingH2 = styled.h2`
  color: ${colors.text};
  font-family: ${fonts.montSerrat};
  font-size: 1.6em;
  font-weight: ${fontWeights.light};
  letter-spacing: 0.2em;
  padding-bottom: 0.7em;
  position: relative;
  text-transform: uppercase;
  text-align: center;
  &:after{
    content: "";
    display: block;
    height: 3px;
    width: 45px;
    background-color: currentColor;
    /* position */
    position: absolute;
    bottom: 0;
    left: 50%;
    transform: translateX(-50%);
  }
`;

export default HeadingH2

Example of calling Heading H2:

import React from 'react';
import HeadingH2 from '../../common/headings/heading_h2';
import HeadingBaseline from '../../common/headings_baseline';


// Features
import {SectionContainer, FeaturesContainer} from './features.style';

import Feature from './feature';
import feature1 from '../../../img/features/feature1.png';
import feature2 from '../../../img/features/feature2.png';
import feature3 from '../../../img/features/feature3.png';

// Text
import Text from '../../../content';

const Features = () => {
  return(
    <SectionContainer id={"what"}>
      <HeadingH2>
        What We Do
      </HeadingH2>
    <HeadingBaseline>
      {Text.headingBaseline}
    </HeadingBaseline>
  <FeaturesContainer>
    <Feature 
      src={feature1} 
      headingText={Text.feature1.heading}
      paragraph={Text.feature1.paragraph}
      />
    <Feature 
      src={feature2} 
      headingText={Text.feature2.heading}
      paragraph={Text.feature2.paragraph}
      />
    <Feature 
      src={feature3} 
      headingText={Text.feature3.heading}
      paragraph={Text.feature3.paragraph}
      />
  </FeaturesContainer>
</SectionContainer>
)
};

export default Features;

I want to extract the following CSS properties

position: relative;
text-transform: uppercase;
text-align: center;
&:after{
 content: "";
 display: block;
 height: 3px;
 width: 45px;
 background-color: currentColor;
 /* position */
 position: absolute;
 bottom: 0;
 left: 50%;
 transform: translateX(-50%);

So, assuming I have the above CSS rule in a separate file, how do I add/import them using props on my styled component HeadingH2.

Thanks for the help :)

Upvotes: 2

Views: 16842

Answers (5)

AWolf
AWolf

Reputation: 8970

You can also use css helper from styled-components to create a SharedStyles.js file.

In the demo you can see it in action. Just using it in a style of an inherited component is not working as expected. If I'm adding it to StyledBase then the variables are not correctly added afterwards (hover style override stops working). That's why I copied ${borderBottom} to each styled component Heading1 / Heading2 instead of adding it to StyledBase.

I think having a level prop for the heading is a good idea but I would handle it differently by creating a HeadingBase component and add your styles to a StyledBase component (also see code in the demo).

The HeadingBase code looks like this:

const HeadingBase = ({ className, children, level = 1 }) =>
    React.createElement(`h${level}`, { className }, children);

It's a component that renders h1,h2,... tags based on the prop level passed (defaults to h1). The h-tag receives className as props (needed for styled-components) and contains the children passed to the component.

SharedStyles.js

import { css } from "styled-components";

export const borderBottom = css`
    &:after{
        content: "";
        display: block;
        height: 3px;
        width: 200px;
        background-color: ${props => props.color || "black"};
        /* position */
        position: absolute;
        bottom: 0;
        left: 50%;
        transform: translateX(-50%);
`;

Then you can import it with import { borderBottom } from "./SharedStyles"; and add it to your styled-component like following:

const Heading1= styled.h1`
    ${borderBottom}
`;

Upvotes: 5

allan00958
allan00958

Reputation: 173

Possible answer:

I add the following CSS rules in a separate file like so. I create a function which returns a string of text. I can import this function and add those rules to any component I wish.

export const borderBottom = () => {
  return `
   position: relative;
   text-transform: uppercase;
   text-align: center;
   &:after{
    content: "";
    display: block;
    height: 3px;
    width: 45px;
    background-color: currentColor;
    /* position */
    position: absolute;
    bottom: 0;
    left: 50%;
    transform: translateX(-50%);
  } 
`
 }

And use it like so on any heading or component that I wish:

import styled from 'styled-components';
import colors from '../../../../colors';
import fonts from '../../../../fonts';
import fontWeights from  '../../../../fontWeights';
import {borderBottom} from '../../../../css';

const HeadingH5 = styled.h5`
  color: ${colors.black};
  font-family: ${fonts.montSerrat};
  font-size: 1em;
  font-weight: ${fontWeights.light};
  letter-spacing: 0.1em;
  padding-bottom: 0.45em;
  margin-bottom: 25px;
  ${borderBottom}
`  
;

export default HeadingH5;

This works for me. Any other suggestions are welcome.

Upvotes: 1

zeduke
zeduke

Reputation: 120

Why not just have a headingLevel prop? and then pass it into the styled component? And just have one StyledHeader styled component as I'm guessing the code is the mostly the same in all the heading styles files? Which is a big no no, you want to always try not to repeat your code.

const Features = () => {
  return(
    <SectionContainer id={"what"}>
      <StyledHeader
        headingLevel={headingLevel}
      >
        What We Do
      </StyledHeader>
    <HeadingBaseline>
      {Text.headingBaseline}
    </HeadingBaseline>
  <FeaturesContainer>
    <Feature 
      src={feature1} 
      headingText={Text.feature1.heading}
      paragraph={Text.feature1.paragraph}
      />
    <Feature 
      src={feature2} 
      headingText={Text.feature2.heading}
      paragraph={Text.feature2.paragraph}
      />
    <Feature 
      src={feature3} 
      headingText={Text.feature3.heading}
      paragraph={Text.feature3.paragraph}
      />
  </FeaturesContainer>
</SectionContainer>
)
};

export default Features;

And in your StyledHeader file

The below function will take your passed in heading level of h1, h2, h3 and will apply a border, if not the above 3 heading level it will be 0 value. I'd do some checks to ensure the value is lower case e.g. toLowerCase()

const getBorder = ({ headingLevel } ) => {
  const headingLevelMap = {
    h1: 0.7,
    h2: 0.6,
    h3: 0.6,
  };

  return headingLevelMap[headingLevel] || 0;
}

const HeadingH2 = styled.headingLevel`
  color: ${colors.text};
  font-family: ${fonts.montSerrat};
  font-size: 1.6em;
  font-weight: ${fontWeights.light};
  letter-spacing: 0.2em;
  padding-bottom: 0.7em;
  border-bottom: ${getCorrectBorderBottom}em solid black
  position: relative;
  text-transform: uppercase;
  text-align: center;
  &:after{
    content: "";
    display: block;
    height: 3px;
    width: 45px;
    background-color: currentColor;
    /* position */
    position: absolute;
    bottom: 0;
    left: 50%;
    transform: translateX(-50%);
  }
`;

I'd also check that if the value of the passed in headingLevel is not any of the 6 heading levels it should have a default value of whatever you want.

The above was just quick pseudo code, but hopefully get the general idea here? Let me know it comments if not.

I'd also recommend that you split your Title component out into a separate component.

Upvotes: 0

Juraj Kocan
Juraj Kocan

Reputation: 2868

You should definely check this: typestyle

the best way you can write dynamic css (for me). Works perfectly with react, even with ssr if you need it...

Upvotes: 0

jsw324
jsw324

Reputation: 812

Something like this works:

const HeadingH2 = styled.h2`
  position: ${props => props.relative && 'relative'};
  padding: ${props => props.paddingBottom ? '0 0 20px 0' : '0'};
}
`;

Then use like this:

<HeadingH2 relative paddingBottom />

Upvotes: 5

Related Questions