Adding Line Numbers to Astro's Syntax Highlighting Using CSS
10/04/23
Astro comes with Shiki syntax highlighting out of the box, which is great. But it’s missing a core feature you get in most IDEs by default: line numbers.
Not only do they provide you a sense of position in the document, but they also help when lines get wrapped over. Then you’ll see a gap in the number sequence and intuitively know that this line belongs to the one above.
It’s CSS Only
The awesome part is that this can be achieved with CSS only.
NOTE: I use the Tailwind CSS integration for Astro. If you don’t use Tailwind, you can just translate those classes to CSS directly.
My solution is based on this Github comment, kudos to the author.
Add the following lines to your global CSS rules.
pre {
@apply rounded-md pl-8;
}
pre code {
@apply block leading-tight p-2 pl-1;
font-size: 17px;
border-left: 1px solid rgba(115, 138, 148, 0.4);
counter-reset: step;
counter-increment: step 0;
}
pre code .line {
@apply relative;
}
pre code .line::before {
@apply absolute overflow-hidden w-7 h-4 -left-9 top-0 text-right;
content: counter(step);
counter-increment: step;
color: rgba(115, 138, 148, 0.4);
}
That’s it!
Bonus Round - SASS-like Rule Nesting
I find the previous approach a little verbose. It would be much nicer to just nest the CSS rules into each other. Astro ships with PostCSS by default, so there is no reason why we shouldn’t be able to do it. However, the issue with using it together with TailwindCSS is, that it can’t handle the tailwinds special syntax out of the box. Luckily Tailwind provides us with a compatibility layer for PostCSS nesting.
For this to work we need to take control over the PostCSS config. We can do this by specifying a postcss.config.cjs
file in the project root with the following contents.
module.exports = {
plugins: {
'postcss-import': {},
'tailwindcss/nesting': {},
tailwindcss: {},
autoprefixer: {},
cssnano: {},
},
};
I kept the other things, like autoprefixer
, postcss-import
and cssnano
because they were provided in the examples and I think they are also used by Astro’s default config, but I’m not sure about that. Anyhow, they sound like a good idea to have. For this we need to install those modules, because if we use it, it should be declared as a dependency.
npm install -D postcss-import autoprefixer cssnano
Now we can enable nesting together with our TailwindCSS @apply
syntax. It doesn’t make it shorter, but it helps a lot with readability and structuring the code.
pre {
@apply rounded-md pl-8;
code {
@apply block leading-tight p-2 pl-1;
font-size: 17px;
border-left: 1px solid rgba(115, 138, 148, 0.4);
counter-reset: step;
counter-increment: step 0;
.line {
@apply relative;
&::before {
@apply absolute overflow-hidden w-7 h-4 -left-9 top-0 text-right;
content: counter(step);
counter-increment: step;
color: rgba(115, 138, 148, 0.4);
}
}
}
}