MD
MD2Card
Development Practice

MD2Card Blog System Development Complete Guide

J
JsonChao
MD2Card Project Developer
May 26, 2025
8 min read
Next.jsInternationalizationTheme SystemMarkdown

MD2Card Blog System Development Complete Guide

MD2Card is a Next.js-based Markdown to knowledge card tool that supports multiple themes and internationalization. This article will detail the design and implementation of its blog system.

Core Features

1. Internationalization Support

MD2Card supports 8 languages through a custom LanguageContext:

// i18n/LanguageContext.js
import { createContext, useContext, useState } from 'react';

const LanguageContext = createContext();

export function LanguageProvider({ children }) {
  const [language, setLanguage] = useState('en');
  
  return (
    <LanguageContext.Provider value={{ language, setLanguage }}>
      {children}
    </LanguageContext.Provider>
  );
}

2. Theme System

The theme system supports 20+ beautiful themes, each with complete style configuration:

// themes/themes.js
export const themes = {
  glassmorphism: {
    background: 'rgba(255, 255, 255, 0.1)',
    backdropFilter: 'blur(10px)',
    border: '1px solid rgba(255, 255, 255, 0.2)',
    // ... other styles
  },
  // ... other themes
};

3. Markdown Rendering

Secure Markdown rendering using marked and DOMPurify:

import { marked } from 'marked';
import DOMPurify from 'dompurify';

const renderMarkdown = (content) => {
  const rawHtml = marked(content);
  return DOMPurify.sanitize(rawHtml);
};

Technology Stack

Frontend Framework

  • Next.js 13.x (Pages Router)
  • Material-UI + Tailwind CSS
  • React Hooks state management

Core Libraries

  • marked + DOMPurify: Markdown parsing
  • html-to-image: Image export
  • framer-motion: Animation effects

Project Structure

md2card/
├── pages/                    # Next.js page routing
├── components/              # React components
├── themes/                  # Theme configuration
├── i18n/                   # Internationalization
└── public/                 # Static assets

Development Standards

Code Style

  • Use functional components
  • Prefer React Hooks
  • Follow ESLint standards
  • Use TypeScript type checking

Performance Optimization

  • Implement code splitting
  • Use React.memo for render optimization
  • Image lazy loading
  • Proper caching strategies

Deployment & Maintenance

Build Optimization

  • Enable production optimizations
  • Check bundle size
  • Implement static export
  • Configure environment variables

Monitoring & Maintenance

  • Error boundary handling
  • Performance monitoring
  • User feedback collection
  • Regular updates and maintenance

Future Plans

  1. Support more themes
  2. Add more languages
  3. Optimize mobile experience
  4. Add more export formats

Conclusion

The development of MD2Card's blog system fully demonstrates modern frontend development best practices. Through reasonable architecture design and standardized code organization, we've achieved a high-performance, maintainable blog system. We will continue to optimize and expand features to provide users with a better experience. --- id: 1 title: "MD2Card Blog System Development Complete Guide" excerpt: "A comprehensive guide to the design and implementation of MD2Card's blog system, including internationalization, theme system, and Markdown rendering." date: "2025-01-15" readTime: 12 category: "Development Practice" tags: ["Next.js", "Internationalization", "Theme System", "Markdown", "React"] image: "https://images.unsplash.com/photo-1555066931-4365d14bab8c?ixlib=rb-4.0.3&auto=format&fit=crop&w=1000&q=80" author: name: "JsonChao" bio: "MD2Card Project Developer"

MD2Card Blog System Development Complete Guide

MD2Card is a Next.js-based innovative Markdown to knowledge card tool that supports multiple themes and complete internationalization. This article provides an in-depth analysis of the blog system's design philosophy and implementation details, offering comprehensive technical guidance for developers.

🎯 Project Overview

Core Objectives

  • User Experience First: Clean and intuitive interface design that lets users focus on content creation
  • Highly Scalable: Flexible architecture design supporting rapid addition of new features and themes
  • Performance Excellence: Optimized loading speeds and smooth interactive experiences
  • Global Support: Complete multi-language support covering global user needs

Key Features

  1. Rich Theme Library - Provides 20+ professionally designed theme templates
  2. Multi-language Support - Supports 8 mainstream languages including English, Chinese, Japanese
  3. Real-time Preview - WYSIWYG editing experience
  4. High-quality Export - Supports high-definition export in multiple formats like PNG, SVG

🏗️ Technical Architecture Deep Dive

Core Technology Stack

// Complete technology stack configuration
const techStack = {
  // Frontend framework
  framework: "Next.js 13.x (Pages Router)",
  
  // UI framework
  ui: {
    components: "Material-UI (MUI)",
    styling: "Tailwind CSS",
    icons: "@mui/icons-material"
  },
  
  // State management
  stateManagement: {
    global: "React Context API",
    local: "React Hooks (useState, useEffect)",
    forms: "React Hook Form"
  },
  
  // Internationalization
  i18n: {
    core: "Custom LanguageContext",
    storage: "localStorage + URL params"
  },
  
  // Content processing
  content: {
    parser: "marked",
    sanitizer: "DOMPurify",
    highlighter: "Prism.js"
  },
  
  // Image processing
  imaging: {
    export: "html-to-image",
    optimization: "Canvas API"
  },
  
  // Animation
  animation: "framer-motion",
  
  // Deployment
  deployment: {
    platform: "Vercel/Netlify",
    type: "Static Site Generation"
  }
};

Project Architecture Diagram

md2card/
├── pages/ # Next.js page routing
│ ├── index.js # Main page - core feature integration
│ ├── blog.js # Blog listing page
│ ├── blog/
│ │ └── [slug].js # Blog detail page
│ ├── app.js # Global app configuration
│ └── document.js # HTML document structure
├── components/ # React component library
│ ├── Introduction.js # Product introduction component
│ ├── CardPreview.js # Card preview component
│ ├── ThemeSelector.js # Theme selector
│ ├── MarkdownEditor.js # Markdown editor
│ ├── ExportOptions.js # Export functionality component
│ ├── LanguageSwitcher.js # Language switcher
│ └── MarkdownGuide.js # Markdown syntax guide
├── themes/ # Theme system
│ └── themes.js # Theme configuration file
├── i18n/ # Internationalization system
│ ├── LanguageContext.js # Language context
│ └── translations.js # Translation resources
├── data/ # Data layer
│ ├── blog/ # Blog article data
│ └── getAllPosts.js # Data fetching utilities
└── public/ # Static assets
├── images/
└── icons/

🌍 Internationalization System Implementation

LanguageContext Core Design

// i18n/LanguageContext.js - Complete implementation
import { createContext, useContext, useState, useEffect, useCallback } from 'react';
import { translations } from './translations';

const LanguageContext = createContext();

export const LanguageProvider = ({ children }) => {
  const [currentLang, setCurrentLang] = useState('en');
  const [isLoading, setIsLoading] = useState(true);

  // Language detection logic
  useEffect(() => {
    const detectLanguage = () => {
      // 1. Check language code in URL path
      const pathLang = window.location.pathname.split('/')[1];
      if (translations[pathLang]) {
        return pathLang;
      }

      // 2. Check user preference saved in localStorage
      const savedLang = localStorage.getItem('preferred-language');
      if (savedLang && translations[savedLang]) {
        return savedLang;
      }

      // 3. Check browser language settings
      const browserLang = navigator.language.split('-')[0];
      if (translations[browserLang]) {
        return browserLang;
      }

      // 4. Default to English
      return 'en';
    };

    const detectedLang = detectLanguage();
    setCurrentLang(detectedLang);
    setIsLoading(false);
  }, []);

  // Translation function
  const t = useCallback((key, params = {}) => {
    const keys = key.split('.');
    let value = translations[currentLang];

    for (const k of keys) {
      if (value && typeof value === 'object') {
        value = value[k];
      } else {
        console.warn(`Translation key "${key}" not found for language "${currentLang}"`);
        return key;
      }
    }

    // Parameter replacement
    if (typeof value === 'string' && Object.keys(params).length > 0) {
      return value.replace(/\{\{(\w+)\}\}/g, (match, param) => {
        return params[param] || match;
      });
    }

    return value || key;
  }, [currentLang]);

  // Set language
  const setLanguage = useCallback((lang) => {
    if (translations[lang]) {
      setCurrentLang(lang);
      localStorage.setItem('preferred-language', lang);
      
      // Update URL path
      const currentPath = window.location.pathname;
      const pathSegments = currentPath.split('/').filter(Boolean);
      
      // If first segment is language code, replace it
      if (pathSegments.length > 0 && translations[pathSegments[0]]) {
        pathSegments[0] = lang;
      } else {
        pathSegments.unshift(lang);
      }
      
      const newPath = '/' + pathSegments.join('/');
      window.history.pushState({}, '', newPath);
    }
  }, []);

  // Get supported languages list
  const getSupportedLanguages = useCallback(() => {
    return Object.keys(translations).map(code => ({
      code,
      name: translations[code].languageName,
      nativeName: translations[code].languageNativeName
    }));
  }, []);

  const contextValue = {
    currentLang,
    setLanguage,
    t,
    isLoading,
    getSupportedLanguages
  };

  return (
    <LanguageContext.Provider value={contextValue}>
      {children}
    </LanguageContext.Provider>
  );
};

// Custom Hook
export const useLanguage = () => {
  const context = useContext(LanguageContext);
  if (!context) {
    throw new Error('useLanguage must be used within a LanguageProvider');
  }
  return context;
};

Translation Resource Management

// i18n/translations.js - Complete translation configuration
export const translations = {
  en: {
    // Language info
    languageName: 'English',
    languageNativeName: 'English',
    
    // App basic info
    appName: 'MD2Card',
    appDescription: 'Transform Markdown into beautiful knowledge cards',
    
    // Navigation menu
    navigation: {
      home: 'Home',
      blog: 'Blog',
      about: 'About',
      contact: 'Contact'
    },
    
    // Home content
    home: {
      hero: {
        title: 'Transform Markdown into Beautiful Cards',
        subtitle: 'Support multiple themes, generate high-quality images with one click',
        ctaButton: 'Get Started',
        learnMore: 'Learn More'
      },
      features: {
        title: 'Core Features',
        subtitle: 'Providing complete Markdown card solutions',
        list: {
          themes: {
            title: 'Rich Themes',
            description: '20+ professionally designed theme templates'
          },
          export: {
            title: 'High-quality Export',
            description: 'Support export in multiple formats like PNG, SVG'
          },
          realtime: {
            title: 'Real-time Preview',
            description: 'WYSIWYG editing experience'
          },
          i18n: {
            title: 'Multi-language Support',
            description: 'Support for 8 mainstream languages'
          }
        }
      }
    },
    
    // Blog related
    blog: {
      title: 'Tech Blog',
      subtitle: 'Share development experience and technical insights',
      readMore: 'Read More',
      readTime: 'Reading Time',
      minutes: 'minutes',
      tags: 'Tags',
      author: 'Author',
      publishedOn: 'Published on',
      categories: {
        development: 'Development Practice',
        design: 'Design Philosophy', 
        tutorial: 'Tutorial',
        news: 'Project News'
      }
    },
    
    // Editor interface
    editor: {
      placeholder: 'Enter Markdown content here...',
      preview: 'Preview',
      edit: 'Edit',
      themes: 'Themes',
      export: 'Export',
      copy: 'Copy',
      download: 'Download',
      settings: 'Settings'
    },
    
    // Theme selector
    themes: {
      title: 'Select Theme',
      search: 'Search themes...',
      categories: {
        all: 'All',
        business: 'Business',
        creative: 'Creative',
        academic: 'Academic',
        modern: 'Modern',
        vintage: 'Vintage'
      },
      descriptions: {
        glassmorphism: 'Modern glassmorphism design',
        neumorphism: 'Soft neumorphism style',
        neon: 'Cool neon light effects',
        executive: 'Professional business style',
        paper: 'Classic paper texture'
      }
    },
    
    // Export functionality
    export: {
      title: 'Export Settings',
      format: 'Format',
      quality: 'Quality',
      size: 'Size',
      background: 'Background',
      transparent: 'Transparent',
      white: 'White',
      custom: 'Custom',
      exporting: 'Exporting...',
      success: 'Export successful!',
      error: 'Export failed, please try again'
    },
    
    // Common buttons and actions
    common: {
      save: 'Save',
      cancel: 'Cancel',
      confirm: 'Confirm',
      delete: 'Delete',
      edit: 'Edit',
      view: 'View',
      close: 'Close',
      loading: 'Loading...',
      error: 'Error occurred',
      retry: 'Retry',
      success: 'Success'
    },
    
    // Error messages
    errors: {
      networkError: 'Network connection failed, please check network settings',
      fileTooBig: 'File size exceeds limit',
      invalidFormat: 'Unsupported file format',
      exportFailed: 'Export failed, please try again'
    }
  },
  
  zh: {
    languageName: '中文',
    languageNativeName: '中文',
    appName: 'MD2Card',
    appDescription: '将 Markdown 转换为精美的知识卡片',
    // ... other Chinese translations
  }
  
  // ... other language configurations
};

🎨 Theme System Deep Analysis

Theme Architecture Design

// themes/themes.js - Complete theme system
export const themes = {
  // Glassmorphism theme
  glassmorphism: {
    id: 'glassmorphism',
    name: 'Glassmorphism',
    category: 'modern',
    description: 'Modern glass texture with blur background effects',
    preview: '/themes/glassmorphism-preview.png',
    
    // Main styles
    styles: {
      background: `
        linear-gradient(135deg, 
          rgba(255, 255, 255, 0.1), 
          rgba(255, 255, 255, 0.05)
        )
      `,
      color: '#ffffff',
      fontFamily: `
        "SF Pro Display", 
        -apple-system, 
        BlinkMacSystemFont, 
        "Segoe UI", 
        Roboto, 
        sans-serif
      `,
      
      // Layout properties
      borderRadius: '20px',
      padding: '2.5rem',
      margin: '1rem',
      
      // Visual effects
      backdropFilter: 'blur(20px) saturate(180%)',
      boxShadow: `
        0 8px 32px 0 rgba(31, 38, 135, 0.37),
        inset 0 1px 0 rgba(255, 255, 255, 0.1),
        0 0 0 1px rgba(255, 255, 255, 0.05)
      `,
      border: '1px solid rgba(255, 255, 255, 0.18)',
      
      // Text styles
      textShadow: '0 1px 2px rgba(0, 0, 0, 0.1)',
      lineHeight: '1.6'
    },
    
    // Custom CSS
    customCSS: `
      .card-container {
        position: relative;
        overflow: hidden;
      }
      
      .card-container::before {
        content: '';
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        background: linear-gradient(
          45deg,
          rgba(255, 255, 255, 0.1) 0%,
          transparent 50%,
          rgba(255, 255, 255, 0.1) 100%
        );
        pointer-events: none;
      }
      
      .card-header {
        background: rgba(255, 255, 255, 0.15);
        backdrop-filter: blur(10px);
        border-bottom: 1px solid rgba(255, 255, 255, 0.1);
        padding: 1.5rem 2rem;
        margin: -2.5rem -2.5rem 2rem -2.5rem;
        border-radius: 20px 20px 0 0;
      }
      
      .card-title {
        font-size: 2rem;
        font-weight: 700;
        margin-bottom: 0.5rem;
        background: linear-gradient(45deg, #ffffff, #f0f8ff);
        -webkit-background-clip: text;
        -webkit-text-fill-color: transparent;
        text-shadow: none;
      }
      
      .card-content {
        background: rgba(255, 255, 255, 0.05);
        border-radius: 15px;
        padding: 1.5rem;
        margin: 1rem 0;
        backdrop-filter: blur(5px);
      }
      
      .card-footer {
        background: linear-gradient(
          90deg, 
          rgba(255, 255, 255, 0.1), 
          rgba(255, 255, 255, 0.05)
        );
        padding: 1rem 2rem;
        margin: 2rem -2.5rem -2.5rem -2.5rem;
        border-radius: 0 0 20px 20px;
        border-top: 1px solid rgba(255, 255, 255, 0.1);
      }
      
      /* Code block styles */
      .card-content pre {
        background: rgba(0, 0, 0, 0.3);
        border-radius: 10px;
        padding: 1rem;
        overflow-x: auto;
        backdrop-filter: blur(10px);
        border: 1px solid rgba(255, 255, 255, 0.1);
      }
      
      .card-content code {
        background: rgba(255, 255, 255, 0.2);
        padding: 0.2rem 0.4rem;
        border-radius: 4px;
        font-family: "SF Mono", Monaco, "Cascadia Code", "Roboto Mono", Consolas, "Courier New", monospace;
      }
      
      /* Responsive design */
      @media (max-width: 768px) {
        .card-container {
          margin: 0.5rem;
        }
        
        .card-header,
        .card-footer {
          padding: 1rem;
        }
        
        .card-title {
          font-size: 1.5rem;
        }
        
        .card-content {
          padding: 1rem;
        }
      }
    `,
    
    // Theme configuration
    config: {
      supportsDarkMode: true,
      supportsCustomBackground: true,
      animationDuration: '0.3s',
      responsiveBreakpoints: {
        mobile: '768px',
        tablet: '1024px'
      }
    }
  },
  
  // Executive theme
  executive: {
    id: 'executive',
    name: 'Executive',
    category: 'business',
    description: 'Professional business style for formal occasions',
    preview: '/themes/executive-preview.png',
    
    styles: {
      background: `
        linear-gradient(145deg, 
          #1a1a2e 0%, 
          #16213e 50%, 
          #0f3460 100%
        )
      `,
      color: '#ffffff',
      fontFamily: '"Georgia", "Times New Roman", serif',
      
      borderRadius: '12px',
      padding: '3rem',
      
      boxShadow: `
        0 25px 50px -12px rgba(0, 0, 0, 0.6),
        0 0 0 1px rgba(255, 255, 255, 0.1),
        inset 0 1px 0 rgba(255, 255, 255, 0.1)
      `,
      
      textShadow: '0 1px 3px rgba(0, 0, 0, 0.3)',
      lineHeight: '1.8'
    },
    
    customCSS: `
      .card-title {
        font-size: 2.5rem;
        font-weight: 700;
        margin-bottom: 1.5rem;
        text-align: center;
        background: linear-gradient(45deg, #ffd700, #ffed4e, #ffd700);
        -webkit-background-clip: text;
        -webkit-text-fill-color: transparent;
        position: relative;
      }
      
      .card-title::after {
        content: '';
        position: absolute;
        bottom: -10px;
        left: 50%;
        transform: translateX(-50%);
        width: 100px;
        height: 3px;
        background: linear-gradient(90deg, #ffd700, #ffed4e);
        border-radius: 2px;
      }
      
      .card-content {
        font-size: 1.1rem;
        line-height: 1.8;
        text-align: justify;
      }
      
      .card-content h1, 
      .card-content h2, 
      .card-content h3 {
        color: #ffd700;
        margin: 1.5rem 0 1rem 0;
      }
      
      .card-content strong {
        color: #ffed4e;
        font-weight: 600;
      }
      
      .card-content blockquote {
        border-left: 4px solid #ffd700;
        padding-left: 1.5rem;
        margin: 1.5rem 0;
        font-style: italic;
        background: rgba(255, 215, 0, 0.1);
        padding: 1rem 1rem 1rem 1.5rem;
        border-radius: 0 8px 8px 0;
      }
    `
  }
  
  // ... other theme definitions
};

// Theme Manager
export class ThemeManager {
  constructor() {
    this.currentTheme = null;
    this.observers = [];
  }
  
  // Apply theme
  applyTheme(themeId) {
    const theme = themes[themeId];
    if (!theme) {
      console.error(`Theme "${themeId}" not found`);
      return;
    }
    
    this.currentTheme = theme;
    this.injectThemeStyles(theme);
    this.notifyObservers(theme);
  }
  
  // Inject theme styles
  injectThemeStyles(theme) {
    // Remove old theme styles
    const existingStyle = document.getElementById('theme-styles');
    if (existingStyle) {
      existingStyle.remove();
    }
    
    // Create new style element
    const styleElement = document.createElement('style');
    styleElement.id = 'theme-styles';
    styleElement.type = 'text/css';
    
    // Generate CSS
    const css = this.generateThemeCSS(theme);
    styleElement.innerHTML = css;
    
    // Add to page
    document.head.appendChild(styleElement);
  }
  
  // Generate theme CSS
  generateThemeCSS(theme) {
    const { styles, customCSS } = theme;
    
    let css = `
      .card-preview {
        background: ${styles.background};
        color: ${styles.color};
        font-family: ${styles.fontFamily};
        border-radius: ${styles.borderRadius};
        padding: ${styles.padding};
        box-shadow: ${styles.boxShadow};
        text-shadow: ${styles.textShadow || 'none'};
        line-height: ${styles.lineHeight || '1.6'};
        transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
      }
    `;
    
    if (styles.backdropFilter) {
      css += `
        .card-preview {
          backdrop-filter: ${styles.backdropFilter};
        }
      `;
    }
    
    if (styles.border) {
      css += `
        .card-preview {
          border: ${styles.border};
        }
      `;
    }
    
    // Add custom CSS
    if (customCSS) {
      css += customCSS;
    }
    
    return css;
  }
  
  // Subscribe to theme changes
  subscribe(callback) {
    this.observers.push(callback);
  }
  
  // Notify observers
  notifyObservers(theme) {
    this.observers.forEach(callback => callback(theme));
  }
  
  // Get themes by category
  getThemesByCategory(category = null) {
    const themeList = Object.values(themes);
    
    if (category) {
      return themeList.filter(theme => theme.category === category);
    }
    
    return themeList;
  }
  
  // Search themes
  searchThemes(query) {
    const lowercaseQuery = query.toLowerCase();
    return Object.values(themes).filter(theme => 
      theme.name.toLowerCase().includes(lowercaseQuery) ||
      theme.description.toLowerCase().includes(lowercaseQuery)
    );
  }
}

📝 Markdown Rendering Engine

Secure Rendering Implementation

// utils/markdownRenderer.js - Complete renderer
import { marked } from 'marked';
import DOMPurify from 'dompurify';
import Prism from 'prismjs';

// Configure marked
marked.setOptions({
  breaks: true,
  gfm: true,
  headerIds: true,
  mangle: false,
  sanitize: false
});

// Custom renderer
const renderer = new marked.Renderer();

// Custom code block rendering
renderer.code = function(code, language) {
  const validLanguage = Prism.languages[language] ? language : 'plaintext';
  const highlightedCode = Prism.highlight(code, Prism.languages[validLanguage], validLanguage);
  
  return `
    <pre class="language-${validLanguage}">
      <code class="language-${validLanguage}">${highlightedCode}</code>
    </pre>
  `;
};

// Custom link rendering
renderer.link = function(href, title, text) {
  const isExternal = href.startsWith('http') && !href.includes(window.location.hostname);
  const target = isExternal ? ' target="_blank" rel="noopener noreferrer"' : '';
  const titleAttr = title ? ` title="${title}"` : '';
  
  return `<a href="${href}"${titleAttr}${target}>${text}</a>`;
};

// Custom image rendering
renderer.image = function(href, title, text) {
  const titleAttr = title ? ` title="${title}"` : '';
  const altAttr = text ? ` alt="${text}"` : '';
  
  return `
    <figure class="image-figure">
      <img src="${href}"${altAttr}${titleAttr} loading="lazy" />
      ${text ? `<figcaption>${text}</figcaption>` : ''}
    </figure>
  `;
};

marked.use({ renderer });

// Markdown Renderer Class
export class MarkdownRenderer {
  constructor(options = {}) {
    this.options = {
      sanitize: true,
      highlightCode: true,
      enableMath: false,
      ...options
    };
    
    this.setupDOMPurify();
  }
  
  // Configure DOMPurify
  setupDOMPurify() {
    this.sanitizerConfig = {
      ALLOWED_TAGS: [
        'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
        'p', 'br', 'strong', 'em', 'u', 's', 'del',
        'a', 'img', 'figure', 'figcaption',
        'ul', 'ol', 'li',
        'blockquote', 'pre', 'code',
        'table', 'thead', 'tbody', 'tr', 'th', 'td',
        'hr', 'div', 'span'
      ],
      ALLOWED_ATTR: [
        'href', 'title', 'alt', 'src', 'target', 'rel',
        'class', 'id', 'style'
      ],
      ALLOW_DATA_ATTR: false,
      FORBID_SCRIPT: true,
      FORBID_TAGS: ['script', 'object', 'embed', 'iframe'],
      KEEP_CONTENT: true
    };
  }
  
  // Render Markdown
  render(markdown) {
    try {
      // Preprocess
      const processedMarkdown = this.preprocess(markdown);
      
      // Convert to HTML
      let html = marked(processedMarkdown);
      
      // Postprocess
      html = this.postprocess(html);
      
      // Sanitize
      if (this.options.sanitize) {
        html = DOMPurify.sanitize(html, this.sanitizerConfig);
      }
      
      return html;
    } catch (error) {
      console.error('Markdown rendering error:', error);
      return '<p>Rendering error, please check Markdown syntax</p>';
    }
  }
  
  // Preprocess Markdown
  preprocess(markdown) {
    let processed = markdown;
    
    // Process math formulas (if enabled)
    if (this.options.enableMath) {
      processed = this.processMath(processed);
    }
    
    // Process custom syntax
    processed = this.processCustomSyntax(processed);
    
    return processed;
  }
  
  // Postprocess HTML
  postprocess(html) {
    let processed = html;
    
    // Add responsive table wrapper
    processed = processed.replace(
      /<table/g, 
      '<div class="table-wrapper"><table'
    ).replace(
      /<\/table>/g, 
      '</table></div>'
    );
    
    // Process checkboxes
    processed = processed.replace(
      /\[x\]/gi, 
      '<input type="checkbox" checked disabled class="task-checkbox" />'
    ).replace(
      /\[ \]/g, 
      '<input type="checkbox" disabled class="task-checkbox" />'
    );
    
    return processed;
  }
  
  // Process custom syntax
  processCustomSyntax(markdown) {
    // Alert box syntax: :::warning content :::
    markdown = markdown.replace(
      /:::(\w+)\s*([\s\S]*?):::/g, 
      (match, type, content) => {
        return `<div class="alert alert-${type}">${content.trim()}</div>`;
      }
    );
    
    // Highlight syntax: ==highlighted text==
    markdown = markdown.replace(
      /==(.*?)==/g, 
      '<mark>$1</mark>'
    );
    
    return markdown;
  }
  
  // Process math formulas
  processMath(markdown) {
    // Inline formulas: $...$
    markdown = markdown.replace(
      /\$(?!\$)(.*?)\$/g, 
      '<span class="math-inline">$1</span>'
    );
    
    // Block formulas: $$...$$
    markdown = markdown.replace(
      /\$\$([\s\S]*?)\$\$/g, 
      '<div class="math-block">$1</div>'
    );
    
    return markdown;
  }
  
  // Generate table of contents
  generateTOC(markdown) {
    const tokens = marked.lexer(markdown);
    const toc = [];
    
    tokens.forEach(token => {
      if (token.type === 'heading') {
        toc.push({
          level: token.depth,
          text: token.text,
          anchor: this.generateAnchor(token.text)
        });
      }
    });
    
    return toc;
  }
  
  // Generate anchor
  generateAnchor(text) {
    return text
      .toLowerCase()
      .replace(/[^a-z0-9]+/g, '-')
      .replace(/^-+|-+$/g, '');
  }
}

// Default renderer instance
export const defaultRenderer = new MarkdownRenderer();

// Render function
export const renderMarkdown = (markdown, options = {}) => {
  const renderer = new MarkdownRenderer(options);
  return renderer.render(markdown);
};

🖼️ Image Export System

High-quality Export Implementation

// utils/imageExporter.js - Complete export system
import { toPng, toSvg, toJpeg } from 'html-to-image';

export class ImageExporter {
  constructor() {
    this.defaultOptions = {
      quality: 1.0,
      pixelRatio: 3,
      backgroundColor: 'transparent',
      skipFonts: false,
      cacheBust: true
    };
  }
  
  // Export to PNG
  async exportToPNG(element, options = {}) {
    const config = { ...this.defaultOptions, ...options };
    
    try {
      // Preprocess element
      const processedElement = await this.preprocessElement(element);
      
      // Generate image
      const dataUrl = await toPng(processedElement, config);
      
      // Postprocess
      return this.postprocessImage(dataUrl, 'png');
    } catch (error) {
      console.error('PNG export failed:', error);
      throw new Error('PNG export failed');
    }
  }
  
  // Export to SVG
  async exportToSVG(element, options = {}) {
    const config = { 
      ...this.defaultOptions, 
      ...options,
      backgroundColor: 'transparent' // SVG usually uses transparent background
    };
    
    try {
      const processedElement = await this.preprocessElement(element);
      const svgString = await toSvg(processedElement, config);
      
      return this.postprocessImage(svgString, 'svg');
    } catch (error) {
      console.error('SVG export failed:', error);
      throw new Error('SVG export failed');
    }
  }
  
  // Export to JPEG
  async exportToJPEG(element, options = {}) {
    const config = { 
      ...this.defaultOptions, 
      ...options,
      backgroundColor: '#ffffff' // JPEG needs background color
    };
    
    try {
      const processedElement = await this.preprocessElement(element);
      const dataUrl = await toJpeg(processedElement, config);
      
      return this.postprocessImage(dataUrl, 'jpeg');
    } catch (error) {
      console.error('JPEG export failed:', error);
      throw new Error('JPEG export failed');
    }
  }
  
  // Preprocess element
  async preprocessElement(element) {
    // Clone element
    const clonedElement = element.cloneNode(true);
    
    // Ensure all fonts are loaded
    await this.ensureFontsLoaded(clonedElement);
    
    // Process images
    await this.processImages(clonedElement);
    
    // Optimize styles
    this.optimizeStyles(clonedElement);
    
    return clonedElement;
  }
  
  // Ensure fonts are loaded
  async ensureFontsLoaded(element) {
    const fonts = this.extractFonts(element);
    
    if (fonts.length > 0) {
      await Promise.all(
        fonts.map(font => this.loadFont(font))
      );
    }
  }
  
  // Extract font information
  extractFonts(element) {
    const fonts = new Set();
    const computedStyles = window.getComputedStyle(element);
    
    // Add element's own font
    fonts.add(computedStyles.fontFamily);
    
    // Recursively check child elements
    const children = element.querySelectorAll('*');
    children.forEach(child => {
      const childStyle = window.getComputedStyle(child);
      fonts.add(childStyle.fontFamily);
    });
    
    return Array.from(fonts);
  }
  
  // Load font
  async loadFont(fontFamily) {
    try {
      // Use Font Loading API
      const font = new FontFace(fontFamily, `local(${fontFamily})`);
      await font.load();
      document.fonts.add(font);
    } catch (error) {
      console.warn(`Failed to load font: ${fontFamily}`);
    }
  }
  
  // Process images
  async processImages(element) {
    const images = element.querySelectorAll('img');
    
    await Promise.all(
      Array.from(images).map(async (img) => {
        try {
          // Ensure image is fully loaded
          if (!img.complete) {
            await new Promise((resolve, reject) => {
              img.onload = resolve;
              img.onerror = reject;
            });
          }
          
          // Convert to base64 (if needed)
          if (img.src.startsWith('http')) {
            const base64 = await this.imageToBase64(img.src);
            img.src = base64;
          }
        } catch (error) {
          console.warn(`Failed to process image: ${img.src}`);
        }
      })
    );
  }
  
  // Image to base64
  async imageToBase64(url) {
    return new Promise((resolve, reject) => {
      const canvas = document.createElement('canvas');
      const ctx = canvas.getContext('2d');
      const img = new Image();
      
      img.crossOrigin = 'anonymous';
      img.onload = () => {
        canvas.width = img.width;
        canvas.height = img.height;
        ctx.drawImage(img, 0, 0);
        resolve(canvas.toDataURL());
      };
      img.onerror = reject;
      img.src = url;
    });
  }
  
  // Optimize styles
  optimizeStyles(element) {
    // Remove unnecessary animations
    const animatedElements = element.querySelectorAll('*');
    animatedElements.forEach(el => {
      el.style.animation = 'none';
      el.style.transition = 'none';
    });
    
    // Ensure all elements are visible
    element.style.visibility = 'visible';
    element.style.opacity = '1';
    
    // Fix possible layout issues
    element.style.transform = 'none';
    element.style.filter = 'none';
  }
  
  // Postprocess image
  postprocessImage(dataUrl, format) {
    return {
      dataUrl,
      format,
      size: this.calculateImageSize(dataUrl),
      timestamp: Date.now()
    };
  }
  
  // Calculate image size
  calculateImageSize(dataUrl) {
    const base64String = dataUrl.split(',')[1];
    const bytes = atob(base64String).length;
    
    return {
      bytes,
      kb: Math.round(bytes / 1024 * 100) / 100,
      mb: Math.round(bytes / 1024 / 1024 * 100) / 100
    };
  }
  
  // Download image
  downloadImage(dataUrl, filename = 'md2card') {
    const link = document.createElement('a');
    link.download = filename;
    link.href = dataUrl;
    
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
  }
  
  // Copy to clipboard
  async copyToClipboard(dataUrl) {
    try {
      // Convert to Blob
      const response = await fetch(dataUrl);
      const blob = await response.blob();
      
      // Copy to clipboard
      await navigator.clipboard.write([
        new ClipboardItem({ [blob.type]: blob })
      ]);
      
      return true;
    } catch (error) {
      console.error('Failed to copy to clipboard:', error);
      return false;
    }
  }
}

// Default exporter instance
export const defaultExporter = new ImageExporter();

// Convenient export function
export const exportCard = async (element, format = 'png', options = {}) => {
  const exporter = new ImageExporter();
  
  switch (format.toLowerCase()) {
    case 'png':
      return await exporter.exportToPNG(element, options);
    case 'svg':
      return await exporter.exportToSVG(element, options);
    case 'jpeg':
    case 'jpg':
      return await exporter.exportToJPEG(element, options);
    default:
      throw new Error(`Unsupported format: ${format}`);
  }
};

🚀 Performance Optimization Strategies

Component-level Optimization

// components/OptimizedCardPreview.js - Optimized preview component
import React, { memo, useMemo, useCallback, useRef, useEffect } from 'react';
import { useLanguage } from '../i18n/LanguageContext';
import { defaultRenderer } from '../utils/markdownRenderer';

const CardPreview = memo(({ 
  markdown, 
  theme, 
  onPreviewReady,
  enableLiveUpdate = true 
}) => {
  const { t } = useLanguage();
  const previewRef = useRef(null);
  const renderTimeoutRef = useRef(null);
  
  // Use useMemo to cache rendering results
  const renderedContent = useMemo(() => {
    if (!markdown) return '';
    
    try {
      return defaultRenderer.render(markdown);
    } catch (error) {
      console.error('Rendering error:', error);
      return `<p>${t('errors.renderingFailed')}</p>`;
    }
  }, [markdown, t]);
  
  // Use useMemo to cache theme styles
  const themeStyles = useMemo(() => {
    if (!theme) return {};
    
    return {
      background: theme.styles.background,
      color: theme.styles.color,
      fontFamily: theme.styles.fontFamily,
      borderRadius: theme.styles.borderRadius,
      padding: theme.styles.padding,
      boxShadow: theme.styles.boxShadow,
      backdropFilter: theme.styles.backdropFilter,
      border: theme.styles.border,
      textShadow: theme.styles.textShadow,
      lineHeight: theme.styles.lineHeight,
      transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)'
    };
  }, [theme]);
  
  // Debounced update callback
  const debouncedUpdate = useCallback(() => {
    if (renderTimeoutRef.current) {
      clearTimeout(renderTimeoutRef.current);
    }
    
    renderTimeoutRef.current = setTimeout(() => {
      if (onPreviewReady && previewRef.current) {
        onPreviewReady(previewRef.current);
      }
    }, 300);
  }, [onPreviewReady]);
  
  // Listen for content changes
  useEffect(() => {
    if (enableLiveUpdate) {
      debouncedUpdate();
    }
  }, [renderedContent, themeStyles, enableLiveUpdate, debouncedUpdate]);
  
  // Cleanup timers
  useEffect(() => {
    return () => {
      if (renderTimeoutRef.current) {
        clearTimeout(renderTimeoutRef.current);
      }
    };
  }, []);
  
  return (
    <div
      ref={previewRef}
      className="card-preview"
      style={themeStyles}
      dangerouslySetInnerHTML={{ __html: renderedContent }}
    />
  );
});

CardPreview.displayName = 'CardPreview';

export default CardPreview;

🔮 Future Development Planning

Technology Roadmap

// Future feature planning
const futureFeatures = {
  // Phase 1: Core feature enhancement
  phase1: {
    aiThemeGeneration: 'AI-based intelligent theme generation',
    collaborativeEditing: 'Real-time collaborative editing',
    advancedExport: 'More export formats and options',
    pluginSystem: 'Plugin system architecture'
  },
  
  // Phase 2: Platform development
  phase2: {
    userAccounts: 'User account system',
    cloudSync: 'Cloud synchronization',
    themeMarketplace: 'Theme marketplace',
    apiAccess: 'Open API interface'
  },
  
  // Phase 3: Ecosystem building
  phase3: {
    mobileApp: 'Mobile application',
    desktopApp: 'Desktop application',
    enterpriseFeatures: 'Enterprise features',
    aiAssistant: 'AI writing assistant'
  }
};

Conclusion

The development of MD2Card's blog system fully demonstrates modern frontend development best practices. Through reasonable architecture design, complete internationalization support, rich theme system, and high-quality export functionality, we have created a powerful and user-friendly Markdown knowledge card platform.

Project Highlights

  1. Modular Architecture: Clear code organization and component division
  2. Internationalization Support: Complete multi-language solution
  3. Theme System: Flexible and extensible theme framework
  4. Performance Optimization: Multi-level performance optimization strategies
  5. Security and Reliability: Comprehensive security protection mechanisms

Technical Innovation

  • Dynamic Theme Engine: Real-time theme switching and style injection
  • Smart Caching System: Multi-level caching for performance improvement
  • Secure Rendering: XSS-protected Markdown rendering
  • High-quality Export: Professional-grade image export functionality

In the future, MD2Card will continue to evolve, providing users worldwide with even better knowledge management and content creation experiences. We believe that through continuous technological innovation and user experience optimization, MD2Card will become a benchmark product in the Markdown content creation field.

Back to articles