Configurar Gatsby con Styled Component

August 4, 2019 • ☕️ 6 minutos de lectura

Antes de empezar, explicaremos un poco que es Styled Component.

¿Que es Styled Component?

Es una biblioteca que nos ayudará a estilizar nuestros componentes de React. Crea un componente y a su vez define sus estilos.

Gracias a Styled component podremos olvidarnos de colisiones de clases css, nos ayudará a obtener Critical Path CSS más fácilmente y a tener un código css más óptimo.

Instalación de paquetes necesarios

npm install --save gatsby-plugin-styled-components styled-components babel-plugin-styled-components

Ahora debemos editar el fichero gatsby-config.js, para añadir nuestro plugin a la configuración de Gatsby.

module.exports = {
  /* Your site config here */
  plugins: [`gatsby-plugin-styled-components`],
}

Con esto ya podemos empezar a usar Styled Componets para crear nuestros componentes.

⚠️ Ten en cuenta que excepto la instalación, todo los demás, es exáctamente igual para trabajar con React.

Creando variables Gloables

Ahora crearemos el fichero GloablStyle.js para gestionar los estilos globales y lo exportaremos pasar usarlo más adelante.

import { createGlobalStyle } from "styled-components"

const GlobalStyle = createGlobalStyle`
  @import url('https://fonts.googleapis.com/css?family=Lato');
  body {
      font-family: 'Lato', sans-serif;
  }
`

export default GlobalStyle

Debemos importarlo en nuestra plantilla src/components/layout.js, para que envuelva toda la aplicación.

import GlobalStyle from "../GlobalStyle"
;<>
  <GlobalStyle />
  <NavBar />
  <main>{children}</main>
</>

Usando Styled components

Ahora para seguir usando styled-components, pero en los propios componentes, me gusta crear un archivo llamado styles.js

Usamos export para poder usarlos luego en nuestro componente.

import styled from "styled-components"
import { Link } from "gatsby"

export const Anchor = styled(Link)`
  background-image: linear-gradient(180deg, transparent 65%, #fcf113 0);
  background-size: 0 100%;
  background-repeat: no-repeat;
  text-decoration: none;
  box-shadow: none;
  transition: background-size 0.4s ease;
  padding: 0px 10px;
  &:hover {
    background-size: 100% 100%;
    cursor: pointer;
  }
`
export const Enlace = styled.a`
  background-image: linear-gradient(180deg, transparent 65%, #fcf113 0);
  background-size: 0 100%;
  background-repeat: no-repeat;
  text-decoration: none;
  box-shadow: none;
  transition: background-size 0.4s ease;
  padding: 0px 10px;
  &:hover {
    background-size: 100% 100%;
    cursor: pointer;
  }
`

export const Header = styled.div`
  display: flex;
  flex: "1";
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  padding: 15px;
  z-index: 1;
`

Aquí podemos observar como para el component react <Link/>, debo importarlo y luego pasarlo como parámetro a styled(Link)

import styled from "styled-components"
import { Link } from "gatsby"

export const Anchor = styled(Link)`
  // Aquí los estilos
`

En cambio, para los objectos normales como podrían ser a, p, div, span…, solo debemos importar styled-components y con styled.a, sin pasar ningún parámtro.

import styled from "styled-components"

export const Anchor = styled.a`
  // Aquí los estilos
`

Ahora solo debemos importar los componentes que queremos haciendo referencia a nuestro styles.js

import React from "react"
import { Link } from "gatsby"
import { Anchor, Enlace, Header } from "./styles"
export default () => (
  <Header>
    <nav>
      <Anchor to="/">Home</Anchor>
      {` `}
      <Enlace href="https://twitter.com/enmaska">Twitter</Enlace>
      {` `}
    </nav>
  </Header>
)

Si quisiéramos usarlos directamente en nuestro componente

Aquí fíjate que no usamos export

import styled from "styled-components"

const Card = styled.div`
  padding: 24px;
  &:hover {
    -webkit-box-shadow: 0 4px 15px 0 rgba(40, 44, 53, 0.06), 0 2px 2px 0 rgba(40, 44, 53, 0.08);
    box-shadow: 0 4px 15px 0 rgba(40, 44, 53, 0.06), 0 2px 2px 0 rgba(40, 44, 53, 0.08);
    background-color: #fff;
    border-radius: 4px;
    z-index: 3;
    transition: box-shadow 0.5s ease, transform 0.5s ease;
  }
`

export default () => <Card>Contenido</Card>

Si queremos hacer nuestros componentes responsivos, debemos usar @media (max-width: 768px) {}

const CardWrapper = styled.div`
  display: flex;
  flex-direction: row;
  @media (max-width: 768px) {
    flex-direction: column;
  }
`

Pasando propiedades a nuestros componentes.

Otras de las ventajas de usar Styled Components, es que podemos pasarle propiedades a nuestros componentes y hacer estos mucho más dinámicos.

// Objeto estático
const Box = styled.div({
  background: "palevioletred",
  height: "50px",
  width: "50px",
})
// Objecto con propiedades.
const PropsBox = styled.div(props => ({
  background: props.background,
  height: "50px",
  width: "50px",
}))
render(
  <div>
    <Box />
    <PropsBox background="verde" />
  </div>
)

También podemos usar las props para realizar condiciones.

export const Section = styled.section`
  background: ${props =>
    (props.background === "verde" && "#8CBE23") ||
    (props.background === "violeta" && "#207fad")};
  padding-top: 6rem;
  padding-bottom: 6rem;
  position: relative;
`

Temas

Para guardar una lógica en nuestor diseño y que no se vuelva un caos, es una buena práctica dar soporte para theme. Para ello styles-components nos provee ThemeProvider, que es un componente que envolverá los elementos que queremos que hereden las propiedades.

import styled, { ThemeProvider } from "styled-components"

const theme = {
  primaryColor: "#8cbe23",
  secondaryColor: "#999",
  danger: "#cc3300",
  success: "#99cc33",
  warning: "#ffcc00",
}

const Button = styled.button`
background: ${props => props.theme.primaryColor}
padding: 20px;
`

const App = () => (
  <ThemeProvider theme={theme}>
    <Button />

    {/* Puedes sobreescribir el valor */}
    <Button theme={{ primaryColor: "green" }} />
  </ThemeProvider>
)

Referenciando otros componentes

Hay muchas formas de aplicar selectores contextuales css a un componente. En css, cuando queríamos hacer referencia a otro componente, teníamos algo así:

ul # primary-navigation li {
  ...;
}
div.modal-window div.content-container {
  ...;
}
body#night-time div.modal-window-content-container {
  ...;
}

Esto era un poco engorroso, por no hablar que si cambiamos el #id o la clase del componente, la liamos parda… pero styled-components nos hace la vida mucho más fácil.

Imaginemos que queremos cambiar el color de un icono al pasar el ratón por encima de un enlace.

const Link = styled.a`
  display: flex;
  align-items: center;
  padding: 5px 10px;
  background: papayawhip;
  color: palevioletred;
`
const Icon = styled.svg`
  flex: none;
  transition: fill 0.25s;
  width: 48px;
  height: 48px;
  ${Link}:hover & {
    fill: rebeccapurple;
  }
`
const Label = styled.span`
  display: flex;
  align-items: center;
  line-height: 1.2;
  &::before {
    content: "◀";
    margin: 0 10px;
  }
`
render(
  <Link href="#">
    <Icon viewBox="0 0 20 20">
      <path d="M10 15h8c1 0 2-1 2-2V3c0-1-1-2-2-2H2C1 1 0 2 0 3v10c0 1 1 2 2 2h4v4l4-4zM5 7h2v2H5V7zm4 0h2v2H9V7zm4 0h2v2h-2V7z" />
    </Icon>
    <Label>Hovering my parent changes my style!</Label>
  </Link>
)

Animaciones

import React from "react"
import { render } from "react-dom"
import styled, { keyframes } from "styled-components"

const fadeIn = keyframes`
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
`

const FadeIn = styled.div`
  display: inline-block;
  animation: ${fadeIn} 2s linear infinite;
  padding: 2rem 1rem;
  font-size: 1.2rem;
`

export default () => (
  <div>
    <FadeIn>💅</FadeIn>
  </div>
)