Overview
aSaaSin uses Shiki for MDX/React code highlighting. We render HTML on the server with codeToHtml
and style it with CSS variables so light/dark themes are switchable without re-rendering.
Docs links
- Shiki overview: shiki.style
- Themes catalog: Themes
- Supported languages: Languages
- API reference for
codeToHtml
:codeToHtml
- Using CSS variables in themes: CSS variables
Theme colors
Pick any built-in theme IDs (e.g., everforest-light
, vitesse-dark
) and set them as light/dark. Shiki exports CSS variables like --shiki-dark
/ --shiki-dark-bg
that you can target in your stylesheet.
// server-side usage
import { codeToHtml } from 'shiki';
const html = await codeToHtml('console.log(1)', {
lang: 'ts',
themes: { light: 'everforest-light', dark: 'vitesse-dark' },
});
aSaaSin CSS
We override Shiki’s dark variables so the page background stays consistent with our theme.
/* app/globals.css - only the Shiki part shown */
@layer base {
html.dark .shiki,
html.dark .shiki span {
color: var(--shiki-dark) !important;
background-color: var(--shiki-dark-bg) !important;
/* Optional font styles */
font-style: var(--shiki-dark-font-style) !important;
font-weight: var(--shiki-dark-font-weight) !important;
text-decoration: var(--shiki-dark-text-decoration) !important;
}
}
!important
is required because Shiki themes inline style attributes on each span. Without it, your CSS variables would be ignored.
Custom colors
Use colorReplacements
to remap a theme’s hardcoded colors to your design tokens (RGB lists). This preserves scopes but swaps surfaces/inks.
import { codeToHtml } from 'shiki';
import { HexColors } from '@/constants/colors';
/**
* Server helper: highlight with brand-matched backgrounds.
*/
export async function generateHighlightedCode(
code: string,
language: string,
): Promise<string> {
return codeToHtml(code, {
lang: language,
themes: {
light: 'everforest-light',
dark: 'vitesse-dark',
},
colorReplacements: {
'everforest-light': {
// original light bg → our token
'#fdf6e3': HexColors['secondary-light'],
},
'vitesse-dark': {
// original dark bg → our token
'#121212': HexColors['primary-dark'],
},
},
});
}
You can replace more colors (e.g., gutter, selection) the same way. See: https://shiki.style/guide/renderer#colorreplacements
You can also replace selection, gutter, or accent colors the same way. Reference: Theme Colors Manipulation
Use in components
Render the precomputed HTML in a server component (or MDX) and mark it as safe.
// components/SectionTemplates/CodeBlockTemplate.tsx (Server Component)
export const CodeBlockTemplate: React.FC<DocSectionsCodeBlock> = async ({
language,
code,
}) => {
const out = await generateHighlightedCode(code as string, language);
return (
<div className="group relative w-full">
<CopyButton textToCopy={code as string} />
<div
className="text-sm selection:bg-accent/40 selection:text-inherit [&>pre]:max-h-[650px] [&>pre]:overflow-x-auto [&>pre]:rounded-xl [&>pre]:p-6"
dangerouslySetInnerHTML={{ __html: out }}
/>
</div>
);
};
Server-side highlighting keeps Shiki off the client, reduces JS size, and returns stable, SEO-friendly HTML.
Theme switch
Toggle dark mode at the root to flip Shiki’s variables without re-highlighting.
Tips
- Keep light/dark pairs consistent across pages.
- Start with background replacements, then tweak selections/gutters.
- When themes update upstream, re-check your
colorReplacements
.