Evite problemas de SEO e Acessibilidade com um componente de tipografia mutante usando React e Styled Components 🦖

Tipografias Para entender este artigo, presumo que você conheça React, Typescript e o básico da biblioteca Styled Component.

Todos nós sabemos o impacto que uma tag errada no lugar errado pode causar no SEO e na Acessibilidade, é um erro alterar apenas o tamanho da fonte em um componente de tipografia e não a sua tag HTML. Como resultado disso, decidi criar um componente de tipografia dinâmico para que eu pudesse definir a tag HTML específica para os momentos certos através de suas propriedades.

Spiderman Meme

Mãos a obra!

Começaremos criando nosso componente dentro de uma pasta Typography com um arquivo para o componente e outro para os estilos.

Pastas do projeto

Vamos começar com o arquivo index. Bora definir algumas regras para bloquear tags que não queremos receber, para isso podemos criar uma interface com todas as tags que queremos para nosso componente.

import { ReactNode, CSSProperties } from 'react'

import * as S from './styled'

// Type/Interface para evitar tags que não queremos como 'divs' e.g
type TagVariants = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span'

type TypographyProps = {
  tag?: TagVariants
  children: ReactNode
}

/**
 * @tag will have default 'p' since it'll probably be our most used tag
 */
const Typography = ({ tag = 'p', children }: TypographyProps) => (
  <S.DynamicTypography tag={tag}>
    {children}
  </S.DynamicTypography>
)

export default Typography

Torne-o dinâmico

A mágica acontece no arquivo styled.ts, ao invés de definir uma tag HTML específica após styled, passaremos uma função de callback em sua propriedade para pegar os props necessários e utilizaremos o createElement() que você pode importar do react para criar um elemento HTML com os valores que a gente pegou das props. Prontinho, agora temos um componente dinamico de tipografia 🥳

import { createElement } from 'react'

import styled from 'styled-components'

export const DynamicTypography = styled(({ tag, children, ...props }) =>
  createElement(tag, props, children),
)`
    /* seu css default aqui */
`

Estilizando nosso componente

Uma coisa legal e clean que podemos fazer para passar propriedades CSS para o nosso componente é definir o operador spread …props dentro da propriedade style da nossa tag DynamicTypography e colocar no componente Typography a interface CSSProperties que você pode importar de react, desta forma bloqueamos nosso componente de receber props que não estão listados em nossas interfaces TypographyProps e em CSSProperties.

Nota: Agora todos os outros props que não estão listados em nossa interface TypographyProps devem fazer parte de CSSProperties, logo, se você quiser adicionar event listeners por exemplo ou qualquer outro prop que queira usar além do CSS, você deve listá-lo no TypographyProps!


import { ReactNode, CSSProperties } from 'react'

import * as S from './styled'

// Type/Interface to avoid unwanted tags such as 'divs' e.g.
type TagVariants = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span'

type TypographyProps = {
  tag?: TagVariants
  children: ReactNode
}

/**
 * @tag vai ter o 'p' como padrão já que provavelmente vai ser nossa tag mais usada
 * @props Contem todo nosso CSS
 */
const Typography = ({ tag = 'p', children, ...props }: TypographyProps 
& CSSProperties) => (
  <S.DynamicTypography tag={tag} style={{ ...props }}>
    {children}
  </S.DynamicTypography>
)

export default Typography

Agora não precisamos mais enviar um objeto de style ou passar valores CSS como color por exemplo duas vezes até chegar em nosso arquivo de styled comoponents como uma prop, já que teria que passar primeiro dentro do componente e depois no arquivo com os estilos. Nosso componente pode definir CSS como propriedades diretamente na instância do componente.

Instanciando nosso componente

Podemos finalmente instanciar nosso componente de tipografia dinâmico e definir as tags certas nos lugares certos! 🎉

import React from 'react'

import Typography from '@/components/typography'

const Home = () => {
  return (
    <Typography tag="h1" color="Highlight" fontFamily="sans-serif">
      Hello World!
    </Typography>
  )
}

export default Home

Repositório no Github

Deixei a fonte com o link do Medium pois postei lá antes