Looking for work! Check my resume here.

· 4 min read

Astro + Rehype Pretty Code

Applying Tailwind styles to your code snippets with light & dark modes.

This an extension on this other guy’s post so read that first if you need to setup everything from scratch.

Background

Astro has been chugging along with new releases at quite a pace and with it came some changes in version 4 regarding plugins. I believe this is what killed my code snippet styling with additional factors from upgrade of shiki to version 1.0 in the background behind rehype-pretty-code so all of my original css has needed changing. This post documents the configuration changes needed to make it work again with the latest Astro 4, Tailwind and rehype-pretty-code plugin.

Configuring Rehype Pretty Code Plugin

My webiste supports dark and light mode code snippets. The new way to support multiple themes is shown below with the imported themes I copied from their respective repo and copy/pasted into my project. Also, I had to update my Tailwind integration’s configuration shown below.

astro.config.mjs
import dark_theme from './src/assets/themes/remedy-dark.json' assert { type: 'json' };
import light_theme from './src/assets/themes/remedy-light.json' assert { type: 'json' };
 
const prettyCodeOptions = {
  theme: {
    dark: dark_theme,
    light: light_theme,
  },
  onVisitLine(node) {
    // Prevent lines from collapsing in `display: grid` mode and allow empty lines
    if (node.children.length === 0) {
      node.children = [
        {
          type: 'text',
          value: '',
        },
      ];
    }
  },
  ...
}
 
export default defineConfig({
  ...
  integrations: [
    tailwind({
      applyBaseStyles: false,
      nesting: true,
    }),
    ...
  ],
  ...
});

I also found I could remove my previous custom onVisitHightlightedLine and onVisitHighlightedWord hooks as this new Shiki version automatically adds data attributes for highlighted lines and characters which I now use in my new css selectors.

Styling Code Snippets with Tailwind

Dark Mode

Again, I followed this blog post becuase it was more clean than my original implementation so I won’t repeat everything but it lacked support for dark mode so I made a few customizations to fit my setup. Firstly, to get dark mode working I added the following to the bottom of my css for code snipets.

base.css
html pre span {
  color: var(--shiki-light);
}
 
html.dark pre span {
  color: var(--shiki-dark);
}

Hightlighted Lines

Tailwind adds the dark class on the root html element so this snippet changes the Shiki code theme when dark mode is active. There are a few ways to do this based on the shiki style guide but this works for me and we should not include the background-color: var(--shiki-foo) as that will get in the way of the css that highlights lines of code. Oh, and with this implementation !important is not required.

Making custom line hightlighting for dark mode is easy since we can still use the regular dark: syntax.

base.css
pre {
  @apply mx-auto overflow-auto p-4;
  @apply scrollbar scrollbar-h-2 scrollbar-track-rounded-md scrollbar-thumb-rounded-sm scrollbar-thumb-slate-200 dark:scrollbar-thumb-slate-800;
 
  [data-line] {
    @apply mx-0 px-2 leading-tight;
  }
 
  [data-highlighted-line] {
    @apply bg-orange/20 dark:bg-orange-500/10 border-l-orange-500 border-l-2 border-opacity-50;
  }
 
  [data-highlighted-chars] {
    @apply rounded-md py-0 px-1.5 border-b-2 border-opacity-75 bg-opacity-30 dark:bg-opacity-20;
  }
 
  mark {
    @apply text-inherit bg-orange-500 border-b-orange-500;
  }
}

Background Color

Then I ran into trouble configuring a custom background color for code snippets in dark mode. Eventually, I realized because I’m using Tailwind’s typography plugin my code snippets are within the prose so the css defined by the prose class which overwrites other background styles. So I added a line with prose-pre classes to define and now we have our defined dark mode code snippet background.

base.css
[data-rehype-pretty-code-figure] {
  @apply relative;
  @apply rounded-lg bg-slate-200 dark:bg-slate-700;
  @apply prose-pre:bg-slate-50 dark:prose-pre:bg-slate-900;
}

Group Highlighted Characters

This didn’t break but I wanted to add this as an addition to the aforementioned blog post for how to highlight custom colors based on characters with a specific ID.

base.css
[data-chars-id='1'] {
  @apply bg-pink-500  border-b-pink-500;
}
 
[data-chars-id='2'] {
  @apply bg-emerald-500 border-b-emerald-500;
}
 
[data-chars-id='3'] {
  @apply bg-blue-500 border-b-blue-500;
}

In the previous section additional character highlighting styling is configured.

(Mostly) Complete CSS Demo

Besides the parts outside of base.css mentioned earlier this is the majority of the css for my code snippet styling. It’s mostly specific style preferences I made for myself but I thought it was good to include the full context for this post.

base.css
[data-rehype-pretty-code-figure] {
  @apply relative;
  @apply rounded-lg bg-slate-200 dark:bg-slate-700;
  @apply prose-pre:bg-slate-50 dark:prose-pre:bg-slate-900;
}
 
[data-rehype-pretty-code-title] {
  @apply font-mono text-sm text-slate-900 dark:text-white ps-2 py-1;
}
 
[data-rehype-pretty-code-title] + pre {
  @apply m-0 px-0 py-1 rounded-none rounded-b-lg;
}
 
pre {
  @apply mx-auto overflow-auto p-4;
  @apply scrollbar scrollbar-h-2 scrollbar-track-rounded-md scrollbar-thumb-rounded-sm scrollbar-thumb-slate-200 dark:scrollbar-thumb-slate-800;
 
  [data-line] {
    @apply mx-0 px-2 leading-tight;
  }
 
  [data-highlighted-line] {
    @apply ps-1.5 bg-orange/20 dark:bg-orange-500/10 border-l-orange-500 border-l-2 border-opacity-50;
  }
 
  [data-highlighted-chars] {
    @apply rounded-md py-0 px-1.5 border-b-2 border-opacity-75 bg-opacity-30 dark:bg-opacity-20;
  }
 
  mark {
    @apply text-inherit bg-orange-500 border-b-orange-500;
  }
}
 
[data-chars-id='1'] {
  @apply bg-pink-500  border-b-pink-500;
}
 
[data-chars-id='2'] {
  @apply bg-emerald-500 border-b-emerald-500;
}
 
[data-chars-id='3'] {
  @apply bg-blue-500 border-b-blue-500;
}
 
[data-rehype-pretty-code-figure] code {
  @apply grid gap-0 font-mono;
 
  &[data-line-numbers] {
    counter-reset: line;
 
    > [data-line]::before {
      counter-increment: line;
      content: counter(line);
 
      @apply ml-2 mr-4 inline-block w-4 text-right text-slate-400 dark:text-slate-700;
    }
  }
}
 
html pre span {
  color: var(--shiki-light);
}
 
html.dark pre span {
  color: var(--shiki-dark);
}
  • astro
  • tailwind
Share:
Back to Blog