Skip to content

feat: replace Prism with Shiki and add support for light/dark themes #1146

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion app/blog/[...slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import 'css/prism.css'
import 'katex/dist/katex.css'

import PageTitle from '@/components/PageTitle'
Expand Down
2 changes: 1 addition & 1 deletion components/MDXComponents.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import TOCInline from 'pliny/ui/TOCInline'
import Pre from 'pliny/ui/Pre'
import BlogNewsletterForm from 'pliny/ui/BlogNewsletterForm'
import type { MDXComponents } from 'mdx/types'
import Image from './Image'
import CustomLink from './Link'
import TableWrapper from './TableWrapper'
import Pre from './Pre'

export const components: MDXComponents = {
Image,
Expand Down
59 changes: 59 additions & 0 deletions components/Pre.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/* eslint-disable prettier/prettier */
'use client'

import { useState, useRef, ReactNode } from 'react'

const Pre = ({ children, ...props }: { children: ReactNode; [key: string] }) => {
const preRef = useRef<HTMLPreElement>(null)
const [copied, setCopied] = useState(false)

const onCopy = () => {
if (preRef.current) {
const codeElements = preRef.current.querySelectorAll('code')
const textToCopy = Array.from(codeElements)
.map((code) => code.textContent)
.join('\n')

navigator.clipboard.writeText(textToCopy)
setCopied(true)
setTimeout(() => setCopied(false), 2000)
}
}

const icons = {
copy: (
<svg width="16" height="16" viewBox="0 0 15 15" fill="none">
<path
d="M1 9.50006C1 10.3285 1.67157 11.0001 2.5 11.0001H4L4 10.0001H2.5C2.22386 10.0001 2 9.7762 2 9.50006L2 2.50006C2 2.22392 2.22386 2.00006 2.5 2.00006L9.5 2.00006C9.77614 2.00006 10 2.22392 10 2.50006V4.00002H5.5C4.67158 4.00002 4 4.67159 4 5.50002V12.5C4 13.3284 4.67158 14 5.5 14H12.5C13.3284 14 14 13.3284 14 12.5V5.50002C14 4.67159 13.3284 4.00002 12.5 4.00002H11V2.50006C11 1.67163 10.3284 1.00006 9.5 1.00006H2.5C1.67157 1.00006 1 1.67163 1 2.50006V9.50006ZM5 5.50002C5 5.22388 5.22386 5.00002 5.5 5.00002H12.5C12.7761 5.00002 13 5.22388 13 5.50002V12.5C13 12.7762 12.7761 13 12.5 13H5.5C5.22386 13 5 12.7762 5 12.5V5.50002Z"
fill="currentColor"
/>
</svg>
),
checked: (
<svg width="18" height="18" viewBox="0 0 15 15" fill="none">
<path
d="M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z"
fill="currentColor"
/>
</svg>
),
}

return (
<div className="group relative border border-gray-200 dark:border-none">
<pre ref={preRef} {...props} className={`${props.className || ''} relative mb-0`}>
{children}
</pre>

<button
aria-label="Copy code"
onClick={onCopy}
className="absolute top-2 right-2 z-10 rounded p-1 text-gray-400 opacity-0 transition-opacity duration-200 group-hover:opacity-100"
>
{copied ? icons.checked : icons.copy}
</button>
</div>
)
}

export default Pre
17 changes: 15 additions & 2 deletions contentlayer.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import rehypeAutolinkHeadings from 'rehype-autolink-headings'
import rehypeKatex from 'rehype-katex'
import rehypeKatexNoTranslate from 'rehype-katex-notranslate'
import rehypeCitation from 'rehype-citation'
import rehypePrismPlus from 'rehype-prism-plus'
import rehypeShiki from '@shikijs/rehype'
import rehypePresetMinify from 'rehype-preset-minify'
import siteMetadata from './data/siteMetadata'
import { allCoreContent, sortPosts } from 'pliny/utils/contentlayer.js'
Expand Down Expand Up @@ -175,7 +175,20 @@ export default makeSource({
rehypeKatex,
rehypeKatexNoTranslate,
[rehypeCitation, { path: path.join(root, 'data') }],
[rehypePrismPlus, { defaultLanguage: 'js', ignoreMissing: true }],

// Configure Shiki with light and dark themes that automatically switch based on the app's theme preference.
// For a complete list of available themes and to experiment with different styles, visit:
// https://shiki.style/themes

[
rehypeShiki,
{
themes: {
light: 'github-light',
dark: 'aurora-x',
},
},
],
rehypePresetMinify,
],
},
Expand Down
142 changes: 0 additions & 142 deletions css/prism.css

This file was deleted.

58 changes: 58 additions & 0 deletions css/tailwind.css
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,61 @@ input:-webkit-autofill:focus {
display: inline-block;
vertical-align: middle;
}

/* Code Block and Code Title styles */
/* Class-based Dark Theme Styles for Shiki Syntax Highlighting. For more info visit - https://shiki.style/guide/dual-themes*/

html.dark .shiki,
html.dark .shiki span {
color: var(--shiki-dark) !important;
background-color: var(--shiki-dark-bg) !important;

font-style: var(--shiki-dark-font-style) !important;
font-weight: var(--shiki-dark-font-weight) !important;
text-decoration: var(--shiki-dark-text-decoration) !important;
}

/* Code block title style. Feel free to edit */
.remark-code-title {
@apply relative border-t border-r border-l border-gray-200 px-4 py-3 font-mono text-sm font-semibold dark:border-none;
@apply bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-300;
@apply rounded-t-md;
padding-right: 3.5rem; /* Make space for dots */
}

/* Create the three dots using a pseudo-element */
.remark-code-title::before {
content: '';
position: absolute;
right: 5rem;
top: 50%;
transform: translateY(-50%);
display: inline-flex;
gap: 0.5rem;
}

/* Create the dots using box shadows */
.remark-code-title::before {
content: '';
width: 8px;
height: 8px;
border-radius: 50%;
/* Mac-style red, yellow, green dots using box-shadow trick */
box-shadow:
20px 0 0 #f87171,
40px 0 0 #fbbf24,
60px 0 0 #4ade80;
}

/* Dark mode colors for three dot */
.dark .remark-code-title::before {
box-shadow:
20px 0 0 #ef4444,
40px 0 0 #eab308,
60px 0 0 #22c55e;
}

/* Adjust the pre element to connect perfectly */
.remark-code-title + div > pre {
@apply mt-0 rounded-t-none;
}
38 changes: 38 additions & 0 deletions data/blog/release-of-tailwind-nextjs-starter-blog-v2.0.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,44 @@ The primary theme color is updated from `teal` to `pink` and the primary gray th

Inter is now replaced with Space Grotesk as the default font.

## New Code Block Light/Dark Themes

We’ve replaced PrismJS with **[Shiki](https://shiki.style/)** for simplified, accurate, and faster syntax highlighting.

### Why Shiki?

- **Faster & More Accurate**: Shiki uses same engine as VS Code's syntax highlighting. Provides very accurate and fast syntax highlighting for almost any mainstream programming language.
- **No Customization Required**: No need for custom RegEx, CSS, or HTML.

### Features and Customization:

- **Light & Dark Mode Support**: Automatically switches between light and dark themes based on the app’s current theme.

- **More Themes**: it offers variety of built-in themes to choose from, just like VS Code.

- **Custom Themes**: You can load custom themes by passing a Theme object into the themes array. ( learn more about custom themes [Shiki custom Themes](https://shiki.style/themes).)

- **Change Themes**: Modify code block light/dark themes in the `contentlayer.ts` file.Default Light/dark theme is `github-light` and `aurora-x`.

- **Code Title Style**: The code title is designed with a minimal three-dot MacBook style. You can adjust or remove it in the `css/tailwind.css` file.

For a complete list of available themes and to experiment with different styles, visit [Shiki Themes](https://shiki.style/themes).

```ts:contentlayer.config.ts
[
rehypeShiki,
{
themes: {
// Here you can play with All built-in Shiki themes.
// You can load custom themes by passing a Theme object into the themes array.

light: 'github-light',
dark: 'aurora-x',
},
},
]
```

### New Layouts

Layout components available in the `layouts` directory, provide a simple way to customize the look and feel of the blog.[^2]
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"dependencies": {
"@headlessui/react": "2.2.0",
"@next/bundle-analyzer": "15.2.4",
"@shikijs/rehype": "^3.3.0",
"@tailwindcss/forms": "^0.5.9",
"@tailwindcss/postcss": "^4.0.5",
"@tailwindcss/typography": "^0.5.15",
Expand All @@ -37,7 +38,6 @@
"rehype-katex": "^7.0.0",
"rehype-katex-notranslate": "^1.1.4",
"rehype-preset-minify": "7.0.0",
"rehype-prism-plus": "^2.0.0",
"rehype-slug": "^6.0.0",
"remark": "^15.0.0",
"remark-gfm": "^4.0.0",
Expand Down
Loading