This is post that shows syntax highlighting
Code Block
This is my first code block:
new text here sadsa
const multiply = (a, b) => a * b;
const multiply = (a, b) => a * b;
multiply(2, 2); // 4
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "../styles/syntax-highlighting.css";
html,
body {
font-smooth: always;
}
import {Manrope} from "@/lib/font/manrope";
import {
allPosts
} from "contentlayer/generated";
import {useLiveReload, useMDXComponent} from "next-contentlayer/hooks";
import {notFound} from "next/navigation";
export default function BlogPage({ params }: { params: { slug: string } }) {
const currentPost = allPosts.find((post) => post.slug === params.slug);
const Content = useMDXComponent(String(currentPost?.body?.code));
if (!currentPost) {
return notFound();
}
return (
<div className="mt-12 mb-20 max-w-lg">
<h1 className="text-2xl">{currentPost.title}</h1>
<div className={Manrope.className}>
<Content />
</div>
</div>
);
}
# Result
CSS only line numbers with CSS counters.
...
<div>
<div className="valkyrie overflow-hidden rounded-lg">
<pre className="relative py-2 text-sm leading-7 before:pointer-events-none before:absolute before:inset-0 before:bg-gradient-to-r before:from-transparent before:via-transparent before:to-gray-900/90">
{/* prettier-ignore */}
<code className="grid [&>span]:px-3">
<span>
<span style={{ color: "rgb(198, 120, 221)" }}>export</span><span style={{ color: "rgb(171, 178, 191)" }}> </span><span style={{ color: "rgb(198, 120, 221)" }}>default</span><span style={{ color: "rgb(171, 178, 191)" }}> </span><span style={{ color: "rgb(198, 120, 221)" }}>function</span><span style={{ color: "rgb(171, 178, 191)" }}> </span><span style={{ color: "rgb(97, 175, 239)" }}>Page</span><span style={{ color: "rgb(171, 178, 191)" }}>() {</span>
</span>
<span>
<span style={{ color: "rgb(171, 178, 191)" }}> </span><span style={{ color: "rgb(198, 120, 221)" }}>return</span><span style={{ color: "rgb(171, 178, 191)" }}> <</span><span style={{ color: "rgb(224, 108, 117)" }}>h1</span><span style={{ color: "rgb(171, 178, 191)" }}>>Hello world!</</span><span style={{ color: "rgb(224, 108, 117)" }}>h1</span><span style={{ color: "rgb(171, 178, 191)" }}>></span>
</span>
<span>
<span style={{ color: "rgb(171, 178, 191)" }}>}</span>
</span>
</code>
</pre>
</div>
</div>
```html
<pre>
<code>
<span>export default function Page() {</span>
<span> return <h1>Hello world!</h1></span>
<span>}</span>
</code>
</pre>
Step 1
Let's start simple. Style the pseudo-element that will house our line numbers using the :before
modifier.
export default function Page() { return <h1>Hello world!</h1>}
<pre>
<code>
<span className="before:mr-3 before:text-gray-500 before:[content-'1']">
export default function Page() {
</span>
<span> return <h1>Hello world!</h1></span>
<span>}</span>
</code>
</pre>
Regardless of how you create your code blocks, it is unlikely you will have direct access to the class of each line of code. We can move our line number styling to the parent <code>
element and use Tailwind's x to target child elements.
We can target direct <span>
descendants of our <code>
element using the special Tailwind [&>span]
selector. This will allow us to target each line of code from the parent element. Look Ma, we're vanilla CSS now.
export default function Page() { return <h1>Hello world!</h1>}
<pre>
<code className="
[&>span]:px-3
[&>span]:before:mr-3 [&>span]:before:text-gray-500
[&>span]:before:[content:'1']">
<span>
export default function Page() {
</span>
<span> return <h1>Hello world!</h1></span>
<span>}</span>
</code>
</pre>
The question is, how do we create dynamic values in CSS? CSS counters to the rescue.
export default function Page() { return <h1>Hello world!</h1>}
<pre>
<code className="
grid
[&>span]:px-3
[&>span]:before:mr-3 [&>span]:before:text-gray-500
[counter-reset:line]
[&>span]:before:[content:counter(line)]
[&>span]:before:[counter-increment:line]
">
<span>
export default function Page() {
</span>
<span> return <h1>Hello world!</h1></span>
<span>}</span>
</code>
</pre>
Finishing touches
export default function Page() { return <h1>Hello world!</h1>}
<pre>
<code className="
grid
[&>span]:px-3
[&>span]:before:mr-3 [&>span]:before:text-gray-500
[&>span]:before:inline-block [&>span]:before:w-4 [&>span]:before:text-right
[counter-reset:line]
[&>span]:before:[content:counter(line)]
[&>span]:before:[counter-increment:line]
">
<span>
export default function Page() {
</span>
<span> return <h1>Hello world!</h1></span>
<span>}</span>
</code>
</pre>
- Account for two digits
- Right align so numbers
Add a placeholder, we'll figure out how to make it dynamic later.
- To keep things simple at the start, we'll style a single line of code
- Style the pseudo-element of
- Establish
- Create a new pseudo-element to house the line numbers using the
:before
modifier. - Arbitrary values allow use to add custom CSS rules not included in Tailwind's pre-defined utility classes.
- Fill the
content
of the pseudo-element with a placeholder value for now.
Arbitrary values allow us to add custom CSS rules not included in Tailwind's pre-defined utility classes. We can use this feature to add a placeholder value to the content
of a pseudo-element.
<pre>
<code>
<span className="before:mr-3 before:inline-block before:w-4 before:text-gray-500 before:[content:'1']">
Line
</span>
</code>
</pre>
XXX
I know I should go to jail for this, but I prefer inline Tailwind classes and will go to extreme lengths not to touch a CSS file. If you find this abhorrent, you can find the vanilla CSS code below.
However, this simply adds a data attribute to indicate the option should be enabled and doesn't add any HTML elements to the code block.
Instead, we can make clever use of the counter()
CSS function to add line numbers to our code blocks.
code[data-line-numbers] {
counter-reset: lineNumber;
}
code[data-line-numbers] .line::before {
counter-increment: lineNumber;
content: counter(lineNumber);
display: inline-block;
text-align: right;
/* stylistic preferences */
margin-right: 0.75rem;
width: 1rem;
color: rgb(255 255 255 / 0.2);
}
- (5): Add a
:before
pseudo element to house our line numbers - (6): Increment the
lineNumber
counter by 1 for every instance of the elements - (7): Populate the contents of the pseudo element with the current
lineNumber
value - (2): Reset the
lineNumber
counter each instance of a code block
Skeleton
- Use Tailwind's range of utility classes to create a skeleton that roughly resembles the content you're about to load.
<div class="space-y-5 rounded-2xl bg-white/5 p-4">
<div class="h-24 rounded-lg bg-rose-100/10"></div>
<div class="space-y-3">
<div class="h-3 w-3/5 rounded-lg bg-rose-100/10"></div>
<div class="h-3 w-4/5 rounded-lg bg-rose-100/20"></div>
<div class="h-3 w-2/5 rounded-lg bg-rose-100/20"></div>
</div>
</div>
Gradient overlay
- Use Tailwind's gradient color stops to create a gradient that fades from transparent to white and back to transparent.
<div
class="
[...]
bg-gradient-to-r from-transparent via-rose-100/10 to-transparent"
></div>
Animation
- Define a CSS keyframe animation that translates elements 100% to the right in the extend keyframes object of
tailwind.config.js
. - Use Tailwind's arbitrary values to apply the keyframe animation to the overlay element.
{
"keyframes": {
"shimmer": {
"100%": {
"transform": "translateX(100%)",
},
},
}
},
<div class="[...] -translate-x-full animate-[shimmer_2s_infinite]"></div>
Combine the skeleton and overlay animation
- Add the overlay to a pseudo-element of the skeleton wrapper using Tailwind's
before:
modifier.
<div
class="
[...]
relative
before:absolute before:inset-0
before:-translate-x-full
before:animate-[shimmer_2s_infinite]
before:bg-gradient-to-r
before:from-transparent before:via-rose-100/10 before:to-transparent"
>
[...]
</div>
Finishing touches
- Hide the overlay while it's positioned outside the skeleton.
- Add a shadow to the skeleton.
- Add a subtle border to the top of the overlay to simulate reflecting light.
- You can fix Safari overflowing the animation on rounded corners with
isolate
.
<div
class="
[...]
isolate
overflow-hidden
shadow-xl shadow-black/5
before:border-t before:border-rose-100/10"
>
[...]
</div>