fix: auto-detect dark mode in CodeBlock component

- Add useDarkMode hook that watches for .dark class on html element
- Remove hardcoded #24292e color override that broke dark mode syntax highlighting
- CodeBlock now auto-detects theme and uses vscDarkPlus style in dark mode
- Theme prop is now optional, only override when explicitly needed
This commit is contained in:
Ajax Davis 2026-01-07 18:51:00 +10:00
parent a137afd65d
commit 1efb9b0003
2 changed files with 35 additions and 8 deletions

View file

@ -1,5 +1,5 @@
import { cn } from '@tpmjs/utils/cn';
import { forwardRef, useState } from 'react';
import { forwardRef, useEffect, useState } from 'react';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
// eslint-disable-next-line import/no-internal-modules -- react-syntax-highlighter requires deep import for styles
import { prism, vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';
@ -11,6 +11,33 @@ import {
codeBlockCopyButtonVariants,
} from './variants';
/**
* Hook to detect if dark mode is active by checking for .dark class on html element
*/
function useDarkMode(): boolean {
const [isDark, setIsDark] = useState(false);
useEffect(() => {
// Check initial state
const checkDarkMode = () => {
setIsDark(document.documentElement.classList.contains('dark'));
};
checkDarkMode();
// Watch for class changes on html element
const observer = new MutationObserver(checkDarkMode);
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['class'],
});
return () => observer.disconnect();
}, []);
return isDark;
}
/**
* CodeBlock component
*
@ -35,11 +62,12 @@ import {
* ```
*/
export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
(
{ className, code, language = 'text', size = 'md', showCopy = true, theme = 'light', ...props },
ref
) => {
({ className, code, language = 'text', size = 'md', showCopy = true, theme, ...props }, ref) => {
const [copied, setCopied] = useState(false);
const isDarkMode = useDarkMode();
// Use provided theme or auto-detect from system
const effectiveTheme = theme ?? (isDarkMode ? 'dark' : 'light');
const handleCopy = async () => {
try {
@ -71,7 +99,7 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
<div className={codeBlockCodeVariants({ size })} data-language={normalizedLanguage}>
<SyntaxHighlighter
language={normalizedLanguage}
style={theme === 'dark' ? vscDarkPlus : prism}
style={effectiveTheme === 'dark' ? vscDarkPlus : prism}
customStyle={{
margin: 0,
padding: 0,
@ -82,7 +110,6 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
codeTagProps={{
style: {
fontFamily: 'inherit',
color: '#24292e', // Darker text color for better contrast
},
}}
>

View file

@ -29,7 +29,7 @@ export interface CodeBlockProps extends Omit<HTMLAttributes<HTMLDivElement>, 'ch
/**
* Color theme for syntax highlighting
* @default 'light'
* If not provided, auto-detects from .dark class on html element
*/
theme?: 'light' | 'dark';
}