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

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.