Heading component in styled system

863 Views Asked by At

How can I create a generic heading component using styled system and styled components?

The following works but all my heading use h1 tag.

import css from "@styled-system/css"
import styled from "styled-components"
import { color, ColorProps, textAlign, TextAlignProps, variant } from "styled-system"

type Props = {
  level: 1 | 2 | 3 | 4 | 5 | 6
} & TextAlignProps &
  ColorProps

const Css = css({
  fontFamily: "heading",
  fontWeight: "heading",
})

const Variant = variant({
  prop: "level",
  variants: {
    1: {
      fontSize: 7,
      lineHeight: 7,
    },
    2: {
      fontSize: 6,
      lineHeight: 6,
    },
    3: {
      fontSize: 5,
      lineHeight: 5,
    },
    4: {
      fontSize: 4,
      lineHeight: 4,
    },
    5: {
      fontSize: 3,
      lineHeight: 3,
    },
    6: {
      fontSize: 2,
      lineHeight: 2,
    },
  },
})

const Heading = styled("h1")<Props>(Css, Variant, textAlign, color)

export default Heading

I tried to create a HeadingBase component like:

type Props = {
  level: 1 | 2 | 3 | 4 | 5 | 6,
  as: string | undefined
} & TextAlignProps &
  ColorProps

const HeadingBase = ({ level, as: Component = `h${level}`, ...props }: Props) => <Component {...props} />

const Heading = styled(HeadingBase)<Props>(Css, Variant, textAlign, color)

But I get an error at the <Component {...props} /> because it doesn't have an API for TextAlignProps and ColorProps.

1

There are 1 best solutions below

0
On

Perhaps you can leverage as so that when a variant is provided, the as key automatically changes to the variant string. This might be easier to explain by code, take a look.

const headingVariant = ({ fontSize }) => {
  let variantConfig = {
    h1: {
      fontSize: [7, 8], //28px 32px
    },
    h2: {
      fontSize: [6, 7], //24px 28px
    },
    h3: {
      fontSize: ['22px', 6], //22px 24px
    },
    h4: {
      fontSize: 5, //20px
    },
    h5: {
      fontSize: 4, //18px
    },
  };

  if (fontSize) {
    variantConfig = Object.keys(variantConfig).reduce((acc, currKey) => {
      acc[currKey] = {
        ...variantConfig[currKey],
        fontSize,
      };
      return acc;
    }, {});
  }
  return variant({
    prop: 'variant',
    variants: variantConfig,
  });
};

const Heading = styled(BaseText).attrs(
  ({ variant: hybridVariant, as: asOverride }) => ({
    as: asOverride ?? hybridVariant,
  })
)(
  ({ isBold, color }) =>
    css({
      fontFamily: `sofia-pro, Helvetica, Arial`,
      lineHeight: '130%',
      color: color || 'text',
      fontWeight: isBold ? 1 : 0,
      fontStyle: 'normal',
    }),
  headingVariant
);

Heading.propTypes = {
  isBold: PropTypes.bool,
  variant: PropTypes.string,
};

Heading.defaultProps = {
  isBold: true,
  variant: 'h2',
};

export default Heading;