Skip to content

Commit 7dd4f40

Browse files
authored
feat: add Shiki transformers for better syntax highlighting (satnaing#534)
* refactor: organize prose related styles inside nested prose * feat: add Shiki transformers for better syntax highlighting * chore: update blog posts using Shiki transformers Closes satnaing#210
1 parent 5b6b01b commit 7dd4f40

9 files changed

Lines changed: 121 additions & 85 deletions

astro.config.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ import sitemap from "@astrojs/sitemap";
44
import remarkToc from "remark-toc";
55
import remarkCollapse from "remark-collapse";
66
import { SITE } from "./src/config";
7+
import {
8+
transformerNotationDiff,
9+
transformerNotationHighlight,
10+
transformerNotationWordHighlight,
11+
} from "@shikijs/transformers";
712

813
// https://astro.build/config
914
export default defineConfig({
@@ -18,7 +23,13 @@ export default defineConfig({
1823
shikiConfig: {
1924
// For more themes, visit https://shiki.style/themes
2025
themes: { light: "min-light", dark: "night-owl" },
21-
wrap: true,
26+
defaultColor: false,
27+
wrap: false,
28+
transformers: [
29+
transformerNotationDiff({ matchAlgorithm: "v3" }),
30+
transformerNotationHighlight(),
31+
transformerNotationWordHighlight(),
32+
],
2233
},
2334
},
2435
vite: {

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"devDependencies": {
3030
"@astrojs/check": "^0.9.4",
3131
"@pagefind/default-ui": "^1.3.0",
32+
"@shikijs/transformers": "^3.6.0",
3233
"@tailwindcss/typography": "^0.5.16",
3334
"@types/lodash.kebabcase": "^4.1.9",
3435
"@typescript-eslint/parser": "^8.34.0",

pnpm-lock.yaml

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/data/blog/adding-new-post.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ If you don’t want subdirectories to affect the post URL, just prefix the folde
4444
src/data/blog/very-first-post.md -> mysite.com/posts/very-first-post
4545
src/data/blog/2025/example-post.md -> mysite.com/posts/2025/example-post
4646
src/data/blog/_2026/another-post.md -> mysite.com/posts/another-post
47-
src/data/blog/docs/_legacy/how-to.md -> mysite.com/docs/how-to
48-
src/data/blog/Example Dir/Dummy Post.md -> mysite.com/example-dir/dummy-post
47+
src/data/blog/docs/_legacy/how-to.md -> mysite.com/posts/docs/how-to
48+
src/data/blog/Example Dir/Dummy Post.md -> mysite.com/posts/example-dir/dummy-post
4949
```
5050

5151
> 💡 Tip: You can override a blog post’s slug in the frontmatter as well. See the next section for more details.
@@ -91,6 +91,7 @@ If you omit `tags` in a blog post (in other words, if no tag is specified), the
9191
export const blogSchema = z.object({
9292
// ---
9393
draft: z.boolean().optional(),
94+
// [!code highlight:1]
9495
tags: z.array(z.string()).default(["others"]), // replace "others" with whatever you want
9596
// ---
9697
});
@@ -128,17 +129,20 @@ Write `Table of contents` in h2 format (## in markdown) and place it where you w
128129

129130
For instance, if you want to place your table of contents just under the intro paragraph (like I usually do), you can do that in the following way.
130131

132+
<!-- prettier-ignore-start -->
131133
```md
132134
---
133135
# some frontmatter
134136
---
135137

136138
Here are some recommendations, tips & ticks for creating new posts in AstroPaper blog theme.
137139

140+
<!-- [!code ++] -->
138141
## Table of contents
139142

140143
<!-- the rest of the post -->
141144
```
145+
<!-- prettier-ignore-end -->
142146

143147
## Headings
144148

src/data/blog/how-to-add-latex-equations-in-blog-posts.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,15 @@ In this section, you will find instructions on how to add support for LaTeX in y
4343
// other configs
4444
markdown: {
4545
remarkPlugins: [
46-
remarkMath, // <- new plugin
46+
remarkMath, // [!code ++]
4747
remarkToc,
4848
[remarkCollapse, { test: "Table of contents" }],
4949
],
50-
rehypePlugins: [rehypeKatex], // <- new plugin
50+
rehypePlugins: [rehypeKatex], // [!code ++]
5151
shikiConfig: {
5252
// For more themes, visit https://shiki.style/themes
5353
themes: { light: "min-light", dark: "night-owl" },
54-
wrap: true,
54+
wrap: false,
5555
},
5656
},
5757
// other configs
@@ -71,6 +71,7 @@ In this section, you will find instructions on how to add support for LaTeX in y
7171
<!-- others... -->
7272
<script is:inline src="/toggle-theme.js"></script>
7373
74+
<!-- [!code highlight:4] -->
7475
<link
7576
rel="stylesheet"
7677
href="https://cdn.jsdelivr.net/npm/katex@0.15.2/dist/katex.min.css"
@@ -90,6 +91,7 @@ In this section, you will find instructions on how to add support for LaTeX in y
9091
/* other classes */
9192

9293
/* Katex text color */
94+
/* [!code highlight:3] */
9395
.prose .katex-display {
9496
@apply text-foreground;
9597
}

src/data/blog/how-to-integrate-giscus-comments.md

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -77,18 +77,18 @@ You should now have a script tag that looks like this:
7777

7878
Simply add that to the source code of the site. Most likely, if you're using _AstroPaper_ and want to enable comments on posts, navigate to `src/layouts/PostDetails.astro` and paste it into the desired location where you want the comments to appear, perhaps underneath the `Share this post on:` buttons.
7979

80-
```diff
81-
<ShareLinks />
82-
</div>
83-
84-
+ <script src="https://giscus.app/client.js"
85-
+ data-repo="[ENTER REPO HERE]"
86-
+ data-repo-id="[ENTER REPO ID HERE]"
87-
+ data-category="[ENTER CATEGORY NAME HERE]"
88-
+ data-category-id="[ENTER CATEGORY ID HERE]"
89-
+ ...
90-
+ </script>
91-
80+
```astro
81+
<Layout {...layoutProps}>
82+
<main>
83+
<ShareLinks />
84+
85+
<!-- [!code ++:6] -->
86+
<script
87+
src="https://giscus.app/client.js"
88+
data-repo="[ENTER REPO HERE]"
89+
data-repo-id="[ENTER REPO ID HERE]"
90+
data-category="[ENTER CATEGORY NAME HERE]"
91+
data-category-id="[ENTER CATEGORY ID HERE]"></script>
9292
</main>
9393
<Footer />
9494
</Layout>
@@ -189,19 +189,18 @@ Note that specifying a `theme` here will override the `lightTheme` and `darkThem
189189

190190
To complete the process, add the new Comments component to `src/layouts/PostDetails.astro` (replacing the `script` tag from the previous step).
191191

192-
```diff
193-
+ import Comments from "@/components/Comments";
192+
```jsx
193+
// [!code ++:1]
194+
import Comments from "@/components/Comments";
194195

195-
<ShareLinks />
196-
</div>
196+
<ShareLinks />
197197

198-
+ <Comments client:only="react" />
198+
// [!code ++:1]
199+
<Comments client:only="react" />
199200

200-
<hr class="my-6 border-dashed" />
201+
<hr class="my-6 border-dashed" />
201202

202-
</main>
203-
<Footer />
204-
</Layout>
203+
<Footer />
205204
```
206205

207206
And that's it!

src/data/blog/setting-dates-via-git-hooks.md

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -142,15 +142,15 @@ To allow Astro to compile the markdown and do its thing, it needs to know what i
142142

143143
To allow the key to be there with no value we need to edit line 10 to add the `.nullable()` function.
144144

145-
```typescript
145+
```ts
146146
const blog = defineCollection({
147147
type: "content",
148148
schema: ({ image }) =>
149149
z.object({
150150
author: z.string().default(SITE.author),
151151
pubDatetime: z.date(),
152-
- modDatetime: z.date().optional(),
153-
+ modDatetime: z.date().optional().nullable(),
152+
modDatetime: z.date().optional(), // [!code --]
153+
modDatetime: z.date().optional().nullable(), // [!code ++]
154154
title: z.string(),
155155
featured: z.boolean().optional(),
156156
draft: z.boolean().optional(),
@@ -167,25 +167,23 @@ To stop the IDE complaining in the blog engine files I have also done the follow
167167

168168
1. added `| null` to line 15 in `src/layouts/Layout.astro` so that it looks like
169169

170-
```typescript
171-
export interface Props {
172-
title?: string;
173-
author?: string;
174-
description?: string;
175-
ogImage?: string;
176-
canonicalURL?: string;
177-
pubDatetime?: Date;
178-
modDatetime?: Date | null;
179-
}
180-
```
181-
182-
<!-- This needs to be 2 as it doesn't pick it up with the code block -->
170+
```typescript
171+
export interface Props {
172+
title?: string;
173+
author?: string;
174+
description?: string;
175+
ogImage?: string;
176+
canonicalURL?: string;
177+
pubDatetime?: Date;
178+
modDatetime?: Date | null;
179+
}
180+
```
183181

184182
2. added `| null` to line 5 in `src/components/Datetime.tsx` so that it looks like
185183

186-
```typescript
187-
interface DatetimesProps {
188-
pubDatetime: string | Date;
189-
modDatetime: string | Date | undefined | null;
190-
}
191-
```
184+
```typescript
185+
interface DatetimesProps {
186+
pubDatetime: string | Date;
187+
modDatetime: string | Date | undefined | null;
188+
}
189+
```

src/layouts/PostDetails.astro

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,10 @@ const nextPost =
103103
<Datetime {pubDatetime} {modDatetime} {timezone} size="lg" class="my-2" />
104104
<EditPost class="max-sm:hidden" {hideEditPost} {post} />
105105
</div>
106-
<article id="article" class="mx-auto prose mt-8 max-w-app">
106+
<article
107+
id="article"
108+
class="mx-auto prose mt-8 max-w-app prose-pre:bg-(--shiki-light-bg) dark:prose-pre:bg-(--shiki-dark-bg)"
109+
>
107110
<Content />
108111
</article>
109112

src/styles/typography.css

Lines changed: 42 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,56 @@
11
@plugin '@tailwindcss/typography';
22

33
@layer base {
4+
/* ===== Override default Tailwind Typography styles ===== */
45
.prose {
56
@apply prose-headings:!mb-3 prose-headings:!text-foreground prose-h3:italic prose-p:!text-foreground prose-a:!text-foreground prose-a:!decoration-dashed prose-a:underline-offset-8 hover:prose-a:text-accent prose-blockquote:!border-l-accent/50 prose-blockquote:opacity-80 prose-figcaption:!text-foreground prose-figcaption:opacity-70 prose-strong:!text-foreground prose-code:rounded prose-code:bg-muted/75 prose-code:p-1 prose-code:!text-foreground prose-code:before:!content-none prose-code:after:!content-none prose-ol:!text-foreground prose-ul:overflow-x-clip prose-ul:!text-foreground prose-li:marker:!text-accent prose-table:text-foreground prose-th:border prose-th:border-border prose-td:border prose-td:border-border prose-img:mx-auto prose-img:!my-2 prose-img:border-2 prose-img:border-border prose-hr:!border-border;
7+
8+
a {
9+
@apply break-words hover:!text-accent;
10+
}
11+
12+
details {
13+
@apply inline-block cursor-pointer text-foreground select-none [&_p]:hidden [&_ul]:!my-0;
14+
}
15+
16+
summary {
17+
@apply focus-visible:no-underline focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-accent focus-visible:outline-dashed;
18+
}
19+
20+
thead th:first-child,
21+
tbody td:first-child,
22+
tfoot td:first-child {
23+
padding-inline-start: 0.5714286em !important;
24+
}
625
}
7-
.prose a {
8-
@apply break-words hover:!text-accent;
9-
}
10-
.prose thead th:first-child,
11-
tbody td:first-child,
12-
tfoot td:first-child {
13-
padding-inline-start: 0.5714286em !important;
14-
}
15-
.prose h2#table-of-contents {
16-
@apply mb-2;
17-
}
18-
.prose details {
19-
@apply inline-block cursor-pointer text-foreground select-none;
20-
}
21-
.prose summary {
22-
@apply focus-visible:no-underline focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-accent focus-visible:outline-dashed;
26+
27+
/* ===== Code Blocks & Syntax Highlighting ===== */
28+
.astro-code {
29+
@apply border bg-(--shiki-light-bg) text-(--shiki-light) outline-border [&_span]:text-(--shiki-light);
2330
}
24-
.prose h2#table-of-contents + p {
25-
@apply hidden;
31+
32+
html[data-theme="dark"] .astro-code {
33+
@apply bg-(--shiki-dark-bg) text-(--shiki-dark) [&_span]:text-(--shiki-dark);
2634
}
2735

28-
/* ===== Code Blocks & Syntax Highlighting ===== */
29-
pre:has(code) {
30-
@apply border border-border;
36+
/* Styles for Shiki transformers */
37+
/* https://shiki.style/packages/transformers */
38+
.astro-code {
39+
.line.diff.add {
40+
@apply relative *:bg-green-500/20 before:absolute before:-left-3 before:text-green-500 before:content-['+'];
41+
}
42+
.line.diff.remove {
43+
@apply relative *:bg-red-500/30 before:absolute before:-left-3 before:text-red-500 before:content-['-'];
44+
}
45+
.line.highlighted {
46+
@apply *:!bg-slate-400/20;
47+
}
48+
.highlighted-word {
49+
@apply rounded-sm border border-border px-0.5 py-px;
50+
}
3151
}
3252

53+
/* Break words in code and blockqoute */
3354
.prose code,
3455
.prose blockquote {
3556
@apply break-words;
@@ -39,18 +60,4 @@
3960
/* add line breaks whenever necessary for codes under table */
4061
@apply break-all sm:break-normal;
4162
}
42-
43-
pre > code {
44-
white-space: pre;
45-
}
46-
47-
/* Apply Dark Theme (if multi-theme specified) */
48-
html[data-theme="dark"] pre:has(code),
49-
html[data-theme="dark"] pre:has(code) span {
50-
color: var(--shiki-dark) !important;
51-
background-color: var(--shiki-dark-bg) !important;
52-
font-style: var(--shiki-dark-font-style) !important;
53-
font-weight: var(--shiki-dark-font-weight) !important;
54-
text-decoration: var(--shiki-dark-text-decoration) !important;
55-
}
5663
}

0 commit comments

Comments
 (0)