# Markdown Editor Theme Integration Guide ## Overview Integrate @uiw/react-md-editor with shadcn/ui theming system for consistent light/dark mode support. ## Theme Switching ### Using next-themes ```tsx 'use client' import { useTheme } from 'next-themes' import MDEditor from '@uiw/react-md-editor' export function ThemedMarkdownEditor({ value, onChange }) { const { theme } = useTheme() return (
) } ``` ### data-color-mode Attribute The `data-color-mode` attribute controls the editor's theme: - `light` - Light mode - `dark` - Dark mode - `auto` - System preference (default) ## CSS Variable Mapping Map shadcn/ui CSS variables to editor styles: ```css /* globals.css */ /* Editor Container */ .w-md-editor { --md-editor-bg: hsl(var(--background)); --md-editor-color: hsl(var(--foreground)); --md-editor-border: hsl(var(--border)); } /* Toolbar */ .w-md-editor-toolbar { background-color: hsl(var(--muted) / 0.3); border-bottom-color: hsl(var(--border)); } .w-md-editor-toolbar button { color: hsl(var(--foreground)); } .w-md-editor-toolbar button:hover { background-color: hsl(var(--accent)); color: hsl(var(--accent-foreground)); } .w-md-editor-toolbar li.active button { background-color: hsl(var(--accent)); color: hsl(var(--accent-foreground)); } /* Editor Content */ .w-md-editor-content { color: hsl(var(--foreground)); } .w-md-editor-text-pre, .w-md-editor-text-input { color: hsl(var(--foreground)); caret-color: hsl(var(--foreground)); } /* Preview */ .w-md-editor-preview { background-color: hsl(var(--background)); color: hsl(var(--foreground)); } .wmde-markdown { background-color: transparent; color: hsl(var(--foreground)); } /* Code Blocks */ .wmde-markdown pre { background-color: hsl(var(--muted)); } .wmde-markdown code { background-color: hsl(var(--muted)); color: hsl(var(--primary)); } /* Inline Code */ .wmde-markdown :not(pre) > code { background-color: hsl(var(--muted)); color: hsl(var(--primary)); padding: 0.2em 0.4em; border-radius: 0.25rem; font-size: 0.875em; } /* Blockquotes */ .wmde-markdown blockquote { border-left-color: hsl(var(--primary) / 0.5); background-color: hsl(var(--muted) / 0.5); color: hsl(var(--muted-foreground)); } /* Links */ .wmde-markdown a { color: hsl(var(--primary)); text-decoration: underline; } .wmde-markdown a:hover { color: hsl(var(--primary) / 0.8); } /* Tables */ .wmde-markdown table { border-color: hsl(var(--border)); } .wmde-markdown th, .wmde-markdown td { border-color: hsl(var(--border)); } .wmde-markdown th { background-color: hsl(var(--muted)); } .wmde-markdown tr:nth-child(even) { background-color: hsl(var(--muted) / 0.3); } /* Horizontal Rule */ .wmde-markdown hr { border-color: hsl(var(--border)); } /* Headings */ .wmde-markdown h1, .wmde-markdown h2, .wmde-markdown h3, .wmde-markdown h4, .wmde-markdown h5, .wmde-markdown h6 { color: hsl(var(--foreground)); border-bottom: none; } ``` ## Tailwind Class Approach Use Tailwind utility classes for theming: ```tsx 'use client' import { useTheme } from 'next-themes' import MDEditor from '@uiw/react-md-editor' import { cn } from '@/lib/utils' export function MarkdownEditor({ value, onChange, className }) { const { theme } = useTheme() return (
) } ``` ## Typography Integration Integrate with Tailwind Typography (prose): ```css /* Preview with prose styling */ .w-md-editor-preview { @apply prose prose-sm dark:prose-invert max-w-none p-4; } /* Override prose defaults with theme colors */ .w-md-editor-preview.prose { --tw-prose-body: hsl(var(--foreground)); --tw-prose-headings: hsl(var(--foreground)); --tw-prose-links: hsl(var(--primary)); --tw-prose-bold: hsl(var(--foreground)); --tw-prose-code: hsl(var(--primary)); --tw-prose-pre-bg: hsl(var(--muted)); --tw-prose-quotes: hsl(var(--muted-foreground)); --tw-prose-quote-borders: hsl(var(--primary) / 0.5); } .w-md-editor-preview.prose.dark { --tw-prose-body: hsl(var(--foreground)); --tw-prose-headings: hsl(var(--foreground)); --tw-prose-links: hsl(var(--primary)); --tw-prose-bold: hsl(var(--foreground)); --tw-prose-code: hsl(var(--primary)); --tw-prose-pre-bg: hsl(var(--muted)); --tw-prose-quotes: hsl(var(--muted-foreground)); --tw-prose-quote-borders: hsl(var(--primary) / 0.5); } ``` ## Complete Component with Theme ```tsx 'use client' import { useTheme } from 'next-themes' import dynamic from 'next/dynamic' import { ComponentProps, useEffect, useState } from 'react' import rehypeSanitize from 'rehype-sanitize' import { cn } from '@/lib/utils' const MDEditor = dynamic( () => import('@uiw/react-md-editor'), { ssr: false } ) type MDEditorProps = ComponentProps interface MarkdownEditorProps extends Omit { value: string onChange?: (value?: string) => void height?: number | string className?: string } export function MarkdownEditor({ value, onChange, height = 400, className, ...props }: MarkdownEditorProps) { const { theme, resolvedTheme } = useTheme() const [mounted, setMounted] = useState(false) useEffect(() => { setMounted(true) }, []) if (!mounted) { return (
Loading editor...
) } const colorMode = (resolvedTheme || theme) === 'dark' ? 'dark' : 'light' return (
) } ``` ## Dark Mode Specifics ### Handle System Preference ```tsx import { useTheme } from 'next-themes' import { useEffect, useState } from 'react' export function useEditorTheme() { const { theme, systemTheme } = useTheme() const [colorMode, setColorMode] = useState<'light' | 'dark'>('light') useEffect(() => { const effectiveTheme = theme === 'system' ? systemTheme : theme setColorMode(effectiveTheme === 'dark' ? 'dark' : 'light') }, [theme, systemTheme]) return colorMode } // Usage const colorMode = useEditorTheme()
``` ### Handle Theme Transitions ```css /* Smooth theme transitions */ .w-md-editor, .w-md-editor-toolbar, .w-md-editor-content, .w-md-editor-preview, .wmde-markdown, .wmde-markdown * { transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease; } ``` ## Custom Syntax Highlighting Integrate with shadcn/ui color scheme: ```tsx import { useTheme } from 'next-themes' import MDEditor from '@uiw/react-md-editor' export function MarkdownEditor({ value, onChange }) { const { theme } = useTheme() return (
{ return inline ? ( {children} ) : ( {children} ) }, }, }} />
) } ``` ## Testing Theme Integration ```tsx import { render, screen } from '@testing-library/react' import { ThemeProvider } from 'next-themes' import { MarkdownEditor } from './MarkdownEditor' describe('MarkdownEditor Theme', () => { it('applies light theme', () => { render( {}} /> ) const wrapper = screen.getByRole('textbox').closest('[data-color-mode]') expect(wrapper).toHaveAttribute('data-color-mode', 'light') }) it('applies dark theme', () => { render( {}} /> ) const wrapper = screen.getByRole('textbox').closest('[data-color-mode]') expect(wrapper).toHaveAttribute('data-color-mode', 'dark') }) }) ``` ## Troubleshooting **Issue:** Theme not applying immediately **Solution:** Ensure editor is mounted after theme is resolved **Issue:** Flash of wrong theme **Solution:** Add loading state while theme resolves **Issue:** CSS variables not working **Solution:** Verify CSS is imported in correct order (globals.css before editor CSS) **Issue:** Dark mode colors not matching shadcn/ui **Solution:** Use HSL values from CSS variables, not hardcoded colors