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:
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¶
Recommended Libraries¶
- Code Execution
- Pyodide (Python in browser)
- CodeMirror (code editor)
-
Monaco Editor (VS Code editor)
-
Visualization
- D3.js (data visualization)
- Chart.js (charts)
-
Three.js (3D graphics)
-
UI Components
- Alpine.js (lightweight interactivity)
- Lit (web components)
- 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:
- Add a simple interactive example
- Gather user feedback
- Iterate and improve
- Expand gradually
Remember: The best documentation teaches through experience!
What interactive features would you like to see in documentation? Let us know! 🎮