Styling Code Blocks with CSS

12.01.2021

There's something inherently fun about building a new project from scratch. The tech stack, the design, the freedom to experiment with new tools. I decided to revamp my site, building off of the current design and centering it around content, specifically blog posts and YouTube videos. I went back to my old friend Gatsby and I've been impressed with the updates. Getting a project set up with support for styled components and Contentful was a simple as entering a few commands in the terminal with Gatsby's CLI. You can spin up a project in no time using Gatsby's quick start docs. Let's talk about how we can get some code blocks going in these posts.

If you haven't heard of Contentful, it's a slick CMS with an intuitive UI that pairs nicely with Gatsby. I made this video a while back that gives a good overview of getting a project set up, but be sure to check out the Gatsby and Contentful docs for any updates.

Contentful allows you to create a variety of content types. The one I'm using to create the body of this post is called rich text. It's a WYSIWYG editor that includes a code block formatter. I've found that typing out the code in plain text first, then selecting all and clicking the code block button produces the most even spacing. I've tried typing with the code block enabled, but it tends to add quite a bit of extra line-height. Once you get your code lined up in the rich text editor, you can apply a few styles with CSS. These can be in your template file via styled-components, in a CSS module file, or applied globally. Here are my styles that makes use of some CSS variables:

code, pre {
  background: var(--dark-green);
  color: var(--white);
  font-size: 16px;        
  padding: 10px;    
}

pre {
  padding: 16px;
  border-radius: 8px;        
  padding: 16px 0;    
}
Since I'm using Gatsby, I needed to set up a template to handle a few things. Having selected Contentful as my CMS during the initial setup, I had access to the relevant Gatsby plugins out of the box. Here's my basic setup in a file called BlogPost.js:

import { MARKS } from "@contentful/rich-text-types";
import { graphql } from "gatsby";
import { renderRichText } from "gatsby-source-contentful/rich-text";       
const options = {
  renderMark: {
    [MARKS.CODE]: (text) => <pre>{text}</pre>,
  }, 
};
export const data = graphql`
  query ($slug: String!) {
      post: contentfulBlog(slug: { eq: $slug }) {
        slug
        title 
        richText: post {
          raw
        }
        date
        tags
      }
   }
`;
const BlogPost = ({ data }) => {
  return (
    <Layout>
      <Seo title={data.post.title} />
      <Wrapper>
        <Content>
          <div>
            <h1>{data.post.title}</h1>
            <small>{data.post.date}</small>
          </div>
          {renderRichText(data.post.richText, options)}
        </Content>
      </Wrapper>                   
    </Layout>
  );
};
export default BlogPost

I've got styled-components in my project which are being defined elsewhere, but for the purposes of this post you can think of each component as a <div/> and can omit the <Seo/> component. Another helpful perspective can be found in the gatsby-source-contentful docs.

The last piece of the equation involves the gatsby-node.js file. You may have noticed that the graphql query takes in a variable called slug. We can access that variable through context in the createPage function. At the root of your project, create a file called gatsby-node.js if there isn't one set up already. After creating a content type in Contentful called Blog, here's what my gatsby-node.js file looks like:

exports.createPages = async ({ graphql, actions: { createPage } }) => {
  const results = await graphql(
    `     
      {
        allContentfulBlog {
          edges {
            node {
              slug
            }
          }
        }
      }
  `
  );
  if (results.errors) {
    console.error("Error retrieving data", results.errors);
    return;
  }
  results.data.allContentfulBlog.edges.forEach((post) => {
    createPage({
      path: `/blog/${post.node.slug}`,
      component: require.resolve("./src/components/templates/BlogPost.js"),
      context: {
        slug: post.node.slug,
      },
    });
  });
};

The basic idea here is that we pull in our data through a graphql call, loop over our blog posts, and generate a unique page for each post using the provided template. By passing in the slug to our context object, we now have access to that string in our template and can use it to grab our individual posts. With our styles in hand, you're now seeing the end result with styled code blocks and all!