Skip to content

Creating Interactive Documentation with MkDocs

Static documentation is good, but interactive documentation is better! Learn how to add interactive elements to your MkDocs site that engage users and improve learning.

Why Interactive Documentation?

Interactive elements help users: - Learn by doing instead of just reading - Experiment safely in sandboxed environments
- Understand complex concepts through visualization - Stay engaged with your content

Interactive Code Playgrounds

Using Jupyter Notebooks

Integrate Jupyter notebooks for interactive Python examples:

plugins:
  - mkdocs-jupyter:
      execute: true
      include_source: true
      theme: dark

Example notebook cell:

# Users can modify and run this code
import matplotlib.pyplot as plt
import numpy as np

# Try changing these values!
frequency = 2  # Hz
duration = 2   # seconds
sampling_rate = 100  # samples per second

t = np.linspace(0, duration, int(sampling_rate * duration))
signal = np.sin(2 * np.pi * frequency * t)

plt.figure(figsize=(10, 4))
plt.plot(t, signal)
plt.title(f'Sine Wave: {frequency} Hz')
plt.xlabel('Time (s)')
plt.ylabel('Amplitude')
plt.grid(True)
plt.show()

CodePen Embeds

Embed live HTML/CSS/JS examples:

<p class="codepen" data-height="400" data-default-tab="html,result"
   data-slug-hash="abcdef" data-user="your-username">
  <span>See the Pen <a href="https://codepen.io/your-username/pen/abcdef">
  Interactive Example</a> by Your Name</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

Interactive Diagrams

Mermaid with Clickable Elements

graph TD
    A[Start] -->|Click me| B{Decision}
    B -->|Yes| C[Process A]
    B -->|No| D[Process B]
    C --> E[End]
    D --> E

    click A "https://example.com/start" "Learn about starting"
    click B "https://example.com/decision" "Decision guide"

PlantUML Sequence Diagrams

@startuml
actor User
participant "Web App" as app
database "Database" as db

User -> app: Login request
activate app
app -> db: Validate credentials
activate db
db --> app: User verified
deactivate db
app --> User: Welcome page
deactivate app

note right of User: Try different scenarios!
@enduml

API Documentation with "Try It"

Interactive API Explorer

<div class="api-explorer">
  <h3>Try the API</h3>
  <form id="api-test">
    <label>Endpoint:</label>
    <select name="endpoint">
      <option value="/users">GET /users</option>
      <option value="/users/123">GET /users/{id}</option>
      <option value="/users" data-method="POST">POST /users</option>
    </select>

    <label>Parameters:</label>
    <textarea name="params">{
  "name": "John Doe",
  "email": "john@example.com"
}</textarea>

    <button type="submit">Send Request</button>
  </form>

  <div id="api-response">
    <!-- Response will appear here -->
  </div>
</div>

<script>
document.getElementById('api-test').addEventListener('submit', async (e) => {
  e.preventDefault();
  // Handle API request
  const response = await fetch(/* ... */);
  document.getElementById('api-response').innerHTML =
    `<pre>${JSON.stringify(await response.json(), null, 2)}</pre>`;
});
</script>

Interactive Widgets

Parameter Playground

Create sliders and inputs to demonstrate concepts:

<div class="parameter-playground">
  <h3>CSS Filter Playground</h3>

  <div class="controls">
    <label>Blur: <input type="range" id="blur" min="0" max="20" value="0">
      <span id="blur-value">0px</span>
    </label>

    <label>Brightness: <input type="range" id="brightness" min="0" max="200" value="100">
      <span id="brightness-value">100%</span>
    </label>

    <label>Contrast: <input type="range" id="contrast" min="0" max="200" value="100">
      <span id="contrast-value">100%</span>
    </label>
  </div>

  <div class="preview">
    <img loading="lazy" id="filter-preview" src="/assets/sample-image.jpg" alt="Sample">
  </div>

  <div class="code-output">
    <pre><code id="css-output">filter: none;</code></pre>
  </div>
</div>

<script>
const updateFilters = () => {
  const blur = document.getElementById('blur').value;
  const brightness = document.getElementById('brightness').value;
  const contrast = document.getElementById('contrast').value;

  const filter = `blur(${blur}px) brightness(${brightness}%) contrast(${contrast}%)`;

  document.getElementById('filter-preview').style.filter = filter;
  document.getElementById('css-output').textContent = `filter: ${filter};`;

  document.getElementById('blur-value').textContent = `${blur}px`;
  document.getElementById('brightness-value').textContent = `${brightness}%`;
  document.getElementById('contrast-value').textContent = `${contrast}%`;
};

['blur', 'brightness', 'contrast'].forEach(id => {
  document.getElementById(id).addEventListener('input', updateFilters);
});
</script>

Interactive Quizzes

Knowledge Checks

<div class="quiz-container">
  <h3>Quick Check</h3>

  <div class="question">
    <p>What is the time complexity of binary search?</p>
    <div class="options">
      <label><input type="radio" name="q1" value="a"> O(n)</label>
      <label><input type="radio" name="q1" value="b"> O(log n)</label>
      <label><input type="radio" name="q1" value="c"> O(n²)</label>
      <label><input type="radio" name="q1" value="d"> O(1)</label>
    </div>
    <button onclick="checkAnswer('q1', 'b')">Check Answer</button>
    <div class="feedback" id="q1-feedback"></div>
  </div>
</div>

<script>
function checkAnswer(question, correct) {
  const selected = document.querySelector(`input[name="${question}"]:checked`);
  const feedback = document.getElementById(`${question}-feedback`);

  if (!selected) {
    feedback.innerHTML = '<p class="warning">Please select an answer!</p>';
    return;
  }

  if (selected.value === correct) {
    feedback.innerHTML = '<p class="success">✅ Correct! Binary search has O(log n) complexity.</p>';
  } else {
    feedback.innerHTML = '<p class="error">❌ Not quite. Think about how binary search divides the problem.</p>';
  }
}
</script>

Live Data Visualization

Real-time Charts

<div id="live-chart"></div>

<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<script>
// Simulate real-time data
let x = [];
let y = [];
let counter = 0;

const layout = {
  title: 'Real-time Performance Metrics',
  xaxis: { title: 'Time (s)' },
  yaxis: { title: 'Response Time (ms)' }
};

Plotly.newPlot('live-chart', [{x, y, type: 'scatter'}], layout);

setInterval(() => {
  x.push(counter);
  y.push(Math.random() * 100 + 50);
  counter += 1;

  // Keep last 50 points
  if (x.length > 50) {
    x.shift();
    y.shift();
  }

  Plotly.update('live-chart', {x: [x], y: [y]});
}, 1000);
</script>

Interactive Command Line

Terminal Emulator

<div id="terminal"></div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm/css/xterm.css">
<script src="https://cdn.jsdelivr.net/npm/xterm/lib/xterm.js"></script>
<script>
const term = new Terminal({
  cursorBlink: true,
  theme: {
    background: '#1e1e1e',
    foreground: '#d4d4d4'
  }
});

term.open(document.getElementById('terminal'));
term.writeln('Welcome to the interactive terminal!');
term.writeln('Try these commands:');
term.writeln('  help - Show available commands');
term.writeln('  demo - Run a demo');
term.writeln('');

term.onData(data => {
  // Handle user input
  term.write(data);
});
</script>

Implementation Tips

1. Progressive Enhancement

Always provide fallbacks:

// Check if JavaScript is enabled
<noscript>
  <div class="warning">
    Interactive features require JavaScript to be enabled.
  </div>
</noscript>

// Feature detection
if ('IntersectionObserver' in window) {
  // Use intersection observer
} else {
  // Fallback behavior
}

2. Performance Considerations

  • Lazy load interactive components
  • Use loading="lazy" for iframes
  • Debounce user inputs
  • Minimize external dependencies

3. Accessibility

<!-- Provide keyboard navigation -->
<div role="application" aria-label="Interactive demo">
  <button aria-label="Run code" aria-keyshortcuts="Ctrl+Enter">
    Run
  </button>
</div>

<!-- Screen reader announcements -->
<div class="sr-only" aria-live="polite" aria-atomic="true">
  <span id="status">Ready</span>
</div>

4. Mobile Responsiveness

/* Ensure touch-friendly interfaces */
.interactive-control {
  min-height: 44px;
  min-width: 44px;
  touch-action: manipulation;
}

/* Responsive layouts */
@media (max-width: 768px) {
  .parameter-playground {
    flex-direction: column;
  }
}

Tools and Libraries

  1. Code Execution
  2. Pyodide (Python in browser)
  3. CodeMirror (code editor)
  4. Monaco Editor (VS Code editor)

  5. Visualization

  6. D3.js (data visualization)
  7. Chart.js (charts)
  8. Three.js (3D graphics)

  9. UI Components

  10. Alpine.js (lightweight interactivity)
  11. Lit (web components)
  12. Svelte (compiled components)

Examples in the Wild

Great interactive documentation examples: - MDN Web Docs - CSS/JS playgrounds - React Documentation - Interactive tutorials - D3.js Examples - Live visualizations - Svelte Tutorial - Step-by-step interactive learning

Conclusion

Interactive documentation transforms passive readers into active learners. Start small:

  1. Add a simple interactive example
  2. Gather user feedback
  3. Iterate and improve
  4. Expand gradually

Remember: The best documentation teaches through experience!


What interactive features would you like to see in documentation? Let us know! 🎮